diff --git a/Demos/Authorization/FMXClientAuthorization.dproj b/Demos/Authorization/FMXClientAuthorization.dproj index 3b2ed514..083c4ef7 100644 --- a/Demos/Authorization/FMXClientAuthorization.dproj +++ b/Demos/Authorization/FMXClientAuthorization.dproj @@ -1,7 +1,7 @@  {0546214C-5B2E-4AE6-B696-DEF7BF88EC13} - 18.4 + 18.5 FMX FMXClientAuthorization.dpr True @@ -159,7 +159,7 @@ $(BDS)\bin\Artwork\iOS\iPhone\FM_ApplicationIcon_87x87.png $(BDS)\bin\Artwork\iOS\iPad\FM_ApplicationIcon_76x76.png $(BDS)\bin\Artwork\iOS\iPad\FM_LaunchImageLandscape_2048x1536.png - CFBundleName=$(MSBuildProjectName);CFBundleDevelopmentRegion=en;CFBundleDisplayName=$(MSBuildProjectName);CFBundleIdentifier=$(MSBuildProjectName);CFBundleInfoDictionaryVersion=7.1;CFBundleVersion=1.0.0.0;CFBundlePackageType=APPL;CFBundleSignature=????;LSRequiresIPhoneOS=true;CFBundleAllowMixedLocalizations=YES;CFBundleExecutable=$(MSBuildProjectName);UIDeviceFamily=iPhone & iPad;CFBundleResourceSpecification=ResourceRules.plist;NSLocationAlwaysUsageDescription=The reason for accessing the location information of the user;NSLocationWhenInUseUsageDescription=The reason for accessing the location information of the user;FMLocalNotificationPermission=false;UIBackgroundModes=;NSContactsUsageDescription=The reason for accessing the contacts;NSPhotoLibraryUsageDescription=The reason for accessing the photo library;NSCameraUsageDescription=The reason for accessing the camera;CFBundleShortVersionString=1.0.0 + CFBundleName=$(MSBuildProjectName);CFBundleDevelopmentRegion=en;CFBundleDisplayName=$(MSBuildProjectName);CFBundleIdentifier=$(MSBuildProjectName);CFBundleInfoDictionaryVersion=7.1;CFBundleVersion=1.0.0.0;CFBundlePackageType=APPL;CFBundleSignature=????;LSRequiresIPhoneOS=true;CFBundleAllowMixedLocalizations=YES;CFBundleExecutable=$(MSBuildProjectName);UIDeviceFamily=iPhone & iPad;CFBundleResourceSpecification=ResourceRules.plist;NSLocationAlwaysUsageDescription=The reason for accessing the location information of the user;NSLocationWhenInUseUsageDescription=The reason for accessing the location information of the user;FMLocalNotificationPermission=false;UIBackgroundModes=;NSContactsUsageDescription=The reason for accessing the contacts;NSPhotoLibraryUsageDescription=The reason for accessing the photo library;NSCameraUsageDescription=The reason for accessing the camera;CFBundleShortVersionString=1.0.0;NSPhotoLibraryAddUsageDescription=The reason for adding to the photo library;NSFaceIDUsageDescription=The reason for accessing the face id;NSLocationAlwaysAndWhenInUseUsageDescription=The reason for accessing the location information of the user;NSMicrophoneUsageDescription=The reason for accessing the microphone;NSSiriUsageDescription=The reason for accessing Siri $(BDS)\bin\Artwork\iOS\iPad\FM_LaunchImagePortrait_1536x2048.png $(BDS)\bin\Artwork\iOS\iPhone\FM_SpotlightSearchIcon_58x58.png $(BDS)\bin\Artwork\iOS\iPhone\FM_LaunchImage_1125x2436.png @@ -203,7 +203,7 @@ $(BDS)\bin\Artwork\iOS\iPhone\FM_ApplicationIcon_87x87.png $(BDS)\bin\Artwork\iOS\iPad\FM_ApplicationIcon_76x76.png $(BDS)\bin\Artwork\iOS\iPad\FM_LaunchImageLandscape_2048x1536.png - CFBundleName=$(MSBuildProjectName);CFBundleDevelopmentRegion=en;CFBundleDisplayName=$(MSBuildProjectName);CFBundleIdentifier=$(MSBuildProjectName);CFBundleInfoDictionaryVersion=7.1;CFBundleVersion=1.0.0.0;CFBundlePackageType=APPL;CFBundleSignature=????;LSRequiresIPhoneOS=true;CFBundleAllowMixedLocalizations=YES;CFBundleExecutable=$(MSBuildProjectName);UIDeviceFamily=iPhone & iPad;CFBundleResourceSpecification=ResourceRules.plist;NSLocationAlwaysUsageDescription=The reason for accessing the location information of the user;NSLocationWhenInUseUsageDescription=The reason for accessing the location information of the user;FMLocalNotificationPermission=false;UIBackgroundModes=;NSContactsUsageDescription=The reason for accessing the contacts;NSPhotoLibraryUsageDescription=The reason for accessing the photo library;NSCameraUsageDescription=The reason for accessing the camera;CFBundleShortVersionString=1.0.0 + CFBundleName=$(MSBuildProjectName);CFBundleDevelopmentRegion=en;CFBundleDisplayName=$(MSBuildProjectName);CFBundleIdentifier=$(MSBuildProjectName);CFBundleInfoDictionaryVersion=7.1;CFBundleVersion=1.0.0.0;CFBundlePackageType=APPL;CFBundleSignature=????;LSRequiresIPhoneOS=true;CFBundleAllowMixedLocalizations=YES;CFBundleExecutable=$(MSBuildProjectName);UIDeviceFamily=iPhone & iPad;CFBundleResourceSpecification=ResourceRules.plist;NSLocationAlwaysUsageDescription=The reason for accessing the location information of the user;NSLocationWhenInUseUsageDescription=The reason for accessing the location information of the user;FMLocalNotificationPermission=false;UIBackgroundModes=;NSContactsUsageDescription=The reason for accessing the contacts;NSPhotoLibraryUsageDescription=The reason for accessing the photo library;NSCameraUsageDescription=The reason for accessing the camera;CFBundleShortVersionString=1.0.0;NSPhotoLibraryAddUsageDescription=The reason for adding to the photo library;NSFaceIDUsageDescription=The reason for accessing the face id;NSLocationAlwaysAndWhenInUseUsageDescription=The reason for accessing the location information of the user;NSMicrophoneUsageDescription=The reason for accessing the microphone;NSSiriUsageDescription=The reason for accessing Siri $(BDS)\bin\Artwork\iOS\iPad\FM_LaunchImagePortrait_1536x2048.png $(BDS)\bin\Artwork\iOS\iPhone\FM_SpotlightSearchIcon_58x58.png $(BDS)\bin\Artwork\iOS\iPhone\FM_LaunchImage_1125x2436.png @@ -244,7 +244,7 @@ $(BDS)\bin\Artwork\iOS\iPhone\FM_ApplicationIcon_57x57.png $(BDS)\bin\Artwork\iOS\iPhone\FM_ApplicationIcon_60x60.png $(BDS)\bin\Artwork\iOS\iPhone\FM_LaunchImage_640x1136.png - CFBundleName=$(MSBuildProjectName);CFBundleDevelopmentRegion=en;CFBundleDisplayName=$(MSBuildProjectName);CFBundleIdentifier=$(MSBuildProjectName);CFBundleInfoDictionaryVersion=7.1;CFBundleVersion=1.0.0.0;CFBundlePackageType=APPL;CFBundleSignature=????;LSRequiresIPhoneOS=true;CFBundleAllowMixedLocalizations=YES;CFBundleExecutable=$(MSBuildProjectName);UIDeviceFamily=iPhone & iPad;CFBundleResourceSpecification=ResourceRules.plist;NSLocationAlwaysUsageDescription=The reason for accessing the location information of the user;NSLocationWhenInUseUsageDescription=The reason for accessing the location information of the user;FMLocalNotificationPermission=false;UIBackgroundModes=;NSContactsUsageDescription=The reason for accessing the contacts;NSPhotoLibraryUsageDescription=The reason for accessing the photo library;NSCameraUsageDescription=The reason for accessing the camera;CFBundleShortVersionString=1.0.0 + CFBundleName=$(MSBuildProjectName);CFBundleDevelopmentRegion=en;CFBundleDisplayName=$(MSBuildProjectName);CFBundleIdentifier=$(MSBuildProjectName);CFBundleInfoDictionaryVersion=7.1;CFBundleVersion=1.0.0.0;CFBundlePackageType=APPL;CFBundleSignature=????;LSRequiresIPhoneOS=true;CFBundleAllowMixedLocalizations=YES;CFBundleExecutable=$(MSBuildProjectName);UIDeviceFamily=iPhone & iPad;CFBundleResourceSpecification=ResourceRules.plist;NSLocationAlwaysUsageDescription=The reason for accessing the location information of the user;NSLocationWhenInUseUsageDescription=The reason for accessing the location information of the user;FMLocalNotificationPermission=false;UIBackgroundModes=;NSContactsUsageDescription=The reason for accessing the contacts;NSPhotoLibraryUsageDescription=The reason for accessing the photo library;NSCameraUsageDescription=The reason for accessing the camera;CFBundleShortVersionString=1.0.0;NSPhotoLibraryAddUsageDescription=The reason for adding to the photo library;NSFaceIDUsageDescription=The reason for accessing the face id;NSLocationAlwaysAndWhenInUseUsageDescription=The reason for accessing the location information of the user;NSMicrophoneUsageDescription=The reason for accessing the microphone;NSSiriUsageDescription=The reason for accessing Siri $(BDS)\bin\Artwork\iOS\iPad\FM_ApplicationIcon_76x76.png $(BDS)\bin\Artwork\iOS\iPad\FM_LaunchImagePortrait_1536x2048.png $(BDS)\bin\Artwork\iOS\iPhone\FM_SpotlightSearchIcon_58x58.png @@ -255,7 +255,7 @@ Debug DBXSqliteDriver;RESTComponents;DataSnapServerMidas;DBXInterBaseDriver;emsclientfiredac;DataSnapFireDAC;tethering;FireDACMSSQLDriver;bindcompfmx;DBXOracleDriver;inetdb;FmxTeeUI;FireDACIBDriver;fmx;fmxdae;FireDACDBXDriver;dbexpress;IndyCore;dsnap;DataSnapCommon;emsclient;FireDACCommon;RESTBackendComponents;soapserver;bindengine;DBXMySQLDriver;FireDACOracleDriver;CloudService;FireDACMySQLDriver;DBXFirebirdDriver;FireDACCommonDriver;DataSnapClient;inet;bindcompdbx;IndyIPCommon;IndyIPServer;IndySystem;fmxFireDAC;FireDAC;FireDACSqliteDriver;FireDACPgDriver;ibmonitor;FireDACASADriver;FireDACTDataDriver;FMXTee;soaprtl;DbxCommonDriver;ibxpress;DataSnapServer;xmlrtl;soapmidas;DataSnapNativeClient;fmxobj;ibxbindings;rtl;FireDACDSDriver;DbxClientDriver;DBXSybaseASADriver;CustomIPTransport;bindcomp;DBXInformixDriver;IndyIPClient;dbxcds;FireDACODBCDriver;DataSnapIndy10ServerTransport;dsnapxml;DataSnapProviderClient;dbrtl;inetdbxpress;FireDACMongoDBDriver;IndyProtocols;fmxase;$(DCC_UsePackage) true - CFBundleName=$(MSBuildProjectName);CFBundleDisplayName=$(MSBuildProjectName);CFBundleIdentifier=$(MSBuildProjectName);CFBundleVersion=1.0.0;CFBundlePackageType=APPL;CFBundleSignature=????;CFBundleAllowMixedLocalizations=YES;CFBundleExecutable=$(MSBuildProjectName);NSHighResolutionCapable=true;LSApplicationCategoryType=public.app-category.utilities;NSLocationAlwaysUsageDescription=The reason for accessing the location information of the user;NSLocationWhenInUseUsageDescription=The reason for accessing the location information of the user;NSContactsUsageDescription=The reason for accessing the contacts;CFBundleShortVersionString=1.0.0 + CFBundleName=$(MSBuildProjectName);CFBundleDisplayName=$(MSBuildProjectName);CFBundleIdentifier=$(MSBuildProjectName);CFBundleVersion=1.0.0;CFBundlePackageType=APPL;CFBundleSignature=????;CFBundleAllowMixedLocalizations=YES;CFBundleExecutable=$(MSBuildProjectName);NSHighResolutionCapable=true;LSApplicationCategoryType=public.app-category.utilities;NSLocationAlwaysUsageDescription=The reason for accessing the location information of the user;NSLocationWhenInUseUsageDescription=The reason for accessing the location information of the user;NSContactsUsageDescription=The reason for accessing the contacts;CFBundleShortVersionString=1.0.0;NSLocationAlwaysAndWhenInUseUsageDescription=The reason for accessing the location information of the user $(BDS)\bin\Artwork\Windows\UWP\delphi_UwpDefault_150.png @@ -292,13 +292,13 @@ 1033 true true - true false + PerMonitor Debug true - true + PerMonitor false @@ -308,11 +308,11 @@ true - true + PerMonitor true - true + PerMonitor @@ -353,12 +353,22 @@ true + + + true + + + + + true + + true - + true @@ -383,7 +393,6 @@ 1 - Contents\MacOS 0 @@ -393,6 +402,12 @@ 1 + + + res\xml + 1 + + library\lib\armeabi-v7a @@ -430,6 +445,12 @@ 1 + + + res\values-v21 + 1 + + res\drawable @@ -508,6 +529,11 @@ 1 .framework + + Contents\MacOS + 1 + .framework + 0 @@ -530,6 +556,11 @@ 1 .dylib + + Contents\MacOS + 1 + .dylib + 0 .dll;.bpl @@ -553,6 +584,11 @@ 1 .dylib + + Contents\MacOS + 1 + .dylib + 0 .bpl @@ -575,6 +611,10 @@ Contents\Resources\StartUp\ 0 + + Contents\Resources\StartUp\ + 0 + 0 @@ -711,23 +751,41 @@ 1 + + + ..\$(PROJECTNAME).app.dSYM\Contents\Resources\DWARF + 1 + + ..\ 1 + + ..\ + 1 + Contents 1 + + Contents + 1 + Contents\Resources 1 + + Contents\Resources + 1 + @@ -750,6 +808,10 @@ Contents\MacOS 1 + + Contents\MacOS + 1 + 0 @@ -789,6 +851,7 @@ + diff --git a/Demos/Authorization/MARSAuthorization.dproj b/Demos/Authorization/MARSAuthorization.dproj index 4e76524a..edc60c8d 100755 --- a/Demos/Authorization/MARSAuthorization.dproj +++ b/Demos/Authorization/MARSAuthorization.dproj @@ -7,7 +7,7 @@ Application VCL DCC32 - 18.4 + 18.5 Win32 1 @@ -51,7 +51,7 @@ CompanyName=;FileDescription=;FileVersion=1.0.0.0;InternalName=;LegalCopyright=;LegalTrademarks=;OriginalFilename=;ProductName=;ProductVersion=1.0.0.0;Comments= 1033 true - Web;Vcl;Vcl.Imaging;Vcl.Touch;Vcl.Samples;Vcl.Shell;System;Xml;Data;Datasnap;Soap;Winapi;$(DCC_NameSpace);$(DCC_NameSpace);$(DCC_NameSpace);$(DCC_NameSpace);$(DCC_NameSpace);$(DCC_NameSpace);$(DCC_NameSpace);$(DCC_NameSpace) + Web;Vcl;Vcl.Imaging;Vcl.Touch;Vcl.Samples;Vcl.Shell;System;Xml;Data;Datasnap;Soap;Winapi;$(DCC_NameSpace);$(DCC_NameSpace);$(DCC_NameSpace);$(DCC_NameSpace);$(DCC_NameSpace);$(DCC_NameSpace);$(DCC_NameSpace);$(DCC_NameSpace);$(DCC_NameSpace) false 00400000 false @@ -83,8 +83,8 @@ 0 - true true + PerMonitor DEBUG;$(DCC_Define) @@ -93,8 +93,8 @@ Debug - true true + PerMonitor diff --git a/Demos/Authorization/Server.Forms.Main.pas b/Demos/Authorization/Server.Forms.Main.pas index b393ec1e..c2978c43 100755 --- a/Demos/Authorization/Server.Forms.Main.pas +++ b/Demos/Authorization/Server.Forms.Main.pas @@ -67,7 +67,7 @@ implementation , MARS.Core.URL , MARS.Core.MessageBodyWriter, MARS.Core.MessageBodyWriters , MARS.Core.MessageBodyReader, MARS.Core.MessageBodyReaders - , MARS.Utils.Parameters.IniFile + , MARS.Utils.Parameters.IniFile, MARS.Core.RequestAndResponse.Interfaces ; procedure TMainForm.FormClose(Sender: TObject; var Action: TCloseAction); @@ -86,10 +86,9 @@ procedure TMainForm.FormCreate(Sender: TObject); // skip favicon requests (browser) FEngine.BeforeHandleRequest := - function (const AEngine: TMARSEngine; const AURL: TMARSURL; - const ARequest: TWebRequest; const AResponse: TWebResponse; - var Handled: Boolean - ): Boolean + function(const AEngine: TMARSEngine; + const AURL: TMARSURL; const ARequest: IMARSRequest; const AResponse: IMARSResponse; + var Handled: Boolean): Boolean begin Result := True; if SameText(AURL.Document, 'favicon.ico') then diff --git a/Demos/ContentTypes/Server.Resources.pas b/Demos/ContentTypes/Server.Resources.pas index 3181aad1..1b6dea09 100755 --- a/Demos/ContentTypes/Server.Resources.pas +++ b/Demos/ContentTypes/Server.Resources.pas @@ -77,10 +77,7 @@ THelloWorldResource = class function DataSet1: TDataSet; {$ifdef DelphiXE3_UP} - [GET, Path('/dataset2'), -// , Produces(TMediaType.APPLICATION_XML) -// , Produces(TMediaType.APPLICATION_JSON) -] + [GET, Path('/dataset2')] function DataSet2: TFDMemTable; [GET, Path('/dataset3') diff --git a/Demos/JsonDataObjects/JsonDataObjectsApacheModule.dproj b/Demos/JsonDataObjects/JsonDataObjectsApacheModule.dproj index 88a550c3..dc246b71 100644 --- a/Demos/JsonDataObjects/JsonDataObjectsApacheModule.dproj +++ b/Demos/JsonDataObjects/JsonDataObjectsApacheModule.dproj @@ -1,7 +1,7 @@  {9F324623-DE30-4882-85B1-E43C2F9E0A9F} - 18.4 + 18.5 VCL JsonDataObjectsApacheModule.dpr True @@ -134,7 +134,6 @@ 1 - Contents\MacOS 0 @@ -144,6 +143,12 @@ 1 + + + res\xml + 1 + + library\lib\armeabi-v7a @@ -180,6 +185,12 @@ 1 + + + res\values-v21 + 1 + + res\drawable @@ -256,6 +267,10 @@ 1 .framework + + 1 + .framework + 0 @@ -265,6 +280,10 @@ 1 .dylib + + 1 + .dylib + 0 .dll;.bpl @@ -287,6 +306,10 @@ 1 .dylib + + 1 + .dylib + 0 .bpl @@ -308,6 +331,9 @@ 0 + + 0 + 0 @@ -418,6 +444,7 @@ 1 + @@ -425,6 +452,10 @@ Contents\Resources 1 + + Contents\Resources + 1 + @@ -446,6 +477,9 @@ 1 + + 1 + 0 @@ -485,6 +519,7 @@ + diff --git a/Demos/JsonDataObjects/JsonDataObjectsConsole.dproj b/Demos/JsonDataObjects/JsonDataObjectsConsole.dproj index 297c0b8b..15bcddc4 100644 --- a/Demos/JsonDataObjects/JsonDataObjectsConsole.dproj +++ b/Demos/JsonDataObjects/JsonDataObjectsConsole.dproj @@ -1,7 +1,7 @@  {3989725D-F9E4-4178-9FBC-93DC284F63C7} - 18.4 + 18.5 VCL JsonDataObjectsConsole.dpr True @@ -130,7 +130,6 @@ 1 - Contents\MacOS 0 @@ -140,6 +139,12 @@ 1 + + + res\xml + 1 + + library\lib\armeabi-v7a @@ -176,6 +181,12 @@ 1 + + + res\values-v21 + 1 + + res\drawable @@ -252,6 +263,10 @@ 1 .framework + + 1 + .framework + 0 @@ -261,6 +276,10 @@ 1 .dylib + + 1 + .dylib + 0 .dll;.bpl @@ -283,6 +302,10 @@ 1 .dylib + + 1 + .dylib + 0 .bpl @@ -304,6 +327,9 @@ 0 + + 0 + 0 @@ -414,6 +440,7 @@ 1 + @@ -421,6 +448,10 @@ Contents\Resources 1 + + Contents\Resources + 1 + @@ -442,6 +473,9 @@ 1 + + 1 + 0 @@ -481,6 +515,7 @@ + diff --git a/Demos/JsonDataObjects/JsonDataObjectsFMX.dproj b/Demos/JsonDataObjects/JsonDataObjectsFMX.dproj index 235803dd..f7817217 100644 --- a/Demos/JsonDataObjects/JsonDataObjectsFMX.dproj +++ b/Demos/JsonDataObjects/JsonDataObjectsFMX.dproj @@ -1,7 +1,7 @@  {E0F4FA79-1B8C-437A-AB05-73EF7AD349A8} - 18.4 + 18.5 FMX JsonDataObjectsFMX.dpr True @@ -145,7 +145,7 @@ $(BDS)\bin\Artwork\iOS\iPad\FM_ApplicationIcon_72x72.png $(BDS)\bin\Artwork\iOS\iPhone\FM_LaunchImage_1242x2208.png $(BDS)\bin\Artwork\iOS\iPhone\FM_SpotlightSearchIcon_29x29.png - CFBundleName=$(MSBuildProjectName);CFBundleDevelopmentRegion=en;CFBundleDisplayName=$(MSBuildProjectName);CFBundleIdentifier=$(MSBuildProjectName);CFBundleInfoDictionaryVersion=7.1;CFBundleVersion=1.0.0.0;CFBundlePackageType=APPL;CFBundleSignature=????;LSRequiresIPhoneOS=true;CFBundleAllowMixedLocalizations=YES;CFBundleExecutable=$(MSBuildProjectName);UIDeviceFamily=iPhone & iPad;CFBundleResourceSpecification=ResourceRules.plist;NSLocationAlwaysUsageDescription=The reason for accessing the location information of the user;NSLocationWhenInUseUsageDescription=The reason for accessing the location information of the user;FMLocalNotificationPermission=false;UIBackgroundModes=;NSContactsUsageDescription=The reason for accessing the contacts;NSPhotoLibraryUsageDescription=The reason for accessing the photo library;NSCameraUsageDescription=The reason for accessing the camera;CFBundleShortVersionString=1.0.0 + CFBundleName=$(MSBuildProjectName);CFBundleDevelopmentRegion=en;CFBundleDisplayName=$(MSBuildProjectName);CFBundleIdentifier=$(MSBuildProjectName);CFBundleInfoDictionaryVersion=7.1;CFBundleVersion=1.0.0.0;CFBundlePackageType=APPL;CFBundleSignature=????;LSRequiresIPhoneOS=true;CFBundleAllowMixedLocalizations=YES;CFBundleExecutable=$(MSBuildProjectName);UIDeviceFamily=iPhone & iPad;CFBundleResourceSpecification=ResourceRules.plist;NSLocationAlwaysUsageDescription=The reason for accessing the location information of the user;NSLocationWhenInUseUsageDescription=The reason for accessing the location information of the user;FMLocalNotificationPermission=false;UIBackgroundModes=;NSContactsUsageDescription=The reason for accessing the contacts;NSPhotoLibraryUsageDescription=The reason for accessing the photo library;NSCameraUsageDescription=The reason for accessing the camera;CFBundleShortVersionString=1.0.0;NSPhotoLibraryAddUsageDescription=The reason for adding to the photo library;NSFaceIDUsageDescription=The reason for accessing the face id;NSLocationAlwaysAndWhenInUseUsageDescription=The reason for accessing the location information of the user;NSMicrophoneUsageDescription=The reason for accessing the microphone;NSSiriUsageDescription=The reason for accessing Siri $(BDS)\bin\Artwork\iOS\iPhone\FM_SpotlightSearchIcon_80x80.png iPhoneAndiPad $(BDS)\bin\Artwork\iOS\iPhone\FM_SpotlightSearchIcon_58x58.png @@ -189,7 +189,7 @@ $(BDS)\bin\Artwork\iOS\iPad\FM_ApplicationIcon_72x72.png $(BDS)\bin\Artwork\iOS\iPhone\FM_LaunchImage_1242x2208.png $(BDS)\bin\Artwork\iOS\iPhone\FM_SpotlightSearchIcon_29x29.png - CFBundleName=$(MSBuildProjectName);CFBundleDevelopmentRegion=en;CFBundleDisplayName=$(MSBuildProjectName);CFBundleIdentifier=$(MSBuildProjectName);CFBundleInfoDictionaryVersion=7.1;CFBundleVersion=1.0.0.0;CFBundlePackageType=APPL;CFBundleSignature=????;LSRequiresIPhoneOS=true;CFBundleAllowMixedLocalizations=YES;CFBundleExecutable=$(MSBuildProjectName);UIDeviceFamily=iPhone & iPad;CFBundleResourceSpecification=ResourceRules.plist;NSLocationAlwaysUsageDescription=The reason for accessing the location information of the user;NSLocationWhenInUseUsageDescription=The reason for accessing the location information of the user;FMLocalNotificationPermission=false;UIBackgroundModes=;NSContactsUsageDescription=The reason for accessing the contacts;NSPhotoLibraryUsageDescription=The reason for accessing the photo library;NSCameraUsageDescription=The reason for accessing the camera;CFBundleShortVersionString=1.0.0 + CFBundleName=$(MSBuildProjectName);CFBundleDevelopmentRegion=en;CFBundleDisplayName=$(MSBuildProjectName);CFBundleIdentifier=$(MSBuildProjectName);CFBundleInfoDictionaryVersion=7.1;CFBundleVersion=1.0.0.0;CFBundlePackageType=APPL;CFBundleSignature=????;LSRequiresIPhoneOS=true;CFBundleAllowMixedLocalizations=YES;CFBundleExecutable=$(MSBuildProjectName);UIDeviceFamily=iPhone & iPad;CFBundleResourceSpecification=ResourceRules.plist;NSLocationAlwaysUsageDescription=The reason for accessing the location information of the user;NSLocationWhenInUseUsageDescription=The reason for accessing the location information of the user;FMLocalNotificationPermission=false;UIBackgroundModes=;NSContactsUsageDescription=The reason for accessing the contacts;NSPhotoLibraryUsageDescription=The reason for accessing the photo library;NSCameraUsageDescription=The reason for accessing the camera;CFBundleShortVersionString=1.0.0;NSPhotoLibraryAddUsageDescription=The reason for adding to the photo library;NSFaceIDUsageDescription=The reason for accessing the face id;NSLocationAlwaysAndWhenInUseUsageDescription=The reason for accessing the location information of the user;NSMicrophoneUsageDescription=The reason for accessing the microphone;NSSiriUsageDescription=The reason for accessing Siri $(BDS)\bin\Artwork\iOS\iPhone\FM_SpotlightSearchIcon_80x80.png iPhoneAndiPad $(BDS)\bin\Artwork\iOS\iPhone\FM_SpotlightSearchIcon_58x58.png @@ -238,7 +238,7 @@ $(BDS)\bin\Artwork\iOS\iPhone\FM_SpotlightSearchIcon_58x58.png $(BDS)\bin\Artwork\iOS\iPad\FM_ApplicationIcon_76x76.png $(BDS)\bin\Artwork\iOS\iPad\FM_LaunchImagePortrait_1536x2008.png - CFBundleName=$(MSBuildProjectName);CFBundleDevelopmentRegion=en;CFBundleDisplayName=$(MSBuildProjectName);CFBundleIdentifier=$(MSBuildProjectName);CFBundleInfoDictionaryVersion=7.1;CFBundleVersion=1.0.0.0;CFBundlePackageType=APPL;CFBundleSignature=????;LSRequiresIPhoneOS=true;CFBundleAllowMixedLocalizations=YES;CFBundleExecutable=$(MSBuildProjectName);UIDeviceFamily=iPhone & iPad;CFBundleResourceSpecification=ResourceRules.plist;NSLocationAlwaysUsageDescription=The reason for accessing the location information of the user;NSLocationWhenInUseUsageDescription=The reason for accessing the location information of the user;FMLocalNotificationPermission=false;UIBackgroundModes=;NSContactsUsageDescription=The reason for accessing the contacts;NSPhotoLibraryUsageDescription=The reason for accessing the photo library;NSCameraUsageDescription=The reason for accessing the camera;CFBundleShortVersionString=1.0.0 + CFBundleName=$(MSBuildProjectName);CFBundleDevelopmentRegion=en;CFBundleDisplayName=$(MSBuildProjectName);CFBundleIdentifier=$(MSBuildProjectName);CFBundleInfoDictionaryVersion=7.1;CFBundleVersion=1.0.0.0;CFBundlePackageType=APPL;CFBundleSignature=????;LSRequiresIPhoneOS=true;CFBundleAllowMixedLocalizations=YES;CFBundleExecutable=$(MSBuildProjectName);UIDeviceFamily=iPhone & iPad;CFBundleResourceSpecification=ResourceRules.plist;NSLocationAlwaysUsageDescription=The reason for accessing the location information of the user;NSLocationWhenInUseUsageDescription=The reason for accessing the location information of the user;FMLocalNotificationPermission=false;UIBackgroundModes=;NSContactsUsageDescription=The reason for accessing the contacts;NSPhotoLibraryUsageDescription=The reason for accessing the photo library;NSCameraUsageDescription=The reason for accessing the camera;CFBundleShortVersionString=1.0.0;NSPhotoLibraryAddUsageDescription=The reason for adding to the photo library;NSFaceIDUsageDescription=The reason for accessing the face id;NSLocationAlwaysAndWhenInUseUsageDescription=The reason for accessing the location information of the user;NSMicrophoneUsageDescription=The reason for accessing the microphone;NSSiriUsageDescription=The reason for accessing Siri $(BDS)\bin\Artwork\iOS\iPhone\FM_ApplicationIcon_87x87.png $(BDS)\bin\Artwork\iOS\iPhone\FM_ApplicationIcon_120x120.png $(BDS)\bin\Artwork\iOS\iPhone\FM_ApplicationIcon_57x57.png @@ -260,7 +260,7 @@ /usr/X11/bin/xterm -e "%debuggee%" DBXSqliteDriver;RESTComponents;DataSnapServerMidas;DBXInterBaseDriver;emsclientfiredac;DataSnapFireDAC;tethering;FireDACMSSQLDriver;bindcompfmx;DBXOracleDriver;inetdb;FmxTeeUI;emsedge;FireDACIBDriver;fmx;fmxdae;FireDACDBXDriver;dbexpress;IndyCore;dsnap;DataSnapCommon;emsclient;FireDACCommon;RESTBackendComponents;soapserver;bindengine;DBXMySQLDriver;FireDACOracleDriver;CloudService;FireDACMySQLDriver;DBXFirebirdDriver;FireDACCommonODBC;FireDACCommonDriver;DataSnapClient;inet;bindcompdbx;IndyIPCommon;IndyIPServer;IndySystem;fmxFireDAC;FireDAC;emshosting;FireDACSqliteDriver;FireDACPgDriver;ibmonitor;FireDACASADriver;FireDACTDataDriver;FMXTee;soaprtl;DbxCommonDriver;ibxpress;DataSnapServer;xmlrtl;soapmidas;DataSnapNativeClient;fmxobj;ibxbindings;rtl;FireDACDSDriver;DbxClientDriver;DBXSybaseASADriver;CustomIPTransport;bindcomp;DBXInformixDriver;IndyIPClient;dbxcds;FireDACODBCDriver;DataSnapIndy10ServerTransport;dsnapxml;DataSnapProviderClient;dbrtl;inetdbxpress;FireDACMongoDBDriver;IndyProtocols;fmxase;$(DCC_UsePackage) true - CFBundleName=$(MSBuildProjectName);CFBundleDisplayName=$(MSBuildProjectName);CFBundleIdentifier=$(MSBuildProjectName);CFBundleVersion=1.0.0;CFBundlePackageType=APPL;CFBundleSignature=????;CFBundleAllowMixedLocalizations=YES;CFBundleExecutable=$(MSBuildProjectName);NSHighResolutionCapable=true;LSApplicationCategoryType=public.app-category.utilities;NSLocationAlwaysUsageDescription=The reason for accessing the location information of the user;NSLocationWhenInUseUsageDescription=The reason for accessing the location information of the user;NSContactsUsageDescription=The reason for accessing the contacts;CFBundleShortVersionString=1.0.0 + CFBundleName=$(MSBuildProjectName);CFBundleDisplayName=$(MSBuildProjectName);CFBundleIdentifier=$(MSBuildProjectName);CFBundleVersion=1.0.0;CFBundlePackageType=APPL;CFBundleSignature=????;CFBundleAllowMixedLocalizations=YES;CFBundleExecutable=$(MSBuildProjectName);NSHighResolutionCapable=true;LSApplicationCategoryType=public.app-category.utilities;NSLocationAlwaysUsageDescription=The reason for accessing the location information of the user;NSLocationWhenInUseUsageDescription=The reason for accessing the location information of the user;NSContactsUsageDescription=The reason for accessing the contacts;CFBundleShortVersionString=1.0.0;NSLocationAlwaysAndWhenInUseUsageDescription=The reason for accessing the location information of the user Debug @@ -301,12 +301,12 @@ true 1033 true - true false + PerMonitor true - true + PerMonitor false @@ -316,11 +316,11 @@ true - true + PerMonitor true - true + PerMonitor @@ -365,15 +365,9 @@ true - - - true - - - - - Assets\ - Logo150x150.png + + + JsonDataObjectsFMX.exe true @@ -382,25 +376,27 @@ true - - - JsonDataObjectsFMX.exe + + + JsonDataObjectsFMX.icns true - + true - - + + + Info.plist true - + - JsonDataObjectsFMX.icns + Assets\ + Logo150x150.png true @@ -410,17 +406,31 @@ true - + - Info.plist true - + + + true + + + true + + + true + + + + + true + + Contents\MacOS\ @@ -437,7 +447,6 @@ 1 - Contents\MacOS 0 @@ -447,6 +456,12 @@ 1 + + + res\xml + 1 + + library\lib\armeabi-v7a @@ -483,6 +498,12 @@ 1 + + + res\values-v21 + 1 + + res\drawable @@ -561,6 +582,11 @@ 1 .framework + + Contents\MacOS + 1 + .framework + 0 @@ -583,6 +609,11 @@ 1 .dylib + + Contents\MacOS + 1 + .dylib + 0 .dll;.bpl @@ -606,6 +637,11 @@ 1 .dylib + + Contents\MacOS + 1 + .dylib + 0 .bpl @@ -628,6 +664,10 @@ Contents\Resources\StartUp\ 0 + + Contents\Resources\StartUp\ + 0 + 0 @@ -764,23 +804,41 @@ 1 + + + ..\$(PROJECTNAME).app.dSYM\Contents\Resources\DWARF + 1 + + ..\ 1 + + ..\ + 1 + Contents 1 + + Contents + 1 + Contents\Resources 1 + + Contents\Resources + 1 + @@ -803,6 +861,10 @@ Contents\MacOS 1 + + Contents\MacOS + 1 + 0 @@ -842,6 +904,7 @@ + diff --git a/Demos/JsonDataObjects/JsonDataObjectsISAPI.dproj b/Demos/JsonDataObjects/JsonDataObjectsISAPI.dproj index f695c824..1b275ffd 100644 --- a/Demos/JsonDataObjects/JsonDataObjectsISAPI.dproj +++ b/Demos/JsonDataObjects/JsonDataObjectsISAPI.dproj @@ -1,7 +1,7 @@  {7F50F5E9-ED2D-4A28-9C9E-10FAA1B36C4D} - 18.4 + 18.5 VCL JsonDataObjectsISAPI.dpr True @@ -134,7 +134,6 @@ 1 - Contents\MacOS 0 @@ -144,6 +143,12 @@ 1 + + + res\xml + 1 + + library\lib\armeabi-v7a @@ -180,6 +185,12 @@ 1 + + + res\values-v21 + 1 + + res\drawable @@ -256,6 +267,10 @@ 1 .framework + + 1 + .framework + 0 @@ -265,6 +280,10 @@ 1 .dylib + + 1 + .dylib + 0 .dll;.bpl @@ -287,6 +306,10 @@ 1 .dylib + + 1 + .dylib + 0 .bpl @@ -308,6 +331,9 @@ 0 + + 0 + 0 @@ -418,6 +444,7 @@ 1 + @@ -425,6 +452,10 @@ Contents\Resources 1 + + Contents\Resources + 1 + @@ -446,6 +477,9 @@ 1 + + 1 + 0 @@ -485,6 +519,7 @@ + diff --git a/Demos/JsonDataObjects/JsonDataObjectsVCL.dproj b/Demos/JsonDataObjects/JsonDataObjectsVCL.dproj index 01c56804..77c18856 100644 --- a/Demos/JsonDataObjects/JsonDataObjectsVCL.dproj +++ b/Demos/JsonDataObjects/JsonDataObjectsVCL.dproj @@ -7,7 +7,7 @@ Application VCL DCC32 - 18.4 + 18.5 1 Win32 @@ -168,7 +168,6 @@ 1 - Contents\MacOS 0 @@ -178,6 +177,12 @@ 1 + + + res\xml + 1 + + library\lib\armeabi-v7a @@ -214,6 +219,12 @@ 1 + + + res\values-v21 + 1 + + res\drawable @@ -290,6 +301,10 @@ 1 .framework + + 1 + .framework + 0 @@ -299,6 +314,10 @@ 1 .dylib + + 1 + .dylib + 0 .dll;.bpl @@ -321,6 +340,10 @@ 1 .dylib + + 1 + .dylib + 0 .bpl @@ -342,6 +365,9 @@ 0 + + 0 + 0 @@ -452,6 +478,7 @@ 1 + @@ -459,6 +486,10 @@ Contents\Resources 1 + + Contents\Resources + 1 + @@ -480,6 +511,9 @@ 1 + + 1 + 0 @@ -519,6 +553,7 @@ + diff --git a/Demos/JsonDataObjects/JsonDataObjectsVCL.res b/Demos/JsonDataObjects/JsonDataObjectsVCL.res index da24bfd7..aff75aeb 100644 Binary files a/Demos/JsonDataObjects/JsonDataObjectsVCL.res and b/Demos/JsonDataObjects/JsonDataObjectsVCL.res differ diff --git a/Demos/JsonDataObjects/JsonDataObjectsWinSvc.dproj b/Demos/JsonDataObjects/JsonDataObjectsWinSvc.dproj index a80b91d8..c84b3c58 100644 --- a/Demos/JsonDataObjects/JsonDataObjectsWinSvc.dproj +++ b/Demos/JsonDataObjects/JsonDataObjectsWinSvc.dproj @@ -1,7 +1,7 @@  {F72901B8-3DC2-48DB-9F80-EE7BA471758C} - 18.4 + 18.5 VCL JsonDataObjectsWinSvc.dpr True @@ -82,10 +82,10 @@ true - true 1033 true false + PerMonitor false @@ -94,8 +94,8 @@ 0 - true true + PerMonitor @@ -154,7 +154,6 @@ 1 - Contents\MacOS 0 @@ -164,6 +163,12 @@ 1 + + + res\xml + 1 + + library\lib\armeabi-v7a @@ -200,6 +205,12 @@ 1 + + + res\values-v21 + 1 + + res\drawable @@ -278,6 +289,11 @@ 1 .framework + + Contents\MacOS + 1 + .framework + 0 @@ -300,6 +316,11 @@ 1 .dylib + + Contents\MacOS + 1 + .dylib + 0 .dll;.bpl @@ -323,6 +344,11 @@ 1 .dylib + + Contents\MacOS + 1 + .dylib + 0 .bpl @@ -345,6 +371,10 @@ Contents\Resources\StartUp\ 0 + + Contents\Resources\StartUp\ + 0 + 0 @@ -481,23 +511,41 @@ 1 + + + ..\$(PROJECTNAME).app.dSYM\Contents\Resources\DWARF + 1 + + ..\ 1 + + ..\ + 1 + Contents 1 + + Contents + 1 + Contents\Resources 1 + + Contents\Resources + 1 + @@ -520,6 +568,10 @@ Contents\MacOS 1 + + Contents\MacOS + 1 + 0 @@ -559,6 +611,7 @@ + diff --git a/Demos/JsonDataObjects/Server.Resources.pas b/Demos/JsonDataObjects/Server.Resources.pas index 031a7b6d..ad0a2eda 100644 --- a/Demos/JsonDataObjects/Server.Resources.pas +++ b/Demos/JsonDataObjects/Server.Resources.pas @@ -10,15 +10,14 @@ interface uses SysUtils, Classes - , MARS.Core.Attributes - , MARS.Core.MediaType - , MARS.Core.Response +, MARS.Core.Attributes +, MARS.Core.MediaType +, MARS.Core.Response - , MARS.Core.Token.Resource +, MARS.Core.Token.Resource - , JsonDataObjects - , MARS.JsonDataObjects.ReadersAndWriters - ; +, JsonDataObjects, MARS.JsonDataObjects.ReadersAndWriters +; type [Path('helloworld')] diff --git a/Demos/MARSTemplate/MARSTemplateClient.dproj b/Demos/MARSTemplate/MARSTemplateClient.dproj index 583fbb0b..252a6005 100755 --- a/Demos/MARSTemplate/MARSTemplateClient.dproj +++ b/Demos/MARSTemplate/MARSTemplateClient.dproj @@ -4,10 +4,10 @@ MARSTemplateClient.dpr True Debug - 1169 + 33809 Application FMX - 18.5 + 19.0 Win32 @@ -18,8 +18,8 @@ Base true - - true + + true Base true @@ -33,13 +33,8 @@ Base true - - true - Base - true - - - true + + true Base true @@ -69,12 +64,6 @@ Base true - - true - Cfg_2 - true - true - true Cfg_2 @@ -87,8 +76,8 @@ true true - - true + + true Cfg_2 true true @@ -140,32 +129,40 @@ true android-support-v4.dex.jar;cloud-messaging.dex.jar;fmx.dex.jar;google-analytics-v2.dex.jar;google-play-billing.dex.jar;google-play-licensing.dex.jar;google-play-services.dex.jar 1 + $(BDS)\bin\Artwork\Android\FM_NotificationIcon_24x24.png + $(BDS)\bin\Artwork\Android\FM_NotificationIcon_36x36.png + $(BDS)\bin\Artwork\Android\FM_NotificationIcon_48x48.png + $(BDS)\bin\Artwork\Android\FM_NotificationIcon_72x72.png + $(BDS)\bin\Artwork\Android\FM_NotificationIcon_96x96.png - - CFBundleName=$(MSBuildProjectName);CFBundleDevelopmentRegion=en;CFBundleDisplayName=$(MSBuildProjectName);CFBundleIdentifier=$(MSBuildProjectName);CFBundleInfoDictionaryVersion=7.1;CFBundleVersion=1.0.0.0;CFBundlePackageType=APPL;CFBundleSignature=????;LSRequiresIPhoneOS=true;CFBundleAllowMixedLocalizations=YES;CFBundleExecutable=$(MSBuildProjectName);UIDeviceFamily=iPhone & iPad;CFBundleResourceSpecification=ResourceRules.plist;NSLocationAlwaysUsageDescription=The reason for accessing the location information of the user;NSLocationWhenInUseUsageDescription=The reason for accessing the location information of the user;FMLocalNotificationPermission=false;UIBackgroundModes=;NSContactsUsageDescription=The reason for accessing the contacts;NSPhotoLibraryUsageDescription=The reason for accessing the photo library;NSCameraUsageDescription=The reason for accessing the camera - iPhoneAndiPad - true + + package=com.embarcadero.$(MSBuildProjectName);label=$(MSBuildProjectName);versionCode=1;versionName=1.0.0;persistent=False;restoreAnyVersion=False;installLocation=auto;largeHeap=False;theme=TitleBar;hardwareAccelerated=true;apiKey= Debug - $(MSBuildProjectName) - $(BDS)\bin\Artwork\iOS\iPhone\FM_ApplicationIcon_60x60.png - $(BDS)\bin\Artwork\iOS\iPhone\FM_ApplicationIcon_120x120.png - $(BDS)\bin\Artwork\iOS\iPhone\FM_SpotlightSearchIcon_40x40.png - $(BDS)\bin\Artwork\iOS\iPhone\FM_SpotlightSearchIcon_80x80.png - $(BDS)\bin\Artwork\iOS\iPad\FM_SpotlightSearchIcon_40x40.png - $(BDS)\bin\Artwork\iOS\iPad\FM_SpotlightSearchIcon_80x80.png - $(BDS)\bin\Artwork\iOS\iPad\FM_ApplicationIcon_76x76.png - $(BDS)\bin\Artwork\iOS\iPad\FM_ApplicationIcon_152x152.png - $(BDS)\bin\Artwork\iOS\iPad\FM_LaunchImagePortrait_768x1024.png - $(BDS)\bin\Artwork\iOS\iPad\FM_LaunchImageLandscape_1024x768.png - $(BDS)\bin\Artwork\iOS\iPad\FM_LaunchImagePortrait_1536x2048.png - $(BDS)\bin\Artwork\iOS\iPad\FM_LaunchImageLandscape_2048x1536.png - $(BDS)\bin\Artwork\iOS\iPhone\FM_ApplicationIcon_87x87.png - $(BDS)\bin\Artwork\iOS\iPhone\FM_ApplicationIcon_180x180.png - $(BDS)\bin\Artwork\iOS\iPhone\FM_LaunchImage_750x1334.png - $(BDS)\bin\Artwork\iOS\iPhone\FM_LaunchImage_1242x2208.png - $(BDS)\bin\Artwork\iOS\iPhone\FM_LaunchImage_2208x1242.png - $(BDS)\bin\Artwork\iOS\iPhone\FM_LaunchImage_1125x2436.png - $(BDS)\bin\Artwork\iOS\iPhone\FM_LaunchImage_2436x1125.png + true + true + Base + true + $(BDS)\bin\Artwork\Android\FM_LauncherIcon_36x36.png + $(BDS)\bin\Artwork\Android\FM_LauncherIcon_48x48.png + $(BDS)\bin\Artwork\Android\FM_LauncherIcon_72x72.png + $(BDS)\bin\Artwork\Android\FM_LauncherIcon_96x96.png + $(BDS)\bin\Artwork\Android\FM_LauncherIcon_144x144.png + $(BDS)\bin\Artwork\Android\FM_SplashImage_426x320.png + $(BDS)\bin\Artwork\Android\FM_SplashImage_470x320.png + $(BDS)\bin\Artwork\Android\FM_SplashImage_640x480.png + $(BDS)\bin\Artwork\Android\FM_SplashImage_960x720.png + true + true + true + true + true + true + true + true + true + true + android-support-v4.dex.jar;cloud-messaging.dex.jar;fmx.dex.jar;google-analytics-v2.dex.jar;google-play-billing.dex.jar;google-play-licensing.dex.jar;google-play-services.dex.jar + 1 $(BDS)\bin\Artwork\iOS\iPhone\FM_ApplicationIcon_87x87.png @@ -175,6 +172,26 @@ $(BDS)\bin\Artwork\iOS\iPhone\FM_LaunchImage_2208x1242.png $(BDS)\bin\Artwork\iOS\iPhone\FM_LaunchImage_1125x2436.png $(BDS)\bin\Artwork\iOS\iPhone\FM_LaunchImage_2436x1125.png + $(BDS)\bin\Artwork\iOS\iPhone\FM_SpotlightSearchIcon_120x120.png + $(BDS)\bin\Artwork\iOS\iPhone\FM_LaunchImage_828x1792.png + $(BDS)\bin\Artwork\iOS\iPhone\FM_LaunchImage_1136x640.png + $(BDS)\bin\Artwork\iOS\iPhone\FM_LaunchImage_1242x2688.png + $(BDS)\bin\Artwork\iOS\iPhone\FM_LaunchImage_1334x750.png + $(BDS)\bin\Artwork\iOS\iPhone\FM_LaunchImage_1792x828.png + $(BDS)\bin\Artwork\iOS\iPhone\FM_LaunchImage_2688x1242.png + $(BDS)\bin\Artwork\iOS\iPad\FM_ApplicationIcon_167x167.png + $(BDS)\bin\Artwork\iOS\iPad\FM_LaunchImagePortrait_1668x2224.png + $(BDS)\bin\Artwork\iOS\iPad\FM_LaunchImagePortrait_1668x2388.png + $(BDS)\bin\Artwork\iOS\iPad\FM_LaunchImagePortrait_2048x2732.png + $(BDS)\bin\Artwork\iOS\iPad\FM_LaunchImageLandscape_2224x1668.png + $(BDS)\bin\Artwork\iOS\iPad\FM_LaunchImageLandscape_2388x1668.png + $(BDS)\bin\Artwork\iOS\iPad\FM_LaunchImageLandscape_2732x2048.png + $(BDS)\bin\Artwork\iOS\iPhone\FM_LaunchImage_2x.png + $(BDS)\bin\Artwork\iOS\iPhone\FM_LaunchImageDark_2x.png + $(BDS)\bin\Artwork\iOS\iPhone\FM_LaunchImage_3x.png + $(BDS)\bin\Artwork\iOS\iPhone\FM_LaunchImageDark_3x.png + $(BDS)\bin\Artwork\iOS\iPad\FM_LaunchImage_2x.png + $(BDS)\bin\Artwork\iOS\iPad\FM_LaunchImageDark_2x.png CFBundleName=$(MSBuildProjectName);CFBundleDevelopmentRegion=en;CFBundleDisplayName=$(MSBuildProjectName);CFBundleIdentifier=$(MSBuildProjectName);CFBundleInfoDictionaryVersion=7.1;CFBundleVersion=1.0.0.0;CFBundlePackageType=APPL;CFBundleSignature=????;LSRequiresIPhoneOS=true;CFBundleAllowMixedLocalizations=YES;CFBundleExecutable=$(MSBuildProjectName);UIDeviceFamily=iPhone & iPad;CFBundleResourceSpecification=ResourceRules.plist;NSLocationAlwaysUsageDescription=The reason for accessing the location information of the user;NSLocationWhenInUseUsageDescription=The reason for accessing the location information of the user;FMLocalNotificationPermission=false;UIBackgroundModes=;NSContactsUsageDescription=The reason for accessing the contacts;NSPhotoLibraryUsageDescription=The reason for accessing the photo library;NSCameraUsageDescription=The reason for accessing the camera @@ -199,13 +216,28 @@ $(BDS)\bin\Artwork\iOS\iPhone\FM_LaunchImage_2208x1242.png $(BDS)\bin\Artwork\iOS\iPhone\FM_LaunchImage_1125x2436.png $(BDS)\bin\Artwork\iOS\iPhone\FM_LaunchImage_2436x1125.png + $(BDS)\bin\Artwork\iOS\iPhone\FM_SpotlightSearchIcon_120x120.png + $(BDS)\bin\Artwork\iOS\iPhone\FM_LaunchImage_828x1792.png + $(BDS)\bin\Artwork\iOS\iPhone\FM_LaunchImage_1136x640.png + $(BDS)\bin\Artwork\iOS\iPhone\FM_LaunchImage_1242x2688.png + $(BDS)\bin\Artwork\iOS\iPhone\FM_LaunchImage_1334x750.png + $(BDS)\bin\Artwork\iOS\iPhone\FM_LaunchImage_1792x828.png + $(BDS)\bin\Artwork\iOS\iPhone\FM_LaunchImage_2688x1242.png + $(BDS)\bin\Artwork\iOS\iPad\FM_ApplicationIcon_167x167.png + $(BDS)\bin\Artwork\iOS\iPad\FM_LaunchImagePortrait_1668x2224.png + $(BDS)\bin\Artwork\iOS\iPad\FM_LaunchImagePortrait_1668x2388.png + $(BDS)\bin\Artwork\iOS\iPad\FM_LaunchImagePortrait_2048x2732.png + $(BDS)\bin\Artwork\iOS\iPad\FM_LaunchImageLandscape_2224x1668.png + $(BDS)\bin\Artwork\iOS\iPad\FM_LaunchImageLandscape_2388x1668.png + $(BDS)\bin\Artwork\iOS\iPad\FM_LaunchImageLandscape_2732x2048.png - - /usr/bin/xterm -e "%debuggee%" - (None) - - - $(BDS)\bin\delphi_PROJECTICNS.icns + + CFBundleName=$(MSBuildProjectName);CFBundleDisplayName=$(MSBuildProjectName);CFBundleIdentifier=$(MSBuildProjectName);CFBundleVersion=1.0.0;CFBundleShortVersionString=1.0.0;CFBundlePackageType=APPL;CFBundleSignature=????;CFBundleAllowMixedLocalizations=YES;CFBundleExecutable=$(MSBuildProjectName);NSHighResolutionCapable=true;LSApplicationCategoryType=public.app-category.utilities;NSLocationUsageDescription=The reason for accessing the location information of the user;NSContactsUsageDescription=The reason for accessing the contacts + Debug + true + true + Base + true Winapi;System.Win;Data.Win;Datasnap.Win;Web.Win;Soap.Win;Xml.Win;Bde;$(DCC_Namespace) @@ -240,17 +272,17 @@ false true - - true - Debug true - - $(BDS)\bin\delphi_PROJECTICNS.icns + + true + true + Cfg_2 + true true @@ -298,11 +330,10 @@ True - False + True True False - True - False + False True False @@ -336,11 +367,22 @@ true + + + true + + true + + + splash_image.png + true + + splash_image.png @@ -364,17 +406,6 @@ true - - - splash_image.png - true - - - - - true - - true @@ -452,12 +483,20 @@ classes 1 + + classes + 1 + res\xml 1 + + res\xml + 1 + @@ -470,96 +509,242 @@ library\lib\armeabi 1 + + library\lib\armeabi + 1 + + + + + library\lib\armeabi-v7a + 1 + library\lib\mips 1 + + library\lib\mips + 1 + library\lib\armeabi-v7a 1 + + library\lib\arm64-v8a + 1 + + + + + library\lib\armeabi-v7a + 1 + res\drawable 1 + + res\drawable + 1 + res\values 1 + + res\values + 1 + res\values-v21 1 + + res\values-v21 + 1 + + + + + res\values + 1 + + + res\values + 1 + res\drawable 1 + + res\drawable + 1 + res\drawable-xxhdpi 1 + + res\drawable-xxhdpi + 1 + res\drawable-ldpi 1 + + res\drawable-ldpi + 1 + res\drawable-mdpi 1 + + res\drawable-mdpi + 1 + res\drawable-hdpi 1 + + res\drawable-hdpi + 1 + res\drawable-xhdpi 1 + + res\drawable-xhdpi + 1 + + + + + res\drawable-mdpi + 1 + + + res\drawable-mdpi + 1 + + + + + res\drawable-hdpi + 1 + + + res\drawable-hdpi + 1 + + + + + res\drawable-xhdpi + 1 + + + res\drawable-xhdpi + 1 + + + + + res\drawable-xxhdpi + 1 + + + res\drawable-xxhdpi + 1 + + + + + res\drawable-xxxhdpi + 1 + + + res\drawable-xxxhdpi + 1 + res\drawable-small 1 + + res\drawable-small + 1 + res\drawable-normal 1 + + res\drawable-normal + 1 + res\drawable-large 1 + + res\drawable-large + 1 + res\drawable-xlarge 1 + + res\drawable-xlarge + 1 + + + + + res\values + 1 + + + res\values + 1 + @@ -648,6 +833,9 @@ 0 + + 0 + 0 @@ -680,6 +868,17 @@ 1 + + + 1 + + + 1 + + + 1 + + 1 @@ -691,6 +890,39 @@ 1 + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + 1 @@ -702,6 +934,71 @@ 1 + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + ..\$(PROJECTNAME).launchscreen\Assets\LaunchScreenImage.imageset + 1 + + + ..\$(PROJECTNAME).launchscreen\Assets\LaunchScreenImage.imageset + 1 + + 1 @@ -713,6 +1010,136 @@ 1 + + + 1 + + + 1 + + + 1 + + + + + ..\$(PROJECTNAME).launchscreen\Assets\LaunchScreenImage.imageset + 1 + + + ..\$(PROJECTNAME).launchscreen\Assets\LaunchScreenImage.imageset + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + ..\$(PROJECTNAME).launchscreen\Assets\LaunchScreenImage.imageset + 1 + + + ..\$(PROJECTNAME).launchscreen\Assets\LaunchScreenImage.imageset + 1 + + 1 @@ -724,6 +1151,16 @@ 1 + + + ..\$(PROJECTNAME).launchscreen\Assets\LaunchScreenImage.imageset + 1 + + + ..\$(PROJECTNAME).launchscreen\Assets\LaunchScreenImage.imageset + 1 + + 1 @@ -746,10 +1183,55 @@ 1 + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + ..\$(PROJECTNAME).launchscreen\Assets\LaunchScreenImage.imageset + 1 + + + ..\$(PROJECTNAME).launchscreen\Assets\LaunchScreenImage.imageset + 1 + + + + + ..\$(PROJECTNAME).launchscreen\Assets\LaunchScreenImage.imageset + 1 + + + ..\$(PROJECTNAME).launchscreen\Assets\LaunchScreenImage.imageset + 1 + + 1 + + 1 + @@ -790,6 +1272,16 @@ 1 + + + ..\$(PROJECTNAME).launchscreen + 64 + + + ..\$(PROJECTNAME).launchscreen + 64 + + 1 @@ -842,6 +1334,10 @@ library\lib\armeabi-v7a 1 + + library\lib\arm64-v8a + 1 + 1 @@ -866,6 +1362,12 @@ 0 + + + library\lib\armeabi-v7a + 1 + + 1 @@ -903,6 +1405,7 @@ + 12 diff --git a/Demos/MARSTemplate/MARSTemplateServerApacheModule.dproj b/Demos/MARSTemplate/MARSTemplateServerApacheModule.dproj index a0905e5f..13717a78 100644 --- a/Demos/MARSTemplate/MARSTemplateServerApacheModule.dproj +++ b/Demos/MARSTemplate/MARSTemplateServerApacheModule.dproj @@ -1,7 +1,7 @@  {0D982E91-6C92-4321-9078-458448B01536} - 18.5 + 19.0 None MARSTemplateServerApacheModule.dpr True @@ -189,12 +189,20 @@ classes 1 + + classes + 1 + res\xml 1 + + res\xml + 1 + @@ -207,96 +215,242 @@ library\lib\armeabi 1 + + library\lib\armeabi + 1 + + + + + library\lib\armeabi-v7a + 1 + library\lib\mips 1 + + library\lib\mips + 1 + library\lib\armeabi-v7a 1 + + library\lib\arm64-v8a + 1 + + + + + library\lib\armeabi-v7a + 1 + res\drawable 1 + + res\drawable + 1 + res\values 1 + + res\values + 1 + res\values-v21 1 + + res\values-v21 + 1 + + + + + res\values + 1 + + + res\values + 1 + res\drawable 1 + + res\drawable + 1 + res\drawable-xxhdpi 1 + + res\drawable-xxhdpi + 1 + res\drawable-ldpi 1 + + res\drawable-ldpi + 1 + res\drawable-mdpi 1 + + res\drawable-mdpi + 1 + res\drawable-hdpi 1 + + res\drawable-hdpi + 1 + res\drawable-xhdpi 1 + + res\drawable-xhdpi + 1 + + + + + res\drawable-mdpi + 1 + + + res\drawable-mdpi + 1 + + + + + res\drawable-hdpi + 1 + + + res\drawable-hdpi + 1 + + + + + res\drawable-xhdpi + 1 + + + res\drawable-xhdpi + 1 + + + + + res\drawable-xxhdpi + 1 + + + res\drawable-xxhdpi + 1 + + + + + res\drawable-xxxhdpi + 1 + + + res\drawable-xxxhdpi + 1 + res\drawable-small 1 + + res\drawable-small + 1 + res\drawable-normal 1 + + res\drawable-normal + 1 + res\drawable-large 1 + + res\drawable-large + 1 + res\drawable-xlarge 1 + + res\drawable-xlarge + 1 + + + + + res\values + 1 + + + res\values + 1 + @@ -366,6 +520,9 @@ 0 + + 0 + 0 @@ -396,6 +553,17 @@ 1 + + + 1 + + + 1 + + + 1 + + 1 @@ -407,6 +575,39 @@ 1 + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + 1 @@ -418,6 +619,71 @@ 1 + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + ..\$(PROJECTNAME).launchscreen\Assets\LaunchScreenImage.imageset + 1 + + + ..\$(PROJECTNAME).launchscreen\Assets\LaunchScreenImage.imageset + 1 + + 1 @@ -429,6 +695,136 @@ 1 + + + 1 + + + 1 + + + 1 + + + + + ..\$(PROJECTNAME).launchscreen\Assets\LaunchScreenImage.imageset + 1 + + + ..\$(PROJECTNAME).launchscreen\Assets\LaunchScreenImage.imageset + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + ..\$(PROJECTNAME).launchscreen\Assets\LaunchScreenImage.imageset + 1 + + + ..\$(PROJECTNAME).launchscreen\Assets\LaunchScreenImage.imageset + 1 + + 1 @@ -440,6 +836,16 @@ 1 + + + ..\$(PROJECTNAME).launchscreen\Assets\LaunchScreenImage.imageset + 1 + + + ..\$(PROJECTNAME).launchscreen\Assets\LaunchScreenImage.imageset + 1 + + 1 @@ -462,10 +868,55 @@ 1 + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + ..\$(PROJECTNAME).launchscreen\Assets\LaunchScreenImage.imageset + 1 + + + ..\$(PROJECTNAME).launchscreen\Assets\LaunchScreenImage.imageset + 1 + + + + + ..\$(PROJECTNAME).launchscreen\Assets\LaunchScreenImage.imageset + 1 + + + ..\$(PROJECTNAME).launchscreen\Assets\LaunchScreenImage.imageset + 1 + + 1 + + 1 + @@ -480,6 +931,7 @@ + 1 @@ -509,6 +961,10 @@ library\lib\armeabi-v7a 1 + + library\lib\arm64-v8a + 1 + 1 @@ -531,6 +987,12 @@ 0 + + + library\lib\armeabi-v7a + 1 + + 1 @@ -568,6 +1030,7 @@ + True diff --git a/Demos/MARSTemplate/MARSTemplateServerApplication.dproj b/Demos/MARSTemplate/MARSTemplateServerApplication.dproj index 4e051b4d..c9ef6f5b 100644 --- a/Demos/MARSTemplate/MARSTemplateServerApplication.dproj +++ b/Demos/MARSTemplate/MARSTemplateServerApplication.dproj @@ -7,7 +7,7 @@ 3 Application VCL - 18.5 + 19.0 Win32 diff --git a/Demos/MARSTemplate/MARSTemplateServerConsoleApplication.dproj b/Demos/MARSTemplate/MARSTemplateServerConsoleApplication.dproj index 4903d93a..33380dbd 100644 --- a/Demos/MARSTemplate/MARSTemplateServerConsoleApplication.dproj +++ b/Demos/MARSTemplate/MARSTemplateServerConsoleApplication.dproj @@ -4,10 +4,10 @@ MARSTemplateServerConsoleApplication.dpr True Release - 133 + 4225 Console None - 18.5 + 19.0 Win32 @@ -18,8 +18,8 @@ Base true - - true + + true Base true @@ -33,8 +33,8 @@ Base true - - true + + true Base true @@ -70,12 +70,6 @@ Base true - - true - Cfg_2 - true - true - true Cfg_2 @@ -94,8 +88,8 @@ true true - - true + + true Cfg_2 true true @@ -145,25 +139,38 @@ true true android-support-v4.dex.jar;cloud-messaging.dex.jar;fmx.dex.jar;google-analytics-v2.dex.jar;google-play-billing.dex.jar;google-play-licensing.dex.jar;google-play-services.dex.jar + $(BDS)\bin\Artwork\Android\FM_NotificationIcon_24x24.png + $(BDS)\bin\Artwork\Android\FM_NotificationIcon_36x36.png + $(BDS)\bin\Artwork\Android\FM_NotificationIcon_48x48.png + $(BDS)\bin\Artwork\Android\FM_NotificationIcon_72x72.png + $(BDS)\bin\Artwork\Android\FM_NotificationIcon_96x96.png - - CFBundleName=$(MSBuildProjectName);CFBundleDevelopmentRegion=en;CFBundleDisplayName=$(MSBuildProjectName);CFBundleIdentifier=$(MSBuildProjectName);CFBundleInfoDictionaryVersion=7.1;CFBundleVersion=1.0.0.0;CFBundlePackageType=APPL;CFBundleSignature=????;LSRequiresIPhoneOS=true;CFBundleAllowMixedLocalizations=YES;CFBundleExecutable=$(MSBuildProjectName);UIDeviceFamily=iPhone & iPad;CFBundleResourceSpecification=ResourceRules.plist;NSLocationAlwaysUsageDescription=The reason for accessing the location information of the user;NSLocationWhenInUseUsageDescription=The reason for accessing the location information of the user;FMLocalNotificationPermission=false;UIBackgroundModes=;NSContactsUsageDescription=The reason for accessing the contacts;NSPhotoLibraryUsageDescription=The reason for accessing the photo library;NSCameraUsageDescription=The reason for accessing the camera - iPhoneAndiPad - true + + package=com.embarcadero.$(MSBuildProjectName);label=$(MSBuildProjectName);versionCode=1;versionName=1.0.0;persistent=False;restoreAnyVersion=False;installLocation=auto;largeHeap=False;theme=TitleBar;hardwareAccelerated=true;apiKey= Debug - $(MSBuildProjectName) - $(BDS)\bin\Artwork\iOS\iPhone\FM_ApplicationIcon_60x60.png - $(BDS)\bin\Artwork\iOS\iPhone\FM_ApplicationIcon_120x120.png - $(BDS)\bin\Artwork\iOS\iPhone\FM_SpotlightSearchIcon_40x40.png - $(BDS)\bin\Artwork\iOS\iPhone\FM_SpotlightSearchIcon_80x80.png - $(BDS)\bin\Artwork\iOS\iPad\FM_SpotlightSearchIcon_40x40.png - $(BDS)\bin\Artwork\iOS\iPad\FM_SpotlightSearchIcon_80x80.png - $(BDS)\bin\Artwork\iOS\iPad\FM_ApplicationIcon_76x76.png - $(BDS)\bin\Artwork\iOS\iPad\FM_ApplicationIcon_152x152.png - $(BDS)\bin\Artwork\iOS\iPad\FM_LaunchImagePortrait_768x1024.png - $(BDS)\bin\Artwork\iOS\iPad\FM_LaunchImageLandscape_1024x768.png - $(BDS)\bin\Artwork\iOS\iPad\FM_LaunchImagePortrait_1536x2048.png - $(BDS)\bin\Artwork\iOS\iPad\FM_LaunchImageLandscape_2048x1536.png + true + Base + true + $(BDS)\bin\Artwork\Android\FM_LauncherIcon_36x36.png + $(BDS)\bin\Artwork\Android\FM_LauncherIcon_48x48.png + $(BDS)\bin\Artwork\Android\FM_LauncherIcon_72x72.png + $(BDS)\bin\Artwork\Android\FM_LauncherIcon_96x96.png + $(BDS)\bin\Artwork\Android\FM_LauncherIcon_144x144.png + $(BDS)\bin\Artwork\Android\FM_SplashImage_426x320.png + $(BDS)\bin\Artwork\Android\FM_SplashImage_470x320.png + $(BDS)\bin\Artwork\Android\FM_SplashImage_640x480.png + $(BDS)\bin\Artwork\Android\FM_SplashImage_960x720.png + true + true + true + true + true + true + true + true + true + true + android-support-v4.dex.jar;cloud-messaging.dex.jar;fmx.dex.jar;google-analytics-v2.dex.jar;google-play-billing.dex.jar;google-play-licensing.dex.jar;google-play-services.dex.jar CFBundleName=$(MSBuildProjectName);CFBundleDevelopmentRegion=en;CFBundleDisplayName=$(MSBuildProjectName);CFBundleIdentifier=$(MSBuildProjectName);CFBundleInfoDictionaryVersion=7.1;CFBundleVersion=1.0.0.0;CFBundlePackageType=APPL;CFBundleSignature=????;LSRequiresIPhoneOS=true;CFBundleAllowMixedLocalizations=YES;CFBundleExecutable=$(MSBuildProjectName);UIDeviceFamily=iPhone & iPad;CFBundleResourceSpecification=ResourceRules.plist;NSLocationAlwaysUsageDescription=The reason for accessing the location information of the user;NSLocationWhenInUseUsageDescription=The reason for accessing the location information of the user;FMLocalNotificationPermission=false;UIBackgroundModes=;NSContactsUsageDescription=The reason for accessing the contacts;NSPhotoLibraryUsageDescription=The reason for accessing the photo library;NSCameraUsageDescription=The reason for accessing the camera @@ -187,9 +194,12 @@ (None) MARSTemplateServer_Icon.ico - - CFBundleName=$(MSBuildProjectName);CFBundleDisplayName=$(MSBuildProjectName);CFBundleIdentifier=$(MSBuildProjectName);CFBundleVersion=1.0.0;CFBundleShortVersionString=1.0.0;CFBundlePackageType=APPL;CFBundleSignature=????;CFBundleAllowMixedLocalizations=YES;CFBundleExecutable=$(MSBuildProjectName);NSHighResolutionCapable=true;LSApplicationCategoryType=public.app-category.utilities;NSLocationAlwaysUsageDescription=The reason for accessing the location information of the user;NSLocationWhenInUseUsageDescription=The reason for accessing the location information of the user;NSContactsUsageDescription=The reason for accessing the contacts;NSLocationAlwaysAndWhenInUseUsageDescription=The reason for accessing the location information of the user + + CFBundleName=$(MSBuildProjectName);CFBundleDisplayName=$(MSBuildProjectName);CFBundleIdentifier=$(MSBuildProjectName);CFBundleVersion=1.0.0;CFBundleShortVersionString=1.0.0;CFBundlePackageType=APPL;CFBundleSignature=????;CFBundleAllowMixedLocalizations=YES;CFBundleExecutable=$(MSBuildProjectName);NSHighResolutionCapable=true;LSApplicationCategoryType=public.app-category.utilities;NSContactsUsageDescription=The reason for accessing the contacts;NSLocationUsageDescription=The reason for accessing the location information of the user;NSCalendarsUsageDescription=The reason for accessing the calendar data;NSRemindersUsageDescription=The reason for accessing the reminders;NSCameraUsageDescription=The reason for accessing the camera;NSMicrophoneUsageDescription=The reason for accessing the microphone;NSMotionUsageDescription=The reason for accessing the accelerometer;NSDesktopFolderUsageDescription=The reason for accessing the Desktop folder;NSDocumentsFolderUsageDescription=The reason for accessing the Documents folder;NSDownloadsFolderUsageDescription=The reason for accessing the Downloads folder;NSNetworkVolumesUsageDescription=The reason for accessing files on a network volume;NSRemovableVolumesUsageDescription=The reason for accessing files on a removable volume;NSSpeechRecognitionUsageDescription=The reason for requesting to send user data to Apple's speech recognition servers Debug + true + Base + true /usr/X11/bin/xterm -e "%debuggee%" (None) @@ -224,9 +234,6 @@ false true - - true - Debug @@ -236,8 +243,11 @@ MARSTemplateServer_Icon.ico - + true + true + Cfg_2 + true 1033 @@ -283,11 +293,11 @@ False - False + False False False True - True + True True False @@ -355,12 +365,20 @@ classes 1 + + classes + 1 + res\xml 1 + + res\xml + 1 + @@ -373,96 +391,242 @@ library\lib\armeabi 1 + + library\lib\armeabi + 1 + + + + + library\lib\armeabi-v7a + 1 + library\lib\mips 1 + + library\lib\mips + 1 + library\lib\armeabi-v7a 1 + + library\lib\arm64-v8a + 1 + + + + + library\lib\armeabi-v7a + 1 + res\drawable 1 + + res\drawable + 1 + res\values 1 + + res\values + 1 + res\values-v21 1 + + res\values-v21 + 1 + + + + + res\values + 1 + + + res\values + 1 + res\drawable 1 + + res\drawable + 1 + res\drawable-xxhdpi 1 + + res\drawable-xxhdpi + 1 + res\drawable-ldpi 1 + + res\drawable-ldpi + 1 + res\drawable-mdpi 1 + + res\drawable-mdpi + 1 + res\drawable-hdpi 1 + + res\drawable-hdpi + 1 + res\drawable-xhdpi 1 + + res\drawable-xhdpi + 1 + + + + + res\drawable-mdpi + 1 + + + res\drawable-mdpi + 1 + + + + + res\drawable-hdpi + 1 + + + res\drawable-hdpi + 1 + + + + + res\drawable-xhdpi + 1 + + + res\drawable-xhdpi + 1 + + + + + res\drawable-xxhdpi + 1 + + + res\drawable-xxhdpi + 1 + + + + + res\drawable-xxxhdpi + 1 + + + res\drawable-xxxhdpi + 1 + res\drawable-small 1 + + res\drawable-small + 1 + res\drawable-normal 1 + + res\drawable-normal + 1 + res\drawable-large 1 + + res\drawable-large + 1 + res\drawable-xlarge 1 + + res\drawable-xlarge + 1 + + + + + res\values + 1 + + + res\values + 1 + @@ -532,6 +696,9 @@ 0 + + 0 + 0 @@ -562,6 +729,17 @@ 1 + + + 1 + + + 1 + + + 1 + + 1 @@ -573,6 +751,39 @@ 1 + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + 1 @@ -584,6 +795,71 @@ 1 + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + ..\$(PROJECTNAME).launchscreen\Assets\LaunchScreenImage.imageset + 1 + + + ..\$(PROJECTNAME).launchscreen\Assets\LaunchScreenImage.imageset + 1 + + 1 @@ -595,6 +871,136 @@ 1 + + + 1 + + + 1 + + + 1 + + + + + ..\$(PROJECTNAME).launchscreen\Assets\LaunchScreenImage.imageset + 1 + + + ..\$(PROJECTNAME).launchscreen\Assets\LaunchScreenImage.imageset + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + ..\$(PROJECTNAME).launchscreen\Assets\LaunchScreenImage.imageset + 1 + + + ..\$(PROJECTNAME).launchscreen\Assets\LaunchScreenImage.imageset + 1 + + 1 @@ -606,6 +1012,16 @@ 1 + + + ..\$(PROJECTNAME).launchscreen\Assets\LaunchScreenImage.imageset + 1 + + + ..\$(PROJECTNAME).launchscreen\Assets\LaunchScreenImage.imageset + 1 + + 1 @@ -628,10 +1044,55 @@ 1 + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + ..\$(PROJECTNAME).launchscreen\Assets\LaunchScreenImage.imageset + 1 + + + ..\$(PROJECTNAME).launchscreen\Assets\LaunchScreenImage.imageset + 1 + + + + + ..\$(PROJECTNAME).launchscreen\Assets\LaunchScreenImage.imageset + 1 + + + ..\$(PROJECTNAME).launchscreen\Assets\LaunchScreenImage.imageset + 1 + + 1 + + 1 + @@ -646,6 +1107,7 @@ + 1 @@ -675,6 +1137,10 @@ library\lib\armeabi-v7a 1 + + library\lib\arm64-v8a + 1 + 1 @@ -697,6 +1163,12 @@ 0 + + + library\lib\armeabi-v7a + 1 + + 1 @@ -734,6 +1206,7 @@ + 12 diff --git a/Demos/MARSTemplate/MARSTemplateServerDaemon.dproj b/Demos/MARSTemplate/MARSTemplateServerDaemon.dproj index 27e6a8a7..522b0dba 100644 --- a/Demos/MARSTemplate/MARSTemplateServerDaemon.dproj +++ b/Demos/MARSTemplate/MARSTemplateServerDaemon.dproj @@ -1,7 +1,7 @@  {9D225C2C-24C2-48B4-8ABE-52C04AF576AE} - 18.5 + 19.0 None MARSTemplateServerDaemon.dpr True @@ -18,8 +18,8 @@ Base true - - true + + true Base true @@ -38,8 +38,8 @@ Base true - - true + + true Base true @@ -99,9 +99,29 @@ $(BDS)\bin\Artwork\Android\FM_SplashImage_640x480.png $(BDS)\bin\Artwork\Android\FM_SplashImage_960x720.png android-support-v4.dex.jar;cloud-messaging.dex.jar;fmx.dex.jar;google-analytics-v2.dex.jar;google-play-billing.dex.jar;google-play-licensing.dex.jar;google-play-services.dex.jar + $(BDS)\bin\Artwork\Android\FM_NotificationIcon_24x24.png + $(BDS)\bin\Artwork\Android\FM_NotificationIcon_36x36.png + $(BDS)\bin\Artwork\Android\FM_NotificationIcon_48x48.png + $(BDS)\bin\Artwork\Android\FM_NotificationIcon_72x72.png + $(BDS)\bin\Artwork\Android\FM_NotificationIcon_96x96.png - - DBXSqliteDriver;DBXInterBaseDriver;tethering;bindcompfmx;FmxTeeUI;fmx;RadiantShapesFmx;FireDACDBXDriver;dbexpress;IndyCore;dsnap;DataSnapCommon;bindengine;DataSnapClient;bindcompdbx;IndyIPCommon;IndyIPServer;IndySystem;fmxFireDAC;ibmonitor;FMXTee;DbxCommonDriver;ibxpress;xmlrtl;DataSnapNativeClient;ibxbindings;rtl;FireDACDSDriver;DbxClientDriver;CustomIPTransport;bindcomp;IndyIPClient;dbxcds;dsnapxml;DataSnapProviderClient;dbrtl;IndyProtocols;fmxase;$(DCC_UsePackage) + + package=com.embarcadero.$(MSBuildProjectName);label=$(MSBuildProjectName);versionCode=1;versionName=1.0.0;persistent=False;restoreAnyVersion=False;installLocation=auto;largeHeap=False;theme=TitleBar;hardwareAccelerated=true;apiKey= + Debug + true + Base + true + DBXSqliteDriver;DBXInterBaseDriver;tethering;bindcompfmx;FmxTeeUI;fmx;RadiantShapesFmx;FireDACDBXDriver;dbexpress;IndyCore;dsnap;DataSnapCommon;bindengine;DataSnapClient;bindcompdbx;IndyIPCommon;IndyIPServer;IndySystem;fmxFireDAC;ibmonitor;FMXTee;DbxCommonDriver;ibxpress;xmlrtl;DataSnapNativeClient;ibxbindings;rtl;FireDACDSDriver;DbxClientDriver;CustomIPTransport;bindcomp;IndyIPClient;dbxcds;dsnapxml;DataSnapProviderClient;dbrtl;IndyProtocols;$(DCC_UsePackage);$(DCC_UsePackage) + $(BDS)\bin\Artwork\Android\FM_LauncherIcon_36x36.png + $(BDS)\bin\Artwork\Android\FM_LauncherIcon_48x48.png + $(BDS)\bin\Artwork\Android\FM_LauncherIcon_72x72.png + $(BDS)\bin\Artwork\Android\FM_LauncherIcon_96x96.png + $(BDS)\bin\Artwork\Android\FM_LauncherIcon_144x144.png + $(BDS)\bin\Artwork\Android\FM_SplashImage_426x320.png + $(BDS)\bin\Artwork\Android\FM_SplashImage_470x320.png + $(BDS)\bin\Artwork\Android\FM_SplashImage_640x480.png + $(BDS)\bin\Artwork\Android\FM_SplashImage_960x720.png + android-support-v4.dex.jar;cloud-messaging.dex.jar;fmx.dex.jar;google-analytics-v2.dex.jar;google-play-billing.dex.jar;google-play-licensing.dex.jar;google-play-services.dex.jar DBXSqliteDriver;DBXInterBaseDriver;tethering;bindcompfmx;FmxTeeUI;fmx;RadiantShapesFmx;FireDACDBXDriver;dbexpress;IndyCore;dsnap;DataSnapCommon;bindengine;DataSnapClient;bindcompdbx;IndyIPCommon;IndyIPServer;IndySystem;fmxFireDAC;ibmonitor;FMXTee;DbxCommonDriver;ibxpress;xmlrtl;DataSnapNativeClient;ibxbindings;rtl;FireDACDSDriver;DbxClientDriver;CustomIPTransport;bindcomp;IndyIPClient;dbxcds;dsnapxml;DataSnapProviderClient;dbrtl;IndyProtocols;fmxase;$(DCC_UsePackage) @@ -115,8 +135,13 @@ MARSTemplateServer_Icon.ico (None) - - DBXSqliteDriver;DataSnapServerMidas;DBXInterBaseDriver;tethering;FireDACMSSQLDriver;bindcompfmx;DBXOracleDriver;inetdb;FmxTeeUI;emsedge;fmx;fmxdae;RadiantShapesFmx;FireDACDBXDriver;dbexpress;IndyCore;dsnap;DataSnapCommon;bindengine;DBXMySQLDriver;FireDACOracleDriver;FireDACMySQLDriver;DBXFirebirdDriver;FireDACCommonODBC;DataSnapClient;bindcompdbx;IndyIPCommon;IndyIPServer;IndySystem;fmxFireDAC;emshosting;FireDACPgDriver;ibmonitor;FireDACASADriver;FireDACTDataDriver;FMXTee;DbxCommonDriver;ibxpress;DataSnapServer;xmlrtl;DataSnapNativeClient;fmxobj;ibxbindings;rtl;FireDACDSDriver;DbxClientDriver;DBXSybaseASADriver;CustomIPTransport;bindcomp;DBXInformixDriver;IndyIPClient;dbxcds;FireDACODBCDriver;DataSnapIndy10ServerTransport;dsnapxml;DataSnapProviderClient;dbrtl;inetdbxpress;FireDACMongoDBDriver;IndyProtocols;fmxase;$(DCC_UsePackage) + + CFBundleName=$(MSBuildProjectName);CFBundleDisplayName=$(MSBuildProjectName);CFBundleIdentifier=$(MSBuildProjectName);CFBundleVersion=1.0.0;CFBundleShortVersionString=1.0.0;CFBundlePackageType=APPL;CFBundleSignature=????;CFBundleAllowMixedLocalizations=YES;CFBundleExecutable=$(MSBuildProjectName);NSHighResolutionCapable=true;LSApplicationCategoryType=public.app-category.utilities;NSLocationUsageDescription=The reason for accessing the location information of the user;NSContactsUsageDescription=The reason for accessing the contacts + Debug + true + Base + true + DBXSqliteDriver;DataSnapServerMidas;DBXInterBaseDriver;tethering;FireDACMSSQLDriver;bindcompfmx;DBXOracleDriver;inetdb;FmxTeeUI;emsedge;fmx;fmxdae;RadiantShapesFmx;FireDACDBXDriver;dbexpress;IndyCore;dsnap;DataSnapCommon;bindengine;DBXMySQLDriver;FireDACOracleDriver;FireDACMySQLDriver;DBXFirebirdDriver;FireDACCommonODBC;DataSnapClient;bindcompdbx;IndyIPCommon;IndyIPServer;IndySystem;fmxFireDAC;emshosting;FireDACPgDriver;ibmonitor;FireDACASADriver;FireDACTDataDriver;FMXTee;DbxCommonDriver;ibxpress;DataSnapServer;xmlrtl;DataSnapNativeClient;fmxobj;ibxbindings;rtl;FireDACDSDriver;DbxClientDriver;DBXSybaseASADriver;CustomIPTransport;bindcomp;DBXInformixDriver;IndyIPClient;dbxcds;FireDACODBCDriver;DataSnapIndy10ServerTransport;dsnapxml;DataSnapProviderClient;dbrtl;inetdbxpress;FireDACMongoDBDriver;IndyProtocols;fmxase;$(DCC_UsePackage);$(DCC_UsePackage) true @@ -244,12 +269,20 @@ classes 1 + + classes + 1 + res\xml 1 + + res\xml + 1 + @@ -262,96 +295,242 @@ library\lib\armeabi 1 + + library\lib\armeabi + 1 + + + + + library\lib\armeabi-v7a + 1 + library\lib\mips 1 + + library\lib\mips + 1 + library\lib\armeabi-v7a 1 + + library\lib\arm64-v8a + 1 + + + + + library\lib\armeabi-v7a + 1 + res\drawable 1 + + res\drawable + 1 + res\values 1 + + res\values + 1 + res\values-v21 1 + + res\values-v21 + 1 + + + + + res\values + 1 + + + res\values + 1 + res\drawable 1 + + res\drawable + 1 + res\drawable-xxhdpi 1 + + res\drawable-xxhdpi + 1 + res\drawable-ldpi 1 + + res\drawable-ldpi + 1 + res\drawable-mdpi 1 + + res\drawable-mdpi + 1 + res\drawable-hdpi 1 + + res\drawable-hdpi + 1 + res\drawable-xhdpi 1 + + res\drawable-xhdpi + 1 + + + + + res\drawable-mdpi + 1 + + + res\drawable-mdpi + 1 + + + + + res\drawable-hdpi + 1 + + + res\drawable-hdpi + 1 + + + + + res\drawable-xhdpi + 1 + + + res\drawable-xhdpi + 1 + + + + + res\drawable-xxhdpi + 1 + + + res\drawable-xxhdpi + 1 + + + + + res\drawable-xxxhdpi + 1 + + + res\drawable-xxxhdpi + 1 + res\drawable-small 1 + + res\drawable-small + 1 + res\drawable-normal 1 + + res\drawable-normal + 1 + res\drawable-large 1 + + res\drawable-large + 1 + res\drawable-xlarge 1 + + res\drawable-xlarge + 1 + + + + + res\values + 1 + + + res\values + 1 + @@ -440,6 +619,9 @@ 0 + + 0 + 0 @@ -472,6 +654,17 @@ 1 + + + 1 + + + 1 + + + 1 + + 1 @@ -483,6 +676,39 @@ 1 + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + 1 @@ -494,6 +720,71 @@ 1 + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + ..\$(PROJECTNAME).launchscreen\Assets\LaunchScreenImage.imageset + 1 + + + ..\$(PROJECTNAME).launchscreen\Assets\LaunchScreenImage.imageset + 1 + + 1 @@ -505,6 +796,136 @@ 1 + + + 1 + + + 1 + + + 1 + + + + + ..\$(PROJECTNAME).launchscreen\Assets\LaunchScreenImage.imageset + 1 + + + ..\$(PROJECTNAME).launchscreen\Assets\LaunchScreenImage.imageset + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + ..\$(PROJECTNAME).launchscreen\Assets\LaunchScreenImage.imageset + 1 + + + ..\$(PROJECTNAME).launchscreen\Assets\LaunchScreenImage.imageset + 1 + + 1 @@ -516,6 +937,16 @@ 1 + + + ..\$(PROJECTNAME).launchscreen\Assets\LaunchScreenImage.imageset + 1 + + + ..\$(PROJECTNAME).launchscreen\Assets\LaunchScreenImage.imageset + 1 + + 1 @@ -538,10 +969,55 @@ 1 + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + ..\$(PROJECTNAME).launchscreen\Assets\LaunchScreenImage.imageset + 1 + + + ..\$(PROJECTNAME).launchscreen\Assets\LaunchScreenImage.imageset + 1 + + + + + ..\$(PROJECTNAME).launchscreen\Assets\LaunchScreenImage.imageset + 1 + + + ..\$(PROJECTNAME).launchscreen\Assets\LaunchScreenImage.imageset + 1 + + 1 + + 1 + @@ -582,6 +1058,16 @@ 1 + + + ..\$(PROJECTNAME).launchscreen + 64 + + + ..\$(PROJECTNAME).launchscreen + 64 + + 1 @@ -634,6 +1120,10 @@ library\lib\armeabi-v7a 1 + + library\lib\arm64-v8a + 1 + 1 @@ -658,6 +1148,12 @@ 0 + + + library\lib\armeabi-v7a + 1 + + 1 @@ -695,14 +1191,15 @@ + False - False + False False False True - False + False False False diff --git a/Demos/MARSTemplate/MARSTemplateServerFMXApplication.dproj b/Demos/MARSTemplate/MARSTemplateServerFMXApplication.dproj index f603e0be..5de257d5 100644 --- a/Demos/MARSTemplate/MARSTemplateServerFMXApplication.dproj +++ b/Demos/MARSTemplate/MARSTemplateServerFMXApplication.dproj @@ -4,10 +4,10 @@ MARSTemplateServerFMXApplication.dpr True Release - 1173 + 37905 Application FMX - 18.5 + 19.0 Win32 @@ -18,8 +18,8 @@ Base true - - true + + true Base true @@ -33,13 +33,8 @@ Base true - - true - Base - true - - - true + + true Base true @@ -64,8 +59,8 @@ true true - - true + + true Cfg_1 true true @@ -81,12 +76,6 @@ Base true - - true - Cfg_2 - true - true - true Cfg_2 @@ -99,8 +88,8 @@ true true - - true + + true Cfg_2 true true @@ -152,32 +141,40 @@ true android-support-v4.dex.jar;cloud-messaging.dex.jar;fmx.dex.jar;google-analytics-v2.dex.jar;google-play-billing.dex.jar;google-play-licensing.dex.jar;google-play-services.dex.jar 1 + $(BDS)\bin\Artwork\Android\FM_NotificationIcon_24x24.png + $(BDS)\bin\Artwork\Android\FM_NotificationIcon_36x36.png + $(BDS)\bin\Artwork\Android\FM_NotificationIcon_48x48.png + $(BDS)\bin\Artwork\Android\FM_NotificationIcon_72x72.png + $(BDS)\bin\Artwork\Android\FM_NotificationIcon_96x96.png - - CFBundleName=$(MSBuildProjectName);CFBundleDevelopmentRegion=en;CFBundleDisplayName=$(MSBuildProjectName);CFBundleIdentifier=$(MSBuildProjectName);CFBundleInfoDictionaryVersion=7.1;CFBundleVersion=1.0.0.0;CFBundlePackageType=APPL;CFBundleSignature=????;LSRequiresIPhoneOS=true;CFBundleAllowMixedLocalizations=YES;CFBundleExecutable=$(MSBuildProjectName);UIDeviceFamily=iPhone & iPad;CFBundleResourceSpecification=ResourceRules.plist;NSLocationAlwaysUsageDescription=The reason for accessing the location information of the user;NSLocationWhenInUseUsageDescription=The reason for accessing the location information of the user;FMLocalNotificationPermission=false;UIBackgroundModes=;NSContactsUsageDescription=The reason for accessing the contacts;NSPhotoLibraryUsageDescription=The reason for accessing the photo library;NSCameraUsageDescription=The reason for accessing the camera - iPhoneAndiPad - true + + package=com.embarcadero.$(MSBuildProjectName);label=$(MSBuildProjectName);versionCode=1;versionName=1.0.0;persistent=False;restoreAnyVersion=False;installLocation=auto;largeHeap=False;theme=TitleBar;hardwareAccelerated=true;apiKey= Debug - $(MSBuildProjectName) - $(BDS)\bin\Artwork\iOS\iPhone\FM_ApplicationIcon_60x60.png - $(BDS)\bin\Artwork\iOS\iPhone\FM_ApplicationIcon_120x120.png - $(BDS)\bin\Artwork\iOS\iPhone\FM_SpotlightSearchIcon_40x40.png - $(BDS)\bin\Artwork\iOS\iPhone\FM_SpotlightSearchIcon_80x80.png - $(BDS)\bin\Artwork\iOS\iPad\FM_SpotlightSearchIcon_40x40.png - $(BDS)\bin\Artwork\iOS\iPad\FM_SpotlightSearchIcon_80x80.png - $(BDS)\bin\Artwork\iOS\iPad\FM_ApplicationIcon_76x76.png - $(BDS)\bin\Artwork\iOS\iPad\FM_ApplicationIcon_152x152.png - $(BDS)\bin\Artwork\iOS\iPad\FM_LaunchImagePortrait_768x1024.png - $(BDS)\bin\Artwork\iOS\iPad\FM_LaunchImageLandscape_1024x768.png - $(BDS)\bin\Artwork\iOS\iPad\FM_LaunchImagePortrait_1536x2048.png - $(BDS)\bin\Artwork\iOS\iPad\FM_LaunchImageLandscape_2048x1536.png - $(BDS)\bin\Artwork\iOS\iPhone\FM_ApplicationIcon_87x87.png - $(BDS)\bin\Artwork\iOS\iPhone\FM_ApplicationIcon_180x180.png - $(BDS)\bin\Artwork\iOS\iPhone\FM_LaunchImage_750x1334.png - $(BDS)\bin\Artwork\iOS\iPhone\FM_LaunchImage_1242x2208.png - $(BDS)\bin\Artwork\iOS\iPhone\FM_LaunchImage_2208x1242.png - $(BDS)\bin\Artwork\iOS\iPhone\FM_LaunchImage_1125x2436.png - $(BDS)\bin\Artwork\iOS\iPhone\FM_LaunchImage_2436x1125.png + true + true + Base + true + $(BDS)\bin\Artwork\Android\FM_LauncherIcon_36x36.png + $(BDS)\bin\Artwork\Android\FM_LauncherIcon_48x48.png + $(BDS)\bin\Artwork\Android\FM_LauncherIcon_72x72.png + $(BDS)\bin\Artwork\Android\FM_LauncherIcon_96x96.png + $(BDS)\bin\Artwork\Android\FM_LauncherIcon_144x144.png + $(BDS)\bin\Artwork\Android\FM_SplashImage_426x320.png + $(BDS)\bin\Artwork\Android\FM_SplashImage_470x320.png + $(BDS)\bin\Artwork\Android\FM_SplashImage_640x480.png + $(BDS)\bin\Artwork\Android\FM_SplashImage_960x720.png + true + true + true + true + true + true + true + true + true + true + android-support-v4.dex.jar;cloud-messaging.dex.jar;fmx.dex.jar;google-analytics-v2.dex.jar;google-play-billing.dex.jar;google-play-licensing.dex.jar;google-play-services.dex.jar + 1 $(BDS)\bin\Artwork\iOS\iPhone\FM_ApplicationIcon_87x87.png @@ -187,6 +184,26 @@ $(BDS)\bin\Artwork\iOS\iPhone\FM_LaunchImage_2208x1242.png $(BDS)\bin\Artwork\iOS\iPhone\FM_LaunchImage_1125x2436.png $(BDS)\bin\Artwork\iOS\iPhone\FM_LaunchImage_2436x1125.png + $(BDS)\bin\Artwork\iOS\iPhone\FM_SpotlightSearchIcon_120x120.png + $(BDS)\bin\Artwork\iOS\iPhone\FM_LaunchImage_828x1792.png + $(BDS)\bin\Artwork\iOS\iPhone\FM_LaunchImage_1136x640.png + $(BDS)\bin\Artwork\iOS\iPhone\FM_LaunchImage_1242x2688.png + $(BDS)\bin\Artwork\iOS\iPhone\FM_LaunchImage_1334x750.png + $(BDS)\bin\Artwork\iOS\iPhone\FM_LaunchImage_1792x828.png + $(BDS)\bin\Artwork\iOS\iPhone\FM_LaunchImage_2688x1242.png + $(BDS)\bin\Artwork\iOS\iPad\FM_ApplicationIcon_167x167.png + $(BDS)\bin\Artwork\iOS\iPad\FM_LaunchImagePortrait_1668x2224.png + $(BDS)\bin\Artwork\iOS\iPad\FM_LaunchImagePortrait_1668x2388.png + $(BDS)\bin\Artwork\iOS\iPad\FM_LaunchImagePortrait_2048x2732.png + $(BDS)\bin\Artwork\iOS\iPad\FM_LaunchImageLandscape_2224x1668.png + $(BDS)\bin\Artwork\iOS\iPad\FM_LaunchImageLandscape_2388x1668.png + $(BDS)\bin\Artwork\iOS\iPad\FM_LaunchImageLandscape_2732x2048.png + $(BDS)\bin\Artwork\iOS\iPhone\FM_LaunchImage_2x.png + $(BDS)\bin\Artwork\iOS\iPhone\FM_LaunchImageDark_2x.png + $(BDS)\bin\Artwork\iOS\iPhone\FM_LaunchImage_3x.png + $(BDS)\bin\Artwork\iOS\iPhone\FM_LaunchImageDark_3x.png + $(BDS)\bin\Artwork\iOS\iPad\FM_LaunchImage_2x.png + $(BDS)\bin\Artwork\iOS\iPad\FM_LaunchImageDark_2x.png CFBundleName=$(MSBuildProjectName);CFBundleDevelopmentRegion=en;CFBundleDisplayName=$(MSBuildProjectName);CFBundleIdentifier=$(MSBuildProjectName);CFBundleInfoDictionaryVersion=7.1;CFBundleVersion=1.0.0.0;CFBundlePackageType=APPL;CFBundleSignature=????;LSRequiresIPhoneOS=true;CFBundleAllowMixedLocalizations=YES;CFBundleExecutable=$(MSBuildProjectName);UIDeviceFamily=iPhone & iPad;CFBundleResourceSpecification=ResourceRules.plist;NSLocationAlwaysUsageDescription=The reason for accessing the location information of the user;NSLocationWhenInUseUsageDescription=The reason for accessing the location information of the user;FMLocalNotificationPermission=false;UIBackgroundModes=;NSContactsUsageDescription=The reason for accessing the contacts;NSPhotoLibraryUsageDescription=The reason for accessing the photo library;NSCameraUsageDescription=The reason for accessing the camera @@ -211,16 +228,28 @@ $(BDS)\bin\Artwork\iOS\iPhone\FM_LaunchImage_2208x1242.png $(BDS)\bin\Artwork\iOS\iPhone\FM_LaunchImage_1125x2436.png $(BDS)\bin\Artwork\iOS\iPhone\FM_LaunchImage_2436x1125.png + $(BDS)\bin\Artwork\iOS\iPhone\FM_SpotlightSearchIcon_120x120.png + $(BDS)\bin\Artwork\iOS\iPhone\FM_LaunchImage_828x1792.png + $(BDS)\bin\Artwork\iOS\iPhone\FM_LaunchImage_1136x640.png + $(BDS)\bin\Artwork\iOS\iPhone\FM_LaunchImage_1242x2688.png + $(BDS)\bin\Artwork\iOS\iPhone\FM_LaunchImage_1334x750.png + $(BDS)\bin\Artwork\iOS\iPhone\FM_LaunchImage_1792x828.png + $(BDS)\bin\Artwork\iOS\iPhone\FM_LaunchImage_2688x1242.png + $(BDS)\bin\Artwork\iOS\iPad\FM_ApplicationIcon_167x167.png + $(BDS)\bin\Artwork\iOS\iPad\FM_LaunchImagePortrait_1668x2224.png + $(BDS)\bin\Artwork\iOS\iPad\FM_LaunchImagePortrait_1668x2388.png + $(BDS)\bin\Artwork\iOS\iPad\FM_LaunchImagePortrait_2048x2732.png + $(BDS)\bin\Artwork\iOS\iPad\FM_LaunchImageLandscape_2224x1668.png + $(BDS)\bin\Artwork\iOS\iPad\FM_LaunchImageLandscape_2388x1668.png + $(BDS)\bin\Artwork\iOS\iPad\FM_LaunchImageLandscape_2732x2048.png - - /usr/bin/xterm -e "%debuggee%" - (None) - MARSTemplateServer_Icon.ico - - - CFBundleName=$(MSBuildProjectName);CFBundleDisplayName=$(MSBuildProjectName);CFBundleIdentifier=$(MSBuildProjectName);CFBundleVersion=1.0.0;CFBundlePackageType=APPL;CFBundleSignature=????;CFBundleAllowMixedLocalizations=YES;CFBundleExecutable=$(MSBuildProjectName);NSHighResolutionCapable=true;LSApplicationCategoryType=public.app-category.utilities;NSLocationAlwaysUsageDescription=The reason for accessing the location information of the user;NSLocationWhenInUseUsageDescription=The reason for accessing the location information of the user;NSContactsUsageDescription=The reason for accessing the contacts;CFBundleShortVersionString=1.0.0;NSLocationAlwaysAndWhenInUseUsageDescription=The reason for accessing the location information of the user + + CFBundleName=$(MSBuildProjectName);CFBundleDisplayName=$(MSBuildProjectName);CFBundleIdentifier=$(MSBuildProjectName);CFBundleVersion=1.0.0;CFBundlePackageType=APPL;CFBundleSignature=????;CFBundleAllowMixedLocalizations=YES;CFBundleExecutable=$(MSBuildProjectName);NSHighResolutionCapable=true;LSApplicationCategoryType=public.app-category.utilities;NSContactsUsageDescription=The reason for accessing the contacts;CFBundleShortVersionString=1.0.0;NSLocationUsageDescription=The reason for accessing the location information of the user;NSCalendarsUsageDescription=The reason for accessing the calendar data;NSRemindersUsageDescription=The reason for accessing the reminders;NSCameraUsageDescription=The reason for accessing the camera;NSMicrophoneUsageDescription=The reason for accessing the microphone;NSMotionUsageDescription=The reason for accessing the accelerometer;NSDesktopFolderUsageDescription=The reason for accessing the Desktop folder;NSDocumentsFolderUsageDescription=The reason for accessing the Documents folder;NSDownloadsFolderUsageDescription=The reason for accessing the Downloads folder;NSNetworkVolumesUsageDescription=The reason for accessing files on a network volume;NSRemovableVolumesUsageDescription=The reason for accessing files on a removable volume;NSSpeechRecognitionUsageDescription=The reason for requesting to send user data to Apple's speech recognition servers Debug true + true + Base + true /usr/X11/bin/xterm -e "%debuggee%" (None) MARSTemplateServer_Icon.ico @@ -253,9 +282,12 @@ Debug - + + true + Cfg_1 + true true - CFBundleName=$(MSBuildProjectName);CFBundleDisplayName=$(MSBuildProjectName);CFBundleIdentifier=$(MSBuildProjectName);CFBundleVersion=1.0.0;CFBundlePackageType=APPL;CFBundleSignature=????;CFBundleAllowMixedLocalizations=YES;CFBundleExecutable=$(MSBuildProjectName);NSHighResolutionCapable=true;LSApplicationCategoryType=public.app-category.utilities;NSLocationAlwaysUsageDescription=The reason for accessing the location information of the user;NSLocationWhenInUseUsageDescription=The reason for accessing the location information of the user;NSContactsUsageDescription=The reason for accessing the contacts;CFBundleShortVersionString=1.0.0;NSLocationAlwaysAndWhenInUseUsageDescription=The reason for accessing the location information of the user + CFBundleName=$(MSBuildProjectName);CFBundleDisplayName=$(MSBuildProjectName);CFBundleIdentifier=$(MSBuildProjectName);CFBundleVersion=1.0.0;CFBundlePackageType=APPL;CFBundleSignature=????;CFBundleAllowMixedLocalizations=YES;CFBundleExecutable=$(MSBuildProjectName);NSHighResolutionCapable=true;LSApplicationCategoryType=public.app-category.utilities;NSContactsUsageDescription=The reason for accessing the contacts;CFBundleShortVersionString=1.0.0;NSLocationUsageDescription=The reason for accessing the location information of the user;NSCalendarsUsageDescription=The reason for accessing the calendar data;NSRemindersUsageDescription=The reason for accessing the reminders;NSCameraUsageDescription=The reason for accessing the camera;NSMicrophoneUsageDescription=The reason for accessing the microphone;NSMotionUsageDescription=The reason for accessing the accelerometer;NSDesktopFolderUsageDescription=The reason for accessing the Desktop folder;NSDocumentsFolderUsageDescription=The reason for accessing the Documents folder;NSDownloadsFolderUsageDescription=The reason for accessing the Downloads folder;NSNetworkVolumesUsageDescription=The reason for accessing files on a network volume;NSRemovableVolumesUsageDescription=The reason for accessing files on a removable volume;NSSpeechRecognitionUsageDescription=The reason for requesting to send user data to Apple's speech recognition servers true @@ -270,18 +302,17 @@ false true - - true - Debug true - - $(BDS)\bin\delphi_PROJECTICNS.icns + true + true + Cfg_2 + true true @@ -327,11 +358,10 @@ True - False + True True False - True - True + True True False @@ -389,12 +419,20 @@ classes 1 + + classes + 1 + res\xml 1 + + res\xml + 1 + @@ -407,96 +445,242 @@ library\lib\armeabi 1 + + library\lib\armeabi + 1 + + + + + library\lib\armeabi-v7a + 1 + library\lib\mips 1 + + library\lib\mips + 1 + library\lib\armeabi-v7a 1 + + library\lib\arm64-v8a + 1 + + + + + library\lib\armeabi-v7a + 1 + res\drawable 1 + + res\drawable + 1 + res\values 1 + + res\values + 1 + res\values-v21 1 + + res\values-v21 + 1 + + + + + res\values + 1 + + + res\values + 1 + res\drawable 1 + + res\drawable + 1 + res\drawable-xxhdpi 1 + + res\drawable-xxhdpi + 1 + res\drawable-ldpi 1 + + res\drawable-ldpi + 1 + res\drawable-mdpi 1 + + res\drawable-mdpi + 1 + res\drawable-hdpi 1 + + res\drawable-hdpi + 1 + res\drawable-xhdpi 1 + + res\drawable-xhdpi + 1 + + + + + res\drawable-mdpi + 1 + + + res\drawable-mdpi + 1 + + + + + res\drawable-hdpi + 1 + + + res\drawable-hdpi + 1 + + + + + res\drawable-xhdpi + 1 + + + res\drawable-xhdpi + 1 + + + + + res\drawable-xxhdpi + 1 + + + res\drawable-xxhdpi + 1 + + + + + res\drawable-xxxhdpi + 1 + + + res\drawable-xxxhdpi + 1 + res\drawable-small 1 + + res\drawable-small + 1 + res\drawable-normal 1 + + res\drawable-normal + 1 + res\drawable-large 1 + + res\drawable-large + 1 + res\drawable-xlarge 1 + + res\drawable-xlarge + 1 + + + + + res\values + 1 + + + res\values + 1 + @@ -585,6 +769,9 @@ 0 + + 0 + 0 @@ -617,6 +804,17 @@ 1 + + + 1 + + + 1 + + + 1 + + 1 @@ -628,6 +826,39 @@ 1 + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + 1 @@ -639,6 +870,71 @@ 1 + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + ..\$(PROJECTNAME).launchscreen\Assets\LaunchScreenImage.imageset + 1 + + + ..\$(PROJECTNAME).launchscreen\Assets\LaunchScreenImage.imageset + 1 + + 1 @@ -650,6 +946,136 @@ 1 + + + 1 + + + 1 + + + 1 + + + + + ..\$(PROJECTNAME).launchscreen\Assets\LaunchScreenImage.imageset + 1 + + + ..\$(PROJECTNAME).launchscreen\Assets\LaunchScreenImage.imageset + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + ..\$(PROJECTNAME).launchscreen\Assets\LaunchScreenImage.imageset + 1 + + + ..\$(PROJECTNAME).launchscreen\Assets\LaunchScreenImage.imageset + 1 + + 1 @@ -661,6 +1087,16 @@ 1 + + + ..\$(PROJECTNAME).launchscreen\Assets\LaunchScreenImage.imageset + 1 + + + ..\$(PROJECTNAME).launchscreen\Assets\LaunchScreenImage.imageset + 1 + + 1 @@ -683,10 +1119,55 @@ 1 + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + ..\$(PROJECTNAME).launchscreen\Assets\LaunchScreenImage.imageset + 1 + + + ..\$(PROJECTNAME).launchscreen\Assets\LaunchScreenImage.imageset + 1 + + + + + ..\$(PROJECTNAME).launchscreen\Assets\LaunchScreenImage.imageset + 1 + + + ..\$(PROJECTNAME).launchscreen\Assets\LaunchScreenImage.imageset + 1 + + 1 + + 1 + @@ -727,6 +1208,16 @@ 1 + + + ..\$(PROJECTNAME).launchscreen + 64 + + + ..\$(PROJECTNAME).launchscreen + 64 + + 1 @@ -779,6 +1270,10 @@ library\lib\armeabi-v7a 1 + + library\lib\arm64-v8a + 1 + 1 @@ -803,6 +1298,12 @@ 0 + + + library\lib\armeabi-v7a + 1 + + 1 @@ -840,6 +1341,7 @@ + 12 diff --git a/Demos/MARSTemplate/MARSTemplateServerISAPI.dproj b/Demos/MARSTemplate/MARSTemplateServerISAPI.dproj index 20690e27..25db71ca 100644 --- a/Demos/MARSTemplate/MARSTemplateServerISAPI.dproj +++ b/Demos/MARSTemplate/MARSTemplateServerISAPI.dproj @@ -7,7 +7,7 @@ 1 Library None - 18.5 + 19.0 Win32 @@ -140,7 +140,6 @@ False False - False True False diff --git a/Demos/MARSTemplate/MARSTemplateServerService.dproj b/Demos/MARSTemplate/MARSTemplateServerService.dproj index 3a4cfef0..590f7458 100644 --- a/Demos/MARSTemplate/MARSTemplateServerService.dproj +++ b/Demos/MARSTemplate/MARSTemplateServerService.dproj @@ -7,7 +7,7 @@ 3 Application None - 18.5 + 19.0 Win32 @@ -192,7 +192,6 @@ False False - False True True @@ -272,12 +271,20 @@ classes 1 + + classes + 1 + res\xml 1 + + res\xml + 1 + @@ -290,96 +297,242 @@ library\lib\armeabi 1 + + library\lib\armeabi + 1 + + + + + library\lib\armeabi-v7a + 1 + library\lib\mips 1 + + library\lib\mips + 1 + library\lib\armeabi-v7a 1 + + library\lib\arm64-v8a + 1 + + + + + library\lib\armeabi-v7a + 1 + res\drawable 1 + + res\drawable + 1 + res\values 1 + + res\values + 1 + res\values-v21 1 + + res\values-v21 + 1 + + + + + res\values + 1 + + + res\values + 1 + res\drawable 1 + + res\drawable + 1 + res\drawable-xxhdpi 1 + + res\drawable-xxhdpi + 1 + res\drawable-ldpi 1 + + res\drawable-ldpi + 1 + res\drawable-mdpi 1 + + res\drawable-mdpi + 1 + res\drawable-hdpi 1 + + res\drawable-hdpi + 1 + res\drawable-xhdpi 1 + + res\drawable-xhdpi + 1 + + + + + res\drawable-mdpi + 1 + + + res\drawable-mdpi + 1 + + + + + res\drawable-hdpi + 1 + + + res\drawable-hdpi + 1 + + + + + res\drawable-xhdpi + 1 + + + res\drawable-xhdpi + 1 + + + + + res\drawable-xxhdpi + 1 + + + res\drawable-xxhdpi + 1 + + + + + res\drawable-xxxhdpi + 1 + + + res\drawable-xxxhdpi + 1 + res\drawable-small 1 + + res\drawable-small + 1 + res\drawable-normal 1 + + res\drawable-normal + 1 + res\drawable-large 1 + + res\drawable-large + 1 + res\drawable-xlarge 1 + + res\drawable-xlarge + 1 + + + + + res\values + 1 + + + res\values + 1 + @@ -468,6 +621,9 @@ 0 + + 0 + 0 @@ -500,6 +656,17 @@ 1 + + + 1 + + + 1 + + + 1 + + 1 @@ -511,6 +678,39 @@ 1 + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + 1 @@ -522,6 +722,71 @@ 1 + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + ..\$(PROJECTNAME).launchscreen\Assets\LaunchScreenImage.imageset + 1 + + + ..\$(PROJECTNAME).launchscreen\Assets\LaunchScreenImage.imageset + 1 + + 1 @@ -533,6 +798,136 @@ 1 + + + 1 + + + 1 + + + 1 + + + + + ..\$(PROJECTNAME).launchscreen\Assets\LaunchScreenImage.imageset + 1 + + + ..\$(PROJECTNAME).launchscreen\Assets\LaunchScreenImage.imageset + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + ..\$(PROJECTNAME).launchscreen\Assets\LaunchScreenImage.imageset + 1 + + + ..\$(PROJECTNAME).launchscreen\Assets\LaunchScreenImage.imageset + 1 + + 1 @@ -544,6 +939,16 @@ 1 + + + ..\$(PROJECTNAME).launchscreen\Assets\LaunchScreenImage.imageset + 1 + + + ..\$(PROJECTNAME).launchscreen\Assets\LaunchScreenImage.imageset + 1 + + 1 @@ -566,10 +971,55 @@ 1 + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + ..\$(PROJECTNAME).launchscreen\Assets\LaunchScreenImage.imageset + 1 + + + ..\$(PROJECTNAME).launchscreen\Assets\LaunchScreenImage.imageset + 1 + + + + + ..\$(PROJECTNAME).launchscreen\Assets\LaunchScreenImage.imageset + 1 + + + ..\$(PROJECTNAME).launchscreen\Assets\LaunchScreenImage.imageset + 1 + + 1 + + 1 + @@ -610,6 +1060,16 @@ 1 + + + ..\$(PROJECTNAME).launchscreen + 64 + + + ..\$(PROJECTNAME).launchscreen + 64 + + 1 @@ -662,6 +1122,10 @@ library\lib\armeabi-v7a 1 + + library\lib\arm64-v8a + 1 + 1 @@ -686,6 +1150,12 @@ 0 + + + library\lib\armeabi-v7a + 1 + + 1 @@ -723,6 +1193,7 @@ + 12 diff --git a/Demos/MARSTemplate/Server.Ignition.pas b/Demos/MARSTemplate/Server.Ignition.pas index 93fbdc6c..434b319e 100644 --- a/Demos/MARSTemplate/Server.Ignition.pas +++ b/Demos/MARSTemplate/Server.Ignition.pas @@ -10,9 +10,12 @@ interface uses - Classes, SysUtils, Rtti - , MARS.Core.Engine -; + System.Classes, + System.SysUtils, + System.RTTI, + System.ZLib, + System.StrUtils, + MARS.Core.Engine; type TServerEngine=class @@ -94,15 +97,28 @@ implementation *) {$ENDREGION} {$REGION 'Global AfterInvoke handler example'} -(* - // to execute something after each activation - TMARSActivation.RegisterAfterInvoke( - procedure (const AActivation: IMARSActivation) - begin - - end - ); -*) + // Compression + if FEngine.Parameters.ByName('Compression.Enabled').AsBoolean then + TMARSActivation.RegisterAfterInvoke( + procedure (const AActivation: IMARSActivation) + var + LOutputStream: TBytesStream; + begin + if ContainsText(AActivation.Request.GetHeaderParamValue('Accept-Encoding'), 'gzip') then + begin + LOutputStream := TBytesStream.Create(nil); + try + ZipStream(AActivation.Response.ContentStream, LOutputStream, 15 + 16); + AActivation.Response.ContentStream.Free; + AActivation.Response.ContentStream := LOutputStream; + AActivation.Response.ContentEncoding := 'gzip'; + except + LOutputStream.Free; + raise; + end; + end; + end + ); {$ENDREGION} {$REGION 'Global InvokeError handler example'} (* diff --git a/Demos/MARSTemplate/Server.Service.pas b/Demos/MARSTemplate/Server.Service.pas index f3882f4d..da27291a 100644 --- a/Demos/MARSTemplate/Server.Service.pas +++ b/Demos/MARSTemplate/Server.Service.pas @@ -79,6 +79,9 @@ procedure TServerService.ServiceCreate(Sender: TObject); var LScheduler: TIdSchedulerOfThreadPool; begin + Name := TServerEngine.Default.Parameters.ByNameText('ServiceName', Name).AsString; + DisplayName := TServerEngine.Default.Parameters.ByNameText('ServiceDisplayName', DisplayName).AsString; + if WebRequestHandler <> nil then WebRequestHandler.WebModuleClass := WebModuleClass; diff --git a/Demos/MARSTemplate/Server.WebModule.pas b/Demos/MARSTemplate/Server.WebModule.pas index db2377d5..72908095 100644 --- a/Demos/MARSTemplate/Server.WebModule.pas +++ b/Demos/MARSTemplate/Server.WebModule.pas @@ -31,14 +31,15 @@ implementation {$R *.dfm} uses - Server.Ignition; + MARS.http.Server.Indy +, Server.Ignition; procedure TServerWebModule.ServerWebModuleDefaultHandlerAction(Sender: TObject; Request: TWebRequest; Response: TWebResponse; var Handled: Boolean); begin inherited; - if not TServerEngine.Default.HandleRequest(Request, Response) then + if not TServerEngine.Default.HandleRequest(TMARSWebRequest.Create(Request), TMARSWebResponse.Create(Response)) then begin Response.ContentType := 'application/json'; Response.Content := diff --git a/Demos/MARSTemplate/bin/MARSTemplateServerApplication.ini b/Demos/MARSTemplate/bin/MARSTemplateServerApplication.ini index e098e5da..4398fa63 100644 --- a/Demos/MARSTemplate/bin/MARSTemplateServerApplication.ini +++ b/Demos/MARSTemplate/bin/MARSTemplateServerApplication.ini @@ -1,2 +1,3 @@ [DefaultEngine] -ThreadPoolSize=100 \ No newline at end of file +ThreadPoolSize=100 +;Compression.Enabled=True diff --git a/Demos/MARSTemplateDCS/FMXClient.DataModules.Main.dfm b/Demos/MARSTemplateDCS/FMXClient.DataModules.Main.dfm new file mode 100644 index 00000000..97328db3 --- /dev/null +++ b/Demos/MARSTemplateDCS/FMXClient.DataModules.Main.dfm @@ -0,0 +1,25 @@ +object MainDataModule: TMainDataModule + OldCreateOrder = False + Height = 411 + Width = 518 + object MARSApplication: TMARSClientApplication + DefaultMediaType = 'application/json' + DefaultContentType = 'application/json' + Client = MARSClient + Left = 88 + Top = 80 + end + object MARSClient: TMARSNetClient + MARSEngineURL = 'http://localhost:8080/rest' + ConnectTimeout = 60000 + ReadTimeout = 60000 + HttpClient.Asynchronous = False + HttpClient.ConnectionTimeout = 60000 + HttpClient.ResponseTimeout = 60000 + HttpClient.AllowCookies = True + HttpClient.HandleRedirects = True + HttpClient.UserAgent = 'Embarcadero URI Client/1.0' + Left = 88 + Top = 24 + end +end diff --git a/Demos/MARSTemplateDCS/FMXClient.DataModules.Main.pas b/Demos/MARSTemplateDCS/FMXClient.DataModules.Main.pas new file mode 100644 index 00000000..4b24449c --- /dev/null +++ b/Demos/MARSTemplateDCS/FMXClient.DataModules.Main.pas @@ -0,0 +1,32 @@ +(* + Copyright 2016, MARS-Curiosity - REST Library + + Home: https://github.com/andrea-magni/MARS +*) +unit FMXClient.DataModules.Main; + +interface + +uses + System.SysUtils, System.Classes, MARS.Client.Application, + MARS.Client.Client, MARS.Client.Client.Net +; + +type + TMainDataModule = class(TDataModule) + MARSApplication: TMARSClientApplication; + MARSClient: TMARSNetClient; + private + public + end; + +var + MainDataModule: TMainDataModule; + +implementation + +{%CLASSGROUP 'FMX.Controls.TControl'} + +{$R *.dfm} + +end. diff --git a/Demos/MARSTemplateDCS/FMXClient.DataModules.Main.vlb b/Demos/MARSTemplateDCS/FMXClient.DataModules.Main.vlb new file mode 100644 index 00000000..95e7a6a2 --- /dev/null +++ b/Demos/MARSTemplateDCS/FMXClient.DataModules.Main.vlb @@ -0,0 +1,10 @@ +[MARSApplication] +Coordinates=150,53,96,33 + +[] +Coordinates=71,70,69,33 +Visible=False + +[MainForm.BindSourceDB1] +Coordinates=0,0,144,267 + diff --git a/Demos/MARSTemplateDCS/FMXClient.Forms.Main.fmx b/Demos/MARSTemplateDCS/FMXClient.Forms.Main.fmx new file mode 100644 index 00000000..9990bf0e --- /dev/null +++ b/Demos/MARSTemplateDCS/FMXClient.Forms.Main.fmx @@ -0,0 +1,27 @@ +object MainForm: TMainForm + Left = 0 + Top = 0 + Caption = 'MARS Template Client' + ClientHeight = 480 + ClientWidth = 640 + FormFactor.Width = 320 + FormFactor.Height = 480 + FormFactor.Devices = [Desktop] + DesignerMasterStyle = 0 + object TopToolBar: TToolBar + Size.Width = 640.000000000000000000 + Size.Height = 48.000000000000000000 + Size.PlatformDefault = False + TabOrder = 0 + object TitleLabel: TLabel + Align = Center + AutoSize = True + Size.Width = 121.000000000000000000 + Size.Height = 16.000000000000000000 + Size.PlatformDefault = False + StyleLookup = 'toollabel' + TextSettings.WordWrap = False + Text = 'MARS Template Client' + end + end +end diff --git a/Demos/MARSTemplateDCS/FMXClient.Forms.Main.pas b/Demos/MARSTemplateDCS/FMXClient.Forms.Main.pas new file mode 100644 index 00000000..e56b7400 --- /dev/null +++ b/Demos/MARSTemplateDCS/FMXClient.Forms.Main.pas @@ -0,0 +1,34 @@ +(* + Copyright 2016, MARS-Curiosity - REST Library + + Home: https://github.com/andrea-magni/MARS +*) +unit FMXClient.Forms.Main; + +interface + +uses + System.SysUtils, System.Types, System.UITypes, System.Classes, System.Variants, + FMX.Types, FMX.Controls, FMX.Forms, FMX.Graphics, FMX.Dialogs, FMX.StdCtrls, + FMX.Layouts, FMX.Controls.Presentation; + +type + TMainForm = class(TForm) + TopToolBar: TToolBar; + TitleLabel: TLabel; + private + public + end; + +var + MainForm: TMainForm; + +implementation + +{$R *.fmx} + +uses + FMXClient.DataModules.Main + ; + +end. diff --git a/Demos/MARSTemplateDCS/FMXClient.Forms.Main.vlb b/Demos/MARSTemplateDCS/FMXClient.Forms.Main.vlb new file mode 100644 index 00000000..b439f67e --- /dev/null +++ b/Demos/MARSTemplateDCS/FMXClient.Forms.Main.vlb @@ -0,0 +1,26 @@ +[TopToolBar] +Coordinates=317,78,82,36 + +[MainDataModule.ItemsQueryDataSet] +Coordinates=10,10,228,212 + +[] +Coordinates=145,78,71,36 +Visible=True + +[TitleLabel] +Coordinates=236,78,71,58 + +[MainDataModule.] +Coordinates=472,428,215,249 +Visible=False + +[MainDataModule.EmployeeQuery1] +Visible=False + +[MainDataModule.EmployeeQueryDataSet] +Coordinates=100,10,253,58 + +[MainDataModule.CountryByName1] +Coordinates=10,78,216,58 + diff --git a/Demos/MARSTemplateDCS/MARSTemplateClientDCS.dpr b/Demos/MARSTemplateDCS/MARSTemplateClientDCS.dpr new file mode 100644 index 00000000..55e764b3 --- /dev/null +++ b/Demos/MARSTemplateDCS/MARSTemplateClientDCS.dpr @@ -0,0 +1,21 @@ +(* + Copyright 2016, MARS-Curiosity library + + Home: https://github.com/andrea-magni/MARS +*) + program MARSTemplateClientDCS; + +uses + System.StartUpCopy, + FMX.Forms, + FMXClient.Forms.Main in 'FMXClient.Forms.Main.pas' {MainForm}, + FMXClient.DataModules.Main in 'FMXClient.DataModules.Main.pas' {MainDataModule: TDataModule}; + +{$R *.res} + +begin + Application.Initialize; + Application.CreateForm(TMainDataModule, MainDataModule); + Application.CreateForm(TMainForm, MainForm); + Application.Run; +end. diff --git a/Demos/MARSTemplateDCS/MARSTemplateClientDCS.dproj b/Demos/MARSTemplateDCS/MARSTemplateClientDCS.dproj new file mode 100644 index 00000000..d2bb36e1 --- /dev/null +++ b/Demos/MARSTemplateDCS/MARSTemplateClientDCS.dproj @@ -0,0 +1,1423 @@ + + + {B6B860BA-E3A5-48E9-8EC6-7FE516AE7FC4} + MARSTemplateClientDCS.dpr + True + Debug + 33809 + Application + FMX + 19.0 + Win32 + + + true + + + true + Base + true + + + true + Base + true + + + true + Base + true + + + true + Base + true + + + true + Base + true + + + true + Base + true + + + true + Base + true + + + true + Base + true + + + true + Cfg_1 + true + true + + + true + Base + true + + + true + Cfg_2 + true + true + + + true + Cfg_2 + true + true + + + true + Cfg_2 + true + true + + + true + Cfg_2 + true + true + + + false + false + false + false + false + 00400000 + MARSTemplateClientDCS + 1040 + CompanyName=;FileDescription=;FileVersion=1.0.0.0;InternalName=;LegalCopyright=;LegalTrademarks=;OriginalFilename=;ProductName=;ProductVersion=1.0.0.0;Comments=;CFBundleName= + System;Xml;Data;Datasnap;Web;Soap;$(DCC_Namespace) + $(BDS)\bin\delphi_PROJECTICON.ico + $(BDS)\bin\delphi_PROJECTICNS.icns + .\bin + .\lib\$(Platform)\$(Config) + + + package=com.embarcadero.$(MSBuildProjectName);label=$(MSBuildProjectName);versionCode=1;versionName=1.0.0;persistent=False;restoreAnyVersion=False;installLocation=auto;largeHeap=False;theme=TitleBar;hardwareAccelerated=true;apiKey= + Debug + true + $(BDS)\bin\Artwork\Android\FM_LauncherIcon_36x36.png + $(BDS)\bin\Artwork\Android\FM_LauncherIcon_48x48.png + $(BDS)\bin\Artwork\Android\FM_LauncherIcon_72x72.png + $(BDS)\bin\Artwork\Android\FM_LauncherIcon_96x96.png + $(BDS)\bin\Artwork\Android\FM_LauncherIcon_144x144.png + $(BDS)\bin\Artwork\Android\FM_SplashImage_426x320.png + $(BDS)\bin\Artwork\Android\FM_SplashImage_470x320.png + $(BDS)\bin\Artwork\Android\FM_SplashImage_640x480.png + $(BDS)\bin\Artwork\Android\FM_SplashImage_960x720.png + true + true + true + true + true + true + true + true + true + true + android-support-v4.dex.jar;cloud-messaging.dex.jar;fmx.dex.jar;google-analytics-v2.dex.jar;google-play-billing.dex.jar;google-play-licensing.dex.jar;google-play-services.dex.jar + 1 + $(BDS)\bin\Artwork\Android\FM_NotificationIcon_24x24.png + $(BDS)\bin\Artwork\Android\FM_NotificationIcon_36x36.png + $(BDS)\bin\Artwork\Android\FM_NotificationIcon_48x48.png + $(BDS)\bin\Artwork\Android\FM_NotificationIcon_72x72.png + $(BDS)\bin\Artwork\Android\FM_NotificationIcon_96x96.png + + + package=com.embarcadero.$(MSBuildProjectName);label=$(MSBuildProjectName);versionCode=1;versionName=1.0.0;persistent=False;restoreAnyVersion=False;installLocation=auto;largeHeap=False;theme=TitleBar;hardwareAccelerated=true;apiKey= + Debug + true + true + Base + true + $(BDS)\bin\Artwork\Android\FM_LauncherIcon_36x36.png + $(BDS)\bin\Artwork\Android\FM_LauncherIcon_48x48.png + $(BDS)\bin\Artwork\Android\FM_LauncherIcon_72x72.png + $(BDS)\bin\Artwork\Android\FM_LauncherIcon_96x96.png + $(BDS)\bin\Artwork\Android\FM_LauncherIcon_144x144.png + $(BDS)\bin\Artwork\Android\FM_SplashImage_426x320.png + $(BDS)\bin\Artwork\Android\FM_SplashImage_470x320.png + $(BDS)\bin\Artwork\Android\FM_SplashImage_640x480.png + $(BDS)\bin\Artwork\Android\FM_SplashImage_960x720.png + true + true + true + true + true + true + true + true + true + true + android-support-v4.dex.jar;cloud-messaging.dex.jar;fmx.dex.jar;google-analytics-v2.dex.jar;google-play-billing.dex.jar;google-play-licensing.dex.jar;google-play-services.dex.jar + 1 + + + $(BDS)\bin\Artwork\iOS\iPhone\FM_ApplicationIcon_87x87.png + $(BDS)\bin\Artwork\iOS\iPhone\FM_ApplicationIcon_180x180.png + $(BDS)\bin\Artwork\iOS\iPhone\FM_LaunchImage_750x1334.png + $(BDS)\bin\Artwork\iOS\iPhone\FM_LaunchImage_1242x2208.png + $(BDS)\bin\Artwork\iOS\iPhone\FM_LaunchImage_2208x1242.png + $(BDS)\bin\Artwork\iOS\iPhone\FM_LaunchImage_1125x2436.png + $(BDS)\bin\Artwork\iOS\iPhone\FM_LaunchImage_2436x1125.png + $(BDS)\bin\Artwork\iOS\iPhone\FM_SpotlightSearchIcon_120x120.png + $(BDS)\bin\Artwork\iOS\iPhone\FM_LaunchImage_828x1792.png + $(BDS)\bin\Artwork\iOS\iPhone\FM_LaunchImage_1136x640.png + $(BDS)\bin\Artwork\iOS\iPhone\FM_LaunchImage_1242x2688.png + $(BDS)\bin\Artwork\iOS\iPhone\FM_LaunchImage_1334x750.png + $(BDS)\bin\Artwork\iOS\iPhone\FM_LaunchImage_1792x828.png + $(BDS)\bin\Artwork\iOS\iPhone\FM_LaunchImage_2688x1242.png + $(BDS)\bin\Artwork\iOS\iPad\FM_ApplicationIcon_167x167.png + $(BDS)\bin\Artwork\iOS\iPad\FM_LaunchImagePortrait_1668x2224.png + $(BDS)\bin\Artwork\iOS\iPad\FM_LaunchImagePortrait_1668x2388.png + $(BDS)\bin\Artwork\iOS\iPad\FM_LaunchImagePortrait_2048x2732.png + $(BDS)\bin\Artwork\iOS\iPad\FM_LaunchImageLandscape_2224x1668.png + $(BDS)\bin\Artwork\iOS\iPad\FM_LaunchImageLandscape_2388x1668.png + $(BDS)\bin\Artwork\iOS\iPad\FM_LaunchImageLandscape_2732x2048.png + $(BDS)\bin\Artwork\iOS\iPhone\FM_LaunchImage_2x.png + $(BDS)\bin\Artwork\iOS\iPhone\FM_LaunchImageDark_2x.png + $(BDS)\bin\Artwork\iOS\iPhone\FM_LaunchImage_3x.png + $(BDS)\bin\Artwork\iOS\iPhone\FM_LaunchImageDark_3x.png + $(BDS)\bin\Artwork\iOS\iPad\FM_LaunchImage_2x.png + $(BDS)\bin\Artwork\iOS\iPad\FM_LaunchImageDark_2x.png + + + CFBundleName=$(MSBuildProjectName);CFBundleDevelopmentRegion=en;CFBundleDisplayName=$(MSBuildProjectName);CFBundleIdentifier=$(MSBuildProjectName);CFBundleInfoDictionaryVersion=7.1;CFBundleVersion=1.0.0.0;CFBundlePackageType=APPL;CFBundleSignature=????;LSRequiresIPhoneOS=true;CFBundleAllowMixedLocalizations=YES;CFBundleExecutable=$(MSBuildProjectName);UIDeviceFamily=iPhone & iPad;CFBundleResourceSpecification=ResourceRules.plist;NSLocationAlwaysUsageDescription=The reason for accessing the location information of the user;NSLocationWhenInUseUsageDescription=The reason for accessing the location information of the user;FMLocalNotificationPermission=false;UIBackgroundModes=;NSContactsUsageDescription=The reason for accessing the contacts;NSPhotoLibraryUsageDescription=The reason for accessing the photo library;NSCameraUsageDescription=The reason for accessing the camera + iPhoneAndiPad + true + $(BDS)\bin\Artwork\iOS\iPhone\FM_ApplicationIcon_60x60.png + $(BDS)\bin\Artwork\iOS\iPhone\FM_ApplicationIcon_120x120.png + $(BDS)\bin\Artwork\iOS\iPhone\FM_SpotlightSearchIcon_40x40.png + $(BDS)\bin\Artwork\iOS\iPhone\FM_SpotlightSearchIcon_80x80.png + $(BDS)\bin\Artwork\iOS\iPad\FM_SpotlightSearchIcon_40x40.png + $(BDS)\bin\Artwork\iOS\iPad\FM_SpotlightSearchIcon_80x80.png + $(BDS)\bin\Artwork\iOS\iPad\FM_ApplicationIcon_76x76.png + $(BDS)\bin\Artwork\iOS\iPad\FM_ApplicationIcon_152x152.png + $(BDS)\bin\Artwork\iOS\iPad\FM_LaunchImagePortrait_768x1024.png + $(BDS)\bin\Artwork\iOS\iPad\FM_LaunchImageLandscape_1024x768.png + $(BDS)\bin\Artwork\iOS\iPad\FM_LaunchImagePortrait_1536x2048.png + $(BDS)\bin\Artwork\iOS\iPad\FM_LaunchImageLandscape_2048x1536.png + $(BDS)\bin\Artwork\iOS\iPhone\FM_ApplicationIcon_87x87.png + $(BDS)\bin\Artwork\iOS\iPhone\FM_ApplicationIcon_180x180.png + $(BDS)\bin\Artwork\iOS\iPhone\FM_LaunchImage_750x1334.png + $(BDS)\bin\Artwork\iOS\iPhone\FM_LaunchImage_1242x2208.png + $(BDS)\bin\Artwork\iOS\iPhone\FM_LaunchImage_2208x1242.png + $(BDS)\bin\Artwork\iOS\iPhone\FM_LaunchImage_1125x2436.png + $(BDS)\bin\Artwork\iOS\iPhone\FM_LaunchImage_2436x1125.png + $(BDS)\bin\Artwork\iOS\iPhone\FM_SpotlightSearchIcon_120x120.png + $(BDS)\bin\Artwork\iOS\iPhone\FM_LaunchImage_828x1792.png + $(BDS)\bin\Artwork\iOS\iPhone\FM_LaunchImage_1136x640.png + $(BDS)\bin\Artwork\iOS\iPhone\FM_LaunchImage_1242x2688.png + $(BDS)\bin\Artwork\iOS\iPhone\FM_LaunchImage_1334x750.png + $(BDS)\bin\Artwork\iOS\iPhone\FM_LaunchImage_1792x828.png + $(BDS)\bin\Artwork\iOS\iPhone\FM_LaunchImage_2688x1242.png + $(BDS)\bin\Artwork\iOS\iPad\FM_ApplicationIcon_167x167.png + $(BDS)\bin\Artwork\iOS\iPad\FM_LaunchImagePortrait_1668x2224.png + $(BDS)\bin\Artwork\iOS\iPad\FM_LaunchImagePortrait_1668x2388.png + $(BDS)\bin\Artwork\iOS\iPad\FM_LaunchImagePortrait_2048x2732.png + $(BDS)\bin\Artwork\iOS\iPad\FM_LaunchImageLandscape_2224x1668.png + $(BDS)\bin\Artwork\iOS\iPad\FM_LaunchImageLandscape_2388x1668.png + $(BDS)\bin\Artwork\iOS\iPad\FM_LaunchImageLandscape_2732x2048.png + + + CFBundleName=$(MSBuildProjectName);CFBundleDisplayName=$(MSBuildProjectName);CFBundleIdentifier=$(MSBuildProjectName);CFBundleVersion=1.0.0;CFBundleShortVersionString=1.0.0;CFBundlePackageType=APPL;CFBundleSignature=????;CFBundleAllowMixedLocalizations=YES;CFBundleExecutable=$(MSBuildProjectName);NSHighResolutionCapable=true;LSApplicationCategoryType=public.app-category.utilities;NSLocationUsageDescription=The reason for accessing the location information of the user;NSContactsUsageDescription=The reason for accessing the contacts + Debug + true + true + Base + true + + + Winapi;System.Win;Data.Win;Datasnap.Win;Web.Win;Soap.Win;Xml.Win;Bde;$(DCC_Namespace) + Debug + true + CompanyName=;FileDescription=$(MSBuildProjectName);FileVersion=1.0.0.0;InternalName=;LegalCopyright=;LegalTrademarks=;OriginalFilename=;ProductName=$(MSBuildProjectName);ProductVersion=1.0.0.0;Comments=;ProgramID=com.embarcadero.$(MSBuildProjectName) + 1033 + $(BDS)\bin\default_app.manifest + true + $(BDS)\bin\Artwork\Windows\UWP\delphi_UwpDefault_44.png + $(BDS)\bin\Artwork\Windows\UWP\delphi_UwpDefault_150.png + MARSTemplateServer_Icon.ico + + + $(BDS)\bin\default_app.manifest + true + $(BDS)\bin\Artwork\Windows\UWP\delphi_UwpDefault_44.png + $(BDS)\bin\Artwork\Windows\UWP\delphi_UwpDefault_150.png + + + RELEASE;$(DCC_Define) + 0 + false + 0 + + + true + PerMonitor + MARSTemplateServer_Icon.ico + + + DEBUG;$(DCC_Define) + false + true + + + Debug + + + true + + + true + true + Cfg_2 + true + + + true + true + 1033 + CompanyName=;FileDescription=$(MSBuildProjectName);FileVersion=1.0.0.0;InternalName=;LegalCopyright=;LegalTrademarks=;OriginalFilename=;ProductName=$(MSBuildProjectName);ProductVersion=1.0.0.0;Comments=;ProgramID=com.embarcadero.$(MSBuildProjectName) + MARSTemplateServer_Icon.ico + PerMonitor + + + + MainSource + + +
MainForm
+
+ +
MainDataModule
+ TDataModule +
+ + Cfg_2 + Base + + + Base + + + Cfg_1 + Base + +
+ + Delphi.Personality.12 + + + + + MARSTemplateClientDCS.dpr + + + Microsoft Office 2000 Sample Automation Server Wrapper Components + Microsoft Office XP Sample Automation Server Wrapper Components + + + + True + True + True + False + False + True + False + + + + + true + + + + + ic_launcher.png + true + + + + + MARSTemplateClientDCS.exe + true + + + + + ic_launcher.png + true + + + + + libMARSTemplateClient.so + true + + + + + ic_launcher.png + true + + + + + true + + + + + true + + + + + splash_image.png + true + + + + + true + + + + + splash_image.png + true + + + + + splash_image.png + true + + + + + splash_image.png + true + + + + + true + + + + + libMARSTemplateClientDCS.so + true + + + + + true + + + + + classes.dex + true + + + + + ic_launcher.png + true + + + + + ic_launcher.png + true + + + + + true + + + + + true + + + + + libMARSTemplateClient.so + true + + + + + true + + + + + true + + + + + 1 + + + Contents\MacOS + 1 + + + 0 + + + + + classes + 1 + + + classes + 1 + + + + + res\xml + 1 + + + res\xml + 1 + + + + + library\lib\armeabi-v7a + 1 + + + + + library\lib\armeabi + 1 + + + library\lib\armeabi + 1 + + + + + library\lib\armeabi-v7a + 1 + + + + + library\lib\mips + 1 + + + library\lib\mips + 1 + + + + + library\lib\armeabi-v7a + 1 + + + library\lib\arm64-v8a + 1 + + + + + library\lib\armeabi-v7a + 1 + + + + + res\drawable + 1 + + + res\drawable + 1 + + + + + res\values + 1 + + + res\values + 1 + + + + + res\values-v21 + 1 + + + res\values-v21 + 1 + + + + + res\values + 1 + + + res\values + 1 + + + + + res\drawable + 1 + + + res\drawable + 1 + + + + + res\drawable-xxhdpi + 1 + + + res\drawable-xxhdpi + 1 + + + + + res\drawable-ldpi + 1 + + + res\drawable-ldpi + 1 + + + + + res\drawable-mdpi + 1 + + + res\drawable-mdpi + 1 + + + + + res\drawable-hdpi + 1 + + + res\drawable-hdpi + 1 + + + + + res\drawable-xhdpi + 1 + + + res\drawable-xhdpi + 1 + + + + + res\drawable-mdpi + 1 + + + res\drawable-mdpi + 1 + + + + + res\drawable-hdpi + 1 + + + res\drawable-hdpi + 1 + + + + + res\drawable-xhdpi + 1 + + + res\drawable-xhdpi + 1 + + + + + res\drawable-xxhdpi + 1 + + + res\drawable-xxhdpi + 1 + + + + + res\drawable-xxxhdpi + 1 + + + res\drawable-xxxhdpi + 1 + + + + + res\drawable-small + 1 + + + res\drawable-small + 1 + + + + + res\drawable-normal + 1 + + + res\drawable-normal + 1 + + + + + res\drawable-large + 1 + + + res\drawable-large + 1 + + + + + res\drawable-xlarge + 1 + + + res\drawable-xlarge + 1 + + + + + res\values + 1 + + + res\values + 1 + + + + + 1 + + + Contents\MacOS + 1 + + + 0 + + + + + Contents\MacOS + 1 + .framework + + + Contents\MacOS + 1 + .framework + + + 0 + + + + + 1 + .dylib + + + 1 + .dylib + + + 1 + .dylib + + + Contents\MacOS + 1 + .dylib + + + Contents\MacOS + 1 + .dylib + + + 0 + .dll;.bpl + + + + + 1 + .dylib + + + 1 + .dylib + + + 1 + .dylib + + + Contents\MacOS + 1 + .dylib + + + Contents\MacOS + 1 + .dylib + + + 0 + .bpl + + + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + Contents\Resources\StartUp\ + 0 + + + Contents\Resources\StartUp\ + 0 + + + 0 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + ..\$(PROJECTNAME).launchscreen\Assets\LaunchScreenImage.imageset + 1 + + + ..\$(PROJECTNAME).launchscreen\Assets\LaunchScreenImage.imageset + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + ..\$(PROJECTNAME).launchscreen\Assets\LaunchScreenImage.imageset + 1 + + + ..\$(PROJECTNAME).launchscreen\Assets\LaunchScreenImage.imageset + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + ..\$(PROJECTNAME).launchscreen\Assets\LaunchScreenImage.imageset + 1 + + + ..\$(PROJECTNAME).launchscreen\Assets\LaunchScreenImage.imageset + 1 + + + + + 1 + + + 1 + + + 1 + + + + + ..\$(PROJECTNAME).launchscreen\Assets\LaunchScreenImage.imageset + 1 + + + ..\$(PROJECTNAME).launchscreen\Assets\LaunchScreenImage.imageset + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + ..\$(PROJECTNAME).launchscreen\Assets\LaunchScreenImage.imageset + 1 + + + ..\$(PROJECTNAME).launchscreen\Assets\LaunchScreenImage.imageset + 1 + + + + + ..\$(PROJECTNAME).launchscreen\Assets\LaunchScreenImage.imageset + 1 + + + ..\$(PROJECTNAME).launchscreen\Assets\LaunchScreenImage.imageset + 1 + + + + + 1 + + + 1 + + + + + ..\$(PROJECTNAME).app.dSYM\Contents\Resources\DWARF + 1 + + + ..\$(PROJECTNAME).app.dSYM\Contents\Resources\DWARF + 1 + + + + + 1 + + + 1 + + + + + ..\ + 1 + + + ..\ + 1 + + + + + 1 + + + 1 + + + 1 + + + + + ..\$(PROJECTNAME).launchscreen + 64 + + + ..\$(PROJECTNAME).launchscreen + 64 + + + + + 1 + + + 1 + + + 1 + + + + + ..\$(PROJECTNAME).app.dSYM\Contents\Resources\DWARF + 1 + + + + + ..\ + 1 + + + ..\ + 1 + + + + + Contents + 1 + + + Contents + 1 + + + + + Contents\Resources + 1 + + + Contents\Resources + 1 + + + + + library\lib\armeabi-v7a + 1 + + + library\lib\arm64-v8a + 1 + + + 1 + + + 1 + + + 1 + + + 1 + + + Contents\MacOS + 1 + + + Contents\MacOS + 1 + + + 0 + + + + + library\lib\armeabi-v7a + 1 + + + + + 1 + + + 1 + + + + + Assets + 1 + + + Assets + 1 + + + + + Assets + 1 + + + Assets + 1 + + + + + + + + + + + + + + + 12 + + + + +
diff --git a/Demos/MARSTemplateDCS/MARSTemplateDCSProjectGroup.groupproj b/Demos/MARSTemplateDCS/MARSTemplateDCSProjectGroup.groupproj new file mode 100644 index 00000000..6f92b123 --- /dev/null +++ b/Demos/MARSTemplateDCS/MARSTemplateDCSProjectGroup.groupproj @@ -0,0 +1,96 @@ + + + {6E23DEFF-F737-42C3-B8AD-2549B8F67C93} + + + + + + + + + + + + + + + + + + + + + + + Default.Personality.12 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Demos/MARSTemplateDCS/MARSTemplateServerDCSApplication.dpr b/Demos/MARSTemplateDCS/MARSTemplateServerDCSApplication.dpr new file mode 100644 index 00000000..607e67af --- /dev/null +++ b/Demos/MARSTemplateDCS/MARSTemplateServerDCSApplication.dpr @@ -0,0 +1,16 @@ +program MARSTemplateServerDCSApplication; + +uses + Vcl.Forms, + Server.DCS.Forms.Main in 'Server.DCS.Forms.Main.pas' {MainForm}, + Server.Ignition in 'Server.Ignition.pas', + Server.Resources in 'Server.Resources.pas'; + +{$R *.res} + +begin + Application.Initialize; + Application.MainFormOnTaskbar := True; + Application.CreateForm(TMainForm, MainForm); + Application.Run; +end. diff --git a/Demos/MARSTemplateDCS/MARSTemplateServerDCSApplication.dproj b/Demos/MARSTemplateDCS/MARSTemplateServerDCSApplication.dproj new file mode 100644 index 00000000..dfd71c9a --- /dev/null +++ b/Demos/MARSTemplateDCS/MARSTemplateServerDCSApplication.dproj @@ -0,0 +1,1133 @@ + + + {044D26EF-293D-4A96-9B9B-2E6CF9811486} + 19.0 + VCL + MARSTemplateServerDCSApplication.dpr + True + Release + Win64 + 3 + Application + + + true + + + true + Base + true + + + true + Base + true + + + true + Base + true + + + true + Cfg_1 + true + true + + + true + Cfg_1 + true + true + + + true + Base + true + + + true + Cfg_2 + true + true + + + true + Cfg_2 + true + true + + + .\$(Platform)\$(Config) + .\bin + false + false + false + false + false + System;Xml;Data;Datasnap;Web;Soap;Vcl;Vcl.Imaging;Vcl.Touch;Vcl.Samples;Vcl.Shell;$(DCC_Namespace) + $(BDS)\bin\delphi_PROJECTICON.ico + $(BDS)\bin\Artwork\Windows\UWP\delphi_UwpDefault_44.png + $(BDS)\bin\Artwork\Windows\UWP\delphi_UwpDefault_150.png + MARSTemplateServerDCSApplication + 1040 + CompanyName=;FileDescription=$(MSBuildProjectName);FileVersion=1.0.0.0;InternalName=;LegalCopyright=;LegalTrademarks=;OriginalFilename=;ProgramID=com.embarcadero.$(MSBuildProjectName);ProductName=$(MSBuildProjectName);ProductVersion=1.0.0.0;Comments= + + + DBXSqliteDriver;RESTComponents;fmxase;DBXDb2Driver;DBXInterBaseDriver;MARSClient.FireDAC;vclactnband;vclFireDAC;emsclientfiredac;tethering;svnui;DataSnapFireDAC;FireDACADSDriver;DBXMSSQLDriver;DatasnapConnectorsFreePascal;FireDACMSSQLDriver;vcltouch;vcldb;bindcompfmx;svn;DBXOracleDriver;inetdb;FmxTeeUI;emsedge;fmx;FireDACIBDriver;fmxdae;FormatFloatPackage;FireDACDBXDriver;dbexpress;IndyCore;vclx;dsnap;emsclient;DataSnapCommon;FireDACCommon;RESTBackendComponents;DataSnapConnectors;VCLRESTComponents;soapserver;vclie;bindengine;DBXMySQLDriver;CloudService;FireDACOracleDriver;FireDACMySQLDriver;DBXFirebirdDriver;FireDACCommonODBC;FireDACCommonDriver;DataSnapClient;inet;IndyIPCommon;bindcompdbx;vcl;IndyIPServer;DBXSybaseASEDriver;DataSetRESTRequestAdapterPackage;IndySystem;FireDACDb2Driver;dsnapcon;FireDACMSAccDriver;fmxFireDAC;FireDACInfxDriver;vclimg;TeeDB;FireDAC;emshosting;FireDACSqliteDriver;FireDACPgDriver;FireDACASADriver;DBXOdbcDriver;FireDACTDataDriver;FMXTee;soaprtl;DbxCommonDriver;Tee;DataSnapServer;xmlrtl;soapmidas;DataSnapNativeClient;fmxobj;vclwinx;FireDACDSDriver;rtl;emsserverresource;DbxClientDriver;DBXSybaseASADriver;CustomIPTransport;vcldsnap;MARSClient.Core;bindcomp;appanalytics;DBXInformixDriver;IndyIPClient;bindcompvcl;TeeUI;dbxcds;VclSmp;adortl;FireDACODBCDriver;DataSnapIndy10ServerTransport;dsnapxml;DataSnapProviderClient;dbrtl;IndyProtocols;inetdbxpress;FireDACMongoDBDriver;DataSnapServerMidas;$(DCC_UsePackage) + Winapi;System.Win;Data.Win;Datasnap.Win;Web.Win;Soap.Win;Xml.Win;Bde;$(DCC_Namespace) + Debug + true + CompanyName=;FileDescription=$(MSBuildProjectName);FileVersion=1.0.0.0;InternalName=;LegalCopyright=;LegalTrademarks=;OriginalFilename=;ProgramID=com.embarcadero.$(MSBuildProjectName);ProductName=$(MSBuildProjectName);ProductVersion=1.0.0.0;Comments= + 1033 + $(BDS)\bin\default_app.manifest + + + DBXSqliteDriver;RESTComponents;fmxase;DBXDb2Driver;DBXInterBaseDriver;vclactnband;vclFireDAC;emsclientfiredac;tethering;DataSnapFireDAC;FireDACADSDriver;DBXMSSQLDriver;DatasnapConnectorsFreePascal;FireDACMSSQLDriver;vcltouch;vcldb;bindcompfmx;DBXOracleDriver;inetdb;FmxTeeUI;emsedge;fmx;FireDACIBDriver;fmxdae;FireDACDBXDriver;dbexpress;IndyCore;vclx;dsnap;emsclient;DataSnapCommon;FireDACCommon;RESTBackendComponents;DataSnapConnectors;VCLRESTComponents;soapserver;vclie;bindengine;DBXMySQLDriver;CloudService;FireDACOracleDriver;FireDACMySQLDriver;DBXFirebirdDriver;FireDACCommonODBC;FireDACCommonDriver;DataSnapClient;inet;IndyIPCommon;bindcompdbx;vcl;IndyIPServer;DBXSybaseASEDriver;IndySystem;FireDACDb2Driver;dsnapcon;FireDACMSAccDriver;fmxFireDAC;FireDACInfxDriver;vclimg;TeeDB;FireDAC;emshosting;FireDACSqliteDriver;FireDACPgDriver;FireDACASADriver;DBXOdbcDriver;FireDACTDataDriver;FMXTee;soaprtl;DbxCommonDriver;Tee;DataSnapServer;xmlrtl;soapmidas;DataSnapNativeClient;fmxobj;vclwinx;FireDACDSDriver;rtl;emsserverresource;DbxClientDriver;DBXSybaseASADriver;CustomIPTransport;vcldsnap;MARSClient.Core;bindcomp;appanalytics;DBXInformixDriver;IndyIPClient;bindcompvcl;TeeUI;dbxcds;VclSmp;adortl;FireDACODBCDriver;DataSnapIndy10ServerTransport;dsnapxml;DataSnapProviderClient;dbrtl;IndyProtocols;inetdbxpress;FireDACMongoDBDriver;DataSnapServerMidas;$(DCC_UsePackage) + Winapi;System.Win;Data.Win;Datasnap.Win;Web.Win;Soap.Win;Xml.Win;$(DCC_Namespace) + Debug + true + CompanyName=;FileDescription=$(MSBuildProjectName);FileVersion=1.0.0.0;InternalName=;LegalCopyright=;LegalTrademarks=;OriginalFilename=;ProgramID=com.embarcadero.$(MSBuildProjectName);ProductName=$(MSBuildProjectName);ProductVersion=1.0.0.0;Comments= + 1033 + $(BDS)\bin\default_app.manifest + + + DEBUG;$(DCC_Define) + true + false + true + true + true + + + false + true + PerMonitorV2 + + + true + PerMonitorV2 + true + 1033 + + + false + RELEASE;$(DCC_Define) + 0 + 0 + + + true + PerMonitorV2 + + + true + PerMonitorV2 + + + + MainSource + + +
MainForm
+
+ + + + Cfg_2 + Base + + + Base + + + Cfg_1 + Base + +
+ + Delphi.Personality.12 + Application + + + + MARSTemplateServerDCSApplication.dpr + + + DBExpress Enterprise Data Explorer Integration + Microsoft Office 2000 Sample Automation Server Wrapper Components + Microsoft Office XP Sample Automation Server Wrapper Components + CodeSite Express 5.3.3 + + + + + + MARSTemplateServerDCSApplication.exe + true + + + + + MARSTemplateServerDCSApplication.exe + true + + + + + MARSTemplateServerDCSApplication.exe + true + + + + + 1 + + + Contents\MacOS + 1 + + + 0 + + + + + classes + 1 + + + classes + 1 + + + + + res\xml + 1 + + + res\xml + 1 + + + + + library\lib\armeabi-v7a + 1 + + + + + library\lib\armeabi + 1 + + + library\lib\armeabi + 1 + + + + + library\lib\armeabi-v7a + 1 + + + + + library\lib\mips + 1 + + + library\lib\mips + 1 + + + + + library\lib\armeabi-v7a + 1 + + + library\lib\arm64-v8a + 1 + + + + + library\lib\armeabi-v7a + 1 + + + + + res\drawable + 1 + + + res\drawable + 1 + + + + + res\values + 1 + + + res\values + 1 + + + + + res\values-v21 + 1 + + + res\values-v21 + 1 + + + + + res\values + 1 + + + res\values + 1 + + + + + res\drawable + 1 + + + res\drawable + 1 + + + + + res\drawable-xxhdpi + 1 + + + res\drawable-xxhdpi + 1 + + + + + res\drawable-ldpi + 1 + + + res\drawable-ldpi + 1 + + + + + res\drawable-mdpi + 1 + + + res\drawable-mdpi + 1 + + + + + res\drawable-hdpi + 1 + + + res\drawable-hdpi + 1 + + + + + res\drawable-xhdpi + 1 + + + res\drawable-xhdpi + 1 + + + + + res\drawable-mdpi + 1 + + + res\drawable-mdpi + 1 + + + + + res\drawable-hdpi + 1 + + + res\drawable-hdpi + 1 + + + + + res\drawable-xhdpi + 1 + + + res\drawable-xhdpi + 1 + + + + + res\drawable-xxhdpi + 1 + + + res\drawable-xxhdpi + 1 + + + + + res\drawable-xxxhdpi + 1 + + + res\drawable-xxxhdpi + 1 + + + + + res\drawable-small + 1 + + + res\drawable-small + 1 + + + + + res\drawable-normal + 1 + + + res\drawable-normal + 1 + + + + + res\drawable-large + 1 + + + res\drawable-large + 1 + + + + + res\drawable-xlarge + 1 + + + res\drawable-xlarge + 1 + + + + + res\values + 1 + + + res\values + 1 + + + + + 1 + + + Contents\MacOS + 1 + + + 0 + + + + + Contents\MacOS + 1 + .framework + + + Contents\MacOS + 1 + .framework + + + 0 + + + + + 1 + .dylib + + + 1 + .dylib + + + 1 + .dylib + + + Contents\MacOS + 1 + .dylib + + + Contents\MacOS + 1 + .dylib + + + 0 + .dll;.bpl + + + + + 1 + .dylib + + + 1 + .dylib + + + 1 + .dylib + + + Contents\MacOS + 1 + .dylib + + + Contents\MacOS + 1 + .dylib + + + 0 + .bpl + + + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + Contents\Resources\StartUp\ + 0 + + + Contents\Resources\StartUp\ + 0 + + + 0 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + ..\$(PROJECTNAME).launchscreen\Assets\LaunchScreenImage.imageset + 1 + + + ..\$(PROJECTNAME).launchscreen\Assets\LaunchScreenImage.imageset + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + ..\$(PROJECTNAME).launchscreen\Assets\LaunchScreenImage.imageset + 1 + + + ..\$(PROJECTNAME).launchscreen\Assets\LaunchScreenImage.imageset + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + ..\$(PROJECTNAME).launchscreen\Assets\LaunchScreenImage.imageset + 1 + + + ..\$(PROJECTNAME).launchscreen\Assets\LaunchScreenImage.imageset + 1 + + + + + 1 + + + 1 + + + 1 + + + + + ..\$(PROJECTNAME).launchscreen\Assets\LaunchScreenImage.imageset + 1 + + + ..\$(PROJECTNAME).launchscreen\Assets\LaunchScreenImage.imageset + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + ..\$(PROJECTNAME).launchscreen\Assets\LaunchScreenImage.imageset + 1 + + + ..\$(PROJECTNAME).launchscreen\Assets\LaunchScreenImage.imageset + 1 + + + + + ..\$(PROJECTNAME).launchscreen\Assets\LaunchScreenImage.imageset + 1 + + + ..\$(PROJECTNAME).launchscreen\Assets\LaunchScreenImage.imageset + 1 + + + + + 1 + + + 1 + + + + + ..\$(PROJECTNAME).app.dSYM\Contents\Resources\DWARF + 1 + + + ..\$(PROJECTNAME).app.dSYM\Contents\Resources\DWARF + 1 + + + + + 1 + + + 1 + + + + + ..\ + 1 + + + ..\ + 1 + + + + + 1 + + + 1 + + + 1 + + + + + ..\$(PROJECTNAME).launchscreen + 64 + + + ..\$(PROJECTNAME).launchscreen + 64 + + + + + 1 + + + 1 + + + 1 + + + + + ..\$(PROJECTNAME).app.dSYM\Contents\Resources\DWARF + 1 + + + + + ..\ + 1 + + + ..\ + 1 + + + + + Contents + 1 + + + Contents + 1 + + + + + Contents\Resources + 1 + + + Contents\Resources + 1 + + + + + library\lib\armeabi-v7a + 1 + + + library\lib\arm64-v8a + 1 + + + 1 + + + 1 + + + 1 + + + 1 + + + Contents\MacOS + 1 + + + Contents\MacOS + 1 + + + 0 + + + + + library\lib\armeabi-v7a + 1 + + + + + 1 + + + 1 + + + + + Assets + 1 + + + Assets + 1 + + + + + Assets + 1 + + + Assets + 1 + + + + + + + + + + + + + + + True + True + + + 12 + + + + +
diff --git a/Demos/MARSTemplateDCS/MARSTemplateServerDCSConsoleApplication.dpr b/Demos/MARSTemplateDCS/MARSTemplateServerDCSConsoleApplication.dpr new file mode 100644 index 00000000..2ed845df --- /dev/null +++ b/Demos/MARSTemplateDCS/MARSTemplateServerDCSConsoleApplication.dpr @@ -0,0 +1,201 @@ +(* + Copyright 2016, MARS-Curiosity - REST Library + + Home: https://github.com/andrea-magni/MARS +*) +program MARSTemplateServerDCSConsoleApplication; +{$APPTYPE CONSOLE} + +{$I MARS.inc} + +uses +{$ifdef DelphiXE3_UP} + System.SysUtils, + System.Types, +// IPPeerServer, IPPeerAPI, + IdHTTPWebBrokerBridge, + IdSchedulerOfThreadPool, + Web.WebReq, + Web.WebBroker, +{$else} + SysUtils, StrUtils, + Types, + IdHTTPWebBrokerBridge, + IdSchedulerOfThreadPool, + WebReq, + WebBroker, +{$endif} + IdContext, + ServerConst in 'ServerConst.pas', + Server.WebModule in 'Server.WebModule.pas' {ServerWebModule: TWebModule}, + Server.Ignition in 'Server.Ignition.pas'; + +{$R *.res} + +type + TDummyIndyServer = class + public + procedure ParseAuthenticationHandler(AContext: TIdContext; + const AAuthType, AAuthData: String; var VUsername, VPassword: String; + var VHandled: Boolean); virtual; + end; + + +procedure StartServer(const AServer: TIdHTTPWebBrokerBridge); +begin + if not (AServer.Active) then + begin + AServer.DefaultPort := TServerEngine.Default.Port; + Writeln(Format(sStartingServer, [AServer.DefaultPort])); + AServer.Active := True; + end + else + Writeln(sServerRunning); + Write(cArrow); +end; + +procedure StopServer(const AServer: TIdHTTPWebBrokerBridge); +begin + if AServer.Active then + begin + Writeln(sStoppingServer); + AServer.Active := False; + Writeln(sServerStopped); + end + else + Writeln(sServerNotRunning); + Write(cArrow); +end; + +procedure SetPort(const AServer: TIdHTTPWebBrokerBridge; const APort: string); +var + LPort: Integer; + LWasActive: Boolean; +begin + LPort := StrToIntDef(APort, -1); + if LPort = -1 then + begin + Writeln('Port should be an integer number. Try again.'); + Exit; + end; + + LWasActive := AServer.Active; + if LWasActive then + StopServer(AServer); + TServerEngine.Default.Port := LPort; + if LWasActive then + StartServer(AServer); + Writeln(Format(sPortSet, [IntToStr(TServerEngine.Default.Port)])); + Write(cArrow); +end; + +procedure WriteCommands; +begin + Writeln(sCommands); + Write(cArrow); +end; + +procedure WriteStatus(const AServer: TIdHTTPWebBrokerBridge); +begin + Writeln(sIndyVersion + AServer.SessionList.Version); + Writeln(sActive + BoolToStr(AServer.Active, True)); + Writeln(sPort + IntToStr(TServerEngine.Default.Port)); + Write(cArrow); +end; + +procedure SetupThreadScheduler(const AServer: TIdHTTPWebBrokerBridge); +var + LScheduler: TIdSchedulerOfThreadPool; +begin + LScheduler := TIdSchedulerOfThreadPool.Create(AServer); + try + LScheduler.PoolSize := TServerEngine.Default.ThreadPoolSize; + AServer.Scheduler := LScheduler; + AServer.MaxConnections := LScheduler.PoolSize; + except + AServer.Scheduler.DisposeOf; + AServer.Scheduler := nil; + raise; + end; +end; + +procedure RunServer(); +var + LServer: TIdHTTPWebBrokerBridge; + LDummyIndy: TDummyIndyServer; + LResponse: string; +begin + WriteCommands; + LDummyIndy := TDummyIndyServer.Create; + try + LServer := TIdHTTPWebBrokerBridge.Create(nil); + try + LServer.DefaultPort := TServerEngine.Default.Port; + LServer.OnParseAuthentication := LDummyIndy.ParseAuthenticationHandler; + {$IFNDEF LINUX} + SetupThreadScheduler(LServer); + {$ENDIF} + + while True do + begin + Readln(LResponse); + LResponse := LowerCase(LResponse); + if sametext(LResponse, cCommandStart) then + StartServer(LServer) + else if sametext(LResponse, cCommandStatus) then + WriteStatus(LServer) + else if sametext(LResponse, cCommandStop) then + StopServer(LServer) + {$ifdef DelphiXE3_UP} + else if LResponse.StartsWith(cCommandSetPort, True) then + SetPort(LServer, LResponse.Split([' '])[2]) + {$else} + else if AnsiStartsText(cCommandSetPort, LResponse) then + SetPort(LServer, Copy(LResponse, Length(cCommandSetPort)+1, MAXINT)) + {$endif} + + else if sametext(LResponse, cCommandHelp) then + WriteCommands + else if sametext(LResponse, cCommandExit) then + if LServer.Active then + begin + StopServer(LServer); + break + end + else + break + else + begin + Writeln(sInvalidCommand); + Write(cArrow); + end; + end; + finally + LServer.Free; + end; + finally + LDummyIndy.Free; + end; +end; + +{ TDummyIndyServer } + +procedure TDummyIndyServer.ParseAuthenticationHandler(AContext: TIdContext; + const AAuthType, AAuthData: String; var VUsername, VPassword: String; + var VHandled: Boolean); +begin + // Allow JWT Bearer authentication's scheme + if SameText(AAuthType, 'Bearer') then + VHandled := True; +end; + +begin + try + if WebRequestHandler <> nil then + WebRequestHandler.WebModuleClass := WebModuleClass; + RunServer(); + except + on E: Exception do + Writeln(E.ClassName, ': ', E.Message); + end +end. diff --git a/Demos/MARSTemplateDCS/MARSTemplateServerDCSConsoleApplication.dproj b/Demos/MARSTemplateDCS/MARSTemplateServerDCSConsoleApplication.dproj new file mode 100644 index 00000000..624418ce --- /dev/null +++ b/Demos/MARSTemplateDCS/MARSTemplateServerDCSConsoleApplication.dproj @@ -0,0 +1,1224 @@ + + + {8EEC8DF2-3740-468A-937A-60168245FB71} + MARSTemplateServerDCSConsoleApplication.dpr + True + Release + 4225 + Console + None + 19.0 + Win32 + + + true + + + true + Base + true + + + true + Base + true + + + true + Base + true + + + true + Base + true + + + true + Base + true + + + true + Base + true + + + true + Base + true + + + true + Base + true + + + true + Cfg_1 + true + true + + + true + Cfg_1 + true + true + + + true + Base + true + + + true + Cfg_2 + true + true + + + true + Cfg_2 + true + true + + + true + Cfg_2 + true + true + + + true + Cfg_2 + true + true + + + true + Cfg_2 + true + true + + + false + false + false + false + false + 00400000 + MARSTemplateServerDCSConsoleApplication + 1040 + CompanyName=;FileDescription=;FileVersion=1.0.0.0;InternalName=;LegalCopyright=;LegalTrademarks=;OriginalFilename=;ProductName=;ProductVersion=1.0.0.0;Comments=;CFBundleName= + System;Xml;Data;Datasnap;Web;Soap;$(DCC_Namespace) + $(BDS)\bin\delphi_PROJECTICON.ico + $(BDS)\bin\delphi_PROJECTICNS.icns + ./bin + .\lib\$(Platform)\$(Config) + + + package=com.embarcadero.$(MSBuildProjectName);label=$(MSBuildProjectName);versionCode=1;versionName=1.0.0;persistent=False;restoreAnyVersion=False;installLocation=auto;largeHeap=False;theme=TitleBar;hardwareAccelerated=true;apiKey= + Debug + $(BDS)\bin\Artwork\Android\FM_LauncherIcon_36x36.png + $(BDS)\bin\Artwork\Android\FM_LauncherIcon_48x48.png + $(BDS)\bin\Artwork\Android\FM_LauncherIcon_72x72.png + $(BDS)\bin\Artwork\Android\FM_LauncherIcon_96x96.png + $(BDS)\bin\Artwork\Android\FM_LauncherIcon_144x144.png + $(BDS)\bin\Artwork\Android\FM_SplashImage_426x320.png + $(BDS)\bin\Artwork\Android\FM_SplashImage_470x320.png + $(BDS)\bin\Artwork\Android\FM_SplashImage_640x480.png + $(BDS)\bin\Artwork\Android\FM_SplashImage_960x720.png + true + true + true + true + true + true + true + true + true + true + android-support-v4.dex.jar;cloud-messaging.dex.jar;fmx.dex.jar;google-analytics-v2.dex.jar;google-play-billing.dex.jar;google-play-licensing.dex.jar;google-play-services.dex.jar + $(BDS)\bin\Artwork\Android\FM_NotificationIcon_24x24.png + $(BDS)\bin\Artwork\Android\FM_NotificationIcon_36x36.png + $(BDS)\bin\Artwork\Android\FM_NotificationIcon_48x48.png + $(BDS)\bin\Artwork\Android\FM_NotificationIcon_72x72.png + $(BDS)\bin\Artwork\Android\FM_NotificationIcon_96x96.png + + + package=com.embarcadero.$(MSBuildProjectName);label=$(MSBuildProjectName);versionCode=1;versionName=1.0.0;persistent=False;restoreAnyVersion=False;installLocation=auto;largeHeap=False;theme=TitleBar;hardwareAccelerated=true;apiKey= + Debug + true + Base + true + $(BDS)\bin\Artwork\Android\FM_LauncherIcon_36x36.png + $(BDS)\bin\Artwork\Android\FM_LauncherIcon_48x48.png + $(BDS)\bin\Artwork\Android\FM_LauncherIcon_72x72.png + $(BDS)\bin\Artwork\Android\FM_LauncherIcon_96x96.png + $(BDS)\bin\Artwork\Android\FM_LauncherIcon_144x144.png + $(BDS)\bin\Artwork\Android\FM_SplashImage_426x320.png + $(BDS)\bin\Artwork\Android\FM_SplashImage_470x320.png + $(BDS)\bin\Artwork\Android\FM_SplashImage_640x480.png + $(BDS)\bin\Artwork\Android\FM_SplashImage_960x720.png + true + true + true + true + true + true + true + true + true + true + android-support-v4.dex.jar;cloud-messaging.dex.jar;fmx.dex.jar;google-analytics-v2.dex.jar;google-play-billing.dex.jar;google-play-licensing.dex.jar;google-play-services.dex.jar + + + CFBundleName=$(MSBuildProjectName);CFBundleDevelopmentRegion=en;CFBundleDisplayName=$(MSBuildProjectName);CFBundleIdentifier=$(MSBuildProjectName);CFBundleInfoDictionaryVersion=7.1;CFBundleVersion=1.0.0.0;CFBundlePackageType=APPL;CFBundleSignature=????;LSRequiresIPhoneOS=true;CFBundleAllowMixedLocalizations=YES;CFBundleExecutable=$(MSBuildProjectName);UIDeviceFamily=iPhone & iPad;CFBundleResourceSpecification=ResourceRules.plist;NSLocationAlwaysUsageDescription=The reason for accessing the location information of the user;NSLocationWhenInUseUsageDescription=The reason for accessing the location information of the user;FMLocalNotificationPermission=false;UIBackgroundModes=;NSContactsUsageDescription=The reason for accessing the contacts;NSPhotoLibraryUsageDescription=The reason for accessing the photo library;NSCameraUsageDescription=The reason for accessing the camera + iPhoneAndiPad + true + $(BDS)\bin\Artwork\iOS\iPhone\FM_ApplicationIcon_60x60.png + $(BDS)\bin\Artwork\iOS\iPhone\FM_ApplicationIcon_120x120.png + $(BDS)\bin\Artwork\iOS\iPhone\FM_SpotlightSearchIcon_40x40.png + $(BDS)\bin\Artwork\iOS\iPhone\FM_SpotlightSearchIcon_80x80.png + $(BDS)\bin\Artwork\iOS\iPad\FM_SpotlightSearchIcon_40x40.png + $(BDS)\bin\Artwork\iOS\iPad\FM_SpotlightSearchIcon_80x80.png + $(BDS)\bin\Artwork\iOS\iPad\FM_ApplicationIcon_76x76.png + $(BDS)\bin\Artwork\iOS\iPad\FM_ApplicationIcon_152x152.png + $(BDS)\bin\Artwork\iOS\iPad\FM_LaunchImagePortrait_768x1024.png + $(BDS)\bin\Artwork\iOS\iPad\FM_LaunchImageLandscape_1024x768.png + $(BDS)\bin\Artwork\iOS\iPad\FM_LaunchImagePortrait_1536x2048.png + $(BDS)\bin\Artwork\iOS\iPad\FM_LaunchImageLandscape_2048x1536.png + + + /usr/bin/xterm -e "%debuggee%" + (None) + MARSTemplateServer_Icon.ico + + + CFBundleName=$(MSBuildProjectName);CFBundleDisplayName=$(MSBuildProjectName);CFBundleIdentifier=$(MSBuildProjectName);CFBundleVersion=1.0.0;CFBundleShortVersionString=1.0.0;CFBundlePackageType=APPL;CFBundleSignature=????;CFBundleAllowMixedLocalizations=YES;CFBundleExecutable=$(MSBuildProjectName);NSHighResolutionCapable=true;LSApplicationCategoryType=public.app-category.utilities;NSContactsUsageDescription=The reason for accessing the contacts;NSLocationUsageDescription=The reason for accessing the location information of the user;NSCalendarsUsageDescription=The reason for accessing the calendar data;NSRemindersUsageDescription=The reason for accessing the reminders;NSCameraUsageDescription=The reason for accessing the camera;NSMicrophoneUsageDescription=The reason for accessing the microphone;NSMotionUsageDescription=The reason for accessing the accelerometer;NSDesktopFolderUsageDescription=The reason for accessing the Desktop folder;NSDocumentsFolderUsageDescription=The reason for accessing the Documents folder;NSDownloadsFolderUsageDescription=The reason for accessing the Downloads folder;NSNetworkVolumesUsageDescription=The reason for accessing files on a network volume;NSRemovableVolumesUsageDescription=The reason for accessing files on a removable volume;NSSpeechRecognitionUsageDescription=The reason for requesting to send user data to Apple's speech recognition servers + Debug + true + Base + true + /usr/X11/bin/xterm -e "%debuggee%" + (None) + + + Winapi;System.Win;Data.Win;Datasnap.Win;Web.Win;Soap.Win;Xml.Win;Bde;$(DCC_Namespace) + Debug + CompanyName=;FileDescription=$(MSBuildProjectName);FileVersion=1.0.0.0;InternalName=;LegalCopyright=;LegalTrademarks=;OriginalFilename=;ProductName=$(MSBuildProjectName);ProductVersion=1.0.0.0;Comments=;ProgramID=com.embarcadero.$(MSBuildProjectName) + 1033 + MARSTemplateServer_Icon.ico + (None) + $(BDS)\bin\Artwork\Windows\UWP\delphi_UwpDefault_44.png + $(BDS)\bin\Artwork\Windows\UWP\delphi_UwpDefault_150.png + + + MARSTemplateServerConsoleApplication_Icon.ico + + + RELEASE;$(DCC_Define) + 0 + false + 0 + + + MARSTemplateServer_Icon.ico + + + 1033 + CompanyName=;FileDescription=$(MSBuildProjectName);FileVersion=1.0.0.0;InternalName=;LegalCopyright=;LegalTrademarks=;OriginalFilename=;ProductName=$(MSBuildProjectName);ProductVersion=1.0.0.0;Comments=;ProgramID=com.embarcadero.$(MSBuildProjectName) + MARSTemplateServer_Icon.ico + + + DEBUG;$(DCC_Define) + false + true + + + Debug + + + true + + + MARSTemplateServer_Icon.ico + + + true + true + Cfg_2 + true + + + 1033 + CompanyName=;FileDescription=$(MSBuildProjectName);FileVersion=1.0.0.0;InternalName=;LegalCopyright=;LegalTrademarks=;OriginalFilename=;ProductName=$(MSBuildProjectName);ProductVersion=1.0.0.0;Comments=;ProgramID=com.embarcadero.$(MSBuildProjectName) + $(BDS)\bin\Artwork\Windows\UWP\delphi_UwpDefault_44.png + $(BDS)\bin\Artwork\Windows\UWP\delphi_UwpDefault_150.png + MARSTemplateServer_Icon.ico + + + + MainSource + + + +
ServerWebModule
+ TWebModule +
+ + + Cfg_2 + Base + + + Base + + + Cfg_1 + Base + +
+ + Delphi.Personality.12 + + + + + MARSTemplateServerDCSConsoleApplication.dpr + + + Microsoft Office 2000 Sample Automation Server Wrapper Components + Microsoft Office XP Sample Automation Server Wrapper Components + + + + False + False + False + False + True + True + True + False + + + + + Assets\ + Logo150x150.png + true + + + + + true + + + + + MARSTemplateServerDCSConsoleApplication + true + + + + + true + + + + + true + + + + + true + + + + + true + + + + + true + + + + + MARSTemplateServerDCSConsoleApplication.exe + true + + + + + Assets\ + Logo44x44.png + true + + + + + 1 + + + 0 + + + + + classes + 1 + + + classes + 1 + + + + + res\xml + 1 + + + res\xml + 1 + + + + + library\lib\armeabi-v7a + 1 + + + + + library\lib\armeabi + 1 + + + library\lib\armeabi + 1 + + + + + library\lib\armeabi-v7a + 1 + + + + + library\lib\mips + 1 + + + library\lib\mips + 1 + + + + + library\lib\armeabi-v7a + 1 + + + library\lib\arm64-v8a + 1 + + + + + library\lib\armeabi-v7a + 1 + + + + + res\drawable + 1 + + + res\drawable + 1 + + + + + res\values + 1 + + + res\values + 1 + + + + + res\values-v21 + 1 + + + res\values-v21 + 1 + + + + + res\values + 1 + + + res\values + 1 + + + + + res\drawable + 1 + + + res\drawable + 1 + + + + + res\drawable-xxhdpi + 1 + + + res\drawable-xxhdpi + 1 + + + + + res\drawable-ldpi + 1 + + + res\drawable-ldpi + 1 + + + + + res\drawable-mdpi + 1 + + + res\drawable-mdpi + 1 + + + + + res\drawable-hdpi + 1 + + + res\drawable-hdpi + 1 + + + + + res\drawable-xhdpi + 1 + + + res\drawable-xhdpi + 1 + + + + + res\drawable-mdpi + 1 + + + res\drawable-mdpi + 1 + + + + + res\drawable-hdpi + 1 + + + res\drawable-hdpi + 1 + + + + + res\drawable-xhdpi + 1 + + + res\drawable-xhdpi + 1 + + + + + res\drawable-xxhdpi + 1 + + + res\drawable-xxhdpi + 1 + + + + + res\drawable-xxxhdpi + 1 + + + res\drawable-xxxhdpi + 1 + + + + + res\drawable-small + 1 + + + res\drawable-small + 1 + + + + + res\drawable-normal + 1 + + + res\drawable-normal + 1 + + + + + res\drawable-large + 1 + + + res\drawable-large + 1 + + + + + res\drawable-xlarge + 1 + + + res\drawable-xlarge + 1 + + + + + res\values + 1 + + + res\values + 1 + + + + + 1 + + + 1 + + + 0 + + + + + 1 + .framework + + + 1 + .framework + + + 0 + + + + + 1 + .dylib + + + 1 + .dylib + + + 0 + .dll;.bpl + + + + + 1 + .dylib + + + 1 + .dylib + + + 1 + .dylib + + + 1 + .dylib + + + 1 + .dylib + + + 0 + .bpl + + + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + ..\$(PROJECTNAME).launchscreen\Assets\LaunchScreenImage.imageset + 1 + + + ..\$(PROJECTNAME).launchscreen\Assets\LaunchScreenImage.imageset + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + ..\$(PROJECTNAME).launchscreen\Assets\LaunchScreenImage.imageset + 1 + + + ..\$(PROJECTNAME).launchscreen\Assets\LaunchScreenImage.imageset + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + ..\$(PROJECTNAME).launchscreen\Assets\LaunchScreenImage.imageset + 1 + + + ..\$(PROJECTNAME).launchscreen\Assets\LaunchScreenImage.imageset + 1 + + + + + 1 + + + 1 + + + 1 + + + + + ..\$(PROJECTNAME).launchscreen\Assets\LaunchScreenImage.imageset + 1 + + + ..\$(PROJECTNAME).launchscreen\Assets\LaunchScreenImage.imageset + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + ..\$(PROJECTNAME).launchscreen\Assets\LaunchScreenImage.imageset + 1 + + + ..\$(PROJECTNAME).launchscreen\Assets\LaunchScreenImage.imageset + 1 + + + + + ..\$(PROJECTNAME).launchscreen\Assets\LaunchScreenImage.imageset + 1 + + + ..\$(PROJECTNAME).launchscreen\Assets\LaunchScreenImage.imageset + 1 + + + + + 1 + + + 1 + + + + + ..\$(PROJECTNAME).app.dSYM\Contents\Resources\DWARF + 1 + + + ..\$(PROJECTNAME).app.dSYM\Contents\Resources\DWARF + 1 + + + + + + + + + 1 + + + 1 + + + 1 + + + + + + + + Contents\Resources + 1 + + + Contents\Resources + 1 + + + + + library\lib\armeabi-v7a + 1 + + + library\lib\arm64-v8a + 1 + + + 1 + + + 1 + + + 1 + + + 1 + + + 1 + + + 1 + + + 0 + + + + + library\lib\armeabi-v7a + 1 + + + + + 1 + + + 1 + + + + + Assets + 1 + + + Assets + 1 + + + + + Assets + 1 + + + Assets + 1 + + + + + + + + + + + + + + + 12 + + + + +
diff --git a/Demos/MARSTemplateDCS/MARSTemplateServerDCSDaemon.dpr b/Demos/MARSTemplateDCS/MARSTemplateServerDCSDaemon.dpr new file mode 100644 index 00000000..7e459dd8 --- /dev/null +++ b/Demos/MARSTemplateDCS/MARSTemplateServerDCSDaemon.dpr @@ -0,0 +1,19 @@ +program MARSTemplateServerDCSDaemon; + +{$APPTYPE CONSOLE} + +{$R *.res} + +uses + Classes, + SysUtils, + {$ENDIF } + Server.Ignition in 'Server.Ignition.pas', + Server.WebModule in 'Server.WebModule.pas' {ServerWebModule: TWebModule}; + +begin + {$IFDEF LINUX} + TMARSDaemon.Current.Name := 'MARSTemplateServerDaemon'; + TMARSDaemon.Current.Start; + {$ENDIF} +end. diff --git a/Demos/MARSTemplateDCS/MARSTemplateServerDCSDaemon.dproj b/Demos/MARSTemplateDCS/MARSTemplateServerDCSDaemon.dproj new file mode 100644 index 00000000..7bea564e --- /dev/null +++ b/Demos/MARSTemplateDCS/MARSTemplateServerDCSDaemon.dproj @@ -0,0 +1,1211 @@ + + + {9D225C2C-24C2-48B4-8ABE-52C04AF576AE} + 19.0 + None + MARSTemplateServerDCSDaemon.dpr + True + Debug + Linux64 + 128 + Console + + + true + + + true + Base + true + + + true + Base + true + + + true + Base + true + + + true + Base + true + + + true + Base + true + + + true + Base + true + + + true + Base + true + + + true + Base + true + + + true + Base + true + + + true + Cfg_1 + true + true + + + true + Cfg_1 + true + true + + + true + Base + true + + + .\lib\$(Platform)\$(Config) + .\bin + false + false + false + false + false + RESTComponents;emsclientfiredac;DataSnapFireDAC;FireDACIBDriver;emsclient;FireDACCommon;RESTBackendComponents;soapserver;CloudService;FireDACCommonDriver;inet;FireDAC;FireDACSqliteDriver;soaprtl;soapmidas;$(DCC_UsePackage) + System;Xml;Data;Datasnap;Web;Soap;$(DCC_Namespace) + MARSTemplateServerDCSDaemon + + + DBXSqliteDriver;DBXInterBaseDriver;tethering;bindcompfmx;FmxTeeUI;fmx;RadiantShapesFmx;FireDACDBXDriver;dbexpress;IndyCore;dsnap;DataSnapCommon;bindengine;DataSnapClient;bindcompdbx;IndyIPCommon;IndyIPServer;IndySystem;fmxFireDAC;ibmonitor;FMXTee;DbxCommonDriver;ibxpress;xmlrtl;DataSnapNativeClient;ibxbindings;rtl;FireDACDSDriver;DbxClientDriver;CustomIPTransport;bindcomp;IndyIPClient;dbxcds;dsnapxml;DataSnapProviderClient;dbrtl;IndyProtocols;$(DCC_UsePackage) + $(BDS)\bin\Artwork\Android\FM_LauncherIcon_36x36.png + $(BDS)\bin\Artwork\Android\FM_LauncherIcon_48x48.png + $(BDS)\bin\Artwork\Android\FM_LauncherIcon_72x72.png + $(BDS)\bin\Artwork\Android\FM_LauncherIcon_96x96.png + $(BDS)\bin\Artwork\Android\FM_LauncherIcon_144x144.png + $(BDS)\bin\Artwork\Android\FM_SplashImage_426x320.png + $(BDS)\bin\Artwork\Android\FM_SplashImage_470x320.png + $(BDS)\bin\Artwork\Android\FM_SplashImage_640x480.png + $(BDS)\bin\Artwork\Android\FM_SplashImage_960x720.png + android-support-v4.dex.jar;cloud-messaging.dex.jar;fmx.dex.jar;google-analytics-v2.dex.jar;google-play-billing.dex.jar;google-play-licensing.dex.jar;google-play-services.dex.jar + $(BDS)\bin\Artwork\Android\FM_NotificationIcon_24x24.png + $(BDS)\bin\Artwork\Android\FM_NotificationIcon_36x36.png + $(BDS)\bin\Artwork\Android\FM_NotificationIcon_48x48.png + $(BDS)\bin\Artwork\Android\FM_NotificationIcon_72x72.png + $(BDS)\bin\Artwork\Android\FM_NotificationIcon_96x96.png + + + package=com.embarcadero.$(MSBuildProjectName);label=$(MSBuildProjectName);versionCode=1;versionName=1.0.0;persistent=False;restoreAnyVersion=False;installLocation=auto;largeHeap=False;theme=TitleBar;hardwareAccelerated=true;apiKey= + Debug + true + Base + true + DBXSqliteDriver;DBXInterBaseDriver;tethering;bindcompfmx;FmxTeeUI;fmx;RadiantShapesFmx;FireDACDBXDriver;dbexpress;IndyCore;dsnap;DataSnapCommon;bindengine;DataSnapClient;bindcompdbx;IndyIPCommon;IndyIPServer;IndySystem;fmxFireDAC;ibmonitor;FMXTee;DbxCommonDriver;ibxpress;xmlrtl;DataSnapNativeClient;ibxbindings;rtl;FireDACDSDriver;DbxClientDriver;CustomIPTransport;bindcomp;IndyIPClient;dbxcds;dsnapxml;DataSnapProviderClient;dbrtl;IndyProtocols;$(DCC_UsePackage);$(DCC_UsePackage) + $(BDS)\bin\Artwork\Android\FM_LauncherIcon_36x36.png + $(BDS)\bin\Artwork\Android\FM_LauncherIcon_48x48.png + $(BDS)\bin\Artwork\Android\FM_LauncherIcon_72x72.png + $(BDS)\bin\Artwork\Android\FM_LauncherIcon_96x96.png + $(BDS)\bin\Artwork\Android\FM_LauncherIcon_144x144.png + $(BDS)\bin\Artwork\Android\FM_SplashImage_426x320.png + $(BDS)\bin\Artwork\Android\FM_SplashImage_470x320.png + $(BDS)\bin\Artwork\Android\FM_SplashImage_640x480.png + $(BDS)\bin\Artwork\Android\FM_SplashImage_960x720.png + android-support-v4.dex.jar;cloud-messaging.dex.jar;fmx.dex.jar;google-analytics-v2.dex.jar;google-play-billing.dex.jar;google-play-licensing.dex.jar;google-play-services.dex.jar + + + DBXSqliteDriver;DBXInterBaseDriver;tethering;bindcompfmx;FmxTeeUI;fmx;RadiantShapesFmx;FireDACDBXDriver;dbexpress;IndyCore;dsnap;DataSnapCommon;bindengine;DataSnapClient;bindcompdbx;IndyIPCommon;IndyIPServer;IndySystem;fmxFireDAC;ibmonitor;FMXTee;DbxCommonDriver;ibxpress;xmlrtl;DataSnapNativeClient;ibxbindings;rtl;FireDACDSDriver;DbxClientDriver;CustomIPTransport;bindcomp;IndyIPClient;dbxcds;dsnapxml;DataSnapProviderClient;dbrtl;IndyProtocols;fmxase;$(DCC_UsePackage) + + + DBXSqliteDriver;DBXInterBaseDriver;tethering;bindcompfmx;FmxTeeUI;fmx;RadiantShapesFmx;FireDACDBXDriver;dbexpress;IndyCore;dsnap;DataSnapCommon;bindengine;DataSnapClient;bindcompdbx;IndyIPCommon;IndyIPServer;IndySystem;fmxFireDAC;ibmonitor;FMXTee;DbxCommonDriver;ibxpress;xmlrtl;DataSnapNativeClient;ibxbindings;rtl;FireDACDSDriver;DbxClientDriver;CustomIPTransport;bindcomp;IndyIPClient;dbxcds;dsnapxml;DataSnapProviderClient;dbrtl;IndyProtocols;fmxase;$(DCC_UsePackage) + + + DataSnapServerMidas;FireDACADSDriver;DatasnapConnectorsFreePascal;FireDACMSSQLDriver;inetdb;emsedge;dbexpress;IndyCore;dsnap;DataSnapCommon;DataSnapConnectors;MARS.Utils;bindengine;FireDACOracleDriver;FireDACMySQLDriver;FireDACCommonODBC;DataSnapClient;IndySystem;FireDACDb2Driver;FireDACInfxDriver;emshosting;FireDACPgDriver;FireDACASADriver;FireDACTDataDriver;DbxCommonDriver;DataSnapServer;xmlrtl;DataSnapNativeClient;rtl;DbxClientDriver;CustomIPTransport;bindcomp;dbxcds;FireDACODBCDriver;DataSnapIndy10ServerTransport;dsnapxml;dbrtl;FireDACMongoDBDriver;IndyProtocols;$(DCC_UsePackage) + /usr/bin/xterm -e "%debuggee%" + MARSTemplateServer_Icon.ico + (None) + + + CFBundleName=$(MSBuildProjectName);CFBundleDisplayName=$(MSBuildProjectName);CFBundleIdentifier=$(MSBuildProjectName);CFBundleVersion=1.0.0;CFBundleShortVersionString=1.0.0;CFBundlePackageType=APPL;CFBundleSignature=????;CFBundleAllowMixedLocalizations=YES;CFBundleExecutable=$(MSBuildProjectName);NSHighResolutionCapable=true;LSApplicationCategoryType=public.app-category.utilities;NSLocationUsageDescription=The reason for accessing the location information of the user;NSContactsUsageDescription=The reason for accessing the contacts + Debug + true + Base + true + DBXSqliteDriver;DataSnapServerMidas;DBXInterBaseDriver;tethering;FireDACMSSQLDriver;bindcompfmx;DBXOracleDriver;inetdb;FmxTeeUI;emsedge;fmx;fmxdae;RadiantShapesFmx;FireDACDBXDriver;dbexpress;IndyCore;dsnap;DataSnapCommon;bindengine;DBXMySQLDriver;FireDACOracleDriver;FireDACMySQLDriver;DBXFirebirdDriver;FireDACCommonODBC;DataSnapClient;bindcompdbx;IndyIPCommon;IndyIPServer;IndySystem;fmxFireDAC;emshosting;FireDACPgDriver;ibmonitor;FireDACASADriver;FireDACTDataDriver;FMXTee;DbxCommonDriver;ibxpress;DataSnapServer;xmlrtl;DataSnapNativeClient;fmxobj;ibxbindings;rtl;FireDACDSDriver;DbxClientDriver;DBXSybaseASADriver;CustomIPTransport;bindcomp;DBXInformixDriver;IndyIPClient;dbxcds;FireDACODBCDriver;DataSnapIndy10ServerTransport;dsnapxml;DataSnapProviderClient;dbrtl;inetdbxpress;FireDACMongoDBDriver;IndyProtocols;fmxase;$(DCC_UsePackage);$(DCC_UsePackage) + true + + + DBXSqliteDriver;DataSnapServerMidas;DBXDb2Driver;DBXInterBaseDriver;vclactnband;vclFireDAC;svnui;tethering;FireDACADSDriver;DBXMSSQLDriver;DatasnapConnectorsFreePascal;FireDACMSSQLDriver;vcltouch;vcldb;bindcompfmx;svn;Intraweb;DBXOracleDriver;inetdb;FmxTeeUI;emsedge;fmx;fmxdae;RadiantShapesFmx;vclib;FireDACDBXDriver;dbexpress;IndyCore;vclx;dsnap;DataSnapCommon;DataSnapConnectors;MARS.Utils;VCLRESTComponents;vclie;bindengine;DBXMySQLDriver;FireDACOracleDriver;FireDACMySQLDriver;DBXFirebirdDriver;FireDACCommonODBC;DataSnapClient;bindcompdbx;IndyIPCommon;vcl;DBXSybaseASEDriver;IndyIPServer;IndySystem;FireDACDb2Driver;dsnapcon;FireDACMSAccDriver;fmxFireDAC;FireDACInfxDriver;vclimg;TeeDB;emshosting;FireDACPgDriver;ibmonitor;FireDACASADriver;DBXOdbcDriver;FireDACTDataDriver;FMXTee;DbxCommonDriver;ibxpress;Tee;DataSnapServer;xmlrtl;DataSnapNativeClient;fmxobj;vclwinx;ibxbindings;rtl;FireDACDSDriver;DbxClientDriver;DBXSybaseASADriver;CustomIPTransport;vcldsnap;CodeSiteExpressPkg;bindcomp;appanalytics;DBXInformixDriver;IndyIPClient;bindcompvcl;TeeUI;dbxcds;VclSmp;adortl;FireDACODBCDriver;RadiantShapesFmx_Design;DataSnapIndy10ServerTransport;dsnapxml;DataSnapProviderClient;dbrtl;inetdbxpress;FireDACMongoDBDriver;IndyProtocols;fmxase;$(DCC_UsePackage) + Winapi;System.Win;Data.Win;Datasnap.Win;Web.Win;Soap.Win;Xml.Win;Bde;$(DCC_Namespace) + Debug + CompanyName=;FileDescription=$(MSBuildProjectName);FileVersion=1.0.0.0;InternalName=;LegalCopyright=;LegalTrademarks=;OriginalFilename=;ProgramID=com.embarcadero.$(MSBuildProjectName);ProductName=$(MSBuildProjectName);ProductVersion=1.0.0.0;Comments= + 1033 + true + $(BDS)\bin\Artwork\Windows\UWP\delphi_UwpDefault_44.png + $(BDS)\bin\Artwork\Windows\UWP\delphi_UwpDefault_150.png + + + DBXSqliteDriver;DataSnapServerMidas;DBXDb2Driver;DBXInterBaseDriver;vclactnband;vclFireDAC;tethering;FireDACADSDriver;DBXMSSQLDriver;DatasnapConnectorsFreePascal;FireDACMSSQLDriver;vcltouch;vcldb;bindcompfmx;Intraweb;DBXOracleDriver;inetdb;FmxTeeUI;emsedge;fmx;fmxdae;RadiantShapesFmx;vclib;FireDACDBXDriver;dbexpress;IndyCore;vclx;dsnap;DataSnapCommon;DataSnapConnectors;MARS.Utils;VCLRESTComponents;vclie;bindengine;DBXMySQLDriver;FireDACOracleDriver;FireDACMySQLDriver;DBXFirebirdDriver;FireDACCommonODBC;DataSnapClient;bindcompdbx;IndyIPCommon;vcl;DBXSybaseASEDriver;IndyIPServer;IndySystem;FireDACDb2Driver;dsnapcon;FireDACMSAccDriver;fmxFireDAC;FireDACInfxDriver;vclimg;TeeDB;emshosting;FireDACPgDriver;ibmonitor;FireDACASADriver;DBXOdbcDriver;FireDACTDataDriver;FMXTee;DbxCommonDriver;ibxpress;Tee;DataSnapServer;xmlrtl;DataSnapNativeClient;fmxobj;vclwinx;ibxbindings;rtl;FireDACDSDriver;DbxClientDriver;DBXSybaseASADriver;CustomIPTransport;vcldsnap;bindcomp;appanalytics;DBXInformixDriver;IndyIPClient;bindcompvcl;TeeUI;dbxcds;VclSmp;adortl;FireDACODBCDriver;DataSnapIndy10ServerTransport;dsnapxml;DataSnapProviderClient;dbrtl;inetdbxpress;FireDACMongoDBDriver;IndyProtocols;fmxase;$(DCC_UsePackage) + true + $(BDS)\bin\Artwork\Windows\UWP\delphi_UwpDefault_44.png + $(BDS)\bin\Artwork\Windows\UWP\delphi_UwpDefault_150.png + + + DEBUG;$(DCC_Define) + true + false + true + true + true + + + /usr/bin/xterm -e "%debuggee%" + + + false + + + false + RELEASE;$(DCC_Define) + 0 + 0 + + + + MainSource + + + +
ServerWebModule
+ TWebModule +
+ + Cfg_2 + Base + + + Base + + + Cfg_1 + Base + +
+ + Delphi.Personality.12 + Application + + + + MARSTemplateServerDCSDaemon.dpr + + + Microsoft Office 2000 Sample Automation Server Wrapper Components + Microsoft Office XP Sample Automation Server Wrapper Components + + + + + + true + + + + + MARSTemplateServerDCSDaemon + true + + + + + true + + + + + true + + + + + true + + + + + true + + + + + true + + + + + 1 + + + Contents\MacOS + 1 + + + 0 + + + + + classes + 1 + + + classes + 1 + + + + + res\xml + 1 + + + res\xml + 1 + + + + + library\lib\armeabi-v7a + 1 + + + + + library\lib\armeabi + 1 + + + library\lib\armeabi + 1 + + + + + library\lib\armeabi-v7a + 1 + + + + + library\lib\mips + 1 + + + library\lib\mips + 1 + + + + + library\lib\armeabi-v7a + 1 + + + library\lib\arm64-v8a + 1 + + + + + library\lib\armeabi-v7a + 1 + + + + + res\drawable + 1 + + + res\drawable + 1 + + + + + res\values + 1 + + + res\values + 1 + + + + + res\values-v21 + 1 + + + res\values-v21 + 1 + + + + + res\values + 1 + + + res\values + 1 + + + + + res\drawable + 1 + + + res\drawable + 1 + + + + + res\drawable-xxhdpi + 1 + + + res\drawable-xxhdpi + 1 + + + + + res\drawable-ldpi + 1 + + + res\drawable-ldpi + 1 + + + + + res\drawable-mdpi + 1 + + + res\drawable-mdpi + 1 + + + + + res\drawable-hdpi + 1 + + + res\drawable-hdpi + 1 + + + + + res\drawable-xhdpi + 1 + + + res\drawable-xhdpi + 1 + + + + + res\drawable-mdpi + 1 + + + res\drawable-mdpi + 1 + + + + + res\drawable-hdpi + 1 + + + res\drawable-hdpi + 1 + + + + + res\drawable-xhdpi + 1 + + + res\drawable-xhdpi + 1 + + + + + res\drawable-xxhdpi + 1 + + + res\drawable-xxhdpi + 1 + + + + + res\drawable-xxxhdpi + 1 + + + res\drawable-xxxhdpi + 1 + + + + + res\drawable-small + 1 + + + res\drawable-small + 1 + + + + + res\drawable-normal + 1 + + + res\drawable-normal + 1 + + + + + res\drawable-large + 1 + + + res\drawable-large + 1 + + + + + res\drawable-xlarge + 1 + + + res\drawable-xlarge + 1 + + + + + res\values + 1 + + + res\values + 1 + + + + + 1 + + + Contents\MacOS + 1 + + + 0 + + + + + Contents\MacOS + 1 + .framework + + + Contents\MacOS + 1 + .framework + + + 0 + + + + + 1 + .dylib + + + 1 + .dylib + + + 1 + .dylib + + + Contents\MacOS + 1 + .dylib + + + Contents\MacOS + 1 + .dylib + + + 0 + .dll;.bpl + + + + + 1 + .dylib + + + 1 + .dylib + + + 1 + .dylib + + + Contents\MacOS + 1 + .dylib + + + Contents\MacOS + 1 + .dylib + + + 0 + .bpl + + + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + Contents\Resources\StartUp\ + 0 + + + Contents\Resources\StartUp\ + 0 + + + 0 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + ..\$(PROJECTNAME).launchscreen\Assets\LaunchScreenImage.imageset + 1 + + + ..\$(PROJECTNAME).launchscreen\Assets\LaunchScreenImage.imageset + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + ..\$(PROJECTNAME).launchscreen\Assets\LaunchScreenImage.imageset + 1 + + + ..\$(PROJECTNAME).launchscreen\Assets\LaunchScreenImage.imageset + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + ..\$(PROJECTNAME).launchscreen\Assets\LaunchScreenImage.imageset + 1 + + + ..\$(PROJECTNAME).launchscreen\Assets\LaunchScreenImage.imageset + 1 + + + + + 1 + + + 1 + + + 1 + + + + + ..\$(PROJECTNAME).launchscreen\Assets\LaunchScreenImage.imageset + 1 + + + ..\$(PROJECTNAME).launchscreen\Assets\LaunchScreenImage.imageset + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + ..\$(PROJECTNAME).launchscreen\Assets\LaunchScreenImage.imageset + 1 + + + ..\$(PROJECTNAME).launchscreen\Assets\LaunchScreenImage.imageset + 1 + + + + + ..\$(PROJECTNAME).launchscreen\Assets\LaunchScreenImage.imageset + 1 + + + ..\$(PROJECTNAME).launchscreen\Assets\LaunchScreenImage.imageset + 1 + + + + + 1 + + + 1 + + + + + ..\$(PROJECTNAME).app.dSYM\Contents\Resources\DWARF + 1 + + + ..\$(PROJECTNAME).app.dSYM\Contents\Resources\DWARF + 1 + + + + + 1 + + + 1 + + + + + ..\ + 1 + + + ..\ + 1 + + + + + 1 + + + 1 + + + 1 + + + + + ..\$(PROJECTNAME).launchscreen + 64 + + + ..\$(PROJECTNAME).launchscreen + 64 + + + + + 1 + + + 1 + + + 1 + + + + + ..\$(PROJECTNAME).app.dSYM\Contents\Resources\DWARF + 1 + + + + + ..\ + 1 + + + ..\ + 1 + + + + + Contents + 1 + + + Contents + 1 + + + + + Contents\Resources + 1 + + + Contents\Resources + 1 + + + + + library\lib\armeabi-v7a + 1 + + + library\lib\arm64-v8a + 1 + + + 1 + + + 1 + + + 1 + + + 1 + + + Contents\MacOS + 1 + + + Contents\MacOS + 1 + + + 0 + + + + + library\lib\armeabi-v7a + 1 + + + + + 1 + + + 1 + + + + + Assets + 1 + + + Assets + 1 + + + + + Assets + 1 + + + Assets + 1 + + + + + + + + + + + + + + + False + False + False + False + True + False + False + False + + + 12 + + + + +
diff --git a/Demos/MARSTemplateDCS/MARSTemplateServerDCSFMXApplication.dpr b/Demos/MARSTemplateDCS/MARSTemplateServerDCSFMXApplication.dpr new file mode 100644 index 00000000..332b72ad --- /dev/null +++ b/Demos/MARSTemplateDCS/MARSTemplateServerDCSFMXApplication.dpr @@ -0,0 +1,21 @@ +(* + Copyright 2016, MARS-Curiosity - REST Library + + Home: https://github.com/andrea-magni/MARS +*) +program MARSTemplateServerDCSFMXApplication; + +uses + System.StartUpCopy, + FMX.Forms, + Server.FMX.Forms.Main in 'Server.FMX.Forms.Main.pas' {MainForm}, + Server.Ignition in 'Server.Ignition.pas', + Server.Resources in 'Server.Resources.pas'; + +{$R *.res} + +begin + Application.Initialize; + Application.CreateForm(TMainForm, MainForm); + Application.Run; +end. diff --git a/Demos/MARSTemplateDCS/MARSTemplateServerDCSFMXApplication.dproj b/Demos/MARSTemplateDCS/MARSTemplateServerDCSFMXApplication.dproj new file mode 100644 index 00000000..0785c5bb --- /dev/null +++ b/Demos/MARSTemplateDCS/MARSTemplateServerDCSFMXApplication.dproj @@ -0,0 +1,1354 @@ + + + {2DC28130-9EB1-48C8-9CA6-0F3CFD5D8E1D} + MARSTemplateServerDCSFMXApplication.dpr + True + Release + 37905 + Application + FMX + 19.0 + Win32 + + + true + + + true + Base + true + + + true + Base + true + + + true + Base + true + + + true + Base + true + + + true + Base + true + + + true + Base + true + + + true + Base + true + + + true + Base + true + + + true + Cfg_1 + true + true + + + true + Cfg_1 + true + true + + + true + Cfg_1 + true + true + + + true + Base + true + + + true + Cfg_2 + true + true + + + true + Cfg_2 + true + true + + + true + Cfg_2 + true + true + + + true + Cfg_2 + true + true + + + false + false + false + false + false + 00400000 + MARSTemplateServerDCSFMXApplication + 1040 + CompanyName=;FileDescription=;FileVersion=1.0.0.0;InternalName=;LegalCopyright=;LegalTrademarks=;OriginalFilename=;ProductName=;ProductVersion=1.0.0.0;Comments=;CFBundleName= + System;Xml;Data;Datasnap;Web;Soap;$(DCC_Namespace) + $(BDS)\bin\delphi_PROJECTICON.ico + $(BDS)\bin\delphi_PROJECTICNS.icns + .\bin + .\lib\$(Platform)\$(Config) + + + package=com.embarcadero.$(MSBuildProjectName);label=$(MSBuildProjectName);versionCode=1;versionName=1.0.0;persistent=False;restoreAnyVersion=False;installLocation=auto;largeHeap=False;theme=TitleBar;hardwareAccelerated=true;apiKey= + Debug + true + $(BDS)\bin\Artwork\Android\FM_LauncherIcon_36x36.png + $(BDS)\bin\Artwork\Android\FM_LauncherIcon_48x48.png + $(BDS)\bin\Artwork\Android\FM_LauncherIcon_72x72.png + $(BDS)\bin\Artwork\Android\FM_LauncherIcon_96x96.png + $(BDS)\bin\Artwork\Android\FM_LauncherIcon_144x144.png + $(BDS)\bin\Artwork\Android\FM_SplashImage_426x320.png + $(BDS)\bin\Artwork\Android\FM_SplashImage_470x320.png + $(BDS)\bin\Artwork\Android\FM_SplashImage_640x480.png + $(BDS)\bin\Artwork\Android\FM_SplashImage_960x720.png + true + true + true + true + true + true + true + true + true + true + android-support-v4.dex.jar;cloud-messaging.dex.jar;fmx.dex.jar;google-analytics-v2.dex.jar;google-play-billing.dex.jar;google-play-licensing.dex.jar;google-play-services.dex.jar + 1 + $(BDS)\bin\Artwork\Android\FM_NotificationIcon_24x24.png + $(BDS)\bin\Artwork\Android\FM_NotificationIcon_36x36.png + $(BDS)\bin\Artwork\Android\FM_NotificationIcon_48x48.png + $(BDS)\bin\Artwork\Android\FM_NotificationIcon_72x72.png + $(BDS)\bin\Artwork\Android\FM_NotificationIcon_96x96.png + + + package=com.embarcadero.$(MSBuildProjectName);label=$(MSBuildProjectName);versionCode=1;versionName=1.0.0;persistent=False;restoreAnyVersion=False;installLocation=auto;largeHeap=False;theme=TitleBar;hardwareAccelerated=true;apiKey= + Debug + true + true + Base + true + $(BDS)\bin\Artwork\Android\FM_LauncherIcon_36x36.png + $(BDS)\bin\Artwork\Android\FM_LauncherIcon_48x48.png + $(BDS)\bin\Artwork\Android\FM_LauncherIcon_72x72.png + $(BDS)\bin\Artwork\Android\FM_LauncherIcon_96x96.png + $(BDS)\bin\Artwork\Android\FM_LauncherIcon_144x144.png + $(BDS)\bin\Artwork\Android\FM_SplashImage_426x320.png + $(BDS)\bin\Artwork\Android\FM_SplashImage_470x320.png + $(BDS)\bin\Artwork\Android\FM_SplashImage_640x480.png + $(BDS)\bin\Artwork\Android\FM_SplashImage_960x720.png + true + true + true + true + true + true + true + true + true + true + android-support-v4.dex.jar;cloud-messaging.dex.jar;fmx.dex.jar;google-analytics-v2.dex.jar;google-play-billing.dex.jar;google-play-licensing.dex.jar;google-play-services.dex.jar + 1 + + + $(BDS)\bin\Artwork\iOS\iPhone\FM_ApplicationIcon_87x87.png + $(BDS)\bin\Artwork\iOS\iPhone\FM_ApplicationIcon_180x180.png + $(BDS)\bin\Artwork\iOS\iPhone\FM_LaunchImage_750x1334.png + $(BDS)\bin\Artwork\iOS\iPhone\FM_LaunchImage_1242x2208.png + $(BDS)\bin\Artwork\iOS\iPhone\FM_LaunchImage_2208x1242.png + $(BDS)\bin\Artwork\iOS\iPhone\FM_LaunchImage_1125x2436.png + $(BDS)\bin\Artwork\iOS\iPhone\FM_LaunchImage_2436x1125.png + $(BDS)\bin\Artwork\iOS\iPhone\FM_SpotlightSearchIcon_120x120.png + $(BDS)\bin\Artwork\iOS\iPhone\FM_LaunchImage_828x1792.png + $(BDS)\bin\Artwork\iOS\iPhone\FM_LaunchImage_1136x640.png + $(BDS)\bin\Artwork\iOS\iPhone\FM_LaunchImage_1242x2688.png + $(BDS)\bin\Artwork\iOS\iPhone\FM_LaunchImage_1334x750.png + $(BDS)\bin\Artwork\iOS\iPhone\FM_LaunchImage_1792x828.png + $(BDS)\bin\Artwork\iOS\iPhone\FM_LaunchImage_2688x1242.png + $(BDS)\bin\Artwork\iOS\iPad\FM_ApplicationIcon_167x167.png + $(BDS)\bin\Artwork\iOS\iPad\FM_LaunchImagePortrait_1668x2224.png + $(BDS)\bin\Artwork\iOS\iPad\FM_LaunchImagePortrait_1668x2388.png + $(BDS)\bin\Artwork\iOS\iPad\FM_LaunchImagePortrait_2048x2732.png + $(BDS)\bin\Artwork\iOS\iPad\FM_LaunchImageLandscape_2224x1668.png + $(BDS)\bin\Artwork\iOS\iPad\FM_LaunchImageLandscape_2388x1668.png + $(BDS)\bin\Artwork\iOS\iPad\FM_LaunchImageLandscape_2732x2048.png + $(BDS)\bin\Artwork\iOS\iPhone\FM_LaunchImage_2x.png + $(BDS)\bin\Artwork\iOS\iPhone\FM_LaunchImageDark_2x.png + $(BDS)\bin\Artwork\iOS\iPhone\FM_LaunchImage_3x.png + $(BDS)\bin\Artwork\iOS\iPhone\FM_LaunchImageDark_3x.png + $(BDS)\bin\Artwork\iOS\iPad\FM_LaunchImage_2x.png + $(BDS)\bin\Artwork\iOS\iPad\FM_LaunchImageDark_2x.png + + + CFBundleName=$(MSBuildProjectName);CFBundleDevelopmentRegion=en;CFBundleDisplayName=$(MSBuildProjectName);CFBundleIdentifier=$(MSBuildProjectName);CFBundleInfoDictionaryVersion=7.1;CFBundleVersion=1.0.0.0;CFBundlePackageType=APPL;CFBundleSignature=????;LSRequiresIPhoneOS=true;CFBundleAllowMixedLocalizations=YES;CFBundleExecutable=$(MSBuildProjectName);UIDeviceFamily=iPhone & iPad;CFBundleResourceSpecification=ResourceRules.plist;NSLocationAlwaysUsageDescription=The reason for accessing the location information of the user;NSLocationWhenInUseUsageDescription=The reason for accessing the location information of the user;FMLocalNotificationPermission=false;UIBackgroundModes=;NSContactsUsageDescription=The reason for accessing the contacts;NSPhotoLibraryUsageDescription=The reason for accessing the photo library;NSCameraUsageDescription=The reason for accessing the camera + iPhoneAndiPad + true + $(BDS)\bin\Artwork\iOS\iPhone\FM_ApplicationIcon_60x60.png + $(BDS)\bin\Artwork\iOS\iPhone\FM_ApplicationIcon_120x120.png + $(BDS)\bin\Artwork\iOS\iPhone\FM_SpotlightSearchIcon_40x40.png + $(BDS)\bin\Artwork\iOS\iPhone\FM_SpotlightSearchIcon_80x80.png + $(BDS)\bin\Artwork\iOS\iPad\FM_SpotlightSearchIcon_40x40.png + $(BDS)\bin\Artwork\iOS\iPad\FM_SpotlightSearchIcon_80x80.png + $(BDS)\bin\Artwork\iOS\iPad\FM_ApplicationIcon_76x76.png + $(BDS)\bin\Artwork\iOS\iPad\FM_ApplicationIcon_152x152.png + $(BDS)\bin\Artwork\iOS\iPad\FM_LaunchImagePortrait_768x1024.png + $(BDS)\bin\Artwork\iOS\iPad\FM_LaunchImageLandscape_1024x768.png + $(BDS)\bin\Artwork\iOS\iPad\FM_LaunchImagePortrait_1536x2048.png + $(BDS)\bin\Artwork\iOS\iPad\FM_LaunchImageLandscape_2048x1536.png + $(BDS)\bin\Artwork\iOS\iPhone\FM_ApplicationIcon_87x87.png + $(BDS)\bin\Artwork\iOS\iPhone\FM_ApplicationIcon_180x180.png + $(BDS)\bin\Artwork\iOS\iPhone\FM_LaunchImage_750x1334.png + $(BDS)\bin\Artwork\iOS\iPhone\FM_LaunchImage_1242x2208.png + $(BDS)\bin\Artwork\iOS\iPhone\FM_LaunchImage_2208x1242.png + $(BDS)\bin\Artwork\iOS\iPhone\FM_LaunchImage_1125x2436.png + $(BDS)\bin\Artwork\iOS\iPhone\FM_LaunchImage_2436x1125.png + $(BDS)\bin\Artwork\iOS\iPhone\FM_SpotlightSearchIcon_120x120.png + $(BDS)\bin\Artwork\iOS\iPhone\FM_LaunchImage_828x1792.png + $(BDS)\bin\Artwork\iOS\iPhone\FM_LaunchImage_1136x640.png + $(BDS)\bin\Artwork\iOS\iPhone\FM_LaunchImage_1242x2688.png + $(BDS)\bin\Artwork\iOS\iPhone\FM_LaunchImage_1334x750.png + $(BDS)\bin\Artwork\iOS\iPhone\FM_LaunchImage_1792x828.png + $(BDS)\bin\Artwork\iOS\iPhone\FM_LaunchImage_2688x1242.png + $(BDS)\bin\Artwork\iOS\iPad\FM_ApplicationIcon_167x167.png + $(BDS)\bin\Artwork\iOS\iPad\FM_LaunchImagePortrait_1668x2224.png + $(BDS)\bin\Artwork\iOS\iPad\FM_LaunchImagePortrait_1668x2388.png + $(BDS)\bin\Artwork\iOS\iPad\FM_LaunchImagePortrait_2048x2732.png + $(BDS)\bin\Artwork\iOS\iPad\FM_LaunchImageLandscape_2224x1668.png + $(BDS)\bin\Artwork\iOS\iPad\FM_LaunchImageLandscape_2388x1668.png + $(BDS)\bin\Artwork\iOS\iPad\FM_LaunchImageLandscape_2732x2048.png + + + CFBundleName=$(MSBuildProjectName);CFBundleDisplayName=$(MSBuildProjectName);CFBundleIdentifier=$(MSBuildProjectName);CFBundleVersion=1.0.0;CFBundlePackageType=APPL;CFBundleSignature=????;CFBundleAllowMixedLocalizations=YES;CFBundleExecutable=$(MSBuildProjectName);NSHighResolutionCapable=true;LSApplicationCategoryType=public.app-category.utilities;NSContactsUsageDescription=The reason for accessing the contacts;CFBundleShortVersionString=1.0.0;NSLocationUsageDescription=The reason for accessing the location information of the user;NSCalendarsUsageDescription=The reason for accessing the calendar data;NSRemindersUsageDescription=The reason for accessing the reminders;NSCameraUsageDescription=The reason for accessing the camera;NSMicrophoneUsageDescription=The reason for accessing the microphone;NSMotionUsageDescription=The reason for accessing the accelerometer;NSDesktopFolderUsageDescription=The reason for accessing the Desktop folder;NSDocumentsFolderUsageDescription=The reason for accessing the Documents folder;NSDownloadsFolderUsageDescription=The reason for accessing the Downloads folder;NSNetworkVolumesUsageDescription=The reason for accessing files on a network volume;NSRemovableVolumesUsageDescription=The reason for accessing files on a removable volume;NSSpeechRecognitionUsageDescription=The reason for requesting to send user data to Apple's speech recognition servers + Debug + true + true + Base + true + /usr/X11/bin/xterm -e "%debuggee%" + (None) + MARSTemplateServer_Icon.ico + + + Winapi;System.Win;Data.Win;Datasnap.Win;Web.Win;Soap.Win;Xml.Win;Bde;$(DCC_Namespace) + Debug + true + CompanyName=;FileDescription=$(MSBuildProjectName);FileVersion=1.0.0.0;InternalName=;LegalCopyright=;LegalTrademarks=;OriginalFilename=;ProductName=$(MSBuildProjectName);ProductVersion=1.0.0.0;Comments=;ProgramID=com.embarcadero.$(MSBuildProjectName) + 1033 + $(BDS)\bin\default_app.manifest + MARSTemplateServer_Icon.ico + true + $(BDS)\bin\Artwork\Windows\UWP\delphi_UwpDefault_44.png + $(BDS)\bin\Artwork\Windows\UWP\delphi_UwpDefault_150.png + + + $(BDS)\bin\default_app.manifest + MARSTemplateServerFMXApplication_Icon.ico + true + $(BDS)\bin\Artwork\Windows\UWP\delphi_UwpDefault_44.png + $(BDS)\bin\Artwork\Windows\UWP\delphi_UwpDefault_150.png + + + RELEASE;$(DCC_Define) + 0 + false + 0 + + + Debug + + + true + Cfg_1 + true + true + CFBundleName=$(MSBuildProjectName);CFBundleDisplayName=$(MSBuildProjectName);CFBundleIdentifier=$(MSBuildProjectName);CFBundleVersion=1.0.0;CFBundlePackageType=APPL;CFBundleSignature=????;CFBundleAllowMixedLocalizations=YES;CFBundleExecutable=$(MSBuildProjectName);NSHighResolutionCapable=true;LSApplicationCategoryType=public.app-category.utilities;NSContactsUsageDescription=The reason for accessing the contacts;CFBundleShortVersionString=1.0.0;NSLocationUsageDescription=The reason for accessing the location information of the user;NSCalendarsUsageDescription=The reason for accessing the calendar data;NSRemindersUsageDescription=The reason for accessing the reminders;NSCameraUsageDescription=The reason for accessing the camera;NSMicrophoneUsageDescription=The reason for accessing the microphone;NSMotionUsageDescription=The reason for accessing the accelerometer;NSDesktopFolderUsageDescription=The reason for accessing the Desktop folder;NSDocumentsFolderUsageDescription=The reason for accessing the Documents folder;NSDownloadsFolderUsageDescription=The reason for accessing the Downloads folder;NSNetworkVolumesUsageDescription=The reason for accessing files on a network volume;NSRemovableVolumesUsageDescription=The reason for accessing files on a removable volume;NSSpeechRecognitionUsageDescription=The reason for requesting to send user data to Apple's speech recognition servers + MARSTemplateServer_Icon.ico + + + true + true + 1033 + CompanyName=;FileDescription=$(MSBuildProjectName);FileVersion=1.0.0.0;InternalName=;LegalCopyright=;LegalTrademarks=;OriginalFilename=;ProductName=$(MSBuildProjectName);ProductVersion=1.0.0.0;Comments=;ProgramID=com.embarcadero.$(MSBuildProjectName) + MARSTemplateServer_Icon.ico + PerMonitor + + + DEBUG;$(DCC_Define) + false + true + + + Debug + + + true + + + true + true + Cfg_2 + true + MARSTemplateServer_Icon.ico + + + true + true + 1033 + CompanyName=;FileDescription=$(MSBuildProjectName);FileVersion=1.0.0.0;InternalName=;LegalCopyright=;LegalTrademarks=;OriginalFilename=;ProductName=$(MSBuildProjectName);ProductVersion=1.0.0.0;Comments=;ProgramID=com.embarcadero.$(MSBuildProjectName) + MARSTemplateServer_Icon.ico + PerMonitor + + + + MainSource + + +
MainForm
+
+ + + + Cfg_2 + Base + + + Base + + + Cfg_1 + Base + +
+ + Delphi.Personality.12 + + + + + MARSTemplateServerDCSFMXApplication.dpr + + + Microsoft Office 2000 Sample Automation Server Wrapper Components + Microsoft Office XP Sample Automation Server Wrapper Components + + + + True + True + True + False + True + True + False + + + + + true + + + + + true + + + + + true + + + + + true + + + + + true + + + + + true + + + + + MARSTemplateServerDCSFMXApplication.exe + true + + + + + 1 + + + Contents\MacOS + 1 + + + 0 + + + + + classes + 1 + + + classes + 1 + + + + + res\xml + 1 + + + res\xml + 1 + + + + + library\lib\armeabi-v7a + 1 + + + + + library\lib\armeabi + 1 + + + library\lib\armeabi + 1 + + + + + library\lib\armeabi-v7a + 1 + + + + + library\lib\mips + 1 + + + library\lib\mips + 1 + + + + + library\lib\armeabi-v7a + 1 + + + library\lib\arm64-v8a + 1 + + + + + library\lib\armeabi-v7a + 1 + + + + + res\drawable + 1 + + + res\drawable + 1 + + + + + res\values + 1 + + + res\values + 1 + + + + + res\values-v21 + 1 + + + res\values-v21 + 1 + + + + + res\values + 1 + + + res\values + 1 + + + + + res\drawable + 1 + + + res\drawable + 1 + + + + + res\drawable-xxhdpi + 1 + + + res\drawable-xxhdpi + 1 + + + + + res\drawable-ldpi + 1 + + + res\drawable-ldpi + 1 + + + + + res\drawable-mdpi + 1 + + + res\drawable-mdpi + 1 + + + + + res\drawable-hdpi + 1 + + + res\drawable-hdpi + 1 + + + + + res\drawable-xhdpi + 1 + + + res\drawable-xhdpi + 1 + + + + + res\drawable-mdpi + 1 + + + res\drawable-mdpi + 1 + + + + + res\drawable-hdpi + 1 + + + res\drawable-hdpi + 1 + + + + + res\drawable-xhdpi + 1 + + + res\drawable-xhdpi + 1 + + + + + res\drawable-xxhdpi + 1 + + + res\drawable-xxhdpi + 1 + + + + + res\drawable-xxxhdpi + 1 + + + res\drawable-xxxhdpi + 1 + + + + + res\drawable-small + 1 + + + res\drawable-small + 1 + + + + + res\drawable-normal + 1 + + + res\drawable-normal + 1 + + + + + res\drawable-large + 1 + + + res\drawable-large + 1 + + + + + res\drawable-xlarge + 1 + + + res\drawable-xlarge + 1 + + + + + res\values + 1 + + + res\values + 1 + + + + + 1 + + + Contents\MacOS + 1 + + + 0 + + + + + Contents\MacOS + 1 + .framework + + + Contents\MacOS + 1 + .framework + + + 0 + + + + + 1 + .dylib + + + 1 + .dylib + + + 1 + .dylib + + + Contents\MacOS + 1 + .dylib + + + Contents\MacOS + 1 + .dylib + + + 0 + .dll;.bpl + + + + + 1 + .dylib + + + 1 + .dylib + + + 1 + .dylib + + + Contents\MacOS + 1 + .dylib + + + Contents\MacOS + 1 + .dylib + + + 0 + .bpl + + + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + Contents\Resources\StartUp\ + 0 + + + Contents\Resources\StartUp\ + 0 + + + 0 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + ..\$(PROJECTNAME).launchscreen\Assets\LaunchScreenImage.imageset + 1 + + + ..\$(PROJECTNAME).launchscreen\Assets\LaunchScreenImage.imageset + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + ..\$(PROJECTNAME).launchscreen\Assets\LaunchScreenImage.imageset + 1 + + + ..\$(PROJECTNAME).launchscreen\Assets\LaunchScreenImage.imageset + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + ..\$(PROJECTNAME).launchscreen\Assets\LaunchScreenImage.imageset + 1 + + + ..\$(PROJECTNAME).launchscreen\Assets\LaunchScreenImage.imageset + 1 + + + + + 1 + + + 1 + + + 1 + + + + + ..\$(PROJECTNAME).launchscreen\Assets\LaunchScreenImage.imageset + 1 + + + ..\$(PROJECTNAME).launchscreen\Assets\LaunchScreenImage.imageset + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + ..\$(PROJECTNAME).launchscreen\Assets\LaunchScreenImage.imageset + 1 + + + ..\$(PROJECTNAME).launchscreen\Assets\LaunchScreenImage.imageset + 1 + + + + + ..\$(PROJECTNAME).launchscreen\Assets\LaunchScreenImage.imageset + 1 + + + ..\$(PROJECTNAME).launchscreen\Assets\LaunchScreenImage.imageset + 1 + + + + + 1 + + + 1 + + + + + ..\$(PROJECTNAME).app.dSYM\Contents\Resources\DWARF + 1 + + + ..\$(PROJECTNAME).app.dSYM\Contents\Resources\DWARF + 1 + + + + + 1 + + + 1 + + + + + ..\ + 1 + + + ..\ + 1 + + + + + 1 + + + 1 + + + 1 + + + + + ..\$(PROJECTNAME).launchscreen + 64 + + + ..\$(PROJECTNAME).launchscreen + 64 + + + + + 1 + + + 1 + + + 1 + + + + + ..\$(PROJECTNAME).app.dSYM\Contents\Resources\DWARF + 1 + + + + + ..\ + 1 + + + ..\ + 1 + + + + + Contents + 1 + + + Contents + 1 + + + + + Contents\Resources + 1 + + + Contents\Resources + 1 + + + + + library\lib\armeabi-v7a + 1 + + + library\lib\arm64-v8a + 1 + + + 1 + + + 1 + + + 1 + + + 1 + + + Contents\MacOS + 1 + + + Contents\MacOS + 1 + + + 0 + + + + + library\lib\armeabi-v7a + 1 + + + + + 1 + + + 1 + + + + + Assets + 1 + + + Assets + 1 + + + + + Assets + 1 + + + Assets + 1 + + + + + + + + + + + + + + + 12 + + + + +
diff --git a/Demos/MARSTemplateDCS/MARSTemplateServerDCSService.dpr b/Demos/MARSTemplateDCS/MARSTemplateServerDCSService.dpr new file mode 100644 index 00000000..87b5702d --- /dev/null +++ b/Demos/MARSTemplateDCS/MARSTemplateServerDCSService.dpr @@ -0,0 +1,43 @@ +(* + Copyright 2016, MARS-Curiosity - REST Library + + Home: https://github.com/andrea-magni/MARS +*) +program MARSTemplateServerDCSService; + +{$I MARS.inc} + +uses +{$ifdef DelphiXE3_UP} + Vcl.SvcMgr, +{$else} + SvcMgr, +{$endif} + + Server.Service in 'Server.Service.pas' {ServerService: TService}, + Server.Ignition in 'Server.Ignition.pas', + Server.Resources in 'Server.Resources.pas', + Server.WebModule in 'Server.WebModule.pas' {ServerWebModule: TWebModule}; + +{$R *.RES} + +begin + // Windows 2003 Server requires StartServiceCtrlDispatcher to be + // called before CoRegisterClassObject, which can be called indirectly + // by Application.Initialize. TServiceApplication.DelayInitialize allows + // Application.Initialize to be called from TService.Main (after + // StartServiceCtrlDispatcher has been called). + // + // Delayed initialization of the Application object may affect + // events which then occur prior to initialization, such as + // TService.OnCreate. It is only recommended if the ServiceApplication + // registers a class object with OLE and is intended for use with + // Windows 2003 Server. + // + // Application.DelayInitialize := True; + // + if not Application.DelayInitialize or Application.Installing then + Application.Initialize; + Application.CreateForm(TServerService, ServerService); + Application.Run; +end. diff --git a/Demos/MARSTemplateDCS/MARSTemplateServerDCSService.dproj b/Demos/MARSTemplateDCS/MARSTemplateServerDCSService.dproj new file mode 100644 index 00000000..6eb3a0c6 --- /dev/null +++ b/Demos/MARSTemplateDCS/MARSTemplateServerDCSService.dproj @@ -0,0 +1,1210 @@ + + + {FEF5DF94-50B0-46F7-9F4B-6C355F140A2F} + MARSTemplateServerDCSService.dpr + True + Release + 3 + Application + None + 19.0 + Win32 + + + true + + + true + Base + true + + + true + Base + true + + + true + Base + true + + + true + Cfg_1 + true + true + + + true + Cfg_1 + true + true + + + true + Base + true + + + true + Cfg_2 + true + true + + + true + Cfg_2 + true + true + + + true + Cfg_2 + true + true + + + false + false + false + false + false + 00400000 + MARSTemplateServerDCSService + 1040 + CompanyName=;FileDescription=;FileVersion=1.0.0.0;InternalName=;LegalCopyright=;LegalTrademarks=;OriginalFilename=;ProductName=;ProductVersion=1.0.0.0;Comments=;CFBundleName= + System;Xml;Data;Datasnap;Web;Soap;Vcl;Winapi;$(DCC_Namespace) + .\lib\$(Platform)\$(Config) + .\bin + + + System.Win;Data.Win;Datasnap.Win;Web.Win;Soap.Win;Xml.Win;Bde;$(DCC_Namespace) + Debug + true + CompanyName=;FileDescription=$(MSBuildProjectName);FileVersion=1.0.0.0;InternalName=;LegalCopyright=;LegalTrademarks=;OriginalFilename=;ProductName=$(MSBuildProjectName);ProductVersion=1.0.0.0;Comments=;ProgramID=com.embarcadero.$(MSBuildProjectName) + 1033 + $(BDS)\bin\default_app.manifest + MARSTemplateServer_Icon.ico + true + $(BDS)\bin\Artwork\Windows\UWP\delphi_UwpDefault_44.png + $(BDS)\bin\Artwork\Windows\UWP\delphi_UwpDefault_150.png + + + $(BDS)\bin\default_app.manifest + MARSTemplateServer_Icon.ico + true + System.Win;Data.Win;Datasnap.Win;Web.Win;Soap.Win;Xml.Win;$(DCC_Namespace) + Debug + true + CompanyName=;FileDescription=$(MSBuildProjectName);FileVersion=1.0.0.0;InternalName=;LegalCopyright=;LegalTrademarks=;OriginalFilename=;ProgramID=com.embarcadero.$(MSBuildProjectName);ProductName=$(MSBuildProjectName);ProductVersion=1.0.0.0;Comments= + 1033 + $(BDS)\bin\Artwork\Windows\UWP\delphi_UwpDefault_44.png + $(BDS)\bin\Artwork\Windows\UWP\delphi_UwpDefault_150.png + + + RELEASE;$(DCC_Define) + 0 + false + 0 + + + true + true + 1033 + CompanyName=;FileDescription=$(MSBuildProjectName);FileVersion=1.0.0.0;InternalName=;LegalCopyright=;LegalTrademarks=;OriginalFilename=;ProductName=$(MSBuildProjectName);ProductVersion=1.0.0.0;Comments=;ProgramID=com.embarcadero.$(MSBuildProjectName) + PerMonitor + + + true + true + 1033 + CompanyName=;FileDescription=$(MSBuildProjectName);FileVersion=1.0.0.0;InternalName=;LegalCopyright=;LegalTrademarks=;OriginalFilename=;ProgramID=com.embarcadero.$(MSBuildProjectName);ProductName=$(MSBuildProjectName);ProductVersion=1.0.0.0;Comments= + $(BDS)\bin\Artwork\Windows\UWP\delphi_UwpDefault_44.png + $(BDS)\bin\Artwork\Windows\UWP\delphi_UwpDefault_150.png + PerMonitor + + + DEBUG;$(DCC_Define) + false + true + + + Debug + + + true + true + 1033 + CompanyName=;FileDescription=$(MSBuildProjectName);FileVersion=1.0.0.0;InternalName=;LegalCopyright=;LegalTrademarks=;OriginalFilename=;ProductName=$(MSBuildProjectName);ProductVersion=1.0.0.0;Comments=;ProgramID=com.embarcadero.$(MSBuildProjectName) + $(BDS)\bin\Artwork\Windows\UWP\delphi_UwpDefault_44.png + $(BDS)\bin\Artwork\Windows\UWP\delphi_UwpDefault_150.png + PerMonitor + + + true + true + 1033 + CompanyName=;FileDescription=$(MSBuildProjectName);FileVersion=1.0.0.0;InternalName=;LegalCopyright=;LegalTrademarks=;OriginalFilename=;ProgramID=com.embarcadero.$(MSBuildProjectName);ProductName=$(MSBuildProjectName);ProductVersion=1.0.0.0;Comments= + MARS.ico + $(BDS)\bin\Artwork\Windows\UWP\delphi_UwpDefault_44.png + $(BDS)\bin\Artwork\Windows\UWP\delphi_UwpDefault_150.png + PerMonitor + + + + MainSource + + +
ServerService
+ TService +
+ + + +
ServerWebModule
+ TWebModule +
+ + Cfg_2 + Base + + + Base + + + Cfg_1 + Base + +
+ + Delphi.Personality.12 + + + + + MARSTemplateServerDCSService.dpr + + + Microsoft Office 2000 Sample Automation Server Wrapper Components + Microsoft Office XP Sample Automation Server Wrapper Components + + + + False + False + True + True + + + + + Assets\ + Logo150x150.png + true + + + + + true + + + + + Assets\ + Logo44x44.png + true + + + + + Assets\ + Logo150x150.png + true + + + + + true + + + + + true + + + + + true + + + + + true + + + + + true + + + + + MARSTemplateServerDCSService.exe + true + + + + + Assets\ + Logo44x44.png + true + + + + + 1 + + + Contents\MacOS + 1 + + + 0 + + + + + classes + 1 + + + classes + 1 + + + + + res\xml + 1 + + + res\xml + 1 + + + + + library\lib\armeabi-v7a + 1 + + + + + library\lib\armeabi + 1 + + + library\lib\armeabi + 1 + + + + + library\lib\armeabi-v7a + 1 + + + + + library\lib\mips + 1 + + + library\lib\mips + 1 + + + + + library\lib\armeabi-v7a + 1 + + + library\lib\arm64-v8a + 1 + + + + + library\lib\armeabi-v7a + 1 + + + + + res\drawable + 1 + + + res\drawable + 1 + + + + + res\values + 1 + + + res\values + 1 + + + + + res\values-v21 + 1 + + + res\values-v21 + 1 + + + + + res\values + 1 + + + res\values + 1 + + + + + res\drawable + 1 + + + res\drawable + 1 + + + + + res\drawable-xxhdpi + 1 + + + res\drawable-xxhdpi + 1 + + + + + res\drawable-ldpi + 1 + + + res\drawable-ldpi + 1 + + + + + res\drawable-mdpi + 1 + + + res\drawable-mdpi + 1 + + + + + res\drawable-hdpi + 1 + + + res\drawable-hdpi + 1 + + + + + res\drawable-xhdpi + 1 + + + res\drawable-xhdpi + 1 + + + + + res\drawable-mdpi + 1 + + + res\drawable-mdpi + 1 + + + + + res\drawable-hdpi + 1 + + + res\drawable-hdpi + 1 + + + + + res\drawable-xhdpi + 1 + + + res\drawable-xhdpi + 1 + + + + + res\drawable-xxhdpi + 1 + + + res\drawable-xxhdpi + 1 + + + + + res\drawable-xxxhdpi + 1 + + + res\drawable-xxxhdpi + 1 + + + + + res\drawable-small + 1 + + + res\drawable-small + 1 + + + + + res\drawable-normal + 1 + + + res\drawable-normal + 1 + + + + + res\drawable-large + 1 + + + res\drawable-large + 1 + + + + + res\drawable-xlarge + 1 + + + res\drawable-xlarge + 1 + + + + + res\values + 1 + + + res\values + 1 + + + + + 1 + + + Contents\MacOS + 1 + + + 0 + + + + + Contents\MacOS + 1 + .framework + + + Contents\MacOS + 1 + .framework + + + 0 + + + + + 1 + .dylib + + + 1 + .dylib + + + 1 + .dylib + + + Contents\MacOS + 1 + .dylib + + + Contents\MacOS + 1 + .dylib + + + 0 + .dll;.bpl + + + + + 1 + .dylib + + + 1 + .dylib + + + 1 + .dylib + + + Contents\MacOS + 1 + .dylib + + + Contents\MacOS + 1 + .dylib + + + 0 + .bpl + + + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + Contents\Resources\StartUp\ + 0 + + + Contents\Resources\StartUp\ + 0 + + + 0 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + ..\$(PROJECTNAME).launchscreen\Assets\LaunchScreenImage.imageset + 1 + + + ..\$(PROJECTNAME).launchscreen\Assets\LaunchScreenImage.imageset + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + ..\$(PROJECTNAME).launchscreen\Assets\LaunchScreenImage.imageset + 1 + + + ..\$(PROJECTNAME).launchscreen\Assets\LaunchScreenImage.imageset + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + ..\$(PROJECTNAME).launchscreen\Assets\LaunchScreenImage.imageset + 1 + + + ..\$(PROJECTNAME).launchscreen\Assets\LaunchScreenImage.imageset + 1 + + + + + 1 + + + 1 + + + 1 + + + + + ..\$(PROJECTNAME).launchscreen\Assets\LaunchScreenImage.imageset + 1 + + + ..\$(PROJECTNAME).launchscreen\Assets\LaunchScreenImage.imageset + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + ..\$(PROJECTNAME).launchscreen\Assets\LaunchScreenImage.imageset + 1 + + + ..\$(PROJECTNAME).launchscreen\Assets\LaunchScreenImage.imageset + 1 + + + + + ..\$(PROJECTNAME).launchscreen\Assets\LaunchScreenImage.imageset + 1 + + + ..\$(PROJECTNAME).launchscreen\Assets\LaunchScreenImage.imageset + 1 + + + + + 1 + + + 1 + + + + + ..\$(PROJECTNAME).app.dSYM\Contents\Resources\DWARF + 1 + + + ..\$(PROJECTNAME).app.dSYM\Contents\Resources\DWARF + 1 + + + + + 1 + + + 1 + + + + + ..\ + 1 + + + ..\ + 1 + + + + + 1 + + + 1 + + + 1 + + + + + ..\$(PROJECTNAME).launchscreen + 64 + + + ..\$(PROJECTNAME).launchscreen + 64 + + + + + 1 + + + 1 + + + 1 + + + + + ..\$(PROJECTNAME).app.dSYM\Contents\Resources\DWARF + 1 + + + + + ..\ + 1 + + + ..\ + 1 + + + + + Contents + 1 + + + Contents + 1 + + + + + Contents\Resources + 1 + + + Contents\Resources + 1 + + + + + library\lib\armeabi-v7a + 1 + + + library\lib\arm64-v8a + 1 + + + 1 + + + 1 + + + 1 + + + 1 + + + Contents\MacOS + 1 + + + Contents\MacOS + 1 + + + 0 + + + + + library\lib\armeabi-v7a + 1 + + + + + 1 + + + 1 + + + + + Assets + 1 + + + Assets + 1 + + + + + Assets + 1 + + + Assets + 1 + + + + + + + + + + + + + + + 12 + + + + +
diff --git a/Demos/MARSTemplateDCS/MARSTemplateServer_Icon.ico b/Demos/MARSTemplateDCS/MARSTemplateServer_Icon.ico new file mode 100644 index 00000000..0067d86b Binary files /dev/null and b/Demos/MARSTemplateDCS/MARSTemplateServer_Icon.ico differ diff --git a/Demos/MARSTemplateDCS/Server.DCS.Forms.Main.dfm b/Demos/MARSTemplateDCS/Server.DCS.Forms.Main.dfm new file mode 100644 index 00000000..dfe3c09f --- /dev/null +++ b/Demos/MARSTemplateDCS/Server.DCS.Forms.Main.dfm @@ -0,0 +1,80 @@ +object MainForm: TMainForm + Left = 0 + Top = 0 + Caption = 'MainForm' + ClientHeight = 289 + ClientWidth = 554 + Color = clBtnFace + Font.Charset = DEFAULT_CHARSET + Font.Color = clWindowText + Font.Height = -11 + Font.Name = 'Tahoma' + Font.Style = [] + OldCreateOrder = False + OnCreate = FormCreate + PixelsPerInch = 96 + TextHeight = 13 + object TopPanel: TPanel + Left = 0 + Top = 0 + Width = 554 + Height = 73 + Align = alTop + BevelOuter = bvNone + TabOrder = 0 + object Label1: TLabel + Left = 28 + Top = 17 + Width = 63 + Height = 13 + Caption = 'Port number:' + end + object StartButton: TButton + Left = 16 + Top = 41 + Width = 75 + Height = 25 + Action = StartServerAction + TabOrder = 0 + end + object StopButton: TButton + Left = 104 + Top = 41 + Width = 75 + Height = 25 + Action = StopServerAction + TabOrder = 1 + end + object PortNumberEdit: TEdit + Left = 97 + Top = 14 + Width = 82 + Height = 21 + TabOrder = 2 + OnChange = PortNumberEditChange + end + end + object MainTreeView: TTreeView + Left = 0 + Top = 73 + Width = 554 + Height = 216 + Align = alClient + Indent = 19 + TabOrder = 1 + end + object MainActionList: TActionList + Left = 384 + Top = 24 + object StartServerAction: TAction + Caption = 'Start Server' + OnExecute = StartServerActionExecute + OnUpdate = StartServerActionUpdate + end + object StopServerAction: TAction + Caption = 'Stop Server' + OnExecute = StopServerActionExecute + OnUpdate = StopServerActionUpdate + end + end +end diff --git a/Demos/MARSTemplateDCS/Server.DCS.Forms.Main.pas b/Demos/MARSTemplateDCS/Server.DCS.Forms.Main.pas new file mode 100644 index 00000000..29bd4402 --- /dev/null +++ b/Demos/MARSTemplateDCS/Server.DCS.Forms.Main.pas @@ -0,0 +1,142 @@ +unit Server.DCS.Forms.Main; + +{$I MARS.inc} + +interface + +uses + Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics, + Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.ComCtrls, System.Actions, Vcl.ActnList, + Vcl.StdCtrls, Vcl.ExtCtrls +, MARS.http.Server.DCS +; + +type + TMainForm = class(TForm) + TopPanel: TPanel; + Label1: TLabel; + StartButton: TButton; + StopButton: TButton; + PortNumberEdit: TEdit; + MainActionList: TActionList; + StartServerAction: TAction; + StopServerAction: TAction; + MainTreeView: TTreeView; + procedure FormCreate(Sender: TObject); + procedure PortNumberEditChange(Sender: TObject); + procedure StartServerActionExecute(Sender: TObject); + procedure StartServerActionUpdate(Sender: TObject); + procedure StopServerActionExecute(Sender: TObject); + procedure StopServerActionUpdate(Sender: TObject); + private + FServer: TMARShttpServerDCS; + protected + procedure RenderEngines(const ATreeView: TTreeView); + public + end; + +var + MainForm: TMainForm; + +implementation + +{$R *.dfm} + +uses + StrUtils +, MARS.Core.URL, MARS.Core.Engine, MARS.Core.Application, MARS.Core.Registry +, MARS.Core.Registry.Utils +, Server.Ignition +; + +{ TMainForm } + +procedure TMainForm.FormCreate(Sender: TObject); +begin + PortNumberEdit.Text := IntToStr(TServerEngine.Default.Port); + RenderEngines(MainTreeView); + StartServerAction.Execute; +end; + +procedure TMainForm.PortNumberEditChange(Sender: TObject); +begin + TServerEngine.Default.Port := StrToInt(PortNumberEdit.Text); +end; + +procedure TMainForm.RenderEngines(const ATreeView: TTreeView); +begin + + ATreeview.Items.BeginUpdate; + try + ATreeview.Items.Clear; + TMARSEngineRegistry.Instance.EnumerateEngines( + procedure (AName: string; AEngine: TMARSEngine) + var + LEngineItem: TTreeNode; + begin + LEngineItem := ATreeview.Items.AddChild(nil + , AName + ' [ :' + AEngine.Port.ToString + AEngine.BasePath + ']' + ); + + AEngine.EnumerateApplications( + procedure (AName: string; AApplication: TMARSApplication) + var + LApplicationItem: TTreeNode; + begin + LApplicationItem := ATreeview.Items.AddChild(LEngineItem + , AApplication.Name + ' [' + AApplication.BasePath + ']' + ); + + AApplication.EnumerateResources( + procedure (AName: string; AInfo: TMARSConstructorInfo) + begin + ATreeview.Items.AddChild( + LApplicationItem + , AInfo.TypeTClass.ClassName + ' [' + AName + ']' + ); + + end + ); + end + ); + end + ); + + if ATreeView.Items.Count > 0 then + ATreeView.Items[0].Expand(True); + finally + ATreeView.Items.EndUpdate; + end; + +end; + +procedure TMainForm.StartServerActionExecute(Sender: TObject); +begin + // http server implementation + FServer := TMARShttpServerDCS.Create(TServerEngine.Default); + try + FServer.DefaultPort := TServerEngine.Default.Port; + FServer.Active := True; + except + FServer.Free; + raise; + end; +end; + +procedure TMainForm.StartServerActionUpdate(Sender: TObject); +begin + StartServerAction.Enabled := (FServer = nil) or (FServer.Active = False); +end; + +procedure TMainForm.StopServerActionExecute(Sender: TObject); +begin + FServer.Active := False; + FreeAndNil(FServer); +end; + +procedure TMainForm.StopServerActionUpdate(Sender: TObject); +begin + StopServerAction.Enabled := Assigned(FServer) and (FServer.Active = True); +end; + +end. diff --git a/Demos/MARSTemplateDCS/Server.FMX.Forms.Main.fmx b/Demos/MARSTemplateDCS/Server.FMX.Forms.Main.fmx new file mode 100644 index 00000000..fdadb145 --- /dev/null +++ b/Demos/MARSTemplateDCS/Server.FMX.Forms.Main.fmx @@ -0,0 +1,66 @@ +object MainForm: TMainForm + Left = 0 + Top = 0 + Caption = 'MARS-Curiosity Template Server FMX' + ClientHeight = 480 + ClientWidth = 640 + FormFactor.Width = 320 + FormFactor.Height = 480 + FormFactor.Devices = [Desktop] + OnCreate = FormCreate + OnClose = FormClose + DesignerMasterStyle = 0 + object Layout1: TLayout + Align = Top + Size.Width = 640.000000000000000000 + Size.Height = 121.000000000000000000 + Size.PlatformDefault = False + TabOrder = 1 + object PortNumberEdit: TEdit + Touch.InteractiveGestures = [LongTap, DoubleTap] + TabOrder = 0 + Text = '8080' + Position.X = 109.000000000000000000 + Position.Y = 22.000000000000000000 + OnChange = PortNumberEditChange + end + object Label1: TLabel + Position.X = 16.000000000000000000 + Position.Y = 24.000000000000000000 + Size.Width = 89.000000000000000000 + Size.Height = 17.000000000000000000 + Size.PlatformDefault = False + Text = 'Port number:' + end + object StartButton: TButton + Action = StartServerAction + Enabled = True + ImageIndex = -1 + Position.X = 16.000000000000000000 + Position.Y = 64.000000000000000000 + TabOrder = 2 + end + object StopButton: TButton + Action = StopServerAction + Enabled = True + ImageIndex = -1 + Position.X = 104.000000000000000000 + Position.Y = 64.000000000000000000 + TabOrder = 3 + end + end + object MainActionList: TActionList + Left = 384 + Top = 24 + object StartServerAction: TAction + Text = 'Start Server' + OnExecute = StartServerActionExecute + OnUpdate = StartServerActionUpdate + end + object StopServerAction: TAction + Text = 'Stop Server' + OnExecute = StopServerActionExecute + OnUpdate = StopServerActionUpdate + end + end +end diff --git a/Demos/MARSTemplateDCS/Server.FMX.Forms.Main.pas b/Demos/MARSTemplateDCS/Server.FMX.Forms.Main.pas new file mode 100644 index 00000000..40aea6cc --- /dev/null +++ b/Demos/MARSTemplateDCS/Server.FMX.Forms.Main.pas @@ -0,0 +1,97 @@ +(* + Copyright 2016, MARS-Curiosity - REST Library + + Home: https://github.com/andrea-magni/MARS +*) +unit Server.FMX.Forms.Main; + +interface + +uses + System.SysUtils, System.Types, System.UITypes, System.Classes, System.Variants, + FMX.Types, FMX.Controls, FMX.Forms, FMX.Graphics, FMX.Dialogs, FMX.StdCtrls, + FMX.Controls.Presentation, FMX.Edit, FMX.Layouts, System.Actions, FMX.ActnList +, MARS.http.Server.DCS +; + +type + TMainForm = class(TForm) + MainActionList: TActionList; + StartServerAction: TAction; + StopServerAction: TAction; + Layout1: TLayout; + PortNumberEdit: TEdit; + Label1: TLabel; + StartButton: TButton; + StopButton: TButton; + procedure FormClose(Sender: TObject; var Action: TCloseAction); + procedure FormCreate(Sender: TObject); + procedure StartServerActionExecute(Sender: TObject); + procedure StopServerActionExecute(Sender: TObject); + procedure StartServerActionUpdate(Sender: TObject); + procedure StopServerActionUpdate(Sender: TObject); + procedure PortNumberEditChange(Sender: TObject); + private + FServer: TMARShttpServerDCS; + public + end; + +var + MainForm: TMainForm; + +implementation + +{$R *.fmx} + +uses + MARS.Core.URL, MARS.Core.Engine +, Server.Ignition +; + +procedure TMainForm.FormClose(Sender: TObject; var Action: TCloseAction); +begin + StopServerAction.Execute; +end; + +procedure TMainForm.FormCreate(Sender: TObject); +begin + PortNumberEdit.Text := TServerEngine.Default.Port.ToString; + + StartServerAction.Execute; +end; + +procedure TMainForm.PortNumberEditChange(Sender: TObject); +begin + TServerEngine.Default.Port := StrToInt(PortNumberEdit.Text); +end; + +procedure TMainForm.StartServerActionExecute(Sender: TObject); +begin + // http server implementation + FServer := TMARShttpServerDCS.Create(TServerEngine.Default); + try + FServer.DefaultPort := TServerEngine.Default.Port; + FServer.Active := True; + except + FServer.Free; + raise; + end; +end; + +procedure TMainForm.StartServerActionUpdate(Sender: TObject); +begin + StartServerAction.Enabled := (FServer = nil) or (FServer.Active = False); +end; + +procedure TMainForm.StopServerActionExecute(Sender: TObject); +begin + FServer.Active := False; + FreeAndNil(FServer); +end; + +procedure TMainForm.StopServerActionUpdate(Sender: TObject); +begin + StopServerAction.Enabled := Assigned(FServer) and (FServer.Active = True); +end; + +end. diff --git a/Demos/MARSTemplateDCS/Server.Forms.Main.dfm b/Demos/MARSTemplateDCS/Server.Forms.Main.dfm new file mode 100644 index 00000000..4bd5c813 --- /dev/null +++ b/Demos/MARSTemplateDCS/Server.Forms.Main.dfm @@ -0,0 +1,83 @@ +object MainForm: TMainForm + Left = 0 + Top = 0 + Caption = 'MARSTemplate Server' + ClientHeight = 201 + ClientWidth = 464 + Color = clBtnFace + Constraints.MinHeight = 240 + Constraints.MinWidth = 480 + Font.Charset = DEFAULT_CHARSET + Font.Color = clWindowText + Font.Height = -11 + Font.Name = 'Tahoma' + Font.Style = [] + OldCreateOrder = False + OnClose = FormClose + OnCreate = FormCreate + PixelsPerInch = 96 + TextHeight = 13 + object TopPanel: TPanel + Left = 0 + Top = 0 + Width = 464 + Height = 73 + Align = alTop + BevelOuter = bvNone + TabOrder = 0 + object Label1: TLabel + Left = 28 + Top = 17 + Width = 63 + Height = 13 + Caption = 'Port number:' + end + object StartButton: TButton + Left = 16 + Top = 41 + Width = 75 + Height = 25 + Action = StartServerAction + TabOrder = 0 + end + object StopButton: TButton + Left = 104 + Top = 41 + Width = 75 + Height = 25 + Action = StopServerAction + TabOrder = 1 + end + object PortNumberEdit: TEdit + Left = 97 + Top = 14 + Width = 82 + Height = 21 + TabOrder = 2 + OnChange = PortNumberEditChange + end + end + object MainTreeView: TTreeView + Left = 0 + Top = 73 + Width = 464 + Height = 128 + Align = alClient + Indent = 19 + TabOrder = 1 + end + object MainActionList: TActionList + Left = 384 + Top = 24 + object StartServerAction: TAction + Caption = 'Start Server' + OnExecute = StartServerActionExecute + OnUpdate = StartServerActionUpdate + end + object StopServerAction: TAction + Caption = 'Stop Server' + OnExecute = StopServerActionExecute + OnUpdate = StopServerActionUpdate + end + end +end diff --git a/Demos/MARSTemplateDCS/Server.Forms.Main.pas b/Demos/MARSTemplateDCS/Server.Forms.Main.pas new file mode 100644 index 00000000..0673418a --- /dev/null +++ b/Demos/MARSTemplateDCS/Server.Forms.Main.pas @@ -0,0 +1,148 @@ +(* + Copyright 2016, MARS-Curiosity - REST Library + + Home: https://github.com/andrea-magni/MARS +*) +unit Server.Forms.Main; + +{$I MARS.inc} + +interface + +uses Classes, SysUtils, Forms, ActnList, ComCtrls, StdCtrls, Controls, ExtCtrls + , System.Actions + , MARS.http.Server.Indy +; + +type + TMainForm = class(TForm) + MainActionList: TActionList; + StartServerAction: TAction; + StopServerAction: TAction; + TopPanel: TPanel; + Label1: TLabel; + StartButton: TButton; + StopButton: TButton; + PortNumberEdit: TEdit; + MainTreeView: TTreeView; + procedure StartServerActionExecute(Sender: TObject); + procedure StartServerActionUpdate(Sender: TObject); + procedure StopServerActionExecute(Sender: TObject); + procedure StopServerActionUpdate(Sender: TObject); + procedure FormCreate(Sender: TObject); + procedure PortNumberEditChange(Sender: TObject); + procedure FormClose(Sender: TObject; var Action: TCloseAction); + private + FServer: TMARShttpServerIndy; + protected + procedure RenderEngines(const ATreeView: TTreeView); + public + end; + +var + MainForm: TMainForm; + +implementation + +{$R *.dfm} + +uses + StrUtils, Web.HttpApp + , MARS.Core.URL, MARS.Core.Engine, MARS.Core.Application, MARS.Core.Registry + , MARS.Core.Registry.Utils + , Server.Ignition +; + +procedure TMainForm.RenderEngines(const ATreeView: TTreeView); +begin + + ATreeview.Items.BeginUpdate; + try + ATreeview.Items.Clear; + TMARSEngineRegistry.Instance.EnumerateEngines( + procedure (AName: string; AEngine: TMARSEngine) + var + LEngineItem: TTreeNode; + begin + LEngineItem := ATreeview.Items.AddChild(nil + , AName + ' [ :' + AEngine.Port.ToString + AEngine.BasePath + ']' + ); + + AEngine.EnumerateApplications( + procedure (AName: string; AApplication: TMARSApplication) + var + LApplicationItem: TTreeNode; + begin + LApplicationItem := ATreeview.Items.AddChild(LEngineItem + , AApplication.Name + ' [' + AApplication.BasePath + ']' + ); + + AApplication.EnumerateResources( + procedure (AName: string; AInfo: TMARSConstructorInfo) + begin + ATreeview.Items.AddChild( + LApplicationItem + , AInfo.TypeTClass.ClassName + ' [' + AName + ']' + ); + + end + ); + end + ); + end + ); + + if ATreeView.Items.Count > 0 then + ATreeView.Items[0].Expand(True); + finally + ATreeView.Items.EndUpdate; + end; +end; + +procedure TMainForm.FormClose(Sender: TObject; var Action: TCloseAction); +begin + StopServerAction.Execute; +end; + +procedure TMainForm.FormCreate(Sender: TObject); +begin + PortNumberEdit.Text := IntToStr(TServerEngine.Default.Port); + RenderEngines(MainTreeView); + StartServerAction.Execute; +end; + +procedure TMainForm.PortNumberEditChange(Sender: TObject); +begin + TServerEngine.Default.Port := StrToInt(PortNumberEdit.Text); +end; + +procedure TMainForm.StartServerActionExecute(Sender: TObject); +begin + // http server implementation + FServer := TMARShttpServerIndy.Create(TServerEngine.Default); + try + FServer.DefaultPort := TServerEngine.Default.Port; + FServer.Active := True; + except + FServer.Free; + raise; + end; +end; + +procedure TMainForm.StartServerActionUpdate(Sender: TObject); +begin + StartServerAction.Enabled := (FServer = nil) or (FServer.Active = False); +end; + +procedure TMainForm.StopServerActionExecute(Sender: TObject); +begin + FServer.Active := False; + FreeAndNil(FServer); +end; + +procedure TMainForm.StopServerActionUpdate(Sender: TObject); +begin + StopServerAction.Enabled := Assigned(FServer) and (FServer.Active = True); +end; + +end. diff --git a/Demos/MARSTemplateDCS/Server.Ignition.pas b/Demos/MARSTemplateDCS/Server.Ignition.pas new file mode 100644 index 00000000..93fbdc6c --- /dev/null +++ b/Demos/MARSTemplateDCS/Server.Ignition.pas @@ -0,0 +1,132 @@ +(* + Copyright 2016, MARS-Curiosity - REST Library + + Home: https://github.com/andrea-magni/MARS +*) +unit Server.Ignition; + +{$I MARS.inc} + +interface + +uses + Classes, SysUtils, Rtti + , MARS.Core.Engine +; + +type + TServerEngine=class + private + class var FEngine: TMARSEngine; +{$IFDEF MARS_FIREDAC} + class var FAvailableConnectionDefs: TArray; +{$ENDIF} + public + class constructor CreateEngine; + class destructor DestroyEngine; + class property Default: TMARSEngine read FEngine; + end; + + +implementation + +uses + MARS.Core.Activation, MARS.Core.Activation.Interfaces + , MARS.Core.Application, MARS.Core.Utils, MARS.Utils.Parameters.IniFile + , MARS.Core.MessageBodyWriter, MARS.Core.MessageBodyWriters + , MARS.Core.MessageBodyReaders, MARS.Data.MessageBodyWriters + {$IFDEF MARS_FIREDAC} , MARS.Data.FireDAC {$ENDIF} + {$IFDEF MSWINDOWS} , MARS.mORMotJWT.Token {$ELSE} , MARS.JOSEJWT.Token {$ENDIF} + , Server.Resources + ; + +{ TServerEngine } + +class constructor TServerEngine.CreateEngine; +begin + FEngine := TMARSEngine.Create; + try + // Engine configuration + FEngine.Parameters.LoadFromIniFile; + + // Application configuration + FEngine.AddApplication('DefaultApp', '/default', [ 'Server.Resources.*']); +{$IFDEF MARS_FIREDAC} + FAvailableConnectionDefs := TMARSFireDAC.LoadConnectionDefs(FEngine.Parameters, 'FireDAC'); +{$ENDIF} +{$REGION 'BeforeHandleRequest example'} +(* + FEngine.BeforeHandleRequest := + function (const AEngine: TMARSEngine; + const AURL: TMARSURL; const ARequest: TWebRequest; const AResponse: TWebResponse; + var Handled: Boolean + ): Boolean + begin + Result := True; +{ + // skip favicon requests (browser) + if SameText(AURL.Document, 'favicon.ico') then + begin + Result := False; + Handled := True; + end; +} +{ + // Handle CORS and PreFlight + if SameText(ARequest.Method, 'OPTIONS') then + begin + Handled := True; + Result := False; + end; +} + end; +*) +{$ENDREGION} +{$REGION 'Global BeforeInvoke handler example'} +(* + // to execute something before each activation + TMARSActivation.RegisterBeforeInvoke( + procedure (const AActivation: IMARSActivation; out AIsAllowed: Boolean) + begin + + end + ); +*) +{$ENDREGION} +{$REGION 'Global AfterInvoke handler example'} +(* + // to execute something after each activation + TMARSActivation.RegisterAfterInvoke( + procedure (const AActivation: IMARSActivation) + begin + + end + ); +*) +{$ENDREGION} +{$REGION 'Global InvokeError handler example'} +(* + // to execute something on error + TMARSActivation.RegisterInvokeError( + procedure (const AActivation: IMARSActivation; const AException: Exception; var AHandled: Boolean) + begin + + end + ); +*) +{$ENDREGION} + except + FreeAndNil(FEngine); + raise; + end; +end; + +class destructor TServerEngine.DestroyEngine; +begin +{$IFDEF MARS_FIREDAC} + TMARSFireDAC.CloseConnectionDefs(FAvailableConnectionDefs); +{$ENDIF} + FreeAndNil(FEngine); +end; + +end. diff --git a/Demos/MARSTemplateDCS/Server.Resources.pas b/Demos/MARSTemplateDCS/Server.Resources.pas new file mode 100644 index 00000000..caafa682 --- /dev/null +++ b/Demos/MARSTemplateDCS/Server.Resources.pas @@ -0,0 +1,48 @@ +(* + Copyright 2016, MARS-Curiosity - REST Library + + Home: https://github.com/andrea-magni/MARS +*) +unit Server.Resources; + +interface + +uses + SysUtils, Classes + + , MARS.Core.Attributes, MARS.Core.MediaType, MARS.Core.JSON, MARS.Core.Response + , MARS.Core.URL + + , MARS.Core.Token.Resource //, MARS.Core.Token +; + +type + [Path('helloworld')] + THelloWorldResource = class + protected + public + [GET, Produces(TMediaType.TEXT_PLAIN)] + function SayHelloWorld: string; + end; + + [Path('token')] + TTokenResource = class(TMARSTokenResource) + end; + +implementation + +uses + MARS.Core.Registry +; + +{ THelloWorldResource } + +function THelloWorldResource.SayHelloWorld: string; +begin + Result := 'Hello World!'; +end; + +initialization + TMARSResourceRegistry.Instance.RegisterResource; + TMARSResourceRegistry.Instance.RegisterResource; +end. diff --git a/Demos/MARSTemplateDCS/Server.Service.dfm b/Demos/MARSTemplateDCS/Server.Service.dfm new file mode 100644 index 00000000..e697b752 --- /dev/null +++ b/Demos/MARSTemplateDCS/Server.Service.dfm @@ -0,0 +1,10 @@ +object ServerService: TServerService + OldCreateOrder = False + OnCreate = ServiceCreate + OnDestroy = ServiceDestroy + DisplayName = 'MARSTemplate Service' + OnStart = ServiceStart + OnStop = ServiceStop + Height = 150 + Width = 215 +end diff --git a/Demos/MARSTemplateDCS/Server.Service.pas b/Demos/MARSTemplateDCS/Server.Service.pas new file mode 100644 index 00000000..86c5a5ca --- /dev/null +++ b/Demos/MARSTemplateDCS/Server.Service.pas @@ -0,0 +1,124 @@ +(* + Copyright 2016, MARS-Curiosity - REST Library + + Home: https://github.com/andrea-magni/MARS +*) +unit Server.Service; + +{$I MARS.inc} + +interface + +uses +{$ifdef DelphiXE3_UP} + Winapi.Windows, Winapi.Messages, System.SysUtils, System.Classes, Vcl.Graphics +, Vcl.Controls, Vcl.SvcMgr, Vcl.Dialogs +//, IPPeerServer, IPPeerAPI +, IdHTTPWebBrokerBridge, Web.WebReq, Web.WebBroker +{$else} + Windows, Messages, SysUtils, Classes, Graphics +, Controls, SvcMgr, Dialogs +//, IPPeerServer, IPPeerAPI +, IdHTTPWebBrokerBridge, WebReq, WebBroker +{$endif} +, IdContext +, MARS.http.Server.DCS +; + +type + TServerService = class(TService) + procedure ServiceCreate(Sender: TObject); + procedure ServiceDestroy(Sender: TObject); + procedure ServiceStart(Sender: TService; var Started: Boolean); + procedure ServiceStop(Sender: TService; var Stopped: Boolean); + private + FServer: TIdHTTPWebBrokerBridge; + + procedure ParseAuthenticationHandler(AContext: TIdContext; + const AAuthType, AAuthData: String; var VUsername, VPassword: String; + var VHandled: Boolean); virtual; + + public + function GetServiceController: TServiceController; override; + + const DEFAULT_PORT = 8080; + end; + +var + ServerService: TServerService; + +implementation + +{$R *.dfm} + +uses + IdSchedulerOfThreadPool +, Server.Ignition +, Server.WebModule +; + +procedure ServiceController(CtrlCode: DWord); stdcall; +begin + ServerService.Controller(CtrlCode); +end; + +function TServerService.GetServiceController: TServiceController; +begin + Result := ServiceController; +end; + +procedure TServerService.ParseAuthenticationHandler(AContext: TIdContext; + const AAuthType, AAuthData: String; var VUsername, VPassword: String; + var VHandled: Boolean); +begin + // Allow JWT Bearer authentication's scheme + if SameText(AAuthType, 'Bearer') then + VHandled := True; +end; + +procedure TServerService.ServiceCreate(Sender: TObject); +var + LScheduler: TIdSchedulerOfThreadPool; +begin + if WebRequestHandler <> nil then + WebRequestHandler.WebModuleClass := WebModuleClass; + + FServer := TIdHTTPWebBrokerBridge.Create(nil); + try + FServer.DefaultPort := TServerEngine.Default.Port; + + LScheduler := TIdSchedulerOfThreadPool.Create(FServer); + try + LScheduler.PoolSize := TServerEngine.Default.ThreadPoolSize; + FServer.Scheduler := LScheduler; + FServer.MaxConnections := LScheduler.PoolSize; + FServer.OnParseAuthentication := ParseAuthenticationHandler; + except + FServer.Scheduler.Free; + FServer.Scheduler := nil; + raise; + end; + except + FServer.Free; + raise; + end; +end; + +procedure TServerService.ServiceDestroy(Sender: TObject); +begin + FreeAndNil(FServer); +end; + +procedure TServerService.ServiceStart(Sender: TService; var Started: Boolean); +begin + FServer.Active := True; + Started := FServer.Active; +end; + +procedure TServerService.ServiceStop(Sender: TService; var Stopped: Boolean); +begin + FServer.Active := False; + Stopped := not FServer.Active; +end; + +end. diff --git a/Demos/MARSTemplateDCS/Server.WebModule.dfm b/Demos/MARSTemplateDCS/Server.WebModule.dfm new file mode 100644 index 00000000..a589a41a --- /dev/null +++ b/Demos/MARSTemplateDCS/Server.WebModule.dfm @@ -0,0 +1,12 @@ +object ServerWebModule: TServerWebModule + OldCreateOrder = False + Actions = < + item + Default = True + Name = 'DefaultHandler' + PathInfo = '/' + OnAction = ServerWebModuleDefaultHandlerAction + end> + Height = 230 + Width = 415 +end diff --git a/Demos/MARSTemplateDCS/Server.WebModule.pas b/Demos/MARSTemplateDCS/Server.WebModule.pas new file mode 100644 index 00000000..72908095 --- /dev/null +++ b/Demos/MARSTemplateDCS/Server.WebModule.pas @@ -0,0 +1,57 @@ +(* + Copyright 2016, MARS-Curiosity - REST Library + + Home: https://github.com/andrea-magni/MARS +*) +unit Server.WebModule; + +{$I MARS.inc} + +interface + +uses System.SysUtils, System.Classes, Web.HTTPApp; + +type + TServerWebModule = class(TWebModule) + procedure ServerWebModuleDefaultHandlerAction(Sender: TObject; + Request: TWebRequest; Response: TWebResponse; var Handled: Boolean); + private + { Private declarations } + public + { Public declarations } + end; + +var + WebModuleClass: TComponentClass = TServerWebModule; + +implementation + +{%CLASSGROUP 'System.Classes.TPersistent'} + +{$R *.dfm} + +uses + MARS.http.Server.Indy +, Server.Ignition; + +procedure TServerWebModule.ServerWebModuleDefaultHandlerAction(Sender: TObject; + Request: TWebRequest; Response: TWebResponse; var Handled: Boolean); +begin + inherited; + + if not TServerEngine.Default.HandleRequest(TMARSWebRequest.Create(Request), TMARSWebResponse.Create(Response)) then + begin + Response.ContentType := 'application/json'; + Response.Content := + '{"success": false, "details": ' + + '{' + + '"error": "Request not found",' + + '"pathinfo": "' + Request.PathInfo + '"' + + '}' + + '}'; + end + else + Handled := True; +end; + +end. diff --git a/Demos/MARSTemplateDCS/ServerConst.pas b/Demos/MARSTemplateDCS/ServerConst.pas new file mode 100644 index 00000000..49558197 --- /dev/null +++ b/Demos/MARSTemplateDCS/ServerConst.pas @@ -0,0 +1,42 @@ +(* + Copyright 2016, MARS-Curiosity - REST Library + + Home: https://github.com/andrea-magni/MARS +*) +unit ServerConst; + +interface + +resourcestring + sPortInUse = '- Error: Port %s already in use'; + sPortSet = '- Port set to %s'; + sServerRunning = '- The Server is already running'; + sStartingServer = '- Starting HTTP Server on port %d'; + sStoppingServer = '- Stopping Server'; + sServerStopped = '- Server Stopped'; + sServerNotRunning = '- The Server is not running'; + sInvalidCommand = '- Error: Invalid Command'; + sIndyVersion = '- Indy Version: '; + sActive = '- Active: '; + sPort = '- Port: '; + sSessionID = '- Session ID CookieName: '; + sCommands = 'Enter a Command: ' + slineBreak + + ' - "start" to start the server'+ slineBreak + + ' - "stop" to stop the server'+ slineBreak + + ' - "set port" to change the default port'+ slineBreak + + ' - "status" for Server status'+ slineBreak + + ' - "help" to show commands'+ slineBreak + + ' - "exit" to close the application'; + +const + cArrow = '->'; + cCommandStart = 'start'; + cCommandStop = 'stop'; + cCommandStatus = 'status'; + cCommandHelp = 'help'; + cCommandSetPort = 'set port'; + cCommandExit = 'exit'; + +implementation + +end. diff --git a/Demos/MARSTemplateDCS/bin/MARSTemplateServerApplication.ini b/Demos/MARSTemplateDCS/bin/MARSTemplateServerApplication.ini new file mode 100644 index 00000000..e098e5da --- /dev/null +++ b/Demos/MARSTemplateDCS/bin/MARSTemplateServerApplication.ini @@ -0,0 +1,2 @@ +[DefaultEngine] +ThreadPoolSize=100 \ No newline at end of file diff --git a/Demos/MultipartFormData/MultipartFormDataServerApplication.dproj b/Demos/MultipartFormData/MultipartFormDataServerApplication.dproj index c3661c21..a9a4f3c5 100644 --- a/Demos/MultipartFormData/MultipartFormDataServerApplication.dproj +++ b/Demos/MultipartFormData/MultipartFormDataServerApplication.dproj @@ -3,7 +3,7 @@ {0A5E1DDC-90B4-4B41-A7DC-2B0FC45D4349} MultipartFormDataServerApplication.dpr True - Release + Debug 3 Application VCL diff --git a/Demos/MultipartFormData/Server.Ignition.pas b/Demos/MultipartFormData/Server.Ignition.pas index 7151c084..d3d7c467 100644 --- a/Demos/MultipartFormData/Server.Ignition.pas +++ b/Demos/MultipartFormData/Server.Ignition.pas @@ -35,6 +35,7 @@ implementation , MARS.Core.Application, MARS.Core.Utils, MARS.Utils.Parameters.IniFile , MARS.Core.MessageBodyWriter, MARS.Core.MessageBodyWriters , MARS.Core.MessageBodyReaders, MARS.Data.MessageBodyWriters + , MARS.Core.RequestAndResponse.Interfaces {$IFDEF MARS_FIREDAC} , MARS.Data.FireDAC {$ENDIF} {$IFDEF MSWINDOWS} , MARS.mORMotJWT.Token {$ELSE} , MARS.JOSEJWT.Token {$ENDIF} , MARS.Core.URL, Web.HttpApp @@ -58,8 +59,8 @@ implementation {$REGION 'BeforeHandleRequest example'} FEngine.BeforeHandleRequest := - function (const AEngine: TMARSEngine; const AURL: TMARSURL; - const ARequest: TWebRequest; const AResponse: TWebResponse; + function (const AEngine: TMARSEngine; + const AURL: TMARSURL; const ARequest: IMARSRequest; const AResponse: IMARSResponse; var Handled: Boolean): Boolean begin Result := True; diff --git a/Demos/Mustache/MARSMustacheServerApacheModule.dproj b/Demos/Mustache/MARSMustacheServerApacheModule.dproj index 1888c829..9aaa3c93 100644 --- a/Demos/Mustache/MARSMustacheServerApacheModule.dproj +++ b/Demos/Mustache/MARSMustacheServerApacheModule.dproj @@ -1,7 +1,7 @@  {F003A08E-799F-4C20-8128-819AAAAC0A88} - 18.2 + 18.8 None MARSMustacheServerApacheModule.dpr True @@ -124,19 +124,19 @@ - - - MARSMustacheServerApacheModule.dll + + true - - + + + libMARSMustacheServerApacheModule.so true - - + + true @@ -145,14 +145,24 @@ true + + + MARSMustacheServerApacheModule.dll + true + + + + + true + + true - - - libMARSMustacheServerApacheModule.so + + true @@ -161,7 +171,6 @@ 1 - Contents\MacOS 0
@@ -170,6 +179,20 @@ classes 1 + + classes + 1 + + + + + res\xml + 1 + + + res\xml + 1 + @@ -182,90 +205,242 @@ library\lib\armeabi 1 + + library\lib\armeabi + 1 + + + + + library\lib\armeabi-v7a + 1 + library\lib\mips 1 + + library\lib\mips + 1 + library\lib\armeabi-v7a 1 + + library\lib\arm64-v8a + 1 + + + + + library\lib\armeabi-v7a + 1 + res\drawable 1 + + res\drawable + 1 + res\values 1 + + res\values + 1 + + + + + res\values-v21 + 1 + + + res\values-v21 + 1 + + + + + res\values + 1 + + + res\values + 1 + res\drawable 1 + + res\drawable + 1 + res\drawable-xxhdpi 1 + + res\drawable-xxhdpi + 1 + res\drawable-ldpi 1 + + res\drawable-ldpi + 1 + res\drawable-mdpi 1 + + res\drawable-mdpi + 1 + res\drawable-hdpi 1 + + res\drawable-hdpi + 1 + res\drawable-xhdpi 1 + + res\drawable-xhdpi + 1 + + + + + res\drawable-mdpi + 1 + + + res\drawable-mdpi + 1 + + + + + res\drawable-hdpi + 1 + + + res\drawable-hdpi + 1 + + + + + res\drawable-xhdpi + 1 + + + res\drawable-xhdpi + 1 + + + + + res\drawable-xxhdpi + 1 + + + res\drawable-xxhdpi + 1 + + + + + res\drawable-xxxhdpi + 1 + + + res\drawable-xxxhdpi + 1 + res\drawable-small 1 + + res\drawable-small + 1 + res\drawable-normal 1 + + res\drawable-normal + 1 + res\drawable-large 1 + + res\drawable-large + 1 + res\drawable-xlarge 1 + + res\drawable-xlarge + 1 + + + + + res\values + 1 + + + res\values + 1 + @@ -283,6 +458,10 @@ 1 .framework + + 1 + .framework + 0 @@ -292,6 +471,10 @@ 1 .dylib + + 1 + .dylib + 0 .dll;.bpl @@ -314,6 +497,10 @@ 1 .dylib + + 1 + .dylib + 0 .bpl @@ -323,6 +510,9 @@ 0 + + 0 + 0 @@ -335,6 +525,9 @@ 0 + + 0 + 0 @@ -350,6 +543,17 @@ 1 + + + 1 + + + 1 + + + 1 + + 1 @@ -361,6 +565,39 @@ 1 + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + 1 @@ -372,6 +609,61 @@ 1 + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + 1 @@ -383,6 +675,116 @@ 1 + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + 1 @@ -416,10 +818,35 @@ 1 + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + 1 + + 1 + @@ -445,6 +872,7 @@ 1 + @@ -452,12 +880,20 @@ Contents\Resources 1 + + Contents\Resources + 1 + library\lib\armeabi-v7a 1 + + library\lib\arm64-v8a + 1 + 1 @@ -473,10 +909,19 @@ 1 + + 1 + 0 + + + library\lib\armeabi-v7a + 1 + + 1 @@ -512,7 +957,9 @@ + + True diff --git a/Demos/Mustache/MARSMustacheServerApplication.dproj b/Demos/Mustache/MARSMustacheServerApplication.dproj index 3f68e7e3..b8dfba51 100644 --- a/Demos/Mustache/MARSMustacheServerApplication.dproj +++ b/Demos/Mustache/MARSMustacheServerApplication.dproj @@ -7,7 +7,7 @@ Application VCL DCC32 - 18.2 + 18.8 1 Win32 @@ -167,7 +167,6 @@ 1 - Contents\MacOS 0 @@ -176,6 +175,20 @@ classes 1 + + classes + 1 + + + + + res\xml + 1 + + + res\xml + 1 + @@ -188,90 +201,242 @@ library\lib\armeabi 1 + + library\lib\armeabi + 1 + + + + + library\lib\armeabi-v7a + 1 + library\lib\mips 1 + + library\lib\mips + 1 + library\lib\armeabi-v7a 1 + + library\lib\arm64-v8a + 1 + + + + + library\lib\armeabi-v7a + 1 + res\drawable 1 + + res\drawable + 1 + res\values 1 + + res\values + 1 + + + + + res\values-v21 + 1 + + + res\values-v21 + 1 + + + + + res\values + 1 + + + res\values + 1 + res\drawable 1 + + res\drawable + 1 + res\drawable-xxhdpi 1 + + res\drawable-xxhdpi + 1 + res\drawable-ldpi 1 + + res\drawable-ldpi + 1 + res\drawable-mdpi 1 + + res\drawable-mdpi + 1 + res\drawable-hdpi 1 + + res\drawable-hdpi + 1 + res\drawable-xhdpi 1 + + res\drawable-xhdpi + 1 + + + + + res\drawable-mdpi + 1 + + + res\drawable-mdpi + 1 + + + + + res\drawable-hdpi + 1 + + + res\drawable-hdpi + 1 + + + + + res\drawable-xhdpi + 1 + + + res\drawable-xhdpi + 1 + + + + + res\drawable-xxhdpi + 1 + + + res\drawable-xxhdpi + 1 + + + + + res\drawable-xxxhdpi + 1 + + + res\drawable-xxxhdpi + 1 + res\drawable-small 1 + + res\drawable-small + 1 + res\drawable-normal 1 + + res\drawable-normal + 1 + res\drawable-large 1 + + res\drawable-large + 1 + res\drawable-xlarge 1 + + res\drawable-xlarge + 1 + + + + + res\values + 1 + + + res\values + 1 + @@ -289,6 +454,10 @@ 1 .framework + + 1 + .framework + 0 @@ -298,6 +467,10 @@ 1 .dylib + + 1 + .dylib + 0 .dll;.bpl @@ -320,6 +493,10 @@ 1 .dylib + + 1 + .dylib + 0 .bpl @@ -329,6 +506,9 @@ 0 + + 0 + 0 @@ -341,6 +521,9 @@ 0 + + 0 + 0 @@ -356,6 +539,17 @@ 1 + + + 1 + + + 1 + + + 1 + + 1 @@ -367,6 +561,39 @@ 1 + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + 1 @@ -378,6 +605,61 @@ 1 + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + 1 @@ -389,6 +671,116 @@ 1 + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + 1 @@ -422,10 +814,35 @@ 1 + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + 1 + + 1 + @@ -451,6 +868,7 @@ 1 + @@ -458,12 +876,20 @@ Contents\Resources 1 + + Contents\Resources + 1 + library\lib\armeabi-v7a 1 + + library\lib\arm64-v8a + 1 + 1 @@ -479,10 +905,19 @@ 1 + + 1 + 0 + + + library\lib\armeabi-v7a + 1 + + 1 @@ -518,7 +953,9 @@ + + 12 diff --git a/Demos/Mustache/MARSMustacheServerApplication.res b/Demos/Mustache/MARSMustacheServerApplication.res index da24bfd7..aff75aeb 100644 Binary files a/Demos/Mustache/MARSMustacheServerApplication.res and b/Demos/Mustache/MARSMustacheServerApplication.res differ diff --git a/Demos/Mustache/MARSMustacheServerConsoleApplication.dproj b/Demos/Mustache/MARSMustacheServerConsoleApplication.dproj index 5b8c88d6..93f8e961 100644 --- a/Demos/Mustache/MARSMustacheServerConsoleApplication.dproj +++ b/Demos/Mustache/MARSMustacheServerConsoleApplication.dproj @@ -1,7 +1,7 @@  {3989725D-F9E4-4178-9FBC-93DC284F63C7} - 18.2 + 18.8 None MARSMustacheServerConsoleApplication.dpr True @@ -130,12 +130,22 @@ true
+ + + true + + + + + true + + true - + true @@ -150,7 +160,6 @@ 1 - Contents\MacOS 0 @@ -159,6 +168,20 @@ classes 1 + + classes + 1 + + + + + res\xml + 1 + + + res\xml + 1 + @@ -171,90 +194,242 @@ library\lib\armeabi 1 + + library\lib\armeabi + 1 + + + + + library\lib\armeabi-v7a + 1 + library\lib\mips 1 + + library\lib\mips + 1 + library\lib\armeabi-v7a 1 + + library\lib\arm64-v8a + 1 + + + + + library\lib\armeabi-v7a + 1 + res\drawable 1 + + res\drawable + 1 + res\values 1 + + res\values + 1 + + + + + res\values-v21 + 1 + + + res\values-v21 + 1 + + + + + res\values + 1 + + + res\values + 1 + res\drawable 1 + + res\drawable + 1 + res\drawable-xxhdpi 1 + + res\drawable-xxhdpi + 1 + res\drawable-ldpi 1 + + res\drawable-ldpi + 1 + res\drawable-mdpi 1 + + res\drawable-mdpi + 1 + res\drawable-hdpi 1 + + res\drawable-hdpi + 1 + res\drawable-xhdpi 1 + + res\drawable-xhdpi + 1 + + + + + res\drawable-mdpi + 1 + + + res\drawable-mdpi + 1 + + + + + res\drawable-hdpi + 1 + + + res\drawable-hdpi + 1 + + + + + res\drawable-xhdpi + 1 + + + res\drawable-xhdpi + 1 + + + + + res\drawable-xxhdpi + 1 + + + res\drawable-xxhdpi + 1 + + + + + res\drawable-xxxhdpi + 1 + + + res\drawable-xxxhdpi + 1 + res\drawable-small 1 + + res\drawable-small + 1 + res\drawable-normal 1 + + res\drawable-normal + 1 + res\drawable-large 1 + + res\drawable-large + 1 + res\drawable-xlarge 1 + + res\drawable-xlarge + 1 + + + + + res\values + 1 + + + res\values + 1 + @@ -272,6 +447,10 @@ 1 .framework + + 1 + .framework + 0 @@ -281,6 +460,10 @@ 1 .dylib + + 1 + .dylib + 0 .dll;.bpl @@ -303,6 +486,10 @@ 1 .dylib + + 1 + .dylib + 0 .bpl @@ -312,6 +499,9 @@ 0 + + 0 + 0 @@ -324,6 +514,9 @@ 0 + + 0 + 0 @@ -339,6 +532,17 @@ 1 + + + 1 + + + 1 + + + 1 + + 1 @@ -350,6 +554,39 @@ 1 + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + 1 @@ -361,6 +598,61 @@ 1 + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + 1 @@ -372,6 +664,116 @@ 1 + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + 1 @@ -405,10 +807,35 @@ 1 + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + 1 + + 1 + @@ -434,6 +861,7 @@ 1 + @@ -441,12 +869,20 @@ Contents\Resources 1 + + Contents\Resources + 1 + library\lib\armeabi-v7a 1 + + library\lib\arm64-v8a + 1 + 1 @@ -462,10 +898,19 @@ 1 + + 1 + 0 + + + library\lib\armeabi-v7a + 1 + + 1 @@ -501,7 +946,9 @@ + + True diff --git a/Demos/Mustache/MARSMustacheServerFMXApplication.dproj b/Demos/Mustache/MARSMustacheServerFMXApplication.dproj index 79bf2807..9260c1bf 100644 --- a/Demos/Mustache/MARSMustacheServerFMXApplication.dproj +++ b/Demos/Mustache/MARSMustacheServerFMXApplication.dproj @@ -1,13 +1,13 @@  {E0F4FA79-1B8C-437A-AB05-73EF7AD349A8} - 18.2 + 18.8 FMX MARSMustacheServerFMXApplication.dpr True Debug Win32 - 1119 + 37983 Application @@ -18,6 +18,11 @@ Base true + + true + Base + true + true Base @@ -38,6 +43,11 @@ Base true + + true + Base + true + true Base @@ -120,6 +130,30 @@ $(BDS)\bin\Artwork\Android\FM_SplashImage_426x320.png Debug package=com.embarcadero.$(MSBuildProjectName);label=$(MSBuildProjectName);versionCode=1;versionName=1.0.0;persistent=False;restoreAnyVersion=False;installLocation=auto;largeHeap=False;theme=TitleBar;hardwareAccelerated=true;apiKey= + $(BDS)\bin\Artwork\Android\FM_NotificationIcon_24x24.png + $(BDS)\bin\Artwork\Android\FM_NotificationIcon_36x36.png + $(BDS)\bin\Artwork\Android\FM_NotificationIcon_48x48.png + $(BDS)\bin\Artwork\Android\FM_NotificationIcon_72x72.png + $(BDS)\bin\Artwork\Android\FM_NotificationIcon_96x96.png + + + package=com.embarcadero.$(MSBuildProjectName);label=$(MSBuildProjectName);versionCode=1;versionName=1.0.0;persistent=False;restoreAnyVersion=False;installLocation=auto;largeHeap=False;theme=TitleBar;hardwareAccelerated=true;apiKey= + Debug + true + true + Base + true + $(BDS)\bin\Artwork\Android\FM_LauncherIcon_36x36.png + $(BDS)\bin\Artwork\Android\FM_LauncherIcon_72x72.png + $(BDS)\bin\Artwork\Android\FM_LauncherIcon_48x48.png + android-support-v4.dex.jar;cloud-messaging.dex.jar;fmx.dex.jar;google-analytics-v2.dex.jar;google-play-billing.dex.jar;google-play-licensing.dex.jar;google-play-services.dex.jar + DBXSqliteDriver;RESTComponents;DBXInterBaseDriver;emsclientfiredac;DataSnapFireDAC;tethering;bindcompfmx;FmxTeeUI;FireDACIBDriver;fmx;FireDACDBXDriver;dbexpress;IndyCore;dsnap;DataSnapCommon;emsclient;FireDACCommon;RESTBackendComponents;soapserver;bindengine;CloudService;FireDACCommonDriver;DataSnapClient;inet;bindcompdbx;IndyIPCommon;IndyIPServer;IndySystem;fmxFireDAC;FireDAC;FireDACSqliteDriver;ibmonitor;FMXTee;soaprtl;DbxCommonDriver;ibxpress;xmlrtl;soapmidas;DataSnapNativeClient;ibxbindings;rtl;FireDACDSDriver;DbxClientDriver;CustomIPTransport;bindcomp;IndyIPClient;dbxcds;dsnapxml;DataSnapProviderClient;dbrtl;IndyProtocols;$(DCC_UsePackage);$(DCC_UsePackage) + $(BDS)\bin\Artwork\Android\FM_SplashImage_640x480.png + $(BDS)\bin\Artwork\Android\FM_SplashImage_470x320.png + $(BDS)\bin\Artwork\Android\FM_LauncherIcon_144x144.png + $(BDS)\bin\Artwork\Android\FM_SplashImage_960x720.png + $(BDS)\bin\Artwork\Android\FM_LauncherIcon_96x96.png + $(BDS)\bin\Artwork\Android\FM_SplashImage_426x320.png $(BDS)\bin\Artwork\iOS\iPad\FM_ApplicationIcon_144x144.png @@ -145,7 +179,7 @@ $(BDS)\bin\Artwork\iOS\iPad\FM_ApplicationIcon_72x72.png $(BDS)\bin\Artwork\iOS\iPhone\FM_LaunchImage_1242x2208.png $(BDS)\bin\Artwork\iOS\iPhone\FM_SpotlightSearchIcon_29x29.png - CFBundleName=$(MSBuildProjectName);CFBundleDevelopmentRegion=en;CFBundleDisplayName=$(MSBuildProjectName);CFBundleIdentifier=$(MSBuildProjectName);CFBundleInfoDictionaryVersion=7.1;CFBundleVersion=1.0.0.0;CFBundlePackageType=APPL;CFBundleSignature=????;LSRequiresIPhoneOS=true;CFBundleAllowMixedLocalizations=YES;CFBundleExecutable=$(MSBuildProjectName);UIDeviceFamily=iPhone & iPad;CFBundleResourceSpecification=ResourceRules.plist;NSLocationAlwaysUsageDescription=The reason for accessing the location information of the user;NSLocationWhenInUseUsageDescription=The reason for accessing the location information of the user;FMLocalNotificationPermission=false;UIBackgroundModes=;NSContactsUsageDescription=The reason for accessing the contacts;NSPhotoLibraryUsageDescription=The reason for accessing the photo library;NSCameraUsageDescription=The reason for accessing the camera + CFBundleName=$(MSBuildProjectName);CFBundleDevelopmentRegion=en;CFBundleDisplayName=$(MSBuildProjectName);CFBundleIdentifier=$(MSBuildProjectName);CFBundleInfoDictionaryVersion=7.1;CFBundleVersion=1.0.0.0;CFBundlePackageType=APPL;CFBundleSignature=????;LSRequiresIPhoneOS=true;CFBundleAllowMixedLocalizations=YES;CFBundleExecutable=$(MSBuildProjectName);UIDeviceFamily=iPhone & iPad;CFBundleResourceSpecification=ResourceRules.plist;NSLocationAlwaysUsageDescription=The reason for accessing the location information of the user;NSLocationWhenInUseUsageDescription=The reason for accessing the location information of the user;FMLocalNotificationPermission=false;UIBackgroundModes=;NSContactsUsageDescription=The reason for accessing the contacts;NSPhotoLibraryUsageDescription=The reason for accessing the photo library;NSCameraUsageDescription=The reason for accessing the camera;CFBundleShortVersionString=1.0.0;NSPhotoLibraryAddUsageDescription=The reason for adding to the photo library;NSFaceIDUsageDescription=The reason for accessing the face id;NSLocationAlwaysAndWhenInUseUsageDescription=The reason for accessing the location information of the user;NSMicrophoneUsageDescription=The reason for accessing the microphone;NSSiriUsageDescription=The reason for accessing Siri;ITSAppUsesNonExemptEncryption=false $(BDS)\bin\Artwork\iOS\iPhone\FM_SpotlightSearchIcon_80x80.png iPhoneAndiPad $(BDS)\bin\Artwork\iOS\iPhone\FM_SpotlightSearchIcon_58x58.png @@ -162,6 +196,22 @@ $(BDS)\bin\Artwork\iOS\iPhone\FM_LaunchImage_2208x1242.png $(BDS)\bin\Artwork\iOS\iPad\FM_LaunchImagePortrait_768x1024.png Debug + $(BDS)\bin\Artwork\iOS\iPhone\FM_LaunchImage_1125x2436.png + $(BDS)\bin\Artwork\iOS\iPhone\FM_LaunchImage_2436x1125.png + $(BDS)\bin\Artwork\iOS\iPhone\FM_SpotlightSearchIcon_120x120.png + $(BDS)\bin\Artwork\iOS\iPhone\FM_LaunchImage_828x1792.png + $(BDS)\bin\Artwork\iOS\iPhone\FM_LaunchImage_1136x640.png + $(BDS)\bin\Artwork\iOS\iPhone\FM_LaunchImage_1242x2688.png + $(BDS)\bin\Artwork\iOS\iPhone\FM_LaunchImage_1334x750.png + $(BDS)\bin\Artwork\iOS\iPhone\FM_LaunchImage_1792x828.png + $(BDS)\bin\Artwork\iOS\iPhone\FM_LaunchImage_2688x1242.png + $(BDS)\bin\Artwork\iOS\iPad\FM_ApplicationIcon_167x167.png + $(BDS)\bin\Artwork\iOS\iPad\FM_LaunchImagePortrait_1668x2224.png + $(BDS)\bin\Artwork\iOS\iPad\FM_LaunchImagePortrait_1668x2388.png + $(BDS)\bin\Artwork\iOS\iPad\FM_LaunchImagePortrait_2048x2732.png + $(BDS)\bin\Artwork\iOS\iPad\FM_LaunchImageLandscape_2224x1668.png + $(BDS)\bin\Artwork\iOS\iPad\FM_LaunchImageLandscape_2388x1668.png + $(BDS)\bin\Artwork\iOS\iPad\FM_LaunchImageLandscape_2732x2048.png $(BDS)\bin\Artwork\iOS\iPad\FM_ApplicationIcon_144x144.png @@ -187,7 +237,7 @@ $(BDS)\bin\Artwork\iOS\iPad\FM_ApplicationIcon_72x72.png $(BDS)\bin\Artwork\iOS\iPhone\FM_LaunchImage_1242x2208.png $(BDS)\bin\Artwork\iOS\iPhone\FM_SpotlightSearchIcon_29x29.png - CFBundleName=$(MSBuildProjectName);CFBundleDevelopmentRegion=en;CFBundleDisplayName=$(MSBuildProjectName);CFBundleIdentifier=$(MSBuildProjectName);CFBundleInfoDictionaryVersion=7.1;CFBundleVersion=1.0.0.0;CFBundlePackageType=APPL;CFBundleSignature=????;LSRequiresIPhoneOS=true;CFBundleAllowMixedLocalizations=YES;CFBundleExecutable=$(MSBuildProjectName);UIDeviceFamily=iPhone & iPad;CFBundleResourceSpecification=ResourceRules.plist;NSLocationAlwaysUsageDescription=The reason for accessing the location information of the user;NSLocationWhenInUseUsageDescription=The reason for accessing the location information of the user;FMLocalNotificationPermission=false;UIBackgroundModes=;NSContactsUsageDescription=The reason for accessing the contacts;NSPhotoLibraryUsageDescription=The reason for accessing the photo library;NSCameraUsageDescription=The reason for accessing the camera + CFBundleName=$(MSBuildProjectName);CFBundleDevelopmentRegion=en;CFBundleDisplayName=$(MSBuildProjectName);CFBundleIdentifier=$(MSBuildProjectName);CFBundleInfoDictionaryVersion=7.1;CFBundleVersion=1.0.0.0;CFBundlePackageType=APPL;CFBundleSignature=????;LSRequiresIPhoneOS=true;CFBundleAllowMixedLocalizations=YES;CFBundleExecutable=$(MSBuildProjectName);UIDeviceFamily=iPhone & iPad;CFBundleResourceSpecification=ResourceRules.plist;NSLocationAlwaysUsageDescription=The reason for accessing the location information of the user;NSLocationWhenInUseUsageDescription=The reason for accessing the location information of the user;FMLocalNotificationPermission=false;UIBackgroundModes=;NSContactsUsageDescription=The reason for accessing the contacts;NSPhotoLibraryUsageDescription=The reason for accessing the photo library;NSCameraUsageDescription=The reason for accessing the camera;CFBundleShortVersionString=1.0.0;NSPhotoLibraryAddUsageDescription=The reason for adding to the photo library;NSFaceIDUsageDescription=The reason for accessing the face id;NSLocationAlwaysAndWhenInUseUsageDescription=The reason for accessing the location information of the user;NSMicrophoneUsageDescription=The reason for accessing the microphone;NSSiriUsageDescription=The reason for accessing Siri;ITSAppUsesNonExemptEncryption=false $(BDS)\bin\Artwork\iOS\iPhone\FM_SpotlightSearchIcon_80x80.png iPhoneAndiPad $(BDS)\bin\Artwork\iOS\iPhone\FM_SpotlightSearchIcon_58x58.png @@ -204,6 +254,22 @@ $(BDS)\bin\Artwork\iOS\iPhone\FM_LaunchImage_2208x1242.png $(BDS)\bin\Artwork\iOS\iPad\FM_LaunchImagePortrait_768x1024.png Debug + $(BDS)\bin\Artwork\iOS\iPhone\FM_LaunchImage_1125x2436.png + $(BDS)\bin\Artwork\iOS\iPhone\FM_LaunchImage_2436x1125.png + $(BDS)\bin\Artwork\iOS\iPhone\FM_SpotlightSearchIcon_120x120.png + $(BDS)\bin\Artwork\iOS\iPhone\FM_LaunchImage_828x1792.png + $(BDS)\bin\Artwork\iOS\iPhone\FM_LaunchImage_1136x640.png + $(BDS)\bin\Artwork\iOS\iPhone\FM_LaunchImage_1242x2688.png + $(BDS)\bin\Artwork\iOS\iPhone\FM_LaunchImage_1334x750.png + $(BDS)\bin\Artwork\iOS\iPhone\FM_LaunchImage_1792x828.png + $(BDS)\bin\Artwork\iOS\iPhone\FM_LaunchImage_2688x1242.png + $(BDS)\bin\Artwork\iOS\iPad\FM_ApplicationIcon_167x167.png + $(BDS)\bin\Artwork\iOS\iPad\FM_LaunchImagePortrait_1668x2224.png + $(BDS)\bin\Artwork\iOS\iPad\FM_LaunchImagePortrait_1668x2388.png + $(BDS)\bin\Artwork\iOS\iPad\FM_LaunchImagePortrait_2048x2732.png + $(BDS)\bin\Artwork\iOS\iPad\FM_LaunchImageLandscape_2224x1668.png + $(BDS)\bin\Artwork\iOS\iPad\FM_LaunchImageLandscape_2388x1668.png + $(BDS)\bin\Artwork\iOS\iPad\FM_LaunchImageLandscape_2732x2048.png $(BDS)\bin\Artwork\iOS\iPhone\FM_SpotlightSearchIcon_40x40.png @@ -234,7 +300,7 @@ $(BDS)\bin\Artwork\iOS\iPhone\FM_SpotlightSearchIcon_58x58.png $(BDS)\bin\Artwork\iOS\iPad\FM_ApplicationIcon_76x76.png $(BDS)\bin\Artwork\iOS\iPad\FM_LaunchImagePortrait_1536x2008.png - CFBundleName=$(MSBuildProjectName);CFBundleDevelopmentRegion=en;CFBundleDisplayName=$(MSBuildProjectName);CFBundleIdentifier=$(MSBuildProjectName);CFBundleInfoDictionaryVersion=7.1;CFBundleVersion=1.0.0.0;CFBundlePackageType=APPL;CFBundleSignature=????;LSRequiresIPhoneOS=true;CFBundleAllowMixedLocalizations=YES;CFBundleExecutable=$(MSBuildProjectName);UIDeviceFamily=iPhone & iPad;CFBundleResourceSpecification=ResourceRules.plist;NSLocationAlwaysUsageDescription=The reason for accessing the location information of the user;NSLocationWhenInUseUsageDescription=The reason for accessing the location information of the user;FMLocalNotificationPermission=false;UIBackgroundModes=;NSContactsUsageDescription=The reason for accessing the contacts;NSPhotoLibraryUsageDescription=The reason for accessing the photo library;NSCameraUsageDescription=The reason for accessing the camera + CFBundleName=$(MSBuildProjectName);CFBundleDevelopmentRegion=en;CFBundleDisplayName=$(MSBuildProjectName);CFBundleIdentifier=$(MSBuildProjectName);CFBundleInfoDictionaryVersion=7.1;CFBundleVersion=1.0.0.0;CFBundlePackageType=APPL;CFBundleSignature=????;LSRequiresIPhoneOS=true;CFBundleAllowMixedLocalizations=YES;CFBundleExecutable=$(MSBuildProjectName);UIDeviceFamily=iPhone & iPad;CFBundleResourceSpecification=ResourceRules.plist;NSLocationAlwaysUsageDescription=The reason for accessing the location information of the user;NSLocationWhenInUseUsageDescription=The reason for accessing the location information of the user;FMLocalNotificationPermission=false;UIBackgroundModes=;NSContactsUsageDescription=The reason for accessing the contacts;NSPhotoLibraryUsageDescription=The reason for accessing the photo library;NSCameraUsageDescription=The reason for accessing the camera;CFBundleShortVersionString=1.0.0;NSPhotoLibraryAddUsageDescription=The reason for adding to the photo library;NSFaceIDUsageDescription=The reason for accessing the face id;NSLocationAlwaysAndWhenInUseUsageDescription=The reason for accessing the location information of the user;NSMicrophoneUsageDescription=The reason for accessing the microphone;NSSiriUsageDescription=The reason for accessing Siri;ITSAppUsesNonExemptEncryption=false $(BDS)\bin\Artwork\iOS\iPhone\FM_ApplicationIcon_87x87.png $(BDS)\bin\Artwork\iOS\iPhone\FM_ApplicationIcon_120x120.png $(BDS)\bin\Artwork\iOS\iPhone\FM_ApplicationIcon_57x57.png @@ -244,6 +310,22 @@ $(BDS)\bin\Artwork\iOS\iPad\FM_LaunchImagePortrait_768x1004.png $(BDS)\bin\Artwork\iOS\iPad\FM_LaunchImagePortrait_768x1024.png $(BDS)\bin\Artwork\iOS\iPhone\FM_LaunchImage_2208x1242.png + $(BDS)\bin\Artwork\iOS\iPhone\FM_LaunchImage_1125x2436.png + $(BDS)\bin\Artwork\iOS\iPhone\FM_LaunchImage_2436x1125.png + $(BDS)\bin\Artwork\iOS\iPhone\FM_SpotlightSearchIcon_120x120.png + $(BDS)\bin\Artwork\iOS\iPhone\FM_LaunchImage_828x1792.png + $(BDS)\bin\Artwork\iOS\iPhone\FM_LaunchImage_1136x640.png + $(BDS)\bin\Artwork\iOS\iPhone\FM_LaunchImage_1242x2688.png + $(BDS)\bin\Artwork\iOS\iPhone\FM_LaunchImage_1334x750.png + $(BDS)\bin\Artwork\iOS\iPhone\FM_LaunchImage_1792x828.png + $(BDS)\bin\Artwork\iOS\iPhone\FM_LaunchImage_2688x1242.png + $(BDS)\bin\Artwork\iOS\iPad\FM_ApplicationIcon_167x167.png + $(BDS)\bin\Artwork\iOS\iPad\FM_LaunchImagePortrait_1668x2224.png + $(BDS)\bin\Artwork\iOS\iPad\FM_LaunchImagePortrait_1668x2388.png + $(BDS)\bin\Artwork\iOS\iPad\FM_LaunchImagePortrait_2048x2732.png + $(BDS)\bin\Artwork\iOS\iPad\FM_LaunchImageLandscape_2224x1668.png + $(BDS)\bin\Artwork\iOS\iPad\FM_LaunchImageLandscape_2388x1668.png + $(BDS)\bin\Artwork\iOS\iPad\FM_LaunchImageLandscape_2732x2048.png $(BDS)\bin\Artwork\Windows\UWP\delphi_UwpDefault_44.png @@ -254,8 +336,23 @@ /usr/X11/bin/xterm -e "%debuggee%" DBXSqliteDriver;RESTComponents;DataSnapServerMidas;DBXInterBaseDriver;emsclientfiredac;DataSnapFireDAC;tethering;FireDACMSSQLDriver;bindcompfmx;DBXOracleDriver;inetdb;FmxTeeUI;emsedge;FireDACIBDriver;fmx;fmxdae;FireDACDBXDriver;dbexpress;IndyCore;dsnap;DataSnapCommon;emsclient;FireDACCommon;RESTBackendComponents;soapserver;bindengine;DBXMySQLDriver;FireDACOracleDriver;CloudService;FireDACMySQLDriver;DBXFirebirdDriver;FireDACCommonODBC;FireDACCommonDriver;DataSnapClient;inet;bindcompdbx;IndyIPCommon;IndyIPServer;IndySystem;fmxFireDAC;FireDAC;emshosting;FireDACSqliteDriver;FireDACPgDriver;ibmonitor;FireDACASADriver;FireDACTDataDriver;FMXTee;soaprtl;DbxCommonDriver;ibxpress;DataSnapServer;xmlrtl;soapmidas;DataSnapNativeClient;fmxobj;ibxbindings;rtl;FireDACDSDriver;DbxClientDriver;DBXSybaseASADriver;CustomIPTransport;bindcomp;DBXInformixDriver;IndyIPClient;dbxcds;FireDACODBCDriver;DataSnapIndy10ServerTransport;dsnapxml;DataSnapProviderClient;dbrtl;inetdbxpress;FireDACMongoDBDriver;IndyProtocols;fmxase;$(DCC_UsePackage) true - CFBundleName=$(MSBuildProjectName);CFBundleDisplayName=$(MSBuildProjectName);CFBundleIdentifier=$(MSBuildProjectName);CFBundleVersion=1.0.0;CFBundlePackageType=APPL;CFBundleSignature=????;CFBundleAllowMixedLocalizations=YES;CFBundleExecutable=$(MSBuildProjectName);NSHighResolutionCapable=true;LSApplicationCategoryType=public.app-category.utilities;NSLocationAlwaysUsageDescription=The reason for accessing the location information of the user;NSLocationWhenInUseUsageDescription=The reason for accessing the location information of the user;NSContactsUsageDescription=The reason for accessing the contacts + CFBundleName=$(MSBuildProjectName);CFBundleDisplayName=$(MSBuildProjectName);CFBundleIdentifier=$(MSBuildProjectName);CFBundleVersion=1.0.0;CFBundlePackageType=APPL;CFBundleSignature=????;CFBundleAllowMixedLocalizations=YES;CFBundleExecutable=$(MSBuildProjectName);NSHighResolutionCapable=true;LSApplicationCategoryType=public.app-category.utilities;NSContactsUsageDescription=The reason for accessing the contacts;CFBundleShortVersionString=1.0.0;NSLocationUsageDescription=The reason for accessing the location information of the user + Debug + + + CFBundleName=$(MSBuildProjectName);CFBundleDisplayName=$(MSBuildProjectName);CFBundleIdentifier=$(MSBuildProjectName);CFBundleVersion=1.0.0;CFBundlePackageType=APPL;CFBundleSignature=????;CFBundleAllowMixedLocalizations=YES;CFBundleExecutable=$(MSBuildProjectName);NSHighResolutionCapable=true;LSApplicationCategoryType=public.app-category.utilities;NSContactsUsageDescription=The reason for accessing the contacts;CFBundleShortVersionString=1.0.0;NSLocationUsageDescription=The reason for accessing the location information of the user Debug + true + true + Base + true + $(BDS)\bin\Artwork\Windows\UWP\delphi_UwpDefault_44.png + $(BDS)\bin\Artwork\Windows\UWP\delphi_UwpDefault_150.png + .\bin\$(Platform) + .\lib\$(Platform) + (None) + /usr/X11/bin/xterm -e "%debuggee%" + DBXSqliteDriver;RESTComponents;DataSnapServerMidas;DBXInterBaseDriver;emsclientfiredac;DataSnapFireDAC;tethering;FireDACMSSQLDriver;bindcompfmx;DBXOracleDriver;inetdb;FmxTeeUI;emsedge;FireDACIBDriver;fmx;fmxdae;FireDACDBXDriver;dbexpress;IndyCore;dsnap;DataSnapCommon;emsclient;FireDACCommon;RESTBackendComponents;soapserver;bindengine;DBXMySQLDriver;FireDACOracleDriver;CloudService;FireDACMySQLDriver;DBXFirebirdDriver;FireDACCommonODBC;FireDACCommonDriver;DataSnapClient;inet;bindcompdbx;IndyIPCommon;IndyIPServer;IndySystem;fmxFireDAC;FireDAC;emshosting;FireDACSqliteDriver;FireDACPgDriver;ibmonitor;FireDACASADriver;FireDACTDataDriver;FMXTee;soaprtl;DbxCommonDriver;ibxpress;DataSnapServer;xmlrtl;soapmidas;DataSnapNativeClient;fmxobj;ibxbindings;rtl;FireDACDSDriver;DbxClientDriver;DBXSybaseASADriver;CustomIPTransport;bindcomp;DBXInformixDriver;IndyIPClient;dbxcds;FireDACODBCDriver;DataSnapIndy10ServerTransport;dsnapxml;DataSnapProviderClient;dbrtl;inetdbxpress;FireDACMongoDBDriver;IndyProtocols;fmxase;$(DCC_UsePackage);$(DCC_UsePackage) .\lib\$(Platform) @@ -295,12 +392,12 @@ true 1033 true - true false + PerMonitor true - true + PerMonitor false @@ -310,11 +407,11 @@ true - true + PerMonitor true - true + PerMonitor @@ -359,13 +456,19 @@ true - + + MARSTemplateServerFMXApplication.icns true - - + + + true + + + + true @@ -375,37 +478,41 @@ true - - + + true - - + + + Assets\ + Logo150x150.png true - + - MARSTemplateServerFMXApplication.icns true - + - Info.plist true - - + + true - + + + true + + + - Assets\ - Logo150x150.png + Info.plist true @@ -424,7 +531,6 @@ 1 - Contents\MacOS 0 @@ -433,6 +539,20 @@ classes 1 + + classes + 1 + + + + + res\xml + 1 + + + res\xml + 1 + @@ -445,90 +565,242 @@ library\lib\armeabi 1 + + library\lib\armeabi + 1 + + + + + library\lib\armeabi-v7a + 1 + library\lib\mips 1 + + library\lib\mips + 1 + library\lib\armeabi-v7a 1 + + library\lib\arm64-v8a + 1 + + + + + library\lib\armeabi-v7a + 1 + res\drawable 1 + + res\drawable + 1 + res\values 1 + + res\values + 1 + + + + + res\values-v21 + 1 + + + res\values-v21 + 1 + + + + + res\values + 1 + + + res\values + 1 + res\drawable 1 + + res\drawable + 1 + res\drawable-xxhdpi 1 + + res\drawable-xxhdpi + 1 + res\drawable-ldpi 1 + + res\drawable-ldpi + 1 + res\drawable-mdpi 1 + + res\drawable-mdpi + 1 + res\drawable-hdpi 1 + + res\drawable-hdpi + 1 + res\drawable-xhdpi 1 + + res\drawable-xhdpi + 1 + + + + + res\drawable-mdpi + 1 + + + res\drawable-mdpi + 1 + + + + + res\drawable-hdpi + 1 + + + res\drawable-hdpi + 1 + + + + + res\drawable-xhdpi + 1 + + + res\drawable-xhdpi + 1 + + + + + res\drawable-xxhdpi + 1 + + + res\drawable-xxhdpi + 1 + + + + + res\drawable-xxxhdpi + 1 + + + res\drawable-xxxhdpi + 1 + res\drawable-small 1 + + res\drawable-small + 1 + res\drawable-normal 1 + + res\drawable-normal + 1 + res\drawable-large 1 + + res\drawable-large + 1 + res\drawable-xlarge 1 + + res\drawable-xlarge + 1 + + + + + res\values + 1 + + + res\values + 1 + @@ -548,6 +820,11 @@ 1 .framework + + Contents\MacOS + 1 + .framework + 0 @@ -570,6 +847,11 @@ 1 .dylib + + Contents\MacOS + 1 + .dylib + 0 .dll;.bpl @@ -593,6 +875,11 @@ 1 .dylib + + Contents\MacOS + 1 + .dylib + 0 .bpl @@ -602,6 +889,9 @@ 0 + + 0 + 0 @@ -615,6 +905,10 @@ Contents\Resources\StartUp\ 0 + + Contents\Resources\StartUp\ + 0 + 0 @@ -630,6 +924,17 @@ 1 + + + 1 + + + 1 + + + 1 + + 1 @@ -641,6 +946,39 @@ 1 + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + 1 @@ -652,6 +990,61 @@ 1 + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + 1 @@ -663,6 +1056,116 @@ 1 + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + 1 @@ -696,10 +1199,35 @@ 1 + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + 1 + + 1 + @@ -751,29 +1279,51 @@ 1 + + + ..\$(PROJECTNAME).app.dSYM\Contents\Resources\DWARF + 1 + + ..\ 1 + + ..\ + 1 + Contents 1 + + Contents + 1 + Contents\Resources 1 + + Contents\Resources + 1 + library\lib\armeabi-v7a 1 + + library\lib\arm64-v8a + 1 + 1 @@ -790,10 +1340,20 @@ Contents\MacOS 1 + + Contents\MacOS + 1 + 0 + + + library\lib\armeabi-v7a + 1 + + 1 @@ -829,14 +1389,18 @@ + + True + True True True True True + True True True diff --git a/Demos/Mustache/MARSMustacheServerISAPI.dproj b/Demos/Mustache/MARSMustacheServerISAPI.dproj index 4a13f336..966175f9 100644 --- a/Demos/Mustache/MARSMustacheServerISAPI.dproj +++ b/Demos/Mustache/MARSMustacheServerISAPI.dproj @@ -1,7 +1,7 @@  {7F50F5E9-ED2D-4A28-9C9E-10FAA1B36C4D} - 18.2 + 18.8 VCL MARSMustacheServerISAPI.dpr True @@ -134,7 +134,6 @@ 1 - Contents\MacOS 0 @@ -143,6 +142,20 @@ classes 1 + + classes + 1 + + + + + res\xml + 1 + + + res\xml + 1 + @@ -155,90 +168,242 @@ library\lib\armeabi 1 + + library\lib\armeabi + 1 + + + + + library\lib\armeabi-v7a + 1 + library\lib\mips 1 + + library\lib\mips + 1 + library\lib\armeabi-v7a 1 + + library\lib\arm64-v8a + 1 + + + + + library\lib\armeabi-v7a + 1 + res\drawable 1 + + res\drawable + 1 + res\values 1 + + res\values + 1 + + + + + res\values-v21 + 1 + + + res\values-v21 + 1 + + + + + res\values + 1 + + + res\values + 1 + res\drawable 1 + + res\drawable + 1 + res\drawable-xxhdpi 1 + + res\drawable-xxhdpi + 1 + res\drawable-ldpi 1 + + res\drawable-ldpi + 1 + res\drawable-mdpi 1 + + res\drawable-mdpi + 1 + res\drawable-hdpi 1 + + res\drawable-hdpi + 1 + res\drawable-xhdpi 1 + + res\drawable-xhdpi + 1 + + + + + res\drawable-mdpi + 1 + + + res\drawable-mdpi + 1 + + + + + res\drawable-hdpi + 1 + + + res\drawable-hdpi + 1 + + + + + res\drawable-xhdpi + 1 + + + res\drawable-xhdpi + 1 + + + + + res\drawable-xxhdpi + 1 + + + res\drawable-xxhdpi + 1 + + + + + res\drawable-xxxhdpi + 1 + + + res\drawable-xxxhdpi + 1 + res\drawable-small 1 + + res\drawable-small + 1 + res\drawable-normal 1 + + res\drawable-normal + 1 + res\drawable-large 1 + + res\drawable-large + 1 + res\drawable-xlarge 1 + + res\drawable-xlarge + 1 + + + + + res\values + 1 + + + res\values + 1 + @@ -256,6 +421,10 @@ 1 .framework + + 1 + .framework + 0 @@ -265,6 +434,10 @@ 1 .dylib + + 1 + .dylib + 0 .dll;.bpl @@ -287,6 +460,10 @@ 1 .dylib + + 1 + .dylib + 0 .bpl @@ -296,6 +473,9 @@ 0 + + 0 + 0 @@ -308,6 +488,9 @@ 0 + + 0 + 0 @@ -323,6 +506,17 @@ 1 + + + 1 + + + 1 + + + 1 + + 1 @@ -334,6 +528,39 @@ 1 + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + 1 @@ -345,6 +572,61 @@ 1 + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + 1 @@ -356,6 +638,116 @@ 1 + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + 1 @@ -389,10 +781,35 @@ 1 + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + 1 + + 1 + @@ -418,6 +835,7 @@ 1 + @@ -425,12 +843,20 @@ Contents\Resources 1 + + Contents\Resources + 1 + library\lib\armeabi-v7a 1 + + library\lib\arm64-v8a + 1 + 1 @@ -446,10 +872,19 @@ 1 + + 1 + 0 + + + library\lib\armeabi-v7a + 1 + + 1 @@ -485,7 +920,9 @@ + + True diff --git a/Demos/Mustache/MARSMustacheServerService.dproj b/Demos/Mustache/MARSMustacheServerService.dproj index d5adb61d..31093e5b 100644 --- a/Demos/Mustache/MARSMustacheServerService.dproj +++ b/Demos/Mustache/MARSMustacheServerService.dproj @@ -1,7 +1,7 @@  {F72901B8-3DC2-48DB-9F80-EE7BA471758C} - 18.2 + 18.8 VCL MARSMustacheServerService.dpr True @@ -82,10 +82,10 @@ true - true 1033 true false + PerMonitor false @@ -94,8 +94,8 @@ 0 - true true + PerMonitor @@ -154,7 +154,6 @@ 1 - Contents\MacOS 0 @@ -163,6 +162,20 @@ classes 1 + + classes + 1 + + + + + res\xml + 1 + + + res\xml + 1 + @@ -175,90 +188,242 @@ library\lib\armeabi 1 + + library\lib\armeabi + 1 + + + + + library\lib\armeabi-v7a + 1 + library\lib\mips 1 + + library\lib\mips + 1 + library\lib\armeabi-v7a 1 + + library\lib\arm64-v8a + 1 + + + + + library\lib\armeabi-v7a + 1 + res\drawable 1 + + res\drawable + 1 + res\values 1 + + res\values + 1 + + + + + res\values-v21 + 1 + + + res\values-v21 + 1 + + + + + res\values + 1 + + + res\values + 1 + res\drawable 1 + + res\drawable + 1 + res\drawable-xxhdpi 1 + + res\drawable-xxhdpi + 1 + res\drawable-ldpi 1 + + res\drawable-ldpi + 1 + res\drawable-mdpi 1 + + res\drawable-mdpi + 1 + res\drawable-hdpi 1 + + res\drawable-hdpi + 1 + res\drawable-xhdpi 1 + + res\drawable-xhdpi + 1 + + + + + res\drawable-mdpi + 1 + + + res\drawable-mdpi + 1 + + + + + res\drawable-hdpi + 1 + + + res\drawable-hdpi + 1 + + + + + res\drawable-xhdpi + 1 + + + res\drawable-xhdpi + 1 + + + + + res\drawable-xxhdpi + 1 + + + res\drawable-xxhdpi + 1 + + + + + res\drawable-xxxhdpi + 1 + + + res\drawable-xxxhdpi + 1 + res\drawable-small 1 + + res\drawable-small + 1 + res\drawable-normal 1 + + res\drawable-normal + 1 + res\drawable-large 1 + + res\drawable-large + 1 + res\drawable-xlarge 1 + + res\drawable-xlarge + 1 + + + + + res\values + 1 + + + res\values + 1 + @@ -278,6 +443,11 @@ 1 .framework + + Contents\MacOS + 1 + .framework + 0 @@ -300,6 +470,11 @@ 1 .dylib + + Contents\MacOS + 1 + .dylib + 0 .dll;.bpl @@ -323,6 +498,11 @@ 1 .dylib + + Contents\MacOS + 1 + .dylib + 0 .bpl @@ -332,6 +512,9 @@ 0 + + 0 + 0 @@ -345,6 +528,10 @@ Contents\Resources\StartUp\ 0 + + Contents\Resources\StartUp\ + 0 + 0 @@ -360,6 +547,17 @@ 1 + + + 1 + + + 1 + + + 1 + + 1 @@ -371,6 +569,39 @@ 1 + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + 1 @@ -382,6 +613,61 @@ 1 + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + 1 @@ -393,6 +679,116 @@ 1 + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + 1 @@ -426,10 +822,35 @@ 1 + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + 1 + + 1 + @@ -481,29 +902,51 @@ 1 + + + ..\$(PROJECTNAME).app.dSYM\Contents\Resources\DWARF + 1 + + ..\ 1 + + ..\ + 1 + Contents 1 + + Contents + 1 + Contents\Resources 1 + + Contents\Resources + 1 + library\lib\armeabi-v7a 1 + + library\lib\arm64-v8a + 1 + 1 @@ -520,10 +963,20 @@ Contents\MacOS 1 + + Contents\MacOS + 1 + 0 + + + library\lib\armeabi-v7a + 1 + + 1 @@ -559,7 +1012,9 @@ + + True diff --git a/Demos/Mustache/Server.Forms.Main.pas b/Demos/Mustache/Server.Forms.Main.pas index c587bfee..03a5f3cf 100644 --- a/Demos/Mustache/Server.Forms.Main.pas +++ b/Demos/Mustache/Server.Forms.Main.pas @@ -44,7 +44,7 @@ implementation uses StrUtils, Web.HttpApp - , MARS.Core.URL, MARS.Core.Engine + , MARS.Core.URL, MARS.Core.Engine, MARS.Core.RequestAndResponse.Interfaces , Server.Ignition; procedure TMainForm.FormClose(Sender: TObject; var Action: TCloseAction); @@ -59,7 +59,7 @@ procedure TMainForm.FormCreate(Sender: TObject); // skip favicon requests (browser) TServerEngine.Default.BeforeHandleRequest := function (const AEngine: TMARSEngine; - const AURL: TMARSURL; const ARequest: TWebRequest; const AResponse: TWebResponse; + const AURL: TMARSURL; const ARequest: IMARSRequest; const AResponse: IMARSResponse; var Handled: Boolean ): Boolean begin diff --git a/Demos/RemoteMic/FMXClient.DataModules.Main.dfm b/Demos/RemoteMic/FMXClient.DataModules.Main.dfm new file mode 100644 index 00000000..650df259 --- /dev/null +++ b/Demos/RemoteMic/FMXClient.DataModules.Main.dfm @@ -0,0 +1,25 @@ +object MainDataModule: TMainDataModule + OldCreateOrder = False + Height = 411 + Width = 518 + object MARSApplication: TMARSClientApplication + DefaultMediaType = 'application/json' + DefaultContentType = 'application/json' + Client = MARSClient + Left = 88 + Top = 80 + end + object MARSClient: TMARSNetClient + MARSEngineURL = 'http://localhost:8080/rest' + ConnectTimeout = 60000 + ReadTimeout = 60000 + HttpClient.Asynchronous = False + HttpClient.ConnectionTimeout = 60000 + HttpClient.ResponseTimeout = 60000 + HttpClient.AllowCookies = True + HttpClient.HandleRedirects = True + HttpClient.UserAgent = 'Embarcadero URI Client/1.0' + Left = 88 + Top = 24 + end +end diff --git a/Demos/RemoteMic/FMXClient.DataModules.Main.pas b/Demos/RemoteMic/FMXClient.DataModules.Main.pas new file mode 100644 index 00000000..a4a66361 --- /dev/null +++ b/Demos/RemoteMic/FMXClient.DataModules.Main.pas @@ -0,0 +1,32 @@ +(* + Copyright 2016, MARS-Curiosity - REST Library + + Home: https://github.com/andrea-magni/MARS +*) +unit FMXClient.DataModules.Main; + +interface + +uses + System.SysUtils, System.Classes, MARS.Client.Application, + MARS.Client.Client, MARS.Client.Client.Net +; + +type + TMainDataModule = class(TDataModule) + MARSApplication: TMARSClientApplication; + MARSClient: TMARSNetClient; + private + public + end; + +var + MainDataModule: TMainDataModule; + +implementation + +{%CLASSGROUP 'FMX.Controls.TControl'} + +{$R *.dfm} + +end. diff --git a/Demos/RemoteMic/FMXClient.DataModules.Main.vlb b/Demos/RemoteMic/FMXClient.DataModules.Main.vlb new file mode 100644 index 00000000..95e7a6a2 --- /dev/null +++ b/Demos/RemoteMic/FMXClient.DataModules.Main.vlb @@ -0,0 +1,10 @@ +[MARSApplication] +Coordinates=150,53,96,33 + +[] +Coordinates=71,70,69,33 +Visible=False + +[MainForm.BindSourceDB1] +Coordinates=0,0,144,267 + diff --git a/Demos/RemoteMic/FMXClient.Forms.Main.fmx b/Demos/RemoteMic/FMXClient.Forms.Main.fmx new file mode 100644 index 00000000..9990bf0e --- /dev/null +++ b/Demos/RemoteMic/FMXClient.Forms.Main.fmx @@ -0,0 +1,27 @@ +object MainForm: TMainForm + Left = 0 + Top = 0 + Caption = 'MARS Template Client' + ClientHeight = 480 + ClientWidth = 640 + FormFactor.Width = 320 + FormFactor.Height = 480 + FormFactor.Devices = [Desktop] + DesignerMasterStyle = 0 + object TopToolBar: TToolBar + Size.Width = 640.000000000000000000 + Size.Height = 48.000000000000000000 + Size.PlatformDefault = False + TabOrder = 0 + object TitleLabel: TLabel + Align = Center + AutoSize = True + Size.Width = 121.000000000000000000 + Size.Height = 16.000000000000000000 + Size.PlatformDefault = False + StyleLookup = 'toollabel' + TextSettings.WordWrap = False + Text = 'MARS Template Client' + end + end +end diff --git a/Demos/RemoteMic/FMXClient.Forms.Main.pas b/Demos/RemoteMic/FMXClient.Forms.Main.pas new file mode 100644 index 00000000..1b939e4d --- /dev/null +++ b/Demos/RemoteMic/FMXClient.Forms.Main.pas @@ -0,0 +1,34 @@ +(* + Copyright 2016, MARS-Curiosity - REST Library + + Home: https://github.com/andrea-magni/MARS +*) +unit FMXClient.Forms.Main; + +interface + +uses + System.SysUtils, System.Types, System.UITypes, System.Classes, System.Variants, + FMX.Types, FMX.Controls, FMX.Forms, FMX.Graphics, FMX.Dialogs, FMX.StdCtrls, + FMX.Layouts, FMX.Controls.Presentation; + +type + TMainForm = class(TForm) + TopToolBar: TToolBar; + TitleLabel: TLabel; + private + public + end; + +var + MainForm: TMainForm; + +implementation + +{$R *.fmx} + +uses + FMXClient.DataModules.Main + ; + +end. diff --git a/Demos/RemoteMic/FMXClient.Forms.Main.vlb b/Demos/RemoteMic/FMXClient.Forms.Main.vlb new file mode 100644 index 00000000..b439f67e --- /dev/null +++ b/Demos/RemoteMic/FMXClient.Forms.Main.vlb @@ -0,0 +1,26 @@ +[TopToolBar] +Coordinates=317,78,82,36 + +[MainDataModule.ItemsQueryDataSet] +Coordinates=10,10,228,212 + +[] +Coordinates=145,78,71,36 +Visible=True + +[TitleLabel] +Coordinates=236,78,71,58 + +[MainDataModule.] +Coordinates=472,428,215,249 +Visible=False + +[MainDataModule.EmployeeQuery1] +Visible=False + +[MainDataModule.EmployeeQueryDataSet] +Coordinates=100,10,253,58 + +[MainDataModule.CountryByName1] +Coordinates=10,78,216,58 + diff --git a/Demos/RemoteMic/RemoteMicClient.dpr b/Demos/RemoteMic/RemoteMicClient.dpr new file mode 100644 index 00000000..52adba22 --- /dev/null +++ b/Demos/RemoteMic/RemoteMicClient.dpr @@ -0,0 +1,21 @@ +(* + Copyright 2016, MARS-Curiosity library + + Home: https://github.com/andrea-magni/MARS +*) + program RemoteMicClient; + +uses + System.StartUpCopy, + FMX.Forms, + FMXClient.Forms.Main in 'FMXClient.Forms.Main.pas' {MainForm}, + FMXClient.DataModules.Main in 'FMXClient.DataModules.Main.pas' {MainDataModule: TDataModule}; + +{$R *.res} + +begin + Application.Initialize; + Application.CreateForm(TMainDataModule, MainDataModule); + Application.CreateForm(TMainForm, MainForm); + Application.Run; +end. diff --git a/Demos/RemoteMic/RemoteMicClient.dproj b/Demos/RemoteMic/RemoteMicClient.dproj new file mode 100644 index 00000000..5e28a596 --- /dev/null +++ b/Demos/RemoteMic/RemoteMicClient.dproj @@ -0,0 +1,1423 @@ + + + {B6B860BA-E3A5-48E9-8EC6-7FE516AE7FC4} + RemoteMicClient.dpr + True + Debug + 33937 + Application + FMX + 18.8 + Win32 + + + true + + + true + Base + true + + + true + Base + true + + + true + Base + true + + + true + Base + true + + + true + Base + true + + + true + Base + true + + + true + Base + true + + + true + Base + true + + + true + Base + true + + + true + Base + true + + + true + Base + true + + + true + Cfg_1 + true + true + + + true + Base + true + + + true + Cfg_2 + true + true + + + true + Cfg_2 + true + true + + + true + Cfg_2 + true + true + + + true + Cfg_2 + true + true + + + true + Cfg_2 + true + true + + + true + Cfg_2 + true + true + + + false + false + false + false + false + 00400000 + RemoteMicClient + 1040 + CompanyName=;FileDescription=;FileVersion=1.0.0.0;InternalName=;LegalCopyright=;LegalTrademarks=;OriginalFilename=;ProductName=;ProductVersion=1.0.0.0;Comments=;CFBundleName= + System;Xml;Data;Datasnap;Web;Soap;$(DCC_Namespace) + $(BDS)\bin\delphi_PROJECTICON.ico + $(BDS)\bin\delphi_PROJECTICNS.icns + .\bin + .\lib\$(Platform)\$(Config) + + + package=com.embarcadero.$(MSBuildProjectName);label=$(MSBuildProjectName);versionCode=1;versionName=1.0.0;persistent=False;restoreAnyVersion=False;installLocation=auto;largeHeap=False;theme=TitleBar;hardwareAccelerated=true;apiKey= + Debug + true + $(BDS)\bin\Artwork\Android\FM_LauncherIcon_36x36.png + $(BDS)\bin\Artwork\Android\FM_LauncherIcon_48x48.png + $(BDS)\bin\Artwork\Android\FM_LauncherIcon_72x72.png + $(BDS)\bin\Artwork\Android\FM_LauncherIcon_96x96.png + $(BDS)\bin\Artwork\Android\FM_LauncherIcon_144x144.png + $(BDS)\bin\Artwork\Android\FM_SplashImage_426x320.png + $(BDS)\bin\Artwork\Android\FM_SplashImage_470x320.png + $(BDS)\bin\Artwork\Android\FM_SplashImage_640x480.png + $(BDS)\bin\Artwork\Android\FM_SplashImage_960x720.png + true + true + true + true + true + true + true + true + true + true + android-support-v4.dex.jar;cloud-messaging.dex.jar;fmx.dex.jar;google-analytics-v2.dex.jar;google-play-billing.dex.jar;google-play-licensing.dex.jar;google-play-services.dex.jar + 1 + $(BDS)\bin\Artwork\Android\FM_NotificationIcon_24x24.png + $(BDS)\bin\Artwork\Android\FM_NotificationIcon_36x36.png + $(BDS)\bin\Artwork\Android\FM_NotificationIcon_48x48.png + $(BDS)\bin\Artwork\Android\FM_NotificationIcon_72x72.png + $(BDS)\bin\Artwork\Android\FM_NotificationIcon_96x96.png + + + package=com.embarcadero.$(MSBuildProjectName);label=$(MSBuildProjectName);versionCode=1;versionName=1.0.0;persistent=False;restoreAnyVersion=False;installLocation=auto;largeHeap=False;theme=TitleBar;hardwareAccelerated=true;apiKey= + Debug + true + true + Base + true + $(BDS)\bin\Artwork\Android\FM_LauncherIcon_36x36.png + $(BDS)\bin\Artwork\Android\FM_LauncherIcon_48x48.png + $(BDS)\bin\Artwork\Android\FM_LauncherIcon_72x72.png + $(BDS)\bin\Artwork\Android\FM_LauncherIcon_96x96.png + $(BDS)\bin\Artwork\Android\FM_LauncherIcon_144x144.png + $(BDS)\bin\Artwork\Android\FM_SplashImage_426x320.png + $(BDS)\bin\Artwork\Android\FM_SplashImage_470x320.png + $(BDS)\bin\Artwork\Android\FM_SplashImage_640x480.png + $(BDS)\bin\Artwork\Android\FM_SplashImage_960x720.png + true + true + true + true + true + true + true + true + true + true + android-support-v4.dex.jar;cloud-messaging.dex.jar;fmx.dex.jar;google-analytics-v2.dex.jar;google-play-billing.dex.jar;google-play-licensing.dex.jar;google-play-services.dex.jar + 1 + + + CFBundleName=$(MSBuildProjectName);CFBundleDevelopmentRegion=en;CFBundleDisplayName=$(MSBuildProjectName);CFBundleIdentifier=$(MSBuildProjectName);CFBundleInfoDictionaryVersion=7.1;CFBundleVersion=1.0.0.0;CFBundlePackageType=APPL;CFBundleSignature=????;LSRequiresIPhoneOS=true;CFBundleAllowMixedLocalizations=YES;CFBundleExecutable=$(MSBuildProjectName);UIDeviceFamily=iPhone & iPad;CFBundleResourceSpecification=ResourceRules.plist;NSLocationAlwaysUsageDescription=The reason for accessing the location information of the user;NSLocationWhenInUseUsageDescription=The reason for accessing the location information of the user;FMLocalNotificationPermission=false;UIBackgroundModes=;NSContactsUsageDescription=The reason for accessing the contacts;NSPhotoLibraryUsageDescription=The reason for accessing the photo library;NSCameraUsageDescription=The reason for accessing the camera + iPhoneAndiPad + true + Debug + $(MSBuildProjectName) + $(BDS)\bin\Artwork\iOS\iPhone\FM_ApplicationIcon_60x60.png + $(BDS)\bin\Artwork\iOS\iPhone\FM_ApplicationIcon_120x120.png + $(BDS)\bin\Artwork\iOS\iPhone\FM_SpotlightSearchIcon_40x40.png + $(BDS)\bin\Artwork\iOS\iPhone\FM_SpotlightSearchIcon_80x80.png + $(BDS)\bin\Artwork\iOS\iPad\FM_SpotlightSearchIcon_40x40.png + $(BDS)\bin\Artwork\iOS\iPad\FM_SpotlightSearchIcon_80x80.png + $(BDS)\bin\Artwork\iOS\iPad\FM_ApplicationIcon_76x76.png + $(BDS)\bin\Artwork\iOS\iPad\FM_ApplicationIcon_152x152.png + $(BDS)\bin\Artwork\iOS\iPad\FM_LaunchImagePortrait_768x1024.png + $(BDS)\bin\Artwork\iOS\iPad\FM_LaunchImageLandscape_1024x768.png + $(BDS)\bin\Artwork\iOS\iPad\FM_LaunchImagePortrait_1536x2048.png + $(BDS)\bin\Artwork\iOS\iPad\FM_LaunchImageLandscape_2048x1536.png + $(BDS)\bin\Artwork\iOS\iPhone\FM_ApplicationIcon_87x87.png + $(BDS)\bin\Artwork\iOS\iPhone\FM_ApplicationIcon_180x180.png + $(BDS)\bin\Artwork\iOS\iPhone\FM_LaunchImage_750x1334.png + $(BDS)\bin\Artwork\iOS\iPhone\FM_LaunchImage_1242x2208.png + $(BDS)\bin\Artwork\iOS\iPhone\FM_LaunchImage_2208x1242.png + $(BDS)\bin\Artwork\iOS\iPhone\FM_LaunchImage_1125x2436.png + $(BDS)\bin\Artwork\iOS\iPhone\FM_LaunchImage_2436x1125.png + $(BDS)\bin\Artwork\iOS\iPhone\FM_SpotlightSearchIcon_120x120.png + $(BDS)\bin\Artwork\iOS\iPhone\FM_LaunchImage_828x1792.png + $(BDS)\bin\Artwork\iOS\iPhone\FM_LaunchImage_1136x640.png + $(BDS)\bin\Artwork\iOS\iPhone\FM_LaunchImage_1242x2688.png + $(BDS)\bin\Artwork\iOS\iPhone\FM_LaunchImage_1334x750.png + $(BDS)\bin\Artwork\iOS\iPhone\FM_LaunchImage_1792x828.png + $(BDS)\bin\Artwork\iOS\iPhone\FM_LaunchImage_2688x1242.png + $(BDS)\bin\Artwork\iOS\iPad\FM_ApplicationIcon_167x167.png + $(BDS)\bin\Artwork\iOS\iPad\FM_LaunchImagePortrait_1668x2224.png + $(BDS)\bin\Artwork\iOS\iPad\FM_LaunchImagePortrait_1668x2388.png + $(BDS)\bin\Artwork\iOS\iPad\FM_LaunchImagePortrait_2048x2732.png + $(BDS)\bin\Artwork\iOS\iPad\FM_LaunchImageLandscape_2224x1668.png + $(BDS)\bin\Artwork\iOS\iPad\FM_LaunchImageLandscape_2388x1668.png + $(BDS)\bin\Artwork\iOS\iPad\FM_LaunchImageLandscape_2732x2048.png + + + $(BDS)\bin\Artwork\iOS\iPhone\FM_ApplicationIcon_87x87.png + $(BDS)\bin\Artwork\iOS\iPhone\FM_ApplicationIcon_180x180.png + $(BDS)\bin\Artwork\iOS\iPhone\FM_LaunchImage_750x1334.png + $(BDS)\bin\Artwork\iOS\iPhone\FM_LaunchImage_1242x2208.png + $(BDS)\bin\Artwork\iOS\iPhone\FM_LaunchImage_2208x1242.png + $(BDS)\bin\Artwork\iOS\iPhone\FM_LaunchImage_1125x2436.png + $(BDS)\bin\Artwork\iOS\iPhone\FM_LaunchImage_2436x1125.png + $(BDS)\bin\Artwork\iOS\iPhone\FM_SpotlightSearchIcon_120x120.png + $(BDS)\bin\Artwork\iOS\iPhone\FM_LaunchImage_828x1792.png + $(BDS)\bin\Artwork\iOS\iPhone\FM_LaunchImage_1136x640.png + $(BDS)\bin\Artwork\iOS\iPhone\FM_LaunchImage_1242x2688.png + $(BDS)\bin\Artwork\iOS\iPhone\FM_LaunchImage_1334x750.png + $(BDS)\bin\Artwork\iOS\iPhone\FM_LaunchImage_1792x828.png + $(BDS)\bin\Artwork\iOS\iPhone\FM_LaunchImage_2688x1242.png + $(BDS)\bin\Artwork\iOS\iPad\FM_ApplicationIcon_167x167.png + $(BDS)\bin\Artwork\iOS\iPad\FM_LaunchImagePortrait_1668x2224.png + $(BDS)\bin\Artwork\iOS\iPad\FM_LaunchImagePortrait_1668x2388.png + $(BDS)\bin\Artwork\iOS\iPad\FM_LaunchImagePortrait_2048x2732.png + $(BDS)\bin\Artwork\iOS\iPad\FM_LaunchImageLandscape_2224x1668.png + $(BDS)\bin\Artwork\iOS\iPad\FM_LaunchImageLandscape_2388x1668.png + $(BDS)\bin\Artwork\iOS\iPad\FM_LaunchImageLandscape_2732x2048.png + + + CFBundleName=$(MSBuildProjectName);CFBundleDevelopmentRegion=en;CFBundleDisplayName=$(MSBuildProjectName);CFBundleIdentifier=$(MSBuildProjectName);CFBundleInfoDictionaryVersion=7.1;CFBundleVersion=1.0.0.0;CFBundlePackageType=APPL;CFBundleSignature=????;LSRequiresIPhoneOS=true;CFBundleAllowMixedLocalizations=YES;CFBundleExecutable=$(MSBuildProjectName);UIDeviceFamily=iPhone & iPad;CFBundleResourceSpecification=ResourceRules.plist;NSLocationAlwaysUsageDescription=The reason for accessing the location information of the user;NSLocationWhenInUseUsageDescription=The reason for accessing the location information of the user;FMLocalNotificationPermission=false;UIBackgroundModes=;NSContactsUsageDescription=The reason for accessing the contacts;NSPhotoLibraryUsageDescription=The reason for accessing the photo library;NSCameraUsageDescription=The reason for accessing the camera + iPhoneAndiPad + true + $(BDS)\bin\Artwork\iOS\iPhone\FM_ApplicationIcon_60x60.png + $(BDS)\bin\Artwork\iOS\iPhone\FM_ApplicationIcon_120x120.png + $(BDS)\bin\Artwork\iOS\iPhone\FM_SpotlightSearchIcon_40x40.png + $(BDS)\bin\Artwork\iOS\iPhone\FM_SpotlightSearchIcon_80x80.png + $(BDS)\bin\Artwork\iOS\iPad\FM_SpotlightSearchIcon_40x40.png + $(BDS)\bin\Artwork\iOS\iPad\FM_SpotlightSearchIcon_80x80.png + $(BDS)\bin\Artwork\iOS\iPad\FM_ApplicationIcon_76x76.png + $(BDS)\bin\Artwork\iOS\iPad\FM_ApplicationIcon_152x152.png + $(BDS)\bin\Artwork\iOS\iPad\FM_LaunchImagePortrait_768x1024.png + $(BDS)\bin\Artwork\iOS\iPad\FM_LaunchImageLandscape_1024x768.png + $(BDS)\bin\Artwork\iOS\iPad\FM_LaunchImagePortrait_1536x2048.png + $(BDS)\bin\Artwork\iOS\iPad\FM_LaunchImageLandscape_2048x1536.png + $(BDS)\bin\Artwork\iOS\iPhone\FM_ApplicationIcon_87x87.png + $(BDS)\bin\Artwork\iOS\iPhone\FM_ApplicationIcon_180x180.png + $(BDS)\bin\Artwork\iOS\iPhone\FM_LaunchImage_750x1334.png + $(BDS)\bin\Artwork\iOS\iPhone\FM_LaunchImage_1242x2208.png + $(BDS)\bin\Artwork\iOS\iPhone\FM_LaunchImage_2208x1242.png + $(BDS)\bin\Artwork\iOS\iPhone\FM_LaunchImage_1125x2436.png + $(BDS)\bin\Artwork\iOS\iPhone\FM_LaunchImage_2436x1125.png + $(BDS)\bin\Artwork\iOS\iPhone\FM_SpotlightSearchIcon_120x120.png + $(BDS)\bin\Artwork\iOS\iPhone\FM_LaunchImage_828x1792.png + $(BDS)\bin\Artwork\iOS\iPhone\FM_LaunchImage_1136x640.png + $(BDS)\bin\Artwork\iOS\iPhone\FM_LaunchImage_1242x2688.png + $(BDS)\bin\Artwork\iOS\iPhone\FM_LaunchImage_1334x750.png + $(BDS)\bin\Artwork\iOS\iPhone\FM_LaunchImage_1792x828.png + $(BDS)\bin\Artwork\iOS\iPhone\FM_LaunchImage_2688x1242.png + $(BDS)\bin\Artwork\iOS\iPad\FM_ApplicationIcon_167x167.png + $(BDS)\bin\Artwork\iOS\iPad\FM_LaunchImagePortrait_1668x2224.png + $(BDS)\bin\Artwork\iOS\iPad\FM_LaunchImagePortrait_1668x2388.png + $(BDS)\bin\Artwork\iOS\iPad\FM_LaunchImagePortrait_2048x2732.png + $(BDS)\bin\Artwork\iOS\iPad\FM_LaunchImageLandscape_2224x1668.png + $(BDS)\bin\Artwork\iOS\iPad\FM_LaunchImageLandscape_2388x1668.png + $(BDS)\bin\Artwork\iOS\iPad\FM_LaunchImageLandscape_2732x2048.png + + + /usr/bin/xterm -e "%debuggee%" + (None) + + + $(BDS)\bin\delphi_PROJECTICNS.icns + + + CFBundleName=$(MSBuildProjectName);CFBundleDisplayName=$(MSBuildProjectName);CFBundleIdentifier=$(MSBuildProjectName);CFBundleVersion=1.0.0;CFBundleShortVersionString=1.0.0;CFBundlePackageType=APPL;CFBundleSignature=????;CFBundleAllowMixedLocalizations=YES;CFBundleExecutable=$(MSBuildProjectName);NSHighResolutionCapable=true;LSApplicationCategoryType=public.app-category.utilities;NSLocationUsageDescription=The reason for accessing the location information of the user;NSContactsUsageDescription=The reason for accessing the contacts + Debug + true + true + Base + true + + + Winapi;System.Win;Data.Win;Datasnap.Win;Web.Win;Soap.Win;Xml.Win;Bde;$(DCC_Namespace) + Debug + true + CompanyName=;FileDescription=$(MSBuildProjectName);FileVersion=1.0.0.0;InternalName=;LegalCopyright=;LegalTrademarks=;OriginalFilename=;ProductName=$(MSBuildProjectName);ProductVersion=1.0.0.0;Comments=;ProgramID=com.embarcadero.$(MSBuildProjectName) + 1033 + $(BDS)\bin\default_app.manifest + true + $(BDS)\bin\Artwork\Windows\UWP\delphi_UwpDefault_44.png + $(BDS)\bin\Artwork\Windows\UWP\delphi_UwpDefault_150.png + RemoteMicServer_Icon.ico + + + $(BDS)\bin\default_app.manifest + true + $(BDS)\bin\Artwork\Windows\UWP\delphi_UwpDefault_44.png + $(BDS)\bin\Artwork\Windows\UWP\delphi_UwpDefault_150.png + + + RELEASE;$(DCC_Define) + 0 + false + 0 + + + true + PerMonitor + + + DEBUG;$(DCC_Define) + false + true + + + true + + + Debug + + + true + + + $(BDS)\bin\delphi_PROJECTICNS.icns + + + true + true + Cfg_2 + true + + + true + true + 1033 + CompanyName=;FileDescription=$(MSBuildProjectName);FileVersion=1.0.0.0;InternalName=;LegalCopyright=;LegalTrademarks=;OriginalFilename=;ProductName=$(MSBuildProjectName);ProductVersion=1.0.0.0;Comments=;ProgramID=com.embarcadero.$(MSBuildProjectName) + RemoteMicServer_Icon.ico + PerMonitor + + + + MainSource + + +
MainForm
+
+ +
MainDataModule
+ TDataModule +
+ + Cfg_2 + Base + + + Base + + + Cfg_1 + Base + +
+ + Delphi.Personality.12 + + + + + RemoteMicClient.dpr + + + Microsoft Office 2000 Sample Automation Server Wrapper Components + Microsoft Office XP Sample Automation Server Wrapper Components + + + + True + True + False + True + False + True + False + False + True + False + + + + + true + + + + + ic_launcher.png + true + + + + + ic_launcher.png + true + + + + + libRemoteMicClient.so + true + + + + + ic_launcher.png + true + + + + + true + + + + + true + + + + + splash_image.png + true + + + + + true + + + + + splash_image.png + true + + + + + splash_image.png + true + + + + + splash_image.png + true + + + + + true + + + + + libRemoteMicClient.so + true + + + + + true + + + + + classes.dex + true + + + + + ic_launcher.png + true + + + + + ic_launcher.png + true + + + + + true + + + + + true + + + + + libRemoteMicClient.so + true + + + + + true + + + + + true + + + + + 1 + + + Contents\MacOS + 1 + + + 0 + + + + + classes + 1 + + + classes + 1 + + + + + res\xml + 1 + + + res\xml + 1 + + + + + library\lib\armeabi-v7a + 1 + + + + + library\lib\armeabi + 1 + + + library\lib\armeabi + 1 + + + + + library\lib\armeabi-v7a + 1 + + + + + library\lib\mips + 1 + + + library\lib\mips + 1 + + + + + library\lib\armeabi-v7a + 1 + + + library\lib\arm64-v8a + 1 + + + + + library\lib\armeabi-v7a + 1 + + + + + res\drawable + 1 + + + res\drawable + 1 + + + + + res\values + 1 + + + res\values + 1 + + + + + res\values-v21 + 1 + + + res\values-v21 + 1 + + + + + res\values + 1 + + + res\values + 1 + + + + + res\drawable + 1 + + + res\drawable + 1 + + + + + res\drawable-xxhdpi + 1 + + + res\drawable-xxhdpi + 1 + + + + + res\drawable-ldpi + 1 + + + res\drawable-ldpi + 1 + + + + + res\drawable-mdpi + 1 + + + res\drawable-mdpi + 1 + + + + + res\drawable-hdpi + 1 + + + res\drawable-hdpi + 1 + + + + + res\drawable-xhdpi + 1 + + + res\drawable-xhdpi + 1 + + + + + res\drawable-mdpi + 1 + + + res\drawable-mdpi + 1 + + + + + res\drawable-hdpi + 1 + + + res\drawable-hdpi + 1 + + + + + res\drawable-xhdpi + 1 + + + res\drawable-xhdpi + 1 + + + + + res\drawable-xxhdpi + 1 + + + res\drawable-xxhdpi + 1 + + + + + res\drawable-xxxhdpi + 1 + + + res\drawable-xxxhdpi + 1 + + + + + res\drawable-small + 1 + + + res\drawable-small + 1 + + + + + res\drawable-normal + 1 + + + res\drawable-normal + 1 + + + + + res\drawable-large + 1 + + + res\drawable-large + 1 + + + + + res\drawable-xlarge + 1 + + + res\drawable-xlarge + 1 + + + + + res\values + 1 + + + res\values + 1 + + + + + 1 + + + Contents\MacOS + 1 + + + 0 + + + + + Contents\MacOS + 1 + .framework + + + Contents\MacOS + 1 + .framework + + + 0 + + + + + 1 + .dylib + + + 1 + .dylib + + + 1 + .dylib + + + Contents\MacOS + 1 + .dylib + + + Contents\MacOS + 1 + .dylib + + + 0 + .dll;.bpl + + + + + 1 + .dylib + + + 1 + .dylib + + + 1 + .dylib + + + Contents\MacOS + 1 + .dylib + + + Contents\MacOS + 1 + .dylib + + + 0 + .bpl + + + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + Contents\Resources\StartUp\ + 0 + + + Contents\Resources\StartUp\ + 0 + + + 0 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + + + ..\$(PROJECTNAME).app.dSYM\Contents\Resources\DWARF + 1 + + + ..\$(PROJECTNAME).app.dSYM\Contents\Resources\DWARF + 1 + + + + + 1 + + + 1 + + + + + ..\ + 1 + + + ..\ + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + ..\$(PROJECTNAME).app.dSYM\Contents\Resources\DWARF + 1 + + + + + ..\ + 1 + + + ..\ + 1 + + + + + Contents + 1 + + + Contents + 1 + + + + + Contents\Resources + 1 + + + Contents\Resources + 1 + + + + + library\lib\armeabi-v7a + 1 + + + library\lib\arm64-v8a + 1 + + + 1 + + + 1 + + + 1 + + + 1 + + + Contents\MacOS + 1 + + + Contents\MacOS + 1 + + + 0 + + + + + library\lib\armeabi-v7a + 1 + + + + + 1 + + + 1 + + + + + Assets + 1 + + + Assets + 1 + + + + + Assets + 1 + + + Assets + 1 + + + + + + + + + + + + + + + 12 + + + + +
diff --git a/Demos/RemoteMic/RemoteMicProjectGroup.groupproj b/Demos/RemoteMic/RemoteMicProjectGroup.groupproj new file mode 100644 index 00000000..bde30c09 --- /dev/null +++ b/Demos/RemoteMic/RemoteMicProjectGroup.groupproj @@ -0,0 +1,120 @@ + + + {6E23DEFF-F737-42C3-B8AD-2549B8F67C93} + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Default.Personality.12 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Demos/RemoteMic/RemoteMicServerApacheModule.dpr b/Demos/RemoteMic/RemoteMicServerApacheModule.dpr new file mode 100644 index 00000000..6185e2cd --- /dev/null +++ b/Demos/RemoteMic/RemoteMicServerApacheModule.dpr @@ -0,0 +1,54 @@ +(* + Copyright 2016, MARS-Curiosity - REST Library + + Home: https://github.com/andrea-magni/MARS +*) +library RemoteMicServerApacheModule; + +uses + {$IFDEF MSWINDOWS} + Winapi.ActiveX, System.Win.ComObj, + {$ENDIF } + Web.WebBroker, + Web.ApacheApp, + Web.HTTPD24Impl, + Server.WebModule in 'Server.WebModule.pas' {ServerWebModule: TWebModule}, + Server.Ignition in 'Server.Ignition.pas', + Server.Resources in 'Server.Resources.pas'; + +{$R *.res} + +// httpd.conf entries: +// +(* + LoadModule marstemplate_module modules/mod_marstemplate.dll + + + SetHandler mod_marstemplate-handler + +*) +// +// These entries assume that the output directory for this project is the apache/modules directory. +// +// httpd.conf entries should be different if the project is changed in these ways: +// 1. The TApacheModuleData variable name is changed. +// 2. The project is renamed. +// 3. The output directory is not the apache/modules directory. +// 4. The dynamic library extension depends on a platform. Use .dll on Windows and .so on Linux. +// + +// Declare exported variable so that Apache can access this module. +var + GModuleData: TApacheModuleData; +exports + GModuleData name 'marstemplate_module'; + +begin +{$IFDEF MSWINDOWS} + CoInitFlags := COINIT_MULTITHREADED; +{$ENDIF} + Web.ApacheApp.InitApplication(@GModuleData); + Application.Initialize; + Application.WebModuleClass := WebModuleClass; + Application.Run; +end. diff --git a/Demos/RemoteMic/RemoteMicServerApacheModule.dproj b/Demos/RemoteMic/RemoteMicServerApacheModule.dproj new file mode 100644 index 00000000..1f6cea83 --- /dev/null +++ b/Demos/RemoteMic/RemoteMicServerApacheModule.dproj @@ -0,0 +1,985 @@ + + + {0D982E91-6C92-4321-9078-458448B01536} + 18.8 + None + RemoteMicServerApacheModule.dpr + True + Release + Win32 + 129 + Library + + + true + + + true + Base + true + + + true + Base + true + + + true + Base + true + + + true + Base + true + + + true + Cfg_1 + true + true + + + true + Base + true + + + .\lib\$(Platform)\$(Config) + .\bin + false + false + false + false + false + true + RESTComponents;emsclientfiredac;DataSnapFireDAC;FireDACIBDriver;emsclient;FireDACCommon;RESTBackendComponents;soapserver;CloudService;FireDACCommonDriver;inet;FireDAC;FireDACSqliteDriver;soaprtl;soapmidas;$(DCC_UsePackage) + System;Xml;Data;Datasnap;Web;Soap;$(DCC_Namespace) + $(BDS)\bin\delphi_PROJECTICON.ico + $(BDS)\bin\delphi_PROJECTICNS.icns + RemoteMicServerApacheModule + + + DataSnapServerMidas;FireDACADSDriver;DatasnapConnectorsFreePascal;FireDACMSSQLDriver;inetdb;emsedge;dbexpress;IndyCore;dsnap;DataSnapCommon;DataSnapConnectors;bindengine;FireDACOracleDriver;FireDACMySQLDriver;FireDACCommonODBC;DataSnapClient;IndySystem;FireDACDb2Driver;FireDACInfxDriver;emshosting;FireDACPgDriver;FireDACASADriver;FireDACTDataDriver;DbxCommonDriver;DataSnapServer;xmlrtl;DataSnapNativeClient;rtl;DbxClientDriver;CustomIPTransport;bindcomp;dbxcds;FireDACODBCDriver;DataSnapIndy10ServerTransport;dsnapxml;dbrtl;FireDACMongoDBDriver;IndyProtocols;$(DCC_UsePackage) + true + /usr/bin/xterm -e "%debuggee%" + (None) + + + DBXSqliteDriver;DataSnapServerMidas;DBXDb2Driver;DBXInterBaseDriver;vclactnband;vclFireDAC;svnui;tethering;FireDACADSDriver;DBXMSSQLDriver;DatasnapConnectorsFreePascal;FireDACMSSQLDriver;vcltouch;vcldb;bindcompfmx;svn;Intraweb;DBXOracleDriver;inetdb;FmxTeeUI;emsedge;fmx;fmxdae;vclib;FireDACDBXDriver;dbexpress;IndyCore;vclx;dsnap;DataSnapCommon;DataSnapConnectors;MARS.Utils;VCLRESTComponents;vclie;bindengine;DBXMySQLDriver;FireDACOracleDriver;FireDACMySQLDriver;DBXFirebirdDriver;FireDACCommonODBC;DataSnapClient;bindcompdbx;IndyIPCommon;vcl;DBXSybaseASEDriver;IndyIPServer;IndySystem;FireDACDb2Driver;dsnapcon;FireDACMSAccDriver;fmxFireDAC;FireDACInfxDriver;vclimg;TeeDB;emshosting;FireDACPgDriver;ibmonitor;FireDACASADriver;DBXOdbcDriver;FireDACTDataDriver;FMXTee;DbxCommonDriver;ibxpress;Tee;DataSnapServer;xmlrtl;DataSnapNativeClient;fmxobj;vclwinx;ibxbindings;rtl;FireDACDSDriver;DbxClientDriver;DBXSybaseASADriver;CustomIPTransport;vcldsnap;CodeSiteExpressPkg;bindcomp;appanalytics;DBXInformixDriver;IndyIPClient;bindcompvcl;TeeUI;dbxcds;VclSmp;adortl;FireDACODBCDriver;DataSnapIndy10ServerTransport;dsnapxml;DataSnapProviderClient;dbrtl;inetdbxpress;FireDACMongoDBDriver;IndyProtocols;fmxase;$(DCC_UsePackage) + Winapi;System.Win;Data.Win;Datasnap.Win;Web.Win;Soap.Win;Xml.Win;Bde;$(DCC_Namespace) + Debug + true + CompanyName=;FileDescription=$(MSBuildProjectName);FileVersion=1.0.0.0;InternalName=;LegalCopyright=;LegalTrademarks=;OriginalFilename=;ProgramID=com.embarcadero.$(MSBuildProjectName);ProductName=$(MSBuildProjectName);ProductVersion=1.0.0.0;Comments= + 1033 + (None) + + + DBXSqliteDriver;DataSnapServerMidas;DBXDb2Driver;DBXInterBaseDriver;vclactnband;vclFireDAC;tethering;FireDACADSDriver;DBXMSSQLDriver;DatasnapConnectorsFreePascal;FireDACMSSQLDriver;vcltouch;vcldb;bindcompfmx;Intraweb;DBXOracleDriver;inetdb;FmxTeeUI;emsedge;fmx;fmxdae;vclib;FireDACDBXDriver;dbexpress;IndyCore;vclx;dsnap;DataSnapCommon;DataSnapConnectors;MARS.Utils;VCLRESTComponents;vclie;bindengine;DBXMySQLDriver;FireDACOracleDriver;FireDACMySQLDriver;DBXFirebirdDriver;FireDACCommonODBC;DataSnapClient;bindcompdbx;IndyIPCommon;vcl;DBXSybaseASEDriver;IndyIPServer;IndySystem;FireDACDb2Driver;dsnapcon;FireDACMSAccDriver;fmxFireDAC;FireDACInfxDriver;vclimg;TeeDB;emshosting;FireDACPgDriver;ibmonitor;FireDACASADriver;DBXOdbcDriver;FireDACTDataDriver;FMXTee;DbxCommonDriver;ibxpress;Tee;DataSnapServer;xmlrtl;DataSnapNativeClient;fmxobj;vclwinx;ibxbindings;rtl;FireDACDSDriver;DbxClientDriver;DBXSybaseASADriver;CustomIPTransport;vcldsnap;bindcomp;appanalytics;DBXInformixDriver;IndyIPClient;bindcompvcl;TeeUI;dbxcds;VclSmp;adortl;FireDACODBCDriver;DataSnapIndy10ServerTransport;dsnapxml;DataSnapProviderClient;dbrtl;inetdbxpress;FireDACMongoDBDriver;IndyProtocols;fmxase;$(DCC_UsePackage) + + + DEBUG;$(DCC_Define) + true + false + true + true + true + + + false + true + 1033 + (None) + + + false + RELEASE;$(DCC_Define) + 0 + 0 + + + + MainSource + + +
ServerWebModule
+ dfm + TWebModule +
+ + + + Cfg_2 + Base + + + Base + + + Cfg_1 + Base + +
+ + Delphi.Personality.12 + Library + + + + RemoteMicServerApacheModule.dpr + + + Microsoft Office 2000 Sample Automation Server Wrapper Components + Microsoft Office XP Sample Automation Server Wrapper Components + + + + + + true + + + + + RemoteMicServerApacheModule.dll + true + + + + + true + + + + + true + + + + + true + + + + + true + + + + + true + + + + + libRemoteMicServerApacheModule.so + true + + + + + 1 + + + 0 + + + + + classes + 1 + + + classes + 1 + + + + + res\xml + 1 + + + res\xml + 1 + + + + + library\lib\armeabi-v7a + 1 + + + + + library\lib\armeabi + 1 + + + library\lib\armeabi + 1 + + + + + library\lib\armeabi-v7a + 1 + + + + + library\lib\mips + 1 + + + library\lib\mips + 1 + + + + + library\lib\armeabi-v7a + 1 + + + library\lib\arm64-v8a + 1 + + + + + library\lib\armeabi-v7a + 1 + + + + + res\drawable + 1 + + + res\drawable + 1 + + + + + res\values + 1 + + + res\values + 1 + + + + + res\values-v21 + 1 + + + res\values-v21 + 1 + + + + + res\values + 1 + + + res\values + 1 + + + + + res\drawable + 1 + + + res\drawable + 1 + + + + + res\drawable-xxhdpi + 1 + + + res\drawable-xxhdpi + 1 + + + + + res\drawable-ldpi + 1 + + + res\drawable-ldpi + 1 + + + + + res\drawable-mdpi + 1 + + + res\drawable-mdpi + 1 + + + + + res\drawable-hdpi + 1 + + + res\drawable-hdpi + 1 + + + + + res\drawable-xhdpi + 1 + + + res\drawable-xhdpi + 1 + + + + + res\drawable-mdpi + 1 + + + res\drawable-mdpi + 1 + + + + + res\drawable-hdpi + 1 + + + res\drawable-hdpi + 1 + + + + + res\drawable-xhdpi + 1 + + + res\drawable-xhdpi + 1 + + + + + res\drawable-xxhdpi + 1 + + + res\drawable-xxhdpi + 1 + + + + + res\drawable-xxxhdpi + 1 + + + res\drawable-xxxhdpi + 1 + + + + + res\drawable-small + 1 + + + res\drawable-small + 1 + + + + + res\drawable-normal + 1 + + + res\drawable-normal + 1 + + + + + res\drawable-large + 1 + + + res\drawable-large + 1 + + + + + res\drawable-xlarge + 1 + + + res\drawable-xlarge + 1 + + + + + res\values + 1 + + + res\values + 1 + + + + + 1 + + + 1 + + + 0 + + + + + 1 + .framework + + + 1 + .framework + + + 0 + + + + + 1 + .dylib + + + 1 + .dylib + + + 0 + .dll;.bpl + + + + + 1 + .dylib + + + 1 + .dylib + + + 1 + .dylib + + + 1 + .dylib + + + 1 + .dylib + + + 0 + .bpl + + + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + + + ..\$(PROJECTNAME).app.dSYM\Contents\Resources\DWARF + 1 + + + ..\$(PROJECTNAME).app.dSYM\Contents\Resources\DWARF + 1 + + + + + + + + 1 + + + 1 + + + 1 + + + + + + + + Contents\Resources + 1 + + + Contents\Resources + 1 + + + + + library\lib\armeabi-v7a + 1 + + + library\lib\arm64-v8a + 1 + + + 1 + + + 1 + + + 1 + + + 1 + + + 1 + + + 1 + + + 0 + + + + + library\lib\armeabi-v7a + 1 + + + + + 1 + + + 1 + + + + + Assets + 1 + + + Assets + 1 + + + + + Assets + 1 + + + Assets + 1 + + + + + + + + + + + + + + + True + True + False + + + 12 + + + + +
diff --git a/Demos/RemoteMic/RemoteMicServerApplication.dpr b/Demos/RemoteMic/RemoteMicServerApplication.dpr new file mode 100644 index 00000000..da6b0674 --- /dev/null +++ b/Demos/RemoteMic/RemoteMicServerApplication.dpr @@ -0,0 +1,23 @@ +(* + Copyright 2016, MARS-Curiosity library + + Home: https://github.com/andrea-magni/MARS +*) + program RemoteMicServerApplication; + +uses + Forms, + Server.Forms.Main in 'Server.Forms.Main.pas' {MainForm}, + Server.Resources in 'Server.Resources.pas', + Server.Ignition in 'Server.Ignition.pas'; + +{$R *.res} + +begin + ReportMemoryLeaksOnShutdown := True; + + Application.Initialize; + Application.MainFormOnTaskbar := True; + Application.CreateForm(TMainForm, MainForm); + Application.Run; +end. diff --git a/Demos/RemoteMic/RemoteMicServerApplication.dproj b/Demos/RemoteMic/RemoteMicServerApplication.dproj new file mode 100644 index 00000000..1ee97abd --- /dev/null +++ b/Demos/RemoteMic/RemoteMicServerApplication.dproj @@ -0,0 +1,190 @@ + + + {0A5E1DDC-90B4-4B41-A7DC-2B0FC45D4349} + RemoteMicServerApplication.dpr + True + Release + 3 + Application + VCL + 18.8 + Win32 + + + true + + + true + Base + true + + + true + Base + true + + + true + Base + true + + + true + Cfg_1 + true + true + + + true + Cfg_1 + true + true + + + true + Base + true + + + true + Cfg_2 + true + true + + + true + Cfg_2 + true + true + + + true + Cfg_2 + true + true + + + false + false + false + false + false + 00400000 + RemoteMicServerApplication + Vcl;Vcl.Imaging;Vcl.Touch;Vcl.Samples;Vcl.Shell;System;Xml;Data;Datasnap;Web;Soap;$(DCC_Namespace) + 1040 + CompanyName=;FileDescription=;FileVersion=1.0.0.0;InternalName=;LegalCopyright=;LegalTrademarks=;OriginalFilename=;ProductName=;ProductVersion=1.0.0.0;Comments=;CFBundleName= + .\bin + .\lib\$(Platform)\$(Config) + + + Winapi;System.Win;Data.Win;Datasnap.Win;Web.Win;Soap.Win;Xml.Win;Bde;$(DCC_Namespace) + Debug + true + CompanyName=;FileDescription=$(MSBuildProjectName);FileVersion=1.0.0.0;InternalName=;LegalCopyright=;LegalTrademarks=;OriginalFilename=;ProductName=$(MSBuildProjectName);ProductVersion=1.0.0.0;Comments=;ProgramID=com.embarcadero.$(MSBuildProjectName) + 1033 + $(BDS)\bin\default_app.manifest + RemoteMicServer_Icon.ico + true + $(BDS)\bin\Artwork\Windows\UWP\delphi_UwpDefault_44.png + $(BDS)\bin\Artwork\Windows\UWP\delphi_UwpDefault_150.png + + + $(BDS)\bin\default_app.manifest + RemoteMicServer_Icon.ico + true + $(BDS)\bin\Artwork\Windows\UWP\delphi_UwpDefault_44.png + $(BDS)\bin\Artwork\Windows\UWP\delphi_UwpDefault_150.png + Winapi;System.Win;Data.Win;Datasnap.Win;Web.Win;Soap.Win;Xml.Win;$(DCC_Namespace) + Debug + true + CompanyName=;FileDescription=$(MSBuildProjectName);FileVersion=1.0.0.0;InternalName=;LegalCopyright=;LegalTrademarks=;OriginalFilename=;ProgramID=com.embarcadero.$(MSBuildProjectName);ProductName=$(MSBuildProjectName);ProductVersion=1.0.0.0;Comments= + 1033 + + + RELEASE;$(DCC_Define) + 0 + false + 0 + + + true + true + 1033 + CompanyName=;FileDescription=$(MSBuildProjectName);FileVersion=1.0.0.0;InternalName=;LegalCopyright=;LegalTrademarks=;OriginalFilename=;ProductName=$(MSBuildProjectName);ProductVersion=1.0.0.0;Comments=;ProgramID=com.embarcadero.$(MSBuildProjectName) + RemoteMicServer_Icon.ico + PerMonitor + + + true + true + 1033 + CompanyName=;FileDescription=$(MSBuildProjectName);FileVersion=1.0.0.0;InternalName=;LegalCopyright=;LegalTrademarks=;OriginalFilename=;ProgramID=com.embarcadero.$(MSBuildProjectName);ProductName=$(MSBuildProjectName);ProductVersion=1.0.0.0;Comments= + PerMonitor + + + DEBUG;$(DCC_Define) + false + true + + + Debug + + + true + true + 1033 + CompanyName=;FileDescription=$(MSBuildProjectName);FileVersion=1.0.0.0;InternalName=;LegalCopyright=;LegalTrademarks=;OriginalFilename=;ProductName=$(MSBuildProjectName);ProductVersion=1.0.0.0;Comments=;ProgramID=com.embarcadero.$(MSBuildProjectName) + RemoteMicServer_Icon.ico + PerMonitor + + + true + PerMonitor + + + + MainSource + + +
MainForm
+
+ + + + Cfg_2 + Base + + + Base + + + Cfg_1 + Base + +
+ + Delphi.Personality.12 + + + + + RemoteMicServerApplication.dpr + + + Microsoft Office 2000 Sample Automation Server Wrapper Components + Microsoft Office XP Sample Automation Server Wrapper Components + + + + False + False + True + True + + + 12 + + + +
diff --git a/Demos/RemoteMic/RemoteMicServerConsoleApplication.dpr b/Demos/RemoteMic/RemoteMicServerConsoleApplication.dpr new file mode 100644 index 00000000..78192f3d --- /dev/null +++ b/Demos/RemoteMic/RemoteMicServerConsoleApplication.dpr @@ -0,0 +1,201 @@ +(* + Copyright 2016, MARS-Curiosity - REST Library + + Home: https://github.com/andrea-magni/MARS +*) +program RemoteMicServerConsoleApplication; +{$APPTYPE CONSOLE} + +{$I MARS.inc} + +uses +{$ifdef DelphiXE3_UP} + System.SysUtils, + System.Types, +// IPPeerServer, IPPeerAPI, + IdHTTPWebBrokerBridge, + IdSchedulerOfThreadPool, + Web.WebReq, + Web.WebBroker, +{$else} + SysUtils, StrUtils, + Types, + IdHTTPWebBrokerBridge, + IdSchedulerOfThreadPool, + WebReq, + WebBroker, +{$endif} + IdContext, + ServerConst in 'ServerConst.pas', + Server.WebModule in 'Server.WebModule.pas' {ServerWebModule: TWebModule}, + Server.Ignition in 'Server.Ignition.pas'; + +{$R *.res} + +type + TDummyIndyServer = class + public + procedure ParseAuthenticationHandler(AContext: TIdContext; + const AAuthType, AAuthData: String; var VUsername, VPassword: String; + var VHandled: Boolean); virtual; + end; + + +procedure StartServer(const AServer: TIdHTTPWebBrokerBridge); +begin + if not (AServer.Active) then + begin + AServer.DefaultPort := TServerEngine.Default.Port; + Writeln(Format(sStartingServer, [AServer.DefaultPort])); + AServer.Active := True; + end + else + Writeln(sServerRunning); + Write(cArrow); +end; + +procedure StopServer(const AServer: TIdHTTPWebBrokerBridge); +begin + if AServer.Active then + begin + Writeln(sStoppingServer); + AServer.Active := False; + Writeln(sServerStopped); + end + else + Writeln(sServerNotRunning); + Write(cArrow); +end; + +procedure SetPort(const AServer: TIdHTTPWebBrokerBridge; const APort: string); +var + LPort: Integer; + LWasActive: Boolean; +begin + LPort := StrToIntDef(APort, -1); + if LPort = -1 then + begin + Writeln('Port should be an integer number. Try again.'); + Exit; + end; + + LWasActive := AServer.Active; + if LWasActive then + StopServer(AServer); + TServerEngine.Default.Port := LPort; + if LWasActive then + StartServer(AServer); + Writeln(Format(sPortSet, [IntToStr(TServerEngine.Default.Port)])); + Write(cArrow); +end; + +procedure WriteCommands; +begin + Writeln(sCommands); + Write(cArrow); +end; + +procedure WriteStatus(const AServer: TIdHTTPWebBrokerBridge); +begin + Writeln(sIndyVersion + AServer.SessionList.Version); + Writeln(sActive + BoolToStr(AServer.Active, True)); + Writeln(sPort + IntToStr(TServerEngine.Default.Port)); + Write(cArrow); +end; + +procedure SetupThreadScheduler(const AServer: TIdHTTPWebBrokerBridge); +var + LScheduler: TIdSchedulerOfThreadPool; +begin + LScheduler := TIdSchedulerOfThreadPool.Create(AServer); + try + LScheduler.PoolSize := TServerEngine.Default.ThreadPoolSize; + AServer.Scheduler := LScheduler; + AServer.MaxConnections := LScheduler.PoolSize; + except + AServer.Scheduler.DisposeOf; + AServer.Scheduler := nil; + raise; + end; +end; + +procedure RunServer(); +var + LServer: TIdHTTPWebBrokerBridge; + LDummyIndy: TDummyIndyServer; + LResponse: string; +begin + WriteCommands; + LDummyIndy := TDummyIndyServer.Create; + try + LServer := TIdHTTPWebBrokerBridge.Create(nil); + try + LServer.DefaultPort := TServerEngine.Default.Port; + LServer.OnParseAuthentication := LDummyIndy.ParseAuthenticationHandler; + {$IFNDEF LINUX} + SetupThreadScheduler(LServer); + {$ENDIF} + + while True do + begin + Readln(LResponse); + LResponse := LowerCase(LResponse); + if sametext(LResponse, cCommandStart) then + StartServer(LServer) + else if sametext(LResponse, cCommandStatus) then + WriteStatus(LServer) + else if sametext(LResponse, cCommandStop) then + StopServer(LServer) + {$ifdef DelphiXE3_UP} + else if LResponse.StartsWith(cCommandSetPort, True) then + SetPort(LServer, LResponse.Split([' '])[2]) + {$else} + else if AnsiStartsText(cCommandSetPort, LResponse) then + SetPort(LServer, Copy(LResponse, Length(cCommandSetPort)+1, MAXINT)) + {$endif} + + else if sametext(LResponse, cCommandHelp) then + WriteCommands + else if sametext(LResponse, cCommandExit) then + if LServer.Active then + begin + StopServer(LServer); + break + end + else + break + else + begin + Writeln(sInvalidCommand); + Write(cArrow); + end; + end; + finally + LServer.Free; + end; + finally + LDummyIndy.Free; + end; +end; + +{ TDummyIndyServer } + +procedure TDummyIndyServer.ParseAuthenticationHandler(AContext: TIdContext; + const AAuthType, AAuthData: String; var VUsername, VPassword: String; + var VHandled: Boolean); +begin + // Allow JWT Bearer authentication's scheme + if SameText(AAuthType, 'Bearer') then + VHandled := True; +end; + +begin + try + if WebRequestHandler <> nil then + WebRequestHandler.WebModuleClass := WebModuleClass; + RunServer(); + except + on E: Exception do + Writeln(E.ClassName, ': ', E.Message); + end +end. diff --git a/Demos/RemoteMic/RemoteMicServerConsoleApplication.dproj b/Demos/RemoteMic/RemoteMicServerConsoleApplication.dproj new file mode 100644 index 00000000..22bf0b90 --- /dev/null +++ b/Demos/RemoteMic/RemoteMicServerConsoleApplication.dproj @@ -0,0 +1,1211 @@ + + + {8EEC8DF2-3740-468A-937A-60168245FB71} + RemoteMicServerConsoleApplication.dpr + True + Release + 4229 + Console + None + 18.8 + Win32 + + + true + + + true + Base + true + + + true + Base + true + + + true + Base + true + + + true + Base + true + + + true + Base + true + + + true + Base + true + + + true + Base + true + + + true + Base + true + + + true + Base + true + + + true + Base + true + + + true + Cfg_1 + true + true + + + true + Cfg_1 + true + true + + + true + Base + true + + + true + Cfg_2 + true + true + + + true + Cfg_2 + true + true + + + true + Cfg_2 + true + true + + + true + Cfg_2 + true + true + + + true + Cfg_2 + true + true + + + true + Cfg_2 + true + true + + + true + Cfg_2 + true + true + + + false + false + false + false + false + 00400000 + RemoteMicServerConsoleApplication + 1040 + CompanyName=;FileDescription=;FileVersion=1.0.0.0;InternalName=;LegalCopyright=;LegalTrademarks=;OriginalFilename=;ProductName=;ProductVersion=1.0.0.0;Comments=;CFBundleName= + System;Xml;Data;Datasnap;Web;Soap;$(DCC_Namespace) + $(BDS)\bin\delphi_PROJECTICON.ico + $(BDS)\bin\delphi_PROJECTICNS.icns + ./bin + .\lib\$(Platform)\$(Config) + + + package=com.embarcadero.$(MSBuildProjectName);label=$(MSBuildProjectName);versionCode=1;versionName=1.0.0;persistent=False;restoreAnyVersion=False;installLocation=auto;largeHeap=False;theme=TitleBar;hardwareAccelerated=true;apiKey= + Debug + $(BDS)\bin\Artwork\Android\FM_LauncherIcon_36x36.png + $(BDS)\bin\Artwork\Android\FM_LauncherIcon_48x48.png + $(BDS)\bin\Artwork\Android\FM_LauncherIcon_72x72.png + $(BDS)\bin\Artwork\Android\FM_LauncherIcon_96x96.png + $(BDS)\bin\Artwork\Android\FM_LauncherIcon_144x144.png + $(BDS)\bin\Artwork\Android\FM_SplashImage_426x320.png + $(BDS)\bin\Artwork\Android\FM_SplashImage_470x320.png + $(BDS)\bin\Artwork\Android\FM_SplashImage_640x480.png + $(BDS)\bin\Artwork\Android\FM_SplashImage_960x720.png + true + true + true + true + true + true + true + true + true + true + android-support-v4.dex.jar;cloud-messaging.dex.jar;fmx.dex.jar;google-analytics-v2.dex.jar;google-play-billing.dex.jar;google-play-licensing.dex.jar;google-play-services.dex.jar + $(BDS)\bin\Artwork\Android\FM_NotificationIcon_24x24.png + $(BDS)\bin\Artwork\Android\FM_NotificationIcon_36x36.png + $(BDS)\bin\Artwork\Android\FM_NotificationIcon_48x48.png + $(BDS)\bin\Artwork\Android\FM_NotificationIcon_72x72.png + $(BDS)\bin\Artwork\Android\FM_NotificationIcon_96x96.png + + + package=com.embarcadero.$(MSBuildProjectName);label=$(MSBuildProjectName);versionCode=1;versionName=1.0.0;persistent=False;restoreAnyVersion=False;installLocation=auto;largeHeap=False;theme=TitleBar;hardwareAccelerated=true;apiKey= + Debug + true + Base + true + $(BDS)\bin\Artwork\Android\FM_LauncherIcon_36x36.png + $(BDS)\bin\Artwork\Android\FM_LauncherIcon_48x48.png + $(BDS)\bin\Artwork\Android\FM_LauncherIcon_72x72.png + $(BDS)\bin\Artwork\Android\FM_LauncherIcon_96x96.png + $(BDS)\bin\Artwork\Android\FM_LauncherIcon_144x144.png + $(BDS)\bin\Artwork\Android\FM_SplashImage_426x320.png + $(BDS)\bin\Artwork\Android\FM_SplashImage_470x320.png + $(BDS)\bin\Artwork\Android\FM_SplashImage_640x480.png + $(BDS)\bin\Artwork\Android\FM_SplashImage_960x720.png + true + true + true + true + true + true + true + true + true + true + android-support-v4.dex.jar;cloud-messaging.dex.jar;fmx.dex.jar;google-analytics-v2.dex.jar;google-play-billing.dex.jar;google-play-licensing.dex.jar;google-play-services.dex.jar + + + CFBundleName=$(MSBuildProjectName);CFBundleDevelopmentRegion=en;CFBundleDisplayName=$(MSBuildProjectName);CFBundleIdentifier=$(MSBuildProjectName);CFBundleInfoDictionaryVersion=7.1;CFBundleVersion=1.0.0.0;CFBundlePackageType=APPL;CFBundleSignature=????;LSRequiresIPhoneOS=true;CFBundleAllowMixedLocalizations=YES;CFBundleExecutable=$(MSBuildProjectName);UIDeviceFamily=iPhone & iPad;CFBundleResourceSpecification=ResourceRules.plist;NSLocationAlwaysUsageDescription=The reason for accessing the location information of the user;NSLocationWhenInUseUsageDescription=The reason for accessing the location information of the user;FMLocalNotificationPermission=false;UIBackgroundModes=;NSContactsUsageDescription=The reason for accessing the contacts;NSPhotoLibraryUsageDescription=The reason for accessing the photo library;NSCameraUsageDescription=The reason for accessing the camera + iPhoneAndiPad + true + Debug + $(MSBuildProjectName) + $(BDS)\bin\Artwork\iOS\iPhone\FM_ApplicationIcon_60x60.png + $(BDS)\bin\Artwork\iOS\iPhone\FM_ApplicationIcon_120x120.png + $(BDS)\bin\Artwork\iOS\iPhone\FM_SpotlightSearchIcon_40x40.png + $(BDS)\bin\Artwork\iOS\iPhone\FM_SpotlightSearchIcon_80x80.png + $(BDS)\bin\Artwork\iOS\iPad\FM_SpotlightSearchIcon_40x40.png + $(BDS)\bin\Artwork\iOS\iPad\FM_SpotlightSearchIcon_80x80.png + $(BDS)\bin\Artwork\iOS\iPad\FM_ApplicationIcon_76x76.png + $(BDS)\bin\Artwork\iOS\iPad\FM_ApplicationIcon_152x152.png + $(BDS)\bin\Artwork\iOS\iPad\FM_LaunchImagePortrait_768x1024.png + $(BDS)\bin\Artwork\iOS\iPad\FM_LaunchImageLandscape_1024x768.png + $(BDS)\bin\Artwork\iOS\iPad\FM_LaunchImagePortrait_1536x2048.png + $(BDS)\bin\Artwork\iOS\iPad\FM_LaunchImageLandscape_2048x1536.png + + + CFBundleName=$(MSBuildProjectName);CFBundleDevelopmentRegion=en;CFBundleDisplayName=$(MSBuildProjectName);CFBundleIdentifier=$(MSBuildProjectName);CFBundleInfoDictionaryVersion=7.1;CFBundleVersion=1.0.0.0;CFBundlePackageType=APPL;CFBundleSignature=????;LSRequiresIPhoneOS=true;CFBundleAllowMixedLocalizations=YES;CFBundleExecutable=$(MSBuildProjectName);UIDeviceFamily=iPhone & iPad;CFBundleResourceSpecification=ResourceRules.plist;NSLocationAlwaysUsageDescription=The reason for accessing the location information of the user;NSLocationWhenInUseUsageDescription=The reason for accessing the location information of the user;FMLocalNotificationPermission=false;UIBackgroundModes=;NSContactsUsageDescription=The reason for accessing the contacts;NSPhotoLibraryUsageDescription=The reason for accessing the photo library;NSCameraUsageDescription=The reason for accessing the camera + iPhoneAndiPad + true + $(BDS)\bin\Artwork\iOS\iPhone\FM_ApplicationIcon_60x60.png + $(BDS)\bin\Artwork\iOS\iPhone\FM_ApplicationIcon_120x120.png + $(BDS)\bin\Artwork\iOS\iPhone\FM_SpotlightSearchIcon_40x40.png + $(BDS)\bin\Artwork\iOS\iPhone\FM_SpotlightSearchIcon_80x80.png + $(BDS)\bin\Artwork\iOS\iPad\FM_SpotlightSearchIcon_40x40.png + $(BDS)\bin\Artwork\iOS\iPad\FM_SpotlightSearchIcon_80x80.png + $(BDS)\bin\Artwork\iOS\iPad\FM_ApplicationIcon_76x76.png + $(BDS)\bin\Artwork\iOS\iPad\FM_ApplicationIcon_152x152.png + $(BDS)\bin\Artwork\iOS\iPad\FM_LaunchImagePortrait_768x1024.png + $(BDS)\bin\Artwork\iOS\iPad\FM_LaunchImageLandscape_1024x768.png + $(BDS)\bin\Artwork\iOS\iPad\FM_LaunchImagePortrait_1536x2048.png + $(BDS)\bin\Artwork\iOS\iPad\FM_LaunchImageLandscape_2048x1536.png + + + /usr/bin/xterm -e "%debuggee%" + (None) + RemoteMicServer_Icon.ico + + + CFBundleName=$(MSBuildProjectName);CFBundleDisplayName=$(MSBuildProjectName);CFBundleIdentifier=$(MSBuildProjectName);CFBundleVersion=1.0.0;CFBundleShortVersionString=1.0.0;CFBundlePackageType=APPL;CFBundleSignature=????;CFBundleAllowMixedLocalizations=YES;CFBundleExecutable=$(MSBuildProjectName);NSHighResolutionCapable=true;LSApplicationCategoryType=public.app-category.utilities;NSContactsUsageDescription=The reason for accessing the contacts;NSLocationUsageDescription=The reason for accessing the location information of the user + Debug + /usr/X11/bin/xterm -e "%debuggee%" + (None) + + + CFBundleName=$(MSBuildProjectName);CFBundleDisplayName=$(MSBuildProjectName);CFBundleIdentifier=$(MSBuildProjectName);CFBundleVersion=1.0.0;CFBundleShortVersionString=1.0.0;CFBundlePackageType=APPL;CFBundleSignature=????;CFBundleAllowMixedLocalizations=YES;CFBundleExecutable=$(MSBuildProjectName);NSHighResolutionCapable=true;LSApplicationCategoryType=public.app-category.utilities;NSContactsUsageDescription=The reason for accessing the contacts;NSLocationUsageDescription=The reason for accessing the location information of the user + Debug + true + Base + true + /usr/X11/bin/xterm -e "%debuggee%" + (None) + + + Winapi;System.Win;Data.Win;Datasnap.Win;Web.Win;Soap.Win;Xml.Win;Bde;$(DCC_Namespace) + Debug + CompanyName=;FileDescription=$(MSBuildProjectName);FileVersion=1.0.0.0;InternalName=;LegalCopyright=;LegalTrademarks=;OriginalFilename=;ProductName=$(MSBuildProjectName);ProductVersion=1.0.0.0;Comments=;ProgramID=com.embarcadero.$(MSBuildProjectName) + 1033 + RemoteMicServer_Icon.ico + (None) + $(BDS)\bin\Artwork\Windows\UWP\delphi_UwpDefault_44.png + $(BDS)\bin\Artwork\Windows\UWP\delphi_UwpDefault_150.png + + + RemoteMicServerConsoleApplication_Icon.ico + + + RELEASE;$(DCC_Define) + 0 + false + 0 + + + RemoteMicServer_Icon.ico + + + 1033 + CompanyName=;FileDescription=$(MSBuildProjectName);FileVersion=1.0.0.0;InternalName=;LegalCopyright=;LegalTrademarks=;OriginalFilename=;ProductName=$(MSBuildProjectName);ProductVersion=1.0.0.0;Comments=;ProgramID=com.embarcadero.$(MSBuildProjectName) + + + DEBUG;$(DCC_Define) + false + true + + + true + + + Debug + + + true + + + RemoteMicServer_Icon.ico + + + true + + + true + true + Cfg_2 + true + + + 1033 + CompanyName=;FileDescription=$(MSBuildProjectName);FileVersion=1.0.0.0;InternalName=;LegalCopyright=;LegalTrademarks=;OriginalFilename=;ProductName=$(MSBuildProjectName);ProductVersion=1.0.0.0;Comments=;ProgramID=com.embarcadero.$(MSBuildProjectName) + $(BDS)\bin\Artwork\Windows\UWP\delphi_UwpDefault_44.png + $(BDS)\bin\Artwork\Windows\UWP\delphi_UwpDefault_150.png + RemoteMicServer_Icon.ico + + + + MainSource + + + +
ServerWebModule
+ TWebModule +
+ + + Cfg_2 + Base + + + Base + + + Cfg_1 + Base + +
+ + Delphi.Personality.12 + + + + + RemoteMicServerConsoleApplication.dpr + + + Microsoft Office 2000 Sample Automation Server Wrapper Components + Microsoft Office XP Sample Automation Server Wrapper Components + + + + False + False + False + False + False + True + True + True + True + False + + + + + Assets\ + Logo150x150.png + true + + + + + true + + + + + true + + + + + true + + + + + true + + + + + true + + + + + true + + + + + RemoteMicServerConsoleApplication + true + + + + + Assets\ + Logo44x44.png + true + + + + + 1 + + + 0 + + + + + classes + 1 + + + classes + 1 + + + + + res\xml + 1 + + + res\xml + 1 + + + + + library\lib\armeabi-v7a + 1 + + + + + library\lib\armeabi + 1 + + + library\lib\armeabi + 1 + + + + + library\lib\armeabi-v7a + 1 + + + + + library\lib\mips + 1 + + + library\lib\mips + 1 + + + + + library\lib\armeabi-v7a + 1 + + + library\lib\arm64-v8a + 1 + + + + + library\lib\armeabi-v7a + 1 + + + + + res\drawable + 1 + + + res\drawable + 1 + + + + + res\values + 1 + + + res\values + 1 + + + + + res\values-v21 + 1 + + + res\values-v21 + 1 + + + + + res\values + 1 + + + res\values + 1 + + + + + res\drawable + 1 + + + res\drawable + 1 + + + + + res\drawable-xxhdpi + 1 + + + res\drawable-xxhdpi + 1 + + + + + res\drawable-ldpi + 1 + + + res\drawable-ldpi + 1 + + + + + res\drawable-mdpi + 1 + + + res\drawable-mdpi + 1 + + + + + res\drawable-hdpi + 1 + + + res\drawable-hdpi + 1 + + + + + res\drawable-xhdpi + 1 + + + res\drawable-xhdpi + 1 + + + + + res\drawable-mdpi + 1 + + + res\drawable-mdpi + 1 + + + + + res\drawable-hdpi + 1 + + + res\drawable-hdpi + 1 + + + + + res\drawable-xhdpi + 1 + + + res\drawable-xhdpi + 1 + + + + + res\drawable-xxhdpi + 1 + + + res\drawable-xxhdpi + 1 + + + + + res\drawable-xxxhdpi + 1 + + + res\drawable-xxxhdpi + 1 + + + + + res\drawable-small + 1 + + + res\drawable-small + 1 + + + + + res\drawable-normal + 1 + + + res\drawable-normal + 1 + + + + + res\drawable-large + 1 + + + res\drawable-large + 1 + + + + + res\drawable-xlarge + 1 + + + res\drawable-xlarge + 1 + + + + + res\values + 1 + + + res\values + 1 + + + + + 1 + + + 1 + + + 0 + + + + + 1 + .framework + + + 1 + .framework + + + 0 + + + + + 1 + .dylib + + + 1 + .dylib + + + 0 + .dll;.bpl + + + + + 1 + .dylib + + + 1 + .dylib + + + 1 + .dylib + + + 1 + .dylib + + + 1 + .dylib + + + 0 + .bpl + + + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + + + ..\$(PROJECTNAME).app.dSYM\Contents\Resources\DWARF + 1 + + + ..\$(PROJECTNAME).app.dSYM\Contents\Resources\DWARF + 1 + + + + + + + + 1 + + + 1 + + + 1 + + + + + + + + Contents\Resources + 1 + + + Contents\Resources + 1 + + + + + library\lib\armeabi-v7a + 1 + + + library\lib\arm64-v8a + 1 + + + 1 + + + 1 + + + 1 + + + 1 + + + 1 + + + 1 + + + 0 + + + + + library\lib\armeabi-v7a + 1 + + + + + 1 + + + 1 + + + + + Assets + 1 + + + Assets + 1 + + + + + Assets + 1 + + + Assets + 1 + + + + + + + + + + + + + + + 12 + + + + +
diff --git a/Demos/RemoteMic/RemoteMicServerDaemon.dpr b/Demos/RemoteMic/RemoteMicServerDaemon.dpr new file mode 100644 index 00000000..cc63cab7 --- /dev/null +++ b/Demos/RemoteMic/RemoteMicServerDaemon.dpr @@ -0,0 +1,21 @@ +program RemoteMicServerDaemon; + +{$APPTYPE CONSOLE} + +{$R *.res} + +uses + Classes, + SysUtils, + {$IFDEF LINUX} + MARS.Linux.Daemon in '..\..\Source\MARS.Linux.Daemon.pas', + {$ENDIF} + Server.Ignition in 'Server.Ignition.pas', + Server.WebModule in 'Server.WebModule.pas' {ServerWebModule: TWebModule}; + +begin + {$IFDEF LINUX} + TMARSDaemon.Current.Name := 'RemoteMicServerDaemon'; + TMARSDaemon.Current.Start; + {$ENDIF} +end. diff --git a/Demos/RemoteMic/RemoteMicServerDaemon.dproj b/Demos/RemoteMic/RemoteMicServerDaemon.dproj new file mode 100644 index 00000000..c8dca5b0 --- /dev/null +++ b/Demos/RemoteMic/RemoteMicServerDaemon.dproj @@ -0,0 +1,1161 @@ + + + {9D225C2C-24C2-48B4-8ABE-52C04AF576AE} + 18.8 + None + RemoteMicServerDaemon.dpr + True + Debug + Linux64 + 128 + Console + + + true + + + true + Base + true + + + true + Base + true + + + true + Base + true + + + true + Base + true + + + true + Base + true + + + true + Base + true + + + true + Base + true + + + true + Base + true + + + true + Base + true + + + true + Base + true + + + true + Base + true + + + true + Cfg_1 + true + true + + + true + Cfg_1 + true + true + + + true + Base + true + + + .\lib\$(Platform)\$(Config) + .\bin + false + false + false + false + false + RESTComponents;emsclientfiredac;DataSnapFireDAC;FireDACIBDriver;emsclient;FireDACCommon;RESTBackendComponents;soapserver;CloudService;FireDACCommonDriver;inet;FireDAC;FireDACSqliteDriver;soaprtl;soapmidas;$(DCC_UsePackage) + System;Xml;Data;Datasnap;Web;Soap;$(DCC_Namespace) + RemoteMicServerDaemon + + + DBXSqliteDriver;DBXInterBaseDriver;tethering;bindcompfmx;FmxTeeUI;fmx;RadiantShapesFmx;FireDACDBXDriver;dbexpress;IndyCore;dsnap;DataSnapCommon;bindengine;DataSnapClient;bindcompdbx;IndyIPCommon;IndyIPServer;IndySystem;fmxFireDAC;ibmonitor;FMXTee;DbxCommonDriver;ibxpress;xmlrtl;DataSnapNativeClient;ibxbindings;rtl;FireDACDSDriver;DbxClientDriver;CustomIPTransport;bindcomp;IndyIPClient;dbxcds;dsnapxml;DataSnapProviderClient;dbrtl;IndyProtocols;$(DCC_UsePackage) + $(BDS)\bin\Artwork\Android\FM_LauncherIcon_36x36.png + $(BDS)\bin\Artwork\Android\FM_LauncherIcon_48x48.png + $(BDS)\bin\Artwork\Android\FM_LauncherIcon_72x72.png + $(BDS)\bin\Artwork\Android\FM_LauncherIcon_96x96.png + $(BDS)\bin\Artwork\Android\FM_LauncherIcon_144x144.png + $(BDS)\bin\Artwork\Android\FM_SplashImage_426x320.png + $(BDS)\bin\Artwork\Android\FM_SplashImage_470x320.png + $(BDS)\bin\Artwork\Android\FM_SplashImage_640x480.png + $(BDS)\bin\Artwork\Android\FM_SplashImage_960x720.png + android-support-v4.dex.jar;cloud-messaging.dex.jar;fmx.dex.jar;google-analytics-v2.dex.jar;google-play-billing.dex.jar;google-play-licensing.dex.jar;google-play-services.dex.jar + $(BDS)\bin\Artwork\Android\FM_NotificationIcon_24x24.png + $(BDS)\bin\Artwork\Android\FM_NotificationIcon_36x36.png + $(BDS)\bin\Artwork\Android\FM_NotificationIcon_48x48.png + $(BDS)\bin\Artwork\Android\FM_NotificationIcon_72x72.png + $(BDS)\bin\Artwork\Android\FM_NotificationIcon_96x96.png + + + package=com.embarcadero.$(MSBuildProjectName);label=$(MSBuildProjectName);versionCode=1;versionName=1.0.0;persistent=False;restoreAnyVersion=False;installLocation=auto;largeHeap=False;theme=TitleBar;hardwareAccelerated=true;apiKey= + Debug + true + Base + true + DBXSqliteDriver;DBXInterBaseDriver;tethering;bindcompfmx;FmxTeeUI;fmx;RadiantShapesFmx;FireDACDBXDriver;dbexpress;IndyCore;dsnap;DataSnapCommon;bindengine;DataSnapClient;bindcompdbx;IndyIPCommon;IndyIPServer;IndySystem;fmxFireDAC;ibmonitor;FMXTee;DbxCommonDriver;ibxpress;xmlrtl;DataSnapNativeClient;ibxbindings;rtl;FireDACDSDriver;DbxClientDriver;CustomIPTransport;bindcomp;IndyIPClient;dbxcds;dsnapxml;DataSnapProviderClient;dbrtl;IndyProtocols;$(DCC_UsePackage);$(DCC_UsePackage) + $(BDS)\bin\Artwork\Android\FM_LauncherIcon_36x36.png + $(BDS)\bin\Artwork\Android\FM_LauncherIcon_48x48.png + $(BDS)\bin\Artwork\Android\FM_LauncherIcon_72x72.png + $(BDS)\bin\Artwork\Android\FM_LauncherIcon_96x96.png + $(BDS)\bin\Artwork\Android\FM_LauncherIcon_144x144.png + $(BDS)\bin\Artwork\Android\FM_SplashImage_426x320.png + $(BDS)\bin\Artwork\Android\FM_SplashImage_470x320.png + $(BDS)\bin\Artwork\Android\FM_SplashImage_640x480.png + $(BDS)\bin\Artwork\Android\FM_SplashImage_960x720.png + android-support-v4.dex.jar;cloud-messaging.dex.jar;fmx.dex.jar;google-analytics-v2.dex.jar;google-play-billing.dex.jar;google-play-licensing.dex.jar;google-play-services.dex.jar + + + DBXSqliteDriver;DBXInterBaseDriver;tethering;bindcompfmx;FmxTeeUI;fmx;RadiantShapesFmx;FireDACDBXDriver;dbexpress;IndyCore;dsnap;DataSnapCommon;bindengine;DataSnapClient;bindcompdbx;IndyIPCommon;IndyIPServer;IndySystem;fmxFireDAC;ibmonitor;FMXTee;DbxCommonDriver;ibxpress;xmlrtl;DataSnapNativeClient;ibxbindings;rtl;FireDACDSDriver;DbxClientDriver;CustomIPTransport;bindcomp;IndyIPClient;dbxcds;dsnapxml;DataSnapProviderClient;dbrtl;IndyProtocols;fmxase;$(DCC_UsePackage) + + + DBXSqliteDriver;DBXInterBaseDriver;tethering;bindcompfmx;FmxTeeUI;fmx;RadiantShapesFmx;FireDACDBXDriver;dbexpress;IndyCore;dsnap;DataSnapCommon;bindengine;DataSnapClient;bindcompdbx;IndyIPCommon;IndyIPServer;IndySystem;fmxFireDAC;ibmonitor;FMXTee;DbxCommonDriver;ibxpress;xmlrtl;DataSnapNativeClient;ibxbindings;rtl;FireDACDSDriver;DbxClientDriver;CustomIPTransport;bindcomp;IndyIPClient;dbxcds;dsnapxml;DataSnapProviderClient;dbrtl;IndyProtocols;fmxase;$(DCC_UsePackage) + + + DBXSqliteDriver;DBXInterBaseDriver;tethering;bindcompfmx;FmxTeeUI;fmx;RadiantShapesFmx;FireDACDBXDriver;dbexpress;IndyCore;dsnap;DataSnapCommon;bindengine;DataSnapClient;bindcompdbx;IndyIPCommon;IndyIPServer;IndySystem;fmxFireDAC;ibmonitor;FMXTee;DbxCommonDriver;ibxpress;xmlrtl;DataSnapNativeClient;ibxbindings;rtl;FireDACDSDriver;DbxClientDriver;CustomIPTransport;bindcomp;IndyIPClient;dbxcds;dsnapxml;DataSnapProviderClient;dbrtl;IndyProtocols;fmxase;$(DCC_UsePackage) + + + DataSnapServerMidas;FireDACADSDriver;DatasnapConnectorsFreePascal;FireDACMSSQLDriver;inetdb;emsedge;dbexpress;IndyCore;dsnap;DataSnapCommon;DataSnapConnectors;MARS.Utils;bindengine;FireDACOracleDriver;FireDACMySQLDriver;FireDACCommonODBC;DataSnapClient;IndySystem;FireDACDb2Driver;FireDACInfxDriver;emshosting;FireDACPgDriver;FireDACASADriver;FireDACTDataDriver;DbxCommonDriver;DataSnapServer;xmlrtl;DataSnapNativeClient;rtl;DbxClientDriver;CustomIPTransport;bindcomp;dbxcds;FireDACODBCDriver;DataSnapIndy10ServerTransport;dsnapxml;dbrtl;FireDACMongoDBDriver;IndyProtocols;$(DCC_UsePackage) + /usr/bin/xterm -e "%debuggee%" + RemoteMicServer_Icon.ico + (None) + + + DBXSqliteDriver;DataSnapServerMidas;DBXInterBaseDriver;tethering;FireDACMSSQLDriver;bindcompfmx;DBXOracleDriver;inetdb;FmxTeeUI;emsedge;fmx;fmxdae;RadiantShapesFmx;FireDACDBXDriver;dbexpress;IndyCore;dsnap;DataSnapCommon;bindengine;DBXMySQLDriver;FireDACOracleDriver;FireDACMySQLDriver;DBXFirebirdDriver;FireDACCommonODBC;DataSnapClient;bindcompdbx;IndyIPCommon;IndyIPServer;IndySystem;fmxFireDAC;emshosting;FireDACPgDriver;ibmonitor;FireDACASADriver;FireDACTDataDriver;FMXTee;DbxCommonDriver;ibxpress;DataSnapServer;xmlrtl;DataSnapNativeClient;fmxobj;ibxbindings;rtl;FireDACDSDriver;DbxClientDriver;DBXSybaseASADriver;CustomIPTransport;bindcomp;DBXInformixDriver;IndyIPClient;dbxcds;FireDACODBCDriver;DataSnapIndy10ServerTransport;dsnapxml;DataSnapProviderClient;dbrtl;inetdbxpress;FireDACMongoDBDriver;IndyProtocols;fmxase;$(DCC_UsePackage) + true + + + CFBundleName=$(MSBuildProjectName);CFBundleDisplayName=$(MSBuildProjectName);CFBundleIdentifier=$(MSBuildProjectName);CFBundleVersion=1.0.0;CFBundleShortVersionString=1.0.0;CFBundlePackageType=APPL;CFBundleSignature=????;CFBundleAllowMixedLocalizations=YES;CFBundleExecutable=$(MSBuildProjectName);NSHighResolutionCapable=true;LSApplicationCategoryType=public.app-category.utilities;NSLocationUsageDescription=The reason for accessing the location information of the user;NSContactsUsageDescription=The reason for accessing the contacts + Debug + true + Base + true + DBXSqliteDriver;DataSnapServerMidas;DBXInterBaseDriver;tethering;FireDACMSSQLDriver;bindcompfmx;DBXOracleDriver;inetdb;FmxTeeUI;emsedge;fmx;fmxdae;RadiantShapesFmx;FireDACDBXDriver;dbexpress;IndyCore;dsnap;DataSnapCommon;bindengine;DBXMySQLDriver;FireDACOracleDriver;FireDACMySQLDriver;DBXFirebirdDriver;FireDACCommonODBC;DataSnapClient;bindcompdbx;IndyIPCommon;IndyIPServer;IndySystem;fmxFireDAC;emshosting;FireDACPgDriver;ibmonitor;FireDACASADriver;FireDACTDataDriver;FMXTee;DbxCommonDriver;ibxpress;DataSnapServer;xmlrtl;DataSnapNativeClient;fmxobj;ibxbindings;rtl;FireDACDSDriver;DbxClientDriver;DBXSybaseASADriver;CustomIPTransport;bindcomp;DBXInformixDriver;IndyIPClient;dbxcds;FireDACODBCDriver;DataSnapIndy10ServerTransport;dsnapxml;DataSnapProviderClient;dbrtl;inetdbxpress;FireDACMongoDBDriver;IndyProtocols;fmxase;$(DCC_UsePackage);$(DCC_UsePackage) + true + + + DBXSqliteDriver;DataSnapServerMidas;DBXDb2Driver;DBXInterBaseDriver;vclactnband;vclFireDAC;svnui;tethering;FireDACADSDriver;DBXMSSQLDriver;DatasnapConnectorsFreePascal;FireDACMSSQLDriver;vcltouch;vcldb;bindcompfmx;svn;Intraweb;DBXOracleDriver;inetdb;FmxTeeUI;emsedge;fmx;fmxdae;RadiantShapesFmx;vclib;FireDACDBXDriver;dbexpress;IndyCore;vclx;dsnap;DataSnapCommon;DataSnapConnectors;MARS.Utils;VCLRESTComponents;vclie;bindengine;DBXMySQLDriver;FireDACOracleDriver;FireDACMySQLDriver;DBXFirebirdDriver;FireDACCommonODBC;DataSnapClient;bindcompdbx;IndyIPCommon;vcl;DBXSybaseASEDriver;IndyIPServer;IndySystem;FireDACDb2Driver;dsnapcon;FireDACMSAccDriver;fmxFireDAC;FireDACInfxDriver;vclimg;TeeDB;emshosting;FireDACPgDriver;ibmonitor;FireDACASADriver;DBXOdbcDriver;FireDACTDataDriver;FMXTee;DbxCommonDriver;ibxpress;Tee;DataSnapServer;xmlrtl;DataSnapNativeClient;fmxobj;vclwinx;ibxbindings;rtl;FireDACDSDriver;DbxClientDriver;DBXSybaseASADriver;CustomIPTransport;vcldsnap;CodeSiteExpressPkg;bindcomp;appanalytics;DBXInformixDriver;IndyIPClient;bindcompvcl;TeeUI;dbxcds;VclSmp;adortl;FireDACODBCDriver;RadiantShapesFmx_Design;DataSnapIndy10ServerTransport;dsnapxml;DataSnapProviderClient;dbrtl;inetdbxpress;FireDACMongoDBDriver;IndyProtocols;fmxase;$(DCC_UsePackage) + Winapi;System.Win;Data.Win;Datasnap.Win;Web.Win;Soap.Win;Xml.Win;Bde;$(DCC_Namespace) + Debug + CompanyName=;FileDescription=$(MSBuildProjectName);FileVersion=1.0.0.0;InternalName=;LegalCopyright=;LegalTrademarks=;OriginalFilename=;ProgramID=com.embarcadero.$(MSBuildProjectName);ProductName=$(MSBuildProjectName);ProductVersion=1.0.0.0;Comments= + 1033 + true + $(BDS)\bin\Artwork\Windows\UWP\delphi_UwpDefault_44.png + $(BDS)\bin\Artwork\Windows\UWP\delphi_UwpDefault_150.png + + + DBXSqliteDriver;DataSnapServerMidas;DBXDb2Driver;DBXInterBaseDriver;vclactnband;vclFireDAC;tethering;FireDACADSDriver;DBXMSSQLDriver;DatasnapConnectorsFreePascal;FireDACMSSQLDriver;vcltouch;vcldb;bindcompfmx;Intraweb;DBXOracleDriver;inetdb;FmxTeeUI;emsedge;fmx;fmxdae;RadiantShapesFmx;vclib;FireDACDBXDriver;dbexpress;IndyCore;vclx;dsnap;DataSnapCommon;DataSnapConnectors;MARS.Utils;VCLRESTComponents;vclie;bindengine;DBXMySQLDriver;FireDACOracleDriver;FireDACMySQLDriver;DBXFirebirdDriver;FireDACCommonODBC;DataSnapClient;bindcompdbx;IndyIPCommon;vcl;DBXSybaseASEDriver;IndyIPServer;IndySystem;FireDACDb2Driver;dsnapcon;FireDACMSAccDriver;fmxFireDAC;FireDACInfxDriver;vclimg;TeeDB;emshosting;FireDACPgDriver;ibmonitor;FireDACASADriver;DBXOdbcDriver;FireDACTDataDriver;FMXTee;DbxCommonDriver;ibxpress;Tee;DataSnapServer;xmlrtl;DataSnapNativeClient;fmxobj;vclwinx;ibxbindings;rtl;FireDACDSDriver;DbxClientDriver;DBXSybaseASADriver;CustomIPTransport;vcldsnap;bindcomp;appanalytics;DBXInformixDriver;IndyIPClient;bindcompvcl;TeeUI;dbxcds;VclSmp;adortl;FireDACODBCDriver;DataSnapIndy10ServerTransport;dsnapxml;DataSnapProviderClient;dbrtl;inetdbxpress;FireDACMongoDBDriver;IndyProtocols;fmxase;$(DCC_UsePackage) + true + $(BDS)\bin\Artwork\Windows\UWP\delphi_UwpDefault_44.png + $(BDS)\bin\Artwork\Windows\UWP\delphi_UwpDefault_150.png + + + DEBUG;$(DCC_Define) + true + false + true + true + true + + + /usr/bin/xterm -e "%debuggee%" + + + false + + + false + RELEASE;$(DCC_Define) + 0 + 0 + + + + MainSource + + + + +
ServerWebModule
+ TWebModule +
+ + Cfg_2 + Base + + + Base + + + Cfg_1 + Base + +
+ + Delphi.Personality.12 + Application + + + + RemoteMicServerDaemon.dpr + + + Microsoft Office 2000 Sample Automation Server Wrapper Components + Microsoft Office XP Sample Automation Server Wrapper Components + + + + + + true + + + + + true + + + + + true + + + + + true + + + + + true + + + + + true + + + + + RemoteMicServerDaemon + true + + + + + 1 + + + Contents\MacOS + 1 + + + 0 + + + + + classes + 1 + + + classes + 1 + + + + + res\xml + 1 + + + res\xml + 1 + + + + + library\lib\armeabi-v7a + 1 + + + + + library\lib\armeabi + 1 + + + library\lib\armeabi + 1 + + + + + library\lib\armeabi-v7a + 1 + + + + + library\lib\mips + 1 + + + library\lib\mips + 1 + + + + + library\lib\armeabi-v7a + 1 + + + library\lib\arm64-v8a + 1 + + + + + library\lib\armeabi-v7a + 1 + + + + + res\drawable + 1 + + + res\drawable + 1 + + + + + res\values + 1 + + + res\values + 1 + + + + + res\values-v21 + 1 + + + res\values-v21 + 1 + + + + + res\values + 1 + + + res\values + 1 + + + + + res\drawable + 1 + + + res\drawable + 1 + + + + + res\drawable-xxhdpi + 1 + + + res\drawable-xxhdpi + 1 + + + + + res\drawable-ldpi + 1 + + + res\drawable-ldpi + 1 + + + + + res\drawable-mdpi + 1 + + + res\drawable-mdpi + 1 + + + + + res\drawable-hdpi + 1 + + + res\drawable-hdpi + 1 + + + + + res\drawable-xhdpi + 1 + + + res\drawable-xhdpi + 1 + + + + + res\drawable-mdpi + 1 + + + res\drawable-mdpi + 1 + + + + + res\drawable-hdpi + 1 + + + res\drawable-hdpi + 1 + + + + + res\drawable-xhdpi + 1 + + + res\drawable-xhdpi + 1 + + + + + res\drawable-xxhdpi + 1 + + + res\drawable-xxhdpi + 1 + + + + + res\drawable-xxxhdpi + 1 + + + res\drawable-xxxhdpi + 1 + + + + + res\drawable-small + 1 + + + res\drawable-small + 1 + + + + + res\drawable-normal + 1 + + + res\drawable-normal + 1 + + + + + res\drawable-large + 1 + + + res\drawable-large + 1 + + + + + res\drawable-xlarge + 1 + + + res\drawable-xlarge + 1 + + + + + res\values + 1 + + + res\values + 1 + + + + + 1 + + + Contents\MacOS + 1 + + + 0 + + + + + Contents\MacOS + 1 + .framework + + + Contents\MacOS + 1 + .framework + + + 0 + + + + + 1 + .dylib + + + 1 + .dylib + + + 1 + .dylib + + + Contents\MacOS + 1 + .dylib + + + Contents\MacOS + 1 + .dylib + + + 0 + .dll;.bpl + + + + + 1 + .dylib + + + 1 + .dylib + + + 1 + .dylib + + + Contents\MacOS + 1 + .dylib + + + Contents\MacOS + 1 + .dylib + + + 0 + .bpl + + + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + Contents\Resources\StartUp\ + 0 + + + Contents\Resources\StartUp\ + 0 + + + 0 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + + + ..\$(PROJECTNAME).app.dSYM\Contents\Resources\DWARF + 1 + + + ..\$(PROJECTNAME).app.dSYM\Contents\Resources\DWARF + 1 + + + + + 1 + + + 1 + + + + + ..\ + 1 + + + ..\ + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + ..\$(PROJECTNAME).app.dSYM\Contents\Resources\DWARF + 1 + + + + + ..\ + 1 + + + ..\ + 1 + + + + + Contents + 1 + + + Contents + 1 + + + + + Contents\Resources + 1 + + + Contents\Resources + 1 + + + + + library\lib\armeabi-v7a + 1 + + + library\lib\arm64-v8a + 1 + + + 1 + + + 1 + + + 1 + + + 1 + + + Contents\MacOS + 1 + + + Contents\MacOS + 1 + + + 0 + + + + + library\lib\armeabi-v7a + 1 + + + + + 1 + + + 1 + + + + + Assets + 1 + + + Assets + 1 + + + + + Assets + 1 + + + Assets + 1 + + + + + + + + + + + + + + + False + False + False + False + False + True + False + False + False + False + + + 12 + + + + +
diff --git a/Demos/RemoteMic/RemoteMicServerFMXApplication.deployproj b/Demos/RemoteMic/RemoteMicServerFMXApplication.deployproj new file mode 100644 index 00000000..a082f2d3 --- /dev/null +++ b/Demos/RemoteMic/RemoteMicServerFMXApplication.deployproj @@ -0,0 +1,132 @@ + + + + 12 + + + + + + iPhone5 + + + + + + + RemoteMicServerFMXApplication\ + RemoteMicServerFMXApplication.exe + ProjectOutput + 0 + + + True + True + + + + + RemoteMicServerFMXApplication.app\Assets\ + Logo44x44.png + UWP_DelphiLogo44 + 0 + + + True + + + RemoteMicServerFMXApplication.app\Contents\MacOS\ + libcgunwind.1.0.dylib + DependencyModule + 1 + + + True + + + RemoteMicServerFMXApplication.app\Contents\MacOS\ + RemoteMicServerFMXApplication.rsm + DebugSymbols + 1 + + + True + + + RemoteMicServerFMXApplication.app\..\ + RemoteMicServerFMXApplication.entitlements + ProjectOSXEntitlements + 1 + + + True + + + RemoteMicServerFMXApplication.app\Contents\Resources\ + RemoteMicServerFMXApplication.icns + ProjectOSXResource + 1 + + + True + + + RemoteMicServerFMXApplication.app\Contents\ + Info.plist + ProjectOSXInfoPList + 1 + + + True + + + RemoteMicServerFMXApplication.app\Contents\MacOS\ + libcgsqlite3.dylib + DependencyModule + 1 + + + True + + + RemoteMicServerFMXApplication.app\Assets\ + Logo150x150.png + UWP_DelphiLogo150 + 0 + + + True + + + RemoteMicServerFMXApplication.app\Contents\MacOS\ + RemoteMicServerFMXApplication + ProjectOutput + 1 + + + True + True + + + + + + RemoteMicServerFMXApplication.app\ + libPCRE.dylib + DependencyModule + 1 + + + True + + + RemoteMicServerFMXApplication.app\ + libcgunwind.1.0.dylib + DependencyModule + 1 + + + True + + + diff --git a/Demos/RemoteMic/RemoteMicServerFMXApplication.dpr b/Demos/RemoteMic/RemoteMicServerFMXApplication.dpr new file mode 100644 index 00000000..f2bcead0 --- /dev/null +++ b/Demos/RemoteMic/RemoteMicServerFMXApplication.dpr @@ -0,0 +1,21 @@ +(* + Copyright 2016, MARS-Curiosity - REST Library + + Home: https://github.com/andrea-magni/MARS +*) +program RemoteMicServerFMXApplication; + +uses + System.StartUpCopy, + FMX.Forms, + Server.FMX.Forms.Main in 'Server.FMX.Forms.Main.pas' {MainForm}, + Server.Ignition in 'Server.Ignition.pas', + Server.Resources in 'Server.Resources.pas'; + +{$R *.res} + +begin + Application.Initialize; + Application.CreateForm(TMainForm, MainForm); + Application.Run; +end. diff --git a/Demos/RemoteMic/RemoteMicServerFMXApplication.dproj b/Demos/RemoteMic/RemoteMicServerFMXApplication.dproj new file mode 100644 index 00000000..f9609abc --- /dev/null +++ b/Demos/RemoteMic/RemoteMicServerFMXApplication.dproj @@ -0,0 +1,1376 @@ + + + {2DC28130-9EB1-48C8-9CA6-0F3CFD5D8E1D} + RemoteMicServerFMXApplication.dpr + True + Release + 38037 + Application + FMX + 18.8 + Win32 + + + true + + + true + Base + true + + + true + Base + true + + + true + Base + true + + + true + Base + true + + + true + Base + true + + + true + Base + true + + + true + Base + true + + + true + Base + true + + + true + Base + true + + + true + Base + true + + + true + Base + true + + + true + Cfg_1 + true + true + + + true + Cfg_1 + true + true + + + true + Cfg_1 + true + true + + + true + Cfg_1 + true + true + + + true + Base + true + + + true + Cfg_2 + true + true + + + true + Cfg_2 + true + true + + + true + Cfg_2 + true + true + + + true + Cfg_2 + true + true + + + true + Cfg_2 + true + true + + + true + Cfg_2 + true + true + + + false + false + false + false + false + 00400000 + RemoteMicServerFMXApplication + 1040 + CompanyName=;FileDescription=;FileVersion=1.0.0.0;InternalName=;LegalCopyright=;LegalTrademarks=;OriginalFilename=;ProductName=;ProductVersion=1.0.0.0;Comments=;CFBundleName= + System;Xml;Data;Datasnap;Web;Soap;$(DCC_Namespace) + $(BDS)\bin\delphi_PROJECTICON.ico + $(BDS)\bin\delphi_PROJECTICNS.icns + .\bin + .\lib\$(Platform)\$(Config) + + + package=com.embarcadero.$(MSBuildProjectName);label=$(MSBuildProjectName);versionCode=1;versionName=1.0.0;persistent=False;restoreAnyVersion=False;installLocation=auto;largeHeap=False;theme=TitleBar;hardwareAccelerated=true;apiKey= + Debug + true + $(BDS)\bin\Artwork\Android\FM_LauncherIcon_36x36.png + $(BDS)\bin\Artwork\Android\FM_LauncherIcon_48x48.png + $(BDS)\bin\Artwork\Android\FM_LauncherIcon_72x72.png + $(BDS)\bin\Artwork\Android\FM_LauncherIcon_96x96.png + $(BDS)\bin\Artwork\Android\FM_LauncherIcon_144x144.png + $(BDS)\bin\Artwork\Android\FM_SplashImage_426x320.png + $(BDS)\bin\Artwork\Android\FM_SplashImage_470x320.png + $(BDS)\bin\Artwork\Android\FM_SplashImage_640x480.png + $(BDS)\bin\Artwork\Android\FM_SplashImage_960x720.png + true + true + true + true + true + true + true + true + true + true + android-support-v4.dex.jar;cloud-messaging.dex.jar;fmx.dex.jar;google-analytics-v2.dex.jar;google-play-billing.dex.jar;google-play-licensing.dex.jar;google-play-services.dex.jar + 1 + $(BDS)\bin\Artwork\Android\FM_NotificationIcon_24x24.png + $(BDS)\bin\Artwork\Android\FM_NotificationIcon_36x36.png + $(BDS)\bin\Artwork\Android\FM_NotificationIcon_48x48.png + $(BDS)\bin\Artwork\Android\FM_NotificationIcon_72x72.png + $(BDS)\bin\Artwork\Android\FM_NotificationIcon_96x96.png + + + package=com.embarcadero.$(MSBuildProjectName);label=$(MSBuildProjectName);versionCode=1;versionName=1.0.0;persistent=False;restoreAnyVersion=False;installLocation=auto;largeHeap=False;theme=TitleBar;hardwareAccelerated=true;apiKey= + Debug + true + true + Base + true + $(BDS)\bin\Artwork\Android\FM_LauncherIcon_36x36.png + $(BDS)\bin\Artwork\Android\FM_LauncherIcon_48x48.png + $(BDS)\bin\Artwork\Android\FM_LauncherIcon_72x72.png + $(BDS)\bin\Artwork\Android\FM_LauncherIcon_96x96.png + $(BDS)\bin\Artwork\Android\FM_LauncherIcon_144x144.png + $(BDS)\bin\Artwork\Android\FM_SplashImage_426x320.png + $(BDS)\bin\Artwork\Android\FM_SplashImage_470x320.png + $(BDS)\bin\Artwork\Android\FM_SplashImage_640x480.png + $(BDS)\bin\Artwork\Android\FM_SplashImage_960x720.png + true + true + true + true + true + true + true + true + true + true + android-support-v4.dex.jar;cloud-messaging.dex.jar;fmx.dex.jar;google-analytics-v2.dex.jar;google-play-billing.dex.jar;google-play-licensing.dex.jar;google-play-services.dex.jar + 1 + + + CFBundleName=$(MSBuildProjectName);CFBundleDevelopmentRegion=en;CFBundleDisplayName=$(MSBuildProjectName);CFBundleIdentifier=$(MSBuildProjectName);CFBundleInfoDictionaryVersion=7.1;CFBundleVersion=1.0.0.0;CFBundlePackageType=APPL;CFBundleSignature=????;LSRequiresIPhoneOS=true;CFBundleAllowMixedLocalizations=YES;CFBundleExecutable=$(MSBuildProjectName);UIDeviceFamily=iPhone & iPad;CFBundleResourceSpecification=ResourceRules.plist;NSLocationAlwaysUsageDescription=The reason for accessing the location information of the user;NSLocationWhenInUseUsageDescription=The reason for accessing the location information of the user;FMLocalNotificationPermission=false;UIBackgroundModes=;NSContactsUsageDescription=The reason for accessing the contacts;NSPhotoLibraryUsageDescription=The reason for accessing the photo library;NSCameraUsageDescription=The reason for accessing the camera + iPhoneAndiPad + true + Debug + $(MSBuildProjectName) + $(BDS)\bin\Artwork\iOS\iPhone\FM_ApplicationIcon_60x60.png + $(BDS)\bin\Artwork\iOS\iPhone\FM_ApplicationIcon_120x120.png + $(BDS)\bin\Artwork\iOS\iPhone\FM_SpotlightSearchIcon_40x40.png + $(BDS)\bin\Artwork\iOS\iPhone\FM_SpotlightSearchIcon_80x80.png + $(BDS)\bin\Artwork\iOS\iPad\FM_SpotlightSearchIcon_40x40.png + $(BDS)\bin\Artwork\iOS\iPad\FM_SpotlightSearchIcon_80x80.png + $(BDS)\bin\Artwork\iOS\iPad\FM_ApplicationIcon_76x76.png + $(BDS)\bin\Artwork\iOS\iPad\FM_ApplicationIcon_152x152.png + $(BDS)\bin\Artwork\iOS\iPad\FM_LaunchImagePortrait_768x1024.png + $(BDS)\bin\Artwork\iOS\iPad\FM_LaunchImageLandscape_1024x768.png + $(BDS)\bin\Artwork\iOS\iPad\FM_LaunchImagePortrait_1536x2048.png + $(BDS)\bin\Artwork\iOS\iPad\FM_LaunchImageLandscape_2048x1536.png + $(BDS)\bin\Artwork\iOS\iPhone\FM_ApplicationIcon_87x87.png + $(BDS)\bin\Artwork\iOS\iPhone\FM_ApplicationIcon_180x180.png + $(BDS)\bin\Artwork\iOS\iPhone\FM_LaunchImage_750x1334.png + $(BDS)\bin\Artwork\iOS\iPhone\FM_LaunchImage_1242x2208.png + $(BDS)\bin\Artwork\iOS\iPhone\FM_LaunchImage_2208x1242.png + $(BDS)\bin\Artwork\iOS\iPhone\FM_LaunchImage_1125x2436.png + $(BDS)\bin\Artwork\iOS\iPhone\FM_LaunchImage_2436x1125.png + $(BDS)\bin\Artwork\iOS\iPhone\FM_SpotlightSearchIcon_120x120.png + $(BDS)\bin\Artwork\iOS\iPhone\FM_LaunchImage_828x1792.png + $(BDS)\bin\Artwork\iOS\iPhone\FM_LaunchImage_1136x640.png + $(BDS)\bin\Artwork\iOS\iPhone\FM_LaunchImage_1242x2688.png + $(BDS)\bin\Artwork\iOS\iPhone\FM_LaunchImage_1334x750.png + $(BDS)\bin\Artwork\iOS\iPhone\FM_LaunchImage_1792x828.png + $(BDS)\bin\Artwork\iOS\iPhone\FM_LaunchImage_2688x1242.png + $(BDS)\bin\Artwork\iOS\iPad\FM_ApplicationIcon_167x167.png + $(BDS)\bin\Artwork\iOS\iPad\FM_LaunchImagePortrait_1668x2224.png + $(BDS)\bin\Artwork\iOS\iPad\FM_LaunchImagePortrait_1668x2388.png + $(BDS)\bin\Artwork\iOS\iPad\FM_LaunchImagePortrait_2048x2732.png + $(BDS)\bin\Artwork\iOS\iPad\FM_LaunchImageLandscape_2224x1668.png + $(BDS)\bin\Artwork\iOS\iPad\FM_LaunchImageLandscape_2388x1668.png + $(BDS)\bin\Artwork\iOS\iPad\FM_LaunchImageLandscape_2732x2048.png + + + $(BDS)\bin\Artwork\iOS\iPhone\FM_ApplicationIcon_87x87.png + $(BDS)\bin\Artwork\iOS\iPhone\FM_ApplicationIcon_180x180.png + $(BDS)\bin\Artwork\iOS\iPhone\FM_LaunchImage_750x1334.png + $(BDS)\bin\Artwork\iOS\iPhone\FM_LaunchImage_1242x2208.png + $(BDS)\bin\Artwork\iOS\iPhone\FM_LaunchImage_2208x1242.png + $(BDS)\bin\Artwork\iOS\iPhone\FM_LaunchImage_1125x2436.png + $(BDS)\bin\Artwork\iOS\iPhone\FM_LaunchImage_2436x1125.png + $(BDS)\bin\Artwork\iOS\iPhone\FM_SpotlightSearchIcon_120x120.png + $(BDS)\bin\Artwork\iOS\iPhone\FM_LaunchImage_828x1792.png + $(BDS)\bin\Artwork\iOS\iPhone\FM_LaunchImage_1136x640.png + $(BDS)\bin\Artwork\iOS\iPhone\FM_LaunchImage_1242x2688.png + $(BDS)\bin\Artwork\iOS\iPhone\FM_LaunchImage_1334x750.png + $(BDS)\bin\Artwork\iOS\iPhone\FM_LaunchImage_1792x828.png + $(BDS)\bin\Artwork\iOS\iPhone\FM_LaunchImage_2688x1242.png + $(BDS)\bin\Artwork\iOS\iPad\FM_ApplicationIcon_167x167.png + $(BDS)\bin\Artwork\iOS\iPad\FM_LaunchImagePortrait_1668x2224.png + $(BDS)\bin\Artwork\iOS\iPad\FM_LaunchImagePortrait_1668x2388.png + $(BDS)\bin\Artwork\iOS\iPad\FM_LaunchImagePortrait_2048x2732.png + $(BDS)\bin\Artwork\iOS\iPad\FM_LaunchImageLandscape_2224x1668.png + $(BDS)\bin\Artwork\iOS\iPad\FM_LaunchImageLandscape_2388x1668.png + $(BDS)\bin\Artwork\iOS\iPad\FM_LaunchImageLandscape_2732x2048.png + + + CFBundleName=$(MSBuildProjectName);CFBundleDevelopmentRegion=en;CFBundleDisplayName=$(MSBuildProjectName);CFBundleIdentifier=$(MSBuildProjectName);CFBundleInfoDictionaryVersion=7.1;CFBundleVersion=1.0.0.0;CFBundlePackageType=APPL;CFBundleSignature=????;LSRequiresIPhoneOS=true;CFBundleAllowMixedLocalizations=YES;CFBundleExecutable=$(MSBuildProjectName);UIDeviceFamily=iPhone & iPad;CFBundleResourceSpecification=ResourceRules.plist;NSLocationAlwaysUsageDescription=The reason for accessing the location information of the user;NSLocationWhenInUseUsageDescription=The reason for accessing the location information of the user;FMLocalNotificationPermission=false;UIBackgroundModes=;NSContactsUsageDescription=The reason for accessing the contacts;NSPhotoLibraryUsageDescription=The reason for accessing the photo library;NSCameraUsageDescription=The reason for accessing the camera + iPhoneAndiPad + true + $(BDS)\bin\Artwork\iOS\iPhone\FM_ApplicationIcon_60x60.png + $(BDS)\bin\Artwork\iOS\iPhone\FM_ApplicationIcon_120x120.png + $(BDS)\bin\Artwork\iOS\iPhone\FM_SpotlightSearchIcon_40x40.png + $(BDS)\bin\Artwork\iOS\iPhone\FM_SpotlightSearchIcon_80x80.png + $(BDS)\bin\Artwork\iOS\iPad\FM_SpotlightSearchIcon_40x40.png + $(BDS)\bin\Artwork\iOS\iPad\FM_SpotlightSearchIcon_80x80.png + $(BDS)\bin\Artwork\iOS\iPad\FM_ApplicationIcon_76x76.png + $(BDS)\bin\Artwork\iOS\iPad\FM_ApplicationIcon_152x152.png + $(BDS)\bin\Artwork\iOS\iPad\FM_LaunchImagePortrait_768x1024.png + $(BDS)\bin\Artwork\iOS\iPad\FM_LaunchImageLandscape_1024x768.png + $(BDS)\bin\Artwork\iOS\iPad\FM_LaunchImagePortrait_1536x2048.png + $(BDS)\bin\Artwork\iOS\iPad\FM_LaunchImageLandscape_2048x1536.png + $(BDS)\bin\Artwork\iOS\iPhone\FM_ApplicationIcon_87x87.png + $(BDS)\bin\Artwork\iOS\iPhone\FM_ApplicationIcon_180x180.png + $(BDS)\bin\Artwork\iOS\iPhone\FM_LaunchImage_750x1334.png + $(BDS)\bin\Artwork\iOS\iPhone\FM_LaunchImage_1242x2208.png + $(BDS)\bin\Artwork\iOS\iPhone\FM_LaunchImage_2208x1242.png + $(BDS)\bin\Artwork\iOS\iPhone\FM_LaunchImage_1125x2436.png + $(BDS)\bin\Artwork\iOS\iPhone\FM_LaunchImage_2436x1125.png + $(BDS)\bin\Artwork\iOS\iPhone\FM_SpotlightSearchIcon_120x120.png + $(BDS)\bin\Artwork\iOS\iPhone\FM_LaunchImage_828x1792.png + $(BDS)\bin\Artwork\iOS\iPhone\FM_LaunchImage_1136x640.png + $(BDS)\bin\Artwork\iOS\iPhone\FM_LaunchImage_1242x2688.png + $(BDS)\bin\Artwork\iOS\iPhone\FM_LaunchImage_1334x750.png + $(BDS)\bin\Artwork\iOS\iPhone\FM_LaunchImage_1792x828.png + $(BDS)\bin\Artwork\iOS\iPhone\FM_LaunchImage_2688x1242.png + $(BDS)\bin\Artwork\iOS\iPad\FM_ApplicationIcon_167x167.png + $(BDS)\bin\Artwork\iOS\iPad\FM_LaunchImagePortrait_1668x2224.png + $(BDS)\bin\Artwork\iOS\iPad\FM_LaunchImagePortrait_1668x2388.png + $(BDS)\bin\Artwork\iOS\iPad\FM_LaunchImagePortrait_2048x2732.png + $(BDS)\bin\Artwork\iOS\iPad\FM_LaunchImageLandscape_2224x1668.png + $(BDS)\bin\Artwork\iOS\iPad\FM_LaunchImageLandscape_2388x1668.png + $(BDS)\bin\Artwork\iOS\iPad\FM_LaunchImageLandscape_2732x2048.png + + + /usr/bin/xterm -e "%debuggee%" + (None) + RemoteMicServer_Icon.ico + + + CFBundleName=$(MSBuildProjectName);CFBundleDisplayName=$(MSBuildProjectName);CFBundleIdentifier=$(MSBuildProjectName);CFBundleVersion=1.0.0;CFBundlePackageType=APPL;CFBundleSignature=????;CFBundleAllowMixedLocalizations=YES;CFBundleExecutable=$(MSBuildProjectName);NSHighResolutionCapable=true;LSApplicationCategoryType=public.app-category.utilities;NSContactsUsageDescription=The reason for accessing the contacts;CFBundleShortVersionString=1.0.0;NSLocationUsageDescription=The reason for accessing the location information of the user + Debug + true + /usr/X11/bin/xterm -e "%debuggee%" + (None) + RemoteMicServer_Icon.ico + + + CFBundleName=$(MSBuildProjectName);CFBundleDisplayName=$(MSBuildProjectName);CFBundleIdentifier=$(MSBuildProjectName);CFBundleVersion=1.0.0;CFBundlePackageType=APPL;CFBundleSignature=????;CFBundleAllowMixedLocalizations=YES;CFBundleExecutable=$(MSBuildProjectName);NSHighResolutionCapable=true;LSApplicationCategoryType=public.app-category.utilities;NSContactsUsageDescription=The reason for accessing the contacts;CFBundleShortVersionString=1.0.0;NSLocationUsageDescription=The reason for accessing the location information of the user + Debug + true + true + Base + true + /usr/X11/bin/xterm -e "%debuggee%" + (None) + RemoteMicServer_Icon.ico + + + Winapi;System.Win;Data.Win;Datasnap.Win;Web.Win;Soap.Win;Xml.Win;Bde;$(DCC_Namespace) + Debug + true + CompanyName=;FileDescription=$(MSBuildProjectName);FileVersion=1.0.0.0;InternalName=;LegalCopyright=;LegalTrademarks=;OriginalFilename=;ProductName=$(MSBuildProjectName);ProductVersion=1.0.0.0;Comments=;ProgramID=com.embarcadero.$(MSBuildProjectName) + 1033 + $(BDS)\bin\default_app.manifest + RemoteMicServer_Icon.ico + true + $(BDS)\bin\Artwork\Windows\UWP\delphi_UwpDefault_44.png + $(BDS)\bin\Artwork\Windows\UWP\delphi_UwpDefault_150.png + + + $(BDS)\bin\default_app.manifest + RemoteMicServerFMXApplication_Icon.ico + true + $(BDS)\bin\Artwork\Windows\UWP\delphi_UwpDefault_44.png + $(BDS)\bin\Artwork\Windows\UWP\delphi_UwpDefault_150.png + + + RELEASE;$(DCC_Define) + 0 + false + 0 + + + Debug + + + true + CFBundleName=$(MSBuildProjectName);CFBundleDisplayName=$(MSBuildProjectName);CFBundleIdentifier=$(MSBuildProjectName);CFBundleVersion=1.0.0;CFBundlePackageType=APPL;CFBundleSignature=????;CFBundleAllowMixedLocalizations=YES;CFBundleExecutable=$(MSBuildProjectName);NSHighResolutionCapable=true;LSApplicationCategoryType=public.app-category.utilities;NSContactsUsageDescription=The reason for accessing the contacts;CFBundleShortVersionString=1.0.0;NSLocationUsageDescription=The reason for accessing the location information of the user + + + true + Cfg_1 + true + true + CFBundleName=$(MSBuildProjectName);CFBundleDisplayName=$(MSBuildProjectName);CFBundleIdentifier=$(MSBuildProjectName);CFBundleVersion=1.0.0;CFBundlePackageType=APPL;CFBundleSignature=????;CFBundleAllowMixedLocalizations=YES;CFBundleExecutable=$(MSBuildProjectName);NSHighResolutionCapable=true;LSApplicationCategoryType=public.app-category.utilities;NSContactsUsageDescription=The reason for accessing the contacts;CFBundleShortVersionString=1.0.0;NSLocationUsageDescription=The reason for accessing the location information of the user + + + true + true + 1033 + CompanyName=;FileDescription=$(MSBuildProjectName);FileVersion=1.0.0.0;InternalName=;LegalCopyright=;LegalTrademarks=;OriginalFilename=;ProductName=$(MSBuildProjectName);ProductVersion=1.0.0.0;Comments=;ProgramID=com.embarcadero.$(MSBuildProjectName) + RemoteMicServer_Icon.ico + PerMonitor + + + DEBUG;$(DCC_Define) + false + true + + + true + + + Debug + + + true + + + $(BDS)\bin\delphi_PROJECTICNS.icns + true + + + true + true + Cfg_2 + true + + + true + true + 1033 + CompanyName=;FileDescription=$(MSBuildProjectName);FileVersion=1.0.0.0;InternalName=;LegalCopyright=;LegalTrademarks=;OriginalFilename=;ProductName=$(MSBuildProjectName);ProductVersion=1.0.0.0;Comments=;ProgramID=com.embarcadero.$(MSBuildProjectName) + RemoteMicServer_Icon.ico + PerMonitor + + + + MainSource + + +
MainForm
+
+ + + + Cfg_2 + Base + + + Base + + + Cfg_1 + Base + +
+ + Delphi.Personality.12 + + + + + RemoteMicServerFMXApplication.dpr + + + Microsoft Office 2000 Sample Automation Server Wrapper Components + Microsoft Office XP Sample Automation Server Wrapper Components + + + + True + True + False + True + False + True + True + True + True + False + + + + + true + + + + + true + + + + + true + + + + + true + + + + + true + + + + + true + + + + + RemoteMicServerFMXApplication.exe + true + + + + + 1 + + + Contents\MacOS + 1 + + + 0 + + + + + classes + 1 + + + classes + 1 + + + + + res\xml + 1 + + + res\xml + 1 + + + + + library\lib\armeabi-v7a + 1 + + + + + library\lib\armeabi + 1 + + + library\lib\armeabi + 1 + + + + + library\lib\armeabi-v7a + 1 + + + + + library\lib\mips + 1 + + + library\lib\mips + 1 + + + + + library\lib\armeabi-v7a + 1 + + + library\lib\arm64-v8a + 1 + + + + + library\lib\armeabi-v7a + 1 + + + + + res\drawable + 1 + + + res\drawable + 1 + + + + + res\values + 1 + + + res\values + 1 + + + + + res\values-v21 + 1 + + + res\values-v21 + 1 + + + + + res\values + 1 + + + res\values + 1 + + + + + res\drawable + 1 + + + res\drawable + 1 + + + + + res\drawable-xxhdpi + 1 + + + res\drawable-xxhdpi + 1 + + + + + res\drawable-ldpi + 1 + + + res\drawable-ldpi + 1 + + + + + res\drawable-mdpi + 1 + + + res\drawable-mdpi + 1 + + + + + res\drawable-hdpi + 1 + + + res\drawable-hdpi + 1 + + + + + res\drawable-xhdpi + 1 + + + res\drawable-xhdpi + 1 + + + + + res\drawable-mdpi + 1 + + + res\drawable-mdpi + 1 + + + + + res\drawable-hdpi + 1 + + + res\drawable-hdpi + 1 + + + + + res\drawable-xhdpi + 1 + + + res\drawable-xhdpi + 1 + + + + + res\drawable-xxhdpi + 1 + + + res\drawable-xxhdpi + 1 + + + + + res\drawable-xxxhdpi + 1 + + + res\drawable-xxxhdpi + 1 + + + + + res\drawable-small + 1 + + + res\drawable-small + 1 + + + + + res\drawable-normal + 1 + + + res\drawable-normal + 1 + + + + + res\drawable-large + 1 + + + res\drawable-large + 1 + + + + + res\drawable-xlarge + 1 + + + res\drawable-xlarge + 1 + + + + + res\values + 1 + + + res\values + 1 + + + + + 1 + + + Contents\MacOS + 1 + + + 0 + + + + + Contents\MacOS + 1 + .framework + + + Contents\MacOS + 1 + .framework + + + 0 + + + + + 1 + .dylib + + + 1 + .dylib + + + 1 + .dylib + + + Contents\MacOS + 1 + .dylib + + + Contents\MacOS + 1 + .dylib + + + 0 + .dll;.bpl + + + + + 1 + .dylib + + + 1 + .dylib + + + 1 + .dylib + + + Contents\MacOS + 1 + .dylib + + + Contents\MacOS + 1 + .dylib + + + 0 + .bpl + + + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + Contents\Resources\StartUp\ + 0 + + + Contents\Resources\StartUp\ + 0 + + + 0 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + + + ..\$(PROJECTNAME).app.dSYM\Contents\Resources\DWARF + 1 + + + ..\$(PROJECTNAME).app.dSYM\Contents\Resources\DWARF + 1 + + + + + 1 + + + 1 + + + + + ..\ + 1 + + + ..\ + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + ..\$(PROJECTNAME).app.dSYM\Contents\Resources\DWARF + 1 + + + + + ..\ + 1 + + + ..\ + 1 + + + + + Contents + 1 + + + Contents + 1 + + + + + Contents\Resources + 1 + + + Contents\Resources + 1 + + + + + library\lib\armeabi-v7a + 1 + + + library\lib\arm64-v8a + 1 + + + 1 + + + 1 + + + 1 + + + 1 + + + Contents\MacOS + 1 + + + Contents\MacOS + 1 + + + 0 + + + + + library\lib\armeabi-v7a + 1 + + + + + 1 + + + 1 + + + + + Assets + 1 + + + Assets + 1 + + + + + Assets + 1 + + + Assets + 1 + + + + + + + + + + + + + + + 12 + + + + +
diff --git a/Demos/RemoteMic/RemoteMicServerISAPI.dpr b/Demos/RemoteMic/RemoteMicServerISAPI.dpr new file mode 100644 index 00000000..b6ce1bde --- /dev/null +++ b/Demos/RemoteMic/RemoteMicServerISAPI.dpr @@ -0,0 +1,30 @@ +(* + Copyright 2016, MARS-Curiosity - REST Library + + Home: https://github.com/andrea-magni/MARS +*) +library RemoteMicServerISAPI; + +uses + Winapi.ActiveX, + System.Win.ComObj, + Web.WebBroker, + Web.Win.ISAPIApp, + Web.Win.ISAPIThreadPool, + Server.Ignition in 'Server.Ignition.pas', + Server.Resources in 'Server.Resources.pas', + Server.WebModule in 'Server.WebModule.pas' {ServerWebModule: TWebModule}; + +{$R *.res} + +exports + GetExtensionVersion, + HttpExtensionProc, + TerminateExtension; + +begin + CoInitFlags := COINIT_MULTITHREADED; + Application.Initialize; + Application.WebModuleClass := WebModuleClass; + Application.Run; +end. diff --git a/Demos/RemoteMic/RemoteMicServerISAPI.dproj b/Demos/RemoteMic/RemoteMicServerISAPI.dproj new file mode 100644 index 00000000..ae9a8091 --- /dev/null +++ b/Demos/RemoteMic/RemoteMicServerISAPI.dproj @@ -0,0 +1,152 @@ + + + {9715EDC7-B308-42F7-B5EF-14BB4698B974} + RemoteMicServerISAPI.dpr + True + Release + 1 + Library + None + 18.8 + Win32 + + + true + + + true + Base + true + + + true + Base + true + + + true + Base + true + + + true + Cfg_1 + true + true + + + true + Base + true + + + true + Cfg_2 + true + true + + + true + Cfg_2 + true + true + + + false + false + false + false + false + 00400000 + true + RemoteMicServerISAPI + 1040 + CompanyName=;FileDescription=;FileVersion=1.0.0.0;InternalName=;LegalCopyright=;LegalTrademarks=;OriginalFilename=;ProductName=;ProductVersion=1.0.0.0;Comments=;CFBundleName= + System;Xml;Data;Datasnap;Web;Soap;$(DCC_Namespace) + .\bin + .\lib\$(Platform)\$(Config) + + + Winapi;System.Win;Data.Win;Datasnap.Win;Web.Win;Soap.Win;Xml.Win;Bde;$(DCC_Namespace) + Debug + true + CompanyName=;FileDescription=$(MSBuildProjectName);FileVersion=1.0.0.0;InternalName=;LegalCopyright=;LegalTrademarks=;OriginalFilename=;ProductName=$(MSBuildProjectName);ProductVersion=1.0.0.0;Comments=;ProgramID=com.embarcadero.$(MSBuildProjectName) + 1033 + (None) + + + RemoteMicServerISAPI_Icon.ico + + + RELEASE;$(DCC_Define) + 0 + false + 0 + + + true + 1033 + CompanyName=;FileDescription=$(MSBuildProjectName);FileVersion=1.0.0.0;InternalName=;LegalCopyright=;LegalTrademarks=;OriginalFilename=;ProductName=$(MSBuildProjectName);ProductVersion=1.0.0.0;Comments=;ProgramID=com.embarcadero.$(MSBuildProjectName) + + + DEBUG;$(DCC_Define) + false + true + + + Debug + + + true + 1033 + CompanyName=;FileDescription=$(MSBuildProjectName);FileVersion=1.0.0.0;InternalName=;LegalCopyright=;LegalTrademarks=;OriginalFilename=;ProductName=$(MSBuildProjectName);ProductVersion=1.0.0.0;Comments=;ProgramID=com.embarcadero.$(MSBuildProjectName) + (None) + + + + MainSource + + + + +
ServerWebModule
+ TWebModule +
+ + Cfg_2 + Base + + + Base + + + Cfg_1 + Base + +
+ + Delphi.Personality.12 + + + + + RemoteMicServerISAPI.dpr + + + Microsoft Office 2000 Sample Automation Server Wrapper Components + Microsoft Office XP Sample Automation Server Wrapper Components + + + + False + False + False + True + False + + + 12 + + + +
diff --git a/Demos/RemoteMic/RemoteMicServerService.dpr b/Demos/RemoteMic/RemoteMicServerService.dpr new file mode 100644 index 00000000..2f8b5cc3 --- /dev/null +++ b/Demos/RemoteMic/RemoteMicServerService.dpr @@ -0,0 +1,43 @@ +(* + Copyright 2016, MARS-Curiosity - REST Library + + Home: https://github.com/andrea-magni/MARS +*) +program RemoteMicServerService; + +{$I MARS.inc} + +uses +{$ifdef DelphiXE3_UP} + Vcl.SvcMgr, +{$else} + SvcMgr, +{$endif} + + Server.Service in 'Server.Service.pas' {ServerService: TService}, + Server.Ignition in 'Server.Ignition.pas', + Server.Resources in 'Server.Resources.pas', + Server.WebModule in 'Server.WebModule.pas' {ServerWebModule: TWebModule}; + +{$R *.RES} + +begin + // Windows 2003 Server requires StartServiceCtrlDispatcher to be + // called before CoRegisterClassObject, which can be called indirectly + // by Application.Initialize. TServiceApplication.DelayInitialize allows + // Application.Initialize to be called from TService.Main (after + // StartServiceCtrlDispatcher has been called). + // + // Delayed initialization of the Application object may affect + // events which then occur prior to initialization, such as + // TService.OnCreate. It is only recommended if the ServiceApplication + // registers a class object with OLE and is intended for use with + // Windows 2003 Server. + // + // Application.DelayInitialize := True; + // + if not Application.DelayInitialize or Application.Installing then + Application.Initialize; + Application.CreateForm(TServerService, ServerService); + Application.Run; +end. diff --git a/Demos/RemoteMic/RemoteMicServerService.dproj b/Demos/RemoteMic/RemoteMicServerService.dproj new file mode 100644 index 00000000..241a9531 --- /dev/null +++ b/Demos/RemoteMic/RemoteMicServerService.dproj @@ -0,0 +1,1135 @@ + + + {FEF5DF94-50B0-46F7-9F4B-6C355F140A2F} + RemoteMicServerService.dpr + True + Release + 3 + Application + None + 18.8 + Win32 + + + true + + + true + Base + true + + + true + Base + true + + + true + Base + true + + + true + Cfg_1 + true + true + + + true + Cfg_1 + true + true + + + true + Base + true + + + true + Cfg_2 + true + true + + + true + Cfg_2 + true + true + + + true + Cfg_2 + true + true + + + false + false + false + false + false + 00400000 + RemoteMicServerService + 1040 + CompanyName=;FileDescription=;FileVersion=1.0.0.0;InternalName=;LegalCopyright=;LegalTrademarks=;OriginalFilename=;ProductName=;ProductVersion=1.0.0.0;Comments=;CFBundleName= + System;Xml;Data;Datasnap;Web;Soap;Vcl;Winapi;$(DCC_Namespace) + .\lib\$(Platform)\$(Config) + .\bin + + + System.Win;Data.Win;Datasnap.Win;Web.Win;Soap.Win;Xml.Win;Bde;$(DCC_Namespace) + Debug + true + CompanyName=;FileDescription=$(MSBuildProjectName);FileVersion=1.0.0.0;InternalName=;LegalCopyright=;LegalTrademarks=;OriginalFilename=;ProductName=$(MSBuildProjectName);ProductVersion=1.0.0.0;Comments=;ProgramID=com.embarcadero.$(MSBuildProjectName) + 1033 + $(BDS)\bin\default_app.manifest + RemoteMicServer_Icon.ico + true + $(BDS)\bin\Artwork\Windows\UWP\delphi_UwpDefault_44.png + $(BDS)\bin\Artwork\Windows\UWP\delphi_UwpDefault_150.png + + + $(BDS)\bin\default_app.manifest + RemoteMicServer_Icon.ico + true + System.Win;Data.Win;Datasnap.Win;Web.Win;Soap.Win;Xml.Win;$(DCC_Namespace) + Debug + true + CompanyName=;FileDescription=$(MSBuildProjectName);FileVersion=1.0.0.0;InternalName=;LegalCopyright=;LegalTrademarks=;OriginalFilename=;ProgramID=com.embarcadero.$(MSBuildProjectName);ProductName=$(MSBuildProjectName);ProductVersion=1.0.0.0;Comments= + 1033 + $(BDS)\bin\Artwork\Windows\UWP\delphi_UwpDefault_44.png + $(BDS)\bin\Artwork\Windows\UWP\delphi_UwpDefault_150.png + + + RELEASE;$(DCC_Define) + 0 + false + 0 + + + true + true + 1033 + CompanyName=;FileDescription=$(MSBuildProjectName);FileVersion=1.0.0.0;InternalName=;LegalCopyright=;LegalTrademarks=;OriginalFilename=;ProductName=$(MSBuildProjectName);ProductVersion=1.0.0.0;Comments=;ProgramID=com.embarcadero.$(MSBuildProjectName) + PerMonitor + + + true + true + 1033 + CompanyName=;FileDescription=$(MSBuildProjectName);FileVersion=1.0.0.0;InternalName=;LegalCopyright=;LegalTrademarks=;OriginalFilename=;ProgramID=com.embarcadero.$(MSBuildProjectName);ProductName=$(MSBuildProjectName);ProductVersion=1.0.0.0;Comments= + $(BDS)\bin\Artwork\Windows\UWP\delphi_UwpDefault_44.png + $(BDS)\bin\Artwork\Windows\UWP\delphi_UwpDefault_150.png + PerMonitor + + + DEBUG;$(DCC_Define) + false + true + + + Debug + + + true + true + 1033 + CompanyName=;FileDescription=$(MSBuildProjectName);FileVersion=1.0.0.0;InternalName=;LegalCopyright=;LegalTrademarks=;OriginalFilename=;ProductName=$(MSBuildProjectName);ProductVersion=1.0.0.0;Comments=;ProgramID=com.embarcadero.$(MSBuildProjectName) + $(BDS)\bin\Artwork\Windows\UWP\delphi_UwpDefault_44.png + $(BDS)\bin\Artwork\Windows\UWP\delphi_UwpDefault_150.png + PerMonitor + + + true + true + 1033 + CompanyName=;FileDescription=$(MSBuildProjectName);FileVersion=1.0.0.0;InternalName=;LegalCopyright=;LegalTrademarks=;OriginalFilename=;ProgramID=com.embarcadero.$(MSBuildProjectName);ProductName=$(MSBuildProjectName);ProductVersion=1.0.0.0;Comments= + MARS.ico + $(BDS)\bin\Artwork\Windows\UWP\delphi_UwpDefault_44.png + $(BDS)\bin\Artwork\Windows\UWP\delphi_UwpDefault_150.png + PerMonitor + + + + MainSource + + +
ServerService
+ TService +
+ + + +
ServerWebModule
+ TWebModule +
+ + Cfg_2 + Base + + + Base + + + Cfg_1 + Base + +
+ + Delphi.Personality.12 + + + + + RemoteMicServerService.dpr + + + Microsoft Office 2000 Sample Automation Server Wrapper Components + Microsoft Office XP Sample Automation Server Wrapper Components + + + + False + False + False + True + True + + + + + Assets\ + Logo150x150.png + true + + + + + true + + + + + Assets\ + Logo44x44.png + true + + + + + Assets\ + Logo150x150.png + true + + + + + true + + + + + true + + + + + true + + + + + true + + + + + true + + + + + Assets\ + Logo44x44.png + true + + + + + 1 + + + Contents\MacOS + 1 + + + 0 + + + + + classes + 1 + + + classes + 1 + + + + + res\xml + 1 + + + res\xml + 1 + + + + + library\lib\armeabi-v7a + 1 + + + + + library\lib\armeabi + 1 + + + library\lib\armeabi + 1 + + + + + library\lib\armeabi-v7a + 1 + + + + + library\lib\mips + 1 + + + library\lib\mips + 1 + + + + + library\lib\armeabi-v7a + 1 + + + library\lib\arm64-v8a + 1 + + + + + library\lib\armeabi-v7a + 1 + + + + + res\drawable + 1 + + + res\drawable + 1 + + + + + res\values + 1 + + + res\values + 1 + + + + + res\values-v21 + 1 + + + res\values-v21 + 1 + + + + + res\values + 1 + + + res\values + 1 + + + + + res\drawable + 1 + + + res\drawable + 1 + + + + + res\drawable-xxhdpi + 1 + + + res\drawable-xxhdpi + 1 + + + + + res\drawable-ldpi + 1 + + + res\drawable-ldpi + 1 + + + + + res\drawable-mdpi + 1 + + + res\drawable-mdpi + 1 + + + + + res\drawable-hdpi + 1 + + + res\drawable-hdpi + 1 + + + + + res\drawable-xhdpi + 1 + + + res\drawable-xhdpi + 1 + + + + + res\drawable-mdpi + 1 + + + res\drawable-mdpi + 1 + + + + + res\drawable-hdpi + 1 + + + res\drawable-hdpi + 1 + + + + + res\drawable-xhdpi + 1 + + + res\drawable-xhdpi + 1 + + + + + res\drawable-xxhdpi + 1 + + + res\drawable-xxhdpi + 1 + + + + + res\drawable-xxxhdpi + 1 + + + res\drawable-xxxhdpi + 1 + + + + + res\drawable-small + 1 + + + res\drawable-small + 1 + + + + + res\drawable-normal + 1 + + + res\drawable-normal + 1 + + + + + res\drawable-large + 1 + + + res\drawable-large + 1 + + + + + res\drawable-xlarge + 1 + + + res\drawable-xlarge + 1 + + + + + res\values + 1 + + + res\values + 1 + + + + + 1 + + + Contents\MacOS + 1 + + + 0 + + + + + Contents\MacOS + 1 + .framework + + + Contents\MacOS + 1 + .framework + + + 0 + + + + + 1 + .dylib + + + 1 + .dylib + + + 1 + .dylib + + + Contents\MacOS + 1 + .dylib + + + Contents\MacOS + 1 + .dylib + + + 0 + .dll;.bpl + + + + + 1 + .dylib + + + 1 + .dylib + + + 1 + .dylib + + + Contents\MacOS + 1 + .dylib + + + Contents\MacOS + 1 + .dylib + + + 0 + .bpl + + + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + Contents\Resources\StartUp\ + 0 + + + Contents\Resources\StartUp\ + 0 + + + 0 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + + + ..\$(PROJECTNAME).app.dSYM\Contents\Resources\DWARF + 1 + + + ..\$(PROJECTNAME).app.dSYM\Contents\Resources\DWARF + 1 + + + + + 1 + + + 1 + + + + + ..\ + 1 + + + ..\ + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + ..\$(PROJECTNAME).app.dSYM\Contents\Resources\DWARF + 1 + + + + + ..\ + 1 + + + ..\ + 1 + + + + + Contents + 1 + + + Contents + 1 + + + + + Contents\Resources + 1 + + + Contents\Resources + 1 + + + + + library\lib\armeabi-v7a + 1 + + + library\lib\arm64-v8a + 1 + + + 1 + + + 1 + + + 1 + + + 1 + + + Contents\MacOS + 1 + + + Contents\MacOS + 1 + + + 0 + + + + + library\lib\armeabi-v7a + 1 + + + + + 1 + + + 1 + + + + + Assets + 1 + + + Assets + 1 + + + + + Assets + 1 + + + Assets + 1 + + + + + + + + + + + + + + + 12 + + + + +
diff --git a/Demos/RemoteMic/RemoteMicServer_Icon.ico b/Demos/RemoteMic/RemoteMicServer_Icon.ico new file mode 100644 index 00000000..0067d86b Binary files /dev/null and b/Demos/RemoteMic/RemoteMicServer_Icon.ico differ diff --git a/Demos/RemoteMic/Server.FMX.Forms.Main.fmx b/Demos/RemoteMic/Server.FMX.Forms.Main.fmx new file mode 100644 index 00000000..fdadb145 --- /dev/null +++ b/Demos/RemoteMic/Server.FMX.Forms.Main.fmx @@ -0,0 +1,66 @@ +object MainForm: TMainForm + Left = 0 + Top = 0 + Caption = 'MARS-Curiosity Template Server FMX' + ClientHeight = 480 + ClientWidth = 640 + FormFactor.Width = 320 + FormFactor.Height = 480 + FormFactor.Devices = [Desktop] + OnCreate = FormCreate + OnClose = FormClose + DesignerMasterStyle = 0 + object Layout1: TLayout + Align = Top + Size.Width = 640.000000000000000000 + Size.Height = 121.000000000000000000 + Size.PlatformDefault = False + TabOrder = 1 + object PortNumberEdit: TEdit + Touch.InteractiveGestures = [LongTap, DoubleTap] + TabOrder = 0 + Text = '8080' + Position.X = 109.000000000000000000 + Position.Y = 22.000000000000000000 + OnChange = PortNumberEditChange + end + object Label1: TLabel + Position.X = 16.000000000000000000 + Position.Y = 24.000000000000000000 + Size.Width = 89.000000000000000000 + Size.Height = 17.000000000000000000 + Size.PlatformDefault = False + Text = 'Port number:' + end + object StartButton: TButton + Action = StartServerAction + Enabled = True + ImageIndex = -1 + Position.X = 16.000000000000000000 + Position.Y = 64.000000000000000000 + TabOrder = 2 + end + object StopButton: TButton + Action = StopServerAction + Enabled = True + ImageIndex = -1 + Position.X = 104.000000000000000000 + Position.Y = 64.000000000000000000 + TabOrder = 3 + end + end + object MainActionList: TActionList + Left = 384 + Top = 24 + object StartServerAction: TAction + Text = 'Start Server' + OnExecute = StartServerActionExecute + OnUpdate = StartServerActionUpdate + end + object StopServerAction: TAction + Text = 'Stop Server' + OnExecute = StopServerActionExecute + OnUpdate = StopServerActionUpdate + end + end +end diff --git a/Demos/RemoteMic/Server.FMX.Forms.Main.pas b/Demos/RemoteMic/Server.FMX.Forms.Main.pas new file mode 100644 index 00000000..36c1f348 --- /dev/null +++ b/Demos/RemoteMic/Server.FMX.Forms.Main.pas @@ -0,0 +1,97 @@ +(* + Copyright 2016, MARS-Curiosity - REST Library + + Home: https://github.com/andrea-magni/MARS +*) +unit Server.FMX.Forms.Main; + +interface + +uses + System.SysUtils, System.Types, System.UITypes, System.Classes, System.Variants, + FMX.Types, FMX.Controls, FMX.Forms, FMX.Graphics, FMX.Dialogs, FMX.StdCtrls, + FMX.Controls.Presentation, FMX.Edit, FMX.Layouts, System.Actions, FMX.ActnList + , MARS.http.Server.Indy +; + +type + TMainForm = class(TForm) + MainActionList: TActionList; + StartServerAction: TAction; + StopServerAction: TAction; + Layout1: TLayout; + PortNumberEdit: TEdit; + Label1: TLabel; + StartButton: TButton; + StopButton: TButton; + procedure FormClose(Sender: TObject; var Action: TCloseAction); + procedure FormCreate(Sender: TObject); + procedure StartServerActionExecute(Sender: TObject); + procedure StopServerActionExecute(Sender: TObject); + procedure StartServerActionUpdate(Sender: TObject); + procedure StopServerActionUpdate(Sender: TObject); + procedure PortNumberEditChange(Sender: TObject); + private + FServer: TMARShttpServerIndy; + public + end; + +var + MainForm: TMainForm; + +implementation + +{$R *.fmx} + +uses + Web.HttpApp + , MARS.Core.URL, MARS.Core.Engine + , Server.Ignition; + +procedure TMainForm.FormClose(Sender: TObject; var Action: TCloseAction); +begin + StopServerAction.Execute; +end; + +procedure TMainForm.FormCreate(Sender: TObject); +begin + PortNumberEdit.Text := TServerEngine.Default.Port.ToString; + + StartServerAction.Execute; +end; + +procedure TMainForm.PortNumberEditChange(Sender: TObject); +begin + TServerEngine.Default.Port := StrToInt(PortNumberEdit.Text); +end; + +procedure TMainForm.StartServerActionExecute(Sender: TObject); +begin + // http server implementation + FServer := TMARShttpServerIndy.Create(TServerEngine.Default); + try + FServer.DefaultPort := TServerEngine.Default.Port; + FServer.Active := True; + except + FServer.Free; + raise; + end; +end; + +procedure TMainForm.StartServerActionUpdate(Sender: TObject); +begin + StartServerAction.Enabled := (FServer = nil) or (FServer.Active = False); +end; + +procedure TMainForm.StopServerActionExecute(Sender: TObject); +begin + FServer.Active := False; + FreeAndNil(FServer); +end; + +procedure TMainForm.StopServerActionUpdate(Sender: TObject); +begin + StopServerAction.Enabled := Assigned(FServer) and (FServer.Active = True); +end; + +end. diff --git a/Demos/RemoteMic/Server.Forms.Main.dfm b/Demos/RemoteMic/Server.Forms.Main.dfm new file mode 100644 index 00000000..8f82c254 --- /dev/null +++ b/Demos/RemoteMic/Server.Forms.Main.dfm @@ -0,0 +1,83 @@ +object MainForm: TMainForm + Left = 0 + Top = 0 + Caption = 'RemoteMic Server' + ClientHeight = 202 + ClientWidth = 464 + Color = clBtnFace + Constraints.MinHeight = 240 + Constraints.MinWidth = 480 + Font.Charset = DEFAULT_CHARSET + Font.Color = clWindowText + Font.Height = -11 + Font.Name = 'Tahoma' + Font.Style = [] + OldCreateOrder = False + OnClose = FormClose + OnCreate = FormCreate + PixelsPerInch = 96 + TextHeight = 13 + object TopPanel: TPanel + Left = 0 + Top = 0 + Width = 464 + Height = 73 + Align = alTop + BevelOuter = bvNone + TabOrder = 0 + object Label1: TLabel + Left = 28 + Top = 17 + Width = 63 + Height = 13 + Caption = 'Port number:' + end + object StartButton: TButton + Left = 16 + Top = 41 + Width = 75 + Height = 25 + Action = StartServerAction + TabOrder = 0 + end + object StopButton: TButton + Left = 104 + Top = 41 + Width = 75 + Height = 25 + Action = StopServerAction + TabOrder = 1 + end + object PortNumberEdit: TEdit + Left = 97 + Top = 14 + Width = 82 + Height = 21 + TabOrder = 2 + OnChange = PortNumberEditChange + end + end + object MainTreeView: TTreeView + Left = 0 + Top = 73 + Width = 464 + Height = 129 + Align = alClient + Indent = 19 + TabOrder = 1 + end + object MainActionList: TActionList + Left = 384 + Top = 24 + object StartServerAction: TAction + Caption = 'Start Server' + OnExecute = StartServerActionExecute + OnUpdate = StartServerActionUpdate + end + object StopServerAction: TAction + Caption = 'Stop Server' + OnExecute = StopServerActionExecute + OnUpdate = StopServerActionUpdate + end + end +end diff --git a/Demos/RemoteMic/Server.Forms.Main.pas b/Demos/RemoteMic/Server.Forms.Main.pas new file mode 100644 index 00000000..02062e70 --- /dev/null +++ b/Demos/RemoteMic/Server.Forms.Main.pas @@ -0,0 +1,148 @@ +(* + Copyright 2016, MARS-Curiosity - REST Library + + Home: https://github.com/andrea-magni/MARS +*) +unit Server.Forms.Main; + +{$I MARS.inc} + +interface + +uses Classes, SysUtils, Forms, ActnList, ComCtrls, StdCtrls, Controls, ExtCtrls + , System.Actions + , MARS.http.Server.Indy +; + +type + TMainForm = class(TForm) + MainActionList: TActionList; + StartServerAction: TAction; + StopServerAction: TAction; + TopPanel: TPanel; + Label1: TLabel; + StartButton: TButton; + StopButton: TButton; + PortNumberEdit: TEdit; + MainTreeView: TTreeView; + procedure StartServerActionExecute(Sender: TObject); + procedure StartServerActionUpdate(Sender: TObject); + procedure StopServerActionExecute(Sender: TObject); + procedure StopServerActionUpdate(Sender: TObject); + procedure FormCreate(Sender: TObject); + procedure PortNumberEditChange(Sender: TObject); + procedure FormClose(Sender: TObject; var Action: TCloseAction); + private + FServer: TMARShttpServerIndy; + protected + procedure RenderEngines(const ATreeView: TTreeView); + public + end; + +var + MainForm: TMainForm; + +implementation + +{$R *.dfm} + +uses + StrUtils, Web.HttpApp, Windows + , MARS.Core.URL, MARS.Core.Engine, MARS.Core.Application, MARS.Core.Registry + , MARS.Core.Registry.Utils + , Server.Ignition +; + +procedure TMainForm.RenderEngines(const ATreeView: TTreeView); +begin + + ATreeview.Items.BeginUpdate; + try + ATreeview.Items.Clear; + TMARSEngineRegistry.Instance.EnumerateEngines( + procedure (AName: string; AEngine: TMARSEngine) + var + LEngineItem: TTreeNode; + begin + LEngineItem := ATreeview.Items.AddChild(nil + , AName + ' [ :' + AEngine.Port.ToString + AEngine.BasePath + ']' + ); + + AEngine.EnumerateApplications( + procedure (AName: string; AApplication: TMARSApplication) + var + LApplicationItem: TTreeNode; + begin + LApplicationItem := ATreeview.Items.AddChild(LEngineItem + , AApplication.Name + ' [' + AApplication.BasePath + ']' + ); + + AApplication.EnumerateResources( + procedure (AName: string; AInfo: TMARSConstructorInfo) + begin + ATreeview.Items.AddChild( + LApplicationItem + , AInfo.TypeTClass.ClassName + ' [' + AName + ']' + ); + + end + ); + end + ); + end + ); + + if ATreeView.Items.Count > 0 then + ATreeView.Items[0].Expand(True); + finally + ATreeView.Items.EndUpdate; + end; +end; + +procedure TMainForm.FormClose(Sender: TObject; var Action: TCloseAction); +begin + StopServerAction.Execute; +end; + +procedure TMainForm.FormCreate(Sender: TObject); +begin + PortNumberEdit.Text := IntToStr(TServerEngine.Default.Port); + RenderEngines(MainTreeView); + StartServerAction.Execute; +end; + +procedure TMainForm.PortNumberEditChange(Sender: TObject); +begin + TServerEngine.Default.Port := StrToInt(PortNumberEdit.Text); +end; + +procedure TMainForm.StartServerActionExecute(Sender: TObject); +begin + // http server implementation + FServer := TMARShttpServerIndy.Create(TServerEngine.Default); + try + FServer.DefaultPort := TServerEngine.Default.Port; + FServer.Active := True; + except + FServer.Free; + raise; + end; +end; + +procedure TMainForm.StartServerActionUpdate(Sender: TObject); +begin + StartServerAction.Enabled := (FServer = nil) or (FServer.Active = False); +end; + +procedure TMainForm.StopServerActionExecute(Sender: TObject); +begin + FServer.Active := False; + FreeAndNil(FServer); +end; + +procedure TMainForm.StopServerActionUpdate(Sender: TObject); +begin + StopServerAction.Enabled := Assigned(FServer) and (FServer.Active = True); +end; + +end. diff --git a/Demos/RemoteMic/Server.Ignition.pas b/Demos/RemoteMic/Server.Ignition.pas new file mode 100644 index 00000000..231c33cf --- /dev/null +++ b/Demos/RemoteMic/Server.Ignition.pas @@ -0,0 +1,148 @@ +(* + Copyright 2016, MARS-Curiosity - REST Library + + Home: https://github.com/andrea-magni/MARS +*) +unit Server.Ignition; + +{$I MARS.inc} + +interface + +uses + System.Classes, + System.SysUtils, + System.RTTI, + System.ZLib, + System.StrUtils, + MARS.Core.Engine; + +type + TServerEngine=class + private + class var FEngine: TMARSEngine; +{$IFDEF MARS_FIREDAC} + class var FAvailableConnectionDefs: TArray; +{$ENDIF} + public + class constructor CreateEngine; + class destructor DestroyEngine; + class property Default: TMARSEngine read FEngine; + end; + + +implementation + +uses + MARS.Core.Activation, MARS.Core.Activation.Interfaces + , MARS.Core.Application, MARS.Core.Utils, MARS.Utils.Parameters.IniFile + , MARS.Core.MessageBodyWriter, MARS.Core.MessageBodyWriters + , MARS.Core.MessageBodyReaders, MARS.Data.MessageBodyWriters + {$IFDEF MARS_FIREDAC} , MARS.Data.FireDAC {$ENDIF} + {$IFDEF MSWINDOWS} , MARS.mORMotJWT.Token {$ELSE} , MARS.JOSEJWT.Token {$ENDIF} + , Server.Resources + ; + +{ TServerEngine } + +class constructor TServerEngine.CreateEngine; +begin + FEngine := TMARSEngine.Create; + try + // Engine configuration + FEngine.Parameters.LoadFromIniFile; + + // Application configuration + FEngine.AddApplication('DefaultApp', '/default', [ 'Server.Resources.*']); +{$IFDEF MARS_FIREDAC} + FAvailableConnectionDefs := TMARSFireDAC.LoadConnectionDefs(FEngine.Parameters, 'FireDAC'); +{$ENDIF} +{$REGION 'BeforeHandleRequest example'} +(* + FEngine.BeforeHandleRequest := + function (const AEngine: TMARSEngine; + const AURL: TMARSURL; const ARequest: TWebRequest; const AResponse: TWebResponse; + var Handled: Boolean + ): Boolean + begin + Result := True; +{ + // skip favicon requests (browser) + if SameText(AURL.Document, 'favicon.ico') then + begin + Result := False; + Handled := True; + end; +} +{ + // Handle CORS and PreFlight + if SameText(ARequest.Method, 'OPTIONS') then + begin + Handled := True; + Result := False; + end; +} + end; +*) +{$ENDREGION} +{$REGION 'Global BeforeInvoke handler example'} +(* + // to execute something before each activation + TMARSActivation.RegisterBeforeInvoke( + procedure (const AActivation: IMARSActivation; out AIsAllowed: Boolean) + begin + + end + ); +*) +{$ENDREGION} +{$REGION 'Global AfterInvoke handler example'} + // Compression + if FEngine.Parameters.ByName('Compression.Enabled').AsBoolean then + TMARSActivation.RegisterAfterInvoke( + procedure (const AActivation: IMARSActivation) + var + LOutputStream: TBytesStream; + begin + if ContainsText(AActivation.Request.GetHeaderParamValue('Accept-Encoding'), 'gzip') then + begin + LOutputStream := TBytesStream.Create(nil); + try + ZipStream(AActivation.Response.ContentStream, LOutputStream, 15 + 16); + AActivation.Response.ContentStream.Free; + AActivation.Response.ContentStream := LOutputStream; + AActivation.Response.ContentEncoding := 'gzip'; + except + LOutputStream.Free; + raise; + end; + end; + end + ); +{$ENDREGION} +{$REGION 'Global InvokeError handler example'} +(* + // to execute something on error + TMARSActivation.RegisterInvokeError( + procedure (const AActivation: IMARSActivation; const AException: Exception; var AHandled: Boolean) + begin + + end + ); +*) +{$ENDREGION} + except + FreeAndNil(FEngine); + raise; + end; +end; + +class destructor TServerEngine.DestroyEngine; +begin +{$IFDEF MARS_FIREDAC} + TMARSFireDAC.CloseConnectionDefs(FAvailableConnectionDefs); +{$ENDIF} + FreeAndNil(FEngine); +end; + +end. diff --git a/Demos/RemoteMic/Server.Resources.pas b/Demos/RemoteMic/Server.Resources.pas new file mode 100644 index 00000000..56a573f6 --- /dev/null +++ b/Demos/RemoteMic/Server.Resources.pas @@ -0,0 +1,119 @@ +(* + Copyright 2016, MARS-Curiosity - REST Library + + Home: https://github.com/andrea-magni/MARS +*) +unit Server.Resources; + +interface + +uses + SysUtils, Classes + + , MARS.Core.Attributes, MARS.Core.MediaType, MARS.Core.JSON, MARS.Core.Response + , MARS.Core.URL + + , MARS.Core.Token.Resource //, MARS.Core.Token +; + +type + [Path('command')] + TCommandResource = class + private + type TApplication = (Skype, Teams, Zoom, Other, Unknown); + function GetCurrentApplication: TApplication; + protected + public + [GET, Path('/{param1}/{param2}'), Produces(TMediaType.TEXT_PLAIN)] + function Execute([PathParam] param1: Integer; [PathParam] param2: Integer): string; + end; + + [Path('token')] + TTokenResource = class(TMARSTokenResource) + end; + +implementation + +uses + Windows +, MARS.Core.Registry +, CodeSiteLogging +; + +{ TCommandResource } + +function TCommandResource.GetCurrentApplication: TApplication; +var + LText: array[0..255] of Char; + LTitle: string; + LHWND: HWND; +begin + Result := Unknown; + + LHWND := GetForegroundWindow; + GetWindowText(LHWND, @LText[0], SizeOf(LText)); + LTitle := LText; + + if LTitle.Contains('Microsoft Teams') then + Result := Teams + else if LTitle.Contains('Skype') then + Result := Skype + else if LTitle.Contains('Zoom') then + Result := Zoom + else + Result := Other; +end; + + +function TCommandResource.Execute(param1: Integer; param2: Integer): string; +begin + if (param1 + param2 < 2) then + begin + case GetCurrentApplication of + Teams: begin + Result := 'Teams: toggle mic'; + SetForegroundWindow(FindWindow(nil, PChar('Microsoft Teams'))); + + keybd_event(VK_LCONTROL, $9D,0 , 0); // Press Control + keybd_event(VK_LSHIFT, $AA,0 , 0); // Press Shift + keybd_event(Ord('M'), Ord('M'), 0 , 0); // Press M + + keybd_event(Ord('M'), Ord('M'), KEYEVENTF_KEYUP , 0); // Release M + keybd_event(VK_LSHIFT, $AA, KEYEVENTF_KEYUP, 0); // Release Shift + keybd_event(VK_LCONTROL, $9D, KEYEVENTF_KEYUP, 0); // Release Control + end; + Zoom: begin + Result := 'Zoom: toggle mic'; + SetForegroundWindow(FindWindow(nil, PChar('Zoom'))); + + keybd_event(VK_LCONTROL, $9D,0 , 0); // Press Control + keybd_event(VK_LSHIFT, $AA,0 , 0); // Press Shift + keybd_event(Ord('A'), Ord('A'), 0 , 0); // Press A + + keybd_event(Ord('A'), Ord('A'), KEYEVENTF_KEYUP , 0); // Release A + keybd_event(VK_LSHIFT, $AA, KEYEVENTF_KEYUP, 0); // Release Shift + keybd_event(VK_LCONTROL, $9D, KEYEVENTF_KEYUP, 0); // Release Control + end; + Skype: begin + Result := 'Skype: toggle mic'; + SetForegroundWindow(FindWindow(nil, PChar('Skype'))); + + keybd_event(VK_LCONTROL, $9D,0 , 0); // Press Control + keybd_event(Ord('M'), Ord('M'), 0 , 0); // Press M + + keybd_event(Ord('M'), Ord('M'), KEYEVENTF_KEYUP , 0); // Release M + keybd_event(VK_LSHIFT, $AA, KEYEVENTF_KEYUP, 0); // Release Shift + keybd_event(VK_LCONTROL, $9D, KEYEVENTF_KEYUP, 0); // Release Control + end; + else + Result := 'Other app'; + end; + + CodeSite.SendMsg(Result); + end; +end; + +initialization + TMARSResourceRegistry.Instance.RegisterResource; + TMARSResourceRegistry.Instance.RegisterResource; +end. diff --git a/Demos/RemoteMic/Server.Service.dfm b/Demos/RemoteMic/Server.Service.dfm new file mode 100644 index 00000000..52cc889a --- /dev/null +++ b/Demos/RemoteMic/Server.Service.dfm @@ -0,0 +1,10 @@ +object ServerService: TServerService + OldCreateOrder = False + OnCreate = ServiceCreate + OnDestroy = ServiceDestroy + DisplayName = 'RemoteMic Service' + OnStart = ServiceStart + OnStop = ServiceStop + Height = 150 + Width = 215 +end diff --git a/Demos/RemoteMic/Server.Service.pas b/Demos/RemoteMic/Server.Service.pas new file mode 100644 index 00000000..5f65d9ca --- /dev/null +++ b/Demos/RemoteMic/Server.Service.pas @@ -0,0 +1,126 @@ +(* + Copyright 2016, MARS-Curiosity - REST Library + + Home: https://github.com/andrea-magni/MARS +*) +unit Server.Service; + +{$I MARS.inc} + +interface + +uses +{$ifdef DelphiXE3_UP} + Winapi.Windows, Winapi.Messages, System.SysUtils, System.Classes, Vcl.Graphics +, Vcl.Controls, Vcl.SvcMgr, Vcl.Dialogs +//, IPPeerServer, IPPeerAPI +, IdHTTPWebBrokerBridge, Web.WebReq, Web.WebBroker +{$else} + Windows, Messages, SysUtils, Classes, Graphics +, Controls, SvcMgr, Dialogs +//, IPPeerServer, IPPeerAPI +, IdHTTPWebBrokerBridge, WebReq, WebBroker +{$endif} +, IdContext +; + +type + TServerService = class(TService) + procedure ServiceCreate(Sender: TObject); + procedure ServiceDestroy(Sender: TObject); + procedure ServiceStart(Sender: TService; var Started: Boolean); + procedure ServiceStop(Sender: TService; var Stopped: Boolean); + private + FServer: TIdHTTPWebBrokerBridge; + + procedure ParseAuthenticationHandler(AContext: TIdContext; + const AAuthType, AAuthData: String; var VUsername, VPassword: String; + var VHandled: Boolean); virtual; + + public + function GetServiceController: TServiceController; override; + + const DEFAULT_PORT = 8080; + end; + +var + ServerService: TServerService; + +implementation + +{$R *.dfm} + +uses + IdSchedulerOfThreadPool +, Server.Ignition +, Server.WebModule +; + +procedure ServiceController(CtrlCode: DWord); stdcall; +begin + ServerService.Controller(CtrlCode); +end; + +function TServerService.GetServiceController: TServiceController; +begin + Result := ServiceController; +end; + +procedure TServerService.ParseAuthenticationHandler(AContext: TIdContext; + const AAuthType, AAuthData: String; var VUsername, VPassword: String; + var VHandled: Boolean); +begin + // Allow JWT Bearer authentication's scheme + if SameText(AAuthType, 'Bearer') then + VHandled := True; +end; + +procedure TServerService.ServiceCreate(Sender: TObject); +var + LScheduler: TIdSchedulerOfThreadPool; +begin + Name := TServerEngine.Default.Parameters.ByNameText('ServiceName', Name).AsString; + DisplayName := TServerEngine.Default.Parameters.ByNameText('ServiceDisplayName', DisplayName).AsString; + + if WebRequestHandler <> nil then + WebRequestHandler.WebModuleClass := WebModuleClass; + + FServer := TIdHTTPWebBrokerBridge.Create(nil); + try + FServer.DefaultPort := TServerEngine.Default.Port; + + LScheduler := TIdSchedulerOfThreadPool.Create(FServer); + try + LScheduler.PoolSize := TServerEngine.Default.ThreadPoolSize; + FServer.Scheduler := LScheduler; + FServer.MaxConnections := LScheduler.PoolSize; + FServer.OnParseAuthentication := ParseAuthenticationHandler; + except + FServer.Scheduler.Free; + FServer.Scheduler := nil; + raise; + end; + except + FServer.Free; + raise; + end; +end; + +procedure TServerService.ServiceDestroy(Sender: TObject); +begin + FreeAndNil(FServer); +end; + +procedure TServerService.ServiceStart(Sender: TService; var Started: Boolean); +begin + FServer.Active := True; + Started := FServer.Active; +end; + +procedure TServerService.ServiceStop(Sender: TService; var Stopped: Boolean); +begin + FServer.Active := False; + Stopped := not FServer.Active; +end; + +end. diff --git a/Demos/RemoteMic/Server.WebModule.dfm b/Demos/RemoteMic/Server.WebModule.dfm new file mode 100644 index 00000000..cfa79a10 --- /dev/null +++ b/Demos/RemoteMic/Server.WebModule.dfm @@ -0,0 +1,12 @@ +object ServerWebModule: TServerWebModule + OldCreateOrder = False + Actions = < + item + Default = True + Name = 'DefaultHandler' + PathInfo = '/' + OnAction = ServerWebModuleDefaultHandlerAction + end> + Height = 230 + Width = 415 +end diff --git a/Demos/RemoteMic/Server.WebModule.pas b/Demos/RemoteMic/Server.WebModule.pas new file mode 100644 index 00000000..2ad473c8 --- /dev/null +++ b/Demos/RemoteMic/Server.WebModule.pas @@ -0,0 +1,57 @@ +(* + Copyright 2016, MARS-Curiosity - REST Library + + Home: https://github.com/andrea-magni/MARS +*) +unit Server.WebModule; + +{$I MARS.inc} + +interface + +uses System.SysUtils, System.Classes, Web.HTTPApp; + +type + TServerWebModule = class(TWebModule) + procedure ServerWebModuleDefaultHandlerAction(Sender: TObject; + Request: TWebRequest; Response: TWebResponse; var Handled: Boolean); + private + { Private declarations } + public + { Public declarations } + end; + +var + WebModuleClass: TComponentClass = TServerWebModule; + +implementation + +{%CLASSGROUP 'System.Classes.TPersistent'} + +{$R *.dfm} + +uses + MARS.http.Server.Indy +, Server.Ignition; + +procedure TServerWebModule.ServerWebModuleDefaultHandlerAction(Sender: TObject; + Request: TWebRequest; Response: TWebResponse; var Handled: Boolean); +begin + inherited; + + if not TServerEngine.Default.HandleRequest(TMARSWebRequest.Create(Request), TMARSWebResponse.Create(Response)) then + begin + Response.ContentType := 'application/json'; + Response.Content := + '{"success": false, "details": ' + + '{' + + '"error": "Request not found",' + + '"pathinfo": "' + Request.PathInfo + '"' + + '}' + + '}'; + end + else + Handled := True; +end; + +end. diff --git a/Demos/RemoteMic/ServerConst.pas b/Demos/RemoteMic/ServerConst.pas new file mode 100644 index 00000000..2ddaea07 --- /dev/null +++ b/Demos/RemoteMic/ServerConst.pas @@ -0,0 +1,42 @@ +(* + Copyright 2016, MARS-Curiosity - REST Library + + Home: https://github.com/andrea-magni/MARS +*) +unit ServerConst; + +interface + +resourcestring + sPortInUse = '- Error: Port %s already in use'; + sPortSet = '- Port set to %s'; + sServerRunning = '- The Server is already running'; + sStartingServer = '- Starting HTTP Server on port %d'; + sStoppingServer = '- Stopping Server'; + sServerStopped = '- Server Stopped'; + sServerNotRunning = '- The Server is not running'; + sInvalidCommand = '- Error: Invalid Command'; + sIndyVersion = '- Indy Version: '; + sActive = '- Active: '; + sPort = '- Port: '; + sSessionID = '- Session ID CookieName: '; + sCommands = 'Enter a Command: ' + slineBreak + + ' - "start" to start the server'+ slineBreak + + ' - "stop" to stop the server'+ slineBreak + + ' - "set port" to change the default port'+ slineBreak + + ' - "status" for Server status'+ slineBreak + + ' - "help" to show commands'+ slineBreak + + ' - "exit" to close the application'; + +const + cArrow = '->'; + cCommandStart = 'start'; + cCommandStop = 'stop'; + cCommandStatus = 'status'; + cCommandHelp = 'help'; + cCommandSetPort = 'set port'; + cCommandExit = 'exit'; + +implementation + +end. diff --git a/Demos/RemoteMic/bin/RemoteMicServerApplication.ini b/Demos/RemoteMic/bin/RemoteMicServerApplication.ini new file mode 100644 index 00000000..861a0b58 --- /dev/null +++ b/Demos/RemoteMic/bin/RemoteMicServerApplication.ini @@ -0,0 +1,4 @@ +[DefaultEngine] +BasePath= +ThreadPoolSize=100 +;Compression.Enabled=True diff --git a/Demos/UniDAC Basic/FMXClient.DataModules.Main.dfm b/Demos/UniDAC Basic/FMXClient.DataModules.Main.dfm new file mode 100644 index 00000000..650df259 --- /dev/null +++ b/Demos/UniDAC Basic/FMXClient.DataModules.Main.dfm @@ -0,0 +1,25 @@ +object MainDataModule: TMainDataModule + OldCreateOrder = False + Height = 411 + Width = 518 + object MARSApplication: TMARSClientApplication + DefaultMediaType = 'application/json' + DefaultContentType = 'application/json' + Client = MARSClient + Left = 88 + Top = 80 + end + object MARSClient: TMARSNetClient + MARSEngineURL = 'http://localhost:8080/rest' + ConnectTimeout = 60000 + ReadTimeout = 60000 + HttpClient.Asynchronous = False + HttpClient.ConnectionTimeout = 60000 + HttpClient.ResponseTimeout = 60000 + HttpClient.AllowCookies = True + HttpClient.HandleRedirects = True + HttpClient.UserAgent = 'Embarcadero URI Client/1.0' + Left = 88 + Top = 24 + end +end diff --git a/Demos/UniDAC Basic/FMXClient.DataModules.Main.pas b/Demos/UniDAC Basic/FMXClient.DataModules.Main.pas new file mode 100644 index 00000000..a4a66361 --- /dev/null +++ b/Demos/UniDAC Basic/FMXClient.DataModules.Main.pas @@ -0,0 +1,32 @@ +(* + Copyright 2016, MARS-Curiosity - REST Library + + Home: https://github.com/andrea-magni/MARS +*) +unit FMXClient.DataModules.Main; + +interface + +uses + System.SysUtils, System.Classes, MARS.Client.Application, + MARS.Client.Client, MARS.Client.Client.Net +; + +type + TMainDataModule = class(TDataModule) + MARSApplication: TMARSClientApplication; + MARSClient: TMARSNetClient; + private + public + end; + +var + MainDataModule: TMainDataModule; + +implementation + +{%CLASSGROUP 'FMX.Controls.TControl'} + +{$R *.dfm} + +end. diff --git a/Demos/UniDAC Basic/FMXClient.DataModules.Main.vlb b/Demos/UniDAC Basic/FMXClient.DataModules.Main.vlb new file mode 100644 index 00000000..95e7a6a2 --- /dev/null +++ b/Demos/UniDAC Basic/FMXClient.DataModules.Main.vlb @@ -0,0 +1,10 @@ +[MARSApplication] +Coordinates=150,53,96,33 + +[] +Coordinates=71,70,69,33 +Visible=False + +[MainForm.BindSourceDB1] +Coordinates=0,0,144,267 + diff --git a/Demos/UniDAC Basic/FMXClient.Forms.Main.fmx b/Demos/UniDAC Basic/FMXClient.Forms.Main.fmx new file mode 100644 index 00000000..9990bf0e --- /dev/null +++ b/Demos/UniDAC Basic/FMXClient.Forms.Main.fmx @@ -0,0 +1,27 @@ +object MainForm: TMainForm + Left = 0 + Top = 0 + Caption = 'MARS Template Client' + ClientHeight = 480 + ClientWidth = 640 + FormFactor.Width = 320 + FormFactor.Height = 480 + FormFactor.Devices = [Desktop] + DesignerMasterStyle = 0 + object TopToolBar: TToolBar + Size.Width = 640.000000000000000000 + Size.Height = 48.000000000000000000 + Size.PlatformDefault = False + TabOrder = 0 + object TitleLabel: TLabel + Align = Center + AutoSize = True + Size.Width = 121.000000000000000000 + Size.Height = 16.000000000000000000 + Size.PlatformDefault = False + StyleLookup = 'toollabel' + TextSettings.WordWrap = False + Text = 'MARS Template Client' + end + end +end diff --git a/Demos/UniDAC Basic/FMXClient.Forms.Main.pas b/Demos/UniDAC Basic/FMXClient.Forms.Main.pas new file mode 100644 index 00000000..1b939e4d --- /dev/null +++ b/Demos/UniDAC Basic/FMXClient.Forms.Main.pas @@ -0,0 +1,34 @@ +(* + Copyright 2016, MARS-Curiosity - REST Library + + Home: https://github.com/andrea-magni/MARS +*) +unit FMXClient.Forms.Main; + +interface + +uses + System.SysUtils, System.Types, System.UITypes, System.Classes, System.Variants, + FMX.Types, FMX.Controls, FMX.Forms, FMX.Graphics, FMX.Dialogs, FMX.StdCtrls, + FMX.Layouts, FMX.Controls.Presentation; + +type + TMainForm = class(TForm) + TopToolBar: TToolBar; + TitleLabel: TLabel; + private + public + end; + +var + MainForm: TMainForm; + +implementation + +{$R *.fmx} + +uses + FMXClient.DataModules.Main + ; + +end. diff --git a/Demos/UniDAC Basic/FMXClient.Forms.Main.vlb b/Demos/UniDAC Basic/FMXClient.Forms.Main.vlb new file mode 100644 index 00000000..b439f67e --- /dev/null +++ b/Demos/UniDAC Basic/FMXClient.Forms.Main.vlb @@ -0,0 +1,26 @@ +[TopToolBar] +Coordinates=317,78,82,36 + +[MainDataModule.ItemsQueryDataSet] +Coordinates=10,10,228,212 + +[] +Coordinates=145,78,71,36 +Visible=True + +[TitleLabel] +Coordinates=236,78,71,58 + +[MainDataModule.] +Coordinates=472,428,215,249 +Visible=False + +[MainDataModule.EmployeeQuery1] +Visible=False + +[MainDataModule.EmployeeQueryDataSet] +Coordinates=100,10,253,58 + +[MainDataModule.CountryByName1] +Coordinates=10,78,216,58 + diff --git a/Demos/UniDAC Basic/Server.FMX.Forms.Main.fmx b/Demos/UniDAC Basic/Server.FMX.Forms.Main.fmx new file mode 100644 index 00000000..fdadb145 --- /dev/null +++ b/Demos/UniDAC Basic/Server.FMX.Forms.Main.fmx @@ -0,0 +1,66 @@ +object MainForm: TMainForm + Left = 0 + Top = 0 + Caption = 'MARS-Curiosity Template Server FMX' + ClientHeight = 480 + ClientWidth = 640 + FormFactor.Width = 320 + FormFactor.Height = 480 + FormFactor.Devices = [Desktop] + OnCreate = FormCreate + OnClose = FormClose + DesignerMasterStyle = 0 + object Layout1: TLayout + Align = Top + Size.Width = 640.000000000000000000 + Size.Height = 121.000000000000000000 + Size.PlatformDefault = False + TabOrder = 1 + object PortNumberEdit: TEdit + Touch.InteractiveGestures = [LongTap, DoubleTap] + TabOrder = 0 + Text = '8080' + Position.X = 109.000000000000000000 + Position.Y = 22.000000000000000000 + OnChange = PortNumberEditChange + end + object Label1: TLabel + Position.X = 16.000000000000000000 + Position.Y = 24.000000000000000000 + Size.Width = 89.000000000000000000 + Size.Height = 17.000000000000000000 + Size.PlatformDefault = False + Text = 'Port number:' + end + object StartButton: TButton + Action = StartServerAction + Enabled = True + ImageIndex = -1 + Position.X = 16.000000000000000000 + Position.Y = 64.000000000000000000 + TabOrder = 2 + end + object StopButton: TButton + Action = StopServerAction + Enabled = True + ImageIndex = -1 + Position.X = 104.000000000000000000 + Position.Y = 64.000000000000000000 + TabOrder = 3 + end + end + object MainActionList: TActionList + Left = 384 + Top = 24 + object StartServerAction: TAction + Text = 'Start Server' + OnExecute = StartServerActionExecute + OnUpdate = StartServerActionUpdate + end + object StopServerAction: TAction + Text = 'Stop Server' + OnExecute = StopServerActionExecute + OnUpdate = StopServerActionUpdate + end + end +end diff --git a/Demos/UniDAC Basic/Server.FMX.Forms.Main.pas b/Demos/UniDAC Basic/Server.FMX.Forms.Main.pas new file mode 100644 index 00000000..36c1f348 --- /dev/null +++ b/Demos/UniDAC Basic/Server.FMX.Forms.Main.pas @@ -0,0 +1,97 @@ +(* + Copyright 2016, MARS-Curiosity - REST Library + + Home: https://github.com/andrea-magni/MARS +*) +unit Server.FMX.Forms.Main; + +interface + +uses + System.SysUtils, System.Types, System.UITypes, System.Classes, System.Variants, + FMX.Types, FMX.Controls, FMX.Forms, FMX.Graphics, FMX.Dialogs, FMX.StdCtrls, + FMX.Controls.Presentation, FMX.Edit, FMX.Layouts, System.Actions, FMX.ActnList + , MARS.http.Server.Indy +; + +type + TMainForm = class(TForm) + MainActionList: TActionList; + StartServerAction: TAction; + StopServerAction: TAction; + Layout1: TLayout; + PortNumberEdit: TEdit; + Label1: TLabel; + StartButton: TButton; + StopButton: TButton; + procedure FormClose(Sender: TObject; var Action: TCloseAction); + procedure FormCreate(Sender: TObject); + procedure StartServerActionExecute(Sender: TObject); + procedure StopServerActionExecute(Sender: TObject); + procedure StartServerActionUpdate(Sender: TObject); + procedure StopServerActionUpdate(Sender: TObject); + procedure PortNumberEditChange(Sender: TObject); + private + FServer: TMARShttpServerIndy; + public + end; + +var + MainForm: TMainForm; + +implementation + +{$R *.fmx} + +uses + Web.HttpApp + , MARS.Core.URL, MARS.Core.Engine + , Server.Ignition; + +procedure TMainForm.FormClose(Sender: TObject; var Action: TCloseAction); +begin + StopServerAction.Execute; +end; + +procedure TMainForm.FormCreate(Sender: TObject); +begin + PortNumberEdit.Text := TServerEngine.Default.Port.ToString; + + StartServerAction.Execute; +end; + +procedure TMainForm.PortNumberEditChange(Sender: TObject); +begin + TServerEngine.Default.Port := StrToInt(PortNumberEdit.Text); +end; + +procedure TMainForm.StartServerActionExecute(Sender: TObject); +begin + // http server implementation + FServer := TMARShttpServerIndy.Create(TServerEngine.Default); + try + FServer.DefaultPort := TServerEngine.Default.Port; + FServer.Active := True; + except + FServer.Free; + raise; + end; +end; + +procedure TMainForm.StartServerActionUpdate(Sender: TObject); +begin + StartServerAction.Enabled := (FServer = nil) or (FServer.Active = False); +end; + +procedure TMainForm.StopServerActionExecute(Sender: TObject); +begin + FServer.Active := False; + FreeAndNil(FServer); +end; + +procedure TMainForm.StopServerActionUpdate(Sender: TObject); +begin + StopServerAction.Enabled := Assigned(FServer) and (FServer.Active = True); +end; + +end. diff --git a/Demos/UniDAC Basic/Server.Forms.Main.dfm b/Demos/UniDAC Basic/Server.Forms.Main.dfm new file mode 100644 index 00000000..5220be6e --- /dev/null +++ b/Demos/UniDAC Basic/Server.Forms.Main.dfm @@ -0,0 +1,83 @@ +object MainForm: TMainForm + Left = 0 + Top = 0 + Caption = 'UniDACBasic Server' + ClientHeight = 201 + ClientWidth = 464 + Color = clBtnFace + Constraints.MinHeight = 240 + Constraints.MinWidth = 480 + Font.Charset = DEFAULT_CHARSET + Font.Color = clWindowText + Font.Height = -11 + Font.Name = 'Tahoma' + Font.Style = [] + OldCreateOrder = False + OnClose = FormClose + OnCreate = FormCreate + PixelsPerInch = 96 + TextHeight = 13 + object TopPanel: TPanel + Left = 0 + Top = 0 + Width = 464 + Height = 73 + Align = alTop + BevelOuter = bvNone + TabOrder = 0 + object Label1: TLabel + Left = 28 + Top = 17 + Width = 63 + Height = 13 + Caption = 'Port number:' + end + object StartButton: TButton + Left = 16 + Top = 41 + Width = 75 + Height = 25 + Action = StartServerAction + TabOrder = 0 + end + object StopButton: TButton + Left = 104 + Top = 41 + Width = 75 + Height = 25 + Action = StopServerAction + TabOrder = 1 + end + object PortNumberEdit: TEdit + Left = 97 + Top = 14 + Width = 82 + Height = 21 + TabOrder = 2 + OnChange = PortNumberEditChange + end + end + object MainTreeView: TTreeView + Left = 0 + Top = 73 + Width = 464 + Height = 128 + Align = alClient + Indent = 19 + TabOrder = 1 + end + object MainActionList: TActionList + Left = 384 + Top = 24 + object StartServerAction: TAction + Caption = 'Start Server' + OnExecute = StartServerActionExecute + OnUpdate = StartServerActionUpdate + end + object StopServerAction: TAction + Caption = 'Stop Server' + OnExecute = StopServerActionExecute + OnUpdate = StopServerActionUpdate + end + end +end diff --git a/Demos/UniDAC Basic/Server.Forms.Main.pas b/Demos/UniDAC Basic/Server.Forms.Main.pas new file mode 100644 index 00000000..7bf3579a --- /dev/null +++ b/Demos/UniDAC Basic/Server.Forms.Main.pas @@ -0,0 +1,148 @@ +(* + Copyright 2016, MARS-Curiosity - REST Library + + Home: https://github.com/andrea-magni/MARS +*) +unit Server.Forms.Main; + +{$I MARS.inc} + +interface + +uses Classes, SysUtils, Forms, ActnList, ComCtrls, StdCtrls, Controls, ExtCtrls + , System.Actions + , MARS.http.Server.Indy +; + +type + TMainForm = class(TForm) + MainActionList: TActionList; + StartServerAction: TAction; + StopServerAction: TAction; + TopPanel: TPanel; + Label1: TLabel; + StartButton: TButton; + StopButton: TButton; + PortNumberEdit: TEdit; + MainTreeView: TTreeView; + procedure StartServerActionExecute(Sender: TObject); + procedure StartServerActionUpdate(Sender: TObject); + procedure StopServerActionExecute(Sender: TObject); + procedure StopServerActionUpdate(Sender: TObject); + procedure FormCreate(Sender: TObject); + procedure PortNumberEditChange(Sender: TObject); + procedure FormClose(Sender: TObject; var Action: TCloseAction); + private + FServer: TMARShttpServerIndy; + protected + procedure RenderEngines(const ATreeView: TTreeView); + public + end; + +var + MainForm: TMainForm; + +implementation + +{$R *.dfm} + +uses + StrUtils, Web.HttpApp + , MARS.Core.URL, MARS.Core.Engine, MARS.Core.Application, MARS.Core.Registry + , MARS.Core.Registry.Utils + , Server.Ignition +; + +procedure TMainForm.RenderEngines(const ATreeView: TTreeView); +begin + + ATreeview.Items.BeginUpdate; + try + ATreeview.Items.Clear; + TMARSEngineRegistry.Instance.EnumerateEngines( + procedure (AName: string; AEngine: TMARSEngine) + var + LEngineItem: TTreeNode; + begin + LEngineItem := ATreeview.Items.AddChild(nil + , AName + ' [ :' + AEngine.Port.ToString + AEngine.BasePath + ']' + ); + + AEngine.EnumerateApplications( + procedure (AName: string; AApplication: TMARSApplication) + var + LApplicationItem: TTreeNode; + begin + LApplicationItem := ATreeview.Items.AddChild(LEngineItem + , AApplication.Name + ' [' + AApplication.BasePath + ']' + ); + + AApplication.EnumerateResources( + procedure (AName: string; AInfo: TMARSConstructorInfo) + begin + ATreeview.Items.AddChild( + LApplicationItem + , AInfo.TypeTClass.ClassName + ' [' + AName + ']' + ); + + end + ); + end + ); + end + ); + + if ATreeView.Items.Count > 0 then + ATreeView.Items[0].Expand(True); + finally + ATreeView.Items.EndUpdate; + end; +end; + +procedure TMainForm.FormClose(Sender: TObject; var Action: TCloseAction); +begin + StopServerAction.Execute; +end; + +procedure TMainForm.FormCreate(Sender: TObject); +begin + PortNumberEdit.Text := IntToStr(TServerEngine.Default.Port); + RenderEngines(MainTreeView); + StartServerAction.Execute; +end; + +procedure TMainForm.PortNumberEditChange(Sender: TObject); +begin + TServerEngine.Default.Port := StrToInt(PortNumberEdit.Text); +end; + +procedure TMainForm.StartServerActionExecute(Sender: TObject); +begin + // http server implementation + FServer := TMARShttpServerIndy.Create(TServerEngine.Default); + try + FServer.DefaultPort := TServerEngine.Default.Port; + FServer.Active := True; + except + FServer.Free; + raise; + end; +end; + +procedure TMainForm.StartServerActionUpdate(Sender: TObject); +begin + StartServerAction.Enabled := (FServer = nil) or (FServer.Active = False); +end; + +procedure TMainForm.StopServerActionExecute(Sender: TObject); +begin + FServer.Active := False; + FreeAndNil(FServer); +end; + +procedure TMainForm.StopServerActionUpdate(Sender: TObject); +begin + StopServerAction.Enabled := Assigned(FServer) and (FServer.Active = True); +end; + +end. diff --git a/Demos/UniDAC Basic/Server.Ignition.pas b/Demos/UniDAC Basic/Server.Ignition.pas new file mode 100644 index 00000000..9565ce8c --- /dev/null +++ b/Demos/UniDAC Basic/Server.Ignition.pas @@ -0,0 +1,149 @@ +(* + Copyright 2016, MARS-Curiosity - REST Library + + Home: https://github.com/andrea-magni/MARS +*) +unit Server.Ignition; + +{$I MARS.inc} + +interface + +uses + Classes, SysUtils, Rtti + , MARS.Core.Engine +; + +type + TServerEngine=class + private + class var FEngine: TMARSEngine; +{$IF Defined(MARS_FIREDAC) or Defined(MARS_UNIDAC)} + class var FAvailableConnectionDefs: TArray; +{$ENDIF} + public + class constructor CreateEngine; + class destructor DestroyEngine; + class property Default: TMARSEngine read FEngine; + end; + + +implementation + +uses + MARS.Core.Activation, MARS.Core.Activation.Interfaces + , MARS.Core.Application, MARS.Core.Utils, MARS.Utils.Parameters.IniFile + , MARS.Core.MessageBodyWriter, MARS.Core.MessageBodyWriters + , MARS.Core.MessageBodyReaders, MARS.Data.MessageBodyWriters + {$IFDEF MARS_FIREDAC} + , MARS.Data.FireDAC + , FireDAC.Phys.FB + {$ENDIF} + {$IFDEF MARS_UNIDAC} + , MARS.Data.UniDAC + , InterBaseUniProvider // Interbase & FirebirdSQL + , SQLiteUniProvider // SQLite + {$ENDIF} + {$IFDEF MSWINDOWS} , MARS.mORMotJWT.Token {$ELSE} , MARS.JOSEJWT.Token {$ENDIF} + , Server.Resources + + ; + +{ TServerEngine } + +class constructor TServerEngine.CreateEngine; +begin + FEngine := TMARSEngine.Create; + try + // Engine configuration + FEngine.Parameters.LoadFromIniFile; + + // Application configuration + FEngine.AddApplication('DefaultApp', '/default', [ 'Server.Resources.*']); +{$IFDEF MARS_FIREDAC} + FAvailableConnectionDefs := TMARSFireDAC.LoadConnectionDefs(FEngine.Parameters, 'FireDAC'); +{$ENDIF} +{$IFDEF MARS_UNIDAC} + FAvailableConnectionDefs := TMARSUniDAC.LoadConnectionDefs(FEngine.Parameters, 'UniDAC'); +{$ENDIF} + +{$REGION 'BeforeHandleRequest example'} +(* + FEngine.BeforeHandleRequest := + function (const AEngine: TMARSEngine; + const AURL: TMARSURL; const ARequest: TWebRequest; const AResponse: TWebResponse; + var Handled: Boolean + ): Boolean + begin + Result := True; +{ + // skip favicon requests (browser) + if SameText(AURL.Document, 'favicon.ico') then + begin + Result := False; + Handled := True; + end; +} +{ + // Handle CORS and PreFlight + if SameText(ARequest.Method, 'OPTIONS') then + begin + Handled := True; + Result := False; + end; +} + end; +*) +{$ENDREGION} +{$REGION 'Global BeforeInvoke handler example'} +(* + // to execute something before each activation + TMARSActivation.RegisterBeforeInvoke( + procedure (const AActivation: IMARSActivation; out AIsAllowed: Boolean) + begin + + end + ); +*) +{$ENDREGION} +{$REGION 'Global AfterInvoke handler example'} +(* + // to execute something after each activation + TMARSActivation.RegisterAfterInvoke( + procedure (const AActivation: IMARSActivation) + begin + + end + ); +*) +{$ENDREGION} +{$REGION 'Global InvokeError handler example'} +(* + // to execute something on error + TMARSActivation.RegisterInvokeError( + procedure (const AActivation: IMARSActivation; const AException: Exception; var AHandled: Boolean) + begin + + end + ); +*) +{$ENDREGION} + except + FreeAndNil(FEngine); + raise; + end; +end; + +class destructor TServerEngine.DestroyEngine; +begin +{$IFDEF MARS_FIREDAC} + TMARSFireDAC.CloseConnectionDefs(FAvailableConnectionDefs); +{$ENDIF} +{$IFDEF MARS_UNIDAC} + TMARSUniDAC.CloseConnectionDefs(FAvailableConnectionDefs); +{$ENDIF} + + FreeAndNil(FEngine); +end; + +end. diff --git a/Demos/UniDAC Basic/Server.Resources.pas b/Demos/UniDAC Basic/Server.Resources.pas new file mode 100644 index 00000000..b8ba835b --- /dev/null +++ b/Demos/UniDAC Basic/Server.Resources.pas @@ -0,0 +1,107 @@ +(* + Copyright 2016, MARS-Curiosity - REST Library + + Home: https://github.com/andrea-magni/MARS +*) +unit Server.Resources; + +interface + +uses + SysUtils, Classes, Data.DB + + , MARS.Core.Attributes, MARS.Core.MediaType, MARS.Core.JSON, MARS.Core.Response + , MARS.Core.URL + + , MARS.Core.Token.Resource //, MARS.Core.Token + + // UniDAC + , Uni, VirtualTable + , MARS.Data.UniDAC, MARS.Data.UniDAC.Utils + + , FireDAC.Comp.Client, MARS.Data.FireDAC +; + +type + [ Path('helloworld') + , Produces(TMediaType.APPLICATION_XML) + , Produces(TMediaType.APPLICATION_JSON) + , Produces(APPLICATION_JSON_UniDAC) + , Produces(TMediaType.APPLICATION_OCTET_STREAM) + ] + THelloWorldResource = class + protected + [Context] UD: TMARSUniDAC; + [Context] FD: TMARSFireDAC; + [Context, Connection('MY_SQLITE')] UDSQLite: TMARSUniDAC; + public + [GET, Path('query/{tablename}')] + function GetEmployee: TUniQuery; + [GET, Path('queryFD/{tablename}')] + function GetEmployeeFD: TFDQuery; + + [GET, Path('querySQLite/{tablename}')] + function GetTable1: TUniQuery; + [GET, Path('queries')] + function GetQueries: TArray; + + [GET, Path('virtualtable')] + function GetVirtualTable: TVirtualTable; + end; + + [Path('token')] + TTokenResource = class(TMARSTokenResource) + end; + +implementation + +uses + MARS.Core.Registry +; + +{ THelloWorldResource } + +function THelloWorldResource.GetEmployee: TUniQuery; +begin + Result := UD.Query('select * from &PathParam_tablename'); +end; + + +function THelloWorldResource.GetEmployeeFD: TFDQuery; +begin + Result := FD.Query('select * from &PathParam_tablename'); +end; + +function THelloWorldResource.GetQueries: TArray; +begin + Result := [ + UD.SetName(UD.Query('select * from EMPLOYEE'), 'Employees') + , UDSQLite.SetName( UDSQLite.Query('select * from MYTABLE1'), 'MyTable1') + ]; +end; + +function THelloWorldResource.GetTable1: TUniQuery; +begin + Result := UDSQLite.Query('select * from &PathParam_tablename'); +end; + +function THelloWorldResource.GetVirtualTable: TVirtualTable; +begin + Result := TVirtualTable.Create(nil); + try + Result.AddField('Lastname', ftString, 100); + Result.AddField('Firstname', ftString, 100); + Result.AddField('DateOfBirth', ftDate); + + Result.Active := True; + Result.AppendRecord(['Magni', 'Andrea', EncodeDate(1982, 05, 24)]); + Result.AppendRecord(['K', 'Ertan', EncodeDate(2000, 01, 01)]); + except + FreeAndNil(Result); + end; +end; + +initialization + TMARSResourceRegistry.Instance.RegisterResource; + TMARSResourceRegistry.Instance.RegisterResource; +end. diff --git a/Demos/UniDAC Basic/Server.Service.dfm b/Demos/UniDAC Basic/Server.Service.dfm new file mode 100644 index 00000000..012a74e0 --- /dev/null +++ b/Demos/UniDAC Basic/Server.Service.dfm @@ -0,0 +1,10 @@ +object ServerService: TServerService + OldCreateOrder = False + OnCreate = ServiceCreate + OnDestroy = ServiceDestroy + DisplayName = 'UniDACBasic Service' + OnStart = ServiceStart + OnStop = ServiceStop + Height = 150 + Width = 215 +end diff --git a/Demos/UniDAC Basic/Server.Service.pas b/Demos/UniDAC Basic/Server.Service.pas new file mode 100644 index 00000000..d47167bd --- /dev/null +++ b/Demos/UniDAC Basic/Server.Service.pas @@ -0,0 +1,123 @@ +(* + Copyright 2016, MARS-Curiosity - REST Library + + Home: https://github.com/andrea-magni/MARS +*) +unit Server.Service; + +{$I MARS.inc} + +interface + +uses +{$ifdef DelphiXE3_UP} + Winapi.Windows, Winapi.Messages, System.SysUtils, System.Classes, Vcl.Graphics +, Vcl.Controls, Vcl.SvcMgr, Vcl.Dialogs +//, IPPeerServer, IPPeerAPI +, IdHTTPWebBrokerBridge, Web.WebReq, Web.WebBroker +{$else} + Windows, Messages, SysUtils, Classes, Graphics +, Controls, SvcMgr, Dialogs +//, IPPeerServer, IPPeerAPI +, IdHTTPWebBrokerBridge, WebReq, WebBroker +{$endif} +, IdContext +; + +type + TServerService = class(TService) + procedure ServiceCreate(Sender: TObject); + procedure ServiceDestroy(Sender: TObject); + procedure ServiceStart(Sender: TService; var Started: Boolean); + procedure ServiceStop(Sender: TService; var Stopped: Boolean); + private + FServer: TIdHTTPWebBrokerBridge; + + procedure ParseAuthenticationHandler(AContext: TIdContext; + const AAuthType, AAuthData: String; var VUsername, VPassword: String; + var VHandled: Boolean); virtual; + + public + function GetServiceController: TServiceController; override; + + const DEFAULT_PORT = 8080; + end; + +var + ServerService: TServerService; + +implementation + +{$R *.dfm} + +uses + IdSchedulerOfThreadPool +, Server.Ignition +, Server.WebModule +; + +procedure ServiceController(CtrlCode: DWord); stdcall; +begin + ServerService.Controller(CtrlCode); +end; + +function TServerService.GetServiceController: TServiceController; +begin + Result := ServiceController; +end; + +procedure TServerService.ParseAuthenticationHandler(AContext: TIdContext; + const AAuthType, AAuthData: String; var VUsername, VPassword: String; + var VHandled: Boolean); +begin + // Allow JWT Bearer authentication's scheme + if SameText(AAuthType, 'Bearer') then + VHandled := True; +end; + +procedure TServerService.ServiceCreate(Sender: TObject); +var + LScheduler: TIdSchedulerOfThreadPool; +begin + if WebRequestHandler <> nil then + WebRequestHandler.WebModuleClass := WebModuleClass; + + FServer := TIdHTTPWebBrokerBridge.Create(nil); + try + FServer.DefaultPort := TServerEngine.Default.Port; + + LScheduler := TIdSchedulerOfThreadPool.Create(FServer); + try + LScheduler.PoolSize := TServerEngine.Default.ThreadPoolSize; + FServer.Scheduler := LScheduler; + FServer.MaxConnections := LScheduler.PoolSize; + FServer.OnParseAuthentication := ParseAuthenticationHandler; + except + FServer.Scheduler.Free; + FServer.Scheduler := nil; + raise; + end; + except + FServer.Free; + raise; + end; +end; + +procedure TServerService.ServiceDestroy(Sender: TObject); +begin + FreeAndNil(FServer); +end; + +procedure TServerService.ServiceStart(Sender: TService; var Started: Boolean); +begin + FServer.Active := True; + Started := FServer.Active; +end; + +procedure TServerService.ServiceStop(Sender: TService; var Stopped: Boolean); +begin + FServer.Active := False; + Stopped := not FServer.Active; +end; + +end. diff --git a/Demos/UniDAC Basic/Server.WebModule.dfm b/Demos/UniDAC Basic/Server.WebModule.dfm new file mode 100644 index 00000000..cfa79a10 --- /dev/null +++ b/Demos/UniDAC Basic/Server.WebModule.dfm @@ -0,0 +1,12 @@ +object ServerWebModule: TServerWebModule + OldCreateOrder = False + Actions = < + item + Default = True + Name = 'DefaultHandler' + PathInfo = '/' + OnAction = ServerWebModuleDefaultHandlerAction + end> + Height = 230 + Width = 415 +end diff --git a/Demos/UniDAC Basic/Server.WebModule.pas b/Demos/UniDAC Basic/Server.WebModule.pas new file mode 100644 index 00000000..2ad473c8 --- /dev/null +++ b/Demos/UniDAC Basic/Server.WebModule.pas @@ -0,0 +1,57 @@ +(* + Copyright 2016, MARS-Curiosity - REST Library + + Home: https://github.com/andrea-magni/MARS +*) +unit Server.WebModule; + +{$I MARS.inc} + +interface + +uses System.SysUtils, System.Classes, Web.HTTPApp; + +type + TServerWebModule = class(TWebModule) + procedure ServerWebModuleDefaultHandlerAction(Sender: TObject; + Request: TWebRequest; Response: TWebResponse; var Handled: Boolean); + private + { Private declarations } + public + { Public declarations } + end; + +var + WebModuleClass: TComponentClass = TServerWebModule; + +implementation + +{%CLASSGROUP 'System.Classes.TPersistent'} + +{$R *.dfm} + +uses + MARS.http.Server.Indy +, Server.Ignition; + +procedure TServerWebModule.ServerWebModuleDefaultHandlerAction(Sender: TObject; + Request: TWebRequest; Response: TWebResponse; var Handled: Boolean); +begin + inherited; + + if not TServerEngine.Default.HandleRequest(TMARSWebRequest.Create(Request), TMARSWebResponse.Create(Response)) then + begin + Response.ContentType := 'application/json'; + Response.Content := + '{"success": false, "details": ' + + '{' + + '"error": "Request not found",' + + '"pathinfo": "' + Request.PathInfo + '"' + + '}' + + '}'; + end + else + Handled := True; +end; + +end. diff --git a/Demos/UniDAC Basic/ServerConst.pas b/Demos/UniDAC Basic/ServerConst.pas new file mode 100644 index 00000000..2ddaea07 --- /dev/null +++ b/Demos/UniDAC Basic/ServerConst.pas @@ -0,0 +1,42 @@ +(* + Copyright 2016, MARS-Curiosity - REST Library + + Home: https://github.com/andrea-magni/MARS +*) +unit ServerConst; + +interface + +resourcestring + sPortInUse = '- Error: Port %s already in use'; + sPortSet = '- Port set to %s'; + sServerRunning = '- The Server is already running'; + sStartingServer = '- Starting HTTP Server on port %d'; + sStoppingServer = '- Stopping Server'; + sServerStopped = '- Server Stopped'; + sServerNotRunning = '- The Server is not running'; + sInvalidCommand = '- Error: Invalid Command'; + sIndyVersion = '- Indy Version: '; + sActive = '- Active: '; + sPort = '- Port: '; + sSessionID = '- Session ID CookieName: '; + sCommands = 'Enter a Command: ' + slineBreak + + ' - "start" to start the server'+ slineBreak + + ' - "stop" to stop the server'+ slineBreak + + ' - "set port" to change the default port'+ slineBreak + + ' - "status" for Server status'+ slineBreak + + ' - "help" to show commands'+ slineBreak + + ' - "exit" to close the application'; + +const + cArrow = '->'; + cCommandStart = 'start'; + cCommandStop = 'stop'; + cCommandStatus = 'status'; + cCommandHelp = 'help'; + cCommandSetPort = 'set port'; + cCommandExit = 'exit'; + +implementation + +end. diff --git a/Demos/UniDAC Basic/UniDACBasicClient.dpr b/Demos/UniDAC Basic/UniDACBasicClient.dpr new file mode 100644 index 00000000..ec85dd83 --- /dev/null +++ b/Demos/UniDAC Basic/UniDACBasicClient.dpr @@ -0,0 +1,21 @@ +(* + Copyright 2016, MARS-Curiosity library + + Home: https://github.com/andrea-magni/MARS +*) + program UniDACBasicClient; + +uses + System.StartUpCopy, + FMX.Forms, + FMXClient.Forms.Main in 'FMXClient.Forms.Main.pas' {MainForm}, + FMXClient.DataModules.Main in 'FMXClient.DataModules.Main.pas' {MainDataModule: TDataModule}; + +{$R *.res} + +begin + Application.Initialize; + Application.CreateForm(TMainDataModule, MainDataModule); + Application.CreateForm(TMainForm, MainForm); + Application.Run; +end. diff --git a/Demos/UniDAC Basic/UniDACBasicClient.dproj b/Demos/UniDAC Basic/UniDACBasicClient.dproj new file mode 100644 index 00000000..bf43c19d --- /dev/null +++ b/Demos/UniDAC Basic/UniDACBasicClient.dproj @@ -0,0 +1,1259 @@ + + + {B6B860BA-E3A5-48E9-8EC6-7FE516AE7FC4} + UniDACBasicClient.dpr + True + Debug + 1169 + Application + FMX + 18.7 + Win32 + + + true + + + true + Base + true + + + true + Base + true + + + true + Base + true + + + true + Base + true + + + true + Base + true + + + true + Base + true + + + true + Base + true + + + true + Base + true + + + true + Base + true + + + true + Base + true + + + true + Cfg_1 + true + true + + + true + Base + true + + + true + Cfg_2 + true + true + + + true + Cfg_2 + true + true + + + true + Cfg_2 + true + true + + + true + Cfg_2 + true + true + + + true + Cfg_2 + true + true + + + true + Cfg_2 + true + true + + + false + false + false + false + false + 00400000 + UniDACBasicClient + 1040 + CompanyName=;FileDescription=;FileVersion=1.0.0.0;InternalName=;LegalCopyright=;LegalTrademarks=;OriginalFilename=;ProductName=;ProductVersion=1.0.0.0;Comments=;CFBundleName= + System;Xml;Data;Datasnap;Web;Soap;$(DCC_Namespace) + $(BDS)\bin\delphi_PROJECTICON.ico + $(BDS)\bin\delphi_PROJECTICNS.icns + .\bin + .\lib\$(Platform)\$(Config) + + + package=com.embarcadero.$(MSBuildProjectName);label=$(MSBuildProjectName);versionCode=1;versionName=1.0.0;persistent=False;restoreAnyVersion=False;installLocation=auto;largeHeap=False;theme=TitleBar;hardwareAccelerated=true;apiKey= + Debug + true + $(BDS)\bin\Artwork\Android\FM_LauncherIcon_36x36.png + $(BDS)\bin\Artwork\Android\FM_LauncherIcon_48x48.png + $(BDS)\bin\Artwork\Android\FM_LauncherIcon_72x72.png + $(BDS)\bin\Artwork\Android\FM_LauncherIcon_96x96.png + $(BDS)\bin\Artwork\Android\FM_LauncherIcon_144x144.png + $(BDS)\bin\Artwork\Android\FM_SplashImage_426x320.png + $(BDS)\bin\Artwork\Android\FM_SplashImage_470x320.png + $(BDS)\bin\Artwork\Android\FM_SplashImage_640x480.png + $(BDS)\bin\Artwork\Android\FM_SplashImage_960x720.png + true + true + true + true + true + true + true + true + true + true + android-support-v4.dex.jar;cloud-messaging.dex.jar;fmx.dex.jar;google-analytics-v2.dex.jar;google-play-billing.dex.jar;google-play-licensing.dex.jar;google-play-services.dex.jar + 1 + $(BDS)\bin\Artwork\Android\FM_NotificationIcon_24x24.png + $(BDS)\bin\Artwork\Android\FM_NotificationIcon_36x36.png + $(BDS)\bin\Artwork\Android\FM_NotificationIcon_48x48.png + $(BDS)\bin\Artwork\Android\FM_NotificationIcon_72x72.png + $(BDS)\bin\Artwork\Android\FM_NotificationIcon_96x96.png + + + CFBundleName=$(MSBuildProjectName);CFBundleDevelopmentRegion=en;CFBundleDisplayName=$(MSBuildProjectName);CFBundleIdentifier=$(MSBuildProjectName);CFBundleInfoDictionaryVersion=7.1;CFBundleVersion=1.0.0.0;CFBundlePackageType=APPL;CFBundleSignature=????;LSRequiresIPhoneOS=true;CFBundleAllowMixedLocalizations=YES;CFBundleExecutable=$(MSBuildProjectName);UIDeviceFamily=iPhone & iPad;CFBundleResourceSpecification=ResourceRules.plist;NSLocationAlwaysUsageDescription=The reason for accessing the location information of the user;NSLocationWhenInUseUsageDescription=The reason for accessing the location information of the user;FMLocalNotificationPermission=false;UIBackgroundModes=;NSContactsUsageDescription=The reason for accessing the contacts;NSPhotoLibraryUsageDescription=The reason for accessing the photo library;NSCameraUsageDescription=The reason for accessing the camera + iPhoneAndiPad + true + Debug + $(MSBuildProjectName) + $(BDS)\bin\Artwork\iOS\iPhone\FM_ApplicationIcon_60x60.png + $(BDS)\bin\Artwork\iOS\iPhone\FM_ApplicationIcon_120x120.png + $(BDS)\bin\Artwork\iOS\iPhone\FM_SpotlightSearchIcon_40x40.png + $(BDS)\bin\Artwork\iOS\iPhone\FM_SpotlightSearchIcon_80x80.png + $(BDS)\bin\Artwork\iOS\iPad\FM_SpotlightSearchIcon_40x40.png + $(BDS)\bin\Artwork\iOS\iPad\FM_SpotlightSearchIcon_80x80.png + $(BDS)\bin\Artwork\iOS\iPad\FM_ApplicationIcon_76x76.png + $(BDS)\bin\Artwork\iOS\iPad\FM_ApplicationIcon_152x152.png + $(BDS)\bin\Artwork\iOS\iPad\FM_LaunchImagePortrait_768x1024.png + $(BDS)\bin\Artwork\iOS\iPad\FM_LaunchImageLandscape_1024x768.png + $(BDS)\bin\Artwork\iOS\iPad\FM_LaunchImagePortrait_1536x2048.png + $(BDS)\bin\Artwork\iOS\iPad\FM_LaunchImageLandscape_2048x1536.png + $(BDS)\bin\Artwork\iOS\iPhone\FM_ApplicationIcon_87x87.png + $(BDS)\bin\Artwork\iOS\iPhone\FM_ApplicationIcon_180x180.png + $(BDS)\bin\Artwork\iOS\iPhone\FM_LaunchImage_750x1334.png + $(BDS)\bin\Artwork\iOS\iPhone\FM_LaunchImage_1242x2208.png + $(BDS)\bin\Artwork\iOS\iPhone\FM_LaunchImage_2208x1242.png + $(BDS)\bin\Artwork\iOS\iPhone\FM_LaunchImage_1125x2436.png + $(BDS)\bin\Artwork\iOS\iPhone\FM_LaunchImage_2436x1125.png + $(BDS)\bin\Artwork\iOS\iPhone\FM_SpotlightSearchIcon_120x120.png + $(BDS)\bin\Artwork\iOS\iPhone\FM_LaunchImage_828x1792.png + $(BDS)\bin\Artwork\iOS\iPhone\FM_LaunchImage_1136x640.png + $(BDS)\bin\Artwork\iOS\iPhone\FM_LaunchImage_1242x2688.png + $(BDS)\bin\Artwork\iOS\iPhone\FM_LaunchImage_1334x750.png + $(BDS)\bin\Artwork\iOS\iPhone\FM_LaunchImage_1792x828.png + $(BDS)\bin\Artwork\iOS\iPhone\FM_LaunchImage_2688x1242.png + $(BDS)\bin\Artwork\iOS\iPad\FM_ApplicationIcon_167x167.png + $(BDS)\bin\Artwork\iOS\iPad\FM_LaunchImagePortrait_1668x2224.png + $(BDS)\bin\Artwork\iOS\iPad\FM_LaunchImagePortrait_1668x2388.png + $(BDS)\bin\Artwork\iOS\iPad\FM_LaunchImagePortrait_2048x2732.png + $(BDS)\bin\Artwork\iOS\iPad\FM_LaunchImageLandscape_2224x1668.png + $(BDS)\bin\Artwork\iOS\iPad\FM_LaunchImageLandscape_2388x1668.png + $(BDS)\bin\Artwork\iOS\iPad\FM_LaunchImageLandscape_2732x2048.png + + + $(BDS)\bin\Artwork\iOS\iPhone\FM_ApplicationIcon_87x87.png + $(BDS)\bin\Artwork\iOS\iPhone\FM_ApplicationIcon_180x180.png + $(BDS)\bin\Artwork\iOS\iPhone\FM_LaunchImage_750x1334.png + $(BDS)\bin\Artwork\iOS\iPhone\FM_LaunchImage_1242x2208.png + $(BDS)\bin\Artwork\iOS\iPhone\FM_LaunchImage_2208x1242.png + $(BDS)\bin\Artwork\iOS\iPhone\FM_LaunchImage_1125x2436.png + $(BDS)\bin\Artwork\iOS\iPhone\FM_LaunchImage_2436x1125.png + $(BDS)\bin\Artwork\iOS\iPhone\FM_SpotlightSearchIcon_120x120.png + $(BDS)\bin\Artwork\iOS\iPhone\FM_LaunchImage_828x1792.png + $(BDS)\bin\Artwork\iOS\iPhone\FM_LaunchImage_1136x640.png + $(BDS)\bin\Artwork\iOS\iPhone\FM_LaunchImage_1242x2688.png + $(BDS)\bin\Artwork\iOS\iPhone\FM_LaunchImage_1334x750.png + $(BDS)\bin\Artwork\iOS\iPhone\FM_LaunchImage_1792x828.png + $(BDS)\bin\Artwork\iOS\iPhone\FM_LaunchImage_2688x1242.png + $(BDS)\bin\Artwork\iOS\iPad\FM_ApplicationIcon_167x167.png + $(BDS)\bin\Artwork\iOS\iPad\FM_LaunchImagePortrait_1668x2224.png + $(BDS)\bin\Artwork\iOS\iPad\FM_LaunchImagePortrait_1668x2388.png + $(BDS)\bin\Artwork\iOS\iPad\FM_LaunchImagePortrait_2048x2732.png + $(BDS)\bin\Artwork\iOS\iPad\FM_LaunchImageLandscape_2224x1668.png + $(BDS)\bin\Artwork\iOS\iPad\FM_LaunchImageLandscape_2388x1668.png + $(BDS)\bin\Artwork\iOS\iPad\FM_LaunchImageLandscape_2732x2048.png + + + CFBundleName=$(MSBuildProjectName);CFBundleDevelopmentRegion=en;CFBundleDisplayName=$(MSBuildProjectName);CFBundleIdentifier=$(MSBuildProjectName);CFBundleInfoDictionaryVersion=7.1;CFBundleVersion=1.0.0.0;CFBundlePackageType=APPL;CFBundleSignature=????;LSRequiresIPhoneOS=true;CFBundleAllowMixedLocalizations=YES;CFBundleExecutable=$(MSBuildProjectName);UIDeviceFamily=iPhone & iPad;CFBundleResourceSpecification=ResourceRules.plist;NSLocationAlwaysUsageDescription=The reason for accessing the location information of the user;NSLocationWhenInUseUsageDescription=The reason for accessing the location information of the user;FMLocalNotificationPermission=false;UIBackgroundModes=;NSContactsUsageDescription=The reason for accessing the contacts;NSPhotoLibraryUsageDescription=The reason for accessing the photo library;NSCameraUsageDescription=The reason for accessing the camera + iPhoneAndiPad + true + $(BDS)\bin\Artwork\iOS\iPhone\FM_ApplicationIcon_60x60.png + $(BDS)\bin\Artwork\iOS\iPhone\FM_ApplicationIcon_120x120.png + $(BDS)\bin\Artwork\iOS\iPhone\FM_SpotlightSearchIcon_40x40.png + $(BDS)\bin\Artwork\iOS\iPhone\FM_SpotlightSearchIcon_80x80.png + $(BDS)\bin\Artwork\iOS\iPad\FM_SpotlightSearchIcon_40x40.png + $(BDS)\bin\Artwork\iOS\iPad\FM_SpotlightSearchIcon_80x80.png + $(BDS)\bin\Artwork\iOS\iPad\FM_ApplicationIcon_76x76.png + $(BDS)\bin\Artwork\iOS\iPad\FM_ApplicationIcon_152x152.png + $(BDS)\bin\Artwork\iOS\iPad\FM_LaunchImagePortrait_768x1024.png + $(BDS)\bin\Artwork\iOS\iPad\FM_LaunchImageLandscape_1024x768.png + $(BDS)\bin\Artwork\iOS\iPad\FM_LaunchImagePortrait_1536x2048.png + $(BDS)\bin\Artwork\iOS\iPad\FM_LaunchImageLandscape_2048x1536.png + $(BDS)\bin\Artwork\iOS\iPhone\FM_ApplicationIcon_87x87.png + $(BDS)\bin\Artwork\iOS\iPhone\FM_ApplicationIcon_180x180.png + $(BDS)\bin\Artwork\iOS\iPhone\FM_LaunchImage_750x1334.png + $(BDS)\bin\Artwork\iOS\iPhone\FM_LaunchImage_1242x2208.png + $(BDS)\bin\Artwork\iOS\iPhone\FM_LaunchImage_2208x1242.png + $(BDS)\bin\Artwork\iOS\iPhone\FM_LaunchImage_1125x2436.png + $(BDS)\bin\Artwork\iOS\iPhone\FM_LaunchImage_2436x1125.png + $(BDS)\bin\Artwork\iOS\iPhone\FM_SpotlightSearchIcon_120x120.png + $(BDS)\bin\Artwork\iOS\iPhone\FM_LaunchImage_828x1792.png + $(BDS)\bin\Artwork\iOS\iPhone\FM_LaunchImage_1136x640.png + $(BDS)\bin\Artwork\iOS\iPhone\FM_LaunchImage_1242x2688.png + $(BDS)\bin\Artwork\iOS\iPhone\FM_LaunchImage_1334x750.png + $(BDS)\bin\Artwork\iOS\iPhone\FM_LaunchImage_1792x828.png + $(BDS)\bin\Artwork\iOS\iPhone\FM_LaunchImage_2688x1242.png + $(BDS)\bin\Artwork\iOS\iPad\FM_ApplicationIcon_167x167.png + $(BDS)\bin\Artwork\iOS\iPad\FM_LaunchImagePortrait_1668x2224.png + $(BDS)\bin\Artwork\iOS\iPad\FM_LaunchImagePortrait_1668x2388.png + $(BDS)\bin\Artwork\iOS\iPad\FM_LaunchImagePortrait_2048x2732.png + $(BDS)\bin\Artwork\iOS\iPad\FM_LaunchImageLandscape_2224x1668.png + $(BDS)\bin\Artwork\iOS\iPad\FM_LaunchImageLandscape_2388x1668.png + $(BDS)\bin\Artwork\iOS\iPad\FM_LaunchImageLandscape_2732x2048.png + + + /usr/bin/xterm -e "%debuggee%" + (None) + + + $(BDS)\bin\delphi_PROJECTICNS.icns + + + CFBundleName=$(MSBuildProjectName);CFBundleDisplayName=$(MSBuildProjectName);CFBundleIdentifier=$(MSBuildProjectName);CFBundleVersion=1.0.0;CFBundleShortVersionString=1.0.0;CFBundlePackageType=APPL;CFBundleSignature=????;CFBundleAllowMixedLocalizations=YES;CFBundleExecutable=$(MSBuildProjectName);NSHighResolutionCapable=true;LSApplicationCategoryType=public.app-category.utilities;NSLocationUsageDescription=The reason for accessing the location information of the user;NSContactsUsageDescription=The reason for accessing the contacts + Debug + true + true + Base + true + + + Winapi;System.Win;Data.Win;Datasnap.Win;Web.Win;Soap.Win;Xml.Win;Bde;$(DCC_Namespace) + Debug + true + CompanyName=;FileDescription=$(MSBuildProjectName);FileVersion=1.0.0.0;InternalName=;LegalCopyright=;LegalTrademarks=;OriginalFilename=;ProductName=$(MSBuildProjectName);ProductVersion=1.0.0.0;Comments=;ProgramID=com.embarcadero.$(MSBuildProjectName) + 1033 + $(BDS)\bin\default_app.manifest + true + $(BDS)\bin\Artwork\Windows\UWP\delphi_UwpDefault_44.png + $(BDS)\bin\Artwork\Windows\UWP\delphi_UwpDefault_150.png + UniDACBasicServer_Icon.ico + + + $(BDS)\bin\default_app.manifest + true + $(BDS)\bin\Artwork\Windows\UWP\delphi_UwpDefault_44.png + $(BDS)\bin\Artwork\Windows\UWP\delphi_UwpDefault_150.png + + + RELEASE;$(DCC_Define) + 0 + false + 0 + + + true + PerMonitor + + + DEBUG;$(DCC_Define) + false + true + + + true + + + Debug + + + true + + + $(BDS)\bin\delphi_PROJECTICNS.icns + + + true + true + Cfg_2 + true + + + true + true + 1033 + CompanyName=;FileDescription=$(MSBuildProjectName);FileVersion=1.0.0.0;InternalName=;LegalCopyright=;LegalTrademarks=;OriginalFilename=;ProductName=$(MSBuildProjectName);ProductVersion=1.0.0.0;Comments=;ProgramID=com.embarcadero.$(MSBuildProjectName) + UniDACBasicServer_Icon.ico + PerMonitor + + + + MainSource + + +
MainForm
+
+ +
MainDataModule
+ TDataModule +
+ + Cfg_2 + Base + + + Base + + + Cfg_1 + Base + +
+ + Delphi.Personality.12 + + + + + UniDACBasicClient.dpr + + + Microsoft Office 2000 Sample Automation Server Wrapper Components + Microsoft Office XP Sample Automation Server Wrapper Components + + + + True + False + True + False + True + False + False + True + False + + + + + true + + + + + ic_launcher.png + true + + + + + ic_launcher.png + true + + + + + libUniDACBasicClient.so + true + + + + + ic_launcher.png + true + + + + + true + + + + + true + + + + + splash_image.png + true + + + + + true + + + + + splash_image.png + true + + + + + splash_image.png + true + + + + + splash_image.png + true + + + + + true + + + + + classes.dex + true + + + + + true + + + + + ic_launcher.png + true + + + + + libUniDACBasicClient.so + true + + + + + ic_launcher.png + true + + + + + true + + + + + true + + + + + libUniDACBasicClient.so + true + + + + + true + + + + + true + + + + + 1 + + + Contents\MacOS + 1 + + + 0 + + + + + classes + 1 + + + + + res\xml + 1 + + + + + library\lib\armeabi-v7a + 1 + + + + + library\lib\armeabi + 1 + + + + + library\lib\mips + 1 + + + + + library\lib\armeabi-v7a + 1 + + + + + res\drawable + 1 + + + + + res\values + 1 + + + + + res\values-v21 + 1 + + + + + res\values + 1 + + + + + res\drawable + 1 + + + + + res\drawable-xxhdpi + 1 + + + + + res\drawable-ldpi + 1 + + + + + res\drawable-mdpi + 1 + + + + + res\drawable-hdpi + 1 + + + + + res\drawable-xhdpi + 1 + + + + + res\drawable-mdpi + 1 + + + + + res\drawable-hdpi + 1 + + + + + res\drawable-xhdpi + 1 + + + + + res\drawable-xxhdpi + 1 + + + + + res\drawable-xxxhdpi + 1 + + + + + res\drawable-small + 1 + + + + + res\drawable-normal + 1 + + + + + res\drawable-large + 1 + + + + + res\drawable-xlarge + 1 + + + + + res\values + 1 + + + + + 1 + + + Contents\MacOS + 1 + + + 0 + + + + + Contents\MacOS + 1 + .framework + + + Contents\MacOS + 1 + .framework + + + 0 + + + + + 1 + .dylib + + + 1 + .dylib + + + 1 + .dylib + + + Contents\MacOS + 1 + .dylib + + + Contents\MacOS + 1 + .dylib + + + 0 + .dll;.bpl + + + + + 1 + .dylib + + + 1 + .dylib + + + 1 + .dylib + + + Contents\MacOS + 1 + .dylib + + + Contents\MacOS + 1 + .dylib + + + 0 + .bpl + + + + + 0 + + + 0 + + + 0 + + + 0 + + + Contents\Resources\StartUp\ + 0 + + + Contents\Resources\StartUp\ + 0 + + + 0 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + + + ..\$(PROJECTNAME).app.dSYM\Contents\Resources\DWARF + 1 + + + ..\$(PROJECTNAME).app.dSYM\Contents\Resources\DWARF + 1 + + + + + 1 + + + 1 + + + + + ..\ + 1 + + + ..\ + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + ..\$(PROJECTNAME).app.dSYM\Contents\Resources\DWARF + 1 + + + + + ..\ + 1 + + + ..\ + 1 + + + + + Contents + 1 + + + Contents + 1 + + + + + Contents\Resources + 1 + + + Contents\Resources + 1 + + + + + library\lib\armeabi-v7a + 1 + + + 1 + + + 1 + + + 1 + + + 1 + + + Contents\MacOS + 1 + + + Contents\MacOS + 1 + + + 0 + + + + + 1 + + + 1 + + + + + Assets + 1 + + + Assets + 1 + + + + + Assets + 1 + + + Assets + 1 + + + + + + + + + + + + + + 12 + + + + +
diff --git a/Demos/UniDAC Basic/UniDACBasicProjectGroup.groupproj b/Demos/UniDAC Basic/UniDACBasicProjectGroup.groupproj new file mode 100644 index 00000000..743e65bd --- /dev/null +++ b/Demos/UniDAC Basic/UniDACBasicProjectGroup.groupproj @@ -0,0 +1,120 @@ + + + {6E23DEFF-F737-42C3-B8AD-2549B8F67C93} + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Default.Personality.12 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Demos/UniDAC Basic/UniDACBasicServerApacheModule.dpr b/Demos/UniDAC Basic/UniDACBasicServerApacheModule.dpr new file mode 100644 index 00000000..55815590 --- /dev/null +++ b/Demos/UniDAC Basic/UniDACBasicServerApacheModule.dpr @@ -0,0 +1,54 @@ +(* + Copyright 2016, MARS-Curiosity - REST Library + + Home: https://github.com/andrea-magni/MARS +*) +library UniDACBasicServerApacheModule; + +uses + {$IFDEF MSWINDOWS} + Winapi.ActiveX, System.Win.ComObj, + {$ENDIF } + Web.WebBroker, + Web.ApacheApp, + Web.HTTPD24Impl, + Server.WebModule in 'Server.WebModule.pas' {ServerWebModule: TWebModule}, + Server.Ignition in 'Server.Ignition.pas', + Server.Resources in 'Server.Resources.pas'; + +{$R *.res} + +// httpd.conf entries: +// +(* + LoadModule marstemplate_module modules/mod_marstemplate.dll + + + SetHandler mod_marstemplate-handler + +*) +// +// These entries assume that the output directory for this project is the apache/modules directory. +// +// httpd.conf entries should be different if the project is changed in these ways: +// 1. The TApacheModuleData variable name is changed. +// 2. The project is renamed. +// 3. The output directory is not the apache/modules directory. +// 4. The dynamic library extension depends on a platform. Use .dll on Windows and .so on Linux. +// + +// Declare exported variable so that Apache can access this module. +var + GModuleData: TApacheModuleData; +exports + GModuleData name 'marstemplate_module'; + +begin +{$IFDEF MSWINDOWS} + CoInitFlags := COINIT_MULTITHREADED; +{$ENDIF} + Web.ApacheApp.InitApplication(@GModuleData); + Application.Initialize; + Application.WebModuleClass := WebModuleClass; + Application.Run; +end. diff --git a/Demos/UniDAC Basic/UniDACBasicServerApacheModule.dproj b/Demos/UniDAC Basic/UniDACBasicServerApacheModule.dproj new file mode 100644 index 00000000..4489e7c8 --- /dev/null +++ b/Demos/UniDAC Basic/UniDACBasicServerApacheModule.dproj @@ -0,0 +1,856 @@ + + + {0D982E91-6C92-4321-9078-458448B01536} + 18.7 + None + UniDACBasicServerApacheModule.dpr + True + Release + Win32 + 129 + Library + + + true + + + true + Base + true + + + true + Base + true + + + true + Base + true + + + true + Base + true + + + true + Cfg_1 + true + true + + + true + Base + true + + + .\lib\$(Platform)\$(Config) + .\bin + false + false + false + false + false + true + RESTComponents;emsclientfiredac;DataSnapFireDAC;FireDACIBDriver;emsclient;FireDACCommon;RESTBackendComponents;soapserver;CloudService;FireDACCommonDriver;inet;FireDAC;FireDACSqliteDriver;soaprtl;soapmidas;$(DCC_UsePackage) + System;Xml;Data;Datasnap;Web;Soap;$(DCC_Namespace) + $(BDS)\bin\delphi_PROJECTICON.ico + $(BDS)\bin\delphi_PROJECTICNS.icns + UniDACBasicServerApacheModule + + + DataSnapServerMidas;FireDACADSDriver;DatasnapConnectorsFreePascal;FireDACMSSQLDriver;inetdb;emsedge;dbexpress;IndyCore;dsnap;DataSnapCommon;DataSnapConnectors;bindengine;FireDACOracleDriver;FireDACMySQLDriver;FireDACCommonODBC;DataSnapClient;IndySystem;FireDACDb2Driver;FireDACInfxDriver;emshosting;FireDACPgDriver;FireDACASADriver;FireDACTDataDriver;DbxCommonDriver;DataSnapServer;xmlrtl;DataSnapNativeClient;rtl;DbxClientDriver;CustomIPTransport;bindcomp;dbxcds;FireDACODBCDriver;DataSnapIndy10ServerTransport;dsnapxml;dbrtl;FireDACMongoDBDriver;IndyProtocols;$(DCC_UsePackage) + true + /usr/bin/xterm -e "%debuggee%" + (None) + + + DBXSqliteDriver;DataSnapServerMidas;DBXDb2Driver;DBXInterBaseDriver;vclactnband;vclFireDAC;svnui;tethering;FireDACADSDriver;DBXMSSQLDriver;DatasnapConnectorsFreePascal;FireDACMSSQLDriver;vcltouch;vcldb;bindcompfmx;svn;Intraweb;DBXOracleDriver;inetdb;FmxTeeUI;emsedge;fmx;fmxdae;vclib;FireDACDBXDriver;dbexpress;IndyCore;vclx;dsnap;DataSnapCommon;DataSnapConnectors;MARS.Utils;VCLRESTComponents;vclie;bindengine;DBXMySQLDriver;FireDACOracleDriver;FireDACMySQLDriver;DBXFirebirdDriver;FireDACCommonODBC;DataSnapClient;bindcompdbx;IndyIPCommon;vcl;DBXSybaseASEDriver;IndyIPServer;IndySystem;FireDACDb2Driver;dsnapcon;FireDACMSAccDriver;fmxFireDAC;FireDACInfxDriver;vclimg;TeeDB;emshosting;FireDACPgDriver;ibmonitor;FireDACASADriver;DBXOdbcDriver;FireDACTDataDriver;FMXTee;DbxCommonDriver;ibxpress;Tee;DataSnapServer;xmlrtl;DataSnapNativeClient;fmxobj;vclwinx;ibxbindings;rtl;FireDACDSDriver;DbxClientDriver;DBXSybaseASADriver;CustomIPTransport;vcldsnap;CodeSiteExpressPkg;bindcomp;appanalytics;DBXInformixDriver;IndyIPClient;bindcompvcl;TeeUI;dbxcds;VclSmp;adortl;FireDACODBCDriver;DataSnapIndy10ServerTransport;dsnapxml;DataSnapProviderClient;dbrtl;inetdbxpress;FireDACMongoDBDriver;IndyProtocols;fmxase;$(DCC_UsePackage) + Winapi;System.Win;Data.Win;Datasnap.Win;Web.Win;Soap.Win;Xml.Win;Bde;$(DCC_Namespace) + Debug + true + CompanyName=;FileDescription=$(MSBuildProjectName);FileVersion=1.0.0.0;InternalName=;LegalCopyright=;LegalTrademarks=;OriginalFilename=;ProgramID=com.embarcadero.$(MSBuildProjectName);ProductName=$(MSBuildProjectName);ProductVersion=1.0.0.0;Comments= + 1033 + (None) + + + DBXSqliteDriver;DataSnapServerMidas;DBXDb2Driver;DBXInterBaseDriver;vclactnband;vclFireDAC;tethering;FireDACADSDriver;DBXMSSQLDriver;DatasnapConnectorsFreePascal;FireDACMSSQLDriver;vcltouch;vcldb;bindcompfmx;Intraweb;DBXOracleDriver;inetdb;FmxTeeUI;emsedge;fmx;fmxdae;vclib;FireDACDBXDriver;dbexpress;IndyCore;vclx;dsnap;DataSnapCommon;DataSnapConnectors;MARS.Utils;VCLRESTComponents;vclie;bindengine;DBXMySQLDriver;FireDACOracleDriver;FireDACMySQLDriver;DBXFirebirdDriver;FireDACCommonODBC;DataSnapClient;bindcompdbx;IndyIPCommon;vcl;DBXSybaseASEDriver;IndyIPServer;IndySystem;FireDACDb2Driver;dsnapcon;FireDACMSAccDriver;fmxFireDAC;FireDACInfxDriver;vclimg;TeeDB;emshosting;FireDACPgDriver;ibmonitor;FireDACASADriver;DBXOdbcDriver;FireDACTDataDriver;FMXTee;DbxCommonDriver;ibxpress;Tee;DataSnapServer;xmlrtl;DataSnapNativeClient;fmxobj;vclwinx;ibxbindings;rtl;FireDACDSDriver;DbxClientDriver;DBXSybaseASADriver;CustomIPTransport;vcldsnap;bindcomp;appanalytics;DBXInformixDriver;IndyIPClient;bindcompvcl;TeeUI;dbxcds;VclSmp;adortl;FireDACODBCDriver;DataSnapIndy10ServerTransport;dsnapxml;DataSnapProviderClient;dbrtl;inetdbxpress;FireDACMongoDBDriver;IndyProtocols;fmxase;$(DCC_UsePackage) + + + DEBUG;$(DCC_Define) + true + false + true + true + true + + + false + true + 1033 + (None) + + + false + RELEASE;$(DCC_Define) + 0 + 0 + + + + MainSource + + +
ServerWebModule
+ dfm + TWebModule +
+ + + + Cfg_2 + Base + + + Base + + + Cfg_1 + Base + +
+ + Delphi.Personality.12 + Library + + + + UniDACBasicServerApacheModule.dpr + + + Microsoft Office 2000 Sample Automation Server Wrapper Components + Microsoft Office XP Sample Automation Server Wrapper Components + + + + + + UniDACBasicServerApacheModule.dll + true + + + + + true + + + + + true + + + + + true + + + + + true + + + + + true + + + + + true + + + + + libUniDACBasicServerApacheModule.so + true + + + + + 1 + + + 0 + + + + + classes + 1 + + + + + res\xml + 1 + + + + + library\lib\armeabi-v7a + 1 + + + + + library\lib\armeabi + 1 + + + + + library\lib\mips + 1 + + + + + library\lib\armeabi-v7a + 1 + + + + + res\drawable + 1 + + + + + res\values + 1 + + + + + res\values-v21 + 1 + + + + + res\values + 1 + + + + + res\drawable + 1 + + + + + res\drawable-xxhdpi + 1 + + + + + res\drawable-ldpi + 1 + + + + + res\drawable-mdpi + 1 + + + + + res\drawable-hdpi + 1 + + + + + res\drawable-xhdpi + 1 + + + + + res\drawable-mdpi + 1 + + + + + res\drawable-hdpi + 1 + + + + + res\drawable-xhdpi + 1 + + + + + res\drawable-xxhdpi + 1 + + + + + res\drawable-xxxhdpi + 1 + + + + + res\drawable-small + 1 + + + + + res\drawable-normal + 1 + + + + + res\drawable-large + 1 + + + + + res\drawable-xlarge + 1 + + + + + res\values + 1 + + + + + 1 + + + 1 + + + 0 + + + + + 1 + .framework + + + 1 + .framework + + + 0 + + + + + 1 + .dylib + + + 1 + .dylib + + + 0 + .dll;.bpl + + + + + 1 + .dylib + + + 1 + .dylib + + + 1 + .dylib + + + 1 + .dylib + + + 1 + .dylib + + + 0 + .bpl + + + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + + + ..\$(PROJECTNAME).app.dSYM\Contents\Resources\DWARF + 1 + + + ..\$(PROJECTNAME).app.dSYM\Contents\Resources\DWARF + 1 + + + + + + + + 1 + + + 1 + + + 1 + + + + + + + + Contents\Resources + 1 + + + Contents\Resources + 1 + + + + + library\lib\armeabi-v7a + 1 + + + 1 + + + 1 + + + 1 + + + 1 + + + 1 + + + 1 + + + 0 + + + + + 1 + + + 1 + + + + + Assets + 1 + + + Assets + 1 + + + + + Assets + 1 + + + Assets + 1 + + + + + + + + + + + + + + True + True + False + + + 12 + + + + +
diff --git a/Demos/UniDAC Basic/UniDACBasicServerApplication.dpr b/Demos/UniDAC Basic/UniDACBasicServerApplication.dpr new file mode 100644 index 00000000..07a8089c --- /dev/null +++ b/Demos/UniDAC Basic/UniDACBasicServerApplication.dpr @@ -0,0 +1,23 @@ +(* + Copyright 2016, MARS-Curiosity library + + Home: https://github.com/andrea-magni/MARS +*) + program UniDACBasicServerApplication; + +uses + Forms, + Server.Forms.Main in 'Server.Forms.Main.pas' {MainForm}, + Server.Resources in 'Server.Resources.pas', + Server.Ignition in 'Server.Ignition.pas'; + +{$R *.res} + +begin + ReportMemoryLeaksOnShutdown := True; + + Application.Initialize; + Application.MainFormOnTaskbar := True; + Application.CreateForm(TMainForm, MainForm); + Application.Run; +end. diff --git a/Demos/UniDAC Basic/UniDACBasicServerApplication.dproj b/Demos/UniDAC Basic/UniDACBasicServerApplication.dproj new file mode 100644 index 00000000..40028b56 --- /dev/null +++ b/Demos/UniDAC Basic/UniDACBasicServerApplication.dproj @@ -0,0 +1,190 @@ + + + {0A5E1DDC-90B4-4B41-A7DC-2B0FC45D4349} + UniDACBasicServerApplication.dpr + True + Debug + 3 + Application + VCL + 18.7 + Win32 + + + true + + + true + Base + true + + + true + Base + true + + + true + Base + true + + + true + Cfg_1 + true + true + + + true + Cfg_1 + true + true + + + true + Base + true + + + true + Cfg_2 + true + true + + + true + Cfg_2 + true + true + + + true + Cfg_2 + true + true + + + false + false + false + false + false + 00400000 + UniDACBasicServerApplication + Vcl;Vcl.Imaging;Vcl.Touch;Vcl.Samples;Vcl.Shell;System;Xml;Data;Datasnap;Web;Soap;$(DCC_Namespace) + 1040 + CompanyName=;FileDescription=;FileVersion=1.0.0.0;InternalName=;LegalCopyright=;LegalTrademarks=;OriginalFilename=;ProductName=;ProductVersion=1.0.0.0;Comments=;CFBundleName= + .\bin + .\lib\$(Platform)\$(Config) + + + Winapi;System.Win;Data.Win;Datasnap.Win;Web.Win;Soap.Win;Xml.Win;Bde;$(DCC_Namespace) + Debug + true + CompanyName=;FileDescription=$(MSBuildProjectName);FileVersion=1.0.0.0;InternalName=;LegalCopyright=;LegalTrademarks=;OriginalFilename=;ProductName=$(MSBuildProjectName);ProductVersion=1.0.0.0;Comments=;ProgramID=com.embarcadero.$(MSBuildProjectName) + 1033 + $(BDS)\bin\default_app.manifest + UniDACBasicServer_Icon.ico + true + $(BDS)\bin\Artwork\Windows\UWP\delphi_UwpDefault_44.png + $(BDS)\bin\Artwork\Windows\UWP\delphi_UwpDefault_150.png + + + $(BDS)\bin\default_app.manifest + UniDACBasicServer_Icon.ico + true + $(BDS)\bin\Artwork\Windows\UWP\delphi_UwpDefault_44.png + $(BDS)\bin\Artwork\Windows\UWP\delphi_UwpDefault_150.png + Winapi;System.Win;Data.Win;Datasnap.Win;Web.Win;Soap.Win;Xml.Win;$(DCC_Namespace) + Debug + true + CompanyName=;FileDescription=$(MSBuildProjectName);FileVersion=1.0.0.0;InternalName=;LegalCopyright=;LegalTrademarks=;OriginalFilename=;ProgramID=com.embarcadero.$(MSBuildProjectName);ProductName=$(MSBuildProjectName);ProductVersion=1.0.0.0;Comments= + 1033 + + + RELEASE;$(DCC_Define) + 0 + false + 0 + + + true + true + 1033 + CompanyName=;FileDescription=$(MSBuildProjectName);FileVersion=1.0.0.0;InternalName=;LegalCopyright=;LegalTrademarks=;OriginalFilename=;ProductName=$(MSBuildProjectName);ProductVersion=1.0.0.0;Comments=;ProgramID=com.embarcadero.$(MSBuildProjectName) + UniDACBasicServer_Icon.ico + PerMonitor + + + true + true + 1033 + CompanyName=;FileDescription=$(MSBuildProjectName);FileVersion=1.0.0.0;InternalName=;LegalCopyright=;LegalTrademarks=;OriginalFilename=;ProgramID=com.embarcadero.$(MSBuildProjectName);ProductName=$(MSBuildProjectName);ProductVersion=1.0.0.0;Comments= + PerMonitor + + + DEBUG;$(DCC_Define) + false + true + + + Debug + + + true + true + 1033 + CompanyName=;FileDescription=$(MSBuildProjectName);FileVersion=1.0.0.0;InternalName=;LegalCopyright=;LegalTrademarks=;OriginalFilename=;ProductName=$(MSBuildProjectName);ProductVersion=1.0.0.0;Comments=;ProgramID=com.embarcadero.$(MSBuildProjectName) + UniDACBasicServer_Icon.ico + PerMonitor + + + true + PerMonitor + + + + MainSource + + +
MainForm
+
+ + + + Cfg_2 + Base + + + Base + + + Cfg_1 + Base + +
+ + Delphi.Personality.12 + + + + + UniDACBasicServerApplication.dpr + + + Microsoft Office 2000 Sample Automation Server Wrapper Components + Microsoft Office XP Sample Automation Server Wrapper Components + + + + False + False + True + True + + + 12 + + + +
diff --git a/Demos/UniDAC Basic/UniDACBasicServerConsoleApplication.dpr b/Demos/UniDAC Basic/UniDACBasicServerConsoleApplication.dpr new file mode 100644 index 00000000..882167f5 --- /dev/null +++ b/Demos/UniDAC Basic/UniDACBasicServerConsoleApplication.dpr @@ -0,0 +1,201 @@ +(* + Copyright 2016, MARS-Curiosity - REST Library + + Home: https://github.com/andrea-magni/MARS +*) +program UniDACBasicServerConsoleApplication; +{$APPTYPE CONSOLE} + +{$I MARS.inc} + +uses +{$ifdef DelphiXE3_UP} + System.SysUtils, + System.Types, +// IPPeerServer, IPPeerAPI, + IdHTTPWebBrokerBridge, + IdSchedulerOfThreadPool, + Web.WebReq, + Web.WebBroker, +{$else} + SysUtils, StrUtils, + Types, + IdHTTPWebBrokerBridge, + IdSchedulerOfThreadPool, + WebReq, + WebBroker, +{$endif} + IdContext, + ServerConst in 'ServerConst.pas', + Server.WebModule in 'Server.WebModule.pas' {ServerWebModule: TWebModule}, + Server.Ignition in 'Server.Ignition.pas'; + +{$R *.res} + +type + TDummyIndyServer = class + public + procedure ParseAuthenticationHandler(AContext: TIdContext; + const AAuthType, AAuthData: String; var VUsername, VPassword: String; + var VHandled: Boolean); virtual; + end; + + +procedure StartServer(const AServer: TIdHTTPWebBrokerBridge); +begin + if not (AServer.Active) then + begin + AServer.DefaultPort := TServerEngine.Default.Port; + Writeln(Format(sStartingServer, [AServer.DefaultPort])); + AServer.Active := True; + end + else + Writeln(sServerRunning); + Write(cArrow); +end; + +procedure StopServer(const AServer: TIdHTTPWebBrokerBridge); +begin + if AServer.Active then + begin + Writeln(sStoppingServer); + AServer.Active := False; + Writeln(sServerStopped); + end + else + Writeln(sServerNotRunning); + Write(cArrow); +end; + +procedure SetPort(const AServer: TIdHTTPWebBrokerBridge; const APort: string); +var + LPort: Integer; + LWasActive: Boolean; +begin + LPort := StrToIntDef(APort, -1); + if LPort = -1 then + begin + Writeln('Port should be an integer number. Try again.'); + Exit; + end; + + LWasActive := AServer.Active; + if LWasActive then + StopServer(AServer); + TServerEngine.Default.Port := LPort; + if LWasActive then + StartServer(AServer); + Writeln(Format(sPortSet, [IntToStr(TServerEngine.Default.Port)])); + Write(cArrow); +end; + +procedure WriteCommands; +begin + Writeln(sCommands); + Write(cArrow); +end; + +procedure WriteStatus(const AServer: TIdHTTPWebBrokerBridge); +begin + Writeln(sIndyVersion + AServer.SessionList.Version); + Writeln(sActive + BoolToStr(AServer.Active, True)); + Writeln(sPort + IntToStr(TServerEngine.Default.Port)); + Write(cArrow); +end; + +procedure SetupThreadScheduler(const AServer: TIdHTTPWebBrokerBridge); +var + LScheduler: TIdSchedulerOfThreadPool; +begin + LScheduler := TIdSchedulerOfThreadPool.Create(AServer); + try + LScheduler.PoolSize := TServerEngine.Default.ThreadPoolSize; + AServer.Scheduler := LScheduler; + AServer.MaxConnections := LScheduler.PoolSize; + except + AServer.Scheduler.DisposeOf; + AServer.Scheduler := nil; + raise; + end; +end; + +procedure RunServer(); +var + LServer: TIdHTTPWebBrokerBridge; + LDummyIndy: TDummyIndyServer; + LResponse: string; +begin + WriteCommands; + LDummyIndy := TDummyIndyServer.Create; + try + LServer := TIdHTTPWebBrokerBridge.Create(nil); + try + LServer.DefaultPort := TServerEngine.Default.Port; + LServer.OnParseAuthentication := LDummyIndy.ParseAuthenticationHandler; + {$IFNDEF LINUX} + SetupThreadScheduler(LServer); + {$ENDIF} + + while True do + begin + Readln(LResponse); + LResponse := LowerCase(LResponse); + if sametext(LResponse, cCommandStart) then + StartServer(LServer) + else if sametext(LResponse, cCommandStatus) then + WriteStatus(LServer) + else if sametext(LResponse, cCommandStop) then + StopServer(LServer) + {$ifdef DelphiXE3_UP} + else if LResponse.StartsWith(cCommandSetPort, True) then + SetPort(LServer, LResponse.Split([' '])[2]) + {$else} + else if AnsiStartsText(cCommandSetPort, LResponse) then + SetPort(LServer, Copy(LResponse, Length(cCommandSetPort)+1, MAXINT)) + {$endif} + + else if sametext(LResponse, cCommandHelp) then + WriteCommands + else if sametext(LResponse, cCommandExit) then + if LServer.Active then + begin + StopServer(LServer); + break + end + else + break + else + begin + Writeln(sInvalidCommand); + Write(cArrow); + end; + end; + finally + LServer.Free; + end; + finally + LDummyIndy.Free; + end; +end; + +{ TDummyIndyServer } + +procedure TDummyIndyServer.ParseAuthenticationHandler(AContext: TIdContext; + const AAuthType, AAuthData: String; var VUsername, VPassword: String; + var VHandled: Boolean); +begin + // Allow JWT Bearer authentication's scheme + if SameText(AAuthType, 'Bearer') then + VHandled := True; +end; + +begin + try + if WebRequestHandler <> nil then + WebRequestHandler.WebModuleClass := WebModuleClass; + RunServer(); + except + on E: Exception do + Writeln(E.ClassName, ': ', E.Message); + end +end. diff --git a/Demos/UniDAC Basic/UniDACBasicServerConsoleApplication.dproj b/Demos/UniDAC Basic/UniDACBasicServerConsoleApplication.dproj new file mode 100644 index 00000000..375ad036 --- /dev/null +++ b/Demos/UniDAC Basic/UniDACBasicServerConsoleApplication.dproj @@ -0,0 +1,1049 @@ + + + {8EEC8DF2-3740-468A-937A-60168245FB71} + UniDACBasicServerConsoleApplication.dpr + True + Release + 4229 + Console + None + 18.7 + Win32 + + + true + + + true + Base + true + + + true + Base + true + + + true + Base + true + + + true + Base + true + + + true + Base + true + + + true + Base + true + + + true + Base + true + + + true + Base + true + + + true + Base + true + + + true + Cfg_1 + true + true + + + true + Cfg_1 + true + true + + + true + Base + true + + + true + Cfg_2 + true + true + + + true + Cfg_2 + true + true + + + true + Cfg_2 + true + true + + + true + Cfg_2 + true + true + + + true + Cfg_2 + true + true + + + true + Cfg_2 + true + true + + + true + Cfg_2 + true + true + + + false + false + false + false + false + 00400000 + UniDACBasicServerConsoleApplication + 1040 + CompanyName=;FileDescription=;FileVersion=1.0.0.0;InternalName=;LegalCopyright=;LegalTrademarks=;OriginalFilename=;ProductName=;ProductVersion=1.0.0.0;Comments=;CFBundleName= + System;Xml;Data;Datasnap;Web;Soap;$(DCC_Namespace) + $(BDS)\bin\delphi_PROJECTICON.ico + $(BDS)\bin\delphi_PROJECTICNS.icns + ./bin + .\lib\$(Platform)\$(Config) + + + package=com.embarcadero.$(MSBuildProjectName);label=$(MSBuildProjectName);versionCode=1;versionName=1.0.0;persistent=False;restoreAnyVersion=False;installLocation=auto;largeHeap=False;theme=TitleBar;hardwareAccelerated=true;apiKey= + Debug + $(BDS)\bin\Artwork\Android\FM_LauncherIcon_36x36.png + $(BDS)\bin\Artwork\Android\FM_LauncherIcon_48x48.png + $(BDS)\bin\Artwork\Android\FM_LauncherIcon_72x72.png + $(BDS)\bin\Artwork\Android\FM_LauncherIcon_96x96.png + $(BDS)\bin\Artwork\Android\FM_LauncherIcon_144x144.png + $(BDS)\bin\Artwork\Android\FM_SplashImage_426x320.png + $(BDS)\bin\Artwork\Android\FM_SplashImage_470x320.png + $(BDS)\bin\Artwork\Android\FM_SplashImage_640x480.png + $(BDS)\bin\Artwork\Android\FM_SplashImage_960x720.png + true + true + true + true + true + true + true + true + true + true + android-support-v4.dex.jar;cloud-messaging.dex.jar;fmx.dex.jar;google-analytics-v2.dex.jar;google-play-billing.dex.jar;google-play-licensing.dex.jar;google-play-services.dex.jar + $(BDS)\bin\Artwork\Android\FM_NotificationIcon_24x24.png + $(BDS)\bin\Artwork\Android\FM_NotificationIcon_36x36.png + $(BDS)\bin\Artwork\Android\FM_NotificationIcon_48x48.png + $(BDS)\bin\Artwork\Android\FM_NotificationIcon_72x72.png + $(BDS)\bin\Artwork\Android\FM_NotificationIcon_96x96.png + + + CFBundleName=$(MSBuildProjectName);CFBundleDevelopmentRegion=en;CFBundleDisplayName=$(MSBuildProjectName);CFBundleIdentifier=$(MSBuildProjectName);CFBundleInfoDictionaryVersion=7.1;CFBundleVersion=1.0.0.0;CFBundlePackageType=APPL;CFBundleSignature=????;LSRequiresIPhoneOS=true;CFBundleAllowMixedLocalizations=YES;CFBundleExecutable=$(MSBuildProjectName);UIDeviceFamily=iPhone & iPad;CFBundleResourceSpecification=ResourceRules.plist;NSLocationAlwaysUsageDescription=The reason for accessing the location information of the user;NSLocationWhenInUseUsageDescription=The reason for accessing the location information of the user;FMLocalNotificationPermission=false;UIBackgroundModes=;NSContactsUsageDescription=The reason for accessing the contacts;NSPhotoLibraryUsageDescription=The reason for accessing the photo library;NSCameraUsageDescription=The reason for accessing the camera + iPhoneAndiPad + true + Debug + $(MSBuildProjectName) + $(BDS)\bin\Artwork\iOS\iPhone\FM_ApplicationIcon_60x60.png + $(BDS)\bin\Artwork\iOS\iPhone\FM_ApplicationIcon_120x120.png + $(BDS)\bin\Artwork\iOS\iPhone\FM_SpotlightSearchIcon_40x40.png + $(BDS)\bin\Artwork\iOS\iPhone\FM_SpotlightSearchIcon_80x80.png + $(BDS)\bin\Artwork\iOS\iPad\FM_SpotlightSearchIcon_40x40.png + $(BDS)\bin\Artwork\iOS\iPad\FM_SpotlightSearchIcon_80x80.png + $(BDS)\bin\Artwork\iOS\iPad\FM_ApplicationIcon_76x76.png + $(BDS)\bin\Artwork\iOS\iPad\FM_ApplicationIcon_152x152.png + $(BDS)\bin\Artwork\iOS\iPad\FM_LaunchImagePortrait_768x1024.png + $(BDS)\bin\Artwork\iOS\iPad\FM_LaunchImageLandscape_1024x768.png + $(BDS)\bin\Artwork\iOS\iPad\FM_LaunchImagePortrait_1536x2048.png + $(BDS)\bin\Artwork\iOS\iPad\FM_LaunchImageLandscape_2048x1536.png + + + CFBundleName=$(MSBuildProjectName);CFBundleDevelopmentRegion=en;CFBundleDisplayName=$(MSBuildProjectName);CFBundleIdentifier=$(MSBuildProjectName);CFBundleInfoDictionaryVersion=7.1;CFBundleVersion=1.0.0.0;CFBundlePackageType=APPL;CFBundleSignature=????;LSRequiresIPhoneOS=true;CFBundleAllowMixedLocalizations=YES;CFBundleExecutable=$(MSBuildProjectName);UIDeviceFamily=iPhone & iPad;CFBundleResourceSpecification=ResourceRules.plist;NSLocationAlwaysUsageDescription=The reason for accessing the location information of the user;NSLocationWhenInUseUsageDescription=The reason for accessing the location information of the user;FMLocalNotificationPermission=false;UIBackgroundModes=;NSContactsUsageDescription=The reason for accessing the contacts;NSPhotoLibraryUsageDescription=The reason for accessing the photo library;NSCameraUsageDescription=The reason for accessing the camera + iPhoneAndiPad + true + $(BDS)\bin\Artwork\iOS\iPhone\FM_ApplicationIcon_60x60.png + $(BDS)\bin\Artwork\iOS\iPhone\FM_ApplicationIcon_120x120.png + $(BDS)\bin\Artwork\iOS\iPhone\FM_SpotlightSearchIcon_40x40.png + $(BDS)\bin\Artwork\iOS\iPhone\FM_SpotlightSearchIcon_80x80.png + $(BDS)\bin\Artwork\iOS\iPad\FM_SpotlightSearchIcon_40x40.png + $(BDS)\bin\Artwork\iOS\iPad\FM_SpotlightSearchIcon_80x80.png + $(BDS)\bin\Artwork\iOS\iPad\FM_ApplicationIcon_76x76.png + $(BDS)\bin\Artwork\iOS\iPad\FM_ApplicationIcon_152x152.png + $(BDS)\bin\Artwork\iOS\iPad\FM_LaunchImagePortrait_768x1024.png + $(BDS)\bin\Artwork\iOS\iPad\FM_LaunchImageLandscape_1024x768.png + $(BDS)\bin\Artwork\iOS\iPad\FM_LaunchImagePortrait_1536x2048.png + $(BDS)\bin\Artwork\iOS\iPad\FM_LaunchImageLandscape_2048x1536.png + + + /usr/bin/xterm -e "%debuggee%" + (None) + UniDACBasicServer_Icon.ico + + + CFBundleName=$(MSBuildProjectName);CFBundleDisplayName=$(MSBuildProjectName);CFBundleIdentifier=$(MSBuildProjectName);CFBundleVersion=1.0.0;CFBundleShortVersionString=1.0.0;CFBundlePackageType=APPL;CFBundleSignature=????;CFBundleAllowMixedLocalizations=YES;CFBundleExecutable=$(MSBuildProjectName);NSHighResolutionCapable=true;LSApplicationCategoryType=public.app-category.utilities;NSContactsUsageDescription=The reason for accessing the contacts;NSLocationUsageDescription=The reason for accessing the location information of the user + Debug + /usr/X11/bin/xterm -e "%debuggee%" + (None) + + + CFBundleName=$(MSBuildProjectName);CFBundleDisplayName=$(MSBuildProjectName);CFBundleIdentifier=$(MSBuildProjectName);CFBundleVersion=1.0.0;CFBundleShortVersionString=1.0.0;CFBundlePackageType=APPL;CFBundleSignature=????;CFBundleAllowMixedLocalizations=YES;CFBundleExecutable=$(MSBuildProjectName);NSHighResolutionCapable=true;LSApplicationCategoryType=public.app-category.utilities;NSContactsUsageDescription=The reason for accessing the contacts;NSLocationUsageDescription=The reason for accessing the location information of the user + Debug + true + Base + true + /usr/X11/bin/xterm -e "%debuggee%" + (None) + + + Winapi;System.Win;Data.Win;Datasnap.Win;Web.Win;Soap.Win;Xml.Win;Bde;$(DCC_Namespace) + Debug + CompanyName=;FileDescription=$(MSBuildProjectName);FileVersion=1.0.0.0;InternalName=;LegalCopyright=;LegalTrademarks=;OriginalFilename=;ProductName=$(MSBuildProjectName);ProductVersion=1.0.0.0;Comments=;ProgramID=com.embarcadero.$(MSBuildProjectName) + 1033 + UniDACBasicServer_Icon.ico + (None) + $(BDS)\bin\Artwork\Windows\UWP\delphi_UwpDefault_44.png + $(BDS)\bin\Artwork\Windows\UWP\delphi_UwpDefault_150.png + + + UniDACBasicServerConsoleApplication_Icon.ico + + + RELEASE;$(DCC_Define) + 0 + false + 0 + + + UniDACBasicServer_Icon.ico + + + 1033 + CompanyName=;FileDescription=$(MSBuildProjectName);FileVersion=1.0.0.0;InternalName=;LegalCopyright=;LegalTrademarks=;OriginalFilename=;ProductName=$(MSBuildProjectName);ProductVersion=1.0.0.0;Comments=;ProgramID=com.embarcadero.$(MSBuildProjectName) + + + DEBUG;$(DCC_Define) + false + true + + + true + + + Debug + + + true + + + UniDACBasicServer_Icon.ico + + + true + + + true + true + Cfg_2 + true + + + 1033 + CompanyName=;FileDescription=$(MSBuildProjectName);FileVersion=1.0.0.0;InternalName=;LegalCopyright=;LegalTrademarks=;OriginalFilename=;ProductName=$(MSBuildProjectName);ProductVersion=1.0.0.0;Comments=;ProgramID=com.embarcadero.$(MSBuildProjectName) + $(BDS)\bin\Artwork\Windows\UWP\delphi_UwpDefault_44.png + $(BDS)\bin\Artwork\Windows\UWP\delphi_UwpDefault_150.png + UniDACBasicServer_Icon.ico + + + + MainSource + + + +
ServerWebModule
+ TWebModule +
+ + + Cfg_2 + Base + + + Base + + + Cfg_1 + Base + +
+ + Delphi.Personality.12 + + + + + UniDACBasicServerConsoleApplication.dpr + + + Microsoft Office 2000 Sample Automation Server Wrapper Components + Microsoft Office XP Sample Automation Server Wrapper Components + + + + False + False + False + False + True + True + True + True + False + + + + + Assets\ + Logo150x150.png + true + + + + + true + + + + + UniDACBasicServerConsoleApplication + true + + + + + true + + + + + true + + + + + true + + + + + true + + + + + true + + + + + Assets\ + Logo44x44.png + true + + + + + 1 + + + 0 + + + + + classes + 1 + + + + + res\xml + 1 + + + + + library\lib\armeabi-v7a + 1 + + + + + library\lib\armeabi + 1 + + + + + library\lib\mips + 1 + + + + + library\lib\armeabi-v7a + 1 + + + + + res\drawable + 1 + + + + + res\values + 1 + + + + + res\values-v21 + 1 + + + + + res\values + 1 + + + + + res\drawable + 1 + + + + + res\drawable-xxhdpi + 1 + + + + + res\drawable-ldpi + 1 + + + + + res\drawable-mdpi + 1 + + + + + res\drawable-hdpi + 1 + + + + + res\drawable-xhdpi + 1 + + + + + res\drawable-mdpi + 1 + + + + + res\drawable-hdpi + 1 + + + + + res\drawable-xhdpi + 1 + + + + + res\drawable-xxhdpi + 1 + + + + + res\drawable-xxxhdpi + 1 + + + + + res\drawable-small + 1 + + + + + res\drawable-normal + 1 + + + + + res\drawable-large + 1 + + + + + res\drawable-xlarge + 1 + + + + + res\values + 1 + + + + + 1 + + + 1 + + + 0 + + + + + 1 + .framework + + + 1 + .framework + + + 0 + + + + + 1 + .dylib + + + 1 + .dylib + + + 0 + .dll;.bpl + + + + + 1 + .dylib + + + 1 + .dylib + + + 1 + .dylib + + + 1 + .dylib + + + 1 + .dylib + + + 0 + .bpl + + + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + + + ..\$(PROJECTNAME).app.dSYM\Contents\Resources\DWARF + 1 + + + ..\$(PROJECTNAME).app.dSYM\Contents\Resources\DWARF + 1 + + + + + + + + 1 + + + 1 + + + 1 + + + + + + + + Contents\Resources + 1 + + + Contents\Resources + 1 + + + + + library\lib\armeabi-v7a + 1 + + + 1 + + + 1 + + + 1 + + + 1 + + + 1 + + + 1 + + + 0 + + + + + 1 + + + 1 + + + + + Assets + 1 + + + Assets + 1 + + + + + Assets + 1 + + + Assets + 1 + + + + + + + + + + + + + + 12 + + + + +
diff --git a/Demos/UniDAC Basic/UniDACBasicServerDaemon.dpr b/Demos/UniDAC Basic/UniDACBasicServerDaemon.dpr new file mode 100644 index 00000000..0b5ff770 --- /dev/null +++ b/Demos/UniDAC Basic/UniDACBasicServerDaemon.dpr @@ -0,0 +1,21 @@ +program UniDACBasicServerDaemon; + +{$APPTYPE CONSOLE} + +{$R *.res} + +uses + Classes, + SysUtils, + {$IFDEF LINUX} + MARS.Linux.Daemon in '..\..\Source\MARS.Linux.Daemon.pas', + {$ENDIF} + Server.Ignition in 'Server.Ignition.pas', + Server.WebModule in 'Server.WebModule.pas' {ServerWebModule: TWebModule}; + +begin + {$IFDEF LINUX} + TMARSDaemon.Current.Name := 'UniDACBasicServerDaemon'; + TMARSDaemon.Current.Start; + {$ENDIF} +end. diff --git a/Demos/UniDAC Basic/UniDACBasicServerDaemon.dproj b/Demos/UniDAC Basic/UniDACBasicServerDaemon.dproj new file mode 100644 index 00000000..ed89f818 --- /dev/null +++ b/Demos/UniDAC Basic/UniDACBasicServerDaemon.dproj @@ -0,0 +1,1008 @@ + + + {9D225C2C-24C2-48B4-8ABE-52C04AF576AE} + 18.7 + None + UniDACBasicServerDaemon.dpr + True + Debug + Linux64 + 128 + Console + + + true + + + true + Base + true + + + true + Base + true + + + true + Base + true + + + true + Base + true + + + true + Base + true + + + true + Base + true + + + true + Base + true + + + true + Base + true + + + true + Base + true + + + true + Base + true + + + true + Cfg_1 + true + true + + + true + Cfg_1 + true + true + + + true + Base + true + + + .\lib\$(Platform)\$(Config) + .\bin + false + false + false + false + false + RESTComponents;emsclientfiredac;DataSnapFireDAC;FireDACIBDriver;emsclient;FireDACCommon;RESTBackendComponents;soapserver;CloudService;FireDACCommonDriver;inet;FireDAC;FireDACSqliteDriver;soaprtl;soapmidas;$(DCC_UsePackage) + System;Xml;Data;Datasnap;Web;Soap;$(DCC_Namespace) + UniDACBasicServerDaemon + + + DBXSqliteDriver;DBXInterBaseDriver;tethering;bindcompfmx;FmxTeeUI;fmx;RadiantShapesFmx;FireDACDBXDriver;dbexpress;IndyCore;dsnap;DataSnapCommon;bindengine;DataSnapClient;bindcompdbx;IndyIPCommon;IndyIPServer;IndySystem;fmxFireDAC;ibmonitor;FMXTee;DbxCommonDriver;ibxpress;xmlrtl;DataSnapNativeClient;ibxbindings;rtl;FireDACDSDriver;DbxClientDriver;CustomIPTransport;bindcomp;IndyIPClient;dbxcds;dsnapxml;DataSnapProviderClient;dbrtl;IndyProtocols;$(DCC_UsePackage) + $(BDS)\bin\Artwork\Android\FM_LauncherIcon_36x36.png + $(BDS)\bin\Artwork\Android\FM_LauncherIcon_48x48.png + $(BDS)\bin\Artwork\Android\FM_LauncherIcon_72x72.png + $(BDS)\bin\Artwork\Android\FM_LauncherIcon_96x96.png + $(BDS)\bin\Artwork\Android\FM_LauncherIcon_144x144.png + $(BDS)\bin\Artwork\Android\FM_SplashImage_426x320.png + $(BDS)\bin\Artwork\Android\FM_SplashImage_470x320.png + $(BDS)\bin\Artwork\Android\FM_SplashImage_640x480.png + $(BDS)\bin\Artwork\Android\FM_SplashImage_960x720.png + android-support-v4.dex.jar;cloud-messaging.dex.jar;fmx.dex.jar;google-analytics-v2.dex.jar;google-play-billing.dex.jar;google-play-licensing.dex.jar;google-play-services.dex.jar + $(BDS)\bin\Artwork\Android\FM_NotificationIcon_24x24.png + $(BDS)\bin\Artwork\Android\FM_NotificationIcon_36x36.png + $(BDS)\bin\Artwork\Android\FM_NotificationIcon_48x48.png + $(BDS)\bin\Artwork\Android\FM_NotificationIcon_72x72.png + $(BDS)\bin\Artwork\Android\FM_NotificationIcon_96x96.png + + + DBXSqliteDriver;DBXInterBaseDriver;tethering;bindcompfmx;FmxTeeUI;fmx;RadiantShapesFmx;FireDACDBXDriver;dbexpress;IndyCore;dsnap;DataSnapCommon;bindengine;DataSnapClient;bindcompdbx;IndyIPCommon;IndyIPServer;IndySystem;fmxFireDAC;ibmonitor;FMXTee;DbxCommonDriver;ibxpress;xmlrtl;DataSnapNativeClient;ibxbindings;rtl;FireDACDSDriver;DbxClientDriver;CustomIPTransport;bindcomp;IndyIPClient;dbxcds;dsnapxml;DataSnapProviderClient;dbrtl;IndyProtocols;fmxase;$(DCC_UsePackage) + + + DBXSqliteDriver;DBXInterBaseDriver;tethering;bindcompfmx;FmxTeeUI;fmx;RadiantShapesFmx;FireDACDBXDriver;dbexpress;IndyCore;dsnap;DataSnapCommon;bindengine;DataSnapClient;bindcompdbx;IndyIPCommon;IndyIPServer;IndySystem;fmxFireDAC;ibmonitor;FMXTee;DbxCommonDriver;ibxpress;xmlrtl;DataSnapNativeClient;ibxbindings;rtl;FireDACDSDriver;DbxClientDriver;CustomIPTransport;bindcomp;IndyIPClient;dbxcds;dsnapxml;DataSnapProviderClient;dbrtl;IndyProtocols;fmxase;$(DCC_UsePackage) + + + DBXSqliteDriver;DBXInterBaseDriver;tethering;bindcompfmx;FmxTeeUI;fmx;RadiantShapesFmx;FireDACDBXDriver;dbexpress;IndyCore;dsnap;DataSnapCommon;bindengine;DataSnapClient;bindcompdbx;IndyIPCommon;IndyIPServer;IndySystem;fmxFireDAC;ibmonitor;FMXTee;DbxCommonDriver;ibxpress;xmlrtl;DataSnapNativeClient;ibxbindings;rtl;FireDACDSDriver;DbxClientDriver;CustomIPTransport;bindcomp;IndyIPClient;dbxcds;dsnapxml;DataSnapProviderClient;dbrtl;IndyProtocols;fmxase;$(DCC_UsePackage) + + + DataSnapServerMidas;FireDACADSDriver;DatasnapConnectorsFreePascal;FireDACMSSQLDriver;inetdb;emsedge;dbexpress;IndyCore;dsnap;DataSnapCommon;DataSnapConnectors;MARS.Utils;bindengine;FireDACOracleDriver;FireDACMySQLDriver;FireDACCommonODBC;DataSnapClient;IndySystem;FireDACDb2Driver;FireDACInfxDriver;emshosting;FireDACPgDriver;FireDACASADriver;FireDACTDataDriver;DbxCommonDriver;DataSnapServer;xmlrtl;DataSnapNativeClient;rtl;DbxClientDriver;CustomIPTransport;bindcomp;dbxcds;FireDACODBCDriver;DataSnapIndy10ServerTransport;dsnapxml;dbrtl;FireDACMongoDBDriver;IndyProtocols;$(DCC_UsePackage) + /usr/bin/xterm -e "%debuggee%" + UniDACBasicServer_Icon.ico + (None) + + + DBXSqliteDriver;DataSnapServerMidas;DBXInterBaseDriver;tethering;FireDACMSSQLDriver;bindcompfmx;DBXOracleDriver;inetdb;FmxTeeUI;emsedge;fmx;fmxdae;RadiantShapesFmx;FireDACDBXDriver;dbexpress;IndyCore;dsnap;DataSnapCommon;bindengine;DBXMySQLDriver;FireDACOracleDriver;FireDACMySQLDriver;DBXFirebirdDriver;FireDACCommonODBC;DataSnapClient;bindcompdbx;IndyIPCommon;IndyIPServer;IndySystem;fmxFireDAC;emshosting;FireDACPgDriver;ibmonitor;FireDACASADriver;FireDACTDataDriver;FMXTee;DbxCommonDriver;ibxpress;DataSnapServer;xmlrtl;DataSnapNativeClient;fmxobj;ibxbindings;rtl;FireDACDSDriver;DbxClientDriver;DBXSybaseASADriver;CustomIPTransport;bindcomp;DBXInformixDriver;IndyIPClient;dbxcds;FireDACODBCDriver;DataSnapIndy10ServerTransport;dsnapxml;DataSnapProviderClient;dbrtl;inetdbxpress;FireDACMongoDBDriver;IndyProtocols;fmxase;$(DCC_UsePackage) + true + + + CFBundleName=$(MSBuildProjectName);CFBundleDisplayName=$(MSBuildProjectName);CFBundleIdentifier=$(MSBuildProjectName);CFBundleVersion=1.0.0;CFBundleShortVersionString=1.0.0;CFBundlePackageType=APPL;CFBundleSignature=????;CFBundleAllowMixedLocalizations=YES;CFBundleExecutable=$(MSBuildProjectName);NSHighResolutionCapable=true;LSApplicationCategoryType=public.app-category.utilities;NSLocationUsageDescription=The reason for accessing the location information of the user;NSContactsUsageDescription=The reason for accessing the contacts + Debug + true + Base + true + DBXSqliteDriver;DataSnapServerMidas;DBXInterBaseDriver;tethering;FireDACMSSQLDriver;bindcompfmx;DBXOracleDriver;inetdb;FmxTeeUI;emsedge;fmx;fmxdae;RadiantShapesFmx;FireDACDBXDriver;dbexpress;IndyCore;dsnap;DataSnapCommon;bindengine;DBXMySQLDriver;FireDACOracleDriver;FireDACMySQLDriver;DBXFirebirdDriver;FireDACCommonODBC;DataSnapClient;bindcompdbx;IndyIPCommon;IndyIPServer;IndySystem;fmxFireDAC;emshosting;FireDACPgDriver;ibmonitor;FireDACASADriver;FireDACTDataDriver;FMXTee;DbxCommonDriver;ibxpress;DataSnapServer;xmlrtl;DataSnapNativeClient;fmxobj;ibxbindings;rtl;FireDACDSDriver;DbxClientDriver;DBXSybaseASADriver;CustomIPTransport;bindcomp;DBXInformixDriver;IndyIPClient;dbxcds;FireDACODBCDriver;DataSnapIndy10ServerTransport;dsnapxml;DataSnapProviderClient;dbrtl;inetdbxpress;FireDACMongoDBDriver;IndyProtocols;fmxase;$(DCC_UsePackage);$(DCC_UsePackage) + true + + + DBXSqliteDriver;DataSnapServerMidas;DBXDb2Driver;DBXInterBaseDriver;vclactnband;vclFireDAC;svnui;tethering;FireDACADSDriver;DBXMSSQLDriver;DatasnapConnectorsFreePascal;FireDACMSSQLDriver;vcltouch;vcldb;bindcompfmx;svn;Intraweb;DBXOracleDriver;inetdb;FmxTeeUI;emsedge;fmx;fmxdae;RadiantShapesFmx;vclib;FireDACDBXDriver;dbexpress;IndyCore;vclx;dsnap;DataSnapCommon;DataSnapConnectors;MARS.Utils;VCLRESTComponents;vclie;bindengine;DBXMySQLDriver;FireDACOracleDriver;FireDACMySQLDriver;DBXFirebirdDriver;FireDACCommonODBC;DataSnapClient;bindcompdbx;IndyIPCommon;vcl;DBXSybaseASEDriver;IndyIPServer;IndySystem;FireDACDb2Driver;dsnapcon;FireDACMSAccDriver;fmxFireDAC;FireDACInfxDriver;vclimg;TeeDB;emshosting;FireDACPgDriver;ibmonitor;FireDACASADriver;DBXOdbcDriver;FireDACTDataDriver;FMXTee;DbxCommonDriver;ibxpress;Tee;DataSnapServer;xmlrtl;DataSnapNativeClient;fmxobj;vclwinx;ibxbindings;rtl;FireDACDSDriver;DbxClientDriver;DBXSybaseASADriver;CustomIPTransport;vcldsnap;CodeSiteExpressPkg;bindcomp;appanalytics;DBXInformixDriver;IndyIPClient;bindcompvcl;TeeUI;dbxcds;VclSmp;adortl;FireDACODBCDriver;RadiantShapesFmx_Design;DataSnapIndy10ServerTransport;dsnapxml;DataSnapProviderClient;dbrtl;inetdbxpress;FireDACMongoDBDriver;IndyProtocols;fmxase;$(DCC_UsePackage) + Winapi;System.Win;Data.Win;Datasnap.Win;Web.Win;Soap.Win;Xml.Win;Bde;$(DCC_Namespace) + Debug + CompanyName=;FileDescription=$(MSBuildProjectName);FileVersion=1.0.0.0;InternalName=;LegalCopyright=;LegalTrademarks=;OriginalFilename=;ProgramID=com.embarcadero.$(MSBuildProjectName);ProductName=$(MSBuildProjectName);ProductVersion=1.0.0.0;Comments= + 1033 + true + $(BDS)\bin\Artwork\Windows\UWP\delphi_UwpDefault_44.png + $(BDS)\bin\Artwork\Windows\UWP\delphi_UwpDefault_150.png + + + DBXSqliteDriver;DataSnapServerMidas;DBXDb2Driver;DBXInterBaseDriver;vclactnband;vclFireDAC;tethering;FireDACADSDriver;DBXMSSQLDriver;DatasnapConnectorsFreePascal;FireDACMSSQLDriver;vcltouch;vcldb;bindcompfmx;Intraweb;DBXOracleDriver;inetdb;FmxTeeUI;emsedge;fmx;fmxdae;RadiantShapesFmx;vclib;FireDACDBXDriver;dbexpress;IndyCore;vclx;dsnap;DataSnapCommon;DataSnapConnectors;MARS.Utils;VCLRESTComponents;vclie;bindengine;DBXMySQLDriver;FireDACOracleDriver;FireDACMySQLDriver;DBXFirebirdDriver;FireDACCommonODBC;DataSnapClient;bindcompdbx;IndyIPCommon;vcl;DBXSybaseASEDriver;IndyIPServer;IndySystem;FireDACDb2Driver;dsnapcon;FireDACMSAccDriver;fmxFireDAC;FireDACInfxDriver;vclimg;TeeDB;emshosting;FireDACPgDriver;ibmonitor;FireDACASADriver;DBXOdbcDriver;FireDACTDataDriver;FMXTee;DbxCommonDriver;ibxpress;Tee;DataSnapServer;xmlrtl;DataSnapNativeClient;fmxobj;vclwinx;ibxbindings;rtl;FireDACDSDriver;DbxClientDriver;DBXSybaseASADriver;CustomIPTransport;vcldsnap;bindcomp;appanalytics;DBXInformixDriver;IndyIPClient;bindcompvcl;TeeUI;dbxcds;VclSmp;adortl;FireDACODBCDriver;DataSnapIndy10ServerTransport;dsnapxml;DataSnapProviderClient;dbrtl;inetdbxpress;FireDACMongoDBDriver;IndyProtocols;fmxase;$(DCC_UsePackage) + true + $(BDS)\bin\Artwork\Windows\UWP\delphi_UwpDefault_44.png + $(BDS)\bin\Artwork\Windows\UWP\delphi_UwpDefault_150.png + + + DEBUG;$(DCC_Define) + true + false + true + true + true + + + /usr/bin/xterm -e "%debuggee%" + + + false + + + false + RELEASE;$(DCC_Define) + 0 + 0 + + + + MainSource + + + + +
ServerWebModule
+ TWebModule +
+ + Cfg_2 + Base + + + Base + + + Cfg_1 + Base + +
+ + Delphi.Personality.12 + Application + + + + UniDACBasicServerDaemon.dpr + + + Microsoft Office 2000 Sample Automation Server Wrapper Components + Microsoft Office XP Sample Automation Server Wrapper Components + + + + + + UniDACBasicServerDaemon + true + + + + + true + + + + + true + + + + + true + + + + + true + + + + + true + + + + + true + + + + + 1 + + + Contents\MacOS + 1 + + + 0 + + + + + classes + 1 + + + + + res\xml + 1 + + + + + library\lib\armeabi-v7a + 1 + + + + + library\lib\armeabi + 1 + + + + + library\lib\mips + 1 + + + + + library\lib\armeabi-v7a + 1 + + + + + res\drawable + 1 + + + + + res\values + 1 + + + + + res\values-v21 + 1 + + + + + res\values + 1 + + + + + res\drawable + 1 + + + + + res\drawable-xxhdpi + 1 + + + + + res\drawable-ldpi + 1 + + + + + res\drawable-mdpi + 1 + + + + + res\drawable-hdpi + 1 + + + + + res\drawable-xhdpi + 1 + + + + + res\drawable-mdpi + 1 + + + + + res\drawable-hdpi + 1 + + + + + res\drawable-xhdpi + 1 + + + + + res\drawable-xxhdpi + 1 + + + + + res\drawable-xxxhdpi + 1 + + + + + res\drawable-small + 1 + + + + + res\drawable-normal + 1 + + + + + res\drawable-large + 1 + + + + + res\drawable-xlarge + 1 + + + + + res\values + 1 + + + + + 1 + + + Contents\MacOS + 1 + + + 0 + + + + + Contents\MacOS + 1 + .framework + + + Contents\MacOS + 1 + .framework + + + 0 + + + + + 1 + .dylib + + + 1 + .dylib + + + 1 + .dylib + + + Contents\MacOS + 1 + .dylib + + + Contents\MacOS + 1 + .dylib + + + 0 + .dll;.bpl + + + + + 1 + .dylib + + + 1 + .dylib + + + 1 + .dylib + + + Contents\MacOS + 1 + .dylib + + + Contents\MacOS + 1 + .dylib + + + 0 + .bpl + + + + + 0 + + + 0 + + + 0 + + + 0 + + + Contents\Resources\StartUp\ + 0 + + + Contents\Resources\StartUp\ + 0 + + + 0 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + + + ..\$(PROJECTNAME).app.dSYM\Contents\Resources\DWARF + 1 + + + ..\$(PROJECTNAME).app.dSYM\Contents\Resources\DWARF + 1 + + + + + 1 + + + 1 + + + + + ..\ + 1 + + + ..\ + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + ..\$(PROJECTNAME).app.dSYM\Contents\Resources\DWARF + 1 + + + + + ..\ + 1 + + + ..\ + 1 + + + + + Contents + 1 + + + Contents + 1 + + + + + Contents\Resources + 1 + + + Contents\Resources + 1 + + + + + library\lib\armeabi-v7a + 1 + + + 1 + + + 1 + + + 1 + + + 1 + + + Contents\MacOS + 1 + + + Contents\MacOS + 1 + + + 0 + + + + + 1 + + + 1 + + + + + Assets + 1 + + + Assets + 1 + + + + + Assets + 1 + + + Assets + 1 + + + + + + + + + + + + + + False + False + False + False + True + False + False + False + False + + + 12 + + + + +
diff --git a/Demos/UniDAC Basic/UniDACBasicServerFMXApplication.deployproj b/Demos/UniDAC Basic/UniDACBasicServerFMXApplication.deployproj new file mode 100644 index 00000000..25ae74ba --- /dev/null +++ b/Demos/UniDAC Basic/UniDACBasicServerFMXApplication.deployproj @@ -0,0 +1,132 @@ + + + + 12 + + + + + + iPhone5 + + + + + + + UniDACBasicServerFMXApplication\ + UniDACBasicServerFMXApplication.exe + ProjectOutput + 0 + + + True + True + + + + + UniDACBasicServerFMXApplication.app\Assets\ + Logo44x44.png + UWP_DelphiLogo44 + 0 + + + True + + + UniDACBasicServerFMXApplication.app\Contents\MacOS\ + libcgunwind.1.0.dylib + DependencyModule + 1 + + + True + + + UniDACBasicServerFMXApplication.app\Contents\MacOS\ + UniDACBasicServerFMXApplication.rsm + DebugSymbols + 1 + + + True + + + UniDACBasicServerFMXApplication.app\..\ + UniDACBasicServerFMXApplication.entitlements + ProjectOSXEntitlements + 1 + + + True + + + UniDACBasicServerFMXApplication.app\Contents\Resources\ + UniDACBasicServerFMXApplication.icns + ProjectOSXResource + 1 + + + True + + + UniDACBasicServerFMXApplication.app\Contents\ + Info.plist + ProjectOSXInfoPList + 1 + + + True + + + UniDACBasicServerFMXApplication.app\Contents\MacOS\ + libcgsqlite3.dylib + DependencyModule + 1 + + + True + + + UniDACBasicServerFMXApplication.app\Assets\ + Logo150x150.png + UWP_DelphiLogo150 + 0 + + + True + + + UniDACBasicServerFMXApplication.app\Contents\MacOS\ + UniDACBasicServerFMXApplication + ProjectOutput + 1 + + + True + True + + + + + + UniDACBasicServerFMXApplication.app\ + libPCRE.dylib + DependencyModule + 1 + + + True + + + UniDACBasicServerFMXApplication.app\ + libcgunwind.1.0.dylib + DependencyModule + 1 + + + True + + + diff --git a/Demos/UniDAC Basic/UniDACBasicServerFMXApplication.dpr b/Demos/UniDAC Basic/UniDACBasicServerFMXApplication.dpr new file mode 100644 index 00000000..ad31a42c --- /dev/null +++ b/Demos/UniDAC Basic/UniDACBasicServerFMXApplication.dpr @@ -0,0 +1,21 @@ +(* + Copyright 2016, MARS-Curiosity - REST Library + + Home: https://github.com/andrea-magni/MARS +*) +program UniDACBasicServerFMXApplication; + +uses + System.StartUpCopy, + FMX.Forms, + Server.FMX.Forms.Main in 'Server.FMX.Forms.Main.pas' {MainForm}, + Server.Ignition in 'Server.Ignition.pas', + Server.Resources in 'Server.Resources.pas'; + +{$R *.res} + +begin + Application.Initialize; + Application.CreateForm(TMainForm, MainForm); + Application.Run; +end. diff --git a/Demos/UniDAC Basic/UniDACBasicServerFMXApplication.dproj b/Demos/UniDAC Basic/UniDACBasicServerFMXApplication.dproj new file mode 100644 index 00000000..b050412b --- /dev/null +++ b/Demos/UniDAC Basic/UniDACBasicServerFMXApplication.dproj @@ -0,0 +1,1212 @@ + + + {2DC28130-9EB1-48C8-9CA6-0F3CFD5D8E1D} + UniDACBasicServerFMXApplication.dpr + True + Release + 5269 + Application + FMX + 18.7 + Win32 + + + true + + + true + Base + true + + + true + Base + true + + + true + Base + true + + + true + Base + true + + + true + Base + true + + + true + Base + true + + + true + Base + true + + + true + Base + true + + + true + Base + true + + + true + Base + true + + + true + Cfg_1 + true + true + + + true + Cfg_1 + true + true + + + true + Cfg_1 + true + true + + + true + Cfg_1 + true + true + + + true + Base + true + + + true + Cfg_2 + true + true + + + true + Cfg_2 + true + true + + + true + Cfg_2 + true + true + + + true + Cfg_2 + true + true + + + true + Cfg_2 + true + true + + + true + Cfg_2 + true + true + + + false + false + false + false + false + 00400000 + UniDACBasicServerFMXApplication + 1040 + CompanyName=;FileDescription=;FileVersion=1.0.0.0;InternalName=;LegalCopyright=;LegalTrademarks=;OriginalFilename=;ProductName=;ProductVersion=1.0.0.0;Comments=;CFBundleName= + System;Xml;Data;Datasnap;Web;Soap;$(DCC_Namespace) + $(BDS)\bin\delphi_PROJECTICON.ico + $(BDS)\bin\delphi_PROJECTICNS.icns + .\bin + .\lib\$(Platform)\$(Config) + + + package=com.embarcadero.$(MSBuildProjectName);label=$(MSBuildProjectName);versionCode=1;versionName=1.0.0;persistent=False;restoreAnyVersion=False;installLocation=auto;largeHeap=False;theme=TitleBar;hardwareAccelerated=true;apiKey= + Debug + true + $(BDS)\bin\Artwork\Android\FM_LauncherIcon_36x36.png + $(BDS)\bin\Artwork\Android\FM_LauncherIcon_48x48.png + $(BDS)\bin\Artwork\Android\FM_LauncherIcon_72x72.png + $(BDS)\bin\Artwork\Android\FM_LauncherIcon_96x96.png + $(BDS)\bin\Artwork\Android\FM_LauncherIcon_144x144.png + $(BDS)\bin\Artwork\Android\FM_SplashImage_426x320.png + $(BDS)\bin\Artwork\Android\FM_SplashImage_470x320.png + $(BDS)\bin\Artwork\Android\FM_SplashImage_640x480.png + $(BDS)\bin\Artwork\Android\FM_SplashImage_960x720.png + true + true + true + true + true + true + true + true + true + true + android-support-v4.dex.jar;cloud-messaging.dex.jar;fmx.dex.jar;google-analytics-v2.dex.jar;google-play-billing.dex.jar;google-play-licensing.dex.jar;google-play-services.dex.jar + 1 + $(BDS)\bin\Artwork\Android\FM_NotificationIcon_24x24.png + $(BDS)\bin\Artwork\Android\FM_NotificationIcon_36x36.png + $(BDS)\bin\Artwork\Android\FM_NotificationIcon_48x48.png + $(BDS)\bin\Artwork\Android\FM_NotificationIcon_72x72.png + $(BDS)\bin\Artwork\Android\FM_NotificationIcon_96x96.png + + + CFBundleName=$(MSBuildProjectName);CFBundleDevelopmentRegion=en;CFBundleDisplayName=$(MSBuildProjectName);CFBundleIdentifier=$(MSBuildProjectName);CFBundleInfoDictionaryVersion=7.1;CFBundleVersion=1.0.0.0;CFBundlePackageType=APPL;CFBundleSignature=????;LSRequiresIPhoneOS=true;CFBundleAllowMixedLocalizations=YES;CFBundleExecutable=$(MSBuildProjectName);UIDeviceFamily=iPhone & iPad;CFBundleResourceSpecification=ResourceRules.plist;NSLocationAlwaysUsageDescription=The reason for accessing the location information of the user;NSLocationWhenInUseUsageDescription=The reason for accessing the location information of the user;FMLocalNotificationPermission=false;UIBackgroundModes=;NSContactsUsageDescription=The reason for accessing the contacts;NSPhotoLibraryUsageDescription=The reason for accessing the photo library;NSCameraUsageDescription=The reason for accessing the camera + iPhoneAndiPad + true + Debug + $(MSBuildProjectName) + $(BDS)\bin\Artwork\iOS\iPhone\FM_ApplicationIcon_60x60.png + $(BDS)\bin\Artwork\iOS\iPhone\FM_ApplicationIcon_120x120.png + $(BDS)\bin\Artwork\iOS\iPhone\FM_SpotlightSearchIcon_40x40.png + $(BDS)\bin\Artwork\iOS\iPhone\FM_SpotlightSearchIcon_80x80.png + $(BDS)\bin\Artwork\iOS\iPad\FM_SpotlightSearchIcon_40x40.png + $(BDS)\bin\Artwork\iOS\iPad\FM_SpotlightSearchIcon_80x80.png + $(BDS)\bin\Artwork\iOS\iPad\FM_ApplicationIcon_76x76.png + $(BDS)\bin\Artwork\iOS\iPad\FM_ApplicationIcon_152x152.png + $(BDS)\bin\Artwork\iOS\iPad\FM_LaunchImagePortrait_768x1024.png + $(BDS)\bin\Artwork\iOS\iPad\FM_LaunchImageLandscape_1024x768.png + $(BDS)\bin\Artwork\iOS\iPad\FM_LaunchImagePortrait_1536x2048.png + $(BDS)\bin\Artwork\iOS\iPad\FM_LaunchImageLandscape_2048x1536.png + $(BDS)\bin\Artwork\iOS\iPhone\FM_ApplicationIcon_87x87.png + $(BDS)\bin\Artwork\iOS\iPhone\FM_ApplicationIcon_180x180.png + $(BDS)\bin\Artwork\iOS\iPhone\FM_LaunchImage_750x1334.png + $(BDS)\bin\Artwork\iOS\iPhone\FM_LaunchImage_1242x2208.png + $(BDS)\bin\Artwork\iOS\iPhone\FM_LaunchImage_2208x1242.png + $(BDS)\bin\Artwork\iOS\iPhone\FM_LaunchImage_1125x2436.png + $(BDS)\bin\Artwork\iOS\iPhone\FM_LaunchImage_2436x1125.png + $(BDS)\bin\Artwork\iOS\iPhone\FM_SpotlightSearchIcon_120x120.png + $(BDS)\bin\Artwork\iOS\iPhone\FM_LaunchImage_828x1792.png + $(BDS)\bin\Artwork\iOS\iPhone\FM_LaunchImage_1136x640.png + $(BDS)\bin\Artwork\iOS\iPhone\FM_LaunchImage_1242x2688.png + $(BDS)\bin\Artwork\iOS\iPhone\FM_LaunchImage_1334x750.png + $(BDS)\bin\Artwork\iOS\iPhone\FM_LaunchImage_1792x828.png + $(BDS)\bin\Artwork\iOS\iPhone\FM_LaunchImage_2688x1242.png + $(BDS)\bin\Artwork\iOS\iPad\FM_ApplicationIcon_167x167.png + $(BDS)\bin\Artwork\iOS\iPad\FM_LaunchImagePortrait_1668x2224.png + $(BDS)\bin\Artwork\iOS\iPad\FM_LaunchImagePortrait_1668x2388.png + $(BDS)\bin\Artwork\iOS\iPad\FM_LaunchImagePortrait_2048x2732.png + $(BDS)\bin\Artwork\iOS\iPad\FM_LaunchImageLandscape_2224x1668.png + $(BDS)\bin\Artwork\iOS\iPad\FM_LaunchImageLandscape_2388x1668.png + $(BDS)\bin\Artwork\iOS\iPad\FM_LaunchImageLandscape_2732x2048.png + + + $(BDS)\bin\Artwork\iOS\iPhone\FM_ApplicationIcon_87x87.png + $(BDS)\bin\Artwork\iOS\iPhone\FM_ApplicationIcon_180x180.png + $(BDS)\bin\Artwork\iOS\iPhone\FM_LaunchImage_750x1334.png + $(BDS)\bin\Artwork\iOS\iPhone\FM_LaunchImage_1242x2208.png + $(BDS)\bin\Artwork\iOS\iPhone\FM_LaunchImage_2208x1242.png + $(BDS)\bin\Artwork\iOS\iPhone\FM_LaunchImage_1125x2436.png + $(BDS)\bin\Artwork\iOS\iPhone\FM_LaunchImage_2436x1125.png + $(BDS)\bin\Artwork\iOS\iPhone\FM_SpotlightSearchIcon_120x120.png + $(BDS)\bin\Artwork\iOS\iPhone\FM_LaunchImage_828x1792.png + $(BDS)\bin\Artwork\iOS\iPhone\FM_LaunchImage_1136x640.png + $(BDS)\bin\Artwork\iOS\iPhone\FM_LaunchImage_1242x2688.png + $(BDS)\bin\Artwork\iOS\iPhone\FM_LaunchImage_1334x750.png + $(BDS)\bin\Artwork\iOS\iPhone\FM_LaunchImage_1792x828.png + $(BDS)\bin\Artwork\iOS\iPhone\FM_LaunchImage_2688x1242.png + $(BDS)\bin\Artwork\iOS\iPad\FM_ApplicationIcon_167x167.png + $(BDS)\bin\Artwork\iOS\iPad\FM_LaunchImagePortrait_1668x2224.png + $(BDS)\bin\Artwork\iOS\iPad\FM_LaunchImagePortrait_1668x2388.png + $(BDS)\bin\Artwork\iOS\iPad\FM_LaunchImagePortrait_2048x2732.png + $(BDS)\bin\Artwork\iOS\iPad\FM_LaunchImageLandscape_2224x1668.png + $(BDS)\bin\Artwork\iOS\iPad\FM_LaunchImageLandscape_2388x1668.png + $(BDS)\bin\Artwork\iOS\iPad\FM_LaunchImageLandscape_2732x2048.png + + + CFBundleName=$(MSBuildProjectName);CFBundleDevelopmentRegion=en;CFBundleDisplayName=$(MSBuildProjectName);CFBundleIdentifier=$(MSBuildProjectName);CFBundleInfoDictionaryVersion=7.1;CFBundleVersion=1.0.0.0;CFBundlePackageType=APPL;CFBundleSignature=????;LSRequiresIPhoneOS=true;CFBundleAllowMixedLocalizations=YES;CFBundleExecutable=$(MSBuildProjectName);UIDeviceFamily=iPhone & iPad;CFBundleResourceSpecification=ResourceRules.plist;NSLocationAlwaysUsageDescription=The reason for accessing the location information of the user;NSLocationWhenInUseUsageDescription=The reason for accessing the location information of the user;FMLocalNotificationPermission=false;UIBackgroundModes=;NSContactsUsageDescription=The reason for accessing the contacts;NSPhotoLibraryUsageDescription=The reason for accessing the photo library;NSCameraUsageDescription=The reason for accessing the camera + iPhoneAndiPad + true + $(BDS)\bin\Artwork\iOS\iPhone\FM_ApplicationIcon_60x60.png + $(BDS)\bin\Artwork\iOS\iPhone\FM_ApplicationIcon_120x120.png + $(BDS)\bin\Artwork\iOS\iPhone\FM_SpotlightSearchIcon_40x40.png + $(BDS)\bin\Artwork\iOS\iPhone\FM_SpotlightSearchIcon_80x80.png + $(BDS)\bin\Artwork\iOS\iPad\FM_SpotlightSearchIcon_40x40.png + $(BDS)\bin\Artwork\iOS\iPad\FM_SpotlightSearchIcon_80x80.png + $(BDS)\bin\Artwork\iOS\iPad\FM_ApplicationIcon_76x76.png + $(BDS)\bin\Artwork\iOS\iPad\FM_ApplicationIcon_152x152.png + $(BDS)\bin\Artwork\iOS\iPad\FM_LaunchImagePortrait_768x1024.png + $(BDS)\bin\Artwork\iOS\iPad\FM_LaunchImageLandscape_1024x768.png + $(BDS)\bin\Artwork\iOS\iPad\FM_LaunchImagePortrait_1536x2048.png + $(BDS)\bin\Artwork\iOS\iPad\FM_LaunchImageLandscape_2048x1536.png + $(BDS)\bin\Artwork\iOS\iPhone\FM_ApplicationIcon_87x87.png + $(BDS)\bin\Artwork\iOS\iPhone\FM_ApplicationIcon_180x180.png + $(BDS)\bin\Artwork\iOS\iPhone\FM_LaunchImage_750x1334.png + $(BDS)\bin\Artwork\iOS\iPhone\FM_LaunchImage_1242x2208.png + $(BDS)\bin\Artwork\iOS\iPhone\FM_LaunchImage_2208x1242.png + $(BDS)\bin\Artwork\iOS\iPhone\FM_LaunchImage_1125x2436.png + $(BDS)\bin\Artwork\iOS\iPhone\FM_LaunchImage_2436x1125.png + $(BDS)\bin\Artwork\iOS\iPhone\FM_SpotlightSearchIcon_120x120.png + $(BDS)\bin\Artwork\iOS\iPhone\FM_LaunchImage_828x1792.png + $(BDS)\bin\Artwork\iOS\iPhone\FM_LaunchImage_1136x640.png + $(BDS)\bin\Artwork\iOS\iPhone\FM_LaunchImage_1242x2688.png + $(BDS)\bin\Artwork\iOS\iPhone\FM_LaunchImage_1334x750.png + $(BDS)\bin\Artwork\iOS\iPhone\FM_LaunchImage_1792x828.png + $(BDS)\bin\Artwork\iOS\iPhone\FM_LaunchImage_2688x1242.png + $(BDS)\bin\Artwork\iOS\iPad\FM_ApplicationIcon_167x167.png + $(BDS)\bin\Artwork\iOS\iPad\FM_LaunchImagePortrait_1668x2224.png + $(BDS)\bin\Artwork\iOS\iPad\FM_LaunchImagePortrait_1668x2388.png + $(BDS)\bin\Artwork\iOS\iPad\FM_LaunchImagePortrait_2048x2732.png + $(BDS)\bin\Artwork\iOS\iPad\FM_LaunchImageLandscape_2224x1668.png + $(BDS)\bin\Artwork\iOS\iPad\FM_LaunchImageLandscape_2388x1668.png + $(BDS)\bin\Artwork\iOS\iPad\FM_LaunchImageLandscape_2732x2048.png + + + /usr/bin/xterm -e "%debuggee%" + (None) + UniDACBasicServer_Icon.ico + + + CFBundleName=$(MSBuildProjectName);CFBundleDisplayName=$(MSBuildProjectName);CFBundleIdentifier=$(MSBuildProjectName);CFBundleVersion=1.0.0;CFBundlePackageType=APPL;CFBundleSignature=????;CFBundleAllowMixedLocalizations=YES;CFBundleExecutable=$(MSBuildProjectName);NSHighResolutionCapable=true;LSApplicationCategoryType=public.app-category.utilities;NSContactsUsageDescription=The reason for accessing the contacts;CFBundleShortVersionString=1.0.0;NSLocationUsageDescription=The reason for accessing the location information of the user + Debug + true + /usr/X11/bin/xterm -e "%debuggee%" + (None) + UniDACBasicServer_Icon.ico + + + CFBundleName=$(MSBuildProjectName);CFBundleDisplayName=$(MSBuildProjectName);CFBundleIdentifier=$(MSBuildProjectName);CFBundleVersion=1.0.0;CFBundlePackageType=APPL;CFBundleSignature=????;CFBundleAllowMixedLocalizations=YES;CFBundleExecutable=$(MSBuildProjectName);NSHighResolutionCapable=true;LSApplicationCategoryType=public.app-category.utilities;NSContactsUsageDescription=The reason for accessing the contacts;CFBundleShortVersionString=1.0.0;NSLocationUsageDescription=The reason for accessing the location information of the user + Debug + true + true + Base + true + /usr/X11/bin/xterm -e "%debuggee%" + (None) + UniDACBasicServer_Icon.ico + + + Winapi;System.Win;Data.Win;Datasnap.Win;Web.Win;Soap.Win;Xml.Win;Bde;$(DCC_Namespace) + Debug + true + CompanyName=;FileDescription=$(MSBuildProjectName);FileVersion=1.0.0.0;InternalName=;LegalCopyright=;LegalTrademarks=;OriginalFilename=;ProductName=$(MSBuildProjectName);ProductVersion=1.0.0.0;Comments=;ProgramID=com.embarcadero.$(MSBuildProjectName) + 1033 + $(BDS)\bin\default_app.manifest + UniDACBasicServer_Icon.ico + true + $(BDS)\bin\Artwork\Windows\UWP\delphi_UwpDefault_44.png + $(BDS)\bin\Artwork\Windows\UWP\delphi_UwpDefault_150.png + + + $(BDS)\bin\default_app.manifest + UniDACBasicServerFMXApplication_Icon.ico + true + $(BDS)\bin\Artwork\Windows\UWP\delphi_UwpDefault_44.png + $(BDS)\bin\Artwork\Windows\UWP\delphi_UwpDefault_150.png + + + RELEASE;$(DCC_Define) + 0 + false + 0 + + + Debug + + + true + CFBundleName=$(MSBuildProjectName);CFBundleDisplayName=$(MSBuildProjectName);CFBundleIdentifier=$(MSBuildProjectName);CFBundleVersion=1.0.0;CFBundlePackageType=APPL;CFBundleSignature=????;CFBundleAllowMixedLocalizations=YES;CFBundleExecutable=$(MSBuildProjectName);NSHighResolutionCapable=true;LSApplicationCategoryType=public.app-category.utilities;NSContactsUsageDescription=The reason for accessing the contacts;CFBundleShortVersionString=1.0.0;NSLocationUsageDescription=The reason for accessing the location information of the user + + + true + Cfg_1 + true + true + CFBundleName=$(MSBuildProjectName);CFBundleDisplayName=$(MSBuildProjectName);CFBundleIdentifier=$(MSBuildProjectName);CFBundleVersion=1.0.0;CFBundlePackageType=APPL;CFBundleSignature=????;CFBundleAllowMixedLocalizations=YES;CFBundleExecutable=$(MSBuildProjectName);NSHighResolutionCapable=true;LSApplicationCategoryType=public.app-category.utilities;NSContactsUsageDescription=The reason for accessing the contacts;CFBundleShortVersionString=1.0.0;NSLocationUsageDescription=The reason for accessing the location information of the user + + + true + true + 1033 + CompanyName=;FileDescription=$(MSBuildProjectName);FileVersion=1.0.0.0;InternalName=;LegalCopyright=;LegalTrademarks=;OriginalFilename=;ProductName=$(MSBuildProjectName);ProductVersion=1.0.0.0;Comments=;ProgramID=com.embarcadero.$(MSBuildProjectName) + UniDACBasicServer_Icon.ico + PerMonitor + + + DEBUG;$(DCC_Define) + false + true + + + true + + + Debug + + + true + + + $(BDS)\bin\delphi_PROJECTICNS.icns + true + + + true + true + Cfg_2 + true + + + true + true + 1033 + CompanyName=;FileDescription=$(MSBuildProjectName);FileVersion=1.0.0.0;InternalName=;LegalCopyright=;LegalTrademarks=;OriginalFilename=;ProductName=$(MSBuildProjectName);ProductVersion=1.0.0.0;Comments=;ProgramID=com.embarcadero.$(MSBuildProjectName) + UniDACBasicServer_Icon.ico + PerMonitor + + + + MainSource + + +
MainForm
+
+ + + + Cfg_2 + Base + + + Base + + + Cfg_1 + Base + +
+ + Delphi.Personality.12 + + + + + UniDACBasicServerFMXApplication.dpr + + + Microsoft Office 2000 Sample Automation Server Wrapper Components + Microsoft Office XP Sample Automation Server Wrapper Components + + + + True + False + True + False + True + True + True + True + False + + + + + true + + + + + true + + + + + true + + + + + true + + + + + true + + + + + true + + + + + UniDACBasicServerFMXApplication.exe + true + + + + + 1 + + + Contents\MacOS + 1 + + + 0 + + + + + classes + 1 + + + + + res\xml + 1 + + + + + library\lib\armeabi-v7a + 1 + + + + + library\lib\armeabi + 1 + + + + + library\lib\mips + 1 + + + + + library\lib\armeabi-v7a + 1 + + + + + res\drawable + 1 + + + + + res\values + 1 + + + + + res\values-v21 + 1 + + + + + res\values + 1 + + + + + res\drawable + 1 + + + + + res\drawable-xxhdpi + 1 + + + + + res\drawable-ldpi + 1 + + + + + res\drawable-mdpi + 1 + + + + + res\drawable-hdpi + 1 + + + + + res\drawable-xhdpi + 1 + + + + + res\drawable-mdpi + 1 + + + + + res\drawable-hdpi + 1 + + + + + res\drawable-xhdpi + 1 + + + + + res\drawable-xxhdpi + 1 + + + + + res\drawable-xxxhdpi + 1 + + + + + res\drawable-small + 1 + + + + + res\drawable-normal + 1 + + + + + res\drawable-large + 1 + + + + + res\drawable-xlarge + 1 + + + + + res\values + 1 + + + + + 1 + + + Contents\MacOS + 1 + + + 0 + + + + + Contents\MacOS + 1 + .framework + + + Contents\MacOS + 1 + .framework + + + 0 + + + + + 1 + .dylib + + + 1 + .dylib + + + 1 + .dylib + + + Contents\MacOS + 1 + .dylib + + + Contents\MacOS + 1 + .dylib + + + 0 + .dll;.bpl + + + + + 1 + .dylib + + + 1 + .dylib + + + 1 + .dylib + + + Contents\MacOS + 1 + .dylib + + + Contents\MacOS + 1 + .dylib + + + 0 + .bpl + + + + + 0 + + + 0 + + + 0 + + + 0 + + + Contents\Resources\StartUp\ + 0 + + + Contents\Resources\StartUp\ + 0 + + + 0 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + + + ..\$(PROJECTNAME).app.dSYM\Contents\Resources\DWARF + 1 + + + ..\$(PROJECTNAME).app.dSYM\Contents\Resources\DWARF + 1 + + + + + 1 + + + 1 + + + + + ..\ + 1 + + + ..\ + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + ..\$(PROJECTNAME).app.dSYM\Contents\Resources\DWARF + 1 + + + + + ..\ + 1 + + + ..\ + 1 + + + + + Contents + 1 + + + Contents + 1 + + + + + Contents\Resources + 1 + + + Contents\Resources + 1 + + + + + library\lib\armeabi-v7a + 1 + + + 1 + + + 1 + + + 1 + + + 1 + + + Contents\MacOS + 1 + + + Contents\MacOS + 1 + + + 0 + + + + + 1 + + + 1 + + + + + Assets + 1 + + + Assets + 1 + + + + + Assets + 1 + + + Assets + 1 + + + + + + + + + + + + + + 12 + + + + +
diff --git a/Demos/UniDAC Basic/UniDACBasicServerISAPI.dpr b/Demos/UniDAC Basic/UniDACBasicServerISAPI.dpr new file mode 100644 index 00000000..8a8120dd --- /dev/null +++ b/Demos/UniDAC Basic/UniDACBasicServerISAPI.dpr @@ -0,0 +1,30 @@ +(* + Copyright 2016, MARS-Curiosity - REST Library + + Home: https://github.com/andrea-magni/MARS +*) +library UniDACBasicServerISAPI; + +uses + Winapi.ActiveX, + System.Win.ComObj, + Web.WebBroker, + Web.Win.ISAPIApp, + Web.Win.ISAPIThreadPool, + Server.Ignition in 'Server.Ignition.pas', + Server.Resources in 'Server.Resources.pas', + Server.WebModule in 'Server.WebModule.pas' {ServerWebModule: TWebModule}; + +{$R *.res} + +exports + GetExtensionVersion, + HttpExtensionProc, + TerminateExtension; + +begin + CoInitFlags := COINIT_MULTITHREADED; + Application.Initialize; + Application.WebModuleClass := WebModuleClass; + Application.Run; +end. diff --git a/Demos/UniDAC Basic/UniDACBasicServerISAPI.dproj b/Demos/UniDAC Basic/UniDACBasicServerISAPI.dproj new file mode 100644 index 00000000..8fd198bc --- /dev/null +++ b/Demos/UniDAC Basic/UniDACBasicServerISAPI.dproj @@ -0,0 +1,152 @@ + + + {9715EDC7-B308-42F7-B5EF-14BB4698B974} + UniDACBasicServerISAPI.dpr + True + Release + 1 + Library + None + 18.7 + Win32 + + + true + + + true + Base + true + + + true + Base + true + + + true + Base + true + + + true + Cfg_1 + true + true + + + true + Base + true + + + true + Cfg_2 + true + true + + + true + Cfg_2 + true + true + + + false + false + false + false + false + 00400000 + true + UniDACBasicServerISAPI + 1040 + CompanyName=;FileDescription=;FileVersion=1.0.0.0;InternalName=;LegalCopyright=;LegalTrademarks=;OriginalFilename=;ProductName=;ProductVersion=1.0.0.0;Comments=;CFBundleName= + System;Xml;Data;Datasnap;Web;Soap;$(DCC_Namespace) + .\bin + .\lib\$(Platform)\$(Config) + + + Winapi;System.Win;Data.Win;Datasnap.Win;Web.Win;Soap.Win;Xml.Win;Bde;$(DCC_Namespace) + Debug + true + CompanyName=;FileDescription=$(MSBuildProjectName);FileVersion=1.0.0.0;InternalName=;LegalCopyright=;LegalTrademarks=;OriginalFilename=;ProductName=$(MSBuildProjectName);ProductVersion=1.0.0.0;Comments=;ProgramID=com.embarcadero.$(MSBuildProjectName) + 1033 + (None) + + + UniDACBasicServerISAPI_Icon.ico + + + RELEASE;$(DCC_Define) + 0 + false + 0 + + + true + 1033 + CompanyName=;FileDescription=$(MSBuildProjectName);FileVersion=1.0.0.0;InternalName=;LegalCopyright=;LegalTrademarks=;OriginalFilename=;ProductName=$(MSBuildProjectName);ProductVersion=1.0.0.0;Comments=;ProgramID=com.embarcadero.$(MSBuildProjectName) + + + DEBUG;$(DCC_Define) + false + true + + + Debug + + + true + 1033 + CompanyName=;FileDescription=$(MSBuildProjectName);FileVersion=1.0.0.0;InternalName=;LegalCopyright=;LegalTrademarks=;OriginalFilename=;ProductName=$(MSBuildProjectName);ProductVersion=1.0.0.0;Comments=;ProgramID=com.embarcadero.$(MSBuildProjectName) + (None) + + + + MainSource + + + + +
ServerWebModule
+ TWebModule +
+ + Cfg_2 + Base + + + Base + + + Cfg_1 + Base + +
+ + Delphi.Personality.12 + + + + + UniDACBasicServerISAPI.dpr + + + Microsoft Office 2000 Sample Automation Server Wrapper Components + Microsoft Office XP Sample Automation Server Wrapper Components + + + + False + False + False + True + False + + + 12 + + + +
diff --git a/Demos/UniDAC Basic/UniDACBasicServerService.dpr b/Demos/UniDAC Basic/UniDACBasicServerService.dpr new file mode 100644 index 00000000..456a4e02 --- /dev/null +++ b/Demos/UniDAC Basic/UniDACBasicServerService.dpr @@ -0,0 +1,43 @@ +(* + Copyright 2016, MARS-Curiosity - REST Library + + Home: https://github.com/andrea-magni/MARS +*) +program UniDACBasicServerService; + +{$I MARS.inc} + +uses +{$ifdef DelphiXE3_UP} + Vcl.SvcMgr, +{$else} + SvcMgr, +{$endif} + + Server.Service in 'Server.Service.pas' {ServerService: TService}, + Server.Ignition in 'Server.Ignition.pas', + Server.Resources in 'Server.Resources.pas', + Server.WebModule in 'Server.WebModule.pas' {ServerWebModule: TWebModule}; + +{$R *.RES} + +begin + // Windows 2003 Server requires StartServiceCtrlDispatcher to be + // called before CoRegisterClassObject, which can be called indirectly + // by Application.Initialize. TServiceApplication.DelayInitialize allows + // Application.Initialize to be called from TService.Main (after + // StartServiceCtrlDispatcher has been called). + // + // Delayed initialization of the Application object may affect + // events which then occur prior to initialization, such as + // TService.OnCreate. It is only recommended if the ServiceApplication + // registers a class object with OLE and is intended for use with + // Windows 2003 Server. + // + // Application.DelayInitialize := True; + // + if not Application.DelayInitialize or Application.Installing then + Application.Initialize; + Application.CreateForm(TServerService, ServerService); + Application.Run; +end. diff --git a/Demos/UniDAC Basic/UniDACBasicServerService.dproj b/Demos/UniDAC Basic/UniDACBasicServerService.dproj new file mode 100644 index 00000000..9b969521 --- /dev/null +++ b/Demos/UniDAC Basic/UniDACBasicServerService.dproj @@ -0,0 +1,1006 @@ + + + {FEF5DF94-50B0-46F7-9F4B-6C355F140A2F} + UniDACBasicServerService.dpr + True + Release + 3 + Application + None + 18.7 + Win32 + + + true + + + true + Base + true + + + true + Base + true + + + true + Base + true + + + true + Cfg_1 + true + true + + + true + Cfg_1 + true + true + + + true + Base + true + + + true + Cfg_2 + true + true + + + true + Cfg_2 + true + true + + + true + Cfg_2 + true + true + + + false + false + false + false + false + 00400000 + UniDACBasicServerService + 1040 + CompanyName=;FileDescription=;FileVersion=1.0.0.0;InternalName=;LegalCopyright=;LegalTrademarks=;OriginalFilename=;ProductName=;ProductVersion=1.0.0.0;Comments=;CFBundleName= + System;Xml;Data;Datasnap;Web;Soap;Vcl;Winapi;$(DCC_Namespace) + .\lib\$(Platform)\$(Config) + .\bin + + + System.Win;Data.Win;Datasnap.Win;Web.Win;Soap.Win;Xml.Win;Bde;$(DCC_Namespace) + Debug + true + CompanyName=;FileDescription=$(MSBuildProjectName);FileVersion=1.0.0.0;InternalName=;LegalCopyright=;LegalTrademarks=;OriginalFilename=;ProductName=$(MSBuildProjectName);ProductVersion=1.0.0.0;Comments=;ProgramID=com.embarcadero.$(MSBuildProjectName) + 1033 + $(BDS)\bin\default_app.manifest + UniDACBasicServer_Icon.ico + true + $(BDS)\bin\Artwork\Windows\UWP\delphi_UwpDefault_44.png + $(BDS)\bin\Artwork\Windows\UWP\delphi_UwpDefault_150.png + + + $(BDS)\bin\default_app.manifest + UniDACBasicServer_Icon.ico + true + System.Win;Data.Win;Datasnap.Win;Web.Win;Soap.Win;Xml.Win;$(DCC_Namespace) + Debug + true + CompanyName=;FileDescription=$(MSBuildProjectName);FileVersion=1.0.0.0;InternalName=;LegalCopyright=;LegalTrademarks=;OriginalFilename=;ProgramID=com.embarcadero.$(MSBuildProjectName);ProductName=$(MSBuildProjectName);ProductVersion=1.0.0.0;Comments= + 1033 + $(BDS)\bin\Artwork\Windows\UWP\delphi_UwpDefault_44.png + $(BDS)\bin\Artwork\Windows\UWP\delphi_UwpDefault_150.png + + + RELEASE;$(DCC_Define) + 0 + false + 0 + + + true + true + 1033 + CompanyName=;FileDescription=$(MSBuildProjectName);FileVersion=1.0.0.0;InternalName=;LegalCopyright=;LegalTrademarks=;OriginalFilename=;ProductName=$(MSBuildProjectName);ProductVersion=1.0.0.0;Comments=;ProgramID=com.embarcadero.$(MSBuildProjectName) + PerMonitor + + + true + true + 1033 + CompanyName=;FileDescription=$(MSBuildProjectName);FileVersion=1.0.0.0;InternalName=;LegalCopyright=;LegalTrademarks=;OriginalFilename=;ProgramID=com.embarcadero.$(MSBuildProjectName);ProductName=$(MSBuildProjectName);ProductVersion=1.0.0.0;Comments= + $(BDS)\bin\Artwork\Windows\UWP\delphi_UwpDefault_44.png + $(BDS)\bin\Artwork\Windows\UWP\delphi_UwpDefault_150.png + PerMonitor + + + DEBUG;$(DCC_Define) + false + true + + + Debug + + + true + true + 1033 + CompanyName=;FileDescription=$(MSBuildProjectName);FileVersion=1.0.0.0;InternalName=;LegalCopyright=;LegalTrademarks=;OriginalFilename=;ProductName=$(MSBuildProjectName);ProductVersion=1.0.0.0;Comments=;ProgramID=com.embarcadero.$(MSBuildProjectName) + $(BDS)\bin\Artwork\Windows\UWP\delphi_UwpDefault_44.png + $(BDS)\bin\Artwork\Windows\UWP\delphi_UwpDefault_150.png + PerMonitor + + + true + true + 1033 + CompanyName=;FileDescription=$(MSBuildProjectName);FileVersion=1.0.0.0;InternalName=;LegalCopyright=;LegalTrademarks=;OriginalFilename=;ProgramID=com.embarcadero.$(MSBuildProjectName);ProductName=$(MSBuildProjectName);ProductVersion=1.0.0.0;Comments= + MARS.ico + $(BDS)\bin\Artwork\Windows\UWP\delphi_UwpDefault_44.png + $(BDS)\bin\Artwork\Windows\UWP\delphi_UwpDefault_150.png + PerMonitor + + + + MainSource + + +
ServerService
+ TService +
+ + + +
ServerWebModule
+ TWebModule +
+ + Cfg_2 + Base + + + Base + + + Cfg_1 + Base + +
+ + Delphi.Personality.12 + + + + + UniDACBasicServerService.dpr + + + Microsoft Office 2000 Sample Automation Server Wrapper Components + Microsoft Office XP Sample Automation Server Wrapper Components + + + + False + False + False + True + True + + + + + Assets\ + Logo150x150.png + true + + + + + true + + + + + Assets\ + Logo44x44.png + true + + + + + Assets\ + Logo150x150.png + true + + + + + true + + + + + true + + + + + true + + + + + true + + + + + true + + + + + Assets\ + Logo44x44.png + true + + + + + 1 + + + Contents\MacOS + 1 + + + 0 + + + + + classes + 1 + + + + + res\xml + 1 + + + + + library\lib\armeabi-v7a + 1 + + + + + library\lib\armeabi + 1 + + + + + library\lib\mips + 1 + + + + + library\lib\armeabi-v7a + 1 + + + + + res\drawable + 1 + + + + + res\values + 1 + + + + + res\values-v21 + 1 + + + + + res\values + 1 + + + + + res\drawable + 1 + + + + + res\drawable-xxhdpi + 1 + + + + + res\drawable-ldpi + 1 + + + + + res\drawable-mdpi + 1 + + + + + res\drawable-hdpi + 1 + + + + + res\drawable-xhdpi + 1 + + + + + res\drawable-mdpi + 1 + + + + + res\drawable-hdpi + 1 + + + + + res\drawable-xhdpi + 1 + + + + + res\drawable-xxhdpi + 1 + + + + + res\drawable-xxxhdpi + 1 + + + + + res\drawable-small + 1 + + + + + res\drawable-normal + 1 + + + + + res\drawable-large + 1 + + + + + res\drawable-xlarge + 1 + + + + + res\values + 1 + + + + + 1 + + + Contents\MacOS + 1 + + + 0 + + + + + Contents\MacOS + 1 + .framework + + + Contents\MacOS + 1 + .framework + + + 0 + + + + + 1 + .dylib + + + 1 + .dylib + + + 1 + .dylib + + + Contents\MacOS + 1 + .dylib + + + Contents\MacOS + 1 + .dylib + + + 0 + .dll;.bpl + + + + + 1 + .dylib + + + 1 + .dylib + + + 1 + .dylib + + + Contents\MacOS + 1 + .dylib + + + Contents\MacOS + 1 + .dylib + + + 0 + .bpl + + + + + 0 + + + 0 + + + 0 + + + 0 + + + Contents\Resources\StartUp\ + 0 + + + Contents\Resources\StartUp\ + 0 + + + 0 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + + + ..\$(PROJECTNAME).app.dSYM\Contents\Resources\DWARF + 1 + + + ..\$(PROJECTNAME).app.dSYM\Contents\Resources\DWARF + 1 + + + + + 1 + + + 1 + + + + + ..\ + 1 + + + ..\ + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + ..\$(PROJECTNAME).app.dSYM\Contents\Resources\DWARF + 1 + + + + + ..\ + 1 + + + ..\ + 1 + + + + + Contents + 1 + + + Contents + 1 + + + + + Contents\Resources + 1 + + + Contents\Resources + 1 + + + + + library\lib\armeabi-v7a + 1 + + + 1 + + + 1 + + + 1 + + + 1 + + + Contents\MacOS + 1 + + + Contents\MacOS + 1 + + + 0 + + + + + 1 + + + 1 + + + + + Assets + 1 + + + Assets + 1 + + + + + Assets + 1 + + + Assets + 1 + + + + + + + + + + + + + + 12 + + + + +
diff --git a/Demos/UniDAC Basic/UniDACBasicServer_Icon.ico b/Demos/UniDAC Basic/UniDACBasicServer_Icon.ico new file mode 100644 index 00000000..0067d86b Binary files /dev/null and b/Demos/UniDAC Basic/UniDACBasicServer_Icon.ico differ diff --git a/Demos/UniDAC Basic/bin/MyDatabase.db b/Demos/UniDAC Basic/bin/MyDatabase.db new file mode 100644 index 00000000..76795749 Binary files /dev/null and b/Demos/UniDAC Basic/bin/MyDatabase.db differ diff --git a/Demos/UniDAC Basic/bin/UniDACBasicServerApplication.ini b/Demos/UniDAC Basic/bin/UniDACBasicServerApplication.ini new file mode 100644 index 00000000..54d1e739 --- /dev/null +++ b/Demos/UniDAC Basic/bin/UniDACBasicServerApplication.ini @@ -0,0 +1,26 @@ +[DefaultEngine] +ThreadPoolSize=100 + +;UniDAC.MAIN_DB.ConnectString=Provider Name=InterBase;Data Source=localhost;Database=employee;User ID=SYSDBA;Password=masterkey + +FireDAC.MAIN_DB.DriverID=FB +FireDAC.MAIN_DB.Database=employee +FireDAC.MAIN_DB.User_Name=SYSDBA +FireDAC.MAIN_DB.Password=masterkey +FireDAC.MAIN_DB.Protocol=TCPIP +FireDAC.MAIN_DB.Server=localhost +FireDAC.MAIN_DB.Pooled=True +FireDAC.MAIN_DB.POOL_MaximumItems=100 + +UniDAC.MAIN_DB.Provider Name=InterBase +UniDAC.MAIN_DB.Data Source=localhost +UniDAC.MAIN_DB.Database=employee +UniDAC.MAIN_DB.User ID=SYSDBA +UniDAC.MAIN_DB.Password=masterkey +UniDAC.MAIN_DB.Login Prompt=False +UniDAC.MAIN_DB.Pooling=True + +UniDAC.MY_SQLITE.Provider Name=SQLite +UniDAC.MY_SQLITE.Database=C:\Sviluppo\Librerie\MARS\Demos\UniDAC Basic\bin\MyDatabase.db +UniDAC.MY_SQLITE.Login Prompt=False +UniDAC.MY_SQLITE.Pooling=False diff --git a/Packages/103Rio/MARS.Core.dpk b/Packages/103Rio/MARS.Core.dpk index a2bcb294..27e0fd5a 100644 --- a/Packages/103Rio/MARS.Core.dpk +++ b/Packages/103Rio/MARS.Core.dpk @@ -9,21 +9,21 @@ package MARS.Core; {$EXTENDEDSYNTAX ON} {$IMPORTEDDATA ON} {$IOCHECKS ON} -{$LOCALSYMBOLS OFF} +{$LOCALSYMBOLS ON} {$LONGSTRINGS ON} {$OPENSTRINGS ON} -{$OPTIMIZATION ON} +{$OPTIMIZATION OFF} {$OVERFLOWCHECKS OFF} {$RANGECHECKS OFF} -{$REFERENCEINFO OFF} +{$REFERENCEINFO ON} {$SAFEDIVIDE OFF} -{$STACKFRAMES OFF} +{$STACKFRAMES ON} {$TYPEDADDRESS OFF} {$VARSTRINGCHECKS ON} {$WRITEABLECONST OFF} {$MINENUMSIZE 1} {$IMAGEBASE $400000} -{$DEFINE RELEASE} +{$DEFINE DEBUG} {$ENDIF IMPLICITBUILDING} {$DESCRIPTION 'MARS-Curiosity Core'} {$LIBSUFFIX '260'} @@ -65,3 +65,4 @@ end. + diff --git a/Packages/103Rio/MARS.Core.dproj b/Packages/103Rio/MARS.Core.dproj index c07495ad..0d27b5ed 100644 --- a/Packages/103Rio/MARS.Core.dproj +++ b/Packages/103Rio/MARS.Core.dproj @@ -1,195 +1,198 @@ - - - {8C6A1564-94E6-420D-BDE4-54A4CD3BD125} - MARS.Core.dpk - True - Release - 131 - Package - None - 18.5 - Win32 - - - true - - - true - Base - true - - - true - Base - true - - - true - Base - true - - - true - Base - true - - - true - Cfg_1 - true - true - - - true - Base - true - - - true - Cfg_2 - true - true - - - true - Cfg_2 - true - true - - - false - false - false - false - false - 00400000 - true - true - MARS_Core - MARS-Curiosity Core - 260 - true - true - 1040 - CompanyName=;FileDescription=;FileVersion=1.0.0.0;InternalName=;LegalCopyright=;LegalTrademarks=;OriginalFilename=;ProductName=;ProductVersion=1.0.0.0;Comments=;CFBundleName= - System;Xml;Data;Datasnap;Web;Soap;$(DCC_Namespace) - ..\..\Source;$(DCC_UnitSearchPath) - ..\..\Lib260\$(Platform)\$(Config) - - - package=com.embarcadero.$(MSBuildProjectName);label=$(MSBuildProjectName);versionCode=1;versionName=1.0.0;persistent=False;restoreAnyVersion=False;installLocation=auto;largeHeap=False;theme=TitleBar;hardwareAccelerated=true;apiKey= - Debug - android-support-v4.dex.jar;cloud-messaging.dex.jar;fmx.dex.jar;google-analytics-v2.dex.jar;google-play-billing.dex.jar;google-play-licensing.dex.jar;google-play-services-ads-7.0.0.dex.jar;google-play-services-analytics-7.0.0.dex.jar;google-play-services-base-7.0.0.dex.jar;google-play-services-gcm-7.0.0.dex.jar;google-play-services-identity-7.0.0.dex.jar;google-play-services-maps-7.0.0.dex.jar;google-play-services-panorama-7.0.0.dex.jar;google-play-services-plus-7.0.0.dex.jar;google-play-services-wallet-7.0.0.dex.jar - - - Winapi;System.Win;Data.Win;Datasnap.Win;Web.Win;Soap.Win;Xml.Win;Bde;$(DCC_Namespace) - Debug - true - CompanyName=;FileDescription=$(MSBuildProjectName);FileVersion=1.0.0.0;InternalName=;LegalCopyright=;LegalTrademarks=;OriginalFilename=;ProductName=$(MSBuildProjectName);ProductVersion=1.0.0.0;Comments=;ProgramID=com.embarcadero.$(MSBuildProjectName) - 1033 - - - Winapi;System.Win;Data.Win;Datasnap.Win;Web.Win;Soap.Win;Xml.Win;$(DCC_Namespace) - Debug - true - CompanyName=;FileDescription=$(MSBuildProjectName);FileVersion=1.0.0.0;InternalName=;LegalCopyright=;LegalTrademarks=;OriginalFilename=;ProgramID=com.embarcadero.$(MSBuildProjectName);ProductName=$(MSBuildProjectName);ProductVersion=1.0.0.0;Comments= - 1033 - - - RELEASE;$(DCC_Define) - 0 - false - 0 - - - true - 1033 - CompanyName=;FileDescription=$(MSBuildProjectName);FileVersion=1.0.0.0;InternalName=;LegalCopyright=;LegalTrademarks=;OriginalFilename=;ProductName=$(MSBuildProjectName);ProductVersion=1.0.0.0;Comments=;ProgramID=com.embarcadero.$(MSBuildProjectName) - - - DEBUG;$(DCC_Define) - false - true - - - Debug - - - true - 1033 - CompanyName=;FileDescription=$(MSBuildProjectName);FileVersion=1.0.0.0;InternalName=;LegalCopyright=;LegalTrademarks=;OriginalFilename=;ProgramID=com.embarcadero.$(MSBuildProjectName);ProductName=$(MSBuildProjectName);ProductVersion=1.0.0.0;Comments= - - - - MainSource - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Cfg_2 - Base - - - Base - - - Cfg_1 - Base - - - - Delphi.Personality.12 - Package - - - - MARS.Core.dpk - - - DBExpress Enterprise Data Explorer Integration - Microsoft Office 2000 Sample Automation Server Wrapper Components - Microsoft Office XP Sample Automation Server Wrapper Components - CodeSite Express 5.3.3 - - - - False - False - False - False - True - False - True - True - - - 12 - - - - + + + {8C6A1564-94E6-420D-BDE4-54A4CD3BD125} + MARS.Core.dpk + True + Release + 131 + Package + None + 18.8 + Win32 + + + true + + + true + Base + true + + + true + Base + true + + + true + Base + true + + + true + Base + true + + + true + Base + true + + + true + Base + true + + + true + Cfg_2 + true + true + + + true + Cfg_2 + true + true + + + false + false + false + false + false + 00400000 + true + true + MARS_Core + MARS-Curiosity Core + 260 + true + true + 1040 + CompanyName=;FileDescription=;FileVersion=1.0.0.0;InternalName=;LegalCopyright=;LegalTrademarks=;OriginalFilename=;ProductName=;ProductVersion=1.0.0.0;Comments=;CFBundleName= + System;Xml;Data;Datasnap;Web;Soap;$(DCC_Namespace) + ..\..\Source;$(DCC_UnitSearchPath) + ..\..\Lib260\$(Platform)\$(Config) + + + package=com.embarcadero.$(MSBuildProjectName);label=$(MSBuildProjectName);versionCode=1;versionName=1.0.0;persistent=False;restoreAnyVersion=False;installLocation=auto;largeHeap=False;theme=TitleBar;hardwareAccelerated=true;apiKey= + Debug + android-support-v4.dex.jar;cloud-messaging.dex.jar;fmx.dex.jar;google-analytics-v2.dex.jar;google-play-billing.dex.jar;google-play-licensing.dex.jar;google-play-services-ads-7.0.0.dex.jar;google-play-services-analytics-7.0.0.dex.jar;google-play-services-base-7.0.0.dex.jar;google-play-services-gcm-7.0.0.dex.jar;google-play-services-identity-7.0.0.dex.jar;google-play-services-maps-7.0.0.dex.jar;google-play-services-panorama-7.0.0.dex.jar;google-play-services-plus-7.0.0.dex.jar;google-play-services-wallet-7.0.0.dex.jar + + + package=com.embarcadero.$(MSBuildProjectName);label=$(MSBuildProjectName);versionCode=1;versionName=1.0.0;persistent=False;restoreAnyVersion=False;installLocation=auto;largeHeap=False;theme=TitleBar;hardwareAccelerated=true;apiKey= + Debug + true + Base + true + android-support-v4.dex.jar;cloud-messaging.dex.jar;fmx.dex.jar;google-analytics-v2.dex.jar;google-play-billing.dex.jar;google-play-licensing.dex.jar;google-play-services-ads-7.0.0.dex.jar;google-play-services-analytics-7.0.0.dex.jar;google-play-services-base-7.0.0.dex.jar;google-play-services-gcm-7.0.0.dex.jar;google-play-services-identity-7.0.0.dex.jar;google-play-services-maps-7.0.0.dex.jar;google-play-services-panorama-7.0.0.dex.jar;google-play-services-plus-7.0.0.dex.jar;google-play-services-wallet-7.0.0.dex.jar + + + Winapi;System.Win;Data.Win;Datasnap.Win;Web.Win;Soap.Win;Xml.Win;Bde;$(DCC_Namespace) + Debug + true + CompanyName=;FileDescription=$(MSBuildProjectName);FileVersion=1.0.0.0;InternalName=;LegalCopyright=;LegalTrademarks=;OriginalFilename=;ProductName=$(MSBuildProjectName);ProductVersion=1.0.0.0;Comments=;ProgramID=com.embarcadero.$(MSBuildProjectName) + 1033 + + + Winapi;System.Win;Data.Win;Datasnap.Win;Web.Win;Soap.Win;Xml.Win;$(DCC_Namespace) + Debug + true + CompanyName=;FileDescription=$(MSBuildProjectName);FileVersion=1.0.0.0;InternalName=;LegalCopyright=;LegalTrademarks=;OriginalFilename=;ProgramID=com.embarcadero.$(MSBuildProjectName);ProductName=$(MSBuildProjectName);ProductVersion=1.0.0.0;Comments= + 1033 + + + RELEASE;$(DCC_Define) + 0 + false + 0 + + + DEBUG;$(DCC_Define) + false + true + + + Debug + + + true + 1033 + CompanyName=;FileDescription=$(MSBuildProjectName);FileVersion=1.0.0.0;InternalName=;LegalCopyright=;LegalTrademarks=;OriginalFilename=;ProgramID=com.embarcadero.$(MSBuildProjectName);ProductName=$(MSBuildProjectName);ProductVersion=1.0.0.0;Comments= + + + + MainSource + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Cfg_2 + Base + + + Base + + + Cfg_1 + Base + + + + Delphi.Personality.12 + Package + + + + MARS.Core.dpk + + + DBExpress Enterprise Data Explorer Integration + Microsoft Office 2000 Sample Automation Server Wrapper Components + Microsoft Office XP Sample Automation Server Wrapper Components + CodeSite Express 5.3.3 + + + + False + False + False + False + False + True + False + True + True + + + 12 + + + + diff --git a/Packages/103Rio/MARS.Core.res b/Packages/103Rio/MARS.Core.res index 8d2bc484..3e070e28 100644 Binary files a/Packages/103Rio/MARS.Core.res and b/Packages/103Rio/MARS.Core.res differ diff --git a/Packages/103Rio/MARS.DCS.dpk b/Packages/103Rio/MARS.DCS.dpk new file mode 100644 index 00000000..f0c7eac1 --- /dev/null +++ b/Packages/103Rio/MARS.DCS.dpk @@ -0,0 +1,57 @@ +package MARS.DCS; + +{$R *.res} +{$IFDEF IMPLICITBUILDING This IFDEF should not be used by users} +{$ALIGN 8} +{$ASSERTIONS ON} +{$BOOLEVAL OFF} +{$DEBUGINFO OFF} +{$EXTENDEDSYNTAX ON} +{$IMPORTEDDATA ON} +{$IOCHECKS ON} +{$LOCALSYMBOLS OFF} +{$LONGSTRINGS ON} +{$OPENSTRINGS ON} +{$OPTIMIZATION ON} +{$OVERFLOWCHECKS OFF} +{$RANGECHECKS OFF} +{$REFERENCEINFO OFF} +{$SAFEDIVIDE OFF} +{$STACKFRAMES OFF} +{$TYPEDADDRESS OFF} +{$VARSTRINGCHECKS ON} +{$WRITEABLECONST OFF} +{$MINENUMSIZE 1} +{$IMAGEBASE $400000} +{$DEFINE RELEASE} +{$ENDIF IMPLICITBUILDING} +{$DESCRIPTION 'MARS-Curiosity REST Library - DelphiCrossSocket support'} +{$LIBSUFFIX '260'} +{$RUNONLY} +{$IMPLICITBUILD OFF} + +requires + rtl, + MARS.Utils, + MARS.Core; + +contains + MARS.http.Server.DCS in '..\..\Source\MARS.http.Server.DCS.pas', + Net.CrossHttpMiddleware in '..\..\ThirdParty\DCS\Net\Net.CrossHttpMiddleware.pas', + Net.CrossHttpParams in '..\..\ThirdParty\DCS\Net\Net.CrossHttpParams.pas', + Net.CrossHttpRouter in '..\..\ThirdParty\DCS\Net\Net.CrossHttpRouter.pas', + Net.CrossHttpServer in '..\..\ThirdParty\DCS\Net\Net.CrossHttpServer.pas', + Net.CrossHttpUtils in '..\..\ThirdParty\DCS\Net\Net.CrossHttpUtils.pas', + Net.CrossServer in '..\..\ThirdParty\DCS\Net\Net.CrossServer.pas', + Net.CrossSocket.Iocp in '..\..\ThirdParty\DCS\Net\Net.CrossSocket.Iocp.pas', + Net.CrossSocket in '..\..\ThirdParty\DCS\Net\Net.CrossSocket.pas', + Net.SocketAPI in '..\..\ThirdParty\DCS\Net\Net.SocketAPI.pas', + Net.Winsock2 in '..\..\ThirdParty\DCS\Net\Net.Winsock2.pas', + Net.Wship6 in '..\..\ThirdParty\DCS\Net\Net.Wship6.pas', + Net.CrossSocket.Base in '..\..\ThirdParty\DCS\Net\Net.CrossSocket.Base.pas', + Utils.DateTime in '..\..\ThirdParty\DCS\Utils\Utils.DateTime.pas', + Utils.Logger in '..\..\ThirdParty\DCS\Utils\Utils.Logger.pas', + Utils.RegEx in '..\..\ThirdParty\DCS\Utils\Utils.RegEx.pas', + Utils.Utils in '..\..\ThirdParty\DCS\Utils\Utils.Utils.pas'; + +end. diff --git a/Packages/103Rio/MARS.DCS.dproj b/Packages/103Rio/MARS.DCS.dproj new file mode 100644 index 00000000..0531394e --- /dev/null +++ b/Packages/103Rio/MARS.DCS.dproj @@ -0,0 +1,1068 @@ + + + {02BBCED0-86EE-4FB8-A124-5BD6E5477506} + MARS.DCS.dpk + 18.8 + None + True + Release + Win32 + 3 + Package + + + true + + + true + Base + true + + + true + Base + true + + + true + Base + true + + + true + Base + true + + + true + Base + true + + + true + Base + true + + + true + Base + true + + + true + Base + true + + + true + Cfg_1 + true + true + + + true + Base + true + + + true + Cfg_2 + true + true + + + true + Cfg_2 + true + true + + + ..\..\Lib260\$(Platform)\$(Config) + .\$(Platform)\$(Config) + false + false + false + false + false + true + true + System;Xml;Data;Datasnap;Web;Soap;$(DCC_Namespace) + All + MARS_DCS + true + true + 1040 + CompanyName=;FileDescription=$(MSBuildProjectName);FileVersion=1.0.0.0;InternalName=;LegalCopyright=;LegalTrademarks=;OriginalFilename=;ProgramID=com.embarcadero.$(MSBuildProjectName);ProductName=$(MSBuildProjectName);ProductVersion=1.0.0.0;Comments= + MARS-Curiosity REST Library - DelphiCrossSocket support + 260 + ..\..\Source;$(DCC_UnitSearchPath) + + + None + android-support-v4.dex.jar;cloud-messaging.dex.jar;fmx.dex.jar;google-analytics-v2.dex.jar;google-play-billing.dex.jar;google-play-licensing.dex.jar;google-play-services-ads-7.0.0.dex.jar;google-play-services-analytics-7.0.0.dex.jar;google-play-services-base-7.0.0.dex.jar;google-play-services-gcm-7.0.0.dex.jar;google-play-services-identity-7.0.0.dex.jar;google-play-services-maps-7.0.0.dex.jar;google-play-services-panorama-7.0.0.dex.jar;google-play-services-plus-7.0.0.dex.jar;google-play-services-wallet-7.0.0.dex.jar + + + package=com.embarcadero.$(MSBuildProjectName);label=$(MSBuildProjectName);versionCode=1;versionName=1.0.0;persistent=False;restoreAnyVersion=False;installLocation=auto;largeHeap=False;theme=TitleBar;hardwareAccelerated=true;apiKey= + Debug + true + Base + true + None + android-support-v4.dex.jar;cloud-messaging.dex.jar;fmx.dex.jar;google-analytics-v2.dex.jar;google-play-billing.dex.jar;google-play-licensing.dex.jar;google-play-services-ads-7.0.0.dex.jar;google-play-services-analytics-7.0.0.dex.jar;google-play-services-base-7.0.0.dex.jar;google-play-services-gcm-7.0.0.dex.jar;google-play-services-identity-7.0.0.dex.jar;google-play-services-maps-7.0.0.dex.jar;google-play-services-panorama-7.0.0.dex.jar;google-play-services-plus-7.0.0.dex.jar;google-play-services-wallet-7.0.0.dex.jar + + + None + + + None + + + None + + + Winapi;System.Win;Data.Win;Datasnap.Win;Web.Win;Soap.Win;Xml.Win;Bde;$(DCC_Namespace) + Debug + true + 1033 + + + Winapi;System.Win;Data.Win;Datasnap.Win;Web.Win;Soap.Win;Xml.Win;$(DCC_Namespace) + Debug + true + 1033 + + + DEBUG;$(DCC_Define) + true + false + true + true + true + + + false + true + 1033 + + + false + RELEASE;$(DCC_Define) + 0 + 0 + + + true + 1033 + + + true + 1033 + + + + MainSource + + + + + + + + + + + + + + + + + + + + + + + Cfg_2 + Base + + + Base + + + Cfg_1 + Base + + + + Delphi.Personality.12 + Package + + + + MARS.DCS.dpk + + + DBExpress Enterprise Data Explorer Integration + Microsoft Office 2000 Sample Automation Server Wrapper Components + Microsoft Office XP Sample Automation Server Wrapper Components + CodeSite Express 5.3.3 + + + + + + true + + + + + MARS_DCS.bpl + true + + + + + MARS_DCS.bpl + true + + + + + true + + + + + true + + + + + true + + + + + true + + + + + MARS_DCS.bpl + true + + + + + 1 + + + 0 + + + + + classes + 1 + + + classes + 1 + + + + + res\xml + 1 + + + res\xml + 1 + + + + + library\lib\armeabi-v7a + 1 + + + + + library\lib\armeabi + 1 + + + library\lib\armeabi + 1 + + + + + library\lib\armeabi-v7a + 1 + + + + + library\lib\mips + 1 + + + library\lib\mips + 1 + + + + + library\lib\armeabi-v7a + 1 + + + library\lib\arm64-v8a + 1 + + + + + library\lib\armeabi-v7a + 1 + + + + + res\drawable + 1 + + + res\drawable + 1 + + + + + res\values + 1 + + + res\values + 1 + + + + + res\values-v21 + 1 + + + res\values-v21 + 1 + + + + + res\values + 1 + + + res\values + 1 + + + + + res\drawable + 1 + + + res\drawable + 1 + + + + + res\drawable-xxhdpi + 1 + + + res\drawable-xxhdpi + 1 + + + + + res\drawable-ldpi + 1 + + + res\drawable-ldpi + 1 + + + + + res\drawable-mdpi + 1 + + + res\drawable-mdpi + 1 + + + + + res\drawable-hdpi + 1 + + + res\drawable-hdpi + 1 + + + + + res\drawable-xhdpi + 1 + + + res\drawable-xhdpi + 1 + + + + + res\drawable-mdpi + 1 + + + res\drawable-mdpi + 1 + + + + + res\drawable-hdpi + 1 + + + res\drawable-hdpi + 1 + + + + + res\drawable-xhdpi + 1 + + + res\drawable-xhdpi + 1 + + + + + res\drawable-xxhdpi + 1 + + + res\drawable-xxhdpi + 1 + + + + + res\drawable-xxxhdpi + 1 + + + res\drawable-xxxhdpi + 1 + + + + + res\drawable-small + 1 + + + res\drawable-small + 1 + + + + + res\drawable-normal + 1 + + + res\drawable-normal + 1 + + + + + res\drawable-large + 1 + + + res\drawable-large + 1 + + + + + res\drawable-xlarge + 1 + + + res\drawable-xlarge + 1 + + + + + res\values + 1 + + + res\values + 1 + + + + + 1 + + + 1 + + + 0 + + + + + 1 + .framework + + + 1 + .framework + + + 0 + + + + + 1 + .dylib + + + 1 + .dylib + + + 0 + .dll;.bpl + + + + + 1 + .dylib + + + 1 + .dylib + + + 1 + .dylib + + + 1 + .dylib + + + 1 + .dylib + + + 0 + .bpl + + + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + + + ..\$(PROJECTNAME).app.dSYM\Contents\Resources\DWARF + 1 + + + ..\$(PROJECTNAME).app.dSYM\Contents\Resources\DWARF + 1 + + + + + + + + 1 + + + 1 + + + 1 + + + + + + + + Contents\Resources + 1 + + + Contents\Resources + 1 + + + + + library\lib\armeabi-v7a + 1 + + + library\lib\arm64-v8a + 1 + + + 1 + + + 1 + + + 1 + + + 1 + + + 1 + + + 1 + + + 0 + + + + + library\lib\armeabi-v7a + 1 + + + + + 1 + + + 1 + + + + + Assets + 1 + + + Assets + 1 + + + + + Assets + 1 + + + Assets + 1 + + + + + + + + + + + + + + + False + False + False + False + False + False + False + True + True + + + 12 + + + + + diff --git a/Packages/103Rio/MARS.DCS.res b/Packages/103Rio/MARS.DCS.res new file mode 100644 index 00000000..6dbcdc6a Binary files /dev/null and b/Packages/103Rio/MARS.DCS.res differ diff --git a/Packages/103Rio/MARS.DelphiRazor.dpk b/Packages/103Rio/MARS.DelphiRazor.dpk index 58085036..19c36215 100644 --- a/Packages/103Rio/MARS.DelphiRazor.dpk +++ b/Packages/103Rio/MARS.DelphiRazor.dpk @@ -26,7 +26,7 @@ package MARS.DelphiRazor; {$DEFINE DEBUG} {$ENDIF IMPLICITBUILDING} {$DESCRIPTION 'MARS-Curiosity Core'} -{$LIBSUFFIX '250'} +{$LIBSUFFIX '260'} {$RUNONLY} {$IMPLICITBUILD OFF} diff --git a/Packages/103Rio/MARS.DelphiRazor.dproj b/Packages/103Rio/MARS.DelphiRazor.dproj index d8e4b85f..15fb892c 100644 --- a/Packages/103Rio/MARS.DelphiRazor.dproj +++ b/Packages/103Rio/MARS.DelphiRazor.dproj @@ -58,7 +58,7 @@ true ..\..\Source;$(DCC_UnitSearchPath) true - 250 + 260 MARS-Curiosity REST Library CompanyName=;FileDescription=;FileVersion=1.0.0.0;InternalName=;LegalCopyright=;LegalTrademarks=;OriginalFilename=;ProductName=;ProductVersion=1.0.0.0;Comments= 1040 @@ -168,7 +168,7 @@ true
- + MARS_DelphiRazor.bpl true diff --git a/Packages/103Rio/MARS.FireDAC.dpk b/Packages/103Rio/MARS.FireDAC.dpk index 1b1dc2d6..b86f459f 100644 --- a/Packages/103Rio/MARS.FireDAC.dpk +++ b/Packages/103Rio/MARS.FireDAC.dpk @@ -9,23 +9,23 @@ package MARS.FireDAC; {$EXTENDEDSYNTAX ON} {$IMPORTEDDATA ON} {$IOCHECKS ON} -{$LOCALSYMBOLS OFF} +{$LOCALSYMBOLS ON} {$LONGSTRINGS ON} {$OPENSTRINGS ON} -{$OPTIMIZATION ON} +{$OPTIMIZATION OFF} {$OVERFLOWCHECKS OFF} {$RANGECHECKS OFF} -{$REFERENCEINFO OFF} +{$REFERENCEINFO ON} {$SAFEDIVIDE OFF} -{$STACKFRAMES OFF} +{$STACKFRAMES ON} {$TYPEDADDRESS OFF} {$VARSTRINGCHECKS ON} {$WRITEABLECONST OFF} {$MINENUMSIZE 1} {$IMAGEBASE $400000} -{$DEFINE RELEASE} +{$DEFINE DEBUG} {$ENDIF IMPLICITBUILDING} -{$DESCRIPTION 'MARS-Curiosity REST Library'} +{$DESCRIPTION 'MARS-Curiosity FireDAC'} {$LIBSUFFIX '260'} {$RUNONLY} {$IMPLICITBUILD OFF} diff --git a/Packages/103Rio/MARS.FireDAC.dproj b/Packages/103Rio/MARS.FireDAC.dproj index f7063962..ce2bee02 100644 --- a/Packages/103Rio/MARS.FireDAC.dproj +++ b/Packages/103Rio/MARS.FireDAC.dproj @@ -2,7 +2,7 @@ {D44362A0-1BCF-45FD-80B4-281FB36C0E08} MARS.FireDAC.dpk - 18.5 + 18.8 None True Release @@ -34,12 +34,6 @@ Base true - - true - Cfg_2 - true - true - true true @@ -88,11 +82,6 @@ 0 0 - - true - 1033 - CompanyName=;FileVersion=1.0.0.0;InternalName=;LegalCopyright=;LegalTrademarks=;OriginalFilename=;ProductVersion=1.0.0.0;Comments=;ProgramID=com.embarcadero.$(MSBuildProjectName);FileDescription=$(MSBuildProjectName);ProductName=$(MSBuildProjectName) - MainSource @@ -140,10 +129,8 @@ MARS.FireDAC.dpk - DBExpress Enterprise Data Explorer Integration - Microsoft Office 2000 Sample Automation Server Wrapper Components - Microsoft Office XP Sample Automation Server Wrapper Components - CodeSite Express 5.3.3 + Microsoft Office 2000 Sample Automation Server Wrapper Components + Microsoft Office XP Sample Automation Server Wrapper Components @@ -152,6 +139,12 @@ true + + + bplMARS_FireDAC.so + true + + true @@ -177,18 +170,12 @@ true - + MARS_FireDAC.bpl true - - - bplMARS_FireDAC.so - true - - 1 @@ -202,12 +189,20 @@ classes 1 + + classes + 1 + res\xml 1 + + res\xml + 1 + @@ -220,12 +215,26 @@ library\lib\armeabi 1 + + library\lib\armeabi + 1 + + + + + library\lib\armeabi-v7a + 1 + library\lib\mips 1 + + library\lib\mips + 1 + @@ -233,84 +242,216 @@ library\lib\armeabi-v7a 1 + + library\lib\arm64-v8a + 1 + + + + + library\lib\armeabi-v7a + 1 + res\drawable 1 + + res\drawable + 1 + res\values 1 + + res\values + 1 + res\values-v21 1 + + res\values-v21 + 1 + + + + + res\values + 1 + + + res\values + 1 + res\drawable 1 + + res\drawable + 1 + res\drawable-xxhdpi 1 + + res\drawable-xxhdpi + 1 + res\drawable-ldpi 1 + + res\drawable-ldpi + 1 + res\drawable-mdpi 1 + + res\drawable-mdpi + 1 + res\drawable-hdpi 1 + + res\drawable-hdpi + 1 + res\drawable-xhdpi 1 + + res\drawable-xhdpi + 1 + + + + + res\drawable-mdpi + 1 + + + res\drawable-mdpi + 1 + + + + + res\drawable-hdpi + 1 + + + res\drawable-hdpi + 1 + + + + + res\drawable-xhdpi + 1 + + + res\drawable-xhdpi + 1 + + + + + res\drawable-xxhdpi + 1 + + + res\drawable-xxhdpi + 1 + + + + + res\drawable-xxxhdpi + 1 + + + res\drawable-xxxhdpi + 1 + res\drawable-small 1 + + res\drawable-small + 1 + res\drawable-normal 1 + + res\drawable-normal + 1 + res\drawable-large 1 + + res\drawable-large + 1 + res\drawable-xlarge 1 + + res\drawable-xlarge + 1 + + + + + res\values + 1 + + + res\values + 1 + @@ -380,6 +521,9 @@ 0 + + 0 + 0 @@ -410,6 +554,17 @@ 1 + + + 1 + + + 1 + + + 1 + + 1 @@ -421,6 +576,39 @@ 1 + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + 1 @@ -432,6 +620,61 @@ 1 + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + 1 @@ -443,6 +686,116 @@ 1 + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + 1 @@ -476,10 +829,35 @@ 1 + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + 1 + + 1 + @@ -523,6 +901,10 @@ library\lib\armeabi-v7a 1 + + library\lib\arm64-v8a + 1 + 1 @@ -545,6 +927,12 @@ 0 + + + library\lib\armeabi-v7a + 1 + + 1 @@ -582,6 +970,7 @@ + True diff --git a/Packages/103Rio/MARS.FireDAC.res b/Packages/103Rio/MARS.FireDAC.res index 83681a4b..527eb12d 100644 Binary files a/Packages/103Rio/MARS.FireDAC.res and b/Packages/103Rio/MARS.FireDAC.res differ diff --git a/Packages/103Rio/MARS.JOSE.dpk b/Packages/103Rio/MARS.JOSE.dpk index 84cf46b4..8cbee885 100644 --- a/Packages/103Rio/MARS.JOSE.dpk +++ b/Packages/103Rio/MARS.JOSE.dpk @@ -39,20 +39,30 @@ requires MARS.Core; contains + MARS.JOSEJWT.Token in '..\..\Source\MARS.JOSEJWT.Token.pas', + MARS.JOSEJWT.Token.InjectionService in '..\..\Source\MARS.JOSEJWT.Token.InjectionService.pas', + JOSE.Builder in '..\..\ThirdParty\delphi-jose-jwt\Source\JOSE.Builder.pas', + JOSE.Consumer in '..\..\ThirdParty\delphi-jose-jwt\Source\JOSE.Consumer.pas', + JOSE.Consumer.Validators in '..\..\ThirdParty\delphi-jose-jwt\Source\JOSE.Consumer.Validators.pas', + JOSE.Context in '..\..\ThirdParty\delphi-jose-jwt\Source\JOSE.Context.pas', JOSE.Core.Base in '..\..\ThirdParty\delphi-jose-jwt\Source\JOSE.Core.Base.pas', JOSE.Core.Builder in '..\..\ThirdParty\delphi-jose-jwt\Source\JOSE.Core.Builder.pas', + JOSE.Core.JWA.Compression in '..\..\ThirdParty\delphi-jose-jwt\Source\JOSE.Core.JWA.Compression.pas', + JOSE.Core.JWA.Encryption in '..\..\ThirdParty\delphi-jose-jwt\Source\JOSE.Core.JWA.Encryption.pas', + JOSE.Core.JWA.Factory in '..\..\ThirdParty\delphi-jose-jwt\Source\JOSE.Core.JWA.Factory.pas', JOSE.Core.JWA in '..\..\ThirdParty\delphi-jose-jwt\Source\JOSE.Core.JWA.pas', + JOSE.Core.JWA.Signing in '..\..\ThirdParty\delphi-jose-jwt\Source\JOSE.Core.JWA.Signing.pas', JOSE.Core.JWE in '..\..\ThirdParty\delphi-jose-jwt\Source\JOSE.Core.JWE.pas', JOSE.Core.JWK in '..\..\ThirdParty\delphi-jose-jwt\Source\JOSE.Core.JWK.pas', JOSE.Core.JWS in '..\..\ThirdParty\delphi-jose-jwt\Source\JOSE.Core.JWS.pas', JOSE.Core.JWT in '..\..\ThirdParty\delphi-jose-jwt\Source\JOSE.Core.JWT.pas', JOSE.Core.Parts in '..\..\ThirdParty\delphi-jose-jwt\Source\JOSE.Core.Parts.pas', - JOSE.Cryptography.RSA in '..\..\ThirdParty\delphi-jose-jwt\Source\JOSE.Cryptography.RSA.pas', JOSE.Encoding.Base64 in '..\..\ThirdParty\delphi-jose-jwt\Source\JOSE.Encoding.Base64.pas', JOSE.Hashing.HMAC in '..\..\ThirdParty\delphi-jose-jwt\Source\JOSE.Hashing.HMAC.pas', + JOSE.Signing.RSA in '..\..\ThirdParty\delphi-jose-jwt\Source\JOSE.Signing.RSA.pas', + JOSE.Types.Arrays in '..\..\ThirdParty\delphi-jose-jwt\Source\JOSE.Types.Arrays.pas', JOSE.Types.Bytes in '..\..\ThirdParty\delphi-jose-jwt\Source\JOSE.Types.Bytes.pas', - MARS.JOSEJWT.Token in '..\..\Source\MARS.JOSEJWT.Token.pas', - MARS.JOSEJWT.Token.InjectionService in '..\..\Source\MARS.JOSEJWT.Token.InjectionService.pas'; + JOSE.Types.JSON in '..\..\ThirdParty\delphi-jose-jwt\Source\JOSE.Types.JSON.pas'; end. diff --git a/Packages/103Rio/MARS.JOSE.dproj b/Packages/103Rio/MARS.JOSE.dproj index b7f4e8f2..4076d19b 100644 --- a/Packages/103Rio/MARS.JOSE.dproj +++ b/Packages/103Rio/MARS.JOSE.dproj @@ -2,7 +2,7 @@ {16057797-C497-46DA-B133-A45E7F564D8D} MARS.JOSE.dpk - 18.5 + 18.8 None True Release @@ -136,20 +136,30 @@ + + + + + + + + + + - + + - - + Cfg_2 Base @@ -171,10 +181,8 @@ MARS.JOSE.dpk - DBExpress Enterprise Data Explorer Integration - Microsoft Office 2000 Sample Automation Server Wrapper Components - Microsoft Office XP Sample Automation Server Wrapper Components - CodeSite Express 5.3.3 + Microsoft Office 2000 Sample Automation Server Wrapper Components + Microsoft Office XP Sample Automation Server Wrapper Components @@ -183,12 +191,6 @@ true - - - MARS_JOSE.bpl - true - - true @@ -214,6 +216,18 @@ true + + + MARS_JOSE.bpl + true + + + + + MARS_JOSE.bpl + true + + 1 @@ -227,12 +241,20 @@ classes 1 + + classes + 1 + res\xml 1 + + res\xml + 1 + @@ -245,12 +267,26 @@ library\lib\armeabi 1 + + library\lib\armeabi + 1 + + + + + library\lib\armeabi-v7a + 1 + library\lib\mips 1 + + library\lib\mips + 1 + @@ -258,84 +294,216 @@ library\lib\armeabi-v7a 1 + + library\lib\arm64-v8a + 1 + + + + + library\lib\armeabi-v7a + 1 + res\drawable 1 + + res\drawable + 1 + res\values 1 + + res\values + 1 + res\values-v21 1 + + res\values-v21 + 1 + + + + + res\values + 1 + + + res\values + 1 + res\drawable 1 + + res\drawable + 1 + res\drawable-xxhdpi 1 + + res\drawable-xxhdpi + 1 + res\drawable-ldpi 1 + + res\drawable-ldpi + 1 + res\drawable-mdpi 1 + + res\drawable-mdpi + 1 + res\drawable-hdpi 1 + + res\drawable-hdpi + 1 + res\drawable-xhdpi 1 + + res\drawable-xhdpi + 1 + + + + + res\drawable-mdpi + 1 + + + res\drawable-mdpi + 1 + + + + + res\drawable-hdpi + 1 + + + res\drawable-hdpi + 1 + + + + + res\drawable-xhdpi + 1 + + + res\drawable-xhdpi + 1 + + + + + res\drawable-xxhdpi + 1 + + + res\drawable-xxhdpi + 1 + + + + + res\drawable-xxxhdpi + 1 + + + res\drawable-xxxhdpi + 1 + res\drawable-small 1 + + res\drawable-small + 1 + res\drawable-normal 1 + + res\drawable-normal + 1 + res\drawable-large 1 + + res\drawable-large + 1 + res\drawable-xlarge 1 + + res\drawable-xlarge + 1 + + + + + res\values + 1 + + + res\values + 1 + @@ -405,6 +573,9 @@ 0 + + 0 + 0 @@ -435,6 +606,17 @@ 1 + + + 1 + + + 1 + + + 1 + + 1 @@ -446,6 +628,39 @@ 1 + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + 1 @@ -457,6 +672,61 @@ 1 + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + 1 @@ -468,6 +738,116 @@ 1 + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + 1 @@ -501,10 +881,35 @@ 1 + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + 1 + + 1 + @@ -548,6 +953,10 @@ library\lib\armeabi-v7a 1 + + library\lib\arm64-v8a + 1 + 1 @@ -570,6 +979,12 @@ 0 + + + library\lib\armeabi-v7a + 1 + + 1 @@ -607,6 +1022,7 @@ + True diff --git a/Packages/103Rio/MARS.ReadersAndWriters.dpk b/Packages/103Rio/MARS.ReadersAndWriters.dpk index 015a5758..00ee25cf 100644 --- a/Packages/103Rio/MARS.ReadersAndWriters.dpk +++ b/Packages/103Rio/MARS.ReadersAndWriters.dpk @@ -9,23 +9,23 @@ package MARS.ReadersAndWriters; {$EXTENDEDSYNTAX ON} {$IMPORTEDDATA ON} {$IOCHECKS ON} -{$LOCALSYMBOLS OFF} +{$LOCALSYMBOLS ON} {$LONGSTRINGS ON} {$OPENSTRINGS ON} -{$OPTIMIZATION ON} +{$OPTIMIZATION OFF} {$OVERFLOWCHECKS OFF} {$RANGECHECKS OFF} -{$REFERENCEINFO OFF} +{$REFERENCEINFO ON} {$SAFEDIVIDE OFF} -{$STACKFRAMES OFF} +{$STACKFRAMES ON} {$TYPEDADDRESS OFF} {$VARSTRINGCHECKS ON} {$WRITEABLECONST OFF} {$MINENUMSIZE 1} {$IMAGEBASE $400000} -{$DEFINE RELEASE} +{$DEFINE DEBUG} {$ENDIF IMPLICITBUILDING} -{$DESCRIPTION 'MARS-Curiosity REST Library'} +{$DESCRIPTION 'MARS-Curiosity Readers and Writers'} {$LIBSUFFIX '260'} {$RUNONLY} {$IMPLICITBUILD OFF} diff --git a/Packages/103Rio/MARS.ReadersAndWriters.dproj b/Packages/103Rio/MARS.ReadersAndWriters.dproj index 7997e9a5..17160347 100644 --- a/Packages/103Rio/MARS.ReadersAndWriters.dproj +++ b/Packages/103Rio/MARS.ReadersAndWriters.dproj @@ -1,603 +1,992 @@ - - - {5170D912-4303-47AD-85A6-D77D3D0874DA} - MARS.ReadersAndWriters.dpk - 18.5 - None - True - Release - Win32 - 131 - Package - - - true - - - true - Base - true - - - true - Base - true - - - true - Base - true - - - true - Cfg_1 - true - true - - - true - Cfg_1 - true - true - - - true - Base - true - - - true - Cfg_2 - true - true - - - true - ..\..\Source;$(DCC_UnitSearchPath) - true - 260 - MARS-Curiosity REST Library - CompanyName=;FileDescription=;FileVersion=1.0.0.0;InternalName=;LegalCopyright=;LegalTrademarks=;OriginalFilename=;ProductName=;ProductVersion=1.0.0.0;Comments= - 1040 - true - System;Xml;Data;Datasnap;Web;Soap;$(DCC_Namespace) - true - MARS_ReadersAndWriters - ..\..\Lib260\$(Platform)\$(Config) - .\$(Platform)\$(Config) - false - false - false - false - false - - - true - Winapi;System.Win;Data.Win;Datasnap.Win;Web.Win;Soap.Win;Xml.Win;Bde;$(DCC_Namespace) - 1033 - CompanyName=;FileVersion=1.0.0.0;InternalName=;LegalCopyright=;LegalTrademarks=;OriginalFilename=;ProductVersion=1.0.0.0;Comments=;ProgramID=com.embarcadero.$(MSBuildProjectName);FileDescription=$(MSBuildProjectName);ProductName=$(MSBuildProjectName) - - - 1033 - true - CompanyName=;FileDescription=$(MSBuildProjectName);FileVersion=1.0.0.0;InternalName=;LegalCopyright=;LegalTrademarks=;OriginalFilename=;ProgramID=com.embarcadero.$(MSBuildProjectName);ProductName=$(MSBuildProjectName);ProductVersion=1.0.0.0;Comments= - Winapi;System.Win;Data.Win;Datasnap.Win;Web.Win;Soap.Win;Xml.Win;$(DCC_Namespace) - Debug - - - DEBUG;$(DCC_Define) - true - false - true - true - true - - - CompanyName=;FileVersion=1.0.0.0;InternalName=;LegalCopyright=;LegalTrademarks=;OriginalFilename=;ProductVersion=1.0.0.0;Comments=;ProgramID=com.embarcadero.$(MSBuildProjectName);FileDescription=$(MSBuildProjectName);ProductName=$(MSBuildProjectName) - MARS-Curiosity Readers and Writers - true - 1033 - false - - - true - 1033 - CompanyName=;FileDescription=$(MSBuildProjectName);FileVersion=1.0.0.0;InternalName=;LegalCopyright=;LegalTrademarks=;OriginalFilename=;ProgramID=com.embarcadero.$(MSBuildProjectName);ProductName=$(MSBuildProjectName);ProductVersion=1.0.0.0;Comments= - - - false - RELEASE;$(DCC_Define) - 0 - 0 - - - true - 1033 - CompanyName=;FileVersion=1.0.0.0;InternalName=;LegalCopyright=;LegalTrademarks=;OriginalFilename=;ProductVersion=1.0.0.0;Comments=;ProgramID=com.embarcadero.$(MSBuildProjectName);FileDescription=$(MSBuildProjectName);ProductName=$(MSBuildProjectName) - - - - MainSource - - - - - - - - - - - - - - - Cfg_2 - Base - - - Base - - - Cfg_1 - Base - - - - Delphi.Personality.12 - Package - - - - MARS.ReadersAndWriters.dpk - - - DBExpress Enterprise Data Explorer Integration - Microsoft Office 2000 Sample Automation Server Wrapper Components - Microsoft Office XP Sample Automation Server Wrapper Components - CodeSite Express 5.3.3 - - - - - - true - - - - - MARS_ReadersAndWriters.bpl - true - - - - - true - - - - - true - - - - - true - - - - - true - - - - - true - - - - - 1 - - - 0 - - - - - classes - 1 - - - - - res\xml - 1 - - - - - library\lib\armeabi-v7a - 1 - - - - - library\lib\armeabi - 1 - - - - - library\lib\mips - 1 - - - - - - library\lib\armeabi-v7a - 1 - - - - - res\drawable - 1 - - - - - res\values - 1 - - - - - res\values-v21 - 1 - - - - - res\drawable - 1 - - - - - res\drawable-xxhdpi - 1 - - - - - res\drawable-ldpi - 1 - - - - - res\drawable-mdpi - 1 - - - - - res\drawable-hdpi - 1 - - - - - res\drawable-xhdpi - 1 - - - - - res\drawable-small - 1 - - - - - res\drawable-normal - 1 - - - - - res\drawable-large - 1 - - - - - res\drawable-xlarge - 1 - - - - - 1 - - - 1 - - - 0 - - - - - 1 - .framework - - - 1 - .framework - - - 0 - - - - - 1 - .dylib - - - 1 - .dylib - - - 0 - .dll;.bpl - - - - - 1 - .dylib - - - 1 - .dylib - - - 1 - .dylib - - - 1 - .dylib - - - 1 - .dylib - - - 0 - .bpl - - - - - 0 - - - 0 - - - 0 - - - 0 - - - 0 - - - 0 - - - 0 - - - - - 1 - - - 1 - - - 1 - - - - - 1 - - - 1 - - - 1 - - - - - 1 - - - 1 - - - 1 - - - - - 1 - - - 1 - - - 1 - - - - - 1 - - - 1 - - - 1 - - - - - 1 - - - 1 - - - 1 - - - - - 1 - - - 1 - - - 1 - - - - - 1 - - - - - ..\$(PROJECTNAME).app.dSYM\Contents\Resources\DWARF - 1 - - - ..\$(PROJECTNAME).app.dSYM\Contents\Resources\DWARF - 1 - - - - - - - - 1 - - - 1 - - - 1 - - - - - - - - Contents\Resources - 1 - - - Contents\Resources - 1 - - - - - library\lib\armeabi-v7a - 1 - - - 1 - - - 1 - - - 1 - - - 1 - - - 1 - - - 1 - - - 0 - - - - - 1 - - - 1 - - - - - Assets - 1 - - - Assets - 1 - - - - - Assets - 1 - - - Assets - 1 - - - - - - - - - - - - - - True - True - True - - - 12 - - - - - + + + {5170D912-4303-47AD-85A6-D77D3D0874DA} + MARS.ReadersAndWriters.dpk + 18.8 + None + True + Release + Win32 + 131 + Package + + + true + + + true + Base + true + + + true + Base + true + + + true + Base + true + + + true + Cfg_1 + true + true + + + true + Cfg_1 + true + true + + + true + Base + true + + + true + ..\..\Source;$(DCC_UnitSearchPath) + true + 260 + MARS-Curiosity REST Library + CompanyName=;FileDescription=;FileVersion=1.0.0.0;InternalName=;LegalCopyright=;LegalTrademarks=;OriginalFilename=;ProductName=;ProductVersion=1.0.0.0;Comments= + 1040 + true + System;Xml;Data;Datasnap;Web;Soap;$(DCC_Namespace) + true + MARS_ReadersAndWriters + ..\..\Lib260\$(Platform)\$(Config) + .\$(Platform)\$(Config) + false + false + false + false + false + + + true + Winapi;System.Win;Data.Win;Datasnap.Win;Web.Win;Soap.Win;Xml.Win;Bde;$(DCC_Namespace) + 1033 + CompanyName=;FileVersion=1.0.0.0;InternalName=;LegalCopyright=;LegalTrademarks=;OriginalFilename=;ProductVersion=1.0.0.0;Comments=;ProgramID=com.embarcadero.$(MSBuildProjectName);FileDescription=$(MSBuildProjectName);ProductName=$(MSBuildProjectName) + + + 1033 + true + CompanyName=;FileDescription=$(MSBuildProjectName);FileVersion=1.0.0.0;InternalName=;LegalCopyright=;LegalTrademarks=;OriginalFilename=;ProgramID=com.embarcadero.$(MSBuildProjectName);ProductName=$(MSBuildProjectName);ProductVersion=1.0.0.0;Comments= + Winapi;System.Win;Data.Win;Datasnap.Win;Web.Win;Soap.Win;Xml.Win;$(DCC_Namespace) + Debug + + + DEBUG;$(DCC_Define) + true + false + true + true + true + + + CompanyName=;FileVersion=1.0.0.0;InternalName=;LegalCopyright=;LegalTrademarks=;OriginalFilename=;ProductVersion=1.0.0.0;Comments=;ProgramID=com.embarcadero.$(MSBuildProjectName);FileDescription=$(MSBuildProjectName);ProductName=$(MSBuildProjectName) + MARS-Curiosity Readers and Writers + true + 1033 + false + + + true + 1033 + CompanyName=;FileDescription=$(MSBuildProjectName);FileVersion=1.0.0.0;InternalName=;LegalCopyright=;LegalTrademarks=;OriginalFilename=;ProgramID=com.embarcadero.$(MSBuildProjectName);ProductName=$(MSBuildProjectName);ProductVersion=1.0.0.0;Comments= + + + false + RELEASE;$(DCC_Define) + 0 + 0 + + + + MainSource + + + + + + + + + + + + + + + Cfg_2 + Base + + + Base + + + Cfg_1 + Base + + + + Delphi.Personality.12 + Package + + + + MARS.ReadersAndWriters.dpk + + + Microsoft Office 2000 Sample Automation Server Wrapper Components + Microsoft Office XP Sample Automation Server Wrapper Components + + + + + + true + + + + + MARS_ReadersAndWriters.bpl + true + + + + + true + + + + + true + + + + + true + + + + + true + + + + + true + + + + + 1 + + + 0 + + + + + classes + 1 + + + classes + 1 + + + + + res\xml + 1 + + + res\xml + 1 + + + + + library\lib\armeabi-v7a + 1 + + + + + library\lib\armeabi + 1 + + + library\lib\armeabi + 1 + + + + + library\lib\armeabi-v7a + 1 + + + + + library\lib\mips + 1 + + + library\lib\mips + 1 + + + + + + library\lib\armeabi-v7a + 1 + + + library\lib\arm64-v8a + 1 + + + + + library\lib\armeabi-v7a + 1 + + + + + res\drawable + 1 + + + res\drawable + 1 + + + + + res\values + 1 + + + res\values + 1 + + + + + res\values-v21 + 1 + + + res\values-v21 + 1 + + + + + res\values + 1 + + + res\values + 1 + + + + + res\drawable + 1 + + + res\drawable + 1 + + + + + res\drawable-xxhdpi + 1 + + + res\drawable-xxhdpi + 1 + + + + + res\drawable-ldpi + 1 + + + res\drawable-ldpi + 1 + + + + + res\drawable-mdpi + 1 + + + res\drawable-mdpi + 1 + + + + + res\drawable-hdpi + 1 + + + res\drawable-hdpi + 1 + + + + + res\drawable-xhdpi + 1 + + + res\drawable-xhdpi + 1 + + + + + res\drawable-mdpi + 1 + + + res\drawable-mdpi + 1 + + + + + res\drawable-hdpi + 1 + + + res\drawable-hdpi + 1 + + + + + res\drawable-xhdpi + 1 + + + res\drawable-xhdpi + 1 + + + + + res\drawable-xxhdpi + 1 + + + res\drawable-xxhdpi + 1 + + + + + res\drawable-xxxhdpi + 1 + + + res\drawable-xxxhdpi + 1 + + + + + res\drawable-small + 1 + + + res\drawable-small + 1 + + + + + res\drawable-normal + 1 + + + res\drawable-normal + 1 + + + + + res\drawable-large + 1 + + + res\drawable-large + 1 + + + + + res\drawable-xlarge + 1 + + + res\drawable-xlarge + 1 + + + + + res\values + 1 + + + res\values + 1 + + + + + 1 + + + 1 + + + 0 + + + + + 1 + .framework + + + 1 + .framework + + + 0 + + + + + 1 + .dylib + + + 1 + .dylib + + + 0 + .dll;.bpl + + + + + 1 + .dylib + + + 1 + .dylib + + + 1 + .dylib + + + 1 + .dylib + + + 1 + .dylib + + + 0 + .bpl + + + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + + + ..\$(PROJECTNAME).app.dSYM\Contents\Resources\DWARF + 1 + + + ..\$(PROJECTNAME).app.dSYM\Contents\Resources\DWARF + 1 + + + + + + + + 1 + + + 1 + + + 1 + + + + + + + + Contents\Resources + 1 + + + Contents\Resources + 1 + + + + + library\lib\armeabi-v7a + 1 + + + library\lib\arm64-v8a + 1 + + + 1 + + + 1 + + + 1 + + + 1 + + + 1 + + + 1 + + + 0 + + + + + library\lib\armeabi-v7a + 1 + + + + + 1 + + + 1 + + + + + Assets + 1 + + + Assets + 1 + + + + + Assets + 1 + + + Assets + 1 + + + + + + + + + + + + + + + True + True + True + + + 12 + + + + + diff --git a/Packages/103Rio/MARS.ReadersAndWriters.res b/Packages/103Rio/MARS.ReadersAndWriters.res index f389daf5..7039c993 100644 Binary files a/Packages/103Rio/MARS.ReadersAndWriters.res and b/Packages/103Rio/MARS.ReadersAndWriters.res differ diff --git a/Packages/103Rio/MARS.UniDAC.dpk b/Packages/103Rio/MARS.UniDAC.dpk new file mode 100644 index 00000000..304921fa --- /dev/null +++ b/Packages/103Rio/MARS.UniDAC.dpk @@ -0,0 +1,47 @@ +package MARS.UniDAC; + +{$R *.res} +{$IFDEF IMPLICITBUILDING This IFDEF should not be used by users} +{$ALIGN 8} +{$ASSERTIONS ON} +{$BOOLEVAL OFF} +{$DEBUGINFO OFF} +{$EXTENDEDSYNTAX ON} +{$IMPORTEDDATA ON} +{$IOCHECKS ON} +{$LOCALSYMBOLS OFF} +{$LONGSTRINGS ON} +{$OPENSTRINGS ON} +{$OPTIMIZATION ON} +{$OVERFLOWCHECKS OFF} +{$RANGECHECKS OFF} +{$REFERENCEINFO OFF} +{$SAFEDIVIDE OFF} +{$STACKFRAMES OFF} +{$TYPEDADDRESS OFF} +{$VARSTRINGCHECKS ON} +{$WRITEABLECONST OFF} +{$MINENUMSIZE 1} +{$IMAGEBASE $400000} +{$DEFINE RELEASE} +{$ENDIF IMPLICITBUILDING} +{$DESCRIPTION 'MARS-Curiosity REST Library'} +{$LIBSUFFIX '260'} +{$RUNONLY} +{$IMPLICITBUILD OFF} + +requires + rtl, + dbrtl, + MARS.Core, + MARS.ReadersAndWriters, + unidac260, + dac260; + +contains + MARS.Data.UniDAC.InjectionService in '..\..\Source\MARS.Data.UniDAC.InjectionService.pas', + MARS.Data.UniDAC in '..\..\Source\MARS.Data.UniDAC.pas', + MARS.Data.UniDAC.ReadersAndWriters in '..\..\Source\MARS.Data.UniDAC.ReadersAndWriters.pas', + MARS.Data.UniDAC.Utils in '..\..\Source\MARS.Data.UniDAC.Utils.pas'; + +end. diff --git a/Packages/103Rio/MARS.UniDAC.dproj b/Packages/103Rio/MARS.UniDAC.dproj new file mode 100644 index 00000000..db7e6063 --- /dev/null +++ b/Packages/103Rio/MARS.UniDAC.dproj @@ -0,0 +1,989 @@ + + + {0B4F7444-23A8-436B-80C9-F0387EFF9DCA} + MARS.UniDAC.dpk + 18.8 + FMX + True + Release + Win32 + 1 + Package + + + true + + + true + Base + true + + + true + Base + true + + + true + Cfg_1 + true + true + + + true + Base + true + + + true + Cfg_2 + true + true + + + true + true + ..\..\Source;$(DCC_UnitSearchPath) + 260 + MARS-Curiosity REST Library + CompanyName=;FileDescription=;FileVersion=1.0.0.0;InternalName=;LegalCopyright=;LegalTrademarks=;OriginalFilename=;ProductName=;ProductVersion=1.0.0.0;Comments= + 1040 + true + System;Xml;Data;Datasnap;Web;Soap;$(DCC_Namespace) + true + MARS_UniDAC + ..\..\Lib260\$(Platform)\$(Config) + .\$(Platform)\$(Config) + false + false + false + false + false + + + rtl;dbrtl;fmx;inet;IndySystem;IndyProtocols;IndyCore;FireDAC;FireDACCommonDriver;FireDACCommon;fmxFireDAC;dsnap;DataSnapFireDAC;$(DCC_UsePackage) + true + Winapi;System.Win;Data.Win;Datasnap.Win;Web.Win;Soap.Win;Xml.Win;Bde;$(DCC_Namespace) + 1033 + CompanyName=;FileVersion=1.0.0.0;InternalName=;LegalCopyright=;LegalTrademarks=;OriginalFilename=;ProductVersion=1.0.0.0;Comments=;ProgramID=com.embarcadero.$(ModuleName);FileDescription=$(ModuleName);ProductName=$(ModuleName) + + + DEBUG;$(DCC_Define) + true + false + true + true + true + + + CompanyName=;FileVersion=1.0.0.0;InternalName=;LegalCopyright=;LegalTrademarks=;OriginalFilename=;ProductVersion=1.0.0.0;Comments=;ProgramID=com.embarcadero.$(MSBuildProjectName);FileDescription=$(MSBuildProjectName);ProductName=$(MSBuildProjectName) + MARS-Curiosity FireDAC + true + 1033 + false + + + false + RELEASE;$(DCC_Define) + 0 + 0 + + + true + 1033 + CompanyName=;FileVersion=1.0.0.0;InternalName=;LegalCopyright=;LegalTrademarks=;OriginalFilename=;ProductVersion=1.0.0.0;Comments=;ProgramID=com.embarcadero.$(MSBuildProjectName);FileDescription=$(MSBuildProjectName);ProductName=$(MSBuildProjectName) + + + + MainSource + + + + + + + + + + + + + Cfg_2 + Base + + + Base + + + Cfg_1 + Base + + + + Delphi.Personality.12 + Package + + + + MARS.UniDAC.dpk + + + Microsoft Office 2000 Sample Automation Server Wrapper Components + Microsoft Office XP Sample Automation Server Wrapper Components + + + + + + true + + + + + MARS_UniDAC.bpl + true + + + + + true + + + + + true + + + + + true + + + + + true + + + + + true + + + + + MARS_UniDAC.bpl + true + + + + + bplMARS_UniDAC.so + true + + + + + 1 + + + 0 + + + + + classes + 1 + + + classes + 1 + + + + + res\xml + 1 + + + res\xml + 1 + + + + + library\lib\armeabi-v7a + 1 + + + + + library\lib\armeabi + 1 + + + library\lib\armeabi + 1 + + + + + library\lib\armeabi-v7a + 1 + + + + + library\lib\mips + 1 + + + library\lib\mips + 1 + + + + + + library\lib\armeabi-v7a + 1 + + + library\lib\arm64-v8a + 1 + + + + + library\lib\armeabi-v7a + 1 + + + + + res\drawable + 1 + + + res\drawable + 1 + + + + + res\values + 1 + + + res\values + 1 + + + + + res\values-v21 + 1 + + + res\values-v21 + 1 + + + + + res\values + 1 + + + res\values + 1 + + + + + res\drawable + 1 + + + res\drawable + 1 + + + + + res\drawable-xxhdpi + 1 + + + res\drawable-xxhdpi + 1 + + + + + res\drawable-ldpi + 1 + + + res\drawable-ldpi + 1 + + + + + res\drawable-mdpi + 1 + + + res\drawable-mdpi + 1 + + + + + res\drawable-hdpi + 1 + + + res\drawable-hdpi + 1 + + + + + res\drawable-xhdpi + 1 + + + res\drawable-xhdpi + 1 + + + + + res\drawable-mdpi + 1 + + + res\drawable-mdpi + 1 + + + + + res\drawable-hdpi + 1 + + + res\drawable-hdpi + 1 + + + + + res\drawable-xhdpi + 1 + + + res\drawable-xhdpi + 1 + + + + + res\drawable-xxhdpi + 1 + + + res\drawable-xxhdpi + 1 + + + + + res\drawable-xxxhdpi + 1 + + + res\drawable-xxxhdpi + 1 + + + + + res\drawable-small + 1 + + + res\drawable-small + 1 + + + + + res\drawable-normal + 1 + + + res\drawable-normal + 1 + + + + + res\drawable-large + 1 + + + res\drawable-large + 1 + + + + + res\drawable-xlarge + 1 + + + res\drawable-xlarge + 1 + + + + + res\values + 1 + + + res\values + 1 + + + + + 1 + + + 1 + + + 0 + + + + + 1 + .framework + + + 1 + .framework + + + 0 + + + + + 1 + .dylib + + + 1 + .dylib + + + 0 + .dll;.bpl + + + + + 1 + .dylib + + + 1 + .dylib + + + 1 + .dylib + + + 1 + .dylib + + + 1 + .dylib + + + 0 + .bpl + + + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + + + ..\$(PROJECTNAME).app.dSYM\Contents\Resources\DWARF + 1 + + + ..\$(PROJECTNAME).app.dSYM\Contents\Resources\DWARF + 1 + + + + + + + + 1 + + + 1 + + + 1 + + + + + + + + Contents\Resources + 1 + + + Contents\Resources + 1 + + + + + library\lib\armeabi-v7a + 1 + + + library\lib\arm64-v8a + 1 + + + 1 + + + 1 + + + 1 + + + 1 + + + 1 + + + 1 + + + 0 + + + + + library\lib\armeabi-v7a + 1 + + + + + 1 + + + 1 + + + + + Assets + 1 + + + Assets + 1 + + + + + Assets + 1 + + + Assets + 1 + + + + + + + + + + + + + + + True + + + 12 + + + + + diff --git a/Packages/103Rio/MARS.UniDAC.res b/Packages/103Rio/MARS.UniDAC.res new file mode 100644 index 00000000..9b419e54 Binary files /dev/null and b/Packages/103Rio/MARS.UniDAC.res differ diff --git a/Packages/103Rio/MARS.Utils.dpk b/Packages/103Rio/MARS.Utils.dpk index 87907dc2..5d05fd26 100644 --- a/Packages/103Rio/MARS.Utils.dpk +++ b/Packages/103Rio/MARS.Utils.dpk @@ -9,23 +9,23 @@ package MARS.Utils; {$EXTENDEDSYNTAX ON} {$IMPORTEDDATA ON} {$IOCHECKS ON} -{$LOCALSYMBOLS OFF} +{$LOCALSYMBOLS ON} {$LONGSTRINGS ON} {$OPENSTRINGS ON} -{$OPTIMIZATION ON} +{$OPTIMIZATION OFF} {$OVERFLOWCHECKS OFF} {$RANGECHECKS OFF} -{$REFERENCEINFO OFF} +{$REFERENCEINFO ON} {$SAFEDIVIDE OFF} -{$STACKFRAMES OFF} +{$STACKFRAMES ON} {$TYPEDADDRESS OFF} {$VARSTRINGCHECKS ON} {$WRITEABLECONST OFF} {$MINENUMSIZE 1} {$IMAGEBASE $400000} -{$DEFINE RELEASE} +{$DEFINE DEBUG} {$ENDIF IMPLICITBUILDING} -{$DESCRIPTION 'MARS-Curiosity REST Library'} +{$DESCRIPTION 'MARS-Curiosity Utils'} {$LIBSUFFIX '260'} {$RUNONLY} {$IMPLICITBUILD OFF} @@ -51,6 +51,9 @@ contains MARS.Core.Exceptions in '..\..\Source\MARS.Core.Exceptions.pas', MARS.Core.Declarations in '..\..\Source\MARS.Core.Declarations.pas', MARS.Core.MediaType in '..\..\Source\MARS.Core.MediaType.pas', - MARS.Utils.JWT in '..\..\Source\MARS.Utils.JWT.pas'; + MARS.Utils.JWT in '..\..\Source\MARS.Utils.JWT.pas', + MARS.Core.RequestAndResponse.Interfaces in '..\..\Source\MARS.Core.RequestAndResponse.Interfaces.pas'; end. + + diff --git a/Packages/103Rio/MARS.Utils.dproj b/Packages/103Rio/MARS.Utils.dproj index 11357f6b..29e50c05 100644 --- a/Packages/103Rio/MARS.Utils.dproj +++ b/Packages/103Rio/MARS.Utils.dproj @@ -1,616 +1,1006 @@ - - - {A5387B60-F9BE-4AEF-8E11-EFB0EDCAB2ED} - MARS.Utils.dpk - 18.5 - None - True - Release - Win32 - 131 - Package - - - true - - - true - Base - true - - - true - Base - true - - - true - Base - true - - - true - Cfg_1 - true - true - - - true - Cfg_1 - true - true - - - true - Base - true - - - true - Cfg_2 - true - true - - - true - ..\..\Source;$(DCC_UnitSearchPath) - true - 260 - MARS-Curiosity REST Library - CompanyName=;FileDescription=;FileVersion=1.0.0.0;InternalName=;LegalCopyright=;LegalTrademarks=;OriginalFilename=;ProductName=;ProductVersion=1.0.0.0;Comments= - 1040 - true - System;Xml;Data;Datasnap;Web;Soap;$(DCC_Namespace) - true - MARS_Utils - ..\..\Lib260\$(Platform)\$(Config) - .\$(Platform)\$(Config) - false - false - false - false - false - - - true - Winapi;System.Win;Data.Win;Datasnap.Win;Web.Win;Soap.Win;Xml.Win;Bde;$(DCC_Namespace) - 1033 - CompanyName=;FileVersion=1.0.0.0;InternalName=;LegalCopyright=;LegalTrademarks=;OriginalFilename=;ProductVersion=1.0.0.0;Comments=;ProgramID=com.embarcadero.$(MSBuildProjectName);FileDescription=$(MSBuildProjectName);ProductName=$(MSBuildProjectName) - - - 1033 - true - CompanyName=;FileDescription=$(MSBuildProjectName);FileVersion=1.0.0.0;InternalName=;LegalCopyright=;LegalTrademarks=;OriginalFilename=;ProgramID=com.embarcadero.$(MSBuildProjectName);ProductName=$(MSBuildProjectName);ProductVersion=1.0.0.0;Comments= - Winapi;System.Win;Data.Win;Datasnap.Win;Web.Win;Soap.Win;Xml.Win;$(DCC_Namespace) - Debug - - - DEBUG;$(DCC_Define) - true - false - true - true - true - - - CompanyName=;FileVersion=1.0.0.0;InternalName=;LegalCopyright=;LegalTrademarks=;OriginalFilename=;ProductVersion=1.0.0.0;Comments=;ProgramID=com.embarcadero.$(MSBuildProjectName);FileDescription=$(MSBuildProjectName);ProductName=$(MSBuildProjectName) - MARS-Curiosity Utils - true - 1033 - false - - - true - 1033 - CompanyName=;FileDescription=$(MSBuildProjectName);FileVersion=1.0.0.0;InternalName=;LegalCopyright=;LegalTrademarks=;OriginalFilename=;ProgramID=com.embarcadero.$(MSBuildProjectName);ProductName=$(MSBuildProjectName);ProductVersion=1.0.0.0;Comments= - - - false - RELEASE;$(DCC_Define) - 0 - 0 - - - true - 1033 - CompanyName=;FileVersion=1.0.0.0;InternalName=;LegalCopyright=;LegalTrademarks=;OriginalFilename=;ProductVersion=1.0.0.0;Comments=;ProgramID=com.embarcadero.$(MSBuildProjectName);FileDescription=$(MSBuildProjectName);ProductName=$(MSBuildProjectName) - - - - MainSource - - - - - - - - - - - - - - - - - - - - - - Cfg_2 - Base - - - Base - - - Cfg_1 - Base - - - - Delphi.Personality.12 - Package - - - - MARS.Utils.dpk - - - DBExpress Enterprise Data Explorer Integration - Microsoft Office 2000 Sample Automation Server Wrapper Components - Microsoft Office XP Sample Automation Server Wrapper Components - CodeSite Express 5.3.3 - - - - - - true - - - - - MARS_Utils.bpl - true - - - - - MARS_Utils.bpl - true - - - - - true - - - - - true - - - - - true - - - - - true - - - - - true - - - - - 1 - - - 0 - - - - - classes - 1 - - - - - res\xml - 1 - - - - - library\lib\armeabi-v7a - 1 - - - - - library\lib\armeabi - 1 - - - - - library\lib\mips - 1 - - - - - - library\lib\armeabi-v7a - 1 - - - - - res\drawable - 1 - - - - - res\values - 1 - - - - - res\values-v21 - 1 - - - - - res\drawable - 1 - - - - - res\drawable-xxhdpi - 1 - - - - - res\drawable-ldpi - 1 - - - - - res\drawable-mdpi - 1 - - - - - res\drawable-hdpi - 1 - - - - - res\drawable-xhdpi - 1 - - - - - res\drawable-small - 1 - - - - - res\drawable-normal - 1 - - - - - res\drawable-large - 1 - - - - - res\drawable-xlarge - 1 - - - - - 1 - - - 1 - - - 0 - - - - - 1 - .framework - - - 1 - .framework - - - 0 - - - - - 1 - .dylib - - - 1 - .dylib - - - 0 - .dll;.bpl - - - - - 1 - .dylib - - - 1 - .dylib - - - 1 - .dylib - - - 1 - .dylib - - - 1 - .dylib - - - 0 - .bpl - - - - - 0 - - - 0 - - - 0 - - - 0 - - - 0 - - - 0 - - - 0 - - - - - 1 - - - 1 - - - 1 - - - - - 1 - - - 1 - - - 1 - - - - - 1 - - - 1 - - - 1 - - - - - 1 - - - 1 - - - 1 - - - - - 1 - - - 1 - - - 1 - - - - - 1 - - - 1 - - - 1 - - - - - 1 - - - 1 - - - 1 - - - - - 1 - - - - - ..\$(PROJECTNAME).app.dSYM\Contents\Resources\DWARF - 1 - - - ..\$(PROJECTNAME).app.dSYM\Contents\Resources\DWARF - 1 - - - - - - - - 1 - - - 1 - - - 1 - - - - - - - - Contents\Resources - 1 - - - Contents\Resources - 1 - - - - - library\lib\armeabi-v7a - 1 - - - 1 - - - 1 - - - 1 - - - 1 - - - 1 - - - 1 - - - 0 - - - - - 1 - - - 1 - - - - - Assets - 1 - - - Assets - 1 - - - - - Assets - 1 - - - Assets - 1 - - - - - - - - - - - - - - True - True - True - - - 12 - - - - - + + + {A5387B60-F9BE-4AEF-8E11-EFB0EDCAB2ED} + MARS.Utils.dpk + 18.8 + None + True + Release + Win32 + 131 + Package + + + true + + + true + Base + true + + + true + Base + true + + + true + Base + true + + + true + Cfg_1 + true + true + + + true + Cfg_1 + true + true + + + true + Base + true + + + true + ..\..\Source;$(DCC_UnitSearchPath) + true + 260 + MARS-Curiosity REST Library + CompanyName=;FileDescription=;FileVersion=1.0.0.0;InternalName=;LegalCopyright=;LegalTrademarks=;OriginalFilename=;ProductName=;ProductVersion=1.0.0.0;Comments= + 1040 + true + System;Xml;Data;Datasnap;Web;Soap;$(DCC_Namespace) + true + MARS_Utils + ..\..\Lib260\$(Platform)\$(Config) + .\$(Platform)\$(Config) + false + false + false + false + false + + + true + Winapi;System.Win;Data.Win;Datasnap.Win;Web.Win;Soap.Win;Xml.Win;Bde;$(DCC_Namespace) + 1033 + CompanyName=;FileVersion=1.0.0.0;InternalName=;LegalCopyright=;LegalTrademarks=;OriginalFilename=;ProductVersion=1.0.0.0;Comments=;ProgramID=com.embarcadero.$(MSBuildProjectName);FileDescription=$(MSBuildProjectName);ProductName=$(MSBuildProjectName) + + + 1033 + true + CompanyName=;FileDescription=$(MSBuildProjectName);FileVersion=1.0.0.0;InternalName=;LegalCopyright=;LegalTrademarks=;OriginalFilename=;ProgramID=com.embarcadero.$(MSBuildProjectName);ProductName=$(MSBuildProjectName);ProductVersion=1.0.0.0;Comments= + Winapi;System.Win;Data.Win;Datasnap.Win;Web.Win;Soap.Win;Xml.Win;$(DCC_Namespace) + Debug + + + DEBUG;$(DCC_Define) + true + false + true + true + true + + + CompanyName=;FileVersion=1.0.0.0;InternalName=;LegalCopyright=;LegalTrademarks=;OriginalFilename=;ProductVersion=1.0.0.0;Comments=;ProgramID=com.embarcadero.$(MSBuildProjectName);FileDescription=$(MSBuildProjectName);ProductName=$(MSBuildProjectName) + MARS-Curiosity Utils + true + 1033 + false + + + true + 1033 + CompanyName=;FileDescription=$(MSBuildProjectName);FileVersion=1.0.0.0;InternalName=;LegalCopyright=;LegalTrademarks=;OriginalFilename=;ProgramID=com.embarcadero.$(MSBuildProjectName);ProductName=$(MSBuildProjectName);ProductVersion=1.0.0.0;Comments= + + + false + RELEASE;$(DCC_Define) + 0 + 0 + + + + MainSource + + + + + + + + + + + + + + + + + + + + + + + Cfg_2 + Base + + + Base + + + Cfg_1 + Base + + + + Delphi.Personality.12 + Package + + + + MARS.Utils.dpk + + + Microsoft Office 2000 Sample Automation Server Wrapper Components + Microsoft Office XP Sample Automation Server Wrapper Components + + + + + + true + + + + + MARS_Utils.bpl + true + + + + + MARS_Utils.bpl + true + + + + + true + + + + + true + + + + + true + + + + + true + + + + + true + + + + + 1 + + + 0 + + + + + classes + 1 + + + classes + 1 + + + + + res\xml + 1 + + + res\xml + 1 + + + + + library\lib\armeabi-v7a + 1 + + + + + library\lib\armeabi + 1 + + + library\lib\armeabi + 1 + + + + + library\lib\armeabi-v7a + 1 + + + + + library\lib\mips + 1 + + + library\lib\mips + 1 + + + + + + library\lib\armeabi-v7a + 1 + + + library\lib\arm64-v8a + 1 + + + + + library\lib\armeabi-v7a + 1 + + + + + res\drawable + 1 + + + res\drawable + 1 + + + + + res\values + 1 + + + res\values + 1 + + + + + res\values-v21 + 1 + + + res\values-v21 + 1 + + + + + res\values + 1 + + + res\values + 1 + + + + + res\drawable + 1 + + + res\drawable + 1 + + + + + res\drawable-xxhdpi + 1 + + + res\drawable-xxhdpi + 1 + + + + + res\drawable-ldpi + 1 + + + res\drawable-ldpi + 1 + + + + + res\drawable-mdpi + 1 + + + res\drawable-mdpi + 1 + + + + + res\drawable-hdpi + 1 + + + res\drawable-hdpi + 1 + + + + + res\drawable-xhdpi + 1 + + + res\drawable-xhdpi + 1 + + + + + res\drawable-mdpi + 1 + + + res\drawable-mdpi + 1 + + + + + res\drawable-hdpi + 1 + + + res\drawable-hdpi + 1 + + + + + res\drawable-xhdpi + 1 + + + res\drawable-xhdpi + 1 + + + + + res\drawable-xxhdpi + 1 + + + res\drawable-xxhdpi + 1 + + + + + res\drawable-xxxhdpi + 1 + + + res\drawable-xxxhdpi + 1 + + + + + res\drawable-small + 1 + + + res\drawable-small + 1 + + + + + res\drawable-normal + 1 + + + res\drawable-normal + 1 + + + + + res\drawable-large + 1 + + + res\drawable-large + 1 + + + + + res\drawable-xlarge + 1 + + + res\drawable-xlarge + 1 + + + + + res\values + 1 + + + res\values + 1 + + + + + 1 + + + 1 + + + 0 + + + + + 1 + .framework + + + 1 + .framework + + + 0 + + + + + 1 + .dylib + + + 1 + .dylib + + + 0 + .dll;.bpl + + + + + 1 + .dylib + + + 1 + .dylib + + + 1 + .dylib + + + 1 + .dylib + + + 1 + .dylib + + + 0 + .bpl + + + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + + + ..\$(PROJECTNAME).app.dSYM\Contents\Resources\DWARF + 1 + + + ..\$(PROJECTNAME).app.dSYM\Contents\Resources\DWARF + 1 + + + + + + + + 1 + + + 1 + + + 1 + + + + + + + + Contents\Resources + 1 + + + Contents\Resources + 1 + + + + + library\lib\armeabi-v7a + 1 + + + library\lib\arm64-v8a + 1 + + + 1 + + + 1 + + + 1 + + + 1 + + + 1 + + + 1 + + + 0 + + + + + library\lib\armeabi-v7a + 1 + + + + + 1 + + + 1 + + + + + Assets + 1 + + + Assets + 1 + + + + + Assets + 1 + + + Assets + 1 + + + + + + + + + + + + + + + True + True + True + + + 12 + + + + + diff --git a/Packages/103Rio/MARS.Utils.res b/Packages/103Rio/MARS.Utils.res index a098ca41..973053c2 100644 Binary files a/Packages/103Rio/MARS.Utils.res and b/Packages/103Rio/MARS.Utils.res differ diff --git a/Packages/103Rio/MARS.groupproj b/Packages/103Rio/MARS.groupproj index 50591fbf..9bf614c5 100644 --- a/Packages/103Rio/MARS.groupproj +++ b/Packages/103Rio/MARS.groupproj @@ -9,6 +9,9 @@ + + + @@ -47,6 +50,15 @@ + + + + + + + + + @@ -84,13 +96,13 @@ - + - + - +
diff --git a/Packages/103Rio/MARS.mORMot.dpk b/Packages/103Rio/MARS.mORMot.dpk index 67db9ee8..47901b85 100644 --- a/Packages/103Rio/MARS.mORMot.dpk +++ b/Packages/103Rio/MARS.mORMot.dpk @@ -44,6 +44,7 @@ contains SynCrypto in '..\..\ThirdParty\mORMot\Source\SynCrypto.pas', SynEcc in '..\..\ThirdParty\mORMot\Source\SynEcc.pas', SynLZ in '..\..\ThirdParty\mORMot\Source\SynLZ.pas', - MARS.mORMotJWT.Token.InjectionService in '..\..\Source\MARS.mORMotJWT.Token.InjectionService.pas'; + MARS.mORMotJWT.Token.InjectionService in '..\..\Source\MARS.mORMotJWT.Token.InjectionService.pas', + SynTable in '..\..\ThirdParty\mORMot\Source\SynTable.pas'; end. diff --git a/Packages/103Rio/MARS.mORMot.dproj b/Packages/103Rio/MARS.mORMot.dproj index c89188a0..974ab08e 100644 --- a/Packages/103Rio/MARS.mORMot.dproj +++ b/Packages/103Rio/MARS.mORMot.dproj @@ -2,7 +2,7 @@ {CFDDCD9D-D0A9-42FE-8036-193E21B00BD9} MARS.mORMot.dpk - 18.5 + 18.8 None True Release @@ -143,6 +143,7 @@ + Cfg_2 Base @@ -164,10 +165,8 @@ MARS.mORMot.dpk - DBExpress Enterprise Data Explorer Integration - Microsoft Office 2000 Sample Automation Server Wrapper Components - Microsoft Office XP Sample Automation Server Wrapper Components - CodeSite Express 5.3.3 + Microsoft Office 2000 Sample Automation Server Wrapper Components + Microsoft Office XP Sample Automation Server Wrapper Components @@ -182,6 +181,12 @@ true + + + MARS_mORMot.bpl + true + + true @@ -220,12 +225,20 @@ classes 1 + + classes + 1 + res\xml 1 + + res\xml + 1 + @@ -238,12 +251,26 @@ library\lib\armeabi 1 + + library\lib\armeabi + 1 + + + + + library\lib\armeabi-v7a + 1 + library\lib\mips 1 + + library\lib\mips + 1 + @@ -251,84 +278,216 @@ library\lib\armeabi-v7a 1 + + library\lib\arm64-v8a + 1 + + + + + library\lib\armeabi-v7a + 1 + res\drawable 1 + + res\drawable + 1 + res\values 1 + + res\values + 1 + res\values-v21 1 + + res\values-v21 + 1 + + + + + res\values + 1 + + + res\values + 1 + res\drawable 1 + + res\drawable + 1 + res\drawable-xxhdpi 1 + + res\drawable-xxhdpi + 1 + res\drawable-ldpi 1 + + res\drawable-ldpi + 1 + res\drawable-mdpi 1 + + res\drawable-mdpi + 1 + res\drawable-hdpi 1 + + res\drawable-hdpi + 1 + res\drawable-xhdpi 1 + + res\drawable-xhdpi + 1 + + + + + res\drawable-mdpi + 1 + + + res\drawable-mdpi + 1 + + + + + res\drawable-hdpi + 1 + + + res\drawable-hdpi + 1 + + + + + res\drawable-xhdpi + 1 + + + res\drawable-xhdpi + 1 + + + + + res\drawable-xxhdpi + 1 + + + res\drawable-xxhdpi + 1 + + + + + res\drawable-xxxhdpi + 1 + + + res\drawable-xxxhdpi + 1 + res\drawable-small 1 + + res\drawable-small + 1 + res\drawable-normal 1 + + res\drawable-normal + 1 + res\drawable-large 1 + + res\drawable-large + 1 + res\drawable-xlarge 1 + + res\drawable-xlarge + 1 + + + + + res\values + 1 + + + res\values + 1 + @@ -398,6 +557,9 @@ 0 + + 0 + 0 @@ -428,6 +590,17 @@ 1 + + + 1 + + + 1 + + + 1 + + 1 @@ -439,6 +612,39 @@ 1 + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + 1 @@ -450,6 +656,61 @@ 1 + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + 1 @@ -461,6 +722,116 @@ 1 + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + 1 @@ -494,10 +865,35 @@ 1 + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + 1 + + 1 + @@ -541,6 +937,10 @@ library\lib\armeabi-v7a 1 + + library\lib\arm64-v8a + 1 + 1 @@ -563,6 +963,12 @@ 0 + + + library\lib\armeabi-v7a + 1 + + 1 @@ -600,6 +1006,7 @@ + True diff --git a/Packages/103Rio/MARS.mORMot.res b/Packages/103Rio/MARS.mORMot.res index 6f0c9b7e..75115b70 100644 Binary files a/Packages/103Rio/MARS.mORMot.res and b/Packages/103Rio/MARS.mORMot.res differ diff --git a/Packages/103Rio/MARSClient.Core.dpk b/Packages/103Rio/MARSClient.Core.dpk index 6652152c..10f9e95f 100644 --- a/Packages/103Rio/MARSClient.Core.dpk +++ b/Packages/103Rio/MARSClient.Core.dpk @@ -51,6 +51,7 @@ contains MARS.Client.Utils in '..\..\Source\MARS.Client.Utils.pas', MARS.Client.Client in '..\..\Source\MARS.Client.Client.pas', MARS.Client.Client.Indy in '..\..\Source\MARS.Client.Client.Indy.pas', - MARS.Client.Resource.FormData in '..\..\Source\MARS.Client.Resource.FormData.pas'; + MARS.Client.Resource.FormData in '..\..\Source\MARS.Client.Resource.FormData.pas', + MARS.Client.Resource.FormUrlEncoded in '..\..\Source\MARS.Client.Resource.FormUrlEncoded.pas'; end. diff --git a/Packages/103Rio/MARSClient.Core.dproj b/Packages/103Rio/MARSClient.Core.dproj index 0f45b144..8f652bfc 100644 --- a/Packages/103Rio/MARSClient.Core.dproj +++ b/Packages/103Rio/MARSClient.Core.dproj @@ -2,7 +2,7 @@ {48E75129-EE24-48D4-AE54-2F549C909CC8} MARSClient.Core.dpk - 18.5 + 18.8 FMX True Release @@ -87,7 +87,7 @@ true - 250 + 260 MARS-Curiosity Client Core 1033 true @@ -126,6 +126,7 @@ + BITMAP TMARSCLIENTMESSAGINGRESOURCE @@ -195,10 +196,8 @@ MARSClient.Core.dpk - DBExpress Enterprise Data Explorer Integration - Microsoft Office 2000 Sample Automation Server Wrapper Components - Microsoft Office XP Sample Automation Server Wrapper Components - CodeSite Express 5.3.3 + Microsoft Office 2000 Sample Automation Server Wrapper Components + Microsoft Office XP Sample Automation Server Wrapper Components @@ -207,15 +206,8 @@ true - - - MARSClient_Core.bpl - true - - - - - MARSClient_Core.bpl + + true @@ -229,6 +221,12 @@ true + + + MARSClient_Core.bpl + true + + true @@ -239,13 +237,14 @@ true - - + + + MARSClient_Core.bpl true - - + + true @@ -262,12 +261,20 @@ classes 1 + + classes + 1 + res\xml 1 + + res\xml + 1 + @@ -280,12 +287,26 @@ library\lib\armeabi 1 + + library\lib\armeabi + 1 + + + + + library\lib\armeabi-v7a + 1 + library\lib\mips 1 + + library\lib\mips + 1 + @@ -293,84 +314,216 @@ library\lib\armeabi-v7a 1 + + library\lib\arm64-v8a + 1 + + + + + library\lib\armeabi-v7a + 1 + res\drawable 1 + + res\drawable + 1 + res\values 1 + + res\values + 1 + res\values-v21 1 + + res\values-v21 + 1 + + + + + res\values + 1 + + + res\values + 1 + res\drawable 1 + + res\drawable + 1 + res\drawable-xxhdpi 1 + + res\drawable-xxhdpi + 1 + res\drawable-ldpi 1 + + res\drawable-ldpi + 1 + res\drawable-mdpi 1 + + res\drawable-mdpi + 1 + res\drawable-hdpi 1 + + res\drawable-hdpi + 1 + res\drawable-xhdpi 1 + + res\drawable-xhdpi + 1 + + + + + res\drawable-mdpi + 1 + + + res\drawable-mdpi + 1 + + + + + res\drawable-hdpi + 1 + + + res\drawable-hdpi + 1 + + + + + res\drawable-xhdpi + 1 + + + res\drawable-xhdpi + 1 + + + + + res\drawable-xxhdpi + 1 + + + res\drawable-xxhdpi + 1 + + + + + res\drawable-xxxhdpi + 1 + + + res\drawable-xxxhdpi + 1 + res\drawable-small 1 + + res\drawable-small + 1 + res\drawable-normal 1 + + res\drawable-normal + 1 + res\drawable-large 1 + + res\drawable-large + 1 + res\drawable-xlarge 1 + + res\drawable-xlarge + 1 + + + + + res\values + 1 + + + res\values + 1 + @@ -440,6 +593,9 @@ 0 + + 0 + 0 @@ -470,6 +626,17 @@ 1 + + + 1 + + + 1 + + + 1 + + 1 @@ -481,6 +648,39 @@ 1 + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + 1 @@ -492,6 +692,61 @@ 1 + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + 1 @@ -503,6 +758,116 @@ 1 + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + 1 @@ -536,10 +901,35 @@ 1 + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + 1 + + 1 + @@ -583,6 +973,10 @@ library\lib\armeabi-v7a 1 + + library\lib\arm64-v8a + 1 + 1 @@ -605,6 +999,12 @@ 0 + + + library\lib\armeabi-v7a + 1 + + 1 @@ -642,6 +1042,7 @@ + True diff --git a/Packages/103Rio/MARSClient.CoreDesign.dproj b/Packages/103Rio/MARSClient.CoreDesign.dproj index 783e1067..ce455dd6 100644 --- a/Packages/103Rio/MARSClient.CoreDesign.dproj +++ b/Packages/103Rio/MARSClient.CoreDesign.dproj @@ -2,7 +2,7 @@ {8EEA5DAB-B80F-44EA-B149-45D051C20610} MARSClient.CoreDesign.dpk - 18.5 + 18.8 FMX True Release @@ -34,12 +34,6 @@ true true - - true - Cfg_1 - true - true - true Base @@ -51,12 +45,6 @@ true true - - true - Cfg_2 - true - true - 260 ../../Source/Core;../../Source/Data/FireDAC;$(DCC_UnitSearchPath) @@ -82,12 +70,14 @@ rtl;inet;DbxCommonDriver;dbrtl;FireDACCommon;FireDACCommonDriver;FireDAC;DataSnapFireDAC;IndySystem;IndyProtocols;IndyCore;bindengine;bindcomp;MARS.Utils;MARSClient.Core;$(DCC_UsePackage) 1033 true + CompanyName=;FileVersion=1.0.0.0;InternalName=;LegalCopyright=;LegalTrademarks=;OriginalFilename=;ProductVersion=1.0.0.0;Comments=;ProgramID=com.embarcadero.$(ModuleName);FileDescription=$(ModuleName);ProductName=$(ModuleName) System.Win;Data.Win;Datasnap.Win;Web.Win;Soap.Win;Xml.Win;Bde;$(DCC_Namespace) rtl;inet;DbxCommonDriver;dbrtl;FireDACCommon;FireDACCommonDriver;FireDAC;DataSnapFireDAC;IndySystem;IndyProtocols;IndyCore;bindengine;bindcomp;MARS.Utils;$(DCC_UsePackage) 1033 true + CompanyName=;FileVersion=1.0.0.0;InternalName=;LegalCopyright=;LegalTrademarks=;OriginalFilename=;ProductVersion=1.0.0.0;Comments=;ProgramID=com.embarcadero.$(ModuleName);FileDescription=$(ModuleName);ProductName=$(ModuleName) System.Win;Data.Win;Datasnap.Win;Web.Win;Soap.Win;Xml.Win;$(DCC_Namespace) @@ -99,15 +89,12 @@ true + 260 MARS-Curiosity Client Core 1033 true false - - true - 1033 - false RELEASE;$(DCC_Define) @@ -117,10 +104,7 @@ true 1033 - - - true - 1033 + 260 @@ -165,6 +149,12 @@ true + + + MARSClient_CoreDesign.bpl + true + + true @@ -190,12 +180,6 @@ true - - - MARSClient_CoreDesign.bpl - true - - true @@ -214,12 +198,20 @@ classes 1 + + classes + 1 + res\xml 1 + + res\xml + 1 + @@ -232,12 +224,26 @@ library\lib\armeabi 1 + + library\lib\armeabi + 1 + + + + + library\lib\armeabi-v7a + 1 + library\lib\mips 1 + + library\lib\mips + 1 + @@ -245,84 +251,216 @@ library\lib\armeabi-v7a 1 + + library\lib\arm64-v8a + 1 + + + + + library\lib\armeabi-v7a + 1 + res\drawable 1 + + res\drawable + 1 + res\values 1 + + res\values + 1 + res\values-v21 1 + + res\values-v21 + 1 + + + + + res\values + 1 + + + res\values + 1 + res\drawable 1 + + res\drawable + 1 + res\drawable-xxhdpi 1 + + res\drawable-xxhdpi + 1 + res\drawable-ldpi 1 + + res\drawable-ldpi + 1 + res\drawable-mdpi 1 + + res\drawable-mdpi + 1 + res\drawable-hdpi 1 + + res\drawable-hdpi + 1 + res\drawable-xhdpi 1 + + res\drawable-xhdpi + 1 + + + + + res\drawable-mdpi + 1 + + + res\drawable-mdpi + 1 + + + + + res\drawable-hdpi + 1 + + + res\drawable-hdpi + 1 + + + + + res\drawable-xhdpi + 1 + + + res\drawable-xhdpi + 1 + + + + + res\drawable-xxhdpi + 1 + + + res\drawable-xxhdpi + 1 + + + + + res\drawable-xxxhdpi + 1 + + + res\drawable-xxxhdpi + 1 + res\drawable-small 1 + + res\drawable-small + 1 + res\drawable-normal 1 + + res\drawable-normal + 1 + res\drawable-large 1 + + res\drawable-large + 1 + res\drawable-xlarge 1 + + res\drawable-xlarge + 1 + + + + + res\values + 1 + + + res\values + 1 + @@ -392,6 +530,9 @@ 0 + + 0 + 0 @@ -422,6 +563,17 @@ 1 + + + 1 + + + 1 + + + 1 + + 1 @@ -433,6 +585,39 @@ 1 + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + 1 @@ -444,6 +629,61 @@ 1 + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + 1 @@ -455,6 +695,116 @@ 1 + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + 1 @@ -488,10 +838,35 @@ 1 + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + 1 + + 1 + @@ -535,6 +910,10 @@ library\lib\armeabi-v7a 1 + + library\lib\arm64-v8a + 1 + 1 @@ -557,6 +936,12 @@ 0 + + + library\lib\armeabi-v7a + 1 + + 1 @@ -594,6 +979,7 @@ + True diff --git a/Packages/103Rio/MARSClient.CoreDesign.res b/Packages/103Rio/MARSClient.CoreDesign.res index a19c3239..da606b6b 100644 Binary files a/Packages/103Rio/MARSClient.CoreDesign.res and b/Packages/103Rio/MARSClient.CoreDesign.res differ diff --git a/Packages/103Rio/MARSClient.CoreResource.rc b/Packages/103Rio/MARSClient.CoreResource.rc new file mode 100644 index 00000000..d2788dbf --- /dev/null +++ b/Packages/103Rio/MARSClient.CoreResource.rc @@ -0,0 +1,12 @@ +TMARSCLIENTMESSAGINGRESOURCE BITMAP "..\\..\\media\\TMARSClientMessagingResource.bmp" +TMARSCLIENTRESOURCE BITMAP "..\\..\\media\\TMARSClientResource.bmp" +TMARSCLIENTRESOURCEJSON BITMAP "..\\..\\media\\TMARSClientResourceJSON.bmp" +TMARSCLIENTRESOURCESTREAM BITMAP "..\\..\\media\\TMARSClientResourceStream.bmp" +TMARSCLIENTSUBRESOURCE BITMAP "..\\..\\media\\TMARSClientSubResource.bmp" +TMARSCLIENTSUBRESOURCEJSON BITMAP "..\\..\\media\\TMARSClientSubResourceJSON.bmp" +TMARSCLIENTSUBRESOURCESTREAM BITMAP "..\\..\\media\\TMARSClientSubResourceStream.bmp" +TMARSCLIENTTOKEN BITMAP "..\\..\\media\\TMARSClientToken.bmp" +TMARSCLIENT BITMAP "..\\..\\media\\TMARSClient.bmp" +TMARSCLIENTAPPLICATION BITMAP "..\\..\\media\\TMARSClientApplication.bmp" +TMARSNETCLIENT BITMAP "..\\..\\media\\TMARSNetClient.bmp" +TMARSCLIENTRESOURCEFORMDATA BITMAP "..\\..\\media\\TMARSClientResourceFormData.bmp" diff --git a/Packages/103Rio/MARSClient.FireDAC.dproj b/Packages/103Rio/MARSClient.FireDAC.dproj index 1fa3f2e1..2e954ac0 100644 --- a/Packages/103Rio/MARSClient.FireDAC.dproj +++ b/Packages/103Rio/MARSClient.FireDAC.dproj @@ -2,7 +2,7 @@ {29A0C2A3-BA6B-44DF-B909-A20D8DBF1B9B} MARSClient.FireDAC.dpk - 18.5 + 18.8 FMX True Release @@ -123,10 +123,8 @@ MARSClient.FireDAC.dpk - DBExpress Enterprise Data Explorer Integration - Microsoft Office 2000 Sample Automation Server Wrapper Components - Microsoft Office XP Sample Automation Server Wrapper Components - CodeSite Express 5.3.3 + Microsoft Office 2000 Sample Automation Server Wrapper Components + Microsoft Office XP Sample Automation Server Wrapper Components @@ -160,14 +158,14 @@ true - - - MARSClient_FireDAC.bpl + + true - - + + + MARSClient_FireDAC.bpl true @@ -184,12 +182,20 @@ classes 1 + + classes + 1 + res\xml 1 + + res\xml + 1 + @@ -202,12 +208,26 @@ library\lib\armeabi 1 + + library\lib\armeabi + 1 + + + + + library\lib\armeabi-v7a + 1 + library\lib\mips 1 + + library\lib\mips + 1 + @@ -215,84 +235,216 @@ library\lib\armeabi-v7a 1 + + library\lib\arm64-v8a + 1 + + + + + library\lib\armeabi-v7a + 1 + res\drawable 1 + + res\drawable + 1 + res\values 1 + + res\values + 1 + res\values-v21 1 + + res\values-v21 + 1 + + + + + res\values + 1 + + + res\values + 1 + res\drawable 1 + + res\drawable + 1 + res\drawable-xxhdpi 1 + + res\drawable-xxhdpi + 1 + res\drawable-ldpi 1 + + res\drawable-ldpi + 1 + res\drawable-mdpi 1 + + res\drawable-mdpi + 1 + res\drawable-hdpi 1 + + res\drawable-hdpi + 1 + res\drawable-xhdpi 1 + + res\drawable-xhdpi + 1 + + + + + res\drawable-mdpi + 1 + + + res\drawable-mdpi + 1 + + + + + res\drawable-hdpi + 1 + + + res\drawable-hdpi + 1 + + + + + res\drawable-xhdpi + 1 + + + res\drawable-xhdpi + 1 + + + + + res\drawable-xxhdpi + 1 + + + res\drawable-xxhdpi + 1 + + + + + res\drawable-xxxhdpi + 1 + + + res\drawable-xxxhdpi + 1 + res\drawable-small 1 + + res\drawable-small + 1 + res\drawable-normal 1 + + res\drawable-normal + 1 + res\drawable-large 1 + + res\drawable-large + 1 + res\drawable-xlarge 1 + + res\drawable-xlarge + 1 + + + + + res\values + 1 + + + res\values + 1 + @@ -362,6 +514,9 @@ 0 + + 0 + 0 @@ -392,6 +547,17 @@ 1 + + + 1 + + + 1 + + + 1 + + 1 @@ -403,6 +569,39 @@ 1 + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + 1 @@ -414,6 +613,61 @@ 1 + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + 1 @@ -425,6 +679,116 @@ 1 + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + 1 @@ -458,10 +822,35 @@ 1 + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + 1 + + 1 + @@ -505,6 +894,10 @@ library\lib\armeabi-v7a 1 + + library\lib\arm64-v8a + 1 + 1 @@ -527,6 +920,12 @@ 0 + + + library\lib\armeabi-v7a + 1 + + 1 @@ -564,6 +963,7 @@ + True diff --git a/Packages/103Rio/MARSClient.FireDACDesign.dproj b/Packages/103Rio/MARSClient.FireDACDesign.dproj index 6ab349c9..3a0bc7ba 100644 --- a/Packages/103Rio/MARSClient.FireDACDesign.dproj +++ b/Packages/103Rio/MARSClient.FireDACDesign.dproj @@ -2,7 +2,7 @@ {6F52BB40-74E2-410E-A54D-0C20E713A0CE} MARSClient.FireDACDesign.dpk - 18.5 + 18.8 FMX True Release @@ -62,7 +62,7 @@ true - rtl;inet;DbxCommonDriver;dbrtl;FireDACCommon;FireDACCommonDriver;FireDAC;DataSnapFireDAC;IndySystem;IndyProtocols;IndyCore;bindengine;bindcomp;MARSClient.FireDAC;MARSClient.CoreDesign250;MARSClient.FireDAC250;$(DCC_UsePackage) + rtl;inet;DbxCommonDriver;dbrtl;FireDACCommon;FireDACCommonDriver;FireDAC;DataSnapFireDAC;IndySystem;IndyProtocols;IndyCore;bindengine;bindcomp;MARSClient.FireDAC;MARSClient.CoreDesign260;MARSClient.FireDAC260;$(DCC_UsePackage) 1033 true CompanyName=;FileVersion=1.0.0.0;InternalName=;LegalCopyright=;LegalTrademarks=;OriginalFilename=;ProductVersion=1.0.0.0;Comments=;ProgramID=com.embarcadero.$(ModuleName);FileDescription=$(ModuleName);ProductName=$(ModuleName) @@ -77,7 +77,7 @@ true - 250 + 260 MARS-Curiosity Client FireDAC 1033 true @@ -159,7 +159,7 @@ true - + MARSClient_FireDACDesign.bpl true @@ -202,6 +202,12 @@ 1 + + + library\lib\armeabi-v7a + 1 + + library\lib\mips @@ -215,6 +221,12 @@ 1 + + + library\lib\armeabi-v7a + 1 + + res\drawable @@ -233,6 +245,16 @@ 1 + + + res\values + 1 + + + res\values + 1 + + res\drawable @@ -269,6 +291,56 @@ 1 + + + res\drawable-mdpi + 1 + + + res\drawable-mdpi + 1 + + + + + res\drawable-hdpi + 1 + + + res\drawable-hdpi + 1 + + + + + res\drawable-xhdpi + 1 + + + res\drawable-xhdpi + 1 + + + + + res\drawable-xxhdpi + 1 + + + res\drawable-xxhdpi + 1 + + + + + res\drawable-xxxhdpi + 1 + + + res\drawable-xxxhdpi + 1 + + res\drawable-small @@ -293,6 +365,16 @@ 1 + + + res\values + 1 + + + res\values + 1 + + 1 @@ -391,6 +473,17 @@ 1 + + + 1 + + + 1 + + + 1 + + 1 @@ -402,6 +495,39 @@ 1 + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + 1 @@ -413,6 +539,61 @@ 1 + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + 1 @@ -424,6 +605,116 @@ 1 + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + 1 @@ -457,6 +748,28 @@ 1 + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + 1 @@ -526,6 +839,12 @@ 0 + + + library\lib\armeabi-v7a + 1 + + 1 @@ -563,6 +882,7 @@ + True diff --git a/Packages/104Sydney/MARS.Core.dpk b/Packages/104Sydney/MARS.Core.dpk new file mode 100644 index 00000000..089e5f6e --- /dev/null +++ b/Packages/104Sydney/MARS.Core.dpk @@ -0,0 +1,68 @@ +package MARS.Core; + +{$R *.res} +{$IFDEF IMPLICITBUILDING This IFDEF should not be used by users} +{$ALIGN 8} +{$ASSERTIONS ON} +{$BOOLEVAL OFF} +{$DEBUGINFO OFF} +{$EXTENDEDSYNTAX ON} +{$IMPORTEDDATA ON} +{$IOCHECKS ON} +{$LOCALSYMBOLS OFF} +{$LONGSTRINGS ON} +{$OPENSTRINGS ON} +{$OPTIMIZATION ON} +{$OVERFLOWCHECKS OFF} +{$RANGECHECKS OFF} +{$REFERENCEINFO OFF} +{$SAFEDIVIDE OFF} +{$STACKFRAMES OFF} +{$TYPEDADDRESS OFF} +{$VARSTRINGCHECKS ON} +{$WRITEABLECONST OFF} +{$MINENUMSIZE 1} +{$IMAGEBASE $400000} +{$DEFINE RELEASE} +{$ENDIF IMPLICITBUILDING} +{$DESCRIPTION 'MARS-Curiosity Core'} +{$LIBSUFFIX '270'} +{$RUNONLY} +{$IMPLICITBUILD OFF} + +requires + rtl, + dbrtl, + inet, + IndySystem, + IndyProtocols, + IndyCore, + xmlrtl, + dsnap, + MARS.Utils; + +contains + MARS.Core.Application in '..\..\Source\MARS.Core.Application.pas', + MARS.Core.Attributes in '..\..\Source\MARS.Core.Attributes.pas', + MARS.Core.Cache in '..\..\Source\MARS.Core.Cache.pas', + MARS.Core.Classes in '..\..\Source\MARS.Core.Classes.pas', + MARS.Core.Engine in '..\..\Source\MARS.Core.Engine.pas', + MARS.Core.MessageBodyReader in '..\..\Source\MARS.Core.MessageBodyReader.pas', + MARS.Core.MessageBodyWriter in '..\..\Source\MARS.Core.MessageBodyWriter.pas', + MARS.Core.Registry in '..\..\Source\MARS.Core.Registry.pas', + MARS.Core.Response in '..\..\Source\MARS.Core.Response.pas', + MARS.Core.Token in '..\..\Source\MARS.Core.Token.pas', + MARS.http.Core in '..\..\Source\MARS.http.Core.pas', + MARS.Core.Activation in '..\..\Source\MARS.Core.Activation.pas', + MARS.Core.Injection.Interfaces in '..\..\Source\MARS.Core.Injection.Interfaces.pas', + MARS.Core.Injection in '..\..\Source\MARS.Core.Injection.pas', + MARS.Core.Injection.Types in '..\..\Source\MARS.Core.Injection.Types.pas', + MARS.Core.Activation.InjectionService in '..\..\Source\MARS.Core.Activation.InjectionService.pas', + MARS.Core.Activation.Interfaces in '..\..\Source\MARS.Core.Activation.Interfaces.pas', + MARS.Core.Registry.Utils in '..\..\Source\MARS.Core.Registry.Utils.pas'; + +end. + + + + diff --git a/Packages/104Sydney/MARS.Core.dproj b/Packages/104Sydney/MARS.Core.dproj new file mode 100644 index 00000000..3224e9c2 --- /dev/null +++ b/Packages/104Sydney/MARS.Core.dproj @@ -0,0 +1,770 @@ + + + {8C6A1564-94E6-420D-BDE4-54A4CD3BD125} + MARS.Core.dpk + True + Release + 131 + Package + None + 19.0 + Win32 + + + true + + + true + Base + true + + + true + Base + true + + + true + Base + true + + + true + Base + true + + + true + Base + true + + + true + Cfg_1 + true + true + + + true + Base + true + + + true + Cfg_2 + true + true + + + true + Cfg_2 + true + true + + + false + false + false + false + false + 00400000 + true + true + MARS_Core + MARS-Curiosity Core + 270 + true + true + 1040 + CompanyName=;FileDescription=;FileVersion=1.0.0.0;InternalName=;LegalCopyright=;LegalTrademarks=;OriginalFilename=;ProductName=;ProductVersion=1.0.0.0;Comments=;CFBundleName= + System;Xml;Data;Datasnap;Web;Soap;$(DCC_Namespace) + ..\..\Source;$(DCC_UnitSearchPath) + ..\..\Lib270\$(Platform)\$(Config) + + + package=com.embarcadero.$(MSBuildProjectName);label=$(MSBuildProjectName);versionCode=1;versionName=1.0.0;persistent=False;restoreAnyVersion=False;installLocation=auto;largeHeap=False;theme=TitleBar;hardwareAccelerated=true;apiKey= + Debug + android-support-v4.dex.jar;cloud-messaging.dex.jar;fmx.dex.jar;google-analytics-v2.dex.jar;google-play-billing.dex.jar;google-play-licensing.dex.jar;google-play-services-ads-7.0.0.dex.jar;google-play-services-analytics-7.0.0.dex.jar;google-play-services-base-7.0.0.dex.jar;google-play-services-gcm-7.0.0.dex.jar;google-play-services-identity-7.0.0.dex.jar;google-play-services-maps-7.0.0.dex.jar;google-play-services-panorama-7.0.0.dex.jar;google-play-services-plus-7.0.0.dex.jar;google-play-services-wallet-7.0.0.dex.jar + + + package=com.embarcadero.$(MSBuildProjectName);label=$(MSBuildProjectName);versionCode=1;versionName=1.0.0;persistent=False;restoreAnyVersion=False;installLocation=auto;largeHeap=False;theme=TitleBar;hardwareAccelerated=true;apiKey= + Debug + true + Base + true + android-support-v4.dex.jar;cloud-messaging.dex.jar;fmx.dex.jar;google-analytics-v2.dex.jar;google-play-billing.dex.jar;google-play-licensing.dex.jar;google-play-services-ads-7.0.0.dex.jar;google-play-services-analytics-7.0.0.dex.jar;google-play-services-base-7.0.0.dex.jar;google-play-services-gcm-7.0.0.dex.jar;google-play-services-identity-7.0.0.dex.jar;google-play-services-maps-7.0.0.dex.jar;google-play-services-panorama-7.0.0.dex.jar;google-play-services-plus-7.0.0.dex.jar;google-play-services-wallet-7.0.0.dex.jar + + + Winapi;System.Win;Data.Win;Datasnap.Win;Web.Win;Soap.Win;Xml.Win;Bde;$(DCC_Namespace) + Debug + true + CompanyName=;FileDescription=$(MSBuildProjectName);FileVersion=1.0.0.0;InternalName=;LegalCopyright=;LegalTrademarks=;OriginalFilename=;ProductName=$(MSBuildProjectName);ProductVersion=1.0.0.0;Comments=;ProgramID=com.embarcadero.$(MSBuildProjectName) + 1033 + + + Winapi;System.Win;Data.Win;Datasnap.Win;Web.Win;Soap.Win;Xml.Win;$(DCC_Namespace) + Debug + true + CompanyName=;FileDescription=$(MSBuildProjectName);FileVersion=1.0.0.0;InternalName=;LegalCopyright=;LegalTrademarks=;OriginalFilename=;ProgramID=com.embarcadero.$(MSBuildProjectName);ProductName=$(MSBuildProjectName);ProductVersion=1.0.0.0;Comments= + 1033 + + + RELEASE;$(DCC_Define) + 0 + false + 0 + + + true + 1033 + CompanyName=;FileDescription=$(MSBuildProjectName);FileVersion=1.0.0.0;InternalName=;LegalCopyright=;LegalTrademarks=;OriginalFilename=;ProductName=$(MSBuildProjectName);ProductVersion=1.0.0.0;Comments=;ProgramID=com.embarcadero.$(MSBuildProjectName) + + + DEBUG;$(DCC_Define) + false + true + + + Debug + + + true + 1033 + CompanyName=;FileDescription=$(MSBuildProjectName);FileVersion=1.0.0.0;InternalName=;LegalCopyright=;LegalTrademarks=;OriginalFilename=;ProgramID=com.embarcadero.$(MSBuildProjectName);ProductName=$(MSBuildProjectName);ProductVersion=1.0.0.0;Comments= + + + + MainSource + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Cfg_2 + Base + + + Base + + + Cfg_1 + Base + + + + Delphi.Personality.12 + Package + + + + MARS.Core.dpk + + + Microsoft Office 2000 Sample Automation Server Wrapper Components + Microsoft Office XP Sample Automation Server Wrapper Components + + + + False + False + False + False + True + True + True + + + + + true + + + + + true + + + + + true + + + + + 1 + + + 0 + + + + + classes + 1 + + + classes + 1 + + + + + res\xml + 1 + + + res\xml + 1 + + + + + library\lib\armeabi-v7a + 1 + + + + + library\lib\armeabi + 1 + + + library\lib\armeabi + 1 + + + + + library\lib\armeabi-v7a + 1 + + + + + library\lib\mips + 1 + + + library\lib\mips + 1 + + + + + library\lib\armeabi-v7a + 1 + + + library\lib\arm64-v8a + 1 + + + + + library\lib\armeabi-v7a + 1 + + + + + res\drawable + 1 + + + res\drawable + 1 + + + + + res\values + 1 + + + res\values + 1 + + + + + res\values-v21 + 1 + + + res\values-v21 + 1 + + + + + res\values + 1 + + + res\values + 1 + + + + + res\drawable + 1 + + + res\drawable + 1 + + + + + res\drawable-xxhdpi + 1 + + + res\drawable-xxhdpi + 1 + + + + + res\drawable-ldpi + 1 + + + res\drawable-ldpi + 1 + + + + + res\drawable-mdpi + 1 + + + res\drawable-mdpi + 1 + + + + + res\drawable-hdpi + 1 + + + res\drawable-hdpi + 1 + + + + + res\drawable-xhdpi + 1 + + + res\drawable-xhdpi + 1 + + + + + res\drawable-mdpi + 1 + + + res\drawable-mdpi + 1 + + + + + res\drawable-hdpi + 1 + + + res\drawable-hdpi + 1 + + + + + res\drawable-xhdpi + 1 + + + res\drawable-xhdpi + 1 + + + + + res\drawable-xxhdpi + 1 + + + res\drawable-xxhdpi + 1 + + + + + res\drawable-xxxhdpi + 1 + + + res\drawable-xxxhdpi + 1 + + + + + res\drawable-small + 1 + + + res\drawable-small + 1 + + + + + res\drawable-normal + 1 + + + res\drawable-normal + 1 + + + + + res\drawable-large + 1 + + + res\drawable-large + 1 + + + + + res\drawable-xlarge + 1 + + + res\drawable-xlarge + 1 + + + + + res\values + 1 + + + res\values + 1 + + + + + 1 + + + 1 + + + 0 + + + + + 1 + .framework + + + 1 + .framework + + + 0 + + + + + 1 + .dylib + + + 1 + .dylib + + + 0 + .dll;.bpl + + + + + 1 + .dylib + + + 1 + .dylib + + + 1 + .dylib + + + 1 + .dylib + + + 1 + .dylib + + + 0 + .bpl + + + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + ..\$(PROJECTNAME).launchscreen\Assets\LaunchScreenImage.imageset + 1 + + + ..\$(PROJECTNAME).launchscreen\Assets\LaunchScreenImage.imageset + 1 + + + + + ..\$(PROJECTNAME).launchscreen\Assets\LaunchScreenImage.imageset + 1 + + + ..\$(PROJECTNAME).launchscreen\Assets\LaunchScreenImage.imageset + 1 + + + + + ..\$(PROJECTNAME).launchscreen\Assets\LaunchScreenImage.imageset + 1 + + + ..\$(PROJECTNAME).launchscreen\Assets\LaunchScreenImage.imageset + 1 + + + + + ..\$(PROJECTNAME).launchscreen\Assets\LaunchScreenImage.imageset + 1 + + + ..\$(PROJECTNAME).launchscreen\Assets\LaunchScreenImage.imageset + 1 + + + + + ..\$(PROJECTNAME).launchscreen\Assets\LaunchScreenImage.imageset + 1 + + + ..\$(PROJECTNAME).launchscreen\Assets\LaunchScreenImage.imageset + 1 + + + + + ..\$(PROJECTNAME).launchscreen\Assets\LaunchScreenImage.imageset + 1 + + + ..\$(PROJECTNAME).launchscreen\Assets\LaunchScreenImage.imageset + 1 + + + + + 1 + + + 1 + + + + + ..\$(PROJECTNAME).app.dSYM\Contents\Resources\DWARF + 1 + + + ..\$(PROJECTNAME).app.dSYM\Contents\Resources\DWARF + 1 + + + + + + + + + 1 + + + 1 + + + 1 + + + + + + + + Contents\Resources + 1 + + + Contents\Resources + 1 + + + + + library\lib\armeabi-v7a + 1 + + + library\lib\arm64-v8a + 1 + + + 1 + + + 1 + + + 1 + + + 1 + + + 1 + + + 1 + + + 0 + + + + + library\lib\armeabi-v7a + 1 + + + + + 1 + + + 1 + + + + + Assets + 1 + + + Assets + 1 + + + + + Assets + 1 + + + Assets + 1 + + + + + + + + + + + + + + + 12 + + + + + diff --git a/Packages/104Sydney/MARS.DCS.dpk b/Packages/104Sydney/MARS.DCS.dpk new file mode 100644 index 00000000..df091358 --- /dev/null +++ b/Packages/104Sydney/MARS.DCS.dpk @@ -0,0 +1,57 @@ +package MARS.DCS; + +{$R *.res} +{$IFDEF IMPLICITBUILDING This IFDEF should not be used by users} +{$ALIGN 8} +{$ASSERTIONS ON} +{$BOOLEVAL OFF} +{$DEBUGINFO OFF} +{$EXTENDEDSYNTAX ON} +{$IMPORTEDDATA ON} +{$IOCHECKS ON} +{$LOCALSYMBOLS OFF} +{$LONGSTRINGS ON} +{$OPENSTRINGS ON} +{$OPTIMIZATION ON} +{$OVERFLOWCHECKS OFF} +{$RANGECHECKS OFF} +{$REFERENCEINFO OFF} +{$SAFEDIVIDE OFF} +{$STACKFRAMES OFF} +{$TYPEDADDRESS OFF} +{$VARSTRINGCHECKS ON} +{$WRITEABLECONST OFF} +{$MINENUMSIZE 1} +{$IMAGEBASE $400000} +{$DEFINE RELEASE} +{$ENDIF IMPLICITBUILDING} +{$DESCRIPTION 'MARS-Curiosity REST Library - DelphiCrossSocket support'} +{$LIBSUFFIX '270'} +{$RUNONLY} +{$IMPLICITBUILD OFF} + +requires + rtl, + MARS.Utils, + MARS.Core; + +contains + MARS.http.Server.DCS in '..\..\Source\MARS.http.Server.DCS.pas', + Net.CrossHttpMiddleware in '..\..\ThirdParty\DCS\Net\Net.CrossHttpMiddleware.pas', + Net.CrossHttpParams in '..\..\ThirdParty\DCS\Net\Net.CrossHttpParams.pas', + Net.CrossHttpRouter in '..\..\ThirdParty\DCS\Net\Net.CrossHttpRouter.pas', + Net.CrossHttpServer in '..\..\ThirdParty\DCS\Net\Net.CrossHttpServer.pas', + Net.CrossHttpUtils in '..\..\ThirdParty\DCS\Net\Net.CrossHttpUtils.pas', + Net.CrossServer in '..\..\ThirdParty\DCS\Net\Net.CrossServer.pas', + Net.CrossSocket.Iocp in '..\..\ThirdParty\DCS\Net\Net.CrossSocket.Iocp.pas', + Net.CrossSocket in '..\..\ThirdParty\DCS\Net\Net.CrossSocket.pas', + Net.SocketAPI in '..\..\ThirdParty\DCS\Net\Net.SocketAPI.pas', + Net.Winsock2 in '..\..\ThirdParty\DCS\Net\Net.Winsock2.pas', + Net.Wship6 in '..\..\ThirdParty\DCS\Net\Net.Wship6.pas', + Net.CrossSocket.Base in '..\..\ThirdParty\DCS\Net\Net.CrossSocket.Base.pas', + Utils.DateTime in '..\..\ThirdParty\DCS\Utils\Utils.DateTime.pas', + Utils.Logger in '..\..\ThirdParty\DCS\Utils\Utils.Logger.pas', + Utils.RegEx in '..\..\ThirdParty\DCS\Utils\Utils.RegEx.pas', + Utils.Utils in '..\..\ThirdParty\DCS\Utils\Utils.Utils.pas'; + +end. diff --git a/Packages/104Sydney/MARS.DCS.dproj b/Packages/104Sydney/MARS.DCS.dproj new file mode 100644 index 00000000..62a4d12c --- /dev/null +++ b/Packages/104Sydney/MARS.DCS.dproj @@ -0,0 +1,1117 @@ + + + {02BBCED0-86EE-4FB8-A124-5BD6E5477506} + MARS.DCS.dpk + 19.0 + None + True + Release + Win32 + 3 + Package + + + true + + + true + Base + true + + + true + Base + true + + + true + Base + true + + + true + Base + true + + + true + Base + true + + + true + Base + true + + + true + Base + true + + + true + Cfg_1 + true + true + + + true + Base + true + + + true + Cfg_2 + true + true + + + true + Cfg_2 + true + true + + + ..\..\Lib270\$(Platform)\$(Config) + .\$(Platform)\$(Config) + false + false + false + false + false + true + true + System;Xml;Data;Datasnap;Web;Soap;$(DCC_Namespace) + All + MARS_DCS + true + true + 1040 + CompanyName=;FileDescription=$(MSBuildProjectName);FileVersion=1.0.0.0;InternalName=;LegalCopyright=;LegalTrademarks=;OriginalFilename=;ProgramID=com.embarcadero.$(MSBuildProjectName);ProductName=$(MSBuildProjectName);ProductVersion=1.0.0.0;Comments= + MARS-Curiosity REST Library - DelphiCrossSocket support + 270 + ..\..\Source;$(DCC_UnitSearchPath) + + + None + android-support-v4.dex.jar;cloud-messaging.dex.jar;fmx.dex.jar;google-analytics-v2.dex.jar;google-play-billing.dex.jar;google-play-licensing.dex.jar;google-play-services-ads-7.0.0.dex.jar;google-play-services-analytics-7.0.0.dex.jar;google-play-services-base-7.0.0.dex.jar;google-play-services-gcm-7.0.0.dex.jar;google-play-services-identity-7.0.0.dex.jar;google-play-services-maps-7.0.0.dex.jar;google-play-services-panorama-7.0.0.dex.jar;google-play-services-plus-7.0.0.dex.jar;google-play-services-wallet-7.0.0.dex.jar + + + package=com.embarcadero.$(MSBuildProjectName);label=$(MSBuildProjectName);versionCode=1;versionName=1.0.0;persistent=False;restoreAnyVersion=False;installLocation=auto;largeHeap=False;theme=TitleBar;hardwareAccelerated=true;apiKey= + Debug + true + Base + true + None + android-support-v4.dex.jar;cloud-messaging.dex.jar;fmx.dex.jar;google-analytics-v2.dex.jar;google-play-billing.dex.jar;google-play-licensing.dex.jar;google-play-services-ads-7.0.0.dex.jar;google-play-services-analytics-7.0.0.dex.jar;google-play-services-base-7.0.0.dex.jar;google-play-services-gcm-7.0.0.dex.jar;google-play-services-identity-7.0.0.dex.jar;google-play-services-maps-7.0.0.dex.jar;google-play-services-panorama-7.0.0.dex.jar;google-play-services-plus-7.0.0.dex.jar;google-play-services-wallet-7.0.0.dex.jar + + + None + + + None + + + Winapi;System.Win;Data.Win;Datasnap.Win;Web.Win;Soap.Win;Xml.Win;Bde;$(DCC_Namespace) + Debug + true + 1033 + + + Winapi;System.Win;Data.Win;Datasnap.Win;Web.Win;Soap.Win;Xml.Win;$(DCC_Namespace) + Debug + true + 1033 + + + DEBUG;$(DCC_Define) + true + false + true + true + true + + + false + true + 1033 + + + false + RELEASE;$(DCC_Define) + 0 + 0 + + + true + 1033 + + + true + 1033 + + + + MainSource + + + + + + + + + + + + + + + + + + + + + + + Cfg_2 + Base + + + Base + + + Cfg_1 + Base + + + + Delphi.Personality.12 + Package + + + + MARS.DCS.dpk + + + Microsoft Office 2000 Sample Automation Server Wrapper Components + Microsoft Office XP Sample Automation Server Wrapper Components + + + + + + true + + + + + MARS_DCS.bpl + true + + + + + true + + + + + true + + + + + MARS_DCS.bpl + true + + + + + true + + + + + true + + + + + MARS_DCS.bpl + true + + + + + 1 + + + 0 + + + + + classes + 1 + + + classes + 1 + + + + + res\xml + 1 + + + res\xml + 1 + + + + + library\lib\armeabi-v7a + 1 + + + + + library\lib\armeabi + 1 + + + library\lib\armeabi + 1 + + + + + library\lib\armeabi-v7a + 1 + + + + + library\lib\mips + 1 + + + library\lib\mips + 1 + + + + + library\lib\armeabi-v7a + 1 + + + library\lib\arm64-v8a + 1 + + + + + library\lib\armeabi-v7a + 1 + + + + + res\drawable + 1 + + + res\drawable + 1 + + + + + res\values + 1 + + + res\values + 1 + + + + + res\values-v21 + 1 + + + res\values-v21 + 1 + + + + + res\values + 1 + + + res\values + 1 + + + + + res\drawable + 1 + + + res\drawable + 1 + + + + + res\drawable-xxhdpi + 1 + + + res\drawable-xxhdpi + 1 + + + + + res\drawable-ldpi + 1 + + + res\drawable-ldpi + 1 + + + + + res\drawable-mdpi + 1 + + + res\drawable-mdpi + 1 + + + + + res\drawable-hdpi + 1 + + + res\drawable-hdpi + 1 + + + + + res\drawable-xhdpi + 1 + + + res\drawable-xhdpi + 1 + + + + + res\drawable-mdpi + 1 + + + res\drawable-mdpi + 1 + + + + + res\drawable-hdpi + 1 + + + res\drawable-hdpi + 1 + + + + + res\drawable-xhdpi + 1 + + + res\drawable-xhdpi + 1 + + + + + res\drawable-xxhdpi + 1 + + + res\drawable-xxhdpi + 1 + + + + + res\drawable-xxxhdpi + 1 + + + res\drawable-xxxhdpi + 1 + + + + + res\drawable-small + 1 + + + res\drawable-small + 1 + + + + + res\drawable-normal + 1 + + + res\drawable-normal + 1 + + + + + res\drawable-large + 1 + + + res\drawable-large + 1 + + + + + res\drawable-xlarge + 1 + + + res\drawable-xlarge + 1 + + + + + res\values + 1 + + + res\values + 1 + + + + + 1 + + + 1 + + + 0 + + + + + 1 + .framework + + + 1 + .framework + + + 0 + + + + + 1 + .dylib + + + 1 + .dylib + + + 0 + .dll;.bpl + + + + + 1 + .dylib + + + 1 + .dylib + + + 1 + .dylib + + + 1 + .dylib + + + 1 + .dylib + + + 0 + .bpl + + + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + ..\$(PROJECTNAME).launchscreen\Assets\LaunchScreenImage.imageset + 1 + + + ..\$(PROJECTNAME).launchscreen\Assets\LaunchScreenImage.imageset + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + ..\$(PROJECTNAME).launchscreen\Assets\LaunchScreenImage.imageset + 1 + + + ..\$(PROJECTNAME).launchscreen\Assets\LaunchScreenImage.imageset + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + ..\$(PROJECTNAME).launchscreen\Assets\LaunchScreenImage.imageset + 1 + + + ..\$(PROJECTNAME).launchscreen\Assets\LaunchScreenImage.imageset + 1 + + + + + 1 + + + 1 + + + 1 + + + + + ..\$(PROJECTNAME).launchscreen\Assets\LaunchScreenImage.imageset + 1 + + + ..\$(PROJECTNAME).launchscreen\Assets\LaunchScreenImage.imageset + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + ..\$(PROJECTNAME).launchscreen\Assets\LaunchScreenImage.imageset + 1 + + + ..\$(PROJECTNAME).launchscreen\Assets\LaunchScreenImage.imageset + 1 + + + + + ..\$(PROJECTNAME).launchscreen\Assets\LaunchScreenImage.imageset + 1 + + + ..\$(PROJECTNAME).launchscreen\Assets\LaunchScreenImage.imageset + 1 + + + + + 1 + + + 1 + + + + + ..\$(PROJECTNAME).app.dSYM\Contents\Resources\DWARF + 1 + + + ..\$(PROJECTNAME).app.dSYM\Contents\Resources\DWARF + 1 + + + + + + + + + 1 + + + 1 + + + 1 + + + + + + + + Contents\Resources + 1 + + + Contents\Resources + 1 + + + + + library\lib\armeabi-v7a + 1 + + + library\lib\arm64-v8a + 1 + + + 1 + + + 1 + + + 1 + + + 1 + + + 1 + + + 1 + + + 0 + + + + + library\lib\armeabi-v7a + 1 + + + + + 1 + + + 1 + + + + + Assets + 1 + + + Assets + 1 + + + + + Assets + 1 + + + Assets + 1 + + + + + + + + + + + + + + + False + False + False + False + False + True + True + + + 12 + + + + + diff --git a/Packages/104Sydney/MARS.FireDAC.dpk b/Packages/104Sydney/MARS.FireDAC.dpk new file mode 100644 index 00000000..c22644a3 --- /dev/null +++ b/Packages/104Sydney/MARS.FireDAC.dpk @@ -0,0 +1,59 @@ +package MARS.FireDAC; + +{$R *.res} +{$IFDEF IMPLICITBUILDING This IFDEF should not be used by users} +{$ALIGN 8} +{$ASSERTIONS ON} +{$BOOLEVAL OFF} +{$DEBUGINFO OFF} +{$EXTENDEDSYNTAX ON} +{$IMPORTEDDATA ON} +{$IOCHECKS ON} +{$LOCALSYMBOLS OFF} +{$LONGSTRINGS ON} +{$OPENSTRINGS ON} +{$OPTIMIZATION ON} +{$OVERFLOWCHECKS OFF} +{$RANGECHECKS OFF} +{$REFERENCEINFO OFF} +{$SAFEDIVIDE OFF} +{$STACKFRAMES OFF} +{$TYPEDADDRESS OFF} +{$VARSTRINGCHECKS ON} +{$WRITEABLECONST OFF} +{$MINENUMSIZE 1} +{$IMAGEBASE $400000} +{$DEFINE RELEASE} +{$ENDIF IMPLICITBUILDING} +{$DESCRIPTION 'MARS-Curiosity REST Library'} +{$LIBSUFFIX '270'} +{$RUNONLY} +{$IMPLICITBUILD OFF} + +requires + rtl, + dbrtl, + FireDAC, + FireDACCommonDriver, + FireDACCommon, + inet, + IndySystem, + IndyProtocols, + IndyCore, + dsnap, + MARS.Core, + MARS.ReadersAndWriters; + +contains + MARS.Data.FireDAC.DataModule in '..\..\Source\MARS.Data.FireDAC.DataModule.pas' {MARSFDDataModuleResource: TDataModule}, + MARS.Data.FireDAC.ReadersAndWriters in '..\..\Source\MARS.Data.FireDAC.ReadersAndWriters.pas', + MARS.Data.FireDAC in '..\..\Source\MARS.Data.FireDAC.pas', + MARS.Data.FireDAC.InjectionService in '..\..\Source\MARS.Data.FireDAC.InjectionService.pas', + MARS.Data.FireDAC.Resources in '..\..\Source\MARS.Data.FireDAC.Resources.pas', + MARS.Data.FireDAC.Utils in '..\..\Source\MARS.Data.FireDAC.Utils.pas'; + +end. + + + + diff --git a/Packages/104Sydney/MARS.FireDAC.dproj b/Packages/104Sydney/MARS.FireDAC.dproj new file mode 100644 index 00000000..55439df2 --- /dev/null +++ b/Packages/104Sydney/MARS.FireDAC.dproj @@ -0,0 +1,1062 @@ + + + {D44362A0-1BCF-45FD-80B4-281FB36C0E08} + MARS.FireDAC.dpk + 19.0 + None + True + Release + Win32 + 1 + Package + + + true + + + true + Base + true + + + true + Base + true + + + true + Cfg_1 + true + true + + + true + Base + true + + + true + Cfg_2 + true + true + + + true + true + ..\..\Source;$(DCC_UnitSearchPath) + 270 + MARS-Curiosity REST Library + CompanyName=;FileDescription=;FileVersion=1.0.0.0;InternalName=;LegalCopyright=;LegalTrademarks=;OriginalFilename=;ProductName=;ProductVersion=1.0.0.0;Comments= + 1040 + true + System;Xml;Data;Datasnap;Web;Soap;$(DCC_Namespace) + true + MARS_FireDAC + ..\..\Lib270\$(Platform)\$(Config) + .\$(Platform)\$(Config) + false + false + false + false + false + + + rtl;dbrtl;fmx;inet;IndySystem;IndyProtocols;IndyCore;FireDAC;FireDACCommonDriver;FireDACCommon;fmxFireDAC;dsnap;DataSnapFireDAC;$(DCC_UsePackage) + true + Winapi;System.Win;Data.Win;Datasnap.Win;Web.Win;Soap.Win;Xml.Win;Bde;$(DCC_Namespace) + 1033 + CompanyName=;FileVersion=1.0.0.0;InternalName=;LegalCopyright=;LegalTrademarks=;OriginalFilename=;ProductVersion=1.0.0.0;Comments=;ProgramID=com.embarcadero.$(ModuleName);FileDescription=$(ModuleName);ProductName=$(ModuleName) + + + DEBUG;$(DCC_Define) + true + false + true + true + true + + + CompanyName=;FileVersion=1.0.0.0;InternalName=;LegalCopyright=;LegalTrademarks=;OriginalFilename=;ProductVersion=1.0.0.0;Comments=;ProgramID=com.embarcadero.$(MSBuildProjectName);FileDescription=$(MSBuildProjectName);ProductName=$(MSBuildProjectName) + MARS-Curiosity FireDAC + true + 1033 + false + + + false + RELEASE;$(DCC_Define) + 0 + 0 + + + true + 1033 + CompanyName=;FileVersion=1.0.0.0;InternalName=;LegalCopyright=;LegalTrademarks=;OriginalFilename=;ProductVersion=1.0.0.0;Comments=;ProgramID=com.embarcadero.$(MSBuildProjectName);FileDescription=$(MSBuildProjectName);ProductName=$(MSBuildProjectName) + + + + MainSource + + + + + + + + + + + + + + +
MARSFDDataModuleResource
+ dfm + TDataModule +
+ + + + + + + Cfg_2 + Base + + + Base + + + Cfg_1 + Base + +
+ + Delphi.Personality.12 + Package + + + + MARS.FireDAC.dpk + + + Microsoft Office 2000 Sample Automation Server Wrapper Components + Microsoft Office XP Sample Automation Server Wrapper Components + + + + + + bplMARS_FireDAC.so + true + + + + + true + + + + + MARS_FireDAC.bpl + true + + + + + MARS_FireDAC.bpl + true + + + + + true + + + + + true + + + + + true + + + + + true + + + + + true + + + + + 1 + + + 0 + + + + + classes + 1 + + + classes + 1 + + + + + res\xml + 1 + + + res\xml + 1 + + + + + library\lib\armeabi-v7a + 1 + + + + + library\lib\armeabi + 1 + + + library\lib\armeabi + 1 + + + + + library\lib\armeabi-v7a + 1 + + + + + library\lib\mips + 1 + + + library\lib\mips + 1 + + + + + + library\lib\armeabi-v7a + 1 + + + library\lib\arm64-v8a + 1 + + + + + library\lib\armeabi-v7a + 1 + + + + + res\drawable + 1 + + + res\drawable + 1 + + + + + res\values + 1 + + + res\values + 1 + + + + + res\values-v21 + 1 + + + res\values-v21 + 1 + + + + + res\values + 1 + + + res\values + 1 + + + + + res\drawable + 1 + + + res\drawable + 1 + + + + + res\drawable-xxhdpi + 1 + + + res\drawable-xxhdpi + 1 + + + + + res\drawable-ldpi + 1 + + + res\drawable-ldpi + 1 + + + + + res\drawable-mdpi + 1 + + + res\drawable-mdpi + 1 + + + + + res\drawable-hdpi + 1 + + + res\drawable-hdpi + 1 + + + + + res\drawable-xhdpi + 1 + + + res\drawable-xhdpi + 1 + + + + + res\drawable-mdpi + 1 + + + res\drawable-mdpi + 1 + + + + + res\drawable-hdpi + 1 + + + res\drawable-hdpi + 1 + + + + + res\drawable-xhdpi + 1 + + + res\drawable-xhdpi + 1 + + + + + res\drawable-xxhdpi + 1 + + + res\drawable-xxhdpi + 1 + + + + + res\drawable-xxxhdpi + 1 + + + res\drawable-xxxhdpi + 1 + + + + + res\drawable-small + 1 + + + res\drawable-small + 1 + + + + + res\drawable-normal + 1 + + + res\drawable-normal + 1 + + + + + res\drawable-large + 1 + + + res\drawable-large + 1 + + + + + res\drawable-xlarge + 1 + + + res\drawable-xlarge + 1 + + + + + res\values + 1 + + + res\values + 1 + + + + + 1 + + + 1 + + + 0 + + + + + 1 + .framework + + + 1 + .framework + + + 0 + + + + + 1 + .dylib + + + 1 + .dylib + + + 0 + .dll;.bpl + + + + + 1 + .dylib + + + 1 + .dylib + + + 1 + .dylib + + + 1 + .dylib + + + 1 + .dylib + + + 0 + .bpl + + + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + ..\$(PROJECTNAME).launchscreen\Assets\LaunchScreenImage.imageset + 1 + + + ..\$(PROJECTNAME).launchscreen\Assets\LaunchScreenImage.imageset + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + ..\$(PROJECTNAME).launchscreen\Assets\LaunchScreenImage.imageset + 1 + + + ..\$(PROJECTNAME).launchscreen\Assets\LaunchScreenImage.imageset + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + ..\$(PROJECTNAME).launchscreen\Assets\LaunchScreenImage.imageset + 1 + + + ..\$(PROJECTNAME).launchscreen\Assets\LaunchScreenImage.imageset + 1 + + + + + 1 + + + 1 + + + 1 + + + + + ..\$(PROJECTNAME).launchscreen\Assets\LaunchScreenImage.imageset + 1 + + + ..\$(PROJECTNAME).launchscreen\Assets\LaunchScreenImage.imageset + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + ..\$(PROJECTNAME).launchscreen\Assets\LaunchScreenImage.imageset + 1 + + + ..\$(PROJECTNAME).launchscreen\Assets\LaunchScreenImage.imageset + 1 + + + + + ..\$(PROJECTNAME).launchscreen\Assets\LaunchScreenImage.imageset + 1 + + + ..\$(PROJECTNAME).launchscreen\Assets\LaunchScreenImage.imageset + 1 + + + + + 1 + + + 1 + + + + + ..\$(PROJECTNAME).app.dSYM\Contents\Resources\DWARF + 1 + + + ..\$(PROJECTNAME).app.dSYM\Contents\Resources\DWARF + 1 + + + + + + + + + 1 + + + 1 + + + 1 + + + + + + + + Contents\Resources + 1 + + + Contents\Resources + 1 + + + + + library\lib\armeabi-v7a + 1 + + + library\lib\arm64-v8a + 1 + + + 1 + + + 1 + + + 1 + + + 1 + + + 1 + + + 1 + + + 0 + + + + + library\lib\armeabi-v7a + 1 + + + + + 1 + + + 1 + + + + + Assets + 1 + + + Assets + 1 + + + + + Assets + 1 + + + Assets + 1 + + + + + + + + + + + + + + + True + + + 12 + + + + +
diff --git a/Packages/104Sydney/MARS.JOSE.dpk b/Packages/104Sydney/MARS.JOSE.dpk new file mode 100644 index 00000000..e1a81f4b --- /dev/null +++ b/Packages/104Sydney/MARS.JOSE.dpk @@ -0,0 +1,70 @@ +package MARS.JOSE; + +{$R *.res} +{$IFDEF IMPLICITBUILDING This IFDEF should not be used by users} +{$ALIGN 8} +{$ASSERTIONS ON} +{$BOOLEVAL OFF} +{$DEBUGINFO OFF} +{$EXTENDEDSYNTAX ON} +{$IMPORTEDDATA ON} +{$IOCHECKS ON} +{$LOCALSYMBOLS OFF} +{$LONGSTRINGS ON} +{$OPENSTRINGS ON} +{$OPTIMIZATION ON} +{$OVERFLOWCHECKS OFF} +{$RANGECHECKS OFF} +{$REFERENCEINFO OFF} +{$SAFEDIVIDE OFF} +{$STACKFRAMES OFF} +{$TYPEDADDRESS OFF} +{$VARSTRINGCHECKS ON} +{$WRITEABLECONST OFF} +{$MINENUMSIZE 1} +{$IMAGEBASE $400000} +{$DEFINE RELEASE} +{$ENDIF IMPLICITBUILDING} +{$DESCRIPTION 'MARS-Curiosity REST Library'} +{$LIBSUFFIX '270'} +{$RUNONLY} +{$IMPLICITBUILD OFF} + +requires + rtl, + IndySystem, + IndyProtocols, + IndyCore, + MARS.Utils, + MARS.Core; + +contains + MARS.JOSEJWT.Token in '..\..\Source\MARS.JOSEJWT.Token.pas', + MARS.JOSEJWT.Token.InjectionService in '..\..\Source\MARS.JOSEJWT.Token.InjectionService.pas', + JOSE.Builder in '..\..\ThirdParty\delphi-jose-jwt\Source\JOSE.Builder.pas', + JOSE.Consumer in '..\..\ThirdParty\delphi-jose-jwt\Source\JOSE.Consumer.pas', + JOSE.Consumer.Validators in '..\..\ThirdParty\delphi-jose-jwt\Source\JOSE.Consumer.Validators.pas', + JOSE.Context in '..\..\ThirdParty\delphi-jose-jwt\Source\JOSE.Context.pas', + JOSE.Core.Base in '..\..\ThirdParty\delphi-jose-jwt\Source\JOSE.Core.Base.pas', + JOSE.Core.Builder in '..\..\ThirdParty\delphi-jose-jwt\Source\JOSE.Core.Builder.pas', + JOSE.Core.JWA.Compression in '..\..\ThirdParty\delphi-jose-jwt\Source\JOSE.Core.JWA.Compression.pas', + JOSE.Core.JWA.Encryption in '..\..\ThirdParty\delphi-jose-jwt\Source\JOSE.Core.JWA.Encryption.pas', + JOSE.Core.JWA.Factory in '..\..\ThirdParty\delphi-jose-jwt\Source\JOSE.Core.JWA.Factory.pas', + JOSE.Core.JWA in '..\..\ThirdParty\delphi-jose-jwt\Source\JOSE.Core.JWA.pas', + JOSE.Core.JWA.Signing in '..\..\ThirdParty\delphi-jose-jwt\Source\JOSE.Core.JWA.Signing.pas', + JOSE.Core.JWE in '..\..\ThirdParty\delphi-jose-jwt\Source\JOSE.Core.JWE.pas', + JOSE.Core.JWK in '..\..\ThirdParty\delphi-jose-jwt\Source\JOSE.Core.JWK.pas', + JOSE.Core.JWS in '..\..\ThirdParty\delphi-jose-jwt\Source\JOSE.Core.JWS.pas', + JOSE.Core.JWT in '..\..\ThirdParty\delphi-jose-jwt\Source\JOSE.Core.JWT.pas', + JOSE.Core.Parts in '..\..\ThirdParty\delphi-jose-jwt\Source\JOSE.Core.Parts.pas', + JOSE.Encoding.Base64 in '..\..\ThirdParty\delphi-jose-jwt\Source\JOSE.Encoding.Base64.pas', + JOSE.Hashing.HMAC in '..\..\ThirdParty\delphi-jose-jwt\Source\JOSE.Hashing.HMAC.pas', + JOSE.Signing.RSA in '..\..\ThirdParty\delphi-jose-jwt\Source\JOSE.Signing.RSA.pas', + JOSE.Types.Arrays in '..\..\ThirdParty\delphi-jose-jwt\Source\JOSE.Types.Arrays.pas', + JOSE.Types.Bytes in '..\..\ThirdParty\delphi-jose-jwt\Source\JOSE.Types.Bytes.pas', + JOSE.Types.JSON in '..\..\ThirdParty\delphi-jose-jwt\Source\JOSE.Types.JSON.pas'; + +end. + + + diff --git a/Packages/104Sydney/MARS.JOSE.dproj b/Packages/104Sydney/MARS.JOSE.dproj new file mode 100644 index 00000000..561b3025 --- /dev/null +++ b/Packages/104Sydney/MARS.JOSE.dproj @@ -0,0 +1,1098 @@ + + + {16057797-C497-46DA-B133-A45E7F564D8D} + MARS.JOSE.dpk + 19.0 + None + True + Release + Win32 + 3 + Package + + + true + + + true + Base + true + + + true + Base + true + + + true + Base + true + + + true + Cfg_1 + true + true + + + true + Cfg_1 + true + true + + + true + Base + true + + + true + Cfg_2 + true + true + + + true + Cfg_2 + true + true + + + true + ..\..\Source;$(DCC_UnitSearchPath) + true + 270 + MARS-Curiosity REST Library + CompanyName=;FileDescription=;FileVersion=1.0.0.0;InternalName=;LegalCopyright=;LegalTrademarks=;OriginalFilename=;ProductName=;ProductVersion=1.0.0.0;Comments= + 1040 + true + System;Xml;Data;Datasnap;Web;Soap;$(DCC_Namespace) + true + MARS_JOSE + ..\..\Lib270\$(Platform)\$(Config) + .\$(Platform)\$(Config) + false + false + false + false + false + + + true + Winapi;System.Win;Data.Win;Datasnap.Win;Web.Win;Soap.Win;Xml.Win;Bde;$(DCC_Namespace) + 1033 + CompanyName=;FileVersion=1.0.0.0;InternalName=;LegalCopyright=;LegalTrademarks=;OriginalFilename=;ProductVersion=1.0.0.0;Comments=;ProgramID=com.embarcadero.$(MSBuildProjectName);FileDescription=$(MSBuildProjectName);ProductName=$(MSBuildProjectName) + + + 1033 + true + CompanyName=;FileDescription=$(MSBuildProjectName);FileVersion=1.0.0.0;InternalName=;LegalCopyright=;LegalTrademarks=;OriginalFilename=;ProgramID=com.embarcadero.$(MSBuildProjectName);ProductName=$(MSBuildProjectName);ProductVersion=1.0.0.0;Comments= + Winapi;System.Win;Data.Win;Datasnap.Win;Web.Win;Soap.Win;Xml.Win;$(DCC_Namespace) + Debug + + + DEBUG;$(DCC_Define) + true + false + true + true + true + + + CompanyName=;FileVersion=1.0.0.0;InternalName=;LegalCopyright=;LegalTrademarks=;OriginalFilename=;ProductVersion=1.0.0.0;Comments=;ProgramID=com.embarcadero.$(MSBuildProjectName);FileDescription=$(MSBuildProjectName);ProductName=$(MSBuildProjectName) + MARS-Curiosity JOSE + true + 1033 + false + + + true + 1033 + CompanyName=;FileDescription=$(MSBuildProjectName);FileVersion=1.0.0.0;InternalName=;LegalCopyright=;LegalTrademarks=;OriginalFilename=;ProgramID=com.embarcadero.$(MSBuildProjectName);ProductName=$(MSBuildProjectName);ProductVersion=1.0.0.0;Comments= + + + false + RELEASE;$(DCC_Define) + 0 + 0 + + + true + 1033 + CompanyName=;FileVersion=1.0.0.0;InternalName=;LegalCopyright=;LegalTrademarks=;OriginalFilename=;ProductVersion=1.0.0.0;Comments=;ProgramID=com.embarcadero.$(MSBuildProjectName);FileDescription=$(MSBuildProjectName);ProductName=$(MSBuildProjectName) + + + true + 1033 + CompanyName=;FileDescription=$(MSBuildProjectName);FileVersion=1.0.0.0;InternalName=;LegalCopyright=;LegalTrademarks=;OriginalFilename=;ProgramID=com.embarcadero.$(MSBuildProjectName);ProductName=$(MSBuildProjectName);ProductVersion=1.0.0.0;Comments= + + + + MainSource + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Cfg_2 + Base + + + Base + + + Cfg_1 + Base + + + + Delphi.Personality.12 + Package + + + + MARS.JOSE.dpk + + + Microsoft Office 2000 Sample Automation Server Wrapper Components + Microsoft Office XP Sample Automation Server Wrapper Components + + + + + + true + + + + + MARS_JOSE.bpl + true + + + + + true + + + + + true + + + + + true + + + + + true + + + + + true + + + + + MARS_JOSE.bpl + true + + + + + 1 + + + 0 + + + + + classes + 1 + + + classes + 1 + + + + + res\xml + 1 + + + res\xml + 1 + + + + + library\lib\armeabi-v7a + 1 + + + + + library\lib\armeabi + 1 + + + library\lib\armeabi + 1 + + + + + library\lib\armeabi-v7a + 1 + + + + + library\lib\mips + 1 + + + library\lib\mips + 1 + + + + + + library\lib\armeabi-v7a + 1 + + + library\lib\arm64-v8a + 1 + + + + + library\lib\armeabi-v7a + 1 + + + + + res\drawable + 1 + + + res\drawable + 1 + + + + + res\values + 1 + + + res\values + 1 + + + + + res\values-v21 + 1 + + + res\values-v21 + 1 + + + + + res\values + 1 + + + res\values + 1 + + + + + res\drawable + 1 + + + res\drawable + 1 + + + + + res\drawable-xxhdpi + 1 + + + res\drawable-xxhdpi + 1 + + + + + res\drawable-ldpi + 1 + + + res\drawable-ldpi + 1 + + + + + res\drawable-mdpi + 1 + + + res\drawable-mdpi + 1 + + + + + res\drawable-hdpi + 1 + + + res\drawable-hdpi + 1 + + + + + res\drawable-xhdpi + 1 + + + res\drawable-xhdpi + 1 + + + + + res\drawable-mdpi + 1 + + + res\drawable-mdpi + 1 + + + + + res\drawable-hdpi + 1 + + + res\drawable-hdpi + 1 + + + + + res\drawable-xhdpi + 1 + + + res\drawable-xhdpi + 1 + + + + + res\drawable-xxhdpi + 1 + + + res\drawable-xxhdpi + 1 + + + + + res\drawable-xxxhdpi + 1 + + + res\drawable-xxxhdpi + 1 + + + + + res\drawable-small + 1 + + + res\drawable-small + 1 + + + + + res\drawable-normal + 1 + + + res\drawable-normal + 1 + + + + + res\drawable-large + 1 + + + res\drawable-large + 1 + + + + + res\drawable-xlarge + 1 + + + res\drawable-xlarge + 1 + + + + + res\values + 1 + + + res\values + 1 + + + + + 1 + + + 1 + + + 0 + + + + + 1 + .framework + + + 1 + .framework + + + 0 + + + + + 1 + .dylib + + + 1 + .dylib + + + 0 + .dll;.bpl + + + + + 1 + .dylib + + + 1 + .dylib + + + 1 + .dylib + + + 1 + .dylib + + + 1 + .dylib + + + 0 + .bpl + + + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + ..\$(PROJECTNAME).launchscreen\Assets\LaunchScreenImage.imageset + 1 + + + ..\$(PROJECTNAME).launchscreen\Assets\LaunchScreenImage.imageset + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + ..\$(PROJECTNAME).launchscreen\Assets\LaunchScreenImage.imageset + 1 + + + ..\$(PROJECTNAME).launchscreen\Assets\LaunchScreenImage.imageset + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + ..\$(PROJECTNAME).launchscreen\Assets\LaunchScreenImage.imageset + 1 + + + ..\$(PROJECTNAME).launchscreen\Assets\LaunchScreenImage.imageset + 1 + + + + + 1 + + + 1 + + + 1 + + + + + ..\$(PROJECTNAME).launchscreen\Assets\LaunchScreenImage.imageset + 1 + + + ..\$(PROJECTNAME).launchscreen\Assets\LaunchScreenImage.imageset + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + ..\$(PROJECTNAME).launchscreen\Assets\LaunchScreenImage.imageset + 1 + + + ..\$(PROJECTNAME).launchscreen\Assets\LaunchScreenImage.imageset + 1 + + + + + ..\$(PROJECTNAME).launchscreen\Assets\LaunchScreenImage.imageset + 1 + + + ..\$(PROJECTNAME).launchscreen\Assets\LaunchScreenImage.imageset + 1 + + + + + 1 + + + 1 + + + + + ..\$(PROJECTNAME).app.dSYM\Contents\Resources\DWARF + 1 + + + ..\$(PROJECTNAME).app.dSYM\Contents\Resources\DWARF + 1 + + + + + + + + + 1 + + + 1 + + + 1 + + + + + + + + Contents\Resources + 1 + + + Contents\Resources + 1 + + + + + library\lib\armeabi-v7a + 1 + + + library\lib\arm64-v8a + 1 + + + 1 + + + 1 + + + 1 + + + 1 + + + 1 + + + 1 + + + 0 + + + + + library\lib\armeabi-v7a + 1 + + + + + 1 + + + 1 + + + + + Assets + 1 + + + Assets + 1 + + + + + Assets + 1 + + + Assets + 1 + + + + + + + + + + + + + + + True + True + + + 12 + + + + + diff --git a/Packages/104Sydney/MARS.ReadersAndWriters.dpk b/Packages/104Sydney/MARS.ReadersAndWriters.dpk new file mode 100644 index 00000000..5047895f --- /dev/null +++ b/Packages/104Sydney/MARS.ReadersAndWriters.dpk @@ -0,0 +1,49 @@ +package MARS.ReadersAndWriters; + +{$R *.res} +{$IFDEF IMPLICITBUILDING This IFDEF should not be used by users} +{$ALIGN 8} +{$ASSERTIONS ON} +{$BOOLEVAL OFF} +{$DEBUGINFO OFF} +{$EXTENDEDSYNTAX ON} +{$IMPORTEDDATA ON} +{$IOCHECKS ON} +{$LOCALSYMBOLS OFF} +{$LONGSTRINGS ON} +{$OPENSTRINGS ON} +{$OPTIMIZATION ON} +{$OVERFLOWCHECKS OFF} +{$RANGECHECKS OFF} +{$REFERENCEINFO OFF} +{$SAFEDIVIDE OFF} +{$STACKFRAMES OFF} +{$TYPEDADDRESS OFF} +{$VARSTRINGCHECKS ON} +{$WRITEABLECONST OFF} +{$MINENUMSIZE 1} +{$IMAGEBASE $400000} +{$DEFINE RELEASE} +{$ENDIF IMPLICITBUILDING} +{$DESCRIPTION 'MARS-Curiosity REST Library'} +{$LIBSUFFIX '270'} +{$RUNONLY} +{$IMPLICITBUILD OFF} + +requires + rtl, + dbrtl, + inet, + IndySystem, + IndyProtocols, + IndyCore, + dsnap, + MARS.Utils, + MARS.Core; + +contains + MARS.Core.MessageBodyWriters in '..\..\Source\MARS.Core.MessageBodyWriters.pas', + MARS.Data.MessageBodyWriters in '..\..\Source\MARS.Data.MessageBodyWriters.pas', + MARS.Core.MessageBodyReaders in '..\..\Source\MARS.Core.MessageBodyReaders.pas'; + +end. diff --git a/Packages/104Sydney/MARS.ReadersAndWriters.dproj b/Packages/104Sydney/MARS.ReadersAndWriters.dproj new file mode 100644 index 00000000..b0f45927 --- /dev/null +++ b/Packages/104Sydney/MARS.ReadersAndWriters.dproj @@ -0,0 +1,1070 @@ + + + {5170D912-4303-47AD-85A6-D77D3D0874DA} + MARS.ReadersAndWriters.dpk + 19.0 + None + True + Release + Win32 + 131 + Package + + + true + + + true + Base + true + + + true + Base + true + + + true + Base + true + + + true + Cfg_1 + true + true + + + true + Cfg_1 + true + true + + + true + Base + true + + + true + Cfg_2 + true + true + + + true + ..\..\Source;$(DCC_UnitSearchPath) + true + 270 + MARS-Curiosity REST Library + CompanyName=;FileDescription=;FileVersion=1.0.0.0;InternalName=;LegalCopyright=;LegalTrademarks=;OriginalFilename=;ProductName=;ProductVersion=1.0.0.0;Comments= + 1040 + true + System;Xml;Data;Datasnap;Web;Soap;$(DCC_Namespace) + true + MARS_ReadersAndWriters + ..\..\Lib270\$(Platform)\$(Config) + .\$(Platform)\$(Config) + false + false + false + false + false + + + true + Winapi;System.Win;Data.Win;Datasnap.Win;Web.Win;Soap.Win;Xml.Win;Bde;$(DCC_Namespace) + 1033 + CompanyName=;FileVersion=1.0.0.0;InternalName=;LegalCopyright=;LegalTrademarks=;OriginalFilename=;ProductVersion=1.0.0.0;Comments=;ProgramID=com.embarcadero.$(MSBuildProjectName);FileDescription=$(MSBuildProjectName);ProductName=$(MSBuildProjectName) + + + 1033 + true + CompanyName=;FileDescription=$(MSBuildProjectName);FileVersion=1.0.0.0;InternalName=;LegalCopyright=;LegalTrademarks=;OriginalFilename=;ProgramID=com.embarcadero.$(MSBuildProjectName);ProductName=$(MSBuildProjectName);ProductVersion=1.0.0.0;Comments= + Winapi;System.Win;Data.Win;Datasnap.Win;Web.Win;Soap.Win;Xml.Win;$(DCC_Namespace) + Debug + + + DEBUG;$(DCC_Define) + true + false + true + true + true + + + CompanyName=;FileVersion=1.0.0.0;InternalName=;LegalCopyright=;LegalTrademarks=;OriginalFilename=;ProductVersion=1.0.0.0;Comments=;ProgramID=com.embarcadero.$(MSBuildProjectName);FileDescription=$(MSBuildProjectName);ProductName=$(MSBuildProjectName) + MARS-Curiosity Readers and Writers + true + 1033 + false + + + true + 1033 + CompanyName=;FileDescription=$(MSBuildProjectName);FileVersion=1.0.0.0;InternalName=;LegalCopyright=;LegalTrademarks=;OriginalFilename=;ProgramID=com.embarcadero.$(MSBuildProjectName);ProductName=$(MSBuildProjectName);ProductVersion=1.0.0.0;Comments= + + + false + RELEASE;$(DCC_Define) + 0 + 0 + + + true + 1033 + CompanyName=;FileVersion=1.0.0.0;InternalName=;LegalCopyright=;LegalTrademarks=;OriginalFilename=;ProductVersion=1.0.0.0;Comments=;ProgramID=com.embarcadero.$(MSBuildProjectName);FileDescription=$(MSBuildProjectName);ProductName=$(MSBuildProjectName) + + + + MainSource + + + + + + + + + + + + + + + Cfg_2 + Base + + + Base + + + Cfg_1 + Base + + + + Delphi.Personality.12 + Package + + + + MARS.ReadersAndWriters.dpk + + + Microsoft Office 2000 Sample Automation Server Wrapper Components + Microsoft Office XP Sample Automation Server Wrapper Components + + + + + + true + + + + + MARS_ReadersAndWriters.bpl + true + + + + + true + + + + + true + + + + + true + + + + + true + + + + + true + + + + + MARS_ReadersAndWriters.bpl + true + + + + + 1 + + + 0 + + + + + classes + 1 + + + classes + 1 + + + + + res\xml + 1 + + + res\xml + 1 + + + + + library\lib\armeabi-v7a + 1 + + + + + library\lib\armeabi + 1 + + + library\lib\armeabi + 1 + + + + + library\lib\armeabi-v7a + 1 + + + + + library\lib\mips + 1 + + + library\lib\mips + 1 + + + + + + library\lib\armeabi-v7a + 1 + + + library\lib\arm64-v8a + 1 + + + + + library\lib\armeabi-v7a + 1 + + + + + res\drawable + 1 + + + res\drawable + 1 + + + + + res\values + 1 + + + res\values + 1 + + + + + res\values-v21 + 1 + + + res\values-v21 + 1 + + + + + res\values + 1 + + + res\values + 1 + + + + + res\drawable + 1 + + + res\drawable + 1 + + + + + res\drawable-xxhdpi + 1 + + + res\drawable-xxhdpi + 1 + + + + + res\drawable-ldpi + 1 + + + res\drawable-ldpi + 1 + + + + + res\drawable-mdpi + 1 + + + res\drawable-mdpi + 1 + + + + + res\drawable-hdpi + 1 + + + res\drawable-hdpi + 1 + + + + + res\drawable-xhdpi + 1 + + + res\drawable-xhdpi + 1 + + + + + res\drawable-mdpi + 1 + + + res\drawable-mdpi + 1 + + + + + res\drawable-hdpi + 1 + + + res\drawable-hdpi + 1 + + + + + res\drawable-xhdpi + 1 + + + res\drawable-xhdpi + 1 + + + + + res\drawable-xxhdpi + 1 + + + res\drawable-xxhdpi + 1 + + + + + res\drawable-xxxhdpi + 1 + + + res\drawable-xxxhdpi + 1 + + + + + res\drawable-small + 1 + + + res\drawable-small + 1 + + + + + res\drawable-normal + 1 + + + res\drawable-normal + 1 + + + + + res\drawable-large + 1 + + + res\drawable-large + 1 + + + + + res\drawable-xlarge + 1 + + + res\drawable-xlarge + 1 + + + + + res\values + 1 + + + res\values + 1 + + + + + 1 + + + 1 + + + 0 + + + + + 1 + .framework + + + 1 + .framework + + + 0 + + + + + 1 + .dylib + + + 1 + .dylib + + + 0 + .dll;.bpl + + + + + 1 + .dylib + + + 1 + .dylib + + + 1 + .dylib + + + 1 + .dylib + + + 1 + .dylib + + + 0 + .bpl + + + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + ..\$(PROJECTNAME).launchscreen\Assets\LaunchScreenImage.imageset + 1 + + + ..\$(PROJECTNAME).launchscreen\Assets\LaunchScreenImage.imageset + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + ..\$(PROJECTNAME).launchscreen\Assets\LaunchScreenImage.imageset + 1 + + + ..\$(PROJECTNAME).launchscreen\Assets\LaunchScreenImage.imageset + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + ..\$(PROJECTNAME).launchscreen\Assets\LaunchScreenImage.imageset + 1 + + + ..\$(PROJECTNAME).launchscreen\Assets\LaunchScreenImage.imageset + 1 + + + + + 1 + + + 1 + + + 1 + + + + + ..\$(PROJECTNAME).launchscreen\Assets\LaunchScreenImage.imageset + 1 + + + ..\$(PROJECTNAME).launchscreen\Assets\LaunchScreenImage.imageset + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + ..\$(PROJECTNAME).launchscreen\Assets\LaunchScreenImage.imageset + 1 + + + ..\$(PROJECTNAME).launchscreen\Assets\LaunchScreenImage.imageset + 1 + + + + + ..\$(PROJECTNAME).launchscreen\Assets\LaunchScreenImage.imageset + 1 + + + ..\$(PROJECTNAME).launchscreen\Assets\LaunchScreenImage.imageset + 1 + + + + + 1 + + + 1 + + + + + ..\$(PROJECTNAME).app.dSYM\Contents\Resources\DWARF + 1 + + + ..\$(PROJECTNAME).app.dSYM\Contents\Resources\DWARF + 1 + + + + + + + + + 1 + + + 1 + + + 1 + + + + + + + + Contents\Resources + 1 + + + Contents\Resources + 1 + + + + + library\lib\armeabi-v7a + 1 + + + library\lib\arm64-v8a + 1 + + + 1 + + + 1 + + + 1 + + + 1 + + + 1 + + + 1 + + + 0 + + + + + library\lib\armeabi-v7a + 1 + + + + + 1 + + + 1 + + + + + Assets + 1 + + + Assets + 1 + + + + + Assets + 1 + + + Assets + 1 + + + + + + + + + + + + + + + True + True + True + + + 12 + + + + + diff --git a/Packages/104Sydney/MARS.Utils.dpk b/Packages/104Sydney/MARS.Utils.dpk new file mode 100644 index 00000000..214c65ad --- /dev/null +++ b/Packages/104Sydney/MARS.Utils.dpk @@ -0,0 +1,59 @@ +package MARS.Utils; + +{$R *.res} +{$IFDEF IMPLICITBUILDING This IFDEF should not be used by users} +{$ALIGN 8} +{$ASSERTIONS ON} +{$BOOLEVAL OFF} +{$DEBUGINFO OFF} +{$EXTENDEDSYNTAX ON} +{$IMPORTEDDATA ON} +{$IOCHECKS ON} +{$LOCALSYMBOLS OFF} +{$LONGSTRINGS ON} +{$OPENSTRINGS ON} +{$OPTIMIZATION ON} +{$OVERFLOWCHECKS OFF} +{$RANGECHECKS OFF} +{$REFERENCEINFO OFF} +{$SAFEDIVIDE OFF} +{$STACKFRAMES OFF} +{$TYPEDADDRESS OFF} +{$VARSTRINGCHECKS ON} +{$WRITEABLECONST OFF} +{$MINENUMSIZE 1} +{$IMAGEBASE $400000} +{$DEFINE RELEASE} +{$ENDIF IMPLICITBUILDING} +{$DESCRIPTION 'MARS-Curiosity REST Library'} +{$LIBSUFFIX '270'} +{$RUNONLY} +{$IMPLICITBUILD OFF} + +requires + rtl, + dbrtl, + inet, + IndySystem, + IndyProtocols, + IndyCore, + RESTComponents; + +contains + MARS.Core.Utils in '..\..\Source\MARS.Core.Utils.pas', + MARS.Data.Utils in '..\..\Source\MARS.Data.Utils.pas', + MARS.Rtti.Utils in '..\..\Source\MARS.Rtti.Utils.pas', + MARS.Utils.Parameters.IniFile in '..\..\Source\MARS.Utils.Parameters.IniFile.pas', + MARS.Utils.Parameters in '..\..\Source\MARS.Utils.Parameters.pas', + MARS.Utils.Parameters.JSON in '..\..\Source\MARS.Utils.Parameters.JSON.pas', + MARS.Core.JSON in '..\..\Source\MARS.Core.JSON.pas', + MARS.Core.URL in '..\..\Source\MARS.Core.URL.pas', + MARS.Core.Exceptions in '..\..\Source\MARS.Core.Exceptions.pas', + MARS.Core.Declarations in '..\..\Source\MARS.Core.Declarations.pas', + MARS.Core.MediaType in '..\..\Source\MARS.Core.MediaType.pas', + MARS.Utils.JWT in '..\..\Source\MARS.Utils.JWT.pas', + MARS.Core.RequestAndResponse.Interfaces in '..\..\Source\MARS.Core.RequestAndResponse.Interfaces.pas'; + +end. + + diff --git a/Packages/104Sydney/MARS.Utils.dproj b/Packages/104Sydney/MARS.Utils.dproj new file mode 100644 index 00000000..e0729929 --- /dev/null +++ b/Packages/104Sydney/MARS.Utils.dproj @@ -0,0 +1,1078 @@ + + + {A5387B60-F9BE-4AEF-8E11-EFB0EDCAB2ED} + MARS.Utils.dpk + 19.0 + None + True + Release + Win32 + 131 + Package + + + true + + + true + Base + true + + + true + Base + true + + + true + Base + true + + + true + Cfg_1 + true + true + + + true + Cfg_1 + true + true + + + true + Base + true + + + true + Cfg_2 + true + true + + + true + ..\..\Source;$(DCC_UnitSearchPath) + true + 270 + MARS-Curiosity REST Library + CompanyName=;FileDescription=;FileVersion=1.0.0.0;InternalName=;LegalCopyright=;LegalTrademarks=;OriginalFilename=;ProductName=;ProductVersion=1.0.0.0;Comments= + 1040 + true + System;Xml;Data;Datasnap;Web;Soap;$(DCC_Namespace) + true + MARS_Utils + ..\..\Lib270\$(Platform)\$(Config) + .\$(Platform)\$(Config) + false + false + false + false + false + + + true + Winapi;System.Win;Data.Win;Datasnap.Win;Web.Win;Soap.Win;Xml.Win;Bde;$(DCC_Namespace) + 1033 + CompanyName=;FileVersion=1.0.0.0;InternalName=;LegalCopyright=;LegalTrademarks=;OriginalFilename=;ProductVersion=1.0.0.0;Comments=;ProgramID=com.embarcadero.$(MSBuildProjectName);FileDescription=$(MSBuildProjectName);ProductName=$(MSBuildProjectName) + + + 1033 + true + CompanyName=;FileDescription=$(MSBuildProjectName);FileVersion=1.0.0.0;InternalName=;LegalCopyright=;LegalTrademarks=;OriginalFilename=;ProgramID=com.embarcadero.$(MSBuildProjectName);ProductName=$(MSBuildProjectName);ProductVersion=1.0.0.0;Comments= + Winapi;System.Win;Data.Win;Datasnap.Win;Web.Win;Soap.Win;Xml.Win;$(DCC_Namespace) + Debug + + + DEBUG;$(DCC_Define) + true + false + true + true + true + + + CompanyName=;FileVersion=1.0.0.0;InternalName=;LegalCopyright=;LegalTrademarks=;OriginalFilename=;ProductVersion=1.0.0.0;Comments=;ProgramID=com.embarcadero.$(MSBuildProjectName);FileDescription=$(MSBuildProjectName);ProductName=$(MSBuildProjectName) + MARS-Curiosity Utils + true + 1033 + false + + + true + 1033 + CompanyName=;FileDescription=$(MSBuildProjectName);FileVersion=1.0.0.0;InternalName=;LegalCopyright=;LegalTrademarks=;OriginalFilename=;ProgramID=com.embarcadero.$(MSBuildProjectName);ProductName=$(MSBuildProjectName);ProductVersion=1.0.0.0;Comments= + + + false + RELEASE;$(DCC_Define) + 0 + 0 + + + true + 1033 + CompanyName=;FileVersion=1.0.0.0;InternalName=;LegalCopyright=;LegalTrademarks=;OriginalFilename=;ProductVersion=1.0.0.0;Comments=;ProgramID=com.embarcadero.$(MSBuildProjectName);FileDescription=$(MSBuildProjectName);ProductName=$(MSBuildProjectName) + + + + MainSource + + + + + + + + + + + + + + + + + + + + + + + Cfg_2 + Base + + + Base + + + Cfg_1 + Base + + + + Delphi.Personality.12 + Package + + + + MARS.Utils.dpk + + + Microsoft Office 2000 Sample Automation Server Wrapper Components + Microsoft Office XP Sample Automation Server Wrapper Components + + + + + + true + + + + + true + + + + + true + + + + + MARS_Utils.bpl + true + + + + + true + + + + + MARS_Utils.bpl + true + + + + + true + + + + + true + + + + + 1 + + + 0 + + + + + classes + 1 + + + classes + 1 + + + + + res\xml + 1 + + + res\xml + 1 + + + + + library\lib\armeabi-v7a + 1 + + + + + library\lib\armeabi + 1 + + + library\lib\armeabi + 1 + + + + + library\lib\armeabi-v7a + 1 + + + + + library\lib\mips + 1 + + + library\lib\mips + 1 + + + + + + library\lib\armeabi-v7a + 1 + + + library\lib\arm64-v8a + 1 + + + + + library\lib\armeabi-v7a + 1 + + + + + res\drawable + 1 + + + res\drawable + 1 + + + + + res\values + 1 + + + res\values + 1 + + + + + res\values-v21 + 1 + + + res\values-v21 + 1 + + + + + res\values + 1 + + + res\values + 1 + + + + + res\drawable + 1 + + + res\drawable + 1 + + + + + res\drawable-xxhdpi + 1 + + + res\drawable-xxhdpi + 1 + + + + + res\drawable-ldpi + 1 + + + res\drawable-ldpi + 1 + + + + + res\drawable-mdpi + 1 + + + res\drawable-mdpi + 1 + + + + + res\drawable-hdpi + 1 + + + res\drawable-hdpi + 1 + + + + + res\drawable-xhdpi + 1 + + + res\drawable-xhdpi + 1 + + + + + res\drawable-mdpi + 1 + + + res\drawable-mdpi + 1 + + + + + res\drawable-hdpi + 1 + + + res\drawable-hdpi + 1 + + + + + res\drawable-xhdpi + 1 + + + res\drawable-xhdpi + 1 + + + + + res\drawable-xxhdpi + 1 + + + res\drawable-xxhdpi + 1 + + + + + res\drawable-xxxhdpi + 1 + + + res\drawable-xxxhdpi + 1 + + + + + res\drawable-small + 1 + + + res\drawable-small + 1 + + + + + res\drawable-normal + 1 + + + res\drawable-normal + 1 + + + + + res\drawable-large + 1 + + + res\drawable-large + 1 + + + + + res\drawable-xlarge + 1 + + + res\drawable-xlarge + 1 + + + + + res\values + 1 + + + res\values + 1 + + + + + 1 + + + 1 + + + 0 + + + + + 1 + .framework + + + 1 + .framework + + + 0 + + + + + 1 + .dylib + + + 1 + .dylib + + + 0 + .dll;.bpl + + + + + 1 + .dylib + + + 1 + .dylib + + + 1 + .dylib + + + 1 + .dylib + + + 1 + .dylib + + + 0 + .bpl + + + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + ..\$(PROJECTNAME).launchscreen\Assets\LaunchScreenImage.imageset + 1 + + + ..\$(PROJECTNAME).launchscreen\Assets\LaunchScreenImage.imageset + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + ..\$(PROJECTNAME).launchscreen\Assets\LaunchScreenImage.imageset + 1 + + + ..\$(PROJECTNAME).launchscreen\Assets\LaunchScreenImage.imageset + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + ..\$(PROJECTNAME).launchscreen\Assets\LaunchScreenImage.imageset + 1 + + + ..\$(PROJECTNAME).launchscreen\Assets\LaunchScreenImage.imageset + 1 + + + + + 1 + + + 1 + + + 1 + + + + + ..\$(PROJECTNAME).launchscreen\Assets\LaunchScreenImage.imageset + 1 + + + ..\$(PROJECTNAME).launchscreen\Assets\LaunchScreenImage.imageset + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + ..\$(PROJECTNAME).launchscreen\Assets\LaunchScreenImage.imageset + 1 + + + ..\$(PROJECTNAME).launchscreen\Assets\LaunchScreenImage.imageset + 1 + + + + + ..\$(PROJECTNAME).launchscreen\Assets\LaunchScreenImage.imageset + 1 + + + ..\$(PROJECTNAME).launchscreen\Assets\LaunchScreenImage.imageset + 1 + + + + + 1 + + + 1 + + + + + ..\$(PROJECTNAME).app.dSYM\Contents\Resources\DWARF + 1 + + + ..\$(PROJECTNAME).app.dSYM\Contents\Resources\DWARF + 1 + + + + + + + + + 1 + + + 1 + + + 1 + + + + + + + + Contents\Resources + 1 + + + Contents\Resources + 1 + + + + + library\lib\armeabi-v7a + 1 + + + library\lib\arm64-v8a + 1 + + + 1 + + + 1 + + + 1 + + + 1 + + + 1 + + + 1 + + + 0 + + + + + library\lib\armeabi-v7a + 1 + + + + + 1 + + + 1 + + + + + Assets + 1 + + + Assets + 1 + + + + + Assets + 1 + + + Assets + 1 + + + + + + + + + + + + + + + True + True + True + + + 12 + + + + + diff --git a/Packages/104Sydney/MARS.groupproj b/Packages/104Sydney/MARS.groupproj new file mode 100644 index 00000000..9bf614c5 --- /dev/null +++ b/Packages/104Sydney/MARS.groupproj @@ -0,0 +1,108 @@ + + + {79526BDC-090B-468F-A29A-B97CAFE42EA6} + + + + + + + + + + + + + + + + + + + + + + + + + + Default.Personality.12 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Packages/104Sydney/MARS.mORMot.dpk b/Packages/104Sydney/MARS.mORMot.dpk new file mode 100644 index 00000000..949786c7 --- /dev/null +++ b/Packages/104Sydney/MARS.mORMot.dpk @@ -0,0 +1,50 @@ +package MARS.mORMot; + +{$R *.res} +{$IFDEF IMPLICITBUILDING This IFDEF should not be used by users} +{$ALIGN 8} +{$ASSERTIONS ON} +{$BOOLEVAL OFF} +{$DEBUGINFO OFF} +{$EXTENDEDSYNTAX ON} +{$IMPORTEDDATA ON} +{$IOCHECKS ON} +{$LOCALSYMBOLS OFF} +{$LONGSTRINGS ON} +{$OPENSTRINGS ON} +{$OPTIMIZATION ON} +{$OVERFLOWCHECKS OFF} +{$RANGECHECKS OFF} +{$REFERENCEINFO OFF} +{$SAFEDIVIDE OFF} +{$STACKFRAMES OFF} +{$TYPEDADDRESS OFF} +{$VARSTRINGCHECKS ON} +{$WRITEABLECONST OFF} +{$MINENUMSIZE 1} +{$IMAGEBASE $400000} +{$DEFINE RELEASE} +{$ENDIF IMPLICITBUILDING} +{$DESCRIPTION 'MARS-Curiosity REST Library'} +{$LIBSUFFIX '270'} +{$RUNONLY} +{$IMPLICITBUILD OFF} + +requires + rtl, + IndySystem, + IndyProtocols, + IndyCore, + MARS.Utils, + MARS.Core; + +contains + MARS.mORMotJWT.Token in '..\..\Source\MARS.mORMotJWT.Token.pas', + SynCommons in '..\..\ThirdParty\mORMot\Source\SynCommons.pas', + SynCrypto in '..\..\ThirdParty\mORMot\Source\SynCrypto.pas', + SynEcc in '..\..\ThirdParty\mORMot\Source\SynEcc.pas', + SynLZ in '..\..\ThirdParty\mORMot\Source\SynLZ.pas', + MARS.mORMotJWT.Token.InjectionService in '..\..\Source\MARS.mORMotJWT.Token.InjectionService.pas', + SynTable in '..\..\ThirdParty\mORMot\Source\SynTable.pas'; + +end. diff --git a/Packages/104Sydney/MARS.mORMot.dproj b/Packages/104Sydney/MARS.mORMot.dproj new file mode 100644 index 00000000..50a5f0d1 --- /dev/null +++ b/Packages/104Sydney/MARS.mORMot.dproj @@ -0,0 +1,1083 @@ + + + {CFDDCD9D-D0A9-42FE-8036-193E21B00BD9} + MARS.mORMot.dpk + 19.0 + None + True + Release + Win32 + 131 + Package + + + true + + + true + Base + true + + + true + Base + true + + + true + Base + true + + + true + Cfg_1 + true + true + + + true + Cfg_1 + true + true + + + true + Base + true + + + true + Cfg_2 + true + true + + + true + Cfg_2 + true + true + + + true + ..\..\Source;$(DCC_UnitSearchPath) + true + 270 + MARS-Curiosity REST Library + CompanyName=;FileDescription=;FileVersion=1.0.0.0;InternalName=;LegalCopyright=;LegalTrademarks=;OriginalFilename=;ProductName=;ProductVersion=1.0.0.0;Comments= + 1040 + true + System;Xml;Data;Datasnap;Web;Soap;$(DCC_Namespace) + true + MARS_mORMot + ..\..\Lib270\$(Platform)\$(Config) + .\$(Platform)\$(Config) + false + false + false + false + false + false + + + true + Winapi;System.Win;Data.Win;Datasnap.Win;Web.Win;Soap.Win;Xml.Win;Bde;$(DCC_Namespace) + 1033 + CompanyName=;FileVersion=1.0.0.0;InternalName=;LegalCopyright=;LegalTrademarks=;OriginalFilename=;ProductVersion=1.0.0.0;Comments=;ProgramID=com.embarcadero.$(MSBuildProjectName);FileDescription=$(MSBuildProjectName);ProductName=$(MSBuildProjectName) + + + 1033 + true + CompanyName=;FileDescription=$(MSBuildProjectName);FileVersion=1.0.0.0;InternalName=;LegalCopyright=;LegalTrademarks=;OriginalFilename=;ProgramID=com.embarcadero.$(MSBuildProjectName);ProductName=$(MSBuildProjectName);ProductVersion=1.0.0.0;Comments= + Winapi;System.Win;Data.Win;Datasnap.Win;Web.Win;Soap.Win;Xml.Win;$(DCC_Namespace) + Debug + + + DEBUG;$(DCC_Define) + true + false + true + true + true + + + CompanyName=;FileVersion=1.0.0.0;InternalName=;LegalCopyright=;LegalTrademarks=;OriginalFilename=;ProductVersion=1.0.0.0;Comments=;ProgramID=com.embarcadero.$(MSBuildProjectName);FileDescription=$(MSBuildProjectName);ProductName=$(MSBuildProjectName) + MARS-Curiosity mORMot JWT + true + 1033 + false + + + true + 1033 + CompanyName=;FileDescription=$(MSBuildProjectName);FileVersion=1.0.0.0;InternalName=;LegalCopyright=;LegalTrademarks=;OriginalFilename=;ProgramID=com.embarcadero.$(MSBuildProjectName);ProductName=$(MSBuildProjectName);ProductVersion=1.0.0.0;Comments= + + + false + RELEASE;$(DCC_Define) + 0 + 0 + + + true + 1033 + CompanyName=;FileVersion=1.0.0.0;InternalName=;LegalCopyright=;LegalTrademarks=;OriginalFilename=;ProductVersion=1.0.0.0;Comments=;ProgramID=com.embarcadero.$(MSBuildProjectName);FileDescription=$(MSBuildProjectName);ProductName=$(MSBuildProjectName) + + + true + 1033 + CompanyName=;FileDescription=$(MSBuildProjectName);FileVersion=1.0.0.0;InternalName=;LegalCopyright=;LegalTrademarks=;OriginalFilename=;ProgramID=com.embarcadero.$(MSBuildProjectName);ProductName=$(MSBuildProjectName);ProductVersion=1.0.0.0;Comments= + + + + MainSource + + + + + + + + + + + + + + + + Cfg_2 + Base + + + Base + + + Cfg_1 + Base + + + + Delphi.Personality.12 + Package + + + + MARS.mORMot.dpk + + + Microsoft Office 2000 Sample Automation Server Wrapper Components + Microsoft Office XP Sample Automation Server Wrapper Components + + + + + + true + + + + + true + + + + + MARS_mORMot.bpl + true + + + + + true + + + + + true + + + + + MARS_mORMot.bpl + true + + + + + true + + + + + true + + + + + 1 + + + 0 + + + + + classes + 1 + + + classes + 1 + + + + + res\xml + 1 + + + res\xml + 1 + + + + + library\lib\armeabi-v7a + 1 + + + + + library\lib\armeabi + 1 + + + library\lib\armeabi + 1 + + + + + library\lib\armeabi-v7a + 1 + + + + + library\lib\mips + 1 + + + library\lib\mips + 1 + + + + + + library\lib\armeabi-v7a + 1 + + + library\lib\arm64-v8a + 1 + + + + + library\lib\armeabi-v7a + 1 + + + + + res\drawable + 1 + + + res\drawable + 1 + + + + + res\values + 1 + + + res\values + 1 + + + + + res\values-v21 + 1 + + + res\values-v21 + 1 + + + + + res\values + 1 + + + res\values + 1 + + + + + res\drawable + 1 + + + res\drawable + 1 + + + + + res\drawable-xxhdpi + 1 + + + res\drawable-xxhdpi + 1 + + + + + res\drawable-ldpi + 1 + + + res\drawable-ldpi + 1 + + + + + res\drawable-mdpi + 1 + + + res\drawable-mdpi + 1 + + + + + res\drawable-hdpi + 1 + + + res\drawable-hdpi + 1 + + + + + res\drawable-xhdpi + 1 + + + res\drawable-xhdpi + 1 + + + + + res\drawable-mdpi + 1 + + + res\drawable-mdpi + 1 + + + + + res\drawable-hdpi + 1 + + + res\drawable-hdpi + 1 + + + + + res\drawable-xhdpi + 1 + + + res\drawable-xhdpi + 1 + + + + + res\drawable-xxhdpi + 1 + + + res\drawable-xxhdpi + 1 + + + + + res\drawable-xxxhdpi + 1 + + + res\drawable-xxxhdpi + 1 + + + + + res\drawable-small + 1 + + + res\drawable-small + 1 + + + + + res\drawable-normal + 1 + + + res\drawable-normal + 1 + + + + + res\drawable-large + 1 + + + res\drawable-large + 1 + + + + + res\drawable-xlarge + 1 + + + res\drawable-xlarge + 1 + + + + + res\values + 1 + + + res\values + 1 + + + + + 1 + + + 1 + + + 0 + + + + + 1 + .framework + + + 1 + .framework + + + 0 + + + + + 1 + .dylib + + + 1 + .dylib + + + 0 + .dll;.bpl + + + + + 1 + .dylib + + + 1 + .dylib + + + 1 + .dylib + + + 1 + .dylib + + + 1 + .dylib + + + 0 + .bpl + + + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + ..\$(PROJECTNAME).launchscreen\Assets\LaunchScreenImage.imageset + 1 + + + ..\$(PROJECTNAME).launchscreen\Assets\LaunchScreenImage.imageset + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + ..\$(PROJECTNAME).launchscreen\Assets\LaunchScreenImage.imageset + 1 + + + ..\$(PROJECTNAME).launchscreen\Assets\LaunchScreenImage.imageset + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + ..\$(PROJECTNAME).launchscreen\Assets\LaunchScreenImage.imageset + 1 + + + ..\$(PROJECTNAME).launchscreen\Assets\LaunchScreenImage.imageset + 1 + + + + + 1 + + + 1 + + + 1 + + + + + ..\$(PROJECTNAME).launchscreen\Assets\LaunchScreenImage.imageset + 1 + + + ..\$(PROJECTNAME).launchscreen\Assets\LaunchScreenImage.imageset + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + ..\$(PROJECTNAME).launchscreen\Assets\LaunchScreenImage.imageset + 1 + + + ..\$(PROJECTNAME).launchscreen\Assets\LaunchScreenImage.imageset + 1 + + + + + ..\$(PROJECTNAME).launchscreen\Assets\LaunchScreenImage.imageset + 1 + + + ..\$(PROJECTNAME).launchscreen\Assets\LaunchScreenImage.imageset + 1 + + + + + 1 + + + 1 + + + + + ..\$(PROJECTNAME).app.dSYM\Contents\Resources\DWARF + 1 + + + ..\$(PROJECTNAME).app.dSYM\Contents\Resources\DWARF + 1 + + + + + + + + + 1 + + + 1 + + + 1 + + + + + + + + Contents\Resources + 1 + + + Contents\Resources + 1 + + + + + library\lib\armeabi-v7a + 1 + + + library\lib\arm64-v8a + 1 + + + 1 + + + 1 + + + 1 + + + 1 + + + 1 + + + 1 + + + 0 + + + + + library\lib\armeabi-v7a + 1 + + + + + 1 + + + 1 + + + + + Assets + 1 + + + Assets + 1 + + + + + Assets + 1 + + + Assets + 1 + + + + + + + + + + + + + + + True + True + True + + + 12 + + + + + diff --git a/Packages/104Sydney/MARSClient.Core.dpk b/Packages/104Sydney/MARSClient.Core.dpk new file mode 100644 index 00000000..13c439e1 --- /dev/null +++ b/Packages/104Sydney/MARSClient.Core.dpk @@ -0,0 +1,57 @@ +package MARSClient.Core; + +{$R *.res} +{$R *.dres} +{$IFDEF IMPLICITBUILDING This IFDEF should not be used by users} +{$ALIGN 8} +{$ASSERTIONS ON} +{$BOOLEVAL OFF} +{$DEBUGINFO OFF} +{$EXTENDEDSYNTAX ON} +{$IMPORTEDDATA ON} +{$IOCHECKS ON} +{$LOCALSYMBOLS OFF} +{$LONGSTRINGS ON} +{$OPENSTRINGS ON} +{$OPTIMIZATION ON} +{$OVERFLOWCHECKS OFF} +{$RANGECHECKS OFF} +{$REFERENCEINFO OFF} +{$SAFEDIVIDE OFF} +{$STACKFRAMES OFF} +{$TYPEDADDRESS OFF} +{$VARSTRINGCHECKS ON} +{$WRITEABLECONST OFF} +{$MINENUMSIZE 1} +{$IMAGEBASE $400000} +{$DEFINE RELEASE} +{$ENDIF IMPLICITBUILDING} +{$DESCRIPTION 'MARS-Curiosity REST Library (Client)'} +{$LIBSUFFIX '270'} +{$RUNONLY} +{$IMPLICITBUILD OFF} + +requires + rtl, + dbrtl, + inet, + IndySystem, + IndyProtocols, + IndyCore, + MARS.Utils; + +contains + MARS.Client.Application in '..\..\Source\MARS.Client.Application.pas', + MARS.Client.Client.Net in '..\..\Source\MARS.Client.Client.Net.pas', + MARS.Client.CustomResource in '..\..\Source\MARS.Client.CustomResource.pas', + MARS.Client.Resource.JSON in '..\..\Source\MARS.Client.Resource.JSON.pas', + MARS.Client.Resource in '..\..\Source\MARS.Client.Resource.pas', + MARS.Client.Resource.Stream in '..\..\Source\MARS.Client.Resource.Stream.pas', + MARS.Client.Token in '..\..\Source\MARS.Client.Token.pas', + MARS.Client.Utils in '..\..\Source\MARS.Client.Utils.pas', + MARS.Client.Client in '..\..\Source\MARS.Client.Client.pas', + MARS.Client.Client.Indy in '..\..\Source\MARS.Client.Client.Indy.pas', + MARS.Client.Resource.FormData in '..\..\Source\MARS.Client.Resource.FormData.pas', + MARS.Client.Resource.FormUrlEncoded in '..\..\Source\MARS.Client.Resource.FormUrlEncoded.pas'; + +end. diff --git a/Packages/104Sydney/MARSClient.Core.dproj b/Packages/104Sydney/MARSClient.Core.dproj new file mode 100644 index 00000000..59576bdb --- /dev/null +++ b/Packages/104Sydney/MARSClient.Core.dproj @@ -0,0 +1,1118 @@ + + + {48E75129-EE24-48D4-AE54-2F549C909CC8} + MARSClient.Core.dpk + 19.0 + FMX + True + Release + Win32 + 3 + Package + + + true + + + true + Base + true + + + true + Base + true + + + true + Base + true + + + true + Cfg_1 + true + true + + + true + Base + true + + + true + Cfg_2 + true + true + + + 270 + ../../Source/Core;../../Source/Data/FireDAC;$(DCC_UnitSearchPath) + MARS-Curiosity REST Library (Client) + -LUDesignIDE + System;Xml;Data;Datasnap;Web;Soap;Winapi;Vcl;$(DCC_Namespace) + true + MARSClient_Core + true + ..\..\Lib270\$(Platform)\$(Config) + .\$(Platform)\$(Config) + false + false + false + false + false + 2057 + CompanyName=;FileVersion=1.0.0.0;InternalName=;LegalCopyright=;LegalTrademarks=;OriginalFilename=;ProductVersion=1.0.0.0;Comments=;ProgramID=com.embarcadero.$(MSBuildProjectName);FileDescription=$(MSBuildProjectName);ProductName=$(MSBuildProjectName) + true + true + + + rtl;inet;DbxCommonDriver;dbrtl;FireDACCommon;FireDACCommonDriver;FireDAC;DataSnapFireDAC;IndySystem;IndyProtocols;IndyCore;bindengine;bindcomp;MARS.Utils;$(DCC_UsePackage) + 1033 + true + System.Win;Data.Win;Datasnap.Win;Web.Win;Soap.Win;Xml.Win;Bde;$(DCC_Namespace) + + + rtl;inet;DbxCommonDriver;dbrtl;FireDACCommon;FireDACCommonDriver;FireDAC;DataSnapFireDAC;IndySystem;IndyProtocols;IndyCore;bindengine;bindcomp;MARS.Utils;$(DCC_UsePackage) + 1033 + true + System.Win;Data.Win;Datasnap.Win;Web.Win;Soap.Win;Xml.Win;$(DCC_Namespace) + + + DEBUG;$(DCC_Define) + true + false + true + true + true + + + 260 + MARS-Curiosity Client Core + 1033 + true + false + CompanyName=;FileVersion=1.0.0.0;InternalName=;LegalCopyright=;LegalTrademarks=;OriginalFilename=;ProductVersion=1.0.0.0;Comments=;ProgramID=com.embarcadero.$(ModuleName);FileDescription=$(ModuleName);ProductName=$(ModuleName) + + + false + RELEASE;$(DCC_Define) + 0 + 0 + + + true + 1033 + + + + MainSource + + + + + + + + + + + + + + + + + + + + + + BITMAP + TMARSCLIENTMESSAGINGRESOURCE + + + BITMAP + TMARSCLIENTRESOURCE + + + BITMAP + TMARSCLIENTRESOURCEJSON + + + BITMAP + TMARSCLIENTRESOURCESTREAM + + + BITMAP + TMARSCLIENTSUBRESOURCE + + + BITMAP + TMARSCLIENTSUBRESOURCEJSON + + + BITMAP + TMARSCLIENTSUBRESOURCESTREAM + + + BITMAP + TMARSCLIENTTOKEN + + + BITMAP + TMARSCLIENT + + + BITMAP + TMARSCLIENTAPPLICATION + + + BITMAP + TMARSNETCLIENT + + + BITMAP + TMARSCLIENTRESOURCEFORMDATA + + + Cfg_2 + Base + + + Base + + + Cfg_1 + Base + + + + Delphi.Personality.12 + Package + + + + MARSClient.Core.dpk + + + Microsoft Office 2000 Sample Automation Server Wrapper Components + Microsoft Office XP Sample Automation Server Wrapper Components + + + + + + true + + + + + MARSClient_Core.bpl + true + + + + + true + + + + + true + + + + + true + + + + + true + + + + + true + + + + + MARSClient_Core.bpl + true + + + + + true + + + + + 1 + + + 0 + + + + + classes + 1 + + + classes + 1 + + + + + res\xml + 1 + + + res\xml + 1 + + + + + library\lib\armeabi-v7a + 1 + + + + + library\lib\armeabi + 1 + + + library\lib\armeabi + 1 + + + + + library\lib\armeabi-v7a + 1 + + + + + library\lib\mips + 1 + + + library\lib\mips + 1 + + + + + + library\lib\armeabi-v7a + 1 + + + library\lib\arm64-v8a + 1 + + + + + library\lib\armeabi-v7a + 1 + + + + + res\drawable + 1 + + + res\drawable + 1 + + + + + res\values + 1 + + + res\values + 1 + + + + + res\values-v21 + 1 + + + res\values-v21 + 1 + + + + + res\values + 1 + + + res\values + 1 + + + + + res\drawable + 1 + + + res\drawable + 1 + + + + + res\drawable-xxhdpi + 1 + + + res\drawable-xxhdpi + 1 + + + + + res\drawable-ldpi + 1 + + + res\drawable-ldpi + 1 + + + + + res\drawable-mdpi + 1 + + + res\drawable-mdpi + 1 + + + + + res\drawable-hdpi + 1 + + + res\drawable-hdpi + 1 + + + + + res\drawable-xhdpi + 1 + + + res\drawable-xhdpi + 1 + + + + + res\drawable-mdpi + 1 + + + res\drawable-mdpi + 1 + + + + + res\drawable-hdpi + 1 + + + res\drawable-hdpi + 1 + + + + + res\drawable-xhdpi + 1 + + + res\drawable-xhdpi + 1 + + + + + res\drawable-xxhdpi + 1 + + + res\drawable-xxhdpi + 1 + + + + + res\drawable-xxxhdpi + 1 + + + res\drawable-xxxhdpi + 1 + + + + + res\drawable-small + 1 + + + res\drawable-small + 1 + + + + + res\drawable-normal + 1 + + + res\drawable-normal + 1 + + + + + res\drawable-large + 1 + + + res\drawable-large + 1 + + + + + res\drawable-xlarge + 1 + + + res\drawable-xlarge + 1 + + + + + res\values + 1 + + + res\values + 1 + + + + + 1 + + + 1 + + + 0 + + + + + 1 + .framework + + + 1 + .framework + + + 0 + + + + + 1 + .dylib + + + 1 + .dylib + + + 0 + .dll;.bpl + + + + + 1 + .dylib + + + 1 + .dylib + + + 1 + .dylib + + + 1 + .dylib + + + 1 + .dylib + + + 0 + .bpl + + + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + ..\$(PROJECTNAME).launchscreen\Assets\LaunchScreenImage.imageset + 1 + + + ..\$(PROJECTNAME).launchscreen\Assets\LaunchScreenImage.imageset + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + ..\$(PROJECTNAME).launchscreen\Assets\LaunchScreenImage.imageset + 1 + + + ..\$(PROJECTNAME).launchscreen\Assets\LaunchScreenImage.imageset + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + ..\$(PROJECTNAME).launchscreen\Assets\LaunchScreenImage.imageset + 1 + + + ..\$(PROJECTNAME).launchscreen\Assets\LaunchScreenImage.imageset + 1 + + + + + 1 + + + 1 + + + 1 + + + + + ..\$(PROJECTNAME).launchscreen\Assets\LaunchScreenImage.imageset + 1 + + + ..\$(PROJECTNAME).launchscreen\Assets\LaunchScreenImage.imageset + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + ..\$(PROJECTNAME).launchscreen\Assets\LaunchScreenImage.imageset + 1 + + + ..\$(PROJECTNAME).launchscreen\Assets\LaunchScreenImage.imageset + 1 + + + + + ..\$(PROJECTNAME).launchscreen\Assets\LaunchScreenImage.imageset + 1 + + + ..\$(PROJECTNAME).launchscreen\Assets\LaunchScreenImage.imageset + 1 + + + + + 1 + + + 1 + + + + + ..\$(PROJECTNAME).app.dSYM\Contents\Resources\DWARF + 1 + + + ..\$(PROJECTNAME).app.dSYM\Contents\Resources\DWARF + 1 + + + + + + + + + 1 + + + 1 + + + 1 + + + + + + + + Contents\Resources + 1 + + + Contents\Resources + 1 + + + + + library\lib\armeabi-v7a + 1 + + + library\lib\arm64-v8a + 1 + + + 1 + + + 1 + + + 1 + + + 1 + + + 1 + + + 1 + + + 0 + + + + + library\lib\armeabi-v7a + 1 + + + + + 1 + + + 1 + + + + + Assets + 1 + + + Assets + 1 + + + + + Assets + 1 + + + Assets + 1 + + + + + + + + + + + + + + + True + True + + + 12 + + + + + diff --git a/Packages/104Sydney/MARSClient.CoreDesign.dpk b/Packages/104Sydney/MARSClient.CoreDesign.dpk new file mode 100644 index 00000000..80d1e6a9 --- /dev/null +++ b/Packages/104Sydney/MARSClient.CoreDesign.dpk @@ -0,0 +1,46 @@ +package MARSClient.CoreDesign; + +{$R *.res} +{$IFDEF IMPLICITBUILDING This IFDEF should not be used by users} +{$ALIGN 8} +{$ASSERTIONS ON} +{$BOOLEVAL OFF} +{$DEBUGINFO OFF} +{$EXTENDEDSYNTAX ON} +{$IMPORTEDDATA ON} +{$IOCHECKS ON} +{$LOCALSYMBOLS OFF} +{$LONGSTRINGS ON} +{$OPENSTRINGS ON} +{$OPTIMIZATION ON} +{$OVERFLOWCHECKS OFF} +{$RANGECHECKS OFF} +{$REFERENCEINFO OFF} +{$SAFEDIVIDE OFF} +{$STACKFRAMES OFF} +{$TYPEDADDRESS OFF} +{$VARSTRINGCHECKS ON} +{$WRITEABLECONST OFF} +{$MINENUMSIZE 1} +{$IMAGEBASE $400000} +{$DEFINE RELEASE} +{$ENDIF IMPLICITBUILDING} +{$DESCRIPTION 'MARS-Curiosity REST Library (Client)'} +{$LIBSUFFIX '270'} +{$DESIGNONLY} +{$IMPLICITBUILD OFF} + +requires + bindengine, + bindcomp, + MARSClient.Core; + +contains + MARS.Client.Utils.LiveBindings in '..\..\Source\MARS.Client.Utils.LiveBindings.pas', + MARS.Client.Register in '..\..\Source\MARS.Client.Register.pas', + MARS.Client.CustomResource.Editor in '..\..\Source\MARS.Client.CustomResource.Editor.pas'; + +end. + + + diff --git a/Packages/104Sydney/MARSClient.CoreDesign.dproj b/Packages/104Sydney/MARSClient.CoreDesign.dproj new file mode 100644 index 00000000..548795e2 --- /dev/null +++ b/Packages/104Sydney/MARSClient.CoreDesign.dproj @@ -0,0 +1,1058 @@ + + + {8EEA5DAB-B80F-44EA-B149-45D051C20610} + MARSClient.CoreDesign.dpk + 19.0 + FMX + True + Release + Win32 + 3 + Package + + + true + + + true + Base + true + + + true + Base + true + + + true + Base + true + + + true + Cfg_1 + true + true + + + true + Base + true + + + true + Cfg_2 + true + true + + + 270 + ../../Source/Core;../../Source/Data/FireDAC;$(DCC_UnitSearchPath) + MARS-Curiosity REST Library (Client) + -LUDesignIDE + System;Xml;Data;Datasnap;Web;Soap;Winapi;Vcl;$(DCC_Namespace) + true + MARSClient_CoreDesign + true + ..\..\Lib270\$(Platform)\$(Config) + .\$(Platform)\$(Config) + false + false + false + false + false + 2057 + CompanyName=;FileVersion=1.0.0.0;InternalName=;LegalCopyright=;LegalTrademarks=;OriginalFilename=;ProductVersion=1.0.0.0;Comments=;ProgramID=com.embarcadero.$(MSBuildProjectName);FileDescription=$(MSBuildProjectName);ProductName=$(MSBuildProjectName) + true + true + + + rtl;inet;DbxCommonDriver;dbrtl;FireDACCommon;FireDACCommonDriver;FireDAC;DataSnapFireDAC;IndySystem;IndyProtocols;IndyCore;bindengine;bindcomp;MARS.Utils;MARSClient.Core;$(DCC_UsePackage) + 1033 + true + CompanyName=;FileVersion=1.0.0.0;InternalName=;LegalCopyright=;LegalTrademarks=;OriginalFilename=;ProductVersion=1.0.0.0;Comments=;ProgramID=com.embarcadero.$(ModuleName);FileDescription=$(ModuleName);ProductName=$(ModuleName) + System.Win;Data.Win;Datasnap.Win;Web.Win;Soap.Win;Xml.Win;Bde;$(DCC_Namespace) + + + rtl;inet;DbxCommonDriver;dbrtl;FireDACCommon;FireDACCommonDriver;FireDAC;DataSnapFireDAC;IndySystem;IndyProtocols;IndyCore;bindengine;bindcomp;MARS.Utils;$(DCC_UsePackage) + 1033 + true + CompanyName=;FileVersion=1.0.0.0;InternalName=;LegalCopyright=;LegalTrademarks=;OriginalFilename=;ProductVersion=1.0.0.0;Comments=;ProgramID=com.embarcadero.$(ModuleName);FileDescription=$(ModuleName);ProductName=$(ModuleName) + System.Win;Data.Win;Datasnap.Win;Web.Win;Soap.Win;Xml.Win;$(DCC_Namespace) + + + DEBUG;$(DCC_Define) + true + false + true + true + true + + + 260 + MARS-Curiosity Client Core + 1033 + true + false + + + false + RELEASE;$(DCC_Define) + 0 + 0 + + + true + 1033 + + + + MainSource + + + + + + + + + Cfg_2 + Base + + + Base + + + Cfg_1 + Base + + + + Delphi.Personality.12 + Package + + + + MARSClient.CoreDesign.dpk + + + Microsoft Office 2000 Sample Automation Server Wrapper Components + Microsoft Office XP Sample Automation Server Wrapper Components + + + + + + true + + + + + MARSClient_CoreDesign.bpl + true + + + + + MARSClient_CoreDesign.bpl + true + + + + + true + + + + + true + + + + + true + + + + + true + + + + + true + + + + + true + + + + + 1 + + + 0 + + + + + classes + 1 + + + classes + 1 + + + + + res\xml + 1 + + + res\xml + 1 + + + + + library\lib\armeabi-v7a + 1 + + + + + library\lib\armeabi + 1 + + + library\lib\armeabi + 1 + + + + + library\lib\armeabi-v7a + 1 + + + + + library\lib\mips + 1 + + + library\lib\mips + 1 + + + + + + library\lib\armeabi-v7a + 1 + + + library\lib\arm64-v8a + 1 + + + + + library\lib\armeabi-v7a + 1 + + + + + res\drawable + 1 + + + res\drawable + 1 + + + + + res\values + 1 + + + res\values + 1 + + + + + res\values-v21 + 1 + + + res\values-v21 + 1 + + + + + res\values + 1 + + + res\values + 1 + + + + + res\drawable + 1 + + + res\drawable + 1 + + + + + res\drawable-xxhdpi + 1 + + + res\drawable-xxhdpi + 1 + + + + + res\drawable-ldpi + 1 + + + res\drawable-ldpi + 1 + + + + + res\drawable-mdpi + 1 + + + res\drawable-mdpi + 1 + + + + + res\drawable-hdpi + 1 + + + res\drawable-hdpi + 1 + + + + + res\drawable-xhdpi + 1 + + + res\drawable-xhdpi + 1 + + + + + res\drawable-mdpi + 1 + + + res\drawable-mdpi + 1 + + + + + res\drawable-hdpi + 1 + + + res\drawable-hdpi + 1 + + + + + res\drawable-xhdpi + 1 + + + res\drawable-xhdpi + 1 + + + + + res\drawable-xxhdpi + 1 + + + res\drawable-xxhdpi + 1 + + + + + res\drawable-xxxhdpi + 1 + + + res\drawable-xxxhdpi + 1 + + + + + res\drawable-small + 1 + + + res\drawable-small + 1 + + + + + res\drawable-normal + 1 + + + res\drawable-normal + 1 + + + + + res\drawable-large + 1 + + + res\drawable-large + 1 + + + + + res\drawable-xlarge + 1 + + + res\drawable-xlarge + 1 + + + + + res\values + 1 + + + res\values + 1 + + + + + 1 + + + 1 + + + 0 + + + + + 1 + .framework + + + 1 + .framework + + + 0 + + + + + 1 + .dylib + + + 1 + .dylib + + + 0 + .dll;.bpl + + + + + 1 + .dylib + + + 1 + .dylib + + + 1 + .dylib + + + 1 + .dylib + + + 1 + .dylib + + + 0 + .bpl + + + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + ..\$(PROJECTNAME).launchscreen\Assets\LaunchScreenImage.imageset + 1 + + + ..\$(PROJECTNAME).launchscreen\Assets\LaunchScreenImage.imageset + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + ..\$(PROJECTNAME).launchscreen\Assets\LaunchScreenImage.imageset + 1 + + + ..\$(PROJECTNAME).launchscreen\Assets\LaunchScreenImage.imageset + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + ..\$(PROJECTNAME).launchscreen\Assets\LaunchScreenImage.imageset + 1 + + + ..\$(PROJECTNAME).launchscreen\Assets\LaunchScreenImage.imageset + 1 + + + + + 1 + + + 1 + + + 1 + + + + + ..\$(PROJECTNAME).launchscreen\Assets\LaunchScreenImage.imageset + 1 + + + ..\$(PROJECTNAME).launchscreen\Assets\LaunchScreenImage.imageset + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + ..\$(PROJECTNAME).launchscreen\Assets\LaunchScreenImage.imageset + 1 + + + ..\$(PROJECTNAME).launchscreen\Assets\LaunchScreenImage.imageset + 1 + + + + + ..\$(PROJECTNAME).launchscreen\Assets\LaunchScreenImage.imageset + 1 + + + ..\$(PROJECTNAME).launchscreen\Assets\LaunchScreenImage.imageset + 1 + + + + + 1 + + + 1 + + + + + ..\$(PROJECTNAME).app.dSYM\Contents\Resources\DWARF + 1 + + + ..\$(PROJECTNAME).app.dSYM\Contents\Resources\DWARF + 1 + + + + + + + + + 1 + + + 1 + + + 1 + + + + + + + + Contents\Resources + 1 + + + Contents\Resources + 1 + + + + + library\lib\armeabi-v7a + 1 + + + library\lib\arm64-v8a + 1 + + + 1 + + + 1 + + + 1 + + + 1 + + + 1 + + + 1 + + + 0 + + + + + library\lib\armeabi-v7a + 1 + + + + + 1 + + + 1 + + + + + Assets + 1 + + + Assets + 1 + + + + + Assets + 1 + + + Assets + 1 + + + + + + + + + + + + + + + True + True + + + 12 + + + + + diff --git a/Packages/104Sydney/MARSClient.FireDAC.dpk b/Packages/104Sydney/MARSClient.FireDAC.dpk new file mode 100644 index 00000000..e4734309 --- /dev/null +++ b/Packages/104Sydney/MARSClient.FireDAC.dpk @@ -0,0 +1,49 @@ +package MARSClient.FireDAC; + +{$R *.res} +{$IFDEF IMPLICITBUILDING This IFDEF should not be used by users} +{$ALIGN 8} +{$ASSERTIONS ON} +{$BOOLEVAL OFF} +{$DEBUGINFO OFF} +{$EXTENDEDSYNTAX ON} +{$IMPORTEDDATA ON} +{$IOCHECKS ON} +{$LOCALSYMBOLS OFF} +{$LONGSTRINGS ON} +{$OPENSTRINGS ON} +{$OPTIMIZATION ON} +{$OVERFLOWCHECKS OFF} +{$RANGECHECKS OFF} +{$REFERENCEINFO OFF} +{$SAFEDIVIDE OFF} +{$STACKFRAMES OFF} +{$TYPEDADDRESS OFF} +{$VARSTRINGCHECKS ON} +{$WRITEABLECONST OFF} +{$MINENUMSIZE 1} +{$IMAGEBASE $400000} +{$DEFINE RELEASE} +{$ENDIF IMPLICITBUILDING} +{$DESCRIPTION 'MARS-Curiosity REST Library (Client)'} +{$LIBSUFFIX '270'} +{$RUNONLY} +{$IMPLICITBUILD OFF} + +requires + rtl, + MARSClient.Core, + FireDAC, + FireDACCommonDriver, + FireDACCommon; + +contains + MARS.Client.FireDAC in '..\..\Source\MARS.Client.FireDAC.pas', + MARS.Data.FireDAC.Utils in '..\..\Source\MARS.Data.FireDAC.Utils.pas'; + +end. + + + + + diff --git a/Packages/104Sydney/MARSClient.FireDAC.dproj b/Packages/104Sydney/MARSClient.FireDAC.dproj new file mode 100644 index 00000000..06ea1514 --- /dev/null +++ b/Packages/104Sydney/MARSClient.FireDAC.dproj @@ -0,0 +1,1044 @@ + + + {29A0C2A3-BA6B-44DF-B909-A20D8DBF1B9B} + MARSClient.FireDAC.dpk + 19.0 + FMX + True + Release + Win32 + 1 + Package + + + true + + + true + Base + true + + + true + Base + true + + + true + Cfg_1 + true + true + + + true + Base + true + + + true + Cfg_2 + true + true + + + 270 + ../../Source/Core;../../Source/Data/FireDAC;$(DCC_UnitSearchPath) + MARS-Curiosity REST Library (Client) + -LUDesignIDE + System;Xml;Data;Datasnap;Web;Soap;Winapi;Vcl;$(DCC_Namespace) + true + MARSClient_FireDAC + true + ..\..\Lib270\$(Platform)\$(Config) + .\$(Platform)\$(Config) + false + false + false + false + false + 2057 + CompanyName=;FileVersion=1.0.0.0;InternalName=;LegalCopyright=;LegalTrademarks=;OriginalFilename=;ProductVersion=1.0.0.0;Comments=;ProgramID=com.embarcadero.$(MSBuildProjectName);FileDescription=$(MSBuildProjectName);ProductName=$(MSBuildProjectName) + true + true + + + rtl;inet;DbxCommonDriver;dbrtl;FireDACCommon;FireDACCommonDriver;FireDAC;DataSnapFireDAC;IndySystem;IndyProtocols;IndyCore;bindengine;bindcomp;$(DCC_UsePackage) + 1033 + true + System.Win;Data.Win;Datasnap.Win;Web.Win;Soap.Win;Xml.Win;Bde;$(DCC_Namespace) + + + DEBUG;$(DCC_Define) + true + false + true + true + true + + + MARS-Curiosity Client FireDAC + 1033 + true + false + + + false + RELEASE;$(DCC_Define) + 0 + 0 + + + true + 1033 + + + + MainSource + + + + + + + + + + Cfg_2 + Base + + + Base + + + Cfg_1 + Base + + + + Delphi.Personality.12 + Package + + + + MARSClient.FireDAC.dpk + + + Microsoft Office 2000 Sample Automation Server Wrapper Components + Microsoft Office XP Sample Automation Server Wrapper Components + + + + + + true + + + + + true + + + + + true + + + + + true + + + + + true + + + + + true + + + + + true + + + + + MARSClient_FireDAC.bpl + true + + + + + MARSClient_FireDAC.bpl + true + + + + + 1 + + + 0 + + + + + classes + 1 + + + classes + 1 + + + + + res\xml + 1 + + + res\xml + 1 + + + + + library\lib\armeabi-v7a + 1 + + + + + library\lib\armeabi + 1 + + + library\lib\armeabi + 1 + + + + + library\lib\armeabi-v7a + 1 + + + + + library\lib\mips + 1 + + + library\lib\mips + 1 + + + + + + library\lib\armeabi-v7a + 1 + + + library\lib\arm64-v8a + 1 + + + + + library\lib\armeabi-v7a + 1 + + + + + res\drawable + 1 + + + res\drawable + 1 + + + + + res\values + 1 + + + res\values + 1 + + + + + res\values-v21 + 1 + + + res\values-v21 + 1 + + + + + res\values + 1 + + + res\values + 1 + + + + + res\drawable + 1 + + + res\drawable + 1 + + + + + res\drawable-xxhdpi + 1 + + + res\drawable-xxhdpi + 1 + + + + + res\drawable-ldpi + 1 + + + res\drawable-ldpi + 1 + + + + + res\drawable-mdpi + 1 + + + res\drawable-mdpi + 1 + + + + + res\drawable-hdpi + 1 + + + res\drawable-hdpi + 1 + + + + + res\drawable-xhdpi + 1 + + + res\drawable-xhdpi + 1 + + + + + res\drawable-mdpi + 1 + + + res\drawable-mdpi + 1 + + + + + res\drawable-hdpi + 1 + + + res\drawable-hdpi + 1 + + + + + res\drawable-xhdpi + 1 + + + res\drawable-xhdpi + 1 + + + + + res\drawable-xxhdpi + 1 + + + res\drawable-xxhdpi + 1 + + + + + res\drawable-xxxhdpi + 1 + + + res\drawable-xxxhdpi + 1 + + + + + res\drawable-small + 1 + + + res\drawable-small + 1 + + + + + res\drawable-normal + 1 + + + res\drawable-normal + 1 + + + + + res\drawable-large + 1 + + + res\drawable-large + 1 + + + + + res\drawable-xlarge + 1 + + + res\drawable-xlarge + 1 + + + + + res\values + 1 + + + res\values + 1 + + + + + 1 + + + 1 + + + 0 + + + + + 1 + .framework + + + 1 + .framework + + + 0 + + + + + 1 + .dylib + + + 1 + .dylib + + + 0 + .dll;.bpl + + + + + 1 + .dylib + + + 1 + .dylib + + + 1 + .dylib + + + 1 + .dylib + + + 1 + .dylib + + + 0 + .bpl + + + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + ..\$(PROJECTNAME).launchscreen\Assets\LaunchScreenImage.imageset + 1 + + + ..\$(PROJECTNAME).launchscreen\Assets\LaunchScreenImage.imageset + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + ..\$(PROJECTNAME).launchscreen\Assets\LaunchScreenImage.imageset + 1 + + + ..\$(PROJECTNAME).launchscreen\Assets\LaunchScreenImage.imageset + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + ..\$(PROJECTNAME).launchscreen\Assets\LaunchScreenImage.imageset + 1 + + + ..\$(PROJECTNAME).launchscreen\Assets\LaunchScreenImage.imageset + 1 + + + + + 1 + + + 1 + + + 1 + + + + + ..\$(PROJECTNAME).launchscreen\Assets\LaunchScreenImage.imageset + 1 + + + ..\$(PROJECTNAME).launchscreen\Assets\LaunchScreenImage.imageset + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + ..\$(PROJECTNAME).launchscreen\Assets\LaunchScreenImage.imageset + 1 + + + ..\$(PROJECTNAME).launchscreen\Assets\LaunchScreenImage.imageset + 1 + + + + + ..\$(PROJECTNAME).launchscreen\Assets\LaunchScreenImage.imageset + 1 + + + ..\$(PROJECTNAME).launchscreen\Assets\LaunchScreenImage.imageset + 1 + + + + + 1 + + + 1 + + + + + ..\$(PROJECTNAME).app.dSYM\Contents\Resources\DWARF + 1 + + + ..\$(PROJECTNAME).app.dSYM\Contents\Resources\DWARF + 1 + + + + + + + + + 1 + + + 1 + + + 1 + + + + + + + + Contents\Resources + 1 + + + Contents\Resources + 1 + + + + + library\lib\armeabi-v7a + 1 + + + library\lib\arm64-v8a + 1 + + + 1 + + + 1 + + + 1 + + + 1 + + + 1 + + + 1 + + + 0 + + + + + library\lib\armeabi-v7a + 1 + + + + + 1 + + + 1 + + + + + Assets + 1 + + + Assets + 1 + + + + + Assets + 1 + + + Assets + 1 + + + + + + + + + + + + + + + True + + + 12 + + + + + diff --git a/Packages/104Sydney/MARSClient.FireDACDesign.dpk b/Packages/104Sydney/MARSClient.FireDACDesign.dpk new file mode 100644 index 00000000..7b0a2a7e --- /dev/null +++ b/Packages/104Sydney/MARSClient.FireDACDesign.dpk @@ -0,0 +1,46 @@ +package MARSClient.FireDACDesign; + +{$R *.res} +{$IFDEF IMPLICITBUILDING This IFDEF should not be used by users} +{$ALIGN 8} +{$ASSERTIONS ON} +{$BOOLEVAL OFF} +{$DEBUGINFO OFF} +{$EXTENDEDSYNTAX ON} +{$IMPORTEDDATA ON} +{$IOCHECKS ON} +{$LOCALSYMBOLS OFF} +{$LONGSTRINGS ON} +{$OPENSTRINGS ON} +{$OPTIMIZATION ON} +{$OVERFLOWCHECKS OFF} +{$RANGECHECKS OFF} +{$REFERENCEINFO OFF} +{$SAFEDIVIDE OFF} +{$STACKFRAMES OFF} +{$TYPEDADDRESS OFF} +{$VARSTRINGCHECKS ON} +{$WRITEABLECONST OFF} +{$MINENUMSIZE 1} +{$IMAGEBASE $400000} +{$DEFINE RELEASE} +{$ENDIF IMPLICITBUILDING} +{$DESCRIPTION 'MARS-Curiosity REST Library (Client) - FireDAC'} +{$LIBSUFFIX '270'} +{$DESIGNONLY} +{$IMPLICITBUILD OFF} + +requires + MARSClient.FireDAC, + MARSClient.CoreDesign; + +contains + MARS.Data.FireDAC.Editor in '..\..\Source\MARS.Data.FireDAC.Editor.pas', + MARS.Client.FireDAC.Register in '..\..\Source\MARS.Client.FireDAC.Register.pas'; + +end. + + + + + diff --git a/Packages/104Sydney/MARSClient.FireDACDesign.dproj b/Packages/104Sydney/MARSClient.FireDACDesign.dproj new file mode 100644 index 00000000..937f937b --- /dev/null +++ b/Packages/104Sydney/MARSClient.FireDACDesign.dproj @@ -0,0 +1,1043 @@ + + + {6F52BB40-74E2-410E-A54D-0C20E713A0CE} + MARSClient.FireDACDesign.dpk + 19.0 + FMX + True + Release + Win32 + 1 + Package + + + true + + + true + Base + true + + + true + Base + true + + + true + Cfg_1 + true + true + + + true + Base + true + + + true + Cfg_2 + true + true + + + 270 + true + ../../Source/Core;../../Source/Data/FireDAC;$(DCC_UnitSearchPath) + MARS-Curiosity REST Library (Client) - FireDAC + -LUDesignIDE + System;Xml;Data;Datasnap;Web;Soap;Winapi;Vcl;$(DCC_Namespace) + true + MARSClient_FireDACDesign + true + ..\..\Lib270\$(Platform)\$(Config) + .\$(Platform)\$(Config) + false + false + false + false + false + 2057 + CompanyName=;FileVersion=1.0.0.0;InternalName=;LegalCopyright=;LegalTrademarks=;OriginalFilename=;ProductVersion=1.0.0.0;Comments=;ProgramID=com.embarcadero.$(MSBuildProjectName);FileDescription=$(MSBuildProjectName);ProductName=$(MSBuildProjectName) + true + + + rtl;inet;DbxCommonDriver;dbrtl;FireDACCommon;FireDACCommonDriver;FireDAC;DataSnapFireDAC;IndySystem;IndyProtocols;IndyCore;bindengine;bindcomp;MARSClient.FireDAC;MARSClient.CoreDesign260;MARSClient.FireDAC260;$(DCC_UsePackage) + 1033 + true + CompanyName=;FileVersion=1.0.0.0;InternalName=;LegalCopyright=;LegalTrademarks=;OriginalFilename=;ProductVersion=1.0.0.0;Comments=;ProgramID=com.embarcadero.$(ModuleName);FileDescription=$(ModuleName);ProductName=$(ModuleName) + System.Win;Data.Win;Datasnap.Win;Web.Win;Soap.Win;Xml.Win;Bde;$(DCC_Namespace) + + + DEBUG;$(DCC_Define) + true + false + true + true + true + + + 260 + MARS-Curiosity Client FireDAC + 1033 + true + false + + + false + RELEASE;$(DCC_Define) + 0 + 0 + + + true + 1033 + + + + MainSource + + + + + + + Cfg_2 + Base + + + Base + + + Cfg_1 + Base + + + + Delphi.Personality.12 + Package + + + + MARSClient.FireDACDesign.dpk + + + Microsoft Office 2000 Sample Automation Server Wrapper Components + Microsoft Office XP Sample Automation Server Wrapper Components + + + + + + true + + + + + true + + + + + true + + + + + true + + + + + true + + + + + true + + + + + MARSClient_FireDACDesign.bpl + true + + + + + true + + + + + MARSClient_FireDACDesign.bpl + true + + + + + 1 + + + 0 + + + + + classes + 1 + + + classes + 1 + + + + + res\xml + 1 + + + res\xml + 1 + + + + + library\lib\armeabi-v7a + 1 + + + + + library\lib\armeabi + 1 + + + library\lib\armeabi + 1 + + + + + library\lib\armeabi-v7a + 1 + + + + + library\lib\mips + 1 + + + library\lib\mips + 1 + + + + + + library\lib\armeabi-v7a + 1 + + + library\lib\arm64-v8a + 1 + + + + + library\lib\armeabi-v7a + 1 + + + + + res\drawable + 1 + + + res\drawable + 1 + + + + + res\values + 1 + + + res\values + 1 + + + + + res\values-v21 + 1 + + + res\values-v21 + 1 + + + + + res\values + 1 + + + res\values + 1 + + + + + res\drawable + 1 + + + res\drawable + 1 + + + + + res\drawable-xxhdpi + 1 + + + res\drawable-xxhdpi + 1 + + + + + res\drawable-ldpi + 1 + + + res\drawable-ldpi + 1 + + + + + res\drawable-mdpi + 1 + + + res\drawable-mdpi + 1 + + + + + res\drawable-hdpi + 1 + + + res\drawable-hdpi + 1 + + + + + res\drawable-xhdpi + 1 + + + res\drawable-xhdpi + 1 + + + + + res\drawable-mdpi + 1 + + + res\drawable-mdpi + 1 + + + + + res\drawable-hdpi + 1 + + + res\drawable-hdpi + 1 + + + + + res\drawable-xhdpi + 1 + + + res\drawable-xhdpi + 1 + + + + + res\drawable-xxhdpi + 1 + + + res\drawable-xxhdpi + 1 + + + + + res\drawable-xxxhdpi + 1 + + + res\drawable-xxxhdpi + 1 + + + + + res\drawable-small + 1 + + + res\drawable-small + 1 + + + + + res\drawable-normal + 1 + + + res\drawable-normal + 1 + + + + + res\drawable-large + 1 + + + res\drawable-large + 1 + + + + + res\drawable-xlarge + 1 + + + res\drawable-xlarge + 1 + + + + + res\values + 1 + + + res\values + 1 + + + + + 1 + + + 1 + + + 0 + + + + + 1 + .framework + + + 1 + .framework + + + 0 + + + + + 1 + .dylib + + + 1 + .dylib + + + 0 + .dll;.bpl + + + + + 1 + .dylib + + + 1 + .dylib + + + 1 + .dylib + + + 1 + .dylib + + + 1 + .dylib + + + 0 + .bpl + + + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + ..\$(PROJECTNAME).launchscreen\Assets\LaunchScreenImage.imageset + 1 + + + ..\$(PROJECTNAME).launchscreen\Assets\LaunchScreenImage.imageset + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + ..\$(PROJECTNAME).launchscreen\Assets\LaunchScreenImage.imageset + 1 + + + ..\$(PROJECTNAME).launchscreen\Assets\LaunchScreenImage.imageset + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + ..\$(PROJECTNAME).launchscreen\Assets\LaunchScreenImage.imageset + 1 + + + ..\$(PROJECTNAME).launchscreen\Assets\LaunchScreenImage.imageset + 1 + + + + + 1 + + + 1 + + + 1 + + + + + ..\$(PROJECTNAME).launchscreen\Assets\LaunchScreenImage.imageset + 1 + + + ..\$(PROJECTNAME).launchscreen\Assets\LaunchScreenImage.imageset + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + ..\$(PROJECTNAME).launchscreen\Assets\LaunchScreenImage.imageset + 1 + + + ..\$(PROJECTNAME).launchscreen\Assets\LaunchScreenImage.imageset + 1 + + + + + ..\$(PROJECTNAME).launchscreen\Assets\LaunchScreenImage.imageset + 1 + + + ..\$(PROJECTNAME).launchscreen\Assets\LaunchScreenImage.imageset + 1 + + + + + 1 + + + 1 + + + + + ..\$(PROJECTNAME).app.dSYM\Contents\Resources\DWARF + 1 + + + ..\$(PROJECTNAME).app.dSYM\Contents\Resources\DWARF + 1 + + + + + + + + + 1 + + + 1 + + + 1 + + + + + + + + Contents\Resources + 1 + + + Contents\Resources + 1 + + + + + library\lib\armeabi-v7a + 1 + + + library\lib\arm64-v8a + 1 + + + 1 + + + 1 + + + 1 + + + 1 + + + 1 + + + 1 + + + 0 + + + + + library\lib\armeabi-v7a + 1 + + + + + 1 + + + 1 + + + + + Assets + 1 + + + Assets + 1 + + + + + Assets + 1 + + + Assets + 1 + + + + + + + + + + + + + + + True + + + 12 + + + + + diff --git a/Packages/104Sydney/MARSClient.groupproj b/Packages/104Sydney/MARSClient.groupproj new file mode 100644 index 00000000..41ffc820 --- /dev/null +++ b/Packages/104Sydney/MARSClient.groupproj @@ -0,0 +1,84 @@ + + + {8B31503E-A2F0-4933-8CB5-13BB39CA0F69} + + + + + + + + + + + + + + + + + + + + Default.Personality.12 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Source/MARS.Client.Client.Net.pas b/Source/MARS.Client.Client.Net.pas index d57eb084..07476414 100644 --- a/Source/MARS.Client.Client.Net.pas +++ b/Source/MARS.Client.Client.Net.pas @@ -11,11 +11,11 @@ interface uses SysUtils, Classes - , MARS.Core.JSON, MARS.Client.Utils, MARS.Core.Utils, MARS.Client.Client + , MARS.Core.JSON, MARS.Client.Utils, MARS.Core.Utils, MARS.Client.Client, MARS.Utils.Parameters // Net , System.Net.URLClient, System.Net.HttpClient, System.Net.HttpClientComponent - , System.Net.Mime +, System.Net.Mime ; type @@ -38,7 +38,6 @@ TMARSNetClient = class(TMARSCustomClient) function CreateMultipartFormData(AFormData: TArray): TMultipartFormData; - procedure EndorseAuthorization; override; procedure CheckLastCmdSuccess; virtual; procedure ApplyProxyConfig; override; @@ -58,6 +57,9 @@ TMARSNetClient = class(TMARSCustomClient) procedure Post(const AURL: string; const AFormData: TArray; const AResponse: TStream; const AAuthToken: string; const AAccept: string; const AContentType: string); override; + procedure Post(const AURL: string; const AFormUrlEncoded: TMARSParameters; + const AResponse: TStream; + const AAuthToken: string; const AAccept: string; const AContentType: string); override; procedure Put(const AURL: string; AContent, AResponse: TStream; const AAuthToken: string; const AAccept: string; const AContentType: string); override; @@ -254,10 +256,8 @@ function TMARSNetClient.GetReadTimeout: Integer; function TMARSNetClient.LastCmdSuccess: Boolean; begin - if assigned(FLastResponse) then - Result := (FLastResponse.StatusCode >= 200) and (FLastResponse.StatusCode < 300) - else - Result := FALSE; + Result := Assigned(FLastResponse) + and ((FLastResponse.StatusCode >= 200) and (FLastResponse.StatusCode < 300)); end; procedure TMARSNetClient.Post(const AURL: string; AContent, AResponse: TStream; @@ -284,18 +284,16 @@ procedure TMARSNetClient.Put(const AURL: string; AContent, AResponse: TStream; function TMARSNetClient.ResponseStatusCode: Integer; begin - if assigned(FLastResponse) then - Result := FLastResponse.StatusCode - else - Result := -1; + Result := -1; + if Assigned(FLastResponse) then + Result := FLastResponse.StatusCode; end; function TMARSNetClient.ResponseText: string; begin - if assigned(FLastResponse) then - Result := FLastResponse.StatusText - else - Result := ''; + Result := ''; + if Assigned(FLastResponse) then + Result := FLastResponse.StatusText; end; procedure TMARSNetClient.SetConnectTimeout(const Value: Integer); @@ -345,6 +343,24 @@ procedure TMARSNetClient.Post(const AURL: string; end; end; +procedure TMARSNetClient.Post(const AURL: string; const AFormUrlEncoded: TMARSParameters; const AResponse: TStream; + const AAuthToken, AAccept, AContentType: string); +var + LFormUrlEncoded: TStrings; +begin + inherited; + + FHttpClient.Accept := AAccept; + FHttpClient.ContentType := AContentType; + LFormUrlEncoded := AFormUrlEncoded.AsStrings; + try + FLastResponse := FHttpClient.Post(AURL, LFormUrlEncoded, AResponse); + CheckLastCmdSuccess; + finally + LFormUrlEncoded.Free; + end; +end; + procedure TMARSNetClient.Put(const AURL: string; const AFormData: System.TArray; const AResponse: TStream; const AAuthToken, AAccept: string; const AContentType: string); @@ -368,5 +384,4 @@ procedure TMARSNetClient.Put(const AURL: string; end; end; - -end. +end. \ No newline at end of file diff --git a/Source/MARS.Client.Client.pas b/Source/MARS.Client.Client.pas index 9a9b2266..648fe2a8 100755 --- a/Source/MARS.Client.Client.pas +++ b/Source/MARS.Client.Client.pas @@ -15,6 +15,7 @@ interface , MARS.Core.Utils , MARS.Core.MediaType , MARS.Client.Utils +, MARS.Utils.Parameters ; type @@ -91,11 +92,17 @@ TMARSCustomClient = class(TComponent) procedure Post(const AURL: string; const AFormData: TArray; const AResponse: TStream; const AAuthToken: string; const AAccept: string; const AContentType: string); overload; virtual; + procedure Post(const AURL: string; const AFormUrlEncoded: TMARSParameters; + const AResponse: TStream; + const AAuthToken: string; const AAccept: string; const AContentType: string); overload; virtual; procedure Put(const AURL: string; AContent, AResponse: TStream; const AAuthToken: string; const AAccept: string; const AContentType: string); overload; virtual; procedure Put(const AURL: string; const AFormData: TArray; const AResponse: TStream; const AAuthToken: string; const AAccept: string; const AContentType: string); overload; virtual; + procedure Put(const AURL: string; const AFormUrlEncoded: TMARSParameters; + const AResponse: TStream; + const AAuthToken: string; const AAccept: string; const AContentType: string); overload; virtual; function LastCmdSuccess: Boolean; virtual; function ResponseStatusCode: Integer; virtual; @@ -612,6 +619,13 @@ procedure TMARSCustomClient.Post(const AURL: string; FireBeforeExecute(AURL, Self); end; +procedure TMARSCustomClient.Post(const AURL: string; const AFormUrlEncoded: TMARSParameters; const AResponse: TStream; + const AAuthToken, AAccept, AContentType: string); +begin + FAuthToken := AAuthToken; + BeforeExecute; +end; + class function TMARSCustomClient.PostJSON(const AEngineURL, AAppName, AResourceName: string; const APathParams: TArray; const AQueryParams: TStrings; const AContent: TJSONValue; const ACompletionHandler: TProc; const AToken: string @@ -794,6 +808,12 @@ class function TMARSCustomClient.PostStream(const AEngineURL, AAppName, end; end; +procedure TMARSCustomClient.Put(const AURL: string; const AFormUrlEncoded: TMARSParameters; const AResponse: TStream; + const AAuthToken, AAccept, AContentType: string); +begin + FAuthToken := AAuthToken; + BeforeExecute; +end; procedure TMARSCustomClient.Put(const AURL: string; const AFormData: TArray; const AResponse: TStream; diff --git a/Source/MARS.Client.Register.pas b/Source/MARS.Client.Register.pas index d77c2cd3..64096a1b 100644 --- a/Source/MARS.Client.Register.pas +++ b/Source/MARS.Client.Register.pas @@ -11,7 +11,8 @@ implementation MARS.Client.Client.Indy, MARS.Client.Application, MARS.Client.Resource, MARS.Client.Resource.FormData, MARS.Client.Resource.JSON, MARS.Client.Resource.Stream, MARS.Client.Token, MARS.Client.Client.Net, - MARS.Client.CustomResource, MARS.Client.CustomResource.Editor; + MARS.Client.CustomResource, MARS.Client.CustomResource.Editor, + MARS.Client.Resource.FormUrlEncoded; procedure Register; begin @@ -19,6 +20,7 @@ procedure Register; TMARSClientApplication, TMARSClientResource, TMARSClientResourceFormData, + TMARSClientResourceFormUrlEncoded, TMARSClientResourceJSON, TMARSClientResourceStream, TMARSClientToken, diff --git a/Source/MARS.Client.Resource.FormUrlEncoded.pas b/Source/MARS.Client.Resource.FormUrlEncoded.pas new file mode 100644 index 00000000..b25c9681 --- /dev/null +++ b/Source/MARS.Client.Resource.FormUrlEncoded.pas @@ -0,0 +1,248 @@ +(* + Copyright 2016, MARS-Curiosity library + + Home: https://github.com/andrea-magni/MARS +*) + +unit MARS.Client.Resource.FormUrlEncoded; + +{$I MARS.inc} + +interface + +uses + SysUtils, Classes, Generics.Collections + + , MARS.Client.Resource, MARS.Client.CustomResource, MARS.Client.Client + , MARS.Client.Utils, MARS.Core.Utils, MARS.Utils.Parameters, MARS.Core.JSON +; + +type + [ComponentPlatformsAttribute(pidAllPlatforms)] + TMARSClientResourceFormUrlEncoded = class(TMARSClientResource) + private + FFormUrlEncoded: TMARSParameters; + FResponse: TMemoryStream; + protected + procedure AssignTo(Dest: TPersistent); override; + procedure AfterPOST(const AContent: TStream); override; + procedure AfterPUT(const AContent: TStream); override; + function GetResponseAsString: string; override; + public + constructor Create(AOwner: TComponent); override; + destructor Destroy; override; + + procedure POST( + const ABeforeExecute: TProc{$ifdef DelphiXE2_UP} = nil{$endif}; + const AAfterExecute: TMARSClientResponseProc{$ifdef DelphiXE2_UP} = nil{$endif}; + const AOnException: TMARSClientExecptionProc{$ifdef DelphiXE2_UP} = nil{$endif}); overload; override; + procedure POST( + const AFormUrlEncoded: TMARSParameters; + const ABeforeExecute: TProc{$ifdef DelphiXE2_UP} = nil{$endif}; + const AAfterExecute: TMARSClientResponseProc{$ifdef DelphiXE2_UP} = nil{$endif}; + const AOnException: TMARSClientExecptionProc{$ifdef DelphiXE2_UP} = nil{$endif}); overload; virtual; + + procedure POSTAsync( + const AFormUrlEncoded: TMARSParameters; + const ABeforeExecute: TProc{$ifdef DelphiXE2_UP} = nil{$endif}; + const ACompletionHandler: TProc{$ifdef DelphiXE2_UP} = nil{$endif}; + const AOnException: TMARSClientExecptionProc{$ifdef DelphiXE2_UP} = nil{$endif}; + const ASynchronize: Boolean = True); overload; virtual; + + procedure PUT( + const ABeforeExecute: TProc{$ifdef DelphiXE2_UP} = nil{$endif}; + const AAfterExecute: TMARSClientResponseProc{$ifdef DelphiXE2_UP} = nil{$endif}; + const AOnException: TMARSClientExecptionProc{$ifdef DelphiXE2_UP} = nil{$endif}); overload; override; + procedure PUT( + const AFormUrlEncoded: TMARSParameters; + const ABeforeExecute: TProc{$ifdef DelphiXE2_UP} = nil{$endif}; + const AAfterExecute: TMARSClientResponseProc{$ifdef DelphiXE2_UP} = nil{$endif}; + const AOnException: TMARSClientExecptionProc{$ifdef DelphiXE2_UP} = nil{$endif}); overload; virtual; + + function ResponseAsJSON: TJSONValue; + function ResponseAs: T; + function ResponseAsArray: TArray; + published + property FormUrlEncoded: TMARSParameters read FFormUrlEncoded write FFormUrlEncoded; + property Response: TMemoryStream read FResponse; + property ResponseAsString; + end; + +const + FORM_URL_ENCODED_SEPARATOR = '-'; + +implementation + +uses + MARS.Core.MediaType +; + +{ TMARSClientResourceFormUrlEncoded } + +procedure TMARSClientResourceFormUrlEncoded.AfterPOST(const AContent: TStream); +begin + inherited; + AContent.Position := 0; + FResponse.Size := 0; // clear + FResponse.CopyFrom(AContent, 0); +end; + +procedure TMARSClientResourceFormUrlEncoded.AfterPUT(const AContent: TStream); +begin + inherited; + AContent.Position := 0; + FResponse.Size := 0; // clear + FResponse.CopyFrom(AContent, 0); +end; + +procedure TMARSClientResourceFormUrlEncoded.AssignTo(Dest: TPersistent); +begin + inherited; + if Dest is TMARSClientResourceFormUrlEncoded then + TMARSClientResourceFormUrlEncoded(Dest).FFormUrlEncoded := FFormUrlEncoded; +end; + +constructor TMARSClientResourceFormUrlEncoded.Create(AOwner: TComponent); +begin + inherited; + FFormUrlEncoded := TMARSParameters.Create('keys'); + FResponse := TMemoryStream.Create; + SpecificContentType := TMediaType.APPLICATION_FORM_URLENCODED_TYPE; +end; + +destructor TMARSClientResourceFormUrlEncoded.Destroy; +begin + FResponse.Free; + FFormUrlEncoded.Free; + inherited; +end; + +function TMARSClientResourceFormUrlEncoded.GetResponseAsString: string; +begin + Result := StreamToString(FResponse); +end; + +procedure TMARSClientResourceFormUrlEncoded.POST(const AFormUrlEncoded: TMARSParameters; + const ABeforeExecute: TProc; const AAfterExecute: TMARSClientResponseProc; + const AOnException: TMARSClientExecptionProc); +begin + FFormUrlEncoded := AFormUrlEncoded; + POST(ABeforeExecute, AAfterExecute, AOnException); +end; + +procedure TMARSClientResourceFormUrlEncoded.POSTAsync(const AFormUrlEncoded: TMARSParameters; + const ABeforeExecute: TProc; const ACompletionHandler: TProc; + const AOnException: TMARSClientExecptionProc; const ASynchronize: Boolean); +begin + FFormUrlEncoded := AFormUrlEncoded; + inherited POSTAsync(ABeforeExecute, ACompletionHandler, AOnException, ASynchronize); +end; + +procedure TMARSClientResourceFormUrlEncoded.PUT(const AFormUrlEncoded: TMARSParameters; + const ABeforeExecute: TProc; const AAfterExecute: TMARSClientResponseProc; + const AOnException: TMARSClientExecptionProc); +begin + FFormUrlEncoded := AFormUrlEncoded; + PUT(ABeforeExecute, AAfterExecute, AOnException); +end; + +function TMARSClientResourceFormUrlEncoded.ResponseAs: T; +var + LJSON: TJSONValue; +begin + LJSON := ResponseAsJSON; + try + Result := (LJSON as TJSONObject).ToRecord; + finally + LJSON.Free; + end; +end; + +function TMARSClientResourceFormUrlEncoded.ResponseAsArray: TArray; +var + LJSON: TJSONValue; +begin + LJSON := ResponseAsJSON; + try + Result := (LJSON as TJSONArray).ToArrayOfRecord; + finally + LJSON.Free; + end; +end; + +function TMARSClientResourceFormUrlEncoded.ResponseAsJSON: TJSONValue; +begin + Result := StreamToJSONValue(Response); +end; + +procedure TMARSClientResourceFormUrlEncoded.PUT(const ABeforeExecute: TProc; + const AAfterExecute: TMARSClientResponseProc; const AOnException: TMARSClientExecptionProc); +var + LResponseStream: TMemoryStream; +begin + // inherited (!) + + try + BeforePUT(nil); + + if Assigned(ABeforeExecute) then + ABeforeExecute(nil); + + LResponseStream := TMemoryStream.Create; + try + Client.Put(URL, FFormUrlEncoded, LResponseStream, AuthToken, Accept, ContentType); + + AfterPUT(LResponseStream); + + if Assigned(AAfterExecute) then + AAfterExecute(LResponseStream); + finally + LResponseStream.Free; + end; + except + on E:Exception do + begin + if Assigned(AOnException) then + AOnException(E) + else + DoError(E, TMARSHttpVerb.Put, AAfterExecute); + end; + end; +end; + +procedure TMARSClientResourceFormUrlEncoded.POST(const ABeforeExecute: TProc; + const AAfterExecute: TMARSClientResponseProc; const AOnException: TMARSClientExecptionProc); +var + LResponseStream: TMemoryStream; +begin + // inherited (!) + + try + BeforePOST(nil); + + if Assigned(ABeforeExecute) then + ABeforeExecute(nil); + + LResponseStream := TMemoryStream.Create; + try + Client.Post(URL, FFormUrlEncoded, LResponseStream, AuthToken, Accept, ContentType); + + AfterPOST(LResponseStream); + + if Assigned(AAfterExecute) then + AAfterExecute(LResponseStream); + finally + LResponseStream.Free; + end; + except + on E:Exception do + begin + if Assigned(AOnException) then + AOnException(E) + else + DoError(E, TMARSHttpVerb.Post, AAfterExecute); + end; + end; +end; + +end. diff --git a/Source/MARS.Core.Activation.InjectionService.pas b/Source/MARS.Core.Activation.InjectionService.pas index 5b4142a2..99fb9004 100644 --- a/Source/MARS.Core.Activation.InjectionService.pas +++ b/Source/MARS.Core.Activation.InjectionService.pas @@ -10,11 +10,9 @@ interface uses - Classes, SysUtils, Rtti - , MARS.Core.Injection - , MARS.Core.Injection.Interfaces - , MARS.Core.Injection.Types - , MARS.Core.Activation.Interfaces + Classes, SysUtils, System.Rtti, System.TypInfo +, MARS.Core.Injection, MARS.Core.Injection.Interfaces, MARS.Core.Injection.Types +, MARS.Core.Activation.Interfaces ; type @@ -27,13 +25,9 @@ TMARSActivationInjectionService = class(TInterfacedObject, IMARSInjectionServi implementation uses - MARS.Rtti.Utils - , MARS.Core.Token, MARS.Core.URL, MARS.Core.Engine, MARS.Core.Application, MARS.Core.Attributes -{$ifdef DelphiXE6_UP} - , Web.HttpApp -{$else} - , HttpApp -{$endif} + MARS.Rtti.Utils +, MARS.Core.Token, MARS.Core.URL, MARS.Core.Engine, MARS.Core.Application, MARS.Core.Attributes +, MARS.Core.RequestAndResponse.Interfaces ; { TMARSActivationInjectionService } @@ -63,10 +57,10 @@ procedure TMARSActivationInjectionService.GetValue(const ADestination: TRttiObje end ) then AValue := LValue - else if (LType.IsObjectOfType(TWebRequest)) then - AValue := TInjectionValue.Create(AActivation.Request, True) - else if (LType.IsObjectOfType(TWebResponse)) then - AValue := TInjectionValue.Create(AActivation.Response, True) + else if (LType is TRttiInterfaceType) and (LType.Handle = TypeInfo(IMARSRequest)) then + AValue := TInjectionValue.Create(TValue.From(AActivation.Request), True) + else if (LType is TRttiInterfaceType) and (LType.Handle = TypeInfo(IMARSResponse)) then + AValue := TInjectionValue.Create(TValue.From(AActivation.Response), True) else if (LType.IsObjectOfType(TMARSURL)) then AValue := TInjectionValue.Create(AActivation.URL, True) else if (LType.IsObjectOfType(TMARSEngine)) then @@ -96,8 +90,8 @@ procedure RegisterServices; Result := ADestination.HasAttribute or ADestination.HasAttribute - or LType.IsObjectOfType(TWebRequest) - or LType.IsObjectOfType(TWebResponse) + or (LType.Handle = TypeInfo(IMARSRequest)) + or (LType.Handle = TypeInfo(IMARSResponse)) or LType.IsObjectOfType(TMARSURL) or LType.IsObjectOfType(TMARSEngine) or LType.IsObjectOfType(TMARSApplication) diff --git a/Source/MARS.Core.Activation.Interfaces.pas b/Source/MARS.Core.Activation.Interfaces.pas index 3f23fb19..0ea46b42 100644 --- a/Source/MARS.Core.Activation.Interfaces.pas +++ b/Source/MARS.Core.Activation.Interfaces.pas @@ -11,18 +11,11 @@ interface uses SysUtils, Classes, Generics.Collections, Rtti, Diagnostics - , HTTPApp - - , MARS.Core.URL - , MARS.Core.Application - , MARS.Core.Engine - , MARS.Core.Token - , MARS.Core.MediaType - , MARS.Core.Injection.Types - ; +, MARS.Core.URL, MARS.Core.Application, MARS.Core.Engine, MARS.Core.Token +, MARS.Core.MediaType, MARS.Core.Injection.Types, MARS.Core.RequestAndResponse.Interfaces +; type - IMARSActivation = interface procedure AddToContext(AValue: TValue); function HasToken: Boolean; @@ -40,11 +33,11 @@ interface function GetMethodArguments: TArray; function GetMethodAttributes: TArray; function GetMethodResult: TValue; - function GetRequest: TWebRequest; + function GetRequest: IMARSRequest; function GetResource: TRttiType; function GetResourceAttributes: TArray; function GetResourceInstance: TObject; - function GetResponse: TWebResponse; + function GetResponse: IMARSResponse; function GetURL: TMARSURL; function GetURLPrototype: TMARSURL; function GetToken: TMARSToken; @@ -60,11 +53,11 @@ interface property MethodArguments: TArray read GetMethodArguments; property MethodAttributes: TArray read GetMethodAttributes; property MethodResult: TValue read GetMethodResult; - property Request: TWebRequest read GetRequest; + property Request: IMARSRequest read GetRequest; property Resource: TRttiType read GetResource; property ResourceAttributes: TArray read GetResourceAttributes; property ResourceInstance: TObject read GetResourceInstance; - property Response: TWebResponse read GetResponse; + property Response: IMARSResponse read GetResponse; property URL: TMARSURL read GetURL; property URLPrototype: TMARSURL read GetURLPrototype; property Token: TMARSToken read GetToken; diff --git a/Source/MARS.Core.Activation.pas b/Source/MARS.Core.Activation.pas index 2e42fede..762a66bd 100644 --- a/Source/MARS.Core.Activation.pas +++ b/Source/MARS.Core.Activation.pas @@ -10,12 +10,12 @@ interface uses - SysUtils, Classes, Generics.Collections, Rtti, Diagnostics, HTTPApp + SysUtils, Classes, Generics.Collections, Rtti, Diagnostics , MARS.Core.Classes, MARS.Core.URL, MARS.Core.MediaType , MARS.Core.Application, MARS.Core.Engine, MARS.Core.Token , MARS.Core.Registry.Utils, MARS.Core.Injection.Types, MARS.Core.Activation.Interfaces -, MARS.Core.MessageBodyWriter +, MARS.Core.MessageBodyWriter, MARS.Core.RequestAndResponse.Interfaces ; type @@ -23,7 +23,7 @@ TMARSActivation = class; TMARSActivationFactoryFunc = reference to function (const AEngine: TMARSEngine; const AApplication: TMARSApplication; - const ARequest: TWebRequest; const AResponse: TWebResponse; + const ARequest: IMARSRequest; const AResponse: IMARSResponse; const AURL: TMARSURL ): IMARSActivation; @@ -43,8 +43,8 @@ TMARSAuthorizationInfo = record TMARSActivation = class(TInterfacedObject, IMARSActivation) private - FRequest: TWebRequest; - FResponse: TWebResponse; + FRequest: IMARSRequest; + FResponse: IMARSResponse; class var FBeforeInvokeProcs: TArray; class var FAfterInvokeProcs: TArray; class var FInvokeErrorProcs: TArray; @@ -97,7 +97,7 @@ TMARSActivation = class(TInterfacedObject, IMARSActivation) procedure CheckAuthorization; virtual; public constructor Create(const AEngine: TMARSEngine; const AApplication: TMARSApplication; - const ARequest: TWebRequest; const AResponse: TWebResponse; const AURL: TMARSURL); virtual; + const ARequest: IMARSRequest; const AResponse: IMARSResponse; const AURL: TMARSURL); virtual; destructor Destroy; override; // --- IMARSActivation implementation -------------- @@ -116,11 +116,11 @@ TMARSActivation = class(TInterfacedObject, IMARSActivation) function GetMethodArguments: TArray; inline; function GetMethodAttributes: TArray; inline; function GetMethodResult: TValue; inline; - function GetRequest: TWebRequest; inline; + function GetRequest: IMARSRequest; inline; function GetResource: TRttiType; inline; function GetResourceAttributes: TArray; inline; function GetResourceInstance: TObject; inline; - function GetResponse: TWebResponse; inline; + function GetResponse: IMARSResponse; inline; function GetURL: TMARSURL; inline; function GetURLPrototype: TMARSURL; inline; function GetToken: TMARSToken; inline; @@ -131,10 +131,10 @@ TMARSActivation = class(TInterfacedObject, IMARSActivation) property InvocationTime: TStopwatch read FInvocationTime; property Method: TRttiMethod read FMethod; property MethodArguments: TArray read FMethodArguments; - property Request: TWebRequest read FRequest; + property Request: IMARSRequest read FRequest; property Resource: TRttiType read FResource; property ResourceInstance: TObject read FResourceInstance; - property Response: TWebResponse read FResponse; + property Response: IMARSResponse read FResponse; property URL: TMARSURL read FURL; property URLPrototype: TMARSURL read FURLPrototype; property Token: TMARSToken read GetToken; @@ -152,7 +152,7 @@ TMARSActivation = class(TInterfacedObject, IMARSActivation) class var CreateActivationFunc: TMARSActivationFactoryFunc; class function CreateActivation(const AEngine: TMARSEngine; const AApplication: TMARSApplication; - const ARequest: TWebRequest; const AResponse: TWebResponse; + const ARequest: IMARSRequest; const AResponse: IMARSResponse; const AURL: TMARSURL): IMARSActivation; end; @@ -208,7 +208,7 @@ function TMARSActivation.GetMethodReturnType: TRttiType; Result := FMethodReturnType; end; -function TMARSActivation.GetRequest: TWebRequest; +function TMARSActivation.GetRequest: IMARSRequest; begin Result := FRequest; end; @@ -228,7 +228,7 @@ function TMARSActivation.GetResourceInstance: TObject; Result := FResourceInstance; end; -function TMARSActivation.GetResponse: TWebResponse; +function TMARSActivation.GetResponse: IMARSResponse; begin Result := FResponse; end; @@ -563,7 +563,7 @@ procedure TMARSActivation.SetCustomHeaders; LCustomAtributeProcessor := procedure (ACustomHeader: CustomHeaderAttribute) begin - Response.CustomHeaders.Values[ACustomHeader.HeaderName] := ACustomHeader.Value; + Response.SetHeader(ACustomHeader.HeaderName, ACustomHeader.Value); end; TRttiHelper.ForEachAttribute(FResourceAttributes, LCustomAtributeProcessor); TRttiHelper.ForEachAttribute(FMethodAttributes, LCustomAtributeProcessor); @@ -606,7 +606,7 @@ procedure TMARSActivation.Invoke; begin try try - Request.ReadTotalContent; // workaround for https://quality.embarcadero.com/browse/RSP-14674 + Request.CheckWorkaroundForISAPI; // setup phase FSetupTime := TStopWatch.StartNew; @@ -741,7 +741,7 @@ function TMARSActivation.GetInvocationTime: TStopwatch; constructor TMARSActivation.Create(const AEngine: TMARSEngine; const AApplication: TMARSApplication; - const ARequest: TWebRequest; const AResponse: TWebResponse; + const ARequest: IMARSRequest; const AResponse: IMARSResponse; const AURL: TMARSURL); begin inherited Create; @@ -775,8 +775,8 @@ constructor TMARSActivation.Create(const AEngine: TMARSEngine; end; class function TMARSActivation.CreateActivation(const AEngine: TMARSEngine; - const AApplication: TMARSApplication; const ARequest: TWebRequest; - const AResponse: TWebResponse; const AURL: TMARSURL): IMARSActivation; + const AApplication: TMARSApplication; const ARequest: IMARSRequest; + const AResponse: IMARSResponse; const AURL: TMARSURL): IMARSActivation; begin if Assigned(CreateActivationFunc) then Result := CreateActivationFunc(AEngine, AApplication, ARequest, AResponse, AURL) diff --git a/Source/MARS.Core.Attributes.pas b/Source/MARS.Core.Attributes.pas index 69382af3..ab242a94 100644 --- a/Source/MARS.Core.Attributes.pas +++ b/Source/MARS.Core.Attributes.pas @@ -10,14 +10,11 @@ interface uses - Classes, SysUtils, RTTI, Generics.Collections - , HttpApp, Web.ReqMulti - , MARS.Core.Declarations - , MARS.Core.Utils - , MARS.Core.URL - , MARS.Core.JSON - , MARS.Core.Activation.Interfaces - , MARS.Core.Exceptions + Classes, SysUtils, System.Rtti, System.TypInfo, Generics.Collections +, Web.ReqMulti +, MARS.Core.Declarations, MARS.Core.Utils, MARS.Core.URL, MARS.Core.JSON +, MARS.Core.Activation.Interfaces, MARS.Core.RequestAndResponse.Interfaces +, MARS.Core.Exceptions ; type @@ -36,48 +33,48 @@ HttpMethodAttribute = class(MARSAttribute) protected function GetHttpMethodName: string; virtual; public - function Matches(const ARequest: TWebRequest): Boolean; virtual; + function Matches(const ARequest: IMARSRequest): Boolean; virtual; property HttpMethodName: string read GetHttpMethodName; end; - ANYAttribute = class(HttpMethodAttribute) - public - function Matches(const ARequest: TWebRequest): Boolean; override; - end; +// ANYAttribute = class(HttpMethodAttribute) +// public +// function Matches(const ARequest: IMARSRequest): Boolean; override; +// end; GETAttribute = class(HttpMethodAttribute) public - function Matches(const ARequest: TWebRequest): Boolean; override; + function Matches(const ARequest: IMARSRequest): Boolean; override; end; POSTAttribute = class(HttpMethodAttribute) public - function Matches(const ARequest: TWebRequest): Boolean; override; + function Matches(const ARequest: IMARSRequest): Boolean; override; end; PUTAttribute = class(HttpMethodAttribute) public - function Matches(const ARequest: TWebRequest): Boolean; override; + function Matches(const ARequest: IMARSRequest): Boolean; override; end; DELETEAttribute = class(HttpMethodAttribute) public - function Matches(const ARequest: TWebRequest): Boolean; override; + function Matches(const ARequest: IMARSRequest): Boolean; override; end; PATCHAttribute = class(HttpMethodAttribute) public - function Matches(const ARequest: TWebRequest): Boolean; override; + function Matches(const ARequest: IMARSRequest): Boolean; override; end; HEADAttribute = class(HttpMethodAttribute) public - function Matches(const ARequest: TWebRequest): Boolean; override; + function Matches(const ARequest: IMARSRequest): Boolean; override; end; OPTIONSAttribute = class(HttpMethodAttribute) public - function Matches(const ARequest: TWebRequest): Boolean; override; + function Matches(const ARequest: IMARSRequest): Boolean; override; end; ConsumesAttribute = class(MARSAttribute) @@ -466,59 +463,51 @@ function HttpMethodAttribute.GetHttpMethodName: string; {$endif} end; -function HttpMethodAttribute.Matches(const ARequest: TWebRequest): Boolean; +function HttpMethodAttribute.Matches(const ARequest: IMARSRequest): Boolean; begin Result := False; end; { GETAttribute } -function GETAttribute.Matches(const ARequest: TWebRequest): Boolean; +function GETAttribute.Matches(const ARequest: IMARSRequest): Boolean; begin - Result := ARequest.MethodType = TMethodType.mtGet; + Result := SameText(ARequest.Method, 'GET'); end; { POSTAttribute } -function POSTAttribute.Matches(const ARequest: TWebRequest): Boolean; +function POSTAttribute.Matches(const ARequest: IMARSRequest): Boolean; begin - Result := ARequest.MethodType = TMethodType.mtPost; + Result := SameText(ARequest.Method, 'POST'); end; { PUTAttribute } -function PUTAttribute.Matches(const ARequest: TWebRequest): Boolean; +function PUTAttribute.Matches(const ARequest: IMARSRequest): Boolean; begin - Result := ARequest.MethodType = TMethodType.mtPut; + Result := SameText(ARequest.Method, 'PUT'); end; { DELETEAttribute } -function DELETEAttribute.Matches(const ARequest: TWebRequest): Boolean; +function DELETEAttribute.Matches(const ARequest: IMARSRequest): Boolean; begin -{$ifdef DelphiXE7_UP} - Result := ARequest.MethodType = TMethodType.mtDelete; -{$else} - Result := SameText(string(ARequest.Method), 'Delete'); -{$endif} + Result := SameText(ARequest.Method, 'DELETE'); end; { PATCHAttribute } -function PATCHAttribute.Matches(const ARequest: TWebRequest): Boolean; +function PATCHAttribute.Matches(const ARequest: IMARSRequest): Boolean; begin -{$ifdef DelphiXE7_UP} - Result := ARequest.MethodType = TMethodType.mtPatch; -{$else} - Result := SameText(string(ARequest.Method), 'Patch'); -{$endif} + Result := SameText(ARequest.Method, 'PATCH'); end; { HEADAttribute } -function HEADAttribute.Matches(const ARequest: TWebRequest): Boolean; +function HEADAttribute.Matches(const ARequest: IMARSRequest): Boolean; begin - Result := ARequest.MethodType = TMethodType.mtHead; + Result := SameText(ARequest.Method, 'HEAD'); end; { CustomHeaderAttribute } @@ -597,7 +586,7 @@ function QueryParamAttribute.GetValue(const ADestination: TRttiObject; LReader: IMessageBodyReader; LIndex: Integer; begin - LIndex := AActivation.Request.QueryFields.IndexOfName(GetActualName(ADestination)); + LIndex := AActivation.Request.GetQueryParamIndex(GetActualName(ADestination)); if (LIndex = -1) then CheckRequiredAttribute(ADestination) else @@ -608,7 +597,7 @@ function QueryParamAttribute.GetValue(const ADestination: TRttiObject; try Result := LReader.ReadFrom( {$ifdef Delphi10Berlin_UP} TEncoding.UTF8.GetBytes( {$endif} - AActivation.Request.QueryFields.ValueFromIndex[LIndex] + AActivation.Request.GetQueryParamValue(LIndex) {$ifdef Delphi10Berlin_UP} ) {$endif} , ADestination, LMediaType, AActivation); finally @@ -617,7 +606,7 @@ function QueryParamAttribute.GetValue(const ADestination: TRttiObject; else // 2 - fallback (raw) begin Result := StringToTValue( - AActivation.Request.QueryFields.ValueFromIndex[LIndex] + AActivation.Request.GetQueryParamValue(LIndex) , ADestination.GetRttiType ); end; @@ -636,23 +625,12 @@ function FormParamAttribute.GetValue(const ADestination: TRttiObject; var LMediaType: TMediaType; LReader: IMessageBodyReader; - LParamIndex: Integer; - LFileIndex: Integer; - LIndex: Integer; - LFile: TAbstractWebRequestFile; + LParamIndex, LFileIndex: Integer; + LActualName: string; begin - LParamIndex := AActivation.Request.ContentFields.IndexOfName(GetActualName(ADestination)); - LFileIndex := -1; - for LIndex := 0 to AActivation.Request.Files.Count-1 do - begin - LFile := AActivation.Request.Files[LIndex]; - if SameText(LFile.FieldName, GetActualName(ADestination)) then - begin - LFileIndex := LIndex; - Break; - end; - end; - + LActualName := GetActualName(ADestination); + LParamIndex := AActivation.Request.GetFormParamIndex(LActualName); + LFileIndex := AActivation.Request.GetFormFileParamIndex(LActualName); if (LParamIndex = -1) and (LFileIndex = -1) then CheckRequiredAttribute(ADestination) else @@ -661,17 +639,23 @@ function FormParamAttribute.GetValue(const ADestination: TRttiObject; TMARSMessageBodyReaderRegistry.Instance.FindReader(ADestination, LReader, LMediaType); if Assigned(LReader) then try - Result := LReader.ReadFrom( - {$ifdef Delphi10Berlin_UP} TEncoding.UTF8.GetBytes( {$endif} - AActivation.Request.ContentFields.ValueFromIndex[LParamIndex] - {$ifdef Delphi10Berlin_UP} ) {$endif} - , ADestination, LMediaType, AActivation); + if LParamIndex <> -1 then + Result := LReader.ReadFrom( + {$ifdef Delphi10Berlin_UP} TEncoding.UTF8.GetBytes( {$endif} + AActivation.Request.GetFormParamValue(LParamIndex) + {$ifdef Delphi10Berlin_UP} ) {$endif} + , ADestination, LMediaType, AActivation) + else if LFileIndex <> -1 then + Result := LReader.ReadFrom( + {$ifdef Delphi10Berlin_UP} TEncoding.UTF8.GetBytes( {$endif} + '' + {$ifdef Delphi10Berlin_UP} ) {$endif} + , ADestination, LMediaType, AActivation); finally FreeAndNil(LMediaType); end else // 2 - fallback (raw) - Result := StringToTValue(AActivation.Request.ContentFields.ValueFromIndex[LParamIndex] - , ADestination.GetRttiType); + Result := StringToTValue(AActivation.Request.GetFormParamValue(LParamIndex), ADestination.GetRttiType); end; end; @@ -692,7 +676,7 @@ function HeaderParamAttribute.GetValue(const ADestination: TRttiObject; LValue: string; begin Result := TValue.Empty; - LValue := AActivation.Request.GetFieldByName(TheName); + LValue := AActivation.Request.GetHeaderParamValue(TheName); if (LValue = '') then CheckRequiredAttribute(TheDestination); @@ -756,11 +740,11 @@ function CookieParamAttribute.GetValue(const ADestination: TRttiObject; var LIndex: Integer; begin - LIndex := AActivation.Request.CookieFields.IndexOfName(GetActualName(ADestination)); + LIndex := AActivation.Request.GetCookieParamIndex(GetActualName(ADestination)); if LIndex = -1 then CheckRequiredAttribute(ADestination); Result := StringToTValue( - AActivation.Request.CookieFields.ValueFromIndex[LIndex] + AActivation.Request.GetCookieParamValue(LIndex) , ADestination.GetRttiType ); end; @@ -821,16 +805,16 @@ function AuthorizationAttribute.ToString: string; Result := Result.Substring(0, Result.Length - 'Attribute'.Length); end; -{ ANYAttribute } - -function ANYAttribute.Matches(const ARequest: TWebRequest): Boolean; -begin - Result := ARequest.MethodType = TMethodType.mtAny; -end; +//{ ANYAttribute } +// +//function ANYAttribute.Matches(const ARequest: IMARSRequest): Boolean; +//begin +// Result := SameText(ARequest.Method, 'ANY'); +//end; { OPTIONSAttribute } -function OPTIONSAttribute.Matches(const ARequest: TWebRequest): Boolean; +function OPTIONSAttribute.Matches(const ARequest: IMARSRequest): Boolean; begin Result := SameText(ARequest.Method, 'OPTIONS'); end; @@ -859,7 +843,7 @@ function FormParamsAttribute.GetValue(const ADestination: TRttiObject; LMediaType: TMediaType; LReader: IMessageBodyReader; begin - if AActivation.Request.ContentFields.Count = 0 then + if AActivation.Request.GetFormParamCount = 0 then CheckRequiredAttribute(ADestination) else begin @@ -874,7 +858,7 @@ function FormParamsAttribute.GetValue(const ADestination: TRttiObject; FreeAndNil(LMediaType); end else // 2 - fallback (raw) - Result := StringToTValue(AActivation.Request.ContentFields.Text, ADestination.GetRttiType); + Result := StringToTValue(AActivation.Request.GetFormParams, ADestination.GetRttiType); end; end; diff --git a/Source/MARS.Core.Engine.pas b/Source/MARS.Core.Engine.pas index 17be5cc6..89c5c330 100644 --- a/Source/MARS.Core.Engine.pas +++ b/Source/MARS.Core.Engine.pas @@ -10,15 +10,11 @@ interface uses - SysUtils, HTTPApp, Classes, Generics.Collections + SysUtils, Classes, Generics.Collections , SyncObjs - , MARS.Core.Classes - , MARS.Core.Registry - , MARS.Core.Application - , MARS.Core.URL - , MARS.Core.Exceptions - , MARS.Utils.Parameters + , MARS.Core.Classes, MARS.Core.Registry, MARS.Core.Application, MARS.Core.URL + , MARS.Core.Exceptions, MARS.Utils.Parameters, MARS.Core.RequestAndResponse.Interfaces ; {$M+} @@ -31,10 +27,10 @@ EMARSEngineException = class(EMARSHttpException); TMARSEngine = class; TBeforeHandleRequestProc = reference to function(const AEngine: TMARSEngine; - const AURL: TMARSURL; const ARequest: TWebRequest; const AResponse: TWebResponse; + const AURL: TMARSURL; const ARequest: IMARSRequest; const AResponse: IMARSResponse; var Handled: Boolean): Boolean; TAfterHandleRequestProc = reference to procedure(const AEngine: TMARSEngine; - const AURL: TMARSURL; const ARequest: TWebRequest; const AResponse: TWebResponse; + const AURL: TMARSURL; const ARequest: IMARSRequest; const AResponse: IMARSResponse; var Handled: Boolean); TMARSEngine = class @@ -52,12 +48,12 @@ TMARSEngine = class procedure SetBasePath(const Value: string); virtual; procedure SetPort(const Value: Integer); virtual; procedure SetThreadPoolSize(const Value: Integer); virtual; - procedure PatchCORS(const ARequest: TWebRequest; const AResponse: TWebResponse); virtual; + procedure PatchCORS(const ARequest: IMARSRequest; const AResponse: IMARSResponse); virtual; public constructor Create(const AName: string = DEFAULT_ENGINE_NAME); virtual; destructor Destroy; override; - function HandleRequest(ARequest: TWebRequest; AResponse: TWebResponse): Boolean; virtual; + function HandleRequest(ARequest: IMARSRequest; AResponse: IMARSResponse): Boolean; virtual; function AddApplication(const AName, ABasePath: string; const AResources: array of string; const AParametersSliceName: string = ''): TMARSApplication; virtual; @@ -190,7 +186,7 @@ procedure TMARSEngine.EnumerateApplications( end; end; -function TMARSEngine.HandleRequest(ARequest: TWebRequest; AResponse: TWebResponse): Boolean; +function TMARSEngine.HandleRequest(ARequest: IMARSRequest; AResponse: IMARSResponse): Boolean; var LApplication: TMARSApplication; LURL: TMARSURL; @@ -240,13 +236,12 @@ function TMARSEngine.HandleRequest(ARequest: TWebRequest; AResponse: TWebRespons end; end; -procedure TMARSEngine.PatchCORS(const ARequest: TWebRequest; - const AResponse: TWebResponse); +procedure TMARSEngine.PatchCORS(const ARequest: IMARSRequest; + const AResponse: IMARSResponse); procedure SetHeaderFromParameter(const AHeader, AParamName, ADefault: string); begin - AResponse.CustomHeaders.Values[AHeader] := - Parameters.ByName(AParamName, ADefault).AsString; + AResponse.SetHeader(AHeader, Parameters.ByName(AParamName, ADefault).AsString); end; begin diff --git a/Source/MARS.Core.JSON.pas b/Source/MARS.Core.JSON.pas index 7177ef7e..4bf1c5e9 100644 --- a/Source/MARS.Core.JSON.pas +++ b/Source/MARS.Core.JSON.pas @@ -1,1007 +1,1010 @@ -(* - Copyright 2016, MARS-Curiosity library - - Home: https://github.com/andrea-magni/MARS -*) -unit MARS.Core.JSON; - -{$I MARS.inc} - -interface - -uses -{$IFDEF Delphi10Rio_UP} - Generics.Collections, -{$ENDIF} -{$ifdef DelphiXE6_UP} - JSON -{$else} - DBXJSON -{$endif} - , SysUtils -{$ifdef DelphiXE2_UP} - , System.Rtti -{$else} - , Rtti -{$endif} - , TypInfo, REST.JSON -; - -type - TJSONAncestor = {$ifdef DelphiXE6_UP}JSON.TJSONAncestor{$else}DBXJSON.TJSONAncestor{$endif}; - TJSONPair = {$ifdef DelphiXE6_UP}JSON.TJSONPair{$else}DBXJSON.TJSONPair{$endif}; - TJSONValue = {$ifdef DelphiXE6_UP}JSON.TJSONValue{$else}DBXJSON.TJSONValue{$endif}; - TJSONTrue = {$ifdef DelphiXE6_UP}JSON.TJSONTrue{$else}DBXJSON.TJSONTrue{$endif}; - TJSONString = {$ifdef DelphiXE6_UP}JSON.TJSONString{$else}DBXJSON.TJSONString{$endif}; - TJSONNumber = {$ifdef DelphiXE6_UP}JSON.TJSONNumber{$else}DBXJSON.TJSONNumber{$endif}; - TJSONObject = {$ifdef DelphiXE6_UP}JSON.TJSONObject{$else}DBXJSON.TJSONObject{$endif}; - TJSONNull = {$ifdef DelphiXE6_UP}JSON.TJSONNull{$else}DBXJSON.TJSONNull{$endif}; - TJSONFalse = {$ifdef DelphiXE6_UP}JSON.TJSONFalse{$else}DBXJSON.TJSONFalse{$endif}; - TJSONArray = {$ifdef DelphiXE6_UP}JSON.TJSONArray{$else}DBXJSON.TJSONArray{$endif}; - - TJSONValueHelper = class helper for TJSONValue - public -{$ifndef DelphiXE7_UP} - function TryGetValue(const APath: string; out AValue: T): Boolean; overload; - function ToJSON: string; -{$endif} - end; - - JSONNameAttribute = class(TCustomAttribute) - private - FName: string; - public - constructor Create(const AName: string); - property Name: string read FName; - end; - - TToRecordFilterProc = reference to procedure (const AField: TRttiField; - const ARecord: TValue; const AJSONObject: TJSONObject; var AAccept: Boolean); - - TToJSONFilterProc = reference to procedure (const AField: TRttiField; - const ARecord: TValue; const AJSONObject: TJSONObject; var AAccept: Boolean); - - TJSONRawString = type string; - -{$ifndef DelphiXE6_UP} - TJSONArrayEnumerator = class - private - FIndex: Integer; - FArray: TJSONArray; - public - constructor Create(const AArray: TJSONArray); - function GetCurrent: TJSONValue; inline; - function MoveNext: Boolean; - property Current: TJSONValue read GetCurrent; - end; -{$endif} - - TJSONArrayHelper= class helper for TJSONArray - private - {$ifndef DelphiXE6_UP} - function GetCount: Integer; inline; - function GetValue(const Index: Integer): TJSONValue; inline; - {$endif} - public - function ToArrayOfRecord(): TArray; - procedure FromArrayOfRecord(const AArray: TArray; - const AFilterProc: TToJSONFilterProc = nil); - procedure FromArrayOfObject(const AArray: TArray; - const AOptions: TJsonOptions = [joDateIsUTC, joDateFormatISO8601]); - function ForEach(const AFunc: TFunc): Integer; - - {$ifndef DelphiXE6_UP} - function GetEnumerator: TJSONArrayEnumerator; - - property Count: Integer read GetCount; - property Items[const Index: Integer]: TJSONValue read GetValue; - {$endif} - - class function ArrayOfRecordToJSON(const AArray: TArray; const AFilterProc: TToJSONFilterProc = nil): TJSONArray; - class function ArrayOfObjectToJSON(const AArray: TArray): TJSONArray; - end; - - TJSONObjectHelper = class helper(TJSONValueHelper) for TJSONObject - private -{$ifndef DelphiXE6_UP} - function GetCount: Integer; inline; - function GetPair(const Index: Integer): TJSONPair; inline; -{$endif} - function GetExactPairName(const ACaseInsensitiveName: string): string; - public - function ReadStringValue(const AName: string; const ADefault: string = ''): string; - function ReadIntegerValue(const AName: string; const ADefault: Integer = 0): Integer; -{$ifdef DelphiXE6_UP} - function ReadInt64Value(const AName: string; const ADefault: Int64 = 0): Int64; -{$endif} - function ReadDoubleValue(const AName: string; const ADefault: Double = 0.0): Double; - function ReadBoolValue(const AName: string; const ADefault: Boolean = False): Boolean; - function ReadDateTimeValue(const AName: string; const ADefault: TDateTime = 0.0; - const AReturnUTC: Boolean = False): TDateTime; - function ReadUnixTimeValue(const AName: string; const ADefault: TDateTime = 0.0): TDateTime; - function ReadValue(const AName: string; const ADefault: TValue; - const ADesiredType: TRttiType; const ANameCaseSensitive: Boolean = True): TValue; overload; - function ReadValue(const AName: string; const ADesiredType: TRttiType; - const ANameCaseSensitive: Boolean; out AValue: TValue): Boolean; overload; - function ReadArrayValue(const AName: string): TJSONArray; overload; inline; - function ReadArrayValue(const AName: string): TArray; overload; inline; - - procedure WriteStringValue(const AName: string; const AValue: string); - procedure WriteIntegerValue(const AName: string; const AValue: Integer); - procedure WriteInt64Value(const AName: string; const AValue: Int64); - procedure WriteDoubleValue(const AName: string; const AValue: Double); - procedure WriteBoolValue(const AName: string; const AValue: Boolean); - procedure WriteDateTimeValue(const AName: string; const AValue: TDateTime; - const AInputIsUTC: Boolean = False); - procedure WriteUnixTimeValue(const AName: string; const AValue: TDateTime); - procedure WriteTValue(const AName: string; const AValue: TValue); - procedure WriteArrayValue(const AName: string; const AArray: TJSONArray); overload; inline; - procedure WriteArrayValue(const AName: string; const AArray: TArray); overload; inline; - - procedure FromRecord(ARecord: T; const AFilterProc: TToJSONFilterProc = nil); overload; - procedure FromRecord(const ARecord: TValue; const AFilterProc: TToJSONFilterProc = nil); overload; - function ToRecord(const AFilterProc: TToRecordFilterProc = nil): T; overload; - function ToRecord(const ARecordType: TRttiType; - const AFilterProc: TToRecordFilterProc = nil): TValue; overload; - -{$ifndef DelphiXE6_UP} - property Count: Integer read GetCount; - property Pairs[const Index: Integer]: TJSONPair read GetPair; -{$endif} - - class function ObjectToJSON(const AObject: TObject; - const AOptions: TJsonOptions = [joDateIsUTC, joDateFormatISO8601]): TJSONObject; overload; - - class function JSONToObject(const AJSON: TJSONObject; - const AOptions: TJsonOptions = [joDateIsUTC, joDateFormatISO8601]): T; overload; - - class function JSONToObject(const AClassType: TClass; const AJSON: TJSONObject; - const AOptions: TJsonOptions = [joDateIsUTC, joDateFormatISO8601]): TObject; overload; - - class function RecordToJSON(ARecord: T; - const AFilterProc: TToJSONFilterProc = nil): TJSONObject; overload; - class function RecordToJSON(const ARecord: TValue; - const AFilterProc: TToJSONFilterProc = nil): TJSONObject; overload; - - class function JSONToRecord(const AJSON: TJSONObject; - const AFilterProc: TToRecordFilterProc = nil): T; overload; - class function JSONToRecord(const ARecordType: TRttiType; const AJSON: TJSONObject; - const AFilterProc: TToRecordFilterProc = nil): TValue; overload; - - class function TValueToJSONValue(const AValue: TValue): TJSONValue; - class function TJSONValueToTValue(const AValue: TJSONValue; const ADesiredType: TRttiType): TValue; - end; - - function StringArrayToJsonArray(const AStringArray: TArray): TJSONArray; - function JsonArrayToStringArray(const AJSONArray: TJSONArray): TArray; - -implementation - -uses - DateUtils, Variants, StrUtils - , MARS.Core.Utils - , MARS.Rtti.Utils -; - -class function TJSONObjectHelper.TValueToJSONValue( - const AValue: TValue): TJSONValue; -var - LArray: TJSONArray; - LIndex: Integer; -begin - if AValue.IsEmpty and not AValue.IsArray then - Result := TJSONNull.Create - - else if (AValue.Kind in [tkString, tkUString, tkChar, {$ifdef DelphiXE6_UP} tkWideChar, {$endif} tkLString, tkWString]) then - Result := TJSONString.Create(AValue.AsString) - - else if AValue.IsArray then - begin - LArray := TJSONArray.Create; - try - for LIndex := 0 to AValue.GetArrayLength-1 do - LArray.AddElement(TValueToJSONValue(AValue.GetArrayElement(LIndex))); - - Result := LArray; - except - LArray.Free; - raise; - end; - end - - else if (AValue.Kind in [tkRecord]) then - Result := TJSONObject.RecordToJSON(AValue) - - else if (AValue.IsType) then - Result := BooleanToTJSON(AValue.AsType) - - else if AValue.TypeInfo = TypeInfo(TDateTime) then - Result := TJSONString.Create( DateToJSON(AValue.AsType) ) - else if AValue.TypeInfo = TypeInfo(TDate) then - Result := TJSONString.Create( DateToJSON(AValue.AsType) ) - else if AValue.TypeInfo = TypeInfo(TTime) then - Result := TJSONString.Create( DateToJSON(AValue.AsType) ) - - else if (AValue.Kind in [tkInt64]) then - Result := TJSONNumber.Create( AValue.AsType ) - else if (AValue.Kind in [tkInteger]) then - Result := TJSONNumber.Create( AValue.AsType ) - - else if (AValue.Kind in [tkFloat]) then - Result := TJSONNumber.Create( AValue.AsType ) - - else if (AValue.Kind in [tkVariant]) then - Result := TValueToJSONValue( TValue.FromVariant(AValue.AsVariant) ) - - else if (AValue.IsInstanceOf(TObject)) then - Result := ObjectToJSON(AValue.AsObject) - - else - Result := TJSONString.Create(AValue.ToString); - -end; - -function StringArrayToJsonArray(const AStringArray: TArray): TJSONArray; -var - LIndex: Integer; -begin - Result := TJSONArray.Create; - try - for LIndex := Low(AStringArray) to High(AStringArray) do - Result.Add(AStringArray[LIndex]); - except - Result.Free; - raise; - end; -end; - -function JsonArrayToStringArray(const AJSONArray: TJSONArray): TArray; -var - LElement: TJSONValue; - LIndex: Integer; -begin - SetLength(Result, AJSONArray.Count); - - for LIndex := 0 to AJSONArray.Count-1 do - begin - LElement := AJSONArray.Items[LIndex]; - if LElement is TJSONString then - Result[LIndex] := TJSONString(LElement).Value - else if LElement is TJSONNumber then - Result[LIndex] := TJSONNumber(LElement).ToString - else if LElement is TJSONTrue then - Result[LIndex] := 'true' - else if LElement is TJSONFalse then - Result[LIndex] := 'false' - else - Result[LIndex] := LElement.ToString; - end; -end; - -{ TJSONValueHelper } -{$ifndef DelphiXE7_UP} -function TJSONValueHelper.TryGetValue(const APath: string; - out AValue: T): Boolean; -var - LJSONValue: TJSONValue; - LPair: TJSONPair; -begin - LJSONValue := nil; - if Self is TJSONObject then - begin - LPair := TJSONObject(Self).Get(APath); - if Assigned(LPair) then - LJSONValue := LPair.JsonValue; - end; - Result := LJSONValue <> nil; - if Result then - begin - try - AValue := T(LJSONValue); - except - Result := False; - end; - end; -end; -{$endif} - -{$ifndef DelphiXE7_UP} -function TJSONValueHelper.ToJSON: string; -var - LBytes: TBytes; -begin - SetLength(LBytes, Length(ToString) * 6); - SetLength(LBytes, ToBytes(LBytes, 0)); - Result := TEncoding.Default.GetString(LBytes); -end; -{$endif} - -{ TJSONArrayEnumerator } - -{$ifndef DelphiXE6_UP} -constructor TJSONArrayEnumerator.Create(const AArray: TJSONArray); -begin - inherited Create; - FIndex := -1; - FArray := AArray; -end; - -function TJSONArrayEnumerator.GetCurrent: TJSONValue; -begin - Result := FArray.GetValue(FIndex); -end; - -function TJSONArrayEnumerator.MoveNext: Boolean; -begin - Result := FIndex < FArray.Count - 1; - if Result then - Inc(FIndex); -end; -{$endif} - -{ TJSONArrayHelper } - -function TJSONArrayHelper.ToArrayOfRecord: TArray; -var - LElement: TJSONValue; -begin - Result := []; - for LElement in Self do - Result := Result + [(LElement as TJSONObject).ToRecord()] -end; - -class function TJSONArrayHelper.ArrayOfObjectToJSON( - const AArray: TArray): TJSONArray; -begin - Result := TJSONArray.Create; - try - Result.FromArrayOfObject(AArray); - except - Result.Free; - raise; - end; -end; - -class function TJSONArrayHelper.ArrayOfRecordToJSON(const AArray: TArray; - const AFilterProc: TToJSONFilterProc): TJSONArray; -begin - Result := TJSONArray.Create; - try - Result.FromArrayOfRecord(AArray, AFilterProc); - except - Result.Free; - raise; - end; -end; - -function TJSONArrayHelper.ForEach(const AFunc: TFunc): Integer; -var - LIndex: Integer; - LItem: TJSONValue; -begin - Result := 0; - if not Assigned(AFunc) then - Exit; - for LIndex := 0 to Count-1 do - begin - LItem := Items[Lindex]; - if LItem is T then - begin - if not AFunc(T(LItem)) then - Break; - Inc(Result); - end; - end; -end; - -procedure TJSONArrayHelper.FromArrayOfObject(const AArray: TArray; - const AOptions: TJsonOptions = [joDateIsUTC, joDateFormatISO8601]); -var - LObject: T; - LObj: TJSONObject; -begin - // clear all - while Count > 0 do - Remove(0); - - for LObject in AArray do - AddElement(TJSONObject.ObjectToJSON(LObject, AOptions)); -end; - -procedure TJSONArrayHelper.FromArrayOfRecord(const AArray: TArray; - const AFilterProc: TToJSONFilterProc); -var - LRecord: T; - LObj: TJSONObject; -begin - // clear all - while Count > 0 do - Remove(0); - - for LRecord in AArray do - begin - LObj := TJSONObject.Create; - try - LObj.FromRecord(LRecord, AFilterProc); - AddElement(LObj); - except - LObj.Free; - raise; - end; - end; -end; - - -{$ifndef DelphiXE6_UP} - -function TJSONArrayHelper.GetCount: Integer; -begin - Result := Size; -end; - -function TJSONArrayHelper.GetEnumerator: TJSONArrayEnumerator; -begin - Result := TJSONArrayEnumerator.Create(Self); -end; - -function TJSONArrayHelper.GetValue(const Index: Integer): TJSONValue; -begin - Result := Get(Index); -end; -{$endif} - -{ TJSONObjectHelper } - -{$ifndef DelphiXE6_UP} -function TJSONObjectHelper.GetCount: Integer; -begin - Result := Size; -end; - -function TJSONObjectHelper.GetPair(const Index: Integer): TJSONPair; -begin - Result := Get(Index); -end; -{$endif} - -function TJSONObjectHelper.GetExactPairName( - const ACaseInsensitiveName: string): string; -var - LIndex: Integer; - LPair: TJSONPair; -begin - Result := ACaseInsensitiveName; - for LIndex := 0 to Count -1 do - begin - LPair := Pairs[LIndex]; - if SameText(LPair.JsonString.Value, ACaseInsensitiveName) then - begin - Result := LPair.JsonString.Value; - Exit; - end; - end; -end; - - -class function TJSONObjectHelper.JSONToObject(const AClassType: TClass; - const AJSON: TJSONObject; const AOptions: TJsonOptions): TObject; -var - LConstructor: TRttiMethod; -begin - Result := nil; - - LConstructor := TRTTIHelper.FindParameterLessConstructor(AClassType); - if not Assigned(LConstructor) then - Exit; - - Result := LConstructor.Invoke(AClassType, []).AsObject; - try - TJson.JsonToObject(Result, AJSON, AOptions); - except - Result.Free; - raise; - end; -end; - -class function TJSONObjectHelper.JSONToObject(const AJSON: TJSONObject; - const AOptions: TJsonOptions = [joDateIsUTC, joDateFormatISO8601]): T; -begin - Result := TJSON.JsonToObject(AJSON, AOptions); -end; - -class function TJSONObjectHelper.JSONToRecord(const ARecordType: TRttiType; - const AJSON: TJSONObject; const AFilterProc: TToRecordFilterProc): TValue; -begin - Assert(Assigned(AJSON)); - Result := AJSON.ToRecord(ARecordType, AFilterProc); -end; - -class function TJSONObjectHelper.JSONToRecord(const AJSON: TJSONObject; - const AFilterProc: TToRecordFilterProc = nil): T; -begin - Assert(Assigned(AJSON)); - Result := AJSON.ToRecord(AFilterProc); -end; - -class function TJSONObjectHelper.ObjectToJSON(const AObject: TObject; - const AOptions: TJsonOptions): TJSONObject; -begin - Result := TJSON.ObjectToJsonObject(AObject, AOptions); -end; - -function TJSONObjectHelper.ReadArrayValue(const AName: string): TJSONArray; -begin - Result := nil; - TryGetValue(AName, Result); -end; - -function TJSONObjectHelper.ReadArrayValue(const AName: string): TArray; -var - LArray: TJSONArray; -begin - LArray := ReadArrayValue(AName); - if Assigned(LArray) then - Result := LArray.ToArrayOfRecord - else - Result := []; -end; - -function TJSONObjectHelper.ReadBoolValue(const AName: string; const ADefault: Boolean): Boolean; -{$ifdef Delphi10Seattle_UP} -var - LValue: TJSONBool; -begin - Result := ADefault; - if Assigned(Self) and TryGetValue(AName, LValue) then - Result := LValue is TJSONTrue; -end; -{$else} -var - LValue: TJSONValue; -begin - Result := ADefault; - if Assigned(Self) and TryGetValue(AName, LValue) then - Result := LValue is TJSONTrue; -end; -{$endif} - - -function TJSONObjectHelper.ReadDateTimeValue(const AName: string; const ADefault: TDateTime; - const AReturnUTC: Boolean): TDateTime; -begin - Result := ADefault; - if Assigned(Self) then - Result := JSONToDate(ReadStringValue(AName), AReturnUTC); -end; - -function TJSONObjectHelper.ReadDoubleValue(const AName: string; - const ADefault: Double): Double; -var - LValue: TJSONNumber; -begin - Result := ADefault; - if Assigned(Self) and TryGetValue(AName, LValue) then - Result := LValue.AsDouble; -end; - -procedure TJSONObjectHelper.FromRecord(const ARecord: TValue; const AFilterProc: TToJSONFilterProc = nil); - - function GetRecordFilterProc(const ARecordType: TRttiType): TToJSONFilterProc; - var - LMethod: TRttiMethod; - begin - Result := nil; - // looking for TMyRecord.ToJSONFilter(const AField: TRttiField; const AObj: TJSONObject): Boolean; - - LMethod := ARecordType.FindMethodFunc('ToJSONFilter'); - if Assigned(LMethod) then - Result := - procedure (const AField: TRttiField; const ARecord: TValue; const AJSONObject: TJSONObject; var AAccept: Boolean) - begin - AAccept := LMethod.Invoke(ARecord, [AField, AJSONObject]).AsBoolean; - end; - end; - -var - LType: TRttiType; - LField: TRttiField; - LFilterProc: TToJSONFilterProc; - LAccept: Boolean; - LValue: TValue; - LJSONName: string; -begin - LType := TRttiContext.Create.GetType(ARecord.TypeInfo); - - LFilterProc := AFilterProc; - if not Assigned(LFilterProc) then - LFilterProc := GetRecordFilterProc(LType); - - for LField in LType.GetFields do - begin - LAccept := True; - if Assigned(LFilterProc) then - LFilterProc(LField, ARecord, Self, LAccept); - - if LAccept then - begin - LJSONName := LField.Name; - LField.HasAttribute( - procedure (AAttr: JSONNameAttribute) - begin - LJSONName := AAttr.Name; - end - ); - if LJSONName <> '' then - begin - LValue := LField.GetValue(ARecord.GetReferenceToRawData); - - {$ifdef Delphi10Tokyo_UP} - if LValue.IsType(False) and (not LValue.IsArray) then - {$else} - if LValue.IsType and (not LValue.IsArray) then - {$endif} - WriteTValue(LJSONName, LValue.AsType) //unboxing TValue from TValue - else - WriteTValue(LJSONName, LValue); - end; - end; - end; -end; - -procedure TJSONObjectHelper.FromRecord(ARecord: T; const AFilterProc: TToJSONFilterProc = nil); -begin - FromRecord(TValue.From(ARecord), AFilterProc); -end; - -{$ifdef DelphiXE6_UP} -function TJSONObjectHelper.ReadInt64Value(const AName: string; - const ADefault: Int64): Int64; -var - LValue: TJSONNumber; -begin - Result := ADefault; - if Assigned(Self) and TryGetValue(AName, LValue) then - Result := LValue.AsInt64; -end; -{$endif} - -function TJSONObjectHelper.ReadIntegerValue(const AName: string; - const ADefault: Integer): Integer; -var - LValue: TJSONNumber; -begin - Result := ADefault; - if Assigned(Self) and TryGetValue(AName, LValue) then - Result := LValue.AsInt; -end; - -function TJSONObjectHelper.ReadStringValue(const AName, - ADefault: string): string; -var - LPair: TJSONPair; -begin - Result := ADefault; - if not Assigned(Self) then - Exit; - -{$ifdef DelphiXE6_UP} - LPair := GetPairByName(AName); -{$else} - LPair := Get(AName); -{$endif} - if Assigned(LPair) then - Result := LPair.JsonValue.Value; -end; - -function TJSONObjectHelper.ReadUnixTimeValue(const AName: string; - const ADefault: TDateTime): TDateTime; -var - LValue: Int64; -begin - Result := ADefault; -{$ifdef DelphiXE6_UP} - LValue := ReadInt64Value(AName); -{$else} - LValue := ReadIntegerValue(AName); -{$endif} - if LValue <> 0 then - Result := UnixToDateTime(LValue) -end; - -function TJSONObjectHelper.ReadValue(const AName: string; - const ADesiredType: TRttiType; const ANameCaseSensitive: Boolean; - out AValue: TValue): Boolean; -var - LValue: TJSONValue; - LName: string; -begin - LName := AName; - if not ANameCaseSensitive then - LName := GetExactPairName(LName); - - Result := TryGetValue(LName, LValue); - if Result then - AValue := TJSONValueToTValue(LValue, ADesiredType); -end; - -function TJSONObjectHelper.ReadValue(const AName: string; - const ADefault: TValue; const ADesiredType: TRttiType; - const ANameCaseSensitive: Boolean): TValue; -begin - Result := ADefault; - ReadValue(AName, ADesiredType, ANameCaseSensitive, Result); -end; - -class function TJSONObjectHelper.RecordToJSON(const ARecord: TValue; - const AFilterProc: TToJSONFilterProc): TJSONObject; -begin - Result := TJSONObject.Create; - try - Result.FromRecord(ARecord, AFilterProc); - except - Result.Free; - raise; - end; -end; - -class function TJSONObjectHelper.RecordToJSON(ARecord: T; - const AFilterProc: TToJSONFilterProc): TJSONObject; -begin - Result := TJSONObject.Create; - try - Result.FromRecord(ARecord, AFilterProc); - except - Result.Free; - raise; - end; -end; - -class function TJSONObjectHelper.TJSONValueToTValue( - const AValue: TJSONValue; const ADesiredType: TRttiType): TValue; -var - LArray: TValue; - LElementType: TRttiType; - LJSONArray: TJSONArray; - LJSONElement: TJSONValue; - LIndex: Integer; -begin -{$ifdef Delphi10Berlin_UP} - if AValue is TJSONBool then // Boolean - Result := TJSONBool(AValue).AsBoolean -{$else} - if (AValue is TJSONTrue) or (AValue is TJSONFalse) then - Result := AValue is TJSONTrue -{$endif} -// else if ADesiredType.Handle = TypeInfo(Variant) then -// Result := TValue. - else if AValue is TJSONNumber then // Numbers (Integer and Float) - begin -{$ifdef DelphiXE6_UP} - if ADesiredType.TypeKind in [tkInt64] then - Result := TJSONNumber(AValue).AsInt64 - else -{$endif} - if ADesiredType.TypeKind in [tkInteger] then - Result := TJSONNumber(AValue).AsInt - else - begin - if ADesiredType.Handle = TypeInfo(TValue) then - Result := GuessTValueFromString(AValue.ToString) - else - Result := TJSONNumber(AValue).AsDouble; - end; - - end - else if AValue is TJSONString then - begin - if ADesiredType is TRttiEnumerationType then // enumerated types - Result := TValue.FromOrdinal(ADesiredType.Handle, GetEnumValue(ADesiredType.Handle, TJSONString(AValue).Value)) - else if (ADesiredType.Handle = TypeInfo(TDateTime)) // dates - or (ADesiredType.Handle = TypeInfo(TDate)) - or (ADesiredType.Handle = TypeInfo(TTime)) - then - Result := JSONToDate(TJSONString(AValue).Value) - else - begin // strings - if (ADesiredType.Handle = TypeInfo(TValue)) or (ADesiredType.Handle = TypeInfo(Variant)) then - Result := GuessTValueFromString(TJSONString(AValue).Value) - else - Result := TJSONString(AValue).Value; - end; - end - else if AValue is TJSONNull then // null values - Result := TValue.Empty - else if AValue is TJSONObject then - Result := TJSONObject(AValue).ToRecord(ADesiredType) - else if (AValue is TJSONArray) then - begin - LJSONArray := TJSONArray(AValue); - if ADesiredType.IsArray(LElementType) then - begin - TValue.Make(nil, ADesiredType.Handle, LArray); - SetArrayLength(LArray, ADesiredType, LJSONArray.Count); - for LIndex := 0 to LJSONArray.Count-1 do - begin - LJSONElement := LJSONArray.Items[LIndex]; - LArray.SetArrayElement(LIndex, TJSONValueToTValue(LJSONElement, LElementType)); - end; - Result := LArray; - end; - end - else - raise Exception.CreateFmt('Unable to put JSON Value [%s] in TValue', [AValue.ClassName]); -end; - -function TJSONObjectHelper.ToRecord(const ARecordType: TRttiType; - const AFilterProc: TToRecordFilterProc = nil): TValue; -var - LField: TRttiField; - LValue: TValue; - LRecordInstance: Pointer; - LFilterProc: TToRecordFilterProc; - LAccept: Boolean; - LJSONName: string; - LAssignedValuesField: TRttiField; - LAssignedValues: TArray; - - function GetRecordFilterProc: TToRecordFilterProc; - var - LMethod: TRttiMethod; - begin - Result := nil; - // looking for TMyRecord.ToRecordFilter(const AField: TRttiField; const AObj: TJSONObject): Boolean; - - LMethod := ARecordType.FindMethodFunc('ToRecordFilter'); - if Assigned(LMethod) then - Result := - procedure (const AField: TRttiField; const ARecord: TValue; const AJSONObject: TJSONObject; var AAccept: Boolean) - begin - AAccept := LMethod.Invoke(ARecord, [AField, AJSONObject]).AsBoolean; - end; - end; - -begin - TValue.Make(nil, ARecordType.Handle, Result); - LRecordInstance := Result.GetReferenceToRawData; - - LFilterProc := AFilterProc; - if not Assigned(LFilterProc) then - LFilterProc := GetRecordFilterProc(); - - LAssignedValuesField := ARecordType.GetField('_AssignedValues'); - if Assigned(LAssignedValuesField) - and not LAssignedValuesField.FieldType.IsDynamicArrayOf - then - LAssignedValuesField := nil; - LAssignedValues := []; - - for LField in ARecordType.GetFields do - begin - LAccept := True; - if Assigned(LFilterProc) then - LFilterProc(LField, Result, Self, LAccept); - - if LAccept then - begin - LJSONName := LField.Name; - LField.HasAttribute( - procedure (AAttr: JSONNameAttribute) - begin - LJSONName := AAttr.Name; - end - ); - if LJSONName <> '' then - begin - if ReadValue(LJSONName, LField.FieldType, True, LValue) then - begin - LField.SetValue(LRecordInstance, LValue); - LAssignedValues := LAssignedValues + [LField.Name]; - end - else - LField.SetValue(LRecordInstance, TValue.Empty); - end; - end; - end; - if Assigned(LAssignedValuesField) then - LAssignedValuesField.SetValue(LRecordInstance, TValue.From>(LAssignedValues)); -end; - -function TJSONObjectHelper.ToRecord(const AFilterProc: TToRecordFilterProc = nil): T; -begin - Result := ToRecord(TRttiContext.Create.GetType(TypeInfo(T)), AFilterProc).AsType; -end; - -procedure TJSONObjectHelper.WriteArrayValue(const AName: string; - const AArray: TJSONArray); -begin - AddPair(AName, AArray); -end; - -procedure TJSONObjectHelper.WriteArrayValue(const AName: string; - const AArray: TArray); -begin - WriteArrayValue(AName, TJSONArray.ArrayOfRecordToJSON(AArray)); -end; - -procedure TJSONObjectHelper.WriteBoolValue(const AName: string; - const AValue: Boolean); -var - LDummy: TJSONValue; -begin - if TryGetValue(AName, LDummy) then - RemovePair(AName); - - AddPair(AName, BooleanToTJSON(AValue)); -end; - -procedure TJSONObjectHelper.WriteDateTimeValue(const AName: string; - const AValue: TDateTime; const AInputIsUTC: Boolean); -begin - WriteStringValue(AName, DateToJSON(AValue, AInputIsUTC)); -end; - -procedure TJSONObjectHelper.WriteDoubleValue(const AName: string; - const AValue: Double); -var - LDummy: TJSONValue; -begin - if TryGetValue(AName, LDummy) then - RemovePair(AName); - - AddPair(AName, TJSONNumber.Create(AValue)); -end; - -procedure TJSONObjectHelper.WriteInt64Value(const AName: string; - const AValue: Int64); -var - LDummy: TJSONValue; -begin - if TryGetValue(AName, LDummy) then - RemovePair(AName); - - AddPair(AName, TJSONNumber.Create(AValue)); -end; - -procedure TJSONObjectHelper.WriteIntegerValue(const AName: string; - const AValue: Integer); -var - LDummy: TJSONValue; -begin - if TryGetValue(AName, LDummy) then - RemovePair(AName); - - AddPair(AName, TJSONNumber.Create(AValue)); -end; - -procedure TJSONObjectHelper.WriteStringValue(const AName, AValue: string); -var - LDummy: TJSONValue; -begin - if TryGetValue(AName, LDummy) then - RemovePair(AName); - - if AValue <> '' then - AddPair(AName, TJSONString.Create(AValue)); -end; - -procedure TJSONObjectHelper.WriteTValue(const AName: string; - const AValue: TValue); -begin - AddPair(AName, TValueToJSONValue(AValue)); -end; - -procedure TJSONObjectHelper.WriteUnixTimeValue(const AName: string; - const AValue: TDateTime); -begin - WriteInt64Value(AName, DateTimeToUnix(AValue)); -end; - -{ JSONNameAttribute } - -constructor JSONNameAttribute.Create(const AName: string); -begin - inherited Create; - FName := AName; -end; - -end. \ No newline at end of file +(* + Copyright 2016, MARS-Curiosity library + + Home: https://github.com/andrea-magni/MARS +*) +unit MARS.Core.JSON; + +{$I MARS.inc} + +interface + +uses +{$IFDEF Delphi10Rio_UP} + Generics.Collections, +{$ENDIF} +{$ifdef DelphiXE6_UP} + JSON +{$else} + DBXJSON +{$endif} + , SysUtils +{$ifdef DelphiXE2_UP} + , System.Rtti +{$else} + , Rtti +{$endif} + , TypInfo, REST.JSON +; + +type + TJSONAncestor = {$ifdef DelphiXE6_UP}JSON.TJSONAncestor{$else}DBXJSON.TJSONAncestor{$endif}; + TJSONPair = {$ifdef DelphiXE6_UP}JSON.TJSONPair{$else}DBXJSON.TJSONPair{$endif}; + TJSONValue = {$ifdef DelphiXE6_UP}JSON.TJSONValue{$else}DBXJSON.TJSONValue{$endif}; + TJSONTrue = {$ifdef DelphiXE6_UP}JSON.TJSONTrue{$else}DBXJSON.TJSONTrue{$endif}; + TJSONString = {$ifdef DelphiXE6_UP}JSON.TJSONString{$else}DBXJSON.TJSONString{$endif}; + TJSONNumber = {$ifdef DelphiXE6_UP}JSON.TJSONNumber{$else}DBXJSON.TJSONNumber{$endif}; + TJSONObject = {$ifdef DelphiXE6_UP}JSON.TJSONObject{$else}DBXJSON.TJSONObject{$endif}; + TJSONNull = {$ifdef DelphiXE6_UP}JSON.TJSONNull{$else}DBXJSON.TJSONNull{$endif}; + TJSONFalse = {$ifdef DelphiXE6_UP}JSON.TJSONFalse{$else}DBXJSON.TJSONFalse{$endif}; + TJSONArray = {$ifdef DelphiXE6_UP}JSON.TJSONArray{$else}DBXJSON.TJSONArray{$endif}; + + TJSONValueHelper = class helper for TJSONValue + public +{$ifndef DelphiXE7_UP} + function TryGetValue(const APath: string; out AValue: T): Boolean; overload; + function ToJSON: string; +{$endif} + end; + + JSONNameAttribute = class(TCustomAttribute) + private + FName: string; + public + constructor Create(const AName: string); + property Name: string read FName; + end; + + TToRecordFilterProc = reference to procedure (const AField: TRttiField; + const ARecord: TValue; const AJSONObject: TJSONObject; var AAccept: Boolean); + + TToJSONFilterProc = reference to procedure (const AField: TRttiField; + const ARecord: TValue; const AJSONObject: TJSONObject; var AAccept: Boolean); + + TJSONRawString = type string; + +{$ifndef DelphiXE6_UP} + TJSONArrayEnumerator = class + private + FIndex: Integer; + FArray: TJSONArray; + public + constructor Create(const AArray: TJSONArray); + function GetCurrent: TJSONValue; inline; + function MoveNext: Boolean; + property Current: TJSONValue read GetCurrent; + end; +{$endif} + + TJSONArrayHelper= class helper for TJSONArray + private + {$ifndef DelphiXE6_UP} + function GetCount: Integer; inline; + function GetValue(const Index: Integer): TJSONValue; inline; + {$endif} + public + function ToArrayOfRecord(): TArray; + procedure FromArrayOfRecord(const AArray: TArray; + const AFilterProc: TToJSONFilterProc = nil); + procedure FromArrayOfObject(const AArray: TArray; + const AOptions: TJsonOptions = [joDateIsUTC, joDateFormatISO8601]); + function ForEach(const AFunc: TFunc): Integer; + + {$ifndef DelphiXE6_UP} + function GetEnumerator: TJSONArrayEnumerator; + + property Count: Integer read GetCount; + property Items[const Index: Integer]: TJSONValue read GetValue; + {$endif} + + class function ArrayOfRecordToJSON(const AArray: TArray; const AFilterProc: TToJSONFilterProc = nil): TJSONArray; + class function ArrayOfObjectToJSON(const AArray: TArray): TJSONArray; + end; + + TJSONObjectHelper = class helper(TJSONValueHelper) for TJSONObject + private +{$ifndef DelphiXE6_UP} + function GetCount: Integer; inline; + function GetPair(const Index: Integer): TJSONPair; inline; +{$endif} + function GetExactPairName(const ACaseInsensitiveName: string): string; + public + function ReadStringValue(const AName: string; const ADefault: string = ''): string; + function ReadIntegerValue(const AName: string; const ADefault: Integer = 0): Integer; +{$ifdef DelphiXE6_UP} + function ReadInt64Value(const AName: string; const ADefault: Int64 = 0): Int64; +{$endif} + function ReadDoubleValue(const AName: string; const ADefault: Double = 0.0): Double; + function ReadBoolValue(const AName: string; const ADefault: Boolean = False): Boolean; + function ReadDateTimeValue(const AName: string; const ADefault: TDateTime = 0.0; + const AReturnUTC: Boolean = False): TDateTime; + function ReadUnixTimeValue(const AName: string; const ADefault: TDateTime = 0.0): TDateTime; + function ReadValue(const AName: string; const ADefault: TValue; + const ADesiredType: TRttiType; const ANameCaseSensitive: Boolean = True): TValue; overload; + function ReadValue(const AName: string; const ADesiredType: TRttiType; + const ANameCaseSensitive: Boolean; out AValue: TValue): Boolean; overload; + function ReadArrayValue(const AName: string): TJSONArray; overload; inline; + function ReadArrayValue(const AName: string): TArray; overload; inline; + + procedure WriteStringValue(const AName: string; const AValue: string); + procedure WriteIntegerValue(const AName: string; const AValue: Integer); + procedure WriteInt64Value(const AName: string; const AValue: Int64); + procedure WriteDoubleValue(const AName: string; const AValue: Double); + procedure WriteBoolValue(const AName: string; const AValue: Boolean); + procedure WriteDateTimeValue(const AName: string; const AValue: TDateTime; + const AInputIsUTC: Boolean = False); + procedure WriteUnixTimeValue(const AName: string; const AValue: TDateTime); + procedure WriteTValue(const AName: string; const AValue: TValue); + procedure WriteArrayValue(const AName: string; const AArray: TJSONArray); overload; inline; + procedure WriteArrayValue(const AName: string; const AArray: TArray); overload; inline; + + procedure FromRecord(ARecord: T; const AFilterProc: TToJSONFilterProc = nil); overload; + procedure FromRecord(const ARecord: TValue; const AFilterProc: TToJSONFilterProc = nil); overload; + function ToRecord(const AFilterProc: TToRecordFilterProc = nil): T; overload; + function ToRecord(const ARecordType: TRttiType; + const AFilterProc: TToRecordFilterProc = nil): TValue; overload; + +{$ifndef DelphiXE6_UP} + property Count: Integer read GetCount; + property Pairs[const Index: Integer]: TJSONPair read GetPair; +{$endif} + + class function ObjectToJSON(const AObject: TObject; + const AOptions: TJsonOptions = [joDateIsUTC, joDateFormatISO8601]): TJSONObject; overload; + + class function JSONToObject(const AJSON: TJSONObject; + const AOptions: TJsonOptions = [joDateIsUTC, joDateFormatISO8601]): T; overload; + + class function JSONToObject(const AClassType: TClass; const AJSON: TJSONObject; + const AOptions: TJsonOptions = [joDateIsUTC, joDateFormatISO8601]): TObject; overload; + + class function RecordToJSON(ARecord: T; + const AFilterProc: TToJSONFilterProc = nil): TJSONObject; overload; + class function RecordToJSON(const ARecord: TValue; + const AFilterProc: TToJSONFilterProc = nil): TJSONObject; overload; + + class function JSONToRecord(const AJSON: TJSONObject; + const AFilterProc: TToRecordFilterProc = nil): T; overload; + class function JSONToRecord(const ARecordType: TRttiType; const AJSON: TJSONObject; + const AFilterProc: TToRecordFilterProc = nil): TValue; overload; + + class function TValueToJSONValue(const AValue: TValue): TJSONValue; + class function TJSONValueToTValue(const AValue: TJSONValue; const ADesiredType: TRttiType): TValue; + end; + + function StringArrayToJsonArray(const AStringArray: TArray): TJSONArray; + function JsonArrayToStringArray(const AJSONArray: TJSONArray): TArray; + +implementation + +uses + DateUtils, Variants, StrUtils + , MARS.Core.Utils + , MARS.Rtti.Utils +; + +class function TJSONObjectHelper.TValueToJSONValue( + const AValue: TValue): TJSONValue; +var + LArray: TJSONArray; + LIndex: Integer; +begin + if AValue.IsEmpty and not AValue.IsArray then + Result := TJSONNull.Create + + else if (AValue.Kind in [tkString, tkUString, tkChar, {$ifdef DelphiXE6_UP} tkWideChar, {$endif} tkLString, tkWString]) then + Result := TJSONString.Create(AValue.AsString) + + else if AValue.IsArray then + begin + LArray := TJSONArray.Create; + try + for LIndex := 0 to AValue.GetArrayLength-1 do + LArray.AddElement(TValueToJSONValue(AValue.GetArrayElement(LIndex))); + + Result := LArray; + except + LArray.Free; + raise; + end; + end + + else if (AValue.Kind in [tkRecord]) then + Result := TJSONObject.RecordToJSON(AValue) + + else if (AValue.IsType) then + Result := BooleanToTJSON(AValue.AsType) + + else if AValue.TypeInfo = TypeInfo(TDateTime) then + Result := TJSONString.Create( DateToJSON(AValue.AsType) ) + else if AValue.TypeInfo = TypeInfo(TDate) then + Result := TJSONString.Create( DateToJSON(AValue.AsType) ) + else if AValue.TypeInfo = TypeInfo(TTime) then + Result := TJSONString.Create( DateToJSON(AValue.AsType) ) + + else if (AValue.Kind in [tkInt64]) then + Result := TJSONNumber.Create( AValue.AsType ) + else if (AValue.Kind in [tkInteger]) then + Result := TJSONNumber.Create( AValue.AsType ) + + else if (AValue.Kind in [tkFloat]) then + Result := TJSONNumber.Create( AValue.AsType ) + + else if (AValue.Kind in [tkVariant]) then + Result := TValueToJSONValue( TValue.FromVariant(AValue.AsVariant) ) + + else if (AValue.IsInstanceOf(TObject)) then + Result := ObjectToJSON(AValue.AsObject) + + else + Result := TJSONString.Create(AValue.ToString); + +end; + +function StringArrayToJsonArray(const AStringArray: TArray): TJSONArray; +var + LIndex: Integer; +begin + Result := TJSONArray.Create; + try + for LIndex := Low(AStringArray) to High(AStringArray) do + Result.Add(AStringArray[LIndex]); + except + Result.Free; + raise; + end; +end; + +function JsonArrayToStringArray(const AJSONArray: TJSONArray): TArray; +var + LElement: TJSONValue; + LIndex: Integer; +begin + SetLength(Result, AJSONArray.Count); + + for LIndex := 0 to AJSONArray.Count-1 do + begin + LElement := AJSONArray.Items[LIndex]; + if LElement is TJSONString then + Result[LIndex] := TJSONString(LElement).Value + else if LElement is TJSONNumber then + Result[LIndex] := TJSONNumber(LElement).ToString + else if LElement is TJSONTrue then + Result[LIndex] := 'true' + else if LElement is TJSONFalse then + Result[LIndex] := 'false' + else + Result[LIndex] := LElement.ToString; + end; +end; + +{ TJSONValueHelper } +{$ifndef DelphiXE7_UP} +function TJSONValueHelper.TryGetValue(const APath: string; + out AValue: T): Boolean; +var + LJSONValue: TJSONValue; + LPair: TJSONPair; +begin + LJSONValue := nil; + if Self is TJSONObject then + begin + LPair := TJSONObject(Self).Get(APath); + if Assigned(LPair) then + LJSONValue := LPair.JsonValue; + end; + Result := LJSONValue <> nil; + if Result then + begin + try + AValue := T(LJSONValue); + except + Result := False; + end; + end; +end; +{$endif} + +{$ifndef DelphiXE7_UP} +function TJSONValueHelper.ToJSON: string; +var + LBytes: TBytes; +begin + SetLength(LBytes, Length(ToString) * 6); + SetLength(LBytes, ToBytes(LBytes, 0)); + Result := TEncoding.Default.GetString(LBytes); +end; +{$endif} + +{ TJSONArrayEnumerator } + +{$ifndef DelphiXE6_UP} +constructor TJSONArrayEnumerator.Create(const AArray: TJSONArray); +begin + inherited Create; + FIndex := -1; + FArray := AArray; +end; + +function TJSONArrayEnumerator.GetCurrent: TJSONValue; +begin + Result := FArray.GetValue(FIndex); +end; + +function TJSONArrayEnumerator.MoveNext: Boolean; +begin + Result := FIndex < FArray.Count - 1; + if Result then + Inc(FIndex); +end; +{$endif} + +{ TJSONArrayHelper } + +function TJSONArrayHelper.ToArrayOfRecord: TArray; +var + LElement: TJSONValue; +begin + Result := []; + for LElement in Self do + Result := Result + [(LElement as TJSONObject).ToRecord()] +end; + +class function TJSONArrayHelper.ArrayOfObjectToJSON( + const AArray: TArray): TJSONArray; +begin + Result := TJSONArray.Create; + try + Result.FromArrayOfObject(AArray); + except + Result.Free; + raise; + end; +end; + +class function TJSONArrayHelper.ArrayOfRecordToJSON(const AArray: TArray; + const AFilterProc: TToJSONFilterProc): TJSONArray; +begin + Result := TJSONArray.Create; + try + Result.FromArrayOfRecord(AArray, AFilterProc); + except + Result.Free; + raise; + end; +end; + +function TJSONArrayHelper.ForEach(const AFunc: TFunc): Integer; +var + LIndex: Integer; + LItem: TJSONValue; +begin + Result := 0; + if not Assigned(AFunc) then + Exit; + for LIndex := 0 to Count-1 do + begin + LItem := Items[Lindex]; + if LItem is T then + begin + if not AFunc(T(LItem)) then + Break; + Inc(Result); + end; + end; +end; + +procedure TJSONArrayHelper.FromArrayOfObject(const AArray: TArray; + const AOptions: TJsonOptions = [joDateIsUTC, joDateFormatISO8601]); +var + LObject: T; + LObj: TJSONObject; +begin + // clear all + while Count > 0 do + Remove(0); + + for LObject in AArray do + AddElement(TJSONObject.ObjectToJSON(LObject, AOptions)); +end; + +procedure TJSONArrayHelper.FromArrayOfRecord(const AArray: TArray; + const AFilterProc: TToJSONFilterProc); +var + LRecord: T; + LObj: TJSONObject; +begin + // clear all + while Count > 0 do + Remove(0); + + for LRecord in AArray do + begin + LObj := TJSONObject.Create; + try + LObj.FromRecord(LRecord, AFilterProc); + AddElement(LObj); + except + LObj.Free; + raise; + end; + end; +end; + + +{$ifndef DelphiXE6_UP} + +function TJSONArrayHelper.GetCount: Integer; +begin + Result := Size; +end; + +function TJSONArrayHelper.GetEnumerator: TJSONArrayEnumerator; +begin + Result := TJSONArrayEnumerator.Create(Self); +end; + +function TJSONArrayHelper.GetValue(const Index: Integer): TJSONValue; +begin + Result := Get(Index); +end; +{$endif} + +{ TJSONObjectHelper } + +{$ifndef DelphiXE6_UP} +function TJSONObjectHelper.GetCount: Integer; +begin + Result := Size; +end; + +function TJSONObjectHelper.GetPair(const Index: Integer): TJSONPair; +begin + Result := Get(Index); +end; +{$endif} + +function TJSONObjectHelper.GetExactPairName( + const ACaseInsensitiveName: string): string; +var + LIndex: Integer; + LPair: TJSONPair; +begin + Result := ACaseInsensitiveName; + for LIndex := 0 to Count -1 do + begin + LPair := Pairs[LIndex]; + if SameText(LPair.JsonString.Value, ACaseInsensitiveName) then + begin + Result := LPair.JsonString.Value; + Exit; + end; + end; +end; + + +class function TJSONObjectHelper.JSONToObject(const AClassType: TClass; + const AJSON: TJSONObject; const AOptions: TJsonOptions): TObject; +var + LConstructor: TRttiMethod; +begin + Result := nil; + + LConstructor := TRTTIHelper.FindParameterLessConstructor(AClassType); + if not Assigned(LConstructor) then + Exit; + + Result := LConstructor.Invoke(AClassType, []).AsObject; + try + TJson.JsonToObject(Result, AJSON, AOptions); + except + Result.Free; + raise; + end; +end; + +class function TJSONObjectHelper.JSONToObject(const AJSON: TJSONObject; + const AOptions: TJsonOptions = [joDateIsUTC, joDateFormatISO8601]): T; +begin + Result := TJSON.JsonToObject(AJSON, AOptions); +end; + +class function TJSONObjectHelper.JSONToRecord(const ARecordType: TRttiType; + const AJSON: TJSONObject; const AFilterProc: TToRecordFilterProc): TValue; +begin + Assert(Assigned(AJSON)); + Result := AJSON.ToRecord(ARecordType, AFilterProc); +end; + +class function TJSONObjectHelper.JSONToRecord(const AJSON: TJSONObject; + const AFilterProc: TToRecordFilterProc = nil): T; +begin + Assert(Assigned(AJSON)); + Result := AJSON.ToRecord(AFilterProc); +end; + +class function TJSONObjectHelper.ObjectToJSON(const AObject: TObject; + const AOptions: TJsonOptions): TJSONObject; +begin + Result := TJSON.ObjectToJsonObject(AObject, AOptions); +end; + +function TJSONObjectHelper.ReadArrayValue(const AName: string): TJSONArray; +begin + Result := nil; + TryGetValue(AName, Result); +end; + +function TJSONObjectHelper.ReadArrayValue(const AName: string): TArray; +var + LArray: TJSONArray; +begin + LArray := ReadArrayValue(AName); + if Assigned(LArray) then + Result := LArray.ToArrayOfRecord + else + Result := []; +end; + +function TJSONObjectHelper.ReadBoolValue(const AName: string; const ADefault: Boolean): Boolean; +{$ifdef Delphi10Seattle_UP} +var + LValue: TJSONBool; +begin + Result := ADefault; + if Assigned(Self) and TryGetValue(AName, LValue) then + Result := LValue is TJSONTrue; +end; +{$else} +var + LValue: TJSONValue; +begin + Result := ADefault; + if Assigned(Self) and TryGetValue(AName, LValue) then + Result := LValue is TJSONTrue; +end; +{$endif} + + +function TJSONObjectHelper.ReadDateTimeValue(const AName: string; const ADefault: TDateTime; + const AReturnUTC: Boolean): TDateTime; +begin + Result := ADefault; + if Assigned(Self) then + Result := JSONToDate(ReadStringValue(AName), AReturnUTC, ADefault); +end; + +function TJSONObjectHelper.ReadDoubleValue(const AName: string; + const ADefault: Double): Double; +var + LValue: TJSONNumber; +begin + Result := ADefault; + if Assigned(Self) and TryGetValue(AName, LValue) then + Result := LValue.AsDouble; +end; + +procedure TJSONObjectHelper.FromRecord(const ARecord: TValue; const AFilterProc: TToJSONFilterProc = nil); + + function GetRecordFilterProc(const ARecordType: TRttiType): TToJSONFilterProc; + var + LMethod: TRttiMethod; + begin + Result := nil; + // looking for TMyRecord.ToJSONFilter(const AField: TRttiField; const AObj: TJSONObject): Boolean; + + LMethod := ARecordType.FindMethodFunc('ToJSONFilter'); + if Assigned(LMethod) then + Result := + procedure (const AField: TRttiField; const ARecord: TValue; const AJSONObject: TJSONObject; var AAccept: Boolean) + begin + AAccept := LMethod.Invoke(ARecord, [AField, AJSONObject]).AsBoolean; + end; + end; + +var + LType: TRttiType; + LField: TRttiField; + LFilterProc: TToJSONFilterProc; + LAccept: Boolean; + LValue: TValue; + LJSONName: string; +begin + LType := TRttiContext.Create.GetType(ARecord.TypeInfo); + + LFilterProc := AFilterProc; + if not Assigned(LFilterProc) then + LFilterProc := GetRecordFilterProc(LType); + + for LField in LType.GetFields do + begin + LAccept := True; + if Assigned(LFilterProc) then + LFilterProc(LField, ARecord, Self, LAccept); + + if LAccept then + begin + LJSONName := LField.Name; + LField.HasAttribute( + procedure (AAttr: JSONNameAttribute) + begin + LJSONName := AAttr.Name; + end + ); + if LJSONName <> '' then + begin + LValue := LField.GetValue(ARecord.GetReferenceToRawData); + + {$ifdef Delphi10Tokyo_UP} + if LValue.IsType(False) and (not LValue.IsArray) then + {$else} + if LValue.IsType and (not LValue.IsArray) then + {$endif} + WriteTValue(LJSONName, LValue.AsType) //unboxing TValue from TValue + else + WriteTValue(LJSONName, LValue); + end; + end; + end; +end; + +procedure TJSONObjectHelper.FromRecord(ARecord: T; const AFilterProc: TToJSONFilterProc = nil); +begin + FromRecord(TValue.From(ARecord), AFilterProc); +end; + +{$ifdef DelphiXE6_UP} +function TJSONObjectHelper.ReadInt64Value(const AName: string; + const ADefault: Int64): Int64; +var + LValue: TJSONNumber; +begin + Result := ADefault; + if Assigned(Self) and TryGetValue(AName, LValue) then + Result := LValue.AsInt64; +end; +{$endif} + +function TJSONObjectHelper.ReadIntegerValue(const AName: string; + const ADefault: Integer): Integer; +var + LValue: TJSONNumber; +begin + Result := ADefault; + if Assigned(Self) and TryGetValue(AName, LValue) then + Result := LValue.AsInt; +end; + +function TJSONObjectHelper.ReadStringValue(const AName, + ADefault: string): string; +var + LPair: TJSONPair; +begin + Result := ADefault; + if not Assigned(Self) then + Exit; + +{$ifdef DelphiXE6_UP} + LPair := GetPairByName(AName); +{$else} + LPair := Get(AName); +{$endif} + if Assigned(LPair) then + Result := LPair.JsonValue.Value; +end; + +function TJSONObjectHelper.ReadUnixTimeValue(const AName: string; + const ADefault: TDateTime): TDateTime; +var + LValue: Int64; +begin + Result := ADefault; +{$ifdef DelphiXE6_UP} + LValue := ReadInt64Value(AName); +{$else} + LValue := ReadIntegerValue(AName); +{$endif} + if LValue <> 0 then + Result := UnixToDateTime(LValue) +end; + +function TJSONObjectHelper.ReadValue(const AName: string; + const ADesiredType: TRttiType; const ANameCaseSensitive: Boolean; + out AValue: TValue): Boolean; +var + LValue: TJSONValue; + LName: string; +begin + LName := AName; + if not ANameCaseSensitive then + LName := GetExactPairName(LName); + + Result := TryGetValue(LName, LValue); + if Result then + AValue := TJSONValueToTValue(LValue, ADesiredType); +end; + +function TJSONObjectHelper.ReadValue(const AName: string; + const ADefault: TValue; const ADesiredType: TRttiType; + const ANameCaseSensitive: Boolean): TValue; +begin + Result := ADefault; + ReadValue(AName, ADesiredType, ANameCaseSensitive, Result); +end; + +class function TJSONObjectHelper.RecordToJSON(const ARecord: TValue; + const AFilterProc: TToJSONFilterProc): TJSONObject; +begin + Result := TJSONObject.Create; + try + Result.FromRecord(ARecord, AFilterProc); + except + Result.Free; + raise; + end; +end; + +class function TJSONObjectHelper.RecordToJSON(ARecord: T; + const AFilterProc: TToJSONFilterProc): TJSONObject; +begin + Result := TJSONObject.Create; + try + Result.FromRecord(ARecord, AFilterProc); + except + Result.Free; + raise; + end; +end; + +class function TJSONObjectHelper.TJSONValueToTValue( + const AValue: TJSONValue; const ADesiredType: TRttiType): TValue; +var + LArray: TValue; + LElementType: TRttiType; + LJSONArray: TJSONArray; + LJSONElement: TJSONValue; + LIndex: Integer; + LNewLength: NativeInt; +begin +{$ifdef Delphi10Berlin_UP} + if AValue is TJSONBool then // Boolean + Result := TJSONBool(AValue).AsBoolean +{$else} + if (AValue is TJSONTrue) or (AValue is TJSONFalse) then + Result := AValue is TJSONTrue +{$endif} +// else if ADesiredType.Handle = TypeInfo(Variant) then +// Result := TValue. + else if AValue is TJSONNumber then // Numbers (Integer and Float) + begin +{$ifdef DelphiXE6_UP} + if ADesiredType.TypeKind in [tkInt64] then + Result := TJSONNumber(AValue).AsInt64 + else +{$endif} + if ADesiredType.TypeKind in [tkInteger] then + Result := TJSONNumber(AValue).AsInt + else + begin + if ADesiredType.Handle = TypeInfo(TValue) then + Result := GuessTValueFromString(AValue.ToString) + else + Result := TJSONNumber(AValue).AsDouble; + end; + + end + else if AValue is TJSONString then + begin + if ADesiredType is TRttiEnumerationType then // enumerated types + Result := TValue.FromOrdinal(ADesiredType.Handle, GetEnumValue(ADesiredType.Handle, TJSONString(AValue).Value)) + else if (ADesiredType.Handle = TypeInfo(TDateTime)) // dates + or (ADesiredType.Handle = TypeInfo(TDate)) + or (ADesiredType.Handle = TypeInfo(TTime)) + then + Result := JSONToDate(TJSONString(AValue).Value) + else + begin // strings + if (ADesiredType.Handle = TypeInfo(TValue)) or (ADesiredType.Handle = TypeInfo(Variant)) then + Result := GuessTValueFromString(TJSONString(AValue).Value) + else + Result := TJSONString(AValue).Value; + end; + end + else if AValue is TJSONNull then // null values + Result := TValue.Empty + else if AValue is TJSONObject then + Result := TJSONObject(AValue).ToRecord(ADesiredType) + else if (AValue is TJSONArray) then + begin + LJSONArray := TJSONArray(AValue); + if ADesiredType.IsArray(LElementType) then + begin + TValue.Make(nil, ADesiredType.Handle, LArray); + LNewLength := LJSONArray.Count; + SetArrayLength(LArray, ADesiredType, @LNewLength); + //------------------------ + for LIndex := 0 to LJSONArray.Count-1 do + begin + LJSONElement := LJSONArray.Items[LIndex]; + LArray.SetArrayElement(LIndex, TJSONValueToTValue(LJSONElement, LElementType)); + end; + Result := LArray; + end; + end + else + raise Exception.CreateFmt('Unable to put JSON Value [%s] in TValue', [AValue.ClassName]); +end; + +function TJSONObjectHelper.ToRecord(const ARecordType: TRttiType; + const AFilterProc: TToRecordFilterProc = nil): TValue; +var + LField: TRttiField; + LValue: TValue; + LRecordInstance: Pointer; + LFilterProc: TToRecordFilterProc; + LAccept: Boolean; + LJSONName: string; + LAssignedValuesField: TRttiField; + LAssignedValues: TArray; + + function GetRecordFilterProc: TToRecordFilterProc; + var + LMethod: TRttiMethod; + begin + Result := nil; + // looking for TMyRecord.ToRecordFilter(const AField: TRttiField; const AObj: TJSONObject): Boolean; + + LMethod := ARecordType.FindMethodFunc('ToRecordFilter'); + if Assigned(LMethod) then + Result := + procedure (const AField: TRttiField; const ARecord: TValue; const AJSONObject: TJSONObject; var AAccept: Boolean) + begin + AAccept := LMethod.Invoke(ARecord, [AField, AJSONObject]).AsBoolean; + end; + end; + +begin + TValue.Make(nil, ARecordType.Handle, Result); + LRecordInstance := Result.GetReferenceToRawData; + + LFilterProc := AFilterProc; + if not Assigned(LFilterProc) then + LFilterProc := GetRecordFilterProc(); + + LAssignedValuesField := ARecordType.GetField('_AssignedValues'); + if Assigned(LAssignedValuesField) + and not LAssignedValuesField.FieldType.IsDynamicArrayOf + then + LAssignedValuesField := nil; + LAssignedValues := []; + + for LField in ARecordType.GetFields do + begin + LAccept := True; + if Assigned(LFilterProc) then + LFilterProc(LField, Result, Self, LAccept); + + if LAccept then + begin + LJSONName := LField.Name; + LField.HasAttribute( + procedure (AAttr: JSONNameAttribute) + begin + LJSONName := AAttr.Name; + end + ); + if LJSONName <> '' then + begin + if ReadValue(LJSONName, LField.FieldType, True, LValue) then + begin + LField.SetValue(LRecordInstance, LValue); + LAssignedValues := LAssignedValues + [LField.Name]; + end + else + LField.SetValue(LRecordInstance, TValue.Empty); + end; + end; + end; + if Assigned(LAssignedValuesField) then + LAssignedValuesField.SetValue(LRecordInstance, TValue.From>(LAssignedValues)); +end; + +function TJSONObjectHelper.ToRecord(const AFilterProc: TToRecordFilterProc = nil): T; +begin + Result := ToRecord(TRttiContext.Create.GetType(TypeInfo(T)), AFilterProc).AsType; +end; + +procedure TJSONObjectHelper.WriteArrayValue(const AName: string; + const AArray: TJSONArray); +begin + AddPair(AName, AArray); +end; + +procedure TJSONObjectHelper.WriteArrayValue(const AName: string; + const AArray: TArray); +begin + WriteArrayValue(AName, TJSONArray.ArrayOfRecordToJSON(AArray)); +end; + +procedure TJSONObjectHelper.WriteBoolValue(const AName: string; + const AValue: Boolean); +var + LDummy: TJSONValue; +begin + if TryGetValue(AName, LDummy) then + RemovePair(AName); + + AddPair(AName, BooleanToTJSON(AValue)); +end; + +procedure TJSONObjectHelper.WriteDateTimeValue(const AName: string; + const AValue: TDateTime; const AInputIsUTC: Boolean); +begin + WriteStringValue(AName, DateToJSON(AValue, AInputIsUTC)); +end; + +procedure TJSONObjectHelper.WriteDoubleValue(const AName: string; + const AValue: Double); +var + LDummy: TJSONValue; +begin + if TryGetValue(AName, LDummy) then + RemovePair(AName); + + AddPair(AName, TJSONNumber.Create(AValue)); +end; + +procedure TJSONObjectHelper.WriteInt64Value(const AName: string; + const AValue: Int64); +var + LDummy: TJSONValue; +begin + if TryGetValue(AName, LDummy) then + RemovePair(AName); + + AddPair(AName, TJSONNumber.Create(AValue)); +end; + +procedure TJSONObjectHelper.WriteIntegerValue(const AName: string; + const AValue: Integer); +var + LDummy: TJSONValue; +begin + if TryGetValue(AName, LDummy) then + RemovePair(AName); + + AddPair(AName, TJSONNumber.Create(AValue)); +end; + +procedure TJSONObjectHelper.WriteStringValue(const AName, AValue: string); +var + LDummy: TJSONValue; +begin + if TryGetValue(AName, LDummy) then + RemovePair(AName); + + if AValue <> '' then + AddPair(AName, TJSONString.Create(AValue)); +end; + +procedure TJSONObjectHelper.WriteTValue(const AName: string; + const AValue: TValue); +begin + AddPair(AName, TValueToJSONValue(AValue)); +end; + +procedure TJSONObjectHelper.WriteUnixTimeValue(const AName: string; + const AValue: TDateTime); +begin + WriteInt64Value(AName, DateTimeToUnix(AValue)); +end; + +{ JSONNameAttribute } + +constructor JSONNameAttribute.Create(const AName: string); +begin + inherited Create; + FName := AName; +end; + +end. diff --git a/Source/MARS.Core.MediaType.pas b/Source/MARS.Core.MediaType.pas index 233b037c..45bec2a4 100644 --- a/Source/MARS.Core.MediaType.pas +++ b/Source/MARS.Core.MediaType.pas @@ -70,6 +70,7 @@ TMediaType = class const TEXT_XML = 'text/xml'; const TEXT_HTML = 'text/html'; const APPLICATION_XML = 'application/xml'; + const APPLICATION_XML_FireDAC = 'application/xml-firedac'; const APPLICATION_JSON = 'application/json'; const APPLICATION_JSON_FireDAC = 'application/json-firedac'; const APPLICATION_XHTML_XML = 'application/xhtml+xml'; @@ -390,7 +391,7 @@ function TMediaTypeList.GetQualityFactor(const AMediaType: string): Double; begin Result := 0.0; for LItem in Self do - if LItem.ToString = AMediaType then + if (LItem.ToString = AMediaType) or (LItem.IsWildcard) then begin Result := LItem.QFactor; Break; @@ -402,14 +403,12 @@ class function TMediaTypeList.Intersect(const AList1: TArray; var LMediaType: string; begin - SetLength(Result, 0); + Result := []; + for LMediaType in AList1 do begin - if AList2.Contains(LMediaType) then - begin - SetLength(Result, Length(Result) + 1); - Result[Length(Result) -1 ] := LMediaType; - end; + if (LMediaType = TMediaType.WILDCARD) or AList2.Contains(LMediaType) then + Result := Result + [LMediaType]; end; end; diff --git a/Source/MARS.Core.MessageBodyReader.pas b/Source/MARS.Core.MessageBodyReader.pas index 6f70e9a3..d6c2eff9 100644 --- a/Source/MARS.Core.MessageBodyReader.pas +++ b/Source/MARS.Core.MessageBodyReader.pas @@ -22,9 +22,7 @@ interface ; type - IMessageBodyReader = interface - ['{C22068E1-3085-482D-9EAB-4829C7AE87C0}'] - + IMessageBodyReader = interface ['{C22068E1-3085-482D-9EAB-4829C7AE87C0}'] function ReadFrom( {$ifdef Delphi10Berlin_UP}const AInputData: TBytes;{$else}const AInputData: AnsiString;{$endif} const ADestination: TRttiObject; const AMediaType: TMediaType; @@ -90,6 +88,19 @@ TMARSMessageBodyReaderRegistry = class const AFFINITY_ZERO = 0; end; + TMARSMessageBodyReader = class + private + protected + public + class function ReadWith( + {$ifdef Delphi10Berlin_UP}const AInputData: TBytes;{$else}const AInputData: AnsiString;{$endif} + const ADestination: TRttiObject; const AMediaType: TMediaType; + const AActivation: IMARSActivation): TValue; inline; + class function GetDesiredEncoding(const AActivation: IMARSActivation; + var AEncoding: TEncoding): Boolean; + end; + + implementation uses @@ -364,4 +375,58 @@ procedure TMARSMessageBodyReaderRegistry.RegisterReader( FRegistry.Add(LEntryInfo) end; +{ TMARSMessageBodyReader } + +class function TMARSMessageBodyReader.GetDesiredEncoding( + const AActivation: IMARSActivation; var AEncoding: TEncoding): Boolean; +var + LEncoding: TEncoding; + LFound: Boolean; +begin + if not Assigned(AActivation) then + begin + AEncoding := TEncoding.UTF8; + Result := False; + Exit; + end; + + LFound := False; + // look for attribute on Method + if Assigned(AActivation.Method) and not AActivation.Method.HasAttribute( + procedure(AAttr: EncodingAttribute) + begin + LEncoding := AAttr.Encoding; + LFound := True; + end + ) then // if not found, fallback looking for attribute on Resource + begin + if Assigned(AActivation.Resource) then + AActivation.Resource.HasAttribute( + procedure(AAttr: EncodingAttribute) + begin + LEncoding := AAttr.Encoding; + LFound := True; + end + ); + end; + + Result := False; + if LFound then + begin + AEncoding := LEncoding; + Result := True; + end; +end; + +class function TMARSMessageBodyReader.ReadWith( + {$ifdef Delphi10Berlin_UP}const AInputData: TBytes;{$else}const AInputData: AnsiString;{$endif} + const ADestination: TRttiObject; const AMediaType: TMediaType; + const AActivation: IMARSActivation): TValue; +var + LMBReader: IMessageBodyReader; +begin + LMBReader := T.Create; + Result := LMBReader.ReadFrom(AInputData, ADestination, AMediaType, AActivation); +end; + end. diff --git a/Source/MARS.Core.MessageBodyReaders.pas b/Source/MARS.Core.MessageBodyReaders.pas index d35c27b2..673beee9 100644 --- a/Source/MARS.Core.MessageBodyReaders.pas +++ b/Source/MARS.Core.MessageBodyReaders.pas @@ -10,14 +10,12 @@ interface uses - Classes, SysUtils, Rtti + Classes, SysUtils, System.Rtti, System.TypInfo - , MARS.Core.Attributes - , MARS.Core.Activation.Interfaces - , MARS.Core.Declarations - , MARS.Core.MediaType - , MARS.Core.MessageBodyReader - ; +, MARS.Core.Attributes, MARS.Core.Activation.Interfaces, MARS.Core.Declarations +, MARS.Core.MediaType, MARS.Core.MessageBodyReader +, MARS.Core.RequestAndResponse.Interfaces +; type [Consumes(TMediaType.APPLICATION_JSON)] @@ -58,6 +56,22 @@ TJSONValueReader = class(TInterfacedObject, IMessageBodyReader) end; + [Consumes(TMediaType.APPLICATION_XML)] + TXMLReader = class(TInterfacedObject, IMessageBodyReader) + public + function ReadFrom( + {$ifdef Delphi10Berlin_UP}const AInputData: TBytes;{$else}const AInputData: AnsiString;{$endif} + const ADestination: TRttiObject; const AMediaType: TMediaType; + const AActivation: IMARSActivation + ): TValue; virtual; + + class function ReadXML( + {$ifdef Delphi10Berlin_UP}const AInputData: TBytes;{$else}const AInputData: AnsiString;{$endif} + const ADestination: TRttiObject; const AMediaType: TMediaType; + const AActivation: IMARSActivation + ): TValue; + end; + [Consumes(TMediaType.APPLICATION_JSON) , Consumes(TMediaType.APPLICATION_FORM_URLENCODED_TYPE) , Consumes(TMediaType.MULTIPART_FORM_DATA) @@ -129,11 +143,11 @@ TArrayOfTFormParamReader = class(TInterfacedObject, IMessageBodyReader) implementation uses - StrUtils, NetEncoding, Web.HttpApp - , MARS.Core.JSON - , MARS.Core.Utils, MARS.Rtti.Utils - {$ifdef DelphiXE7_UP}, System.JSON {$endif} - ; + StrUtils, NetEncoding, Generics.Collections +{$ifdef DelphiXE7_UP}, System.JSON {$endif} +, Xml.XMLIntf, XMLDoc +, MARS.Core.JSON, MARS.Core.Utils, MARS.Rtti.Utils +; { TJSONValueReader } @@ -205,7 +219,7 @@ function TRecordReader.ReadFrom( ): TValue; var LJSON: TJSONObject; - LRequest: TWebRequest; + LRequest: IMARSRequest; begin Result := TValue.Empty; @@ -214,7 +228,7 @@ function TRecordReader.ReadFrom( then begin LRequest := AActivation.Request; - Result := StringsToRecord(LRequest.ContentFields, ADestination.GetRttiType + Result := StringsToRecord(LRequest.GetFormParams, ADestination.GetRttiType , procedure (const AName: string; const AField: TRttiField; var AValue: TValue) begin if AField.FieldType.Handle = TypeInfo(TFormParamFile) then @@ -277,6 +291,7 @@ function TArrayOfObjectReader.ReadFrom( LArray: TValue; LArrayType: TRttiType; LIndex: Integer; + LNewLength: NativeInt; begin Result := TValue.Empty; LArrayType := ADestination.GetRttiType; @@ -295,8 +310,9 @@ function TArrayOfObjectReader.ReadFrom( if LJSONValue is TJSONArray then begin LJSONArray := TJSONArray(LJSONValue); - - SetArrayLength(LArray, LArrayType, LJSONArray.Count); + LNewLength := LJSONArray.Count; + SetArrayLength(LArray, LArrayType, @LNewLength); + //------------------------ for LIndex := 0 to LJSONArray.Count-1 do //AM Refactor using ForEach begin LJSONObject := LJSONArray.Items[LIndex] as TJSONObject; @@ -312,7 +328,9 @@ function TArrayOfObjectReader.ReadFrom( end else if LJSONValue is TJSONObject then // a single obj, let's build an array of one element begin - SetArrayLength(LArray, LArrayType, 1); + LNewLength := 1; + SetArrayLength(LArray, LArrayType, @LNewLength); + //------------------------ LArray.SetArrayElement( 0 , TJSONObject.JSONToObject( @@ -344,6 +362,7 @@ function TArrayOfRecordReader.ReadFrom( LArray: TValue; LArrayType: TRttiType; LIndex: Integer; + LNewLength: NativeInt; begin Result := TValue.Empty; LArrayType := ADestination.GetRttiType; @@ -359,8 +378,9 @@ function TArrayOfRecordReader.ReadFrom( if LJSONValue is TJSONArray then begin LJSONArray := TJSONArray(LJSONValue); - - SetArrayLength(LArray, LArrayType, LJSONArray.Count); + LNewLength := LJSONArray.Count; + SetArrayLength(LArray, LArrayType, @LNewLength); + //------------------------ for LIndex := 0 to LJSONArray.Count-1 do //AM Refactor using ForEach begin LJSONObject := LJSONArray.Items[LIndex] as TJSONObject; @@ -370,7 +390,9 @@ function TArrayOfRecordReader.ReadFrom( end else if LJSONValue is TJSONObject then // a single obj, let's build an array of one element begin - SetArrayLength(LArray, LArrayType, 1); + LNewLength := 1; + SetArrayLength(LArray, LArrayType, @LNewLength); + //------------------------ LArray.SetArrayElement(0, TJSONObject(LJSONValue).ToRecord(LElementType)); end; @@ -388,42 +410,24 @@ function TStringReader.ReadFrom( const AActivation: IMARSActivation): TValue; var LType: TRttiType; - LSL: TStringList; - {$ifdef Delphi10Berlin_UP} - LBytesStream: TBytesStream; - {$endif} + LEncoding: TEncoding; + LText: string; begin Result := TValue.Empty; LType := ADestination.GetRttiType; {$ifdef Delphi10Berlin_UP} - LBytesStream := TBytesStream.Create(AInputData); - try - LSL := TStringList.Create; - try - LSL.LoadFromStream(LBytesStream); - if LType.IsDynamicArrayOf then - Result := TValue.From>( LSL.ToStringArray ) - else if LType.Handle = TypeInfo(string) then - Result := LSL.Text; - finally - LSL.Free; - end; - finally - LBytesStream.Free; - end; + if not TMARSMessageBodyReader.GetDesiredEncoding(AActivation, LEncoding) then + LEncoding := TEncoding.UTF8; // UTF8 by default + LText := LEncoding.GetString(AInputData); {$else} - LSL := TStringList.Create; - try - LSL.Text := string(AInputData); - if LType.IsDynamicArrayOf then - Result := TValue.From>( LSL.ToStringArray ) - else if LType.Handle = TypeInfo(string) then - Result := LSL.Text; - finally - LSL.Free; - end; + LText := string(AInputData); {$endif} + + if LType.IsDynamicArrayOf then + Result := TValue.From>( LText.Split([sLineBreak]) ) + else if LType.Handle = TypeInfo(string) then + Result := LText; end; { TFormParamReader } @@ -465,7 +469,7 @@ function TArrayOfTFormParamReader.ReadFrom( ): TValue; var LResult: TArray; - LRequest: TWebRequest; + LRequest: IMARSRequest; LIndex: Integer; begin LResult := []; @@ -476,10 +480,10 @@ function TArrayOfTFormParamReader.ReadFrom( begin LRequest := AActivation.Request; - for LIndex := 0 to LRequest.ContentFields.Count - 1 do - LResult := LResult + [TFormParam.CreateFromRequest(LRequest, LRequest.ContentFields.Names[LIndex])]; + for LIndex := 0 to LRequest.GetFormParamCount - 1 do + LResult := LResult + [TFormParam.CreateFromRequest(LRequest, LRequest.GetFormParamName(LIndex))]; - for LIndex := 0 to LRequest.Files.Count - 1 do + for LIndex := 0 to LRequest.GetFilesCount - 1 do LResult := LResult + [TFormParam.CreateFromRequest(LRequest, LIndex)]; end; @@ -487,6 +491,45 @@ function TArrayOfTFormParamReader.ReadFrom( end; +{ TXMLReader } + +function TXMLReader.ReadFrom( +{$ifdef Delphi10Berlin_UP}const AInputData: TBytes;{$else}const AInputData: AnsiString;{$endif} + const ADestination: TRttiObject; const AMediaType: TMediaType; + const AActivation: IMARSActivation): TValue; +var + LXMLDoc: IXMLDocument; + LEncoding: TEncoding; +begin + Result := TValue.Empty; + + LEncoding := TEncoding.UTF8; + + LXMLDoc := TXMLDocument.Create(nil); +{$ifdef Delphi10Berlin_UP} + LXMLDoc.LoadFromXML(LEncoding.GetString(AInputData)); +{$else} + LXMLDoc.LoadFromXML(AInputData); +{$endif} + Result := TValue.From(LXMLDoc); +end; + +class function TXMLReader.ReadXML( +{$ifdef Delphi10Berlin_UP}const AInputData: TBytes;{$else}const AInputData: AnsiString;{$endif} + const ADestination: TRttiObject; const AMediaType: TMediaType; + const AActivation: IMARSActivation): TValue; +var + LXMLReader: TXMLReader; +begin + LXMLReader := TXMLReader.Create; + try + Result := LXMLReader.ReadFrom(AInputData, ADestination, AMediaType, AActivation); + finally + LXMLReader.Free; + end; +end; + + procedure RegisterReaders; begin TMARSMessageBodyReaderRegistry.Instance.RegisterReader( @@ -514,6 +557,7 @@ procedure RegisterReaders; ); TMARSMessageBodyReaderRegistry.Instance.RegisterReader(TJSONValueReader); + TMARSMessageBodyReaderRegistry.Instance.RegisterReader(TXMLReader); TMARSMessageBodyReaderRegistry.Instance.RegisterReader(TStreamReader); TMARSMessageBodyReaderRegistry.Instance.RegisterReader( diff --git a/Source/MARS.Core.MessageBodyWriter.pas b/Source/MARS.Core.MessageBodyWriter.pas index d9c3635a..9c47b83c 100644 --- a/Source/MARS.Core.MessageBodyWriter.pas +++ b/Source/MARS.Core.MessageBodyWriter.pas @@ -61,7 +61,11 @@ TMARSMessageBodyRegistry = class const AGetAffinity: TGetAffinityFunction = nil); overload; procedure FindWriter(const AActivation: IMARSActivation; - out AWriter: IMessageBodyWriter; out AMediaType: TMediaType); + out AWriter: IMessageBodyWriter; out AMediaType: TMediaType); overload; + procedure FindWriter(const AActivation: IMARSActivation; + const AAccept: string; + out AWriter: IMessageBodyWriter; out AMediaType: TMediaType); overload; + procedure Enumerate(const AProc: TProc); @@ -76,8 +80,16 @@ TMARSMessageBodyRegistry = class const AFFINITY_ZERO = 0; end; -function GetDesiredEncoding(const AActivation: IMARSActivation; var AEncoding: TEncoding): Boolean; -function GetEncodingName(const AEncoding: TEncoding): string; + TMARSMessageBodyWriter = class + private + protected + public + class procedure WriteWith( + const AValue: TValue; const AMediaType: TMediaType; + AOutputStream: TStream; const AActivation: IMARSActivation); inline; + class function GetDesiredEncoding(const AActivation: IMARSActivation; + var AEncoding: TEncoding): Boolean; + end; implementation @@ -85,7 +97,7 @@ implementation MARS.Core.Utils, MARS.Rtti.Utils, MARS.Core.Exceptions, MARS.Core.Attributes ; -function GetDesiredEncoding(const AActivation: IMARSActivation; var AEncoding: TEncoding): Boolean; +class function TMARSMessageBodyWriter.GetDesiredEncoding(const AActivation: IMARSActivation; var AEncoding: TEncoding): Boolean; var LEncoding: TEncoding; LFound: Boolean; @@ -125,19 +137,19 @@ function GetDesiredEncoding(const AActivation: IMARSActivation; var AEncoding: T end; end; -function GetEncodingName(const AEncoding: TEncoding): string; +{ TMARSMessageBodyWriter } + +class procedure TMARSMessageBodyWriter.WriteWith(const AValue: TValue; + const AMediaType: TMediaType; AOutputStream: TStream; + const AActivation: IMARSActivation); +var + LMBWriter: IMessageBodyWriter; begin - Result := ''; - - if AEncoding = TEncoding.ANSI then Result := 'ANSI' - else if AEncoding = TEncoding.ASCII then Result := 'ASCII' - else if AEncoding = TEncoding.BigEndianUnicode then Result :='BigEndianUnicode' - else if AEncoding = TEncoding.Default then Result :='Default' - else if AEncoding = TEncoding.Unicode then Result :='Unicode' - else if AEncoding = TEncoding.UTF7 then Result :='UTF7' - else if AEncoding = TEncoding.UTF8 then Result :='UTF8'; + LMBWriter := T.Create; + LMBWriter.WriteTo(AValue, AMediaType, AOutputStream, AActivation); end; + { TMARSMessageBodyRegistry } class destructor TMARSMessageBodyRegistry.ClassDestructor; @@ -170,6 +182,13 @@ procedure TMARSMessageBodyRegistry.Enumerate(const AProc: TProc); procedure TMARSMessageBodyRegistry.FindWriter(const AActivation: IMARSActivation; out AWriter: IMessageBodyWriter; out AMediaType: TMediaType); +begin + FindWriter(AActivation, AActivation.Request.Accept, AWriter, AMediaType); +end; + +procedure TMARSMessageBodyRegistry.FindWriter( + const AActivation: IMARSActivation; const AAccept: string; + out AWriter: IMessageBodyWriter; out AMediaType: TMediaType); var LWriterEntry: TEntryInfo; LFound: Boolean; @@ -185,11 +204,10 @@ procedure TMARSMessageBodyRegistry.FindWriter(const AActivation: IMARSActivation LMediaType: string; LCandidateMediaType: string; LCandidateQualityFactor: Double; - LAccept: string; LMethodReturnType: TRttiType; LMethodAttributes: TArray; + LAffinity: Integer; begin - LAccept := AActivation.Request.Accept; LMethodReturnType := AActivation.MethodReturnType; LMethodAttributes := AActivation.MethodAttributes; @@ -204,7 +222,7 @@ procedure TMARSMessageBodyRegistry.FindWriter(const AActivation: IMARSActivation Exit; // no serialization (it's a procedure!) // consider client's Accept - LAcceptParser := TAcceptParser.Create(LAccept); + LAcceptParser := TAcceptParser.Create(AAccept); try LAcceptMediaTypes := LAcceptParser.MediaTypeList; @@ -241,12 +259,15 @@ procedure TMARSMessageBodyRegistry.FindWriter(const AActivation: IMARSActivation else LMediaTypes := TMediaTypeList.Intersect(LAllowedMediaTypes, LWriterMediaTypes); for LMediaType in LMediaTypes do + begin + LAffinity := LWriterEntry.GetAffinity(LMethodReturnType, LMethodAttributes, LMediaType); if LWriterEntry.IsWritable(LMethodReturnType, LMethodAttributes, LMediaType) then begin if not LFound + or (LCandidateAffinity < LAffinity) or ( - (LCandidateAffinity < LWriterEntry.GetAffinity(LMethodReturnType, LMethodAttributes, LMediaType)) - or (LCandidateQualityFactor < LAcceptMediaTypes.GetQualityFactor(LMediaType)) + (LCandidateAffinity = LAffinity) + and (LCandidateQualityFactor < LAcceptMediaTypes.GetQualityFactor(LMediaType)) ) then begin @@ -257,6 +278,7 @@ procedure TMARSMessageBodyRegistry.FindWriter(const AActivation: IMARSActivation LFound := True; end; end; + end; finally LWriterMediaTypes.Free; end; diff --git a/Source/MARS.Core.MessageBodyWriters.pas b/Source/MARS.Core.MessageBodyWriters.pas index 2167f7c1..ffb6702c 100644 --- a/Source/MARS.Core.MessageBodyWriters.pas +++ b/Source/MARS.Core.MessageBodyWriters.pas @@ -38,7 +38,18 @@ TJSONValueWriter = class(TInterfacedObject, IMessageBodyWriter) AOutputStream: TStream; const AActivation: IMARSActivation); class procedure WriteJSONValue(const AValue: TValue; const AMediaType: TMediaType; + AOutputStream: TStream; const AActivation: IMARSActivation); inline; + end; + + [Produces(TMediaType.APPLICATION_XML)] + TXMLWriter = class(TInterfacedObject, IMessageBodyWriter) + protected + public + procedure WriteTo(const AValue: TValue; const AMediaType: TMediaType; AOutputStream: TStream; const AActivation: IMARSActivation); + + class procedure WriteXML(const AValue: TValue; const AMediaType: TMediaType; + AOutputStream: TStream; const AActivation: IMARSActivation); inline; end; [Produces(TMediaType.APPLICATION_JSON)] @@ -86,11 +97,9 @@ TPrimitiveTypesWriter = class(TInterfacedObject, IMessageBodyWriter) implementation uses - System.TypInfo - , MARS.Core.JSON - , MARS.Core.Utils - , MARS.Rtti.Utils - ; + System.TypInfo, Xml.XMLIntf, System.JSON +, MARS.Core.JSON, MARS.Core.Utils, MARS.Rtti.Utils +; { TObjectWriter } @@ -140,15 +149,8 @@ procedure TArrayOfObjectWriter.WriteTo(const AValue: TValue; class procedure TJSONValueWriter.WriteJSONValue(const AValue: TValue; const AMediaType: TMediaType; AOutputStream: TStream; const AActivation: IMARSActivation); -var - LJSONWriter: TJSONValueWriter; begin - LJSONWriter := TJSONValueWriter.Create; - try - LJSONWriter.WriteTo(AValue, AMediaType, AOutputStream, AActivation); - finally - LJSONWriter.Free; - end; + TMARSMessageBodyWriter.WriteWith(AValue, AMediaType, AOutputStream, AActivation); end; procedure TJSONValueWriter.WriteTo(const AValue: TValue; const AMediaType: TMediaType; @@ -164,7 +166,7 @@ procedure TJSONValueWriter.WriteTo(const AValue: TValue; const AMediaType: TMedi LEncoding: TEncoding; LContentBytes: TBytes; begin - if not GetDesiredEncoding(AActivation, LEncoding) then + if not TMARSMessageBodyWriter.GetDesiredEncoding(AActivation, LEncoding) then LEncoding := TEncoding.UTF8; // UTF8 by default LJSONString := ''; @@ -391,7 +393,7 @@ procedure TPrimitiveTypesWriter.WriteTo(const AValue: TValue; LContentType: string; LEncodingName: string; begin - if not GetDesiredEncoding(AActivation, LEncoding) then + if not TMARSMessageBodyWriter.GetDesiredEncoding(AActivation, LEncoding) then LEncoding := TEncoding.UTF8; // UTF8 by default LEncodingName := GetEncodingName(LEncoding); @@ -413,6 +415,48 @@ procedure TPrimitiveTypesWriter.WriteTo(const AValue: TValue; AOutputStream.Write(LContentBytes, Length(LContentBytes)); end; +{ TXMLWriter } + +procedure TXMLWriter.WriteTo(const AValue: TValue; const AMediaType: TMediaType; + AOutputStream: TStream; const AActivation: IMARSActivation); +var + LEncoding: TEncoding; + LContentBytes: TBytes; + LXMLDocument: IXMLDocument; + LXMLString: string; + LXMLNode: IXMLNode; +begin + if not TMARSMessageBodyWriter.GetDesiredEncoding(AActivation, LEncoding) then + LEncoding := TEncoding.UTF8; // UTF8 by default + + LXMLString := ''; + if AValue.IsType then + LXMLString := AValue.AsType + else if AValue.IsType then + begin + LXMLDocument := AValue.AsType; + LXMLDocument.SaveToXML(LXMLString); + end + else if AValue.IsType then + begin + LXMLNode := AValue.AsType; + LXMLString := LXMLNode.XML; + end; + + if LXMLString = '' then + Exit; + + LContentBytes := LEncoding.GetBytes(LXMLString); + AOutputStream.Write(LContentBytes, Length(LContentBytes)); +end; + +class procedure TXMLWriter.WriteXML(const AValue: TValue; + const AMediaType: TMediaType; AOutputStream: TStream; + const AActivation: IMARSActivation); +begin + TMARSMessageBodyWriter.WriteWith(AValue, AMediaType, AOutputStream, AActivation); +end; + procedure RegisterWriters; begin TMARSMessageBodyRegistry.Instance.RegisterWriter(TJSONValueWriter); @@ -428,6 +472,9 @@ procedure RegisterWriters; end ); + TMARSMessageBodyRegistry.Instance.RegisterWriter(TXMLWriter); + TMARSMessageBodyRegistry.Instance.RegisterWriter(TXMLWriter); + TMARSMessageBodyRegistry.Instance.RegisterWriter(TStreamValueWriter); TMARSMessageBodyRegistry.Instance.RegisterWriter( diff --git a/Source/MARS.Core.RequestAndResponse.Interfaces.pas b/Source/MARS.Core.RequestAndResponse.Interfaces.pas new file mode 100644 index 00000000..59ac5118 --- /dev/null +++ b/Source/MARS.Core.RequestAndResponse.Interfaces.pas @@ -0,0 +1,76 @@ +unit MARS.Core.RequestAndResponse.Interfaces; + +interface + +uses + Classes, SysUtils; + +type + IMARSRequest = interface + function GetRawContent: TBytes; + function GetContent: string; + function GetQueryParamIndex(const AName: string): Integer; + function GetQueryParamValue(const AIndex: Integer): string; + function GetQueryParamCount: Integer; + function GetFormParamIndex(const AName: string): Integer; + function GetFormParamName(const AIndex: Integer): string; + function GetFormParamValue(const AIndex: Integer): string; overload; + function GetFormParamValue(const AName: string): string; overload; + function GetFormFileParamIndex(const AName: string): Integer; + function GetFormFileParam(const AIndex: Integer; out AFieldName, AFileName: string; + out ABytes: TBytes; out AContentType: string): Boolean; + function GetFormParamCount: Integer; + function GetHeaderParamValue(const AHeaderName: string): string; + function GetCookieParamIndex(const AName: string): Integer; + function GetCookieParamValue(const AIndex: Integer): string; overload; + function GetCookieParamValue(const AName: string): string; overload; + function GetCookieParamCount: Integer; + function GetFilesCount: Integer; + function GetFormParams: string; + function GetAccept: string; + function GetAuthorization: string; + function GetMethod: string; + function GetQueryString: string; + function GetHostName: string; + function GetPort: Integer; + function GetRawPath: string; + + function AsObject: TObject; + procedure CheckWorkaroundForISAPI; + + property RawContent: TBytes read GetRawContent; + property Content: string read GetContent; + property Accept: string read GetAccept; + property Authorization: string read GetAuthorization; + property Method: string read GetMethod; + property QueryString: string read GetQueryString; + property HostName: string read GetHostName; + property Port: Integer read GetPort; + property RawPath: string read GetRawPath; + end; + + IMARSResponse = interface + function GetContentStream: TStream; + procedure SetContentStream(const AContentStream: TStream); + function GetContentType: string; + procedure SetContentType(const AContentType: string); + function GetContentEncoding: string; + procedure SetContentEncoding(const AContentEncoding: string); + function GetStatusCode: Integer; + procedure SetStatusCode(const AStatusCode: Integer); + function GetContent: string; + procedure SetContent(const AContent: string); + procedure SetHeader(const AName, AValue: string); + procedure SetCookie(const AName, AValue, ADomain, APath: string; const AExpiration: TDateTime; const ASecure: Boolean); + + property Content: string read GetContent write SetContent; + property ContentStream: TStream read GetContentStream write SetContentStream; + property ContentType: string read GetContentType write SetContentType; + property ContentEncoding: string read GetContentEncoding write SetContentEncoding; + property StatusCode: Integer read GetStatusCode write SetStatusCode; + end; + + +implementation + +end. diff --git a/Source/MARS.Core.Response.pas b/Source/MARS.Core.Response.pas index 0e3b9c08..dd5734b8 100644 --- a/Source/MARS.Core.Response.pas +++ b/Source/MARS.Core.Response.pas @@ -9,8 +9,7 @@ interface uses SysUtils, Classes - , HTTPApp - , MARS.Core.MediaType; +, MARS.Core.RequestAndResponse.Interfaces, MARS.Core.MediaType; type @@ -23,7 +22,7 @@ TMARSResponse = class FContentStream: TStream; FFreeContentStream: Boolean; public - procedure CopyTo(AWebResponse: TWebResponse); + procedure CopyTo(AResponse: IMARSResponse); destructor Destroy; override; property Content: string read FContent write FContent; @@ -40,19 +39,19 @@ implementation { TMARSResponse } -procedure TMARSResponse.CopyTo(AWebResponse: TWebResponse); +procedure TMARSResponse.CopyTo(AResponse: IMARSResponse); begin if Assigned(ContentStream) then begin - AWebResponse.ContentStream := ContentStream; + AResponse.ContentStream := ContentStream; FreeContentStream := False; end else - AWebResponse.Content := Content; + AResponse.Content := Content; - AWebResponse.ContentType := ContentType; - AWebResponse.ContentEncoding := ContentEncoding; - AWebResponse.StatusCode := StatusCode; + AResponse.ContentType := ContentType; + AResponse.ContentEncoding := ContentEncoding; + AResponse.StatusCode := StatusCode; end; destructor TMARSResponse.Destroy; diff --git a/Source/MARS.Core.Token.pas b/Source/MARS.Core.Token.pas index 97d66394..03e8a83b 100644 --- a/Source/MARS.Core.Token.pas +++ b/Source/MARS.Core.Token.pas @@ -11,10 +11,9 @@ interface uses SysUtils, Classes, Generics.Collections, SyncObjs, Rtti -, HTTPApp, IdGlobal +, IdGlobal -, MARS.Core.URL -, MARS.Utils.Parameters +, MARS.Core.URL, MARS.Utils.Parameters, MARS.Core.RequestAndResponse.Interfaces //{$IFDEF mORMot-JWT} @@ -39,8 +38,8 @@ TMARSToken = class FCookieDomain: string; FCookiePath: string; FCookieSecure: Boolean; - FRequest: TWebRequest; - FResponse: TWebResponse; + FRequest: IMARSRequest; + FResponse: IMARSResponse; FDuration: TDateTime; FIssuer: string; function GetUserName: string; @@ -52,22 +51,22 @@ TMARSToken = class function GetDurationMins: Int64; function GetDurationSecs: Int64; protected - function GetTokenFromBearer(const ARequest: TWebRequest): string; virtual; - function GetTokenFromCookie(const ARequest: TWebRequest): string; virtual; - function GetToken(const ARequest: TWebRequest): string; virtual; + function GetTokenFromBearer(const ARequest: IMARSRequest): string; virtual; + function GetTokenFromCookie(const ARequest: IMARSRequest): string; virtual; + function GetToken(const ARequest: IMARSRequest): string; virtual; function GetIsExpired: Boolean; virtual; function BuildJWTToken(const ASecret: string; const AClaims: TMARSParameters): string; virtual; function LoadJWTToken(const AToken: string; const ASecret: string; var AClaims: TMARSParameters): Boolean; virtual; - property Request: TWebRequest read FRequest; - property Response: TWebResponse read FResponse; + property Request: IMARSRequest read FRequest; + property Response: IMARSResponse read FResponse; public constructor Create(); reintroduce; overload; virtual; constructor Create(const AToken: string; const AParameters: TMARSParameters); overload; virtual; constructor Create(const AToken: string; const ASecret: string; const AIssuer: string; const ADuration: TDateTime); overload; virtual; - constructor Create(const ARequest: TWebRequest; const AResponse: TWebResponse; + constructor Create(const ARequest: IMARSRequest; const AResponse: IMARSResponse; const AParameters: TMARSParameters; const AURL: TMARSURL); overload; virtual; destructor Destroy; override; @@ -176,7 +175,7 @@ constructor TMARSToken.Create(const AToken, ASecret, AIssuer: string; Load(AToken, ASecret); end; -constructor TMARSToken.Create(const ARequest: TWebRequest; const AResponse: TWebResponse; +constructor TMARSToken.Create(const ARequest: IMARSRequest; const AResponse: IMARSResponse; const AParameters: TMARSParameters; const AURL: TMARSURL); begin FRequest := ARequest; @@ -254,7 +253,7 @@ function TMARSToken.GetRoles: TArray; {$endif} end; -function TMARSToken.GetToken(const ARequest: TWebRequest): string; +function TMARSToken.GetToken(const ARequest: IMARSRequest): string; begin // Beware: First match wins! @@ -265,7 +264,7 @@ function TMARSToken.GetToken(const ARequest: TWebRequest): string; Result := GetTokenFromCookie(ARequest); end; -function TMARSToken.GetTokenFromBearer(const ARequest: TWebRequest): string; +function TMARSToken.GetTokenFromBearer(const ARequest: IMARSRequest): string; var LAuth: string; LAuthTokens: TArray; @@ -293,12 +292,12 @@ function TMARSToken.GetTokenFromBearer(const ARequest: TWebRequest): string; Result := LAuthTokens[1]; end; -function TMARSToken.GetTokenFromCookie(const ARequest: TWebRequest): string; +function TMARSToken.GetTokenFromCookie(const ARequest: IMARSRequest): string; begin Result := ''; if CookieEnabled and (CookieName <> '') then {$ifdef DelphiXE7_UP} - Result := TNetEncoding.URL.Decode(ARequest.CookieFields.Values[CookieName]); + Result := TNetEncoding.URL.Decode(ARequest.GetCookieParamValue(CookieName)); {$else} Result := TIdURI.URLDecode(ARequest.CookieFields.Values[CookieName]); {$endif} @@ -381,33 +380,15 @@ procedure TMARSToken.SetUserNameAndRoles(const AUserName: string; end; procedure TMARSToken.UpdateCookie; -var - LContent: TStringList; begin if CookieEnabled then begin Assert(Assigned(Response)); - LContent := TStringList.Create; - try - if IsVerified and not IsExpired then - begin - LContent.Values[CookieName] := Token; - - Response.SetCookieField( - LContent, CookieDomain, CookiePath, Expiration, CookieSecure); - end - else begin - if Request.CookieFields.Values[CookieName] <> '' then - begin - LContent.Values[CookieName] := 'dummy'; - Response.SetCookieField( - LContent, CookieDomain, CookiePath, Now-1, CookieSecure); - end; - end; - finally - LContent.Free; - end; + if IsVerified and not IsExpired then + Response.SetCookie(CookieName, Token, CookieDomain, CookiePath, Expiration, CookieSecure) + else if Request.GetCookieParamValue(CookieName) <> '' then + Response.SetCookie(CookieName, 'dummy', CookieDomain, CookiePath, Now-1, CookieSecure); end; end; diff --git a/Source/MARS.Core.URL.pas b/Source/MARS.Core.URL.pas index 675573ff..7b6f365a 100644 --- a/Source/MARS.Core.URL.pas +++ b/Source/MARS.Core.URL.pas @@ -13,10 +13,9 @@ interface uses - Classes, SysUtils - , MARS.Core.JSON - , HTTPApp - , Generics.Collections + Classes, SysUtils, System.JSON +, MARS.Core.JSON, MARS.Core.RequestAndResponse.Interfaces +, Generics.Collections ; type @@ -61,7 +60,7 @@ TMARSURL = class constructor Create(const AURL: string); overload; virtual; constructor CreateDummy(const APath: string; const ABaseURL: string = DUMMY_URL); overload; virtual; constructor CreateDummy(const APaths: array of string; const ABaseURL: string = DUMMY_URL); overload; virtual; - constructor Create(AWebRequest: TWebRequest); overload; virtual; + constructor Create(ARequest: IMARSRequest); overload; virtual; destructor Destroy; override; function MatchPath(AOtherURL: TMARSURL; const ACaseSensitive: Boolean = False): Boolean; overload; virtual; @@ -202,16 +201,16 @@ class function TMARSURL.CombinePath(const APathTokens: array of string; Result := EnsureLastPathDelimiter(Result); end; -constructor TMARSURL.Create(AWebRequest: TWebRequest); +constructor TMARSURL.Create(ARequest: IMARSRequest); var LQuery: string; begin - LQuery := string(AWebRequest.Query); + LQuery := ARequest.QueryString; if LQuery <> '' then LQuery := URL_QUERY_PREFIX + LQuery; // Add the protocol in order to make Parse work. - Create('http://' + string(AWebRequest.Host) + ':' + IntToStr(AWebRequest.ServerPort) + string(AWebRequest.RawPathInfo) + LQuery); + Create('http://' + ARequest.HostName + ':' + ARequest.Port.ToString + ARequest.RawPath + LQuery); end; constructor TMARSURL.CreateDummy(const APaths: array of string; const ABaseURL: string); diff --git a/Source/MARS.Core.Utils.pas b/Source/MARS.Core.Utils.pas index e013b020..689b7ad5 100644 --- a/Source/MARS.Core.Utils.pas +++ b/Source/MARS.Core.Utils.pas @@ -10,8 +10,8 @@ interface uses - SysUtils, Classes, RTTI, SyncObjs, Web.HttpApp, REST.JSON -, MARS.Core.JSON + SysUtils, Classes, RTTI, SyncObjs, REST.JSON, System.JSON +, MARS.Core.JSON, MARS.Core.RequestAndResponse.Interfaces ; type @@ -21,8 +21,8 @@ TFormParamFile = record Bytes: TBytes; ContentType: string; procedure Clear; - constructor CreateFromRequest(const ARequest: TWebRequest; const AFieldName: string); overload; - constructor CreateFromRequest(const ARequest: TWebRequest; const AFileIndex: Integer); overload; + constructor CreateFromRequest(const ARequest: IMARSRequest; const AFieldName: string); overload; + constructor CreateFromRequest(const ARequest: IMARSRequest; const AFileIndex: Integer); overload; constructor Create(const AFieldName: string; const AFileName: string; const ABytes: TBytes; const AContentType: string); function ToString: string; end; @@ -33,8 +33,8 @@ TFormParam = record function IsFile: Boolean; function AsFile: TFormParamFile; procedure Clear; - constructor CreateFromRequest(const ARequest: TWebRequest; const AFieldName: string); overload; - constructor CreateFromRequest(const ARequest: TWebRequest; const AFileIndex: Integer); overload; + constructor CreateFromRequest(const ARequest: IMARSRequest; const AFieldName: string); overload; + constructor CreateFromRequest(const ARequest: IMARSRequest; const AFileIndex: Integer); overload; constructor Create(const AFieldName: string; const AValue: TValue); constructor CreateFile(const AFieldName: string; const AFileName: string; const ABytes: TBytes = nil; const AContentType: string = ''); function ToString: string; @@ -42,7 +42,7 @@ TFormParam = record TDump = class public - class procedure Request(const ARequest: TWebRequest; const AFileName: string); overload; virtual; + class procedure Request(const ARequest: IMARSRequest; const AFileName: string); overload; virtual; end; @@ -75,7 +75,7 @@ TDump = class {$endif} function DateToJSON(const ADate: TDateTime; AInputIsUTC: Boolean = False): string; - function JSONToDate(const ADate: string; AReturnUTC: Boolean = False): TDateTime; + function JSONToDate(const ADate: string; AReturnUTC: Boolean = False; const ADefault: TDateTime = 0.0): TDateTime; function IsMask(const AString: string): Boolean; function MatchesMask(const AString, AMask: string): Boolean; @@ -83,13 +83,15 @@ TDump = class function GuessTValueFromString(const AString: string): TValue; function TValueToString(const AValue: TValue; const ARecursion: Integer = 0): string; - procedure ZipStream(const ASource: TStream; const ADest: TStream); - procedure UnzipStream(const ASource: TStream; const ADest: TStream); + procedure ZipStream(const ASource: TStream; const ADest: TStream; const WindowBits: Integer = 15); + procedure UnzipStream(const ASource: TStream; const ADest: TStream; const WindowBits: Integer = 15); function StreamToBase64(const AStream: TStream): string; procedure Base64ToStream(const ABase64: string; const ADestStream: TStream); function StreamToBytes(const ASource: TStream): TBytes; + function GetEncodingName(const AEncoding: TEncoding): string; + implementation uses @@ -100,6 +102,20 @@ implementation , StrUtils, DateUtils, Masks, ZLib, Zip, NetEncoding ; +function GetEncodingName(const AEncoding: TEncoding): string; +begin + Result := ''; + + if AEncoding = TEncoding.ANSI then Result := 'ANSI' + else if AEncoding = TEncoding.ASCII then Result := 'ASCII' + else if AEncoding = TEncoding.BigEndianUnicode then Result :='BigEndianUnicode' + else if AEncoding = TEncoding.Unicode then Result :='Unicode' + else if AEncoding = TEncoding.UTF7 then Result :='UTF7' + else if AEncoding = TEncoding.UTF8 then Result :='UTF8' + else if AEncoding = TEncoding.Default then Result :='Default'; +end; + + function StreamToBytes(const ASource: TStream): TBytes; begin SetLength(Result, ASource.Size); @@ -108,14 +124,14 @@ function StreamToBytes(const ASource: TStream): TBytes; raise Exception.Create('Unable to copy all content to TBytes'); end; -procedure ZipStream(const ASource: TStream; const ADest: TStream); +procedure ZipStream(const ASource: TStream; const ADest: TStream; const WindowBits: Integer = 15); var LZipStream: TZCompressionStream; begin Assert(Assigned(ASource)); Assert(Assigned(ADest)); - LZipStream := TZCompressionStream.Create(clDefault, ADest); + LZipStream := TZCompressionStream.Create(ADest, TZCompressionLevel.zcDefault, WindowBits); try ASource.Position := 0; LZipStream.CopyFrom(ASource, ASource.Size); @@ -124,14 +140,14 @@ procedure ZipStream(const ASource: TStream; const ADest: TStream); end; end; -procedure UnzipStream(const ASource: TStream; const ADest: TStream); +procedure UnzipStream(const ASource: TStream; const ADest: TStream; const WindowBits: Integer = 15); var LZipStream: TZDecompressionStream; begin Assert(Assigned(ASource)); Assert(Assigned(ADest)); - LZipStream := TZDecompressionStream.Create(ASource); + LZipStream := TZDecompressionStream.Create(ASource, WindowBits); try ASource.Position := 0; ADest.CopyFrom(LZipStream, LZipStream.Size); @@ -315,9 +331,9 @@ function DateToJSON(const ADate: TDateTime; AInputIsUTC: Boolean = False): strin Result := DateToISO8601(ADate, AInputIsUTC); end; -function JSONToDate(const ADate: string; AReturnUTC: Boolean = False): TDateTime; +function JSONToDate(const ADate: string; AReturnUTC: Boolean = False; const ADefault: TDateTime = 0.0): TDateTime; begin - Result := 0.0; + Result := ADefault; if ADate<>'' then Result := ISO8601ToDate(ADate, AReturnUTC); end; @@ -474,24 +490,9 @@ procedure TFormParamFile.Clear; ContentType := ''; end; -constructor TFormParamFile.CreateFromRequest(const ARequest: TWebRequest; const AFieldName: string); -var - LIndex, LFileIndex: Integer; - LFile: TAbstractWebRequestFile; +constructor TFormParamFile.CreateFromRequest(const ARequest: IMARSRequest; const AFieldName: string); begin - Clear; - LFileIndex := -1; - for LIndex := 0 to ARequest.Files.Count - 1 do - begin - LFile := ARequest.Files[LIndex]; - if SameText(LFile.FieldName, AFieldName) then - begin - LFileIndex := LIndex; - Break; - end; - end; - - CreateFromRequest(ARequest, LFileIndex); + CreateFromRequest(ARequest, ARequest.GetFormFileParamIndex(AFieldName)); end; constructor TFormParamFile.Create(const AFieldName, AFileName: string; @@ -503,18 +504,16 @@ constructor TFormParamFile.Create(const AFieldName, AFileName: string; ContentType := AContentType; end; -constructor TFormParamFile.CreateFromRequest(const ARequest: TWebRequest; +constructor TFormParamFile.CreateFromRequest(const ARequest: IMARSRequest; const AFileIndex: Integer); var - LFile: TAbstractWebRequestFile; + LFieldName, LFileName, LContentType: string; + LBytes: TBytes; begin - Clear; - if (AFileIndex >= 0) and (AFileIndex < ARequest.Files.Count) then - begin - LFile := ARequest.Files[AFileIndex]; - - Create(LFile.FieldName, LFile.FileName, StreamToBytes(LFile.Stream), LFile.ContentType); - end; + if ARequest.GetFormFileParam(AFileIndex, LFieldName, LFileName, LBytes, LContentType) then + Create(LFieldName, LFileName, LBytes, LContentType) + else + raise Exception.CreateFmt('Unable to extract data for file form param index %d', [AFileIndex]); end; function TFormParamFile.ToString: string; @@ -552,7 +551,7 @@ constructor TFormParam.CreateFile(const AFieldName, AFileName: string; ); end; -constructor TFormParam.CreateFromRequest(const ARequest: TWebRequest; +constructor TFormParam.CreateFromRequest(const ARequest: IMARSRequest; const AFileIndex: Integer); var LValue: TFormParamFile; @@ -563,17 +562,17 @@ constructor TFormParam.CreateFromRequest(const ARequest: TWebRequest; FieldName := LValue.FieldName; end; -constructor TFormParam.CreateFromRequest(const ARequest: TWebRequest; +constructor TFormParam.CreateFromRequest(const ARequest: IMARSRequest; const AFieldName: string); var LIndex: Integer; begin Clear; - LIndex := ARequest.ContentFields.IndexOfName(AFieldName); + LIndex := ARequest.GetFormParamIndex(AFieldName); if LIndex <> -1 then begin FieldName := AFieldName; - Value := ARequest.ContentFields.ValueFromIndex[LIndex]; + Value := ARequest.GetFormParamValue(LIndex); end else begin @@ -599,7 +598,7 @@ function TFormParam.ToString: string; { TDump } -class procedure TDump.Request(const ARequest: TWebRequest; +class procedure TDump.Request(const ARequest: IMARSRequest; const AFileName: string); var LSS: TStringStream; @@ -634,24 +633,23 @@ class procedure TDump.Request(const ARequest: TWebRequest; end; LHeaders := string.join(sLineBreak, [ - 'PathInfo: ' + ARequest.PathInfo + 'RawPath: ' + ARequest.RawPath , 'Method: ' + ARequest.Method - , 'ProtocolVersion: ' + ARequest.ProtocolVersion , 'Authorization: ' + ARequest.Authorization , 'Accept: ' + ARequest.Accept - , 'ContentFields: ' + ARequest.ContentFields.CommaText - , 'CookieFields: ' + ARequest.CookieFields.CommaText - , 'QueryFields: ' + ARequest.QueryFields.CommaText +// , 'ContentFields: ' + ARequest.ContentFields.CommaText +// , 'CookieFields: ' + ARequest.CookieFields.CommaText +// , 'QueryFields: ' + ARequest.QueryFields.CommaText - , 'ContentType: ' + ARequest.ContentType - , 'ContentEncoding: ' + ARequest.ContentEncoding - , 'ContentLength: ' + ARequest.ContentLength.ToString - , 'ContentVersion: ' + ARequest.ContentVersion +// , 'ContentType: ' + ARequest.ContentType +// , 'ContentEncoding: ' + ARequest.ContentEncoding +// , 'ContentLength: ' + ARequest.ContentLength.ToString +// , 'ContentVersion: ' + ARequest.ContentVersion - , 'RemoteAddr: ' + ARequest.RemoteAddr - , 'RemoteHost: ' + ARequest.RemoteHost - , 'RemoteIP: ' + ARequest.RemoteIP +// , 'RemoteAddr: ' + ARequest.RemoteAddr +// , 'RemoteHost: ' + ARequest.RemoteHost +// , 'RemoteIP: ' + ARequest.RemoteIP ]); LSS := TStringStream.Create(LHeaders + sLineBreak + sLineBreak + LRawString); diff --git a/Source/MARS.Data.FireDAC.ReadersAndWriters.pas b/Source/MARS.Data.FireDAC.ReadersAndWriters.pas index defef415..7f04f750 100644 --- a/Source/MARS.Data.FireDAC.ReadersAndWriters.pas +++ b/Source/MARS.Data.FireDAC.ReadersAndWriters.pas @@ -10,19 +10,13 @@ interface uses - Classes, SysUtils, Rtti + Classes, SysUtils, Rtti, DB - , DB - - , MARS.Core.Attributes - , MARS.Core.Activation.Interfaces - , MARS.Core.Declarations - , MARS.Core.MediaType - , MARS.Core.Classes - , MARS.Core.MessageBodyWriter - , MARS.Core.MessageBodyReader - , MARS.Core.Utils - , MARS.Data.Utils; +, MARS.Core.Attributes, MARS.Core.Activation.Interfaces, MARS.Core.Declarations +, MARS.Core.MediaType, MARS.Core.Classes +, MARS.Core.MessageBodyWriter, MARS.Core.MessageBodyReader +, MARS.Core.Utils, MARS.Data.Utils +; type // --- READERS --- @@ -37,10 +31,9 @@ TArrayFDMemTableReader = class(TInterfacedObject, IMessageBodyReader) end; // --- WRITERS --- - [ Produces(TMediaType.APPLICATION_XML) - , Produces(TMediaType.APPLICATION_JSON_FIREDAC) - , Produces(TMediaType.APPLICATION_OCTET_STREAM) - ] + [ Produces(TMediaType.APPLICATION_JSON_FIREDAC) + , Produces(TMediaType.APPLICATION_XML_FIREDAC) + , Produces(TMediaType.APPLICATION_OCTET_STREAM) ] TFDDataSetWriter = class(TInterfacedObject, IMessageBodyWriter) procedure WriteTo(const AValue: TValue; const AMediaType: TMediaType; AOutputStream: TStream; const AActivation: IMARSActivation); @@ -50,6 +43,8 @@ TFDDataSetWriter = class(TInterfacedObject, IMessageBodyWriter) TArrayFDDataSetWriter = class(TInterfacedObject, IMessageBodyWriter) procedure WriteTo(const AValue: TValue; const AMediaType: TMediaType; AOutputStream: TStream; const AActivation: IMARSActivation); + class procedure WriteDataSet(const ADataSet: TValue; const AMediaType: TMediaType; + AOutputStream: TStream; const AActivation: IMARSActivation); class procedure WriteDataSets(const ADataSets: TValue; const AMediaType: TMediaType; AOutputStream: TStream; const AActivation: IMARSActivation); end; @@ -58,23 +53,25 @@ TArrayFDDataSetWriter = class(TInterfacedObject, IMessageBodyWriter) implementation uses - Generics.Collections - , FireDAC.Comp.Client, FireDAC.Comp.DataSet - , FireDAC.Stan.Intf - - , FireDAC.Stan.StorageBIN - , FireDAC.Stan.StorageJSON - , FireDAC.Stan.StorageXML - - , MARS.Core.JSON - , MARS.Core.MessageBodyWriters, MARS.Core.MessageBodyReaders - {$ifdef DelphiXE7_UP}, System.JSON {$endif} - , MARS.Core.Exceptions - , MARS.Rtti.Utils, MARS.Data.FireDAC.Utils + Generics.Collections +, FireDAC.Comp.Client, FireDAC.Comp.DataSet, FireDAC.Stan.Intf +, FireDAC.Stan.StorageBIN, FireDAC.Stan.StorageJSON, FireDAC.Stan.StorageXML + +, MARS.Core.JSON {$ifdef DelphiXE7_UP}, System.JSON {$endif} +, MARS.Core.MessageBodyWriters, MARS.Core.MessageBodyReaders, MARS.Data.MessageBodyWriters +, MARS.Core.Exceptions, MARS.Rtti.Utils, MARS.Data.FireDAC.Utils ; { TArrayFDDataSetWriter } +class procedure TArrayFDDataSetWriter.WriteDataSet(const ADataSet: TValue; + const AMediaType: TMediaType; AOutputStream: TStream; + const AActivation: IMARSActivation); +begin + WriteDataSets(TValue.From>([ADataSet.AsType]) + , AMediaType, AOutputStream, AActivation); +end; + class procedure TArrayFDDataSetWriter.WriteDataSets(const ADataSets: TValue; const AMediaType: TMediaType; AOutputStream: TStream; const AActivation: IMARSActivation); @@ -111,11 +108,15 @@ procedure TFDDataSetWriter.WriteTo(const AValue: TValue; const AMediaType: TMedi begin LDataset := AValue.AsType; - if AMediaType.Matches(TMediaType.APPLICATION_XML) then + if AMediaType.IsWildcard then + begin + TDataSetWriterJSON.WriteDataSet(LDataSet, AMediaType, AOutputStream, AActivation); + AActivation.Response.ContentType := TMediaType.APPLICATION_JSON; + end + else if AMediaType.Matches(TMediaType.APPLICATION_XML_FIREDAC) then LDataSet.SaveToStream(AOutputStream, sfXML) else if AMediaType.Matches(TMediaType.APPLICATION_JSON_FireDAC) then - TArrayFDDataSetWriter.WriteDataSets(TValue.From>([LDataSet]), - AMediaType, AOutputStream, AActivation) + TArrayFDDataSetWriter.WriteDataSet(LDataSet, AMediaType, AOutputStream, AActivation) else if AMediaType.Matches(TMediaType.APPLICATION_OCTET_STREAM) then LDataSet.SaveToStream(AOutputStream, sfBinary) else diff --git a/Source/MARS.Data.FireDAC.pas b/Source/MARS.Data.FireDAC.pas index 847a7b72..722c64ce 100644 --- a/Source/MARS.Data.FireDAC.pas +++ b/Source/MARS.Data.FireDAC.pas @@ -11,7 +11,6 @@ interface uses System.Classes, System.SysUtils, Generics.Collections, Rtti - , Data.DB // *** BEWARE *** // if your Delphi edition/license does not include FireDAC, @@ -229,8 +228,11 @@ class function TMARSFireDAC.LoadConnectionDefs(const AParameters: TMARSParameter LConnectionParams.CopyFrom(LData, LConnectionDefName); LParams := GetAsTStrings(LConnectionParams); try - FDManager.AddConnectionDef(LConnectionDefName, LParams.Values['DriverID'], LParams); - Result := Result + [LConnectionDefName]; + if FDManager.ConnectionDefs.FindConnectionDef(LConnectionDefName) = nil then + begin + FDManager.AddConnectionDef(LConnectionDefName, LParams.Values['DriverID'], LParams); + Result := Result + [LConnectionDefName]; + end; finally LParams.Free; end; @@ -573,9 +575,9 @@ class function TMARSFireDAC.GetContextValue(const AName: string; const AActivati else if SameText(LFirstToken, 'QueryParam') then Result := AActivation.URL.QueryTokenByName(LSecondTokenAndAll) else if SameText(LFirstToken, 'FormParam') then - Result := AActivation.Request.ContentFields.Values[LSecondTokenAndAll] + Result := AActivation.Request.GetFormParamValue(LSecondTokenAndAll) else if SameText(LFirstToken, 'Request') then - Result := ReadPropertyValue(AActivation.Request, LSecondTokenAndAll) + Result := ReadPropertyValue(AActivation.Request.AsObject, LSecondTokenAndAll) // else if SameText(LFirstToken, 'Response') then // Result := ReadPropertyValue(AActivation.Response, LSecondToken) else if SameText(LFirstToken, 'URL') then @@ -599,6 +601,9 @@ procedure TMARSFireDAC.InjectMacroValues(const ACommand: TFDCustomCommand; const LIndex: Integer; LMacro: TFDMacro; begin + if not Assigned(ACommand) then + Exit; + for LIndex := 0 to ACommand.Macros.Count-1 do begin LMacro := ACommand.Macros[LIndex]; @@ -612,6 +617,8 @@ procedure TMARSFireDAC.InjectParamValues(const ACommand: TFDCustomCommand; const LIndex: Integer; LParam: TFDParam; begin + if not Assigned(ACommand) then + Exit; for LIndex := 0 to ACommand.Params.Count-1 do begin LParam := ACommand.Params[LIndex]; @@ -681,4 +688,7 @@ procedure TMARSFDMemTable.DoUpdateErrorHandler(ARow: TFDDatSRow; FApplyUpdatesRes.AddError(ARow, AException, ARequest); end; +initialization + FDManager.SilentMode := True; + end. diff --git a/Source/MARS.Data.MessageBodyWriters.pas b/Source/MARS.Data.MessageBodyWriters.pas index 10584e19..b82b6b81 100644 --- a/Source/MARS.Data.MessageBodyWriters.pas +++ b/Source/MARS.Data.MessageBodyWriters.pas @@ -21,6 +21,8 @@ interface TDataSetWriterJSON = class(TInterfacedObject, IMessageBodyWriter) procedure WriteTo(const AValue: TValue; const AMediaType: TMediaType; AOutputStream: TStream; const AActivation: IMARSActivation); + class procedure WriteDataSet(const ADataSet: TValue; const AMediaType: TMediaType; + AOutputStream: TStream; const AActivation: IMARSActivation); end; [Produces(TMediaType.APPLICATION_JSON)] @@ -33,6 +35,8 @@ TArrayDataSetWriter = class(TInterfacedObject, IMessageBodyWriter) TDataSetWriterXML = class(TInterfacedObject, IMessageBodyWriter) procedure WriteTo(const AValue: TValue; const AMediaType: TMediaType; AOutputStream: TStream; const AActivation: IMARSActivation); + class procedure WriteDataSet(const ADataSet: TValue; const AMediaType: TMediaType; + AOutputStream: TStream; const AActivation: IMARSActivation); end; implementation @@ -46,6 +50,20 @@ implementation { TDataSetWriterJSON } +class procedure TDataSetWriterJSON.WriteDataSet(const ADataSet: TValue; + const AMediaType: TMediaType; AOutputStream: TStream; + const AActivation: IMARSActivation); +var + LWriter: TDataSetWriterJSON; +begin + LWriter := TDataSetWriterJSON.Create; + try + LWriter.WriteTo(ADataSet, AMediaType, AOutputStream, AActivation); + finally + LWriter.Free; + end; +end; + procedure TDataSetWriterJSON.WriteTo(const AValue: TValue; const AMediaType: TMediaType; AOutputStream: TStream; const AActivation: IMARSActivation); var @@ -61,13 +79,27 @@ procedure TDataSetWriterJSON.WriteTo(const AValue: TValue; const AMediaType: TMe { TDataSetWriterXML } +class procedure TDataSetWriterXML.WriteDataSet(const ADataSet: TValue; + const AMediaType: TMediaType; AOutputStream: TStream; + const AActivation: IMARSActivation); +var + LWriter: TDataSetWriterXML; +begin + LWriter := TDataSetWriterXML.Create; + try + LWriter.WriteTo(ADataSet, AMediaType, AOutputStream, AActivation); + finally + LWriter.Free; + end; +end; + procedure TDataSetWriterXML.WriteTo(const AValue: TValue; const AMediaType: TMediaType; AOutputStream: TStream; const AActivation: IMARSActivation); var LEncoding: TEncoding; LXML: string; begin - if not GetDesiredEncoding(AActivation, LEncoding) then + if not TMARSMessageBodyWriter.GetDesiredEncoding(AActivation, LEncoding) then LEncoding := TEncoding.UTF8; // UTF8 by default if AValue.AsObject is TClientDataSet then // CDS diff --git a/Source/MARS.Data.UniDAC.InjectionService.pas b/Source/MARS.Data.UniDAC.InjectionService.pas new file mode 100644 index 00000000..ad411daf --- /dev/null +++ b/Source/MARS.Data.UniDAC.InjectionService.pas @@ -0,0 +1,142 @@ +(* + Copyright 2016, MARS-Curiosity library + + Home: https://github.com/andrea-magni/MARS +*) +unit MARS.Data.UniDAC.InjectionService; + +{$I MARS.inc} + +interface + +uses + Classes, SysUtils, Rtti, Types + , MARS.Core.Injection + , MARS.Core.Injection.Interfaces + , MARS.Core.Injection.Types + , MARS.Core.Activation.Interfaces +; + +type + TMARSUniDACInjectionService = class(TInterfacedObject, IMARSInjectionService) + protected + function GetConnectionDefName(const ADestination: TRttiObject; + const AActivation: IMARSActivation): string; + public + procedure GetValue(const ADestination: TRttiObject; + const AActivation: IMARSActivation; out AValue: TInjectionValue); + + const UniDAC_ConnectionDefName_PARAM = 'UniDAC.ConnectionDefName'; + const UniDAC_ConnectionExpandMacros_PARAM = 'UniDAC.ConnectionExpandMacros'; + const UniDAC_ConnectionDefName_PARAM_DEFAULT = 'MAIN_DB'; + const UniDAC_ConnectionExpandMacros_PARAM_DEFAULT = False; + end; + +implementation + +uses + MARS.Rtti.Utils +, MARS.Core.Token, MARS.Core.URL, MARS.Core.Engine, MARS.Core.Application +, Data.DB +, Uni +, MARS.Data.UniDAC +; + + +{ TMARSUniDACInjectionService } + +function TMARSUniDACInjectionService.GetConnectionDefName( + const ADestination: TRttiObject; + const AActivation: IMARSActivation): string; +var + LConnectionDefName: string; + LExpandMacros: Boolean; +begin + LConnectionDefName := ''; + LExpandMacros := False; + + // field, property or method param annotation + ADestination.HasAttribute( + procedure (AAttrib: ConnectionAttribute) + begin + LConnectionDefName := AAttrib.ConnectionDefName; + LExpandMacros := AAttrib.ExpandMacros; + end + ); + + // second chance: method annotation + if (LConnectionDefName = '') then + AActivation.Method.HasAttribute( + procedure (AAttrib: ConnectionAttribute) + begin + LConnectionDefName := AAttrib.ConnectionDefName; + LExpandMacros := AAttrib.ExpandMacros; + end + ); + + // third chance: resource annotation + if (LConnectionDefName = '') then + AActivation.Resource.HasAttribute( + procedure (AAttrib: ConnectionAttribute) + begin + LConnectionDefName := AAttrib.ConnectionDefName; + LExpandMacros := AAttrib.ExpandMacros; + end + ); + + // last chance: application parameters + if (LConnectionDefName = '') then + begin + LConnectionDefName := AActivation.Application.Parameters.ByName( + UniDAC_ConnectionDefName_PARAM, UniDAC_ConnectionDefName_PARAM_DEFAULT + ).AsString; + LExpandMacros := AActivation.Application.Parameters.ByName( + UniDAC_ConnectionExpandMacros_PARAM, UniDAC_ConnectionExpandMacros_PARAM_DEFAULT + ).AsBoolean; + end; + + if LExpandMacros then + LConnectionDefName := TMARSUniDAC.GetContextValue(LConnectionDefName, AActivation, ftString).AsString; + + Result := LConnectionDefName; +end; + +procedure TMARSUniDACInjectionService.GetValue(const ADestination: TRttiObject; + const AActivation: IMARSActivation; out AValue: TInjectionValue); +begin + if ADestination.GetRttiType.IsObjectOfType(TUniConnection) then + AValue := TInjectionValue.Create( + TMARSUniDAC.CreateConnectionByDefName(GetConnectionDefName(ADestination, AActivation), AActivation) + ) + else if ADestination.GetRttiType.IsObjectOfType(TMARSUniDAC) then + AValue := TInjectionValue.Create( + TMARSUniDAC.Create(GetConnectionDefName(ADestination, AActivation), AActivation) + ); +end; + +procedure RegisterServices; +begin + TMARSInjectionServiceRegistry.Instance.RegisterService( + function :IMARSInjectionService + begin + Result := TMARSUniDACInjectionService.Create; + end + , function (const ADestination: TRttiObject): Boolean + var + LType: TRttiType; + begin + Result := ((ADestination is TRttiParameter) or (ADestination is TRttiField) or (ADestination is TRttiProperty)); + if Result then + begin + LType := ADestination.GetRttiType; + Result := LType.IsObjectOfType(TUniConnection) + or LType.IsObjectOfType(TMARSUniDAC); + end; + end + ); +end; + +initialization + RegisterServices; + +end. \ No newline at end of file diff --git a/Source/MARS.Data.UniDAC.ReadersAndWriters.pas b/Source/MARS.Data.UniDAC.ReadersAndWriters.pas new file mode 100644 index 00000000..ca0ae0f6 --- /dev/null +++ b/Source/MARS.Data.UniDAC.ReadersAndWriters.pas @@ -0,0 +1,177 @@ +(* + Copyright 2016, MARS-Curiosity library + + Home: https://github.com/andrea-magni/MARS +*) +unit MARS.Data.UniDAC.ReadersAndWriters; + +{$I MARS.inc} + +interface + +uses + Classes, SysUtils, Rtti, DB + // Devart UniDAC + , MemDS, VirtualTable + // MARS + , MARS.Core.Attributes + , MARS.Core.Activation.Interfaces + , MARS.Core.Declarations + , MARS.Core.MediaType + , MARS.Core.Classes + , MARS.Core.MessageBodyWriter + , MARS.Core.MessageBodyReader + , MARS.Core.Utils + , MARS.Data.Utils + , MARS.Data.UniDAC.Utils +; + +type + // --- READERS --- + [Consumes(APPLICATION_JSON_UniDAC)] + TArrayUniMemTableReader = class(TInterfacedObject, IMessageBodyReader) + public + function ReadFrom( + {$ifdef Delphi10Berlin_UP}const AInputData: TBytes;{$else}const AInputData: AnsiString;{$endif} + const ADestination: TRttiObject; const AMediaType: TMediaType; + const AActivation: IMARSActivation + ): TValue; virtual; + end; + + // --- WRITERS --- + [ Produces(TMediaType.APPLICATION_XML) + , Produces(APPLICATION_JSON_UniDAC) + , Produces(TMediaType.APPLICATION_OCTET_STREAM) + ] + TUniDataSetWriter = class(TInterfacedObject, IMessageBodyWriter) + procedure WriteTo(const AValue: TValue; const AMediaType: TMediaType; + AOutputStream: TStream; const AActivation: IMARSActivation); + end; + + [Produces(APPLICATION_JSON_UniDAC)] + TArrayUniDataSetWriter = class(TInterfacedObject, IMessageBodyWriter) + procedure WriteTo(const AValue: TValue; const AMediaType: TMediaType; + AOutputStream: TStream; const AActivation: IMARSActivation); + class procedure WriteDataSets(const ADataSets: TValue; const AMediaType: TMediaType; + AOutputStream: TStream; const AActivation: IMARSActivation); + end; + + +implementation + +uses + Generics.Collections + + , MARS.Core.JSON + , MARS.Core.MessageBodyWriters, MARS.Core.MessageBodyReaders + {$ifdef DelphiXE7_UP}, System.JSON {$endif} + , MARS.Core.Exceptions + , MARS.Rtti.Utils +; + +{ TArrayFDDataSetWriter } + +class procedure TArrayUniDataSetWriter.WriteDataSets(const ADataSets: TValue; + const AMediaType: TMediaType; AOutputStream: TStream; + const AActivation: IMARSActivation); +var + LWriter: TArrayUniDataSetWriter; +begin + LWriter := TArrayUniDataSetWriter.Create; + try + LWriter.WriteTo(ADataSets, AMediaType, AOutputStream, AActivation); + finally + LWriter.Free; + end; +end; + +procedure TArrayUniDataSetWriter.WriteTo(const AValue: TValue; const AMediaType: TMediaType; + AOutputStream: TStream; const AActivation: IMARSActivation); +var + LResult: TJSONObject; +begin + LResult := TUniDataSets.ToJSON(AValue); + try + TJSONValueWriter.WriteJSONValue(LResult, AMediaType, AOutputStream, AActivation); + finally + LResult.Free; + end; +end; + +{ TFDDataSetWriter } + +procedure TUniDataSetWriter.WriteTo(const AValue: TValue; const AMediaType: TMediaType; + AOutputStream: TStream; const AActivation: IMARSActivation); +var + LDataset: TMemDataSet; +begin + LDataset := AValue.AsType; + + if AMediaType.Matches(TMediaType.APPLICATION_XML) then + LDataSet.SaveToXML(AOutputStream) + else if AMediaType.Matches(APPLICATION_JSON_UniDAC) then + TArrayUniDataSetWriter.WriteDataSets( + TValue.From>([LDataSet]) + , AMediaType, AOutputStream, AActivation + ) + else if AMediaType.Matches(TMediaType.APPLICATION_OCTET_STREAM) then + LDataSet.SaveToXML(AOutputStream) + else + raise EMARSException.CreateFmt('Unsupported media type: %s', [AMediaType.ToString]); +end; + +{ TArrayFDMemTableReader } + +function TArrayUniMemTableReader.ReadFrom( +{$ifdef Delphi10Berlin_UP}const AInputData: TBytes;{$else}const AInputData: AnsiString;{$endif} + const ADestination: TRttiObject; const AMediaType: TMediaType; + const AActivation: IMARSActivation +): TValue; +var + LJSON: TJSONObject; +begin + Result := TValue.Empty; + + LJSON := TJSONValueReader.ReadJSONValue(AInputData, ADestination, AMediaType, AActivation).AsType; + if not Assigned(LJSON) then + Exit; + try + Result := TValue.From>(TUniDataSets.FromJSON(LJSON)); + finally + LJSON.Free; + end; +end; + +procedure RegisterReadersAndWriters; +begin + TMARSMessageBodyRegistry.Instance.RegisterWriter(TUniDataSetWriter); + + TMARSMessageBodyRegistry.Instance.RegisterWriter( + TArrayUniDataSetWriter + , function (AType: TRttiType; const AAttributes: TAttributeArray; AMediaType: string): Boolean + begin + Result := Assigned(AType) and AType.IsDynamicArrayOf; + end + , function (AType: TRttiType; const AAttributes: TAttributeArray; AMediaType: string): Integer + begin + Result := TMARSMessageBodyRegistry.AFFINITY_MEDIUM; + end + ); + + TMARSMessageBodyReaderRegistry.Instance.RegisterReader( + TArrayUniMemTableReader + , function(AType: TRttiType; const AAttributes: TAttributeArray; AMediaType: string): Boolean + begin + Result := Assigned(AType) and AType.IsDynamicArrayOf; + end + , function (AType: TRttiType; const AAttributes: TAttributeArray; AMediaType: string): Integer + begin + Result := TMARSMessageBodyRegistry.AFFINITY_MEDIUM + end + ); +end; + +initialization + RegisterReadersAndWriters; + +end. diff --git a/Source/MARS.Data.UniDAC.Utils.pas b/Source/MARS.Data.UniDAC.Utils.pas new file mode 100644 index 00000000..f9d4e749 --- /dev/null +++ b/Source/MARS.Data.UniDAC.Utils.pas @@ -0,0 +1,260 @@ +unit MARS.Data.UniDAC.Utils; + +interface + +uses + Classes, SysUtils, Generics.Collections, Rtti, System.JSON, Data.DB + // Devart UniDAC + , MemDS, VirtualTable + // MARS + , MARS.Core.JSON +; + +const + APPLICATION_JSON_UniDAC = 'application/json-unidac'; + +type + TMARSUniApplyUpdatesRes = record + dataset: string; + result: Integer; + errorCount: Integer; + errors: TArray; + constructor Create(const ADatasetName: string); + procedure AddError(E: EDatabaseError); + procedure Clear; + end; + + + TUniDataSets = class + private + protected + class procedure WriteDataSet(const ADest: TJSONObject; const ADataSet: TMemDataSet; + const ADefaultName: string); + public + // Base64(Zip(binary format)) + class function DataSetToEncodedBinaryString(const ADataSet: TMemDataSet): string; + class procedure EncodedBinaryStringToDataSet(const AString: string; const ADataSet: TVirtualTable); + + class function ToJSON(const ADataSets: TValue): TJSONObject; overload; + class procedure ToJSON(const ADataSets: TValue; const AStream: TStream; + const AEncoding: TEncoding = nil); overload; + + class function ToJSON(const ADataSets: TArray): TJSONObject; overload; + class procedure ToJSON(const ADataSets: TArray; const AStream: TStream; + const AEncoding: TEncoding = nil); overload; + + class function FromJSON(const AJSON: TJSONObject): TArray; overload; + class function FromJSON(const AStream: TStream; const AEncoding: TEncoding = nil): TArray; overload; + + class procedure FreeAll(var ADataSets: TArray); overload; +// class procedure FreeAll(var ADataSets: TArray); overload; + end; + +implementation + +uses + MARS.Core.Utils, MARS.Core.Exceptions +; + +class function TUniDataSets.ToJSON(const ADataSets: TArray): TJSONObject; +var + LIndex: Integer; +begin + Result := TJSONObject.Create; + try + for LIndex := Low(ADataSets) to High(ADataSets) do + WriteDataSet(Result, ADataSets[LIndex], 'DataSet' + LIndex.ToString); + except + Result.Free; + raise; + end; +end; + +class procedure TUniDataSets.FreeAll(var ADataSets: TArray); +var + LDataSet: TMemDataSet; +begin + for LDataSet in ADataSets do + LDataSet.DisposeOf; + ADataSets := []; +end; + +class function TUniDataSets.DataSetToEncodedBinaryString( + const ADataSet: TMemDataSet): string; +var + LBinStream, LZippedStream: TMemoryStream; +begin + Result := ''; + // Get Binary representation + LBinStream := TMemoryStream.Create; + try + ADataSet.SaveToXML(LBinStream); + + // Zip + LZippedStream := TMemoryStream.Create; + try + ZipStream(LBinStream, LZippedStream); + + Result := StreamToBase64(LZippedStream); + finally + LZippedStream.Free; + end; + finally + LBinStream.Free; + end; +end; + +class procedure TUniDataSets.EncodedBinaryStringToDataSet(const AString: string; + const ADataSet: TVirtualTable); +var + LZippedStream, LStream: TMemoryStream; +begin + Assert(Assigned(ADataSet)); + + LZippedStream := TMemoryStream.Create; + try + Base64ToStream(AString, LZippedStream); + LZippedStream.Position := 0; + + LStream := TMemoryStream.Create; + try + UnzipStream(LZippedStream, LStream); + LStream.Position := 0; + + + ADataSet.LoadFromStream(LStream); + finally + LStream.Free; + end; + finally + LZippedStream.Free; + end; +end; + +// not sure if still needed +//class procedure TUniDataSets.FreeAll(var ADataSets: TArray); +//begin +// FreeAll(TArray(ADataSets)); +//end; + +class function TUniDataSets.FromJSON(const AStream: TStream; + const AEncoding: TEncoding): TArray; +var + LJSONObject: TJSONObject; +begin + LJSONObject := StreamToJSONValue(AStream, AEncoding) as TJSONObject; + try + Result := TUniDataSets.FromJSON(LJSONObject); + finally + LJSONObject.Free; + end; +end; + +class function TUniDataSets.ToJSON(const ADataSets: TValue): TJSONObject; +var + LIndex: Integer; +begin + Assert(ADataSets.IsArray); + + Result := TJSONObject.Create; + try + for LIndex := 0 to ADataSets.GetArrayLength-1 do + WriteDataSet(Result, ADataSets.GetArrayElement(LIndex).AsObject as TMemDataSet + , 'DataSet' + LIndex.ToString); + except + Result.Free; + raise; + end; +end; + +class function TUniDataSets.FromJSON(const AJSON: TJSONObject): TArray; +var + LPair: TJSONPair; + LMemTable: TVirtualTable; +begin + Result := []; + for LPair in AJSON do + begin + if not (LPair.JsonValue is TJSONString) then + raise EMARSException.Create('Invalid JSON format [JSONToFDDataSets]'); + + LMemTable := TVirtualTable.Create(nil); + try + EncodedBinaryStringToDataSet((LPair.JsonValue as TJSONString).Value, LMemTable); + LMemTable.Name := LPair.JsonString.Value; + Result := Result + [LMemTable]; + except + LMemTable.Free; + raise; + end; + end; +end; + + +class procedure TUniDataSets.ToJSON(const ADataSets: TArray; + const AStream: TStream; const AEncoding: TEncoding); +var + LJSONObject: TJSONObject; +begin + LJSONObject := TUniDataSets.ToJSON(ADataSets); + try + JSONValueToStream(LJSONObject, AStream, AEncoding); + finally + LJSONObject.Free; + end; +end; + +class procedure TUniDataSets.ToJSON(const ADataSets: TValue; + const AStream: TStream; const AEncoding: TEncoding); +var + LJSONObject: TJSONObject; +begin + LJSONObject := TUniDataSets.ToJSON(ADataSets); + try + JSONValueToStream(LJSONObject, AStream, AEncoding); + finally + LJSONObject.Free; + end; +end; + +class procedure TUniDataSets.WriteDataSet(const ADest: TJSONObject; const ADataSet: TMemDataSet; + const ADefaultName: string); +var + LName: string; +begin + Assert(Assigned(ADest)); + Assert(Assigned(ADataSet)); + + if not ADataSet.Active then + ADataSet.Active := True; + LName := ADataSet.Name; + if LName = '' then + LName := ADefaultName; + + ADest.WriteStringValue(LName, DataSetToEncodedBinaryString(ADataSet)); +end; + +{ TMARSFDApplyUpdatesRes } + +procedure TMARSUniApplyUpdatesRes.AddError(E: EDatabaseError); +begin + errorCount := errorCount + 1; + errors := errors + [E.ClassName + ': ' + E.Message]; +end; + +procedure TMARSUniApplyUpdatesRes.Clear; +begin + dataset := ''; + result := 0; + errorCount := 0; + errors := []; +end; + +constructor TMARSUniApplyUpdatesRes.Create(const ADatasetName: string); +begin + Clear; + dataset := ADatasetName; +end; + + +end. diff --git a/Source/MARS.Data.UniDAC.pas b/Source/MARS.Data.UniDAC.pas new file mode 100644 index 00000000..55ea7ca4 --- /dev/null +++ b/Source/MARS.Data.UniDAC.pas @@ -0,0 +1,721 @@ +(* + Copyright 2016, MARS-Curiosity library + + Home: https://github.com/andrea-magni/MARS +*) +unit MARS.Data.UniDAC; + +{$I MARS.inc} + +interface + +uses + System.Classes, System.SysUtils, Generics.Collections, Rtti, Data.DB + // Devart UniDAC + , Uni, UniProvider, VirtualTable + // MARS + , MARS.Core.Application + , MARS.Core.Attributes + , MARS.Core.Classes + , MARS.Core.Declarations + , MARS.Core.JSON + , MARS.Core.MediaType + , MARS.Core.Registry + , MARS.Core.Token + , MARS.Core.URL + , MARS.Utils.Parameters + , MARS.Core.Activation.Interfaces + , MARS.Data.UniDAC.Utils +; + +type + EMARSUniDACException = class(EMARSApplicationException); + + MARSUniDACAttribute = class(TCustomAttribute); + + ConnectionAttribute = class(MARSUniDACAttribute) + private + FConnectionDefName: string; + FExpandMacros: Boolean; + public + constructor Create(AConnectionDefName: string; const AExpandMacros: Boolean = False); + property ConnectionDefName: string read FConnectionDefName; + property ExpandMacros: Boolean read FExpandMacros; + end; + + SQLStatementAttribute = class(MARSUniDACAttribute) + private + FName: string; + FSQLStatement: string; + public + constructor Create(AName, ASQLStatement: string); + property Name: string read FName; + property SQLStatement: string read FSQLStatement; + end; + + TContextValueProviderProc = reference to procedure (const AActivation: IMARSActivation; + const AName: string; const ADesiredType: TFieldType; out AValue: TValue); + + TMARSUniMemTable = class(TVirtualTable) + private + FApplyUpdatesRes: TMARSUniApplyUpdatesRes; + protected +// function DoApplyUpdates(ATable: TVirtualTable; AMaxErrors: Integer): Integer; override; +// procedure DoBeforeApplyUpdate; override; +// procedure DoUpdateError(DataSet: TDataSet; E: EDatabaseError; +// UpdateKind: TUpdateKind; var UpdateAction: TUpdateAction); override; + public + property ApplyUpdatesRes: TMARSUniApplyUpdatesRes read FApplyUpdatesRes; + end; + + TMARSUniDAC = class + private + FConnectionDefName: string; + FConnection: TUniConnection; + FActivation: IMARSActivation; + class var FConnectionDefs: TDictionary; + protected + procedure DoUpdateError(DataSet: TDataSet; E: EDatabaseError; + UpdateKind: TUpdateKind; var UpdateAction: TUpdateAction); + procedure SetConnectionDefName(const Value: string); virtual; + function GetConnection: TUniConnection; virtual; + class var FContextValueProviders: TArray; + public + const PARAM_AND_MACRO_DELIMITER = '_'; + + class function GetContextValue(const AName: string; const AActivation: IMARSActivation; + const ADesiredType: TFieldType = ftUnknown): TValue; virtual; + + procedure InjectParamValues(const ACommand: TUniSQL; + const AOnlyIfEmpty: Boolean = True); overload; virtual; + procedure InjectParamValues(const ACommand: TUniQuery; + const AOnlyIfEmpty: Boolean = True); overload; virtual; + procedure InjectMacroValues(const ACommand: TUniSQL; + const AOnlyIfEmpty: Boolean = True); overload; virtual; + procedure InjectMacroValues(const ACommand: TUniQuery; + const AOnlyIfEmpty: Boolean = True); overload; virtual; + + procedure InjectMacroAndParamValues(const ACommand: TUniSQL; const AOnlyIfEmpty: Boolean = True); overload; + procedure InjectMacroAndParamValues(const ACommand: TUniQuery; const AOnlyIfEmpty: Boolean = True); overload; + + constructor Create(const AConnectionDefName: string; + const AActivation: IMARSActivation = nil); virtual; + destructor Destroy; override; + + // Need to understand better before I do coding about below -Ertan +// function ApplyUpdates(const ADelta: TVirtualTable; const AUniQuery: TUniQuery; +// const AMaxErrors: Integer = -1): TMARSUniApplyUpdatesRes; overload; virtual; + + // Need to understand better before I do coding about below -Ertan +// function ApplyUpdates(ADataSets: TArray; ADeltas: TArray; +// AOnApplyUpdates: TProc = nil; +// ): TArray; overload; virtual; + + function CreateCommand(const ASQL: string = ''; const ATransaction: TUniTransaction = nil; + const AContextOwned: Boolean = True): TUniSQL; virtual; + function CreateQuery(const ASQL: string = ''; const ATransaction: TUniTransaction = nil; + const AContextOwned: Boolean = True; const AName: string = 'DataSet'): TUniQuery; virtual; + function CreateTransaction(const AContextOwned: Boolean = True): TUniTransaction; virtual; + + procedure ExecuteSQL(const ASQL: string; const ATransaction: TUniTransaction = nil; + const ABeforeExecute: TProc = nil; + const AAfterExecute: TProc = nil); virtual; + + function Query(const ASQL: string): TUniQuery; overload; virtual; + + function Query(const ASQL: string; + const ATransaction: TUniTransaction): TUniQuery; overload; virtual; + + function Query(const ASQL: string; const ATransaction: TUniTransaction; + const AContextOwned: Boolean): TUniQuery; overload; virtual; + + function Query(const ASQL: string; const ATransaction: TUniTransaction; + const AContextOwned: Boolean; + const AOnBeforeOpen: TProc): TUniQuery; overload; virtual; + + procedure Query(const ASQL: string; const ATransaction: TUniTransaction; + const AOnBeforeOpen: TProc; + const AOnDataSetReady: TProc); overload; virtual; + + function SetName(const AComponent: T; const AName: string): T; overload; + + procedure InTransaction(const ADoSomething: TProc); + + property Connection: TUniConnection read GetConnection; + property ConnectionDefName: string read FConnectionDefName write SetConnectionDefName; + property Activation: IMARSActivation read FActivation; + + class function LoadConnectionDefs(const AParameters: TMARSParameters; + const ASliceName: string = ''): TArray; + class procedure CloseConnectionDefs(const AConnectionDefNames: TArray); + class function CreateConnectionByDefName(const AConnectionDefName: string; + const AActivation: IMARSActivation): TUniConnection; + class function CreateConnectionByConnectString(const AConnectString: string): TUniConnection; + + class constructor CreateClass; + class destructor DestroyClass; + class procedure AddContextValueProvider(const AContextValueProviderProc: TContextValueProviderProc); + end; + +implementation + +uses + StrUtils, Variants, DBAccess + , MARS.Core.Utils + , MARS.Core.Exceptions + , MARS.Data.Utils + , MARS.Rtti.Utils + , MARS.Data.UniDAC.InjectionService + , MARS.Data.UniDAC.ReadersAndWriters +; + +function GetAsTStrings(const AParameters: TMARSParameters): TStrings; +var + LParam: TPair; +begin + Result := TStringList.Create; + try + for LParam in AParameters do + Result.Values[LParam.Key] := LParam.Value.ToString; + except + Result.Free; + raise; + end; +end; + +class function TMARSUniDAC.LoadConnectionDefs(const AParameters: TMARSParameters; + const ASliceName: string = ''): TArray; +var + LData, LConnectionParams: TMARSParameters; + LConnectionDefNames: TArray; + LConnectionDefName: string; + LParams: TStrings; + LConnectString: string; +begin + Result := []; + LData := TMARSParameters.Create(''); + try + LData.CopyFrom(AParameters, ASliceName); + LConnectionDefNames := LData.SliceNames; + + for LConnectionDefName in LConnectionDefNames do + begin + LConnectionParams := TMARSParameters.Create(LConnectionDefName); + try + LConnectionParams.CopyFrom(LData, LConnectionDefName); + LParams := GetAsTStrings(LConnectionParams); + try + LConnectString := LConnectionParams.ByNameText('ConnectString', '').AsString; + if LConnectString = '' then + begin + LParams.Delimiter := ';'; + LParams.QuoteChar := #0; + LConnectString := LParams.DelimitedText; + end; + + FConnectionDefs.AddOrSetValue(LConnectionDefName, LConnectString); + + Result := Result + [LConnectionDefName]; + finally + LParams.Free; + end; + finally + LConnectionParams.Free; + end; + end; + finally + LData.Free; + end; +end; + +function TMARSUniDAC.Query(const ASQL: string; + const ATransaction: TUniTransaction): TUniQuery; +begin + Result := Query(ASQL, ATransaction, True); +end; + +function TMARSUniDAC.Query(const ASQL: string; + const ATransaction: TUniTransaction; const AContextOwned: Boolean; + const AOnBeforeOpen: TProc): TUniQuery; +begin + Result := CreateQuery(ASQL, ATransaction, AContextOwned); + try + if Assigned(AOnBeforeOpen) then + AOnBeforeOpen(Result); + Result.Open; + except + if not AContextOwned then + Result.Free; + raise; + end; +end; + +function TMARSUniDAC.Query(const ASQL: string): TUniQuery; +begin + Result := Query(ASQL, nil, True); +end; + +procedure TMARSUniDAC.Query(const ASQL: string; const ATransaction: TUniTransaction; + const AOnBeforeOpen, AOnDataSetReady: TProc); +var + LQuery: TUniQuery; +begin + LQuery := Query(ASQL, ATransaction, False, AOnBeforeOpen); + try + if Assigned(AOnDataSetReady) then + AOnDataSetReady(LQuery); + finally + LQuery.Free; + end; +end; + +function TMARSUniDAC.Query(const ASQL: string; + const ATransaction: TUniTransaction; const AContextOwned: Boolean): TUniQuery; +begin + Result := CreateQuery(ASQL, ATransaction, AContextOwned); + Result.Open; +end; + +class function TMARSUniDAC.CreateConnectionByConnectString(const AConnectString: string): TUniConnection; +begin + Result := TUniConnection.Create(nil); + try + Result.ConnectString := AConnectString; + except + Result.Free(); + raise; + end; +end; + +class function TMARSUniDAC.CreateConnectionByDefName( + const AConnectionDefName: string; const AActivation: IMARSActivation): TUniConnection; +var + LConnectString: string; +begin + //AM TDictionary is not thread-safe but connection definitions are not supposed + // to change during server execution. A monitor object would be a safer choice, if + // errors should arise. + Result := nil; + if FConnectionDefs.TryGetValue(AConnectionDefName, LConnectString) then + Result := CreateConnectionByConnectString(LConnectString) +end; + +{ ConnectionAttribute } + +constructor ConnectionAttribute.Create(AConnectionDefName: string; const AExpandMacros: Boolean = False); +begin + inherited Create; + FConnectionDefName := AConnectionDefName; + FExpandMacros := AExpandMacros; +end; + +{ SQLStatementAttribute } + +constructor SQLStatementAttribute.Create(AName, ASQLStatement: string); +begin + inherited Create; + FName := AName; + FSQLStatement := ASQLStatement; +end; + +{ TMARSUniDAC } + +class procedure TMARSUniDAC.AddContextValueProvider( + const AContextValueProviderProc: TContextValueProviderProc); +begin + FContextValueProviders := FContextValueProviders + [TContextValueProviderProc(AContextValueProviderProc)]; +end; + +function DataSetByName(const ADataSets: TArray; const AName: string): TVirtualTable; +var + LCurrent: TVirtualTable; +begin + Result := nil; + for LCurrent in ADataSets do + if SameText(LCurrent.Name, AName) then + begin + Result := LCurrent; + Break; + end; + if Result = nil then + raise EMARSUniDACException.CreateFmt('DataSet %s not found', [AName]); +end; + +// Need to understand ApplyUpdates in MARS better before trying to convert below code lines -Ertan +(* +function TMARSUniDAC.ApplyUpdates(const ADelta: TMARSUniMemTable; + const AUniQuery: TUniQuery; + const AMaxErrors: Integer): TMARSUniApplyUpdatesRes; +var + LUniMemTable: TMARSUniMemTable; + LUniQuery: TUniQuery; +begin + Assert(AUniQuery <> nil); + + Result.Clear; + + LUniMemTable := TMARSUniMemTable.Create(nil); + try + LUniMemTable.Name := AUniQuery.Name; + LUniQuery := TUniQuery.Create(nil); + try + if Assigned(AUniQuery.SQL) then + LUniQuery.SQL := AUniQuery.SQL; + if Assigned(AUniQuery.SQLInsert) then + LUniQuery.SQLInsert := AUniQuery.SQLInsert; + if Assigned(AUniQuery.SQLUpdate) then + LUniQuery.SQLUpdate := AUniQuery.SQLUpdate; + if Assigned(AUniQuery.SQLDelete) then + LUniQuery.SQLDelete := AUniQuery.SQLDelete; + + LUniQuery.UpdatingTable := AUniQuery.UpdatingTable; + if LUniQuery.UpdatingTable = '' then + LUniQuery.UpdatingTable := LUniQuery.SQL.UpdateOptions.UpdateTableName; + + LUniMemTable.ResourceOptions.StoreItems := [siMeta, siDelta]; + LUniMemTable.CachedUpdates := True; + LUniMemTable.Adapter := LUniQuery; + LUniMemTable.Data := ADelta.Data; + LUniMemTable.ApplyUpdates(AMaxErrors); + Result := LUniMemTable.ApplyUpdatesRes; + finally + LUniQuery.Free; + end; + finally + LUniMemTable.Free; + end; +end; + +function TMARSUniDAC.ApplyUpdates(ADataSets: TArray; + ADeltas: TArray; + AOnBeforeApplyUpdates: TProc): TArray; +var + LDelta: TVirtualTable; + LDataSet: TFDAdaptedDataSet; + LFDAdapter: TFDTableAdapter; + +begin + Result := []; + for LDelta in ADeltas do + begin + LDataSet := DataSetByName(ADataSets, LDelta.Name) as TFDAdaptedDataSet; + Assert(LDataSet <> nil); + Assert(LDataSet.Command <> nil); + + InjectMacroAndParamValues(LDataSet.Command); + + if Assigned(AOnBeforeApplyUpdates) then + AOnBeforeApplyUpdates(LDataSet, LDelta); + + LFDAdapter := TFDTableAdapter.Create(nil); + try + LFDAdapter.Name := LDataSet.Name; + LFDAdapter.SelectCommand := LDataSet.Command; + Result := Result + [ApplyUpdates(LDelta, LFDAdapter, -1)]; + finally + LFDAdapter.Free; + end; + end; +end; +*) + +class procedure TMARSUniDAC.CloseConnectionDefs( + const AConnectionDefNames: TArray); +begin + FConnectionDefs.Clear; +end; + +constructor TMARSUniDAC.Create(const AConnectionDefName: string; + const AActivation: IMARSActivation); +begin + inherited Create(); + ConnectionDefName := AConnectionDefName; + FActivation := AActivation; +end; + +class constructor TMARSUniDAC.CreateClass; +begin + FContextValueProviders := []; + FConnectionDefs := TDictionary.Create(); +end; + +function TMARSUniDAC.CreateCommand(const ASQL: string; + const ATransaction: TUniTransaction; const AContextOwned: Boolean): TUniSQL; +begin + Result := TUniSQL.Create(nil); + try + Result.Connection := Connection; + Result.Transaction := ATransaction; + Result.SQL.Text := ASQL; + InjectMacroAndParamValues(Result); + if AContextOwned then + Activation.AddToContext(Result); + except + Result.Free; + raise; + end; +end; + +function TMARSUniDAC.CreateQuery(const ASQL: string; const ATransaction: TUniTransaction; + const AContextOwned: Boolean; const AName: string): TUniQuery; +begin + Result := TUniQuery.Create(nil); + try + Result.Name := AName; + Result.Connection := Connection; + Result.Transaction := ATransaction; + Result.SQL.Text := ASQL; + InjectMacroAndParamValues(Result); + if AContextOwned then + Activation.AddToContext(Result); + except + Result.Free; + raise; + end; +end; + +function TMARSUniDAC.CreateTransaction(const AContextOwned: Boolean): TUniTransaction; +begin + Result := TUniTransaction.Create(nil); + try + Result.DefaultConnection := Connection; + if AContextOwned then + Activation.AddToContext(Result); + except + Result.Free; + raise; + end; +end; + +destructor TMARSUniDAC.Destroy; +begin + FreeAndNil(FConnection); + inherited; +end; + +class destructor TMARSUniDAC.DestroyClass; +begin + FreeAndNil(FConnectionDefs); +end; + +procedure TMARSUniDAC.DoUpdateError(DataSet: TDataSet; E: EDatabaseError; + UpdateKind: TUpdateKind; var UpdateAction: TUpdateAction); +begin + +end; + +procedure TMARSUniDAC.ExecuteSQL(const ASQL: string; const ATransaction: TUniTransaction; + const ABeforeExecute, AAfterExecute: TProc); +var + LCommand: TUniSQL; +begin + LCommand := CreateCommand(ASQL, ATransaction, False); + try + if Assigned(ABeforeExecute) then + ABeforeExecute(LCommand); + LCommand.Execute(); + if Assigned(AAfterExecute) then + AAfterExecute(LCommand); + finally + LCommand.Free; + end; +end; + +function TMARSUniDAC.GetConnection: TUniConnection; +begin + if not Assigned(FConnection) then + FConnection := CreateConnectionByDefName(ConnectionDefName, Activation); + Result := FConnection; +end; + +class function TMARSUniDAC.GetContextValue(const AName: string; const AActivation: IMARSActivation; + const ADesiredType: TFieldType): TValue; +var + LFirstToken, LSecondToken: string; + LHasThirdToken: Boolean; + LSecondTokenAndAll, LThirdTokenAndAll: string; + LNameTokens: TArray; + LFirstDelim, LSecondDelim: Integer; + + LIndex: Integer; + LCustomProvider: TContextValueProviderProc; +begin + Result := TValue.Empty; + LNameTokens := AName.Split([PARAM_AND_MACRO_DELIMITER]); + if Length(LNameTokens) < 2 then + Exit; + + LFirstToken := LNameTokens[0]; + LSecondToken := LNameTokens[1]; + LFirstDelim := AName.IndexOf(PARAM_AND_MACRO_DELIMITER); + LSecondTokenAndAll := AName.Substring(LFirstDelim + 1); + LHasThirdToken := Length(LNameTokens) > 2; + if LHasThirdToken then + begin + LSecondDelim := AName.IndexOf(PARAM_AND_MACRO_DELIMITER, LFirstDelim + Length(PARAM_AND_MACRO_DELIMITER)); + LThirdTokenAndAll := AName.Substring(LSecondDelim + 1); + end; + + if SameText(LFirstToken, 'Token') then + begin + Result := ReadPropertyValue(AActivation.Token, LSecondToken); + + if SameText(LSecondToken, 'HasRole') and LHasThirdToken then + Result := AActivation.Token.HasRole(LThirdTokenAndAll) + else if SameText(LSecondToken, 'Claim') and LHasThirdToken then + Result := AActivation.Token.Claims.ByNameText(LThirdTokenAndAll); + end + else if SameText(LFirstToken, 'PathParam') then + begin + LIndex := AActivation.URLPrototype.GetPathParamIndex(LSecondTokenAndAll); + if (LIndex > -1) and (LIndex < Length(AActivation.URL.PathTokens)) then + Result := AActivation.URL.PathTokens[LIndex] { TODO -oAndrea : Try to convert according to ADesiredType } + else + raise EMARSUniDACException.CreateFmt('PathParam not found: %s', [LSecondTokenAndAll]); + end + else if SameText(LFirstToken, 'QueryParam') then + Result := AActivation.URL.QueryTokenByName(LSecondTokenAndAll) + else if SameText(LFirstToken, 'FormParam') then + Result := AActivation.Request.GetFormParamValue(LSecondTokenAndAll) + else if SameText(LFirstToken, 'Request') then + Result := ReadPropertyValue(AActivation.Request.AsObject, LSecondTokenAndAll) +// else if SameText(LFirstToken, 'Response') then +// Result := ReadPropertyValue(AActivation.Response, LSecondToken) + else if SameText(LFirstToken, 'URL') then + Result := ReadPropertyValue(AActivation.URL, LSecondTokenAndAll) + else if SameText(LFirstToken, 'URLPrototype') then + Result := ReadPropertyValue(AActivation.URLPrototype, LSecondTokenAndAll) + else // last chance, custom injection + for LCustomProvider in FContextValueProviders do + LCustomProvider(AActivation, AName, ADesiredType, Result); +end; + +procedure TMARSUniDAC.InjectMacroAndParamValues( + const ACommand: TUniSQL; const AOnlyIfEmpty: Boolean); +begin + InjectMacroValues(ACommand, AOnlyIfEmpty); + InjectParamValues(ACommand, AOnlyIfEmpty); +end; + +procedure TMARSUniDAC.InjectMacroAndParamValues( + const ACommand: TUniQuery; const AOnlyIfEmpty: Boolean); +begin + InjectMacroValues(ACommand, AOnlyIfEmpty); + InjectParamValues(ACommand, AOnlyIfEmpty); +end; + +procedure TMARSUniDAC.InjectMacroValues(const ACommand: TUniSQL; const AOnlyIfEmpty: Boolean); +var + LIndex: Integer; + LMacro: TMacro; +begin + for LIndex := 0 to ACommand.Macros.Count-1 do + begin + LMacro := ACommand.Macros[LIndex]; + if (not AOnlyIfEmpty) or (LMacro.Value = '') then + LMacro.Value := GetContextValue(LMacro.Name, Activation).AsVariant; + end; +end; + +procedure TMARSUniDAC.InjectMacroValues(const ACommand: TUniQuery; const AOnlyIfEmpty: Boolean); +var + LIndex: Integer; + LMacro: TMacro; +begin + for LIndex := 0 to ACommand.Macros.Count-1 do + begin + LMacro := ACommand.Macros[LIndex]; + if (not AOnlyIfEmpty) or (LMacro.Value = '') then + LMacro.Value := GetContextValue(LMacro.Name, Activation).AsVariant; + end; +end; + +procedure TMARSUniDAC.InjectParamValues(const ACommand: TUniSQL; const AOnlyIfEmpty: Boolean); +var + LIndex: Integer; + LParam: TUniParam; +begin + for LIndex := 0 to ACommand.Params.Count-1 do + begin + LParam := ACommand.Params[LIndex]; + if ((not AOnlyIfEmpty) or LParam.IsNull) then + LParam.Value := GetContextValue(LParam.Name, Activation, LParam.DataType).AsVariant; + end; +end; + +procedure TMARSUniDAC.InjectParamValues(const ACommand: TUniQuery; const AOnlyIfEmpty: Boolean); +var + LIndex: Integer; + LParam: TUniParam; +begin + for LIndex := 0 to ACommand.Params.Count-1 do + begin + LParam := ACommand.Params[LIndex]; + if ((not AOnlyIfEmpty) or LParam.IsNull) then + LParam.Value := GetContextValue(LParam.Name, Activation, LParam.DataType).AsVariant; + end; +end; + +procedure TMARSUniDAC.InTransaction(const ADoSomething: TProc); +var + LTransaction: TUniTransaction; +begin + if Assigned(ADoSomething) then + begin + LTransaction := CreateTransaction(False); + try + LTransaction.StartTransaction; + try + ADoSomething(LTransaction); + LTransaction.Commit; + except + LTransaction.Rollback; + raise; + end; + finally + LTransaction.Free; + end; + end; +end; + +procedure TMARSUniDAC.SetConnectionDefName(const Value: string); +begin + if FConnectionDefName <> Value then + begin + FreeAndNil(FConnection); + FConnectionDefName := Value; + end; +end; + +function TMARSUniDAC.SetName(const AComponent: T; const AName: string): T; +begin + AComponent.Name := AName; + Result := AComponent; +end; + +{ TMARSFDMemTable } + +// Need to understand better before doing anything below -Ertan +(* +function TMARSFDMemTable.DoApplyUpdates(ATable: TFDDatSTable; + AMaxErrors: Integer): Integer; +begin + Result := inherited DoApplyUpdates(ATable, AMaxErrors); + FApplyUpdatesRes.result := Result; +end; + +procedure TMARSFDMemTable.DoBeforeApplyUpdate; +begin + inherited; + FApplyUpdatesRes := TMARSUniApplyUpdatesRes.Create(Self.Name); +end; + +procedure TMARSUniMemTable.DoUpdateErrorHandler(DataSet: TDataSet; E: EDatabaseError; + UpdateKind: TUpdateKind; var UpdateAction: TUpdateAction); +begin + inherited; + FApplyUpdatesRes.AddError(ARow, AException, ARequest); +end; +*) + +end. diff --git a/Source/MARS.Data.Utils.pas b/Source/MARS.Data.Utils.pas index f7a4953a..95aa21b8 100644 --- a/Source/MARS.Data.Utils.pas +++ b/Source/MARS.Data.Utils.pas @@ -28,10 +28,8 @@ function DatasetMetadataToJSONObject(const ADataSet: TDataSet): TJSONObject; implementation uses - Rtti - , StrUtils, DateUtils - , MARS.Rtti.Utils - , MARS.Core.Utils; + Rtti, StrUtils, DateUtils, System.JSON +, MARS.Rtti.Utils, MARS.Core.Utils; type TJSONFieldType = (NestedObject, NestedArray, SimpleValue); @@ -260,6 +258,8 @@ function DataSetToXML(const ADataSet: TDataSet; const AAcceptFunc: TFunc'; end; function DatasetMetadataToJSONObject(const ADataSet: TDataSet): TJSONObject; diff --git a/Source/MARS.JOSEJWT.Token.pas b/Source/MARS.JOSEJWT.Token.pas index 77506dfb..12a47542 100644 --- a/Source/MARS.JOSEJWT.Token.pas +++ b/Source/MARS.JOSEJWT.Token.pas @@ -46,7 +46,7 @@ function TMARSJOSEJWTToken.BuildJWTToken(const ASecret: string; try LKey := TJWK.Create(ASecret); try - LSigner.Sign(LKey, HS256); + LSigner.Sign(LKey, TJOSEAlgorithmId.HS256); Result := LSigner.CompactToken; finally diff --git a/Source/MARS.JsonDataObjects.ReadersAndWriters.pas b/Source/MARS.JsonDataObjects.ReadersAndWriters.pas index 5b11e3db..b4b2bffb 100644 --- a/Source/MARS.JsonDataObjects.ReadersAndWriters.pas +++ b/Source/MARS.JsonDataObjects.ReadersAndWriters.pas @@ -50,7 +50,7 @@ procedure TJsonDataObjectsWriter.WriteTo(const AValue: TValue; const AMediaType: LJsonBO: TJsonBaseObject; LEncoding: TEncoding; begin - if not GetDesiredEncoding(AActivation, LEncoding) then + if not TMARSMessageBodyWriter.GetDesiredEncoding(AActivation, LEncoding) then LEncoding := TEncoding.UTF8; LJsonBO := AValue.AsObject as TJsonBaseObject; @@ -87,5 +87,3 @@ initialization RegisterReadersAndWriters; end. - - diff --git a/Source/MARS.Rtti.Utils.pas b/Source/MARS.Rtti.Utils.pas index fb4559d3..a087ba00 100644 --- a/Source/MARS.Rtti.Utils.pas +++ b/Source/MARS.Rtti.Utils.pas @@ -1,1058 +1,1077 @@ -(* - Copyright 2016, MARS-Curiosity library - - Home: https://github.com/andrea-magni/MARS -*) -unit MARS.Rtti.Utils; - -{$I MARS.inc} - -interface - -uses - SysUtils, Classes, RTTI, TypInfo - , MARS.Core.JSON - , DB; - -type - TRttiObjectHelper = class helper for TRttiObject - public - function GetRttiType: TRttiType; - procedure SetValue(AInstance: Pointer; const AValue: TValue); - - function HasAttribute(const AInherited: Boolean = False): Boolean; overload; inline; - function HasAttribute( - const ADoSomething: TProc; const AInherited: Boolean = False): Boolean; overload; inline; - - function GetAllAttributes(const AInherited: Boolean = False): TArray; inline; - - function ForEachAttribute( - const ADoSomething: TProc; const AInherited: Boolean = False): Integer; overload; inline; - function ForEachAttribute( - const ADoSomething: TFunc; const AInherited: Boolean = False): Integer; overload; inline; - end; - - TRttiTypeHelper = class helper(TRttiObjectHelper) for TRttiType - protected - public - function ForEachMethodWithAttribute( - const ADoSomething: TFunc): Integer; inline; - - function ForEachFieldWithAttribute( - const ADoSomething: TFunc): Integer; - - function ForEachPropertyWithAttribute( - const ADoSomething: TFunc): Integer; - - function IsDynamicArrayOf(const AAllowInherithance: Boolean = True): Boolean; - function IsDynamicArrayOfRecord: Boolean; - function IsArray: Boolean; overload; - function IsArray(out AElementType: TRttiType): Boolean; overload; - function GetArrayElementType: TRttiType; - - function IsObjectOfType(const AAllowInherithance: Boolean = True): Boolean; overload; - function IsObjectOfType(const AClass: TClass; const AAllowInherithance: Boolean = True): Boolean; overload; - - function FindMethodFunc(const AName: string): TRttiMethod; overload; - function FindMethodFunc(const AName: string): TRttiMethod; overload; - function FindMethodFunc(const AName: string): TRttiMethod; overload; - function FindMethodFunc(const AName: string): TRttiMethod; overload; - - function FindMethodFunc(const AName: string): TRttiMethod; overload; - end; - - TRttiHelper = class - public - class function MethodParametersMatch(const AMethod: TRttiMethod): Boolean; overload; - class function MethodParametersMatch(const AMethod: TRttiMethod): Boolean; overload; - class function MethodParametersMatch(const AMethod: TRttiMethod): Boolean; overload; - class function MethodParametersMatch(const AMethod: TRttiMethod): Boolean; overload; - class function MethodParametersMatch(const AMethod: TRttiMethod): Boolean; overload; - - class function IfHasAttribute(AInstance: TObject): Boolean; overload; - class function IfHasAttribute(AInstance: TObject; const ADoSomething: TProc): Boolean; overload; - - class function ForEachAttribute(const AInstance: TObject; - const ADoSomething: TProc): Integer; overload; - - class function ForEachAttribute(const AAttributes: TArray; - const ADoSomething: TProc): Integer; overload; - class function ForEachAttribute(const AAttributes: TArray; - const ADoSomething: TFunc): Integer; overload; - - class function ForEachMethodWithAttribute(const AMethods: TArray; - const ADoSomething: TFunc): Integer; - - class function ForEachFieldWithAttribute(AInstance: TObject; const ADoSomething: TFunc): Integer; overload; - class function ForEachField(AInstance: TObject; const ADoSomething: TFunc): Integer; - - class function FindParameterLessConstructor(const AClass: TClass): TRttiMethod; - end; - - TRecord = class - public - class procedure ToDataSet(const ARecord: R; const ADataSet: TDataSet; const AAppend: Boolean = False); - class procedure FromDataSet(var ARecord: R; const ADataSet: TDataSet); - class function DataSetToArray(const ADataSet: TDataSet): TArray; - class procedure SetFieldByName(var ARecord: R; const AFieldName: string; const AValue: TValue); - class function GetFieldByName(var ARecord: R; const AFieldName: string): TValue; overload; - class function GetFieldByName(var ARecord: R; const AFieldName: string; const ADefault: TValue): TValue; overload; - class function FromStrings(const AStrings: TStrings): R; - class procedure ToStrings(const ARecord: R; var AStrings: TStrings; const AClear: Boolean = True); - class function ToArrayOfString(const ARecord: R): TArray; - end; - TOnGetRecordFieldValueProc = reference to procedure (const AName: string; const AField: TRttiField; var AValue: TValue); - - function StringsToRecord(const AStrings: TStrings; const ARecordType: TRttiType; - const AOnGetFieldValue: TOnGetRecordFieldValueProc = nil): TValue; - -function ExecuteMethod(const AInstance: TValue; const AMethodName: string; const AArguments: array of TValue; - const ABeforeExecuteProc: TProc{ = nil}; const AAfterExecuteProc: TProc{ = nil}): Boolean; overload; - -function ExecuteMethod(const AInstance: TValue; AMethod: TRttiMethod; const AArguments: array of TValue; - const ABeforeExecuteProc: TProc{ = nil}; const AAfterExecuteProc: TProc{ = nil}): Boolean; overload; - -function ReadPropertyValue(AInstance: TObject; const APropertyName: string): TValue; -procedure SetArrayLength(var AArray: TValue; const AArrayType: TRttiType; const ANewSize: Integer); - -function StringToTValue(const AString: string; const ADesiredType: TRttiType): TValue; - -implementation - -uses - StrUtils, DateUtils, Generics.Collections - , MARS.Core.Utils -; - -function StringsToRecord(const AStrings: TStrings; const ARecordType: TRttiType; - const AOnGetFieldValue: TOnGetRecordFieldValueProc = nil): TValue; -var - LField: TRttiField; -// LValue: TValue; - LRecordInstance: Pointer; -// LFilterProc: TToRecordFilterProc; - LAccept: Boolean; - LJSONName: string; - LAssignedValuesField: TRttiField; - LAssignedValues: TArray; - LValue: TValue; - -// function GetRecordFilterProc: TToRecordFilterProc; -// var -// LMethod: TRttiMethod; -// begin -// Result := nil; -// // looking for TMyRecord.ToRecordFilter(const AField: TRttiField; const AObj: TJSONObject): Boolean; -// -// LMethod := ARecordType.FindMethodFunc('ToRecordFilter'); -// if Assigned(LMethod) then -// Result := -// procedure (const AField: TRttiField; const ARecord: TValue; const AJSONObject: TJSONObject; var AAccept: Boolean) -// begin -// AAccept := LMethod.Invoke(ARecord, [AField, AJSONObject]).AsBoolean; -// end; -// end; - -begin - TValue.Make(nil, ARecordType.Handle, Result); - LRecordInstance := Result.GetReferenceToRawData; - -// LFilterProc := AFilterProc; -// if not Assigned(LFilterProc) then -// LFilterProc := GetRecordFilterProc(); - - LAssignedValuesField := ARecordType.GetField('_AssignedValues'); - if Assigned(LAssignedValuesField) - and not LAssignedValuesField.FieldType.IsDynamicArrayOf - then - LAssignedValuesField := nil; - LAssignedValues := []; - - for LField in ARecordType.GetFields do - begin - LAccept := True; -// if Assigned(LFilterProc) then -// LFilterProc(LField, Result, Self, LAccept); - - if LAccept then - begin - LJSONName := LField.Name; - LField.HasAttribute( - procedure (AAttr: JSONNameAttribute) - begin - LJSONName := AAttr.Name; - end - ); - if LJSONName <> '' then - begin - LValue := StringToTValue(AStrings.Values[LJSONName], LField.FieldType); - if Assigned(AOnGetFieldValue) then - AOnGetFieldValue(LJSONName, LField, LValue); - LField.SetValue(LRecordInstance, LValue); - LAssignedValues := LAssignedValues + [LField.Name]; - end; - end; - end; - if Assigned(LAssignedValuesField) then - LAssignedValuesField.SetValue(LRecordInstance, TValue.From>(LAssignedValues)); -end; - -function StringToTValue(const AString: string; const ADesiredType: TRttiType): TValue; -begin - if ADesiredType.IsObjectOfType then - Result := TJSONObject.ParseJSONValue(AString) - else - begin - case ADesiredType.TypeKind of - tkInt64: Result := StrToInt64Def(AString, 0); - tkInteger: Result := StrToIntDef(AString, 0); - tkEnumeration: begin - if SameText(ADesiredType.Name, 'Boolean') then - Result := StrToBoolDef(AString, False); - end; - tkFloat: begin - if IndexStr(ADesiredType.Name, ['TDate', 'TDateTime', 'TTime']) <> -1 then - begin - try - Result := ISO8601ToDate(AString); - except - Result := StrToDateTime(AString) - end; - end - else - begin - Result := StrToFloatDef(AString - , StrToFloatDef(AString, 0.0, TFormatSettings.Create('en')) // second chance - ); - end; - end; - - {$ifdef DelphiXE7_UP} - tkChar: begin - if AString.IsEmpty then - Result := '' - else - Result := TValue.From(AString.Chars[0]); - end; - {$else} - tkChar: Result := TValue.From(Copy(AString, 1, 1)); - {$endif} - else - Result := AString; - end; - end; -end; - - -procedure SetArrayLength(var AArray: TValue; const AArrayType: TRttiType; const ANewSize: Integer); -begin - if AArrayType is TRttiArrayType then - begin - raise Exception.Create('Not yet implemented: SetArrayLength TRttiArrayType'); - { TODO -oAndrea : probably not needed } - end - else if AArrayType is TRttiDynamicArrayType then - DynArraySetLength(PPointer(AArray.GetReferenceToRawData)^, AArrayType.Handle, 1, @ANewSize); -end; - -function ReadPropertyValue(AInstance: TObject; const APropertyName: string): TValue; -var - LContext: TRttiContext; - LType: TRttiType; - LProperty: TRttiProperty; -begin - Result := TValue.Empty; - LType := LContext.GetType(AInstance.ClassType); - if Assigned(LType) then - begin - LProperty := LType.GetProperty(APropertyName); - if Assigned(LProperty) then - Result := LProperty.GetValue(AInstance); - end; -end; - -function ExecuteMethod(const AInstance: TValue; AMethod: TRttiMethod; - const AArguments: array of TValue; const ABeforeExecuteProc: TProc{ = nil}; - const AAfterExecuteProc: TProc{ = nil}): Boolean; -var - LResult: TValue; -begin - if Assigned(ABeforeExecuteProc) then - ABeforeExecuteProc(); - LResult := AMethod.Invoke(AInstance, AArguments); - Result := True; - if Assigned(AAfterExecuteProc) then - AAfterExecuteProc(LResult); -end; - -function ExecuteMethod(const AInstance: TValue; const AMethodName: string; - const AArguments: array of TValue; const ABeforeExecuteProc: TProc{ = nil}; - const AAfterExecuteProc: TProc{ = nil}): Boolean; -var - LContext: TRttiContext; - LType: TRttiType; - LMethod: TRttiMethod; -begin - Result := False; - LType := LContext.GetType(AInstance.TypeInfo); - if Assigned(LType) then - begin - LMethod := LType.GetMethod(AMethodName); - if Assigned(LMethod) then - Result := ExecuteMethod(AInstance, LMethod, AArguments, ABeforeExecuteProc, AAfterExecuteProc); - end; -end; - -{ TRttiObjectHelper } - -function TRttiObjectHelper.ForEachAttribute( - const ADoSomething: TProc; const AInherited: Boolean): Integer; -begin - Result := TRttiHelper.ForEachAttribute(GetAllAttributes(AInherited), ADoSomething); -end; - -function TRttiObjectHelper.HasAttribute(const AInherited: Boolean): Boolean; -begin - Result := HasAttribute(nil, AInherited); -end; - -function TRttiObjectHelper.ForEachAttribute( - const ADoSomething: TFunc; const AInherited: Boolean): Integer; -begin - Result := TRttiHelper.ForEachAttribute(GetAllAttributes(AInherited), ADoSomething); -end; - -function TRttiObjectHelper.GetAllAttributes( - const AInherited: Boolean): TArray; -var - LBaseType: TRttiType; - LType: TRttiType; -begin - Result := Self.GetAttributes; - - { TODO -oAndrea : Implement AInherited = True behavior when Self is a TRttiMethod instance } - LType := Self.GetRttiType; - if AInherited and Assigned(LType) then - begin - LBaseType := LType.BaseType; - while Assigned(LBaseType) do - begin - Result := Result + LBaseType.GetAttributes; - LBaseType := LBaseType.BaseType; - end; - end; -end; - -function TRttiObjectHelper.GetRttiType: TRttiType; -begin - Result := nil; - if Self is TRttiField then - Result := TRttiField(Self).FieldType - else if Self is TRttiProperty then - Result := TRttiProperty(Self).PropertyType - else if Self is TRttiParameter then - Result := TRttiParameter(Self).ParamType - else if Self is TRttiType then - Result := TRttiType(Self); -end; - -function TRttiObjectHelper.HasAttribute( - const ADoSomething: TProc; const AInherited: Boolean): Boolean; -var - LAttribute: TCustomAttribute; - LResult: Boolean; -begin - LResult := False; - - ForEachAttribute( - function (AAttr: T): Boolean - begin - Result := False; - LResult := True; - if Assigned(ADoSomething) then - ADoSomething(AAttr); - end - , AInherited - ); - - Result := LResult; -end; - -procedure TRttiObjectHelper.SetValue(AInstance: Pointer; const AValue: TValue); -begin - if Self is TRttiField then - TRttiField(Self).SetValue(AInstance, AValue) - else if Self is TRttiProperty then - TRttiProperty(Self).SetValue(AInstance, AValue) - else if Self is TRttiParameter then - raise ENotSupportedException.Create('Setting value of TRttiParameter not supported'); -end; - -{ TRttiTypeHelper } - -function TRttiTypeHelper.FindMethodFunc(const AName: string): TRttiMethod; -var - LMethod: TRttiMethod; - LParameters: TArray; -begin - LMethod := GetMethod(AName); - if Assigned(LMethod) then - begin - if not ( - (LMethod.ReturnType.Handle = TypeInfo(R)) - and TRttiHelper.MethodParametersMatch(LMethod) - ) then - LMethod := nil; - end; - - Result := LMethod; -end; - -function TRttiTypeHelper.FindMethodFunc(const AName: string): TRttiMethod; -var - LMethod: TRttiMethod; - LParameters: TArray; -begin - LMethod := GetMethod(AName); - if Assigned(LMethod) then - begin - if not ( - (LMethod.ReturnType.Handle = TypeInfo(R)) - and TRttiHelper.MethodParametersMatch(LMethod) - ) then - LMethod := nil; - end; - - Result := LMethod; -end; - -function TRttiTypeHelper.FindMethodFunc(const AName: string): TRttiMethod; -var - LMethod: TRttiMethod; - LParameters: TArray; -begin - LMethod := GetMethod(AName); - if Assigned(LMethod) then - begin - if not ( - (LMethod.ReturnType.Handle = TypeInfo(R)) - and TRttiHelper.MethodParametersMatch(LMethod) - ) then - LMethod := nil; - end; - - Result := LMethod; -end; - -function TRttiTypeHelper.FindMethodFunc(const AName: string): TRttiMethod; -var - LMethod: TRttiMethod; - LParameters: TArray; -begin - LMethod := GetMethod(AName); - if Assigned(LMethod) then - begin - if not ( - (LMethod.ReturnType.Handle = TypeInfo(R)) - and TRttiHelper.MethodParametersMatch(LMethod) - ) then - LMethod := nil; - end; - - Result := LMethod; -end; - -function TRttiTypeHelper.FindMethodFunc(const AName: string): TRttiMethod; -var - LMethod: TRttiMethod; - LParameters: TArray; -begin - LMethod := GetMethod(AName); - if Assigned(LMethod) then - begin - if not ( - (LMethod.ReturnType.Handle = TypeInfo(R)) - and TRttiHelper.MethodParametersMatch(LMethod) - ) then - LMethod := nil; - end; - - Result := LMethod; -end; - -function TRttiTypeHelper.ForEachFieldWithAttribute( - const ADoSomething: TFunc): Integer; -var - LField: TRttiField; - LBreak: Boolean; -begin - for LField in Self.GetFields do - begin - LBreak := False; - if LField.HasAttribute( - procedure (AAttrib: T) - begin - if Assigned(ADoSomething) then - begin - if not ADoSomething(LField, AAttrib) then - LBreak := True; - end; - end - ) - then - Inc(Result); - - if LBreak then - Break; - end; -end; - -function TRttiTypeHelper.ForEachMethodWithAttribute( - const ADoSomething: TFunc): Integer; -begin - Result := TRttiHelper.ForEachMethodWithAttribute(Self.GetMethods, ADoSomething); -end; - -function TRttiTypeHelper.ForEachPropertyWithAttribute( - const ADoSomething: TFunc): Integer; -var - LProperty: TRttiProperty; - LBreak: Boolean; -begin - Result := 0; - for LProperty in Self.GetProperties do - begin - LBreak := False; - if LProperty.HasAttribute( - procedure (AAttrib: T) - begin - if Assigned(ADoSomething) then - begin - if not ADoSomething(LProperty, AAttrib) then - LBreak := True; - end; - end - ) - then - Inc(Result); - - if LBreak then - Break; - end; -end; - -function TRttiTypeHelper.GetArrayElementType: TRttiType; -begin - Result := nil; - if Self is TRttiDynamicArrayType then - Result := TRttiDynamicArrayType(Self).ElementType; -end; - -function TRttiTypeHelper.IsArray: Boolean; -begin - Result := (Self is TRttiArrayType) or (Self is TRttiDynamicArrayType); -end; - -function TRttiTypeHelper.IsArray(out AElementType: TRttiType): Boolean; -begin - Result := False; - if (Self is TRttiDynamicArrayType) then - begin - AElementType := TRttiDynamicArrayType(Self).ElementType; - Result := True; - end - else if (Self is TRttiArrayType) then - begin - AElementType := TRttiArrayType(Self).ElementType; - Result := True; - end; -end; - -function TRttiTypeHelper.IsDynamicArrayOf( - const AAllowInherithance: Boolean): Boolean; -var - LElementType: TRttiType; - LType: TRttiType; -begin - Result := False; - if Self is TRttiDynamicArrayType then - begin - LType := TRttiContext.Create.GetType(TypeInfo(T)); - LElementType := TRttiDynamicArrayType(Self).ElementType; - - Result := (LElementType = LType) // exact match - or ( // classes with inheritance check (wrt AAllowInheritance argument) - (LElementType.IsInstance and LType.IsInstance) - and LElementType.IsObjectOfType(TRttiInstanceType(LType).MetaclassType, AAllowInherithance) - ); - end; -end; - -function TRttiTypeHelper.IsDynamicArrayOfRecord: Boolean; -var - LElementType: TRttiType; -begin - Result := False; - if Self is TRttiDynamicArrayType then - begin - LElementType := TRttiDynamicArrayType(Self).ElementType; - Result := LElementType.IsRecord; - end; -end; - -function TRttiTypeHelper.IsObjectOfType(const AClass: TClass; - const AAllowInherithance: Boolean): Boolean; -begin - Result := False; - if IsInstance then - begin - if AAllowInherithance then - Result := TRttiInstanceType(Self).MetaclassType.InheritsFrom(AClass) - else - Result := TRttiInstanceType(Self).MetaclassType = AClass; - end; -end; - -function TRttiTypeHelper.IsObjectOfType( - const AAllowInherithance: Boolean): Boolean; -var - LType: TRttiType; -begin - Result := False; - LType := TRttiContext.Create.GetType(TypeInfo(T)); - if LType.IsInstance then - Result := IsObjectOfType((LType as TRttiInstanceType).MetaclassType, AAllowInherithance); -end; - -{ TRttiHelper } - -class function TRttiHelper.MethodParametersMatch( - const AMethod: TRttiMethod): Boolean; -begin - Result := Length(AMethod.GetParameters) = 0; -end; - -class function TRttiHelper.MethodParametersMatch( - const AMethod: TRttiMethod): Boolean; -var - LParameters: TArray; -begin - LParameters := AMethod.GetParameters; - Result := (Length(LParameters) = 4) - and (LParameters[0].ParamType.Handle = TypeInfo(A1)) - and (LParameters[1].ParamType.Handle = TypeInfo(A2)) - and (LParameters[2].ParamType.Handle = TypeInfo(A3)) - and (LParameters[3].ParamType.Handle = TypeInfo(A4)); -end; - -class function TRttiHelper.MethodParametersMatch( - const AMethod: TRttiMethod): Boolean; -var - LParameters: TArray; -begin - LParameters := AMethod.GetParameters; - Result := (Length(LParameters) = 3) - and (LParameters[0].ParamType.Handle = TypeInfo(A1)) - and (LParameters[1].ParamType.Handle = TypeInfo(A2)) - and (LParameters[2].ParamType.Handle = TypeInfo(A3)); -end; - -class function TRttiHelper.MethodParametersMatch( - const AMethod: TRttiMethod): Boolean; -var - LParameters: TArray; -begin - LParameters := AMethod.GetParameters; - Result := (Length(LParameters) = 2) - and (LParameters[0].ParamType.Handle = TypeInfo(A1)) - and (LParameters[1].ParamType.Handle = TypeInfo(A2)); -end; - -class function TRttiHelper.MethodParametersMatch( - const AMethod: TRttiMethod): Boolean; -var - LParameters: TArray; -begin - LParameters := AMethod.GetParameters; - Result := (Length(LParameters) = 1) - and (LParameters[0].ParamType.Handle = TypeInfo(A1)); -end; - -class function TRttiHelper.FindParameterLessConstructor( - const AClass: TClass): TRttiMethod; -var - LContext: TRttiContext; - LType: TRttiType; - LMethod: TRttiMethod; -begin - Result := nil; - LType := LContext.GetType(AClass); - - for LMethod in LType.GetMethods do - begin - if LMethod.IsConstructor and (Length(LMethod.GetParameters) = 0) then - begin - Result := LMethod; - Break; - end; - end; -end; - -class function TRttiHelper.ForEachAttribute(const AInstance: TObject; - const ADoSomething: TProc): Integer; -var - LContext: TRttiContext; - LType: TRttiType; -begin - Result := 0; - LType := LContext.GetType(AInstance.ClassType); - if Assigned(LType) then - Result := LType.ForEachAttribute(ADoSomething); -end; - -class function TRttiHelper.ForEachAttribute(const AAttributes: TArray; - const ADoSomething: TFunc): Integer; -var - LAttribute: TCustomAttribute; - LContinue: Boolean; -begin - Result := 0; - if not Assigned(ADoSomething) then - Exit; - - for LAttribute in AAttributes do - begin - if LAttribute.InheritsFrom(TClass(T)) then - begin - LContinue := ADoSomething(T(LAttribute)); - Inc(Result); - - if not LContinue then - Break; - end; - end; -end; - -class function TRttiHelper.ForEachAttribute(const AAttributes: TArray; - const ADoSomething: TProc): Integer; -var - LAttribute: TCustomAttribute; -begin - if Assigned(ADoSomething) then - for LAttribute in AAttributes do - begin - if LAttribute.InheritsFrom(TClass(T)) then - begin - ADoSomething(T(LAttribute)); - Inc(Result); - end; - end; -end; - -class function TRttiHelper.ForEachField(AInstance: TObject; - const ADoSomething: TFunc): Integer; -var - LContext: TRttiContext; - LField: TRttiField; - LType: TRttiType; - LBreak: Boolean; -begin - Result := 0; - LType := LContext.GetType(AInstance.ClassType); - for LField in LType.GetFields do - begin - LBreak := False; - - if Assigned(ADoSomething) then - begin - if not ADoSomething(LField) then - LBreak := True - else - Inc(Result); - end; - - if LBreak then - Break; - end; -end; - -class function TRttiHelper.ForEachFieldWithAttribute(AInstance: TObject; - const ADoSomething: TFunc): Integer; -var - LContext: TRttiContext; - LType: TRttiType; -begin - Result := 0; - LType := LContext.GetType(AInstance.ClassType); - if Assigned(LType) then - Result := LType.ForEachFieldWithAttribute(ADoSomething); -end; - -class function TRttiHelper.ForEachMethodWithAttribute( - const AMethods: TArray; - const ADoSomething: TFunc): Integer; -var - LMethod: TRttiMethod; - LBreak: Boolean; -begin - Result := 0; - if not Assigned(ADoSomething) then - Exit; - - for LMethod in AMethods do - begin - LBreak := False; - if LMethod.HasAttribute( - procedure (AAttrib: T) - begin - if not ADoSomething(LMethod, AAttrib) then - LBreak := True; - end - ) - then - Inc(Result); - - if LBreak then - Break; - end; -end; - -class function TRttiHelper.IfHasAttribute(AInstance: TObject): Boolean; -begin - Result := IfHasAttribute(AInstance, nil); -end; - -class function TRttiHelper.IfHasAttribute(AInstance: TObject; - const ADoSomething: TProc): Boolean; -var - LContext: TRttiContext; - LType: TRttiType; -begin - Result := False; - LType := LContext.GetType(AInstance.ClassType); - if Assigned(LType) then - Result := LType.HasAttribute(ADoSomething); -end; - - -{ TRecord } - -class function TRecord.DataSetToArray(const ADataSet: TDataSet): TArray; -var - LItem: R; -begin - if not ADataSet.Active then - ADataSet.Active := True - else - ADataSet.First; - -{$ifdef DelphiXE7_UP} - Result := []; -{$else} - SetLength(Result, 0); -{$endif} - - while not ADataSet.Eof do - begin - TRecord.FromDataSet(LItem, ADataSet); - -{$ifdef DelphiXE7_UP} - Result := Result + [LItem]; -{$else} - SetLength(Result, Length(Result) + 1); - Result[Length(Result)-1] := LItem; -{$endif} - - ADataSet.Next; - end; -end; - -class procedure TRecord.FromDataSet(var ARecord: R; - const ADataSet: TDataSet); -var - LRecordType: TRttiType; - LRecordField: TRttiField; - LDataSetField: TField; - LValue: TValue; -begin - if not ADataSet.Active then - ADataSet.Active := True; - - LRecordType := TRttiContext.Create.GetType(TypeInfo(R)); - for LRecordField in LRecordType.GetFields do - begin - LDataSetField := ADataSet.FindField(LRecordField.Name); - if Assigned(LDataSetField) then - begin - if LDataSetField.IsNull then - LRecordField.SetValue(@ARecord, TValue.Empty) - - else if (LDataSetField.DataType = ftBCD) then // MF 20170726 - LRecordField.SetValue(@ARecord, LDataSetField.AsFloat) - else if (LDataSetField.DataType = ftFMTBcd) then // MF 20170726 - LRecordField.SetValue(@ARecord, LDataSetField.AsFloat) - - else if LRecordField.FieldType.Handle = TypeInfo(Boolean) then - begin - if LDataSetField.DataType = ftBoolean then - LRecordField.SetValue(@ARecord, LDataSetField.AsBoolean) - else - LRecordField.SetValue(@ARecord, LDataSetField.AsInteger <> 0); - end - else if (LRecordField.FieldType.Handle = TypeInfo(TDateTime)) - or (LRecordField.FieldType.Handle = TypeInfo(TDate)) - or (LRecordField.FieldType.Handle = TypeInfo(TTime)) - then - begin - if (LDataSetField.DataType in [ftDate, ftDateTime, ftTime, ftTimeStamp]) then - LRecordField.SetValue(@ARecord, LDataSetField.AsDateTime) - else - LRecordField.SetValue(@ARecord, StrToDateTimeDef(LDataSetField.AsString, 0)); - end - else if LRecordField.FieldType is TRttiEnumerationType then - begin - if LDataSetField is TNumericField then - LRecordField.SetValue(@ARecord, TValue.FromOrdinal(LRecordField.FieldType.Handle, LDataSetField.AsInteger)) - else if LDataSetField is TStringField then - LRecordField.SetValue(@ARecord - , TValue.FromOrdinal( - LRecordField.FieldType.Handle - , GetEnumValue(LRecordField.FieldType.Handle, LDataSetField.AsString) - ) - ); - end - else - LRecordField.SetValue(@ARecord, TValue.FromVariant(LDataSetField.Value)); - end; - end; -end; - -class function TRecord.FromStrings(const AStrings: TStrings): R; -var - LRecordType: TRttiType; -begin - LRecordType := TRttiContext.Create.GetType(TypeInfo(R)); - Result := StringsToRecord(AStrings, LRecordType).AsType; -end; - -class function TRecord.GetFieldByName(var ARecord: R; - const AFieldName: string): TValue; -begin - Result := GetFieldByName(ARecord, AFieldName, TValue.Empty); -end; - -class function TRecord.GetFieldByName(var ARecord: R; - const AFieldName: string; const ADefault: TValue): TValue; -var - LRecordType: TRttiType; - LField: TRttiField; -begin - Result := ADefault; - LRecordType := TRttiContext.Create.GetType(TypeInfo(R)); - LField := LRecordType.GetField(AFieldName); - if Assigned(LField) then - Result := LField.GetValue(@ARecord); -end; - -class procedure TRecord.SetFieldByName(var ARecord: R; - const AFieldName: string; const AValue: TValue); -var - LRecordType: TRttiType; - LField: TRttiField; -begin - LRecordType := TRttiContext.Create.GetType(TypeInfo(R)); - - LField := LRecordType.GetField(AFieldName); - LField.SetValue(@ARecord, AValue); -end; - - -class function TRecord.ToArrayOfString(const ARecord: R): TArray; -var - LDummy: TStrings; -begin - LDummy := TStringList.Create; - try - ToStrings(ARecord, LDummy); - Result := LDummy.ToStringArray; - finally - LDummy.Free; - end; -end; - -class procedure TRecord.ToDataSet(const ARecord: R; const ADataSet: TDataSet; - const AAppend: Boolean); -var - LRecordType: TRttiType; - LRecordField: TRttiField; - LDataSetField: TField; - LValue: TValue; -begin - LRecordType := TRttiContext.Create.GetType(TypeInfo(R)); - - if AAppend then - ADataSet.Append - else - ADataSet.Edit; - try - for LRecordField in LRecordType.GetFields do - begin - LDataSetField := ADataSet.FindField(LRecordField.Name); - if Assigned(LDataSetField) and not (AAppend and (LDataSetField.DataType = ftAutoInc)) then - begin - LValue := LRecordField.GetValue(@ARecord); - if LValue.IsEmpty then - LDataSetField.Clear - else - LDataSetField.Value := LValue.AsVariant; - - // set NULL for 0.0 DateTime values - if (LDataSetField.DataType in [ftDate, ftDateTime, ftTime, ftTimeStamp]) and (LDataSetField.Value = 0.0) then - LDataSetField.Clear; - end; - end; - ADataSet.Post; - except - ADataSet.Cancel; - raise; - end; -end; - -class procedure TRecord.ToStrings(const ARecord: R; var AStrings: TStrings; - const AClear: Boolean); -var - LRecordType: TRttiType; - LField: TRttiField; - LFieldType: TRttiType; - LFieldValue: TValue; - LToStringMethod: TRttiMethod; -begin - Assert(Assigned(AStrings)); - - if AClear then - AStrings.Clear; - - LRecordType := TRttiContext.Create.GetType(TypeInfo(R)); - - for LField in LRecordType.GetFields do - begin - LFieldType := LField.FieldType; - LFieldValue := LField.GetValue(@ARecord); - - if LFieldType.IsRecord then - begin - LToStringMethod := LFieldType.GetMethod('ToString'); - if Assigned(LToStringMethod) then - AStrings.Values[LField.Name] := LToStringMethod.Invoke(LFieldValue, []).ToString - else - begin - //AM TODO recursion using ToStrings here - AStrings.Values[LField.Name] := LField.GetValue(@ARecord).ToString; - end; - end - else - AStrings.Values[LField.Name] := LField.GetValue(@ARecord).ToString; - end; -end; - -end. +(* + Copyright 2016, MARS-Curiosity library + + Home: https://github.com/andrea-magni/MARS +*) +unit MARS.Rtti.Utils; + +{$I MARS.inc} + +interface + +uses + SysUtils, Classes, RTTI, TypInfo + , MARS.Core.JSON + , DB; + +type + TRttiObjectHelper = class helper for TRttiObject + public + function GetRttiType: TRttiType; + procedure SetValue(AInstance: Pointer; const AValue: TValue); + + function HasAttribute(const AInherited: Boolean = False): Boolean; overload; inline; + function HasAttribute( + const ADoSomething: TProc; const AInherited: Boolean = False): Boolean; overload; inline; + + function GetAllAttributes(const AInherited: Boolean = False): TArray; inline; + + function ForEachAttribute( + const ADoSomething: TProc; const AInherited: Boolean = False): Integer; overload; inline; + function ForEachAttribute( + const ADoSomething: TFunc; const AInherited: Boolean = False): Integer; overload; inline; + end; + + TRttiTypeHelper = class helper(TRttiObjectHelper) for TRttiType + protected + public + function ForEachMethodWithAttribute( + const ADoSomething: TFunc): Integer; inline; + + function ForEachFieldWithAttribute( + const ADoSomething: TFunc): Integer; + + function ForEachPropertyWithAttribute( + const ADoSomething: TFunc): Integer; + + function IsDynamicArrayOf(const AAllowInherithance: Boolean = True): Boolean; + function IsDynamicArrayOfRecord: Boolean; + function IsArray: Boolean; overload; + function IsArray(out AElementType: TRttiType): Boolean; overload; + function GetArrayElementType: TRttiType; + + function IsObjectOfType(const AAllowInherithance: Boolean = True): Boolean; overload; + function IsObjectOfType(const AClass: TClass; const AAllowInherithance: Boolean = True): Boolean; overload; + + function FindMethodFunc(const AName: string): TRttiMethod; overload; + function FindMethodFunc(const AName: string): TRttiMethod; overload; + function FindMethodFunc(const AName: string): TRttiMethod; overload; + function FindMethodFunc(const AName: string): TRttiMethod; overload; + + function FindMethodFunc(const AName: string): TRttiMethod; overload; + end; + + TRttiHelper = class + public + class function MethodParametersMatch(const AMethod: TRttiMethod): Boolean; overload; + class function MethodParametersMatch(const AMethod: TRttiMethod): Boolean; overload; + class function MethodParametersMatch(const AMethod: TRttiMethod): Boolean; overload; + class function MethodParametersMatch(const AMethod: TRttiMethod): Boolean; overload; + class function MethodParametersMatch(const AMethod: TRttiMethod): Boolean; overload; + + class function IfHasAttribute(AInstance: TObject): Boolean; overload; + class function IfHasAttribute(AInstance: TObject; const ADoSomething: TProc): Boolean; overload; + + class function ForEachAttribute(const AInstance: TObject; + const ADoSomething: TProc): Integer; overload; + + class function ForEachAttribute(const AAttributes: TArray; + const ADoSomething: TProc): Integer; overload; + class function ForEachAttribute(const AAttributes: TArray; + const ADoSomething: TFunc): Integer; overload; + + class function ForEachMethodWithAttribute(const AMethods: TArray; + const ADoSomething: TFunc): Integer; + + class function ForEachFieldWithAttribute(AInstance: TObject; const ADoSomething: TFunc): Integer; overload; + class function ForEachField(AInstance: TObject; const ADoSomething: TFunc): Integer; + + class function FindParameterLessConstructor(const AClass: TClass): TRttiMethod; + end; + + TRecord = class + public + class procedure ToDataSet(const ARecord: R; const ADataSet: TDataSet; const AAppend: Boolean = False); + class procedure FromDataSet(var ARecord: R; const ADataSet: TDataSet); + class function DataSetToArray(const ADataSet: TDataSet): TArray; + class procedure SetFieldByName(var ARecord: R; const AFieldName: string; const AValue: TValue); + class function GetFieldByName(var ARecord: R; const AFieldName: string): TValue; overload; + class function GetFieldByName(var ARecord: R; const AFieldName: string; const ADefault: TValue): TValue; overload; + class function FromStrings(const AStrings: TStrings): R; + class procedure ToStrings(const ARecord: R; var AStrings: TStrings; const AClear: Boolean = True); + class function ToArrayOfString(const ARecord: R): TArray; + end; + TOnGetRecordFieldValueProc = reference to procedure (const AName: string; const AField: TRttiField; var AValue: TValue); + + + function StringsToRecord(const AStrings: string; const ARecordType: TRttiType; + const AOnGetFieldValue: TOnGetRecordFieldValueProc = nil): TValue; overload; + + function StringsToRecord(const AStrings: TStrings; const ARecordType: TRttiType; + const AOnGetFieldValue: TOnGetRecordFieldValueProc = nil): TValue; overload; + +function ExecuteMethod(const AInstance: TValue; const AMethodName: string; const AArguments: array of TValue; + const ABeforeExecuteProc: TProc{ = nil}; const AAfterExecuteProc: TProc{ = nil}): Boolean; overload; + +function ExecuteMethod(const AInstance: TValue; AMethod: TRttiMethod; const AArguments: array of TValue; + const ABeforeExecuteProc: TProc{ = nil}; const AAfterExecuteProc: TProc{ = nil}): Boolean; overload; + +function ReadPropertyValue(AInstance: TObject; const APropertyName: string): TValue; + +procedure SetArrayLength(var AArray: TValue; const AArrayType: TRttiType; const ANewSize: PNativeInt); + +function StringToTValue(const AString: string; const ADesiredType: TRttiType): TValue; + +implementation + +uses + StrUtils, DateUtils, Generics.Collections + , MARS.Core.Utils +; + +function StringsToRecord(const AStrings: string; const ARecordType: TRttiType; + const AOnGetFieldValue: TOnGetRecordFieldValueProc = nil): TValue; +var + LSL: TStringList; +begin + LSL := TStringList.Create; + try + LSL.Text := AStrings; + Result := StringsToRecord(LSL, ARecordType, AOnGetFieldValue); + finally + LSL.Free; + end; +end; + + +function StringsToRecord(const AStrings: TStrings; const ARecordType: TRttiType; + const AOnGetFieldValue: TOnGetRecordFieldValueProc = nil): TValue; +var + LField: TRttiField; +// LValue: TValue; + LRecordInstance: Pointer; +// LFilterProc: TToRecordFilterProc; + LAccept: Boolean; + LJSONName: string; + LAssignedValuesField: TRttiField; + LAssignedValues: TArray; + LValue: TValue; + +// function GetRecordFilterProc: TToRecordFilterProc; +// var +// LMethod: TRttiMethod; +// begin +// Result := nil; +// // looking for TMyRecord.ToRecordFilter(const AField: TRttiField; const AObj: TJSONObject): Boolean; +// +// LMethod := ARecordType.FindMethodFunc('ToRecordFilter'); +// if Assigned(LMethod) then +// Result := +// procedure (const AField: TRttiField; const ARecord: TValue; const AJSONObject: TJSONObject; var AAccept: Boolean) +// begin +// AAccept := LMethod.Invoke(ARecord, [AField, AJSONObject]).AsBoolean; +// end; +// end; + +begin + TValue.Make(nil, ARecordType.Handle, Result); + LRecordInstance := Result.GetReferenceToRawData; + +// LFilterProc := AFilterProc; +// if not Assigned(LFilterProc) then +// LFilterProc := GetRecordFilterProc(); + + LAssignedValuesField := ARecordType.GetField('_AssignedValues'); + if Assigned(LAssignedValuesField) + and not LAssignedValuesField.FieldType.IsDynamicArrayOf + then + LAssignedValuesField := nil; + LAssignedValues := []; + + for LField in ARecordType.GetFields do + begin + LAccept := True; +// if Assigned(LFilterProc) then +// LFilterProc(LField, Result, Self, LAccept); + + if LAccept then + begin + LJSONName := LField.Name; + LField.HasAttribute( + procedure (AAttr: JSONNameAttribute) + begin + LJSONName := AAttr.Name; + end + ); + if LJSONName <> '' then + begin + LValue := StringToTValue(AStrings.Values[LJSONName], LField.FieldType); + if Assigned(AOnGetFieldValue) then + AOnGetFieldValue(LJSONName, LField, LValue); + LField.SetValue(LRecordInstance, LValue); + LAssignedValues := LAssignedValues + [LField.Name]; + end; + end; + end; + if Assigned(LAssignedValuesField) then + LAssignedValuesField.SetValue(LRecordInstance, TValue.From>(LAssignedValues)); +end; + +function StringToTValue(const AString: string; const ADesiredType: TRttiType): TValue; +begin + if ADesiredType.IsObjectOfType then + Result := TJSONObject.ParseJSONValue(AString) + else + begin + case ADesiredType.TypeKind of + tkInt64: Result := StrToInt64Def(AString, 0); + tkInteger: Result := StrToIntDef(AString, 0); + tkEnumeration: begin + if SameText(ADesiredType.Name, 'Boolean') then + Result := StrToBoolDef(AString, False); + end; + tkFloat: begin + if IndexStr(ADesiredType.Name, ['TDate', 'TDateTime', 'TTime']) <> -1 then + begin + try + Result := ISO8601ToDate(AString); + except + Result := StrToDateTime(AString) + end; + end + else + begin + Result := StrToFloatDef(AString + , StrToFloatDef(AString, 0.0, TFormatSettings.Create('en')) // second chance + ); + end; + end; + + {$ifdef DelphiXE7_UP} + tkChar: begin + if AString.IsEmpty then + Result := '' + else + Result := TValue.From(AString.Chars[0]); + end; + {$else} + tkChar: Result := TValue.From(Copy(AString, 1, 1)); + {$endif} + else + Result := AString; + end; + end; +end; + +procedure SetArrayLength(var AArray: TValue; const AArrayType: TRttiType; const ANewSize: PNativeInt); +begin + if AArrayType is TRttiArrayType then + begin + raise Exception.Create('Not yet implemented: SetArrayLength TRttiArrayType'); + { TODO -oAndrea : probably not needed } + end + else if AArrayType is TRttiDynamicArrayType then + DynArraySetLength(PPointer(AArray.GetReferenceToRawData)^, AArrayType.Handle, 1, ANewSize); +end; + +function ReadPropertyValue(AInstance: TObject; const APropertyName: string): TValue; +var + LContext: TRttiContext; + LType: TRttiType; + LProperty: TRttiProperty; +begin + Result := TValue.Empty; + LType := LContext.GetType(AInstance.ClassType); + if Assigned(LType) then + begin + LProperty := LType.GetProperty(APropertyName); + if Assigned(LProperty) then + Result := LProperty.GetValue(AInstance); + end; +end; + +function ExecuteMethod(const AInstance: TValue; AMethod: TRttiMethod; + const AArguments: array of TValue; const ABeforeExecuteProc: TProc{ = nil}; + const AAfterExecuteProc: TProc{ = nil}): Boolean; +var + LResult: TValue; +begin + if Assigned(ABeforeExecuteProc) then + ABeforeExecuteProc(); + LResult := AMethod.Invoke(AInstance, AArguments); + Result := True; + if Assigned(AAfterExecuteProc) then + AAfterExecuteProc(LResult); +end; + +function ExecuteMethod(const AInstance: TValue; const AMethodName: string; + const AArguments: array of TValue; const ABeforeExecuteProc: TProc{ = nil}; + const AAfterExecuteProc: TProc{ = nil}): Boolean; +var + LContext: TRttiContext; + LType: TRttiType; + LMethod: TRttiMethod; +begin + Result := False; + LType := LContext.GetType(AInstance.TypeInfo); + if Assigned(LType) then + begin + LMethod := LType.GetMethod(AMethodName); + if Assigned(LMethod) then + Result := ExecuteMethod(AInstance, LMethod, AArguments, ABeforeExecuteProc, AAfterExecuteProc); + end; +end; + +{ TRttiObjectHelper } + +function TRttiObjectHelper.ForEachAttribute( + const ADoSomething: TProc; const AInherited: Boolean): Integer; +begin + Result := TRttiHelper.ForEachAttribute(GetAllAttributes(AInherited), ADoSomething); +end; + +function TRttiObjectHelper.HasAttribute(const AInherited: Boolean): Boolean; +begin + Result := HasAttribute(nil, AInherited); +end; + +function TRttiObjectHelper.ForEachAttribute( + const ADoSomething: TFunc; const AInherited: Boolean): Integer; +begin + Result := TRttiHelper.ForEachAttribute(GetAllAttributes(AInherited), ADoSomething); +end; + +function TRttiObjectHelper.GetAllAttributes( + const AInherited: Boolean): TArray; +var + LBaseType: TRttiType; + LType: TRttiType; +begin + Result := Self.GetAttributes; + + { TODO -oAndrea : Implement AInherited = True behavior when Self is a TRttiMethod instance } + LType := Self.GetRttiType; + if AInherited and Assigned(LType) then + begin + LBaseType := LType.BaseType; + while Assigned(LBaseType) do + begin + Result := Result + LBaseType.GetAttributes; + LBaseType := LBaseType.BaseType; + end; + end; +end; + +function TRttiObjectHelper.GetRttiType: TRttiType; +begin + Result := nil; + if Self is TRttiField then + Result := TRttiField(Self).FieldType + else if Self is TRttiProperty then + Result := TRttiProperty(Self).PropertyType + else if Self is TRttiParameter then + Result := TRttiParameter(Self).ParamType + else if Self is TRttiType then + Result := TRttiType(Self); +end; + +function TRttiObjectHelper.HasAttribute( + const ADoSomething: TProc; const AInherited: Boolean): Boolean; +var + LAttribute: TCustomAttribute; + LResult: Boolean; +begin + LResult := False; + + ForEachAttribute( + function (AAttr: T): Boolean + begin + Result := False; + LResult := True; + if Assigned(ADoSomething) then + ADoSomething(AAttr); + end + , AInherited + ); + + Result := LResult; +end; + +procedure TRttiObjectHelper.SetValue(AInstance: Pointer; const AValue: TValue); +begin + if Self is TRttiField then + TRttiField(Self).SetValue(AInstance, AValue) + else if Self is TRttiProperty then + TRttiProperty(Self).SetValue(AInstance, AValue) + else if Self is TRttiParameter then + raise ENotSupportedException.Create('Setting value of TRttiParameter not supported'); +end; + +{ TRttiTypeHelper } + +function TRttiTypeHelper.FindMethodFunc(const AName: string): TRttiMethod; +var + LMethod: TRttiMethod; + LParameters: TArray; +begin + LMethod := GetMethod(AName); + if Assigned(LMethod) then + begin + if not ( + (LMethod.ReturnType.Handle = TypeInfo(R)) + and TRttiHelper.MethodParametersMatch(LMethod) + ) then + LMethod := nil; + end; + + Result := LMethod; +end; + +function TRttiTypeHelper.FindMethodFunc(const AName: string): TRttiMethod; +var + LMethod: TRttiMethod; + LParameters: TArray; +begin + LMethod := GetMethod(AName); + if Assigned(LMethod) then + begin + if not ( + (LMethod.ReturnType.Handle = TypeInfo(R)) + and TRttiHelper.MethodParametersMatch(LMethod) + ) then + LMethod := nil; + end; + + Result := LMethod; +end; + +function TRttiTypeHelper.FindMethodFunc(const AName: string): TRttiMethod; +var + LMethod: TRttiMethod; + LParameters: TArray; +begin + LMethod := GetMethod(AName); + if Assigned(LMethod) then + begin + if not ( + (LMethod.ReturnType.Handle = TypeInfo(R)) + and TRttiHelper.MethodParametersMatch(LMethod) + ) then + LMethod := nil; + end; + + Result := LMethod; +end; + +function TRttiTypeHelper.FindMethodFunc(const AName: string): TRttiMethod; +var + LMethod: TRttiMethod; + LParameters: TArray; +begin + LMethod := GetMethod(AName); + if Assigned(LMethod) then + begin + if not ( + (LMethod.ReturnType.Handle = TypeInfo(R)) + and TRttiHelper.MethodParametersMatch(LMethod) + ) then + LMethod := nil; + end; + + Result := LMethod; +end; + +function TRttiTypeHelper.FindMethodFunc(const AName: string): TRttiMethod; +var + LMethod: TRttiMethod; + LParameters: TArray; +begin + LMethod := GetMethod(AName); + if Assigned(LMethod) then + begin + if not ( + (LMethod.ReturnType.Handle = TypeInfo(R)) + and TRttiHelper.MethodParametersMatch(LMethod) + ) then + LMethod := nil; + end; + + Result := LMethod; +end; + +function TRttiTypeHelper.ForEachFieldWithAttribute( + const ADoSomething: TFunc): Integer; +var + LField: TRttiField; + LBreak: Boolean; +begin + for LField in Self.GetFields do + begin + LBreak := False; + if LField.HasAttribute( + procedure (AAttrib: T) + begin + if Assigned(ADoSomething) then + begin + if not ADoSomething(LField, AAttrib) then + LBreak := True; + end; + end + ) + then + Inc(Result); + + if LBreak then + Break; + end; +end; + +function TRttiTypeHelper.ForEachMethodWithAttribute( + const ADoSomething: TFunc): Integer; +begin + Result := TRttiHelper.ForEachMethodWithAttribute(Self.GetMethods, ADoSomething); +end; + +function TRttiTypeHelper.ForEachPropertyWithAttribute( + const ADoSomething: TFunc): Integer; +var + LProperty: TRttiProperty; + LBreak: Boolean; +begin + Result := 0; + for LProperty in Self.GetProperties do + begin + LBreak := False; + if LProperty.HasAttribute( + procedure (AAttrib: T) + begin + if Assigned(ADoSomething) then + begin + if not ADoSomething(LProperty, AAttrib) then + LBreak := True; + end; + end + ) + then + Inc(Result); + + if LBreak then + Break; + end; +end; + +function TRttiTypeHelper.GetArrayElementType: TRttiType; +begin + Result := nil; + if Self is TRttiDynamicArrayType then + Result := TRttiDynamicArrayType(Self).ElementType; +end; + +function TRttiTypeHelper.IsArray: Boolean; +begin + Result := (Self is TRttiArrayType) or (Self is TRttiDynamicArrayType); +end; + +function TRttiTypeHelper.IsArray(out AElementType: TRttiType): Boolean; +begin + Result := False; + if (Self is TRttiDynamicArrayType) then + begin + AElementType := TRttiDynamicArrayType(Self).ElementType; + Result := True; + end + else if (Self is TRttiArrayType) then + begin + AElementType := TRttiArrayType(Self).ElementType; + Result := True; + end; +end; + +function TRttiTypeHelper.IsDynamicArrayOf( + const AAllowInherithance: Boolean): Boolean; +var + LElementType: TRttiType; + LType: TRttiType; +begin + Result := False; + if Self is TRttiDynamicArrayType then + begin + LType := TRttiContext.Create.GetType(TypeInfo(T)); + LElementType := TRttiDynamicArrayType(Self).ElementType; + + Result := (LElementType = LType) // exact match + or ( // classes with inheritance check (wrt AAllowInheritance argument) + (LElementType.IsInstance and LType.IsInstance) + and LElementType.IsObjectOfType(TRttiInstanceType(LType).MetaclassType, AAllowInherithance) + ); + end; +end; + +function TRttiTypeHelper.IsDynamicArrayOfRecord: Boolean; +var + LElementType: TRttiType; +begin + Result := False; + if Self is TRttiDynamicArrayType then + begin + LElementType := TRttiDynamicArrayType(Self).ElementType; + Result := LElementType.IsRecord; + end; +end; + +function TRttiTypeHelper.IsObjectOfType(const AClass: TClass; + const AAllowInherithance: Boolean): Boolean; +begin + Result := False; + if IsInstance then + begin + if AAllowInherithance then + Result := TRttiInstanceType(Self).MetaclassType.InheritsFrom(AClass) + else + Result := TRttiInstanceType(Self).MetaclassType = AClass; + end; +end; + +function TRttiTypeHelper.IsObjectOfType( + const AAllowInherithance: Boolean): Boolean; +var + LType: TRttiType; +begin + Result := False; + LType := TRttiContext.Create.GetType(TypeInfo(T)); + if LType.IsInstance then + Result := IsObjectOfType((LType as TRttiInstanceType).MetaclassType, AAllowInherithance); +end; + +{ TRttiHelper } + +class function TRttiHelper.MethodParametersMatch( + const AMethod: TRttiMethod): Boolean; +begin + Result := Length(AMethod.GetParameters) = 0; +end; + +class function TRttiHelper.MethodParametersMatch( + const AMethod: TRttiMethod): Boolean; +var + LParameters: TArray; +begin + LParameters := AMethod.GetParameters; + Result := (Length(LParameters) = 4) + and (LParameters[0].ParamType.Handle = TypeInfo(A1)) + and (LParameters[1].ParamType.Handle = TypeInfo(A2)) + and (LParameters[2].ParamType.Handle = TypeInfo(A3)) + and (LParameters[3].ParamType.Handle = TypeInfo(A4)); +end; + +class function TRttiHelper.MethodParametersMatch( + const AMethod: TRttiMethod): Boolean; +var + LParameters: TArray; +begin + LParameters := AMethod.GetParameters; + Result := (Length(LParameters) = 3) + and (LParameters[0].ParamType.Handle = TypeInfo(A1)) + and (LParameters[1].ParamType.Handle = TypeInfo(A2)) + and (LParameters[2].ParamType.Handle = TypeInfo(A3)); +end; + +class function TRttiHelper.MethodParametersMatch( + const AMethod: TRttiMethod): Boolean; +var + LParameters: TArray; +begin + LParameters := AMethod.GetParameters; + Result := (Length(LParameters) = 2) + and (LParameters[0].ParamType.Handle = TypeInfo(A1)) + and (LParameters[1].ParamType.Handle = TypeInfo(A2)); +end; + +class function TRttiHelper.MethodParametersMatch( + const AMethod: TRttiMethod): Boolean; +var + LParameters: TArray; +begin + LParameters := AMethod.GetParameters; + Result := (Length(LParameters) = 1) + and (LParameters[0].ParamType.Handle = TypeInfo(A1)); +end; + +class function TRttiHelper.FindParameterLessConstructor( + const AClass: TClass): TRttiMethod; +var + LContext: TRttiContext; + LType: TRttiType; + LMethod: TRttiMethod; +begin + Result := nil; + LType := LContext.GetType(AClass); + + for LMethod in LType.GetMethods do + begin + if LMethod.IsConstructor and (Length(LMethod.GetParameters) = 0) then + begin + Result := LMethod; + Break; + end; + end; +end; + +class function TRttiHelper.ForEachAttribute(const AInstance: TObject; + const ADoSomething: TProc): Integer; +var + LContext: TRttiContext; + LType: TRttiType; +begin + Result := 0; + LType := LContext.GetType(AInstance.ClassType); + if Assigned(LType) then + Result := LType.ForEachAttribute(ADoSomething); +end; + +class function TRttiHelper.ForEachAttribute(const AAttributes: TArray; + const ADoSomething: TFunc): Integer; +var + LAttribute: TCustomAttribute; + LContinue: Boolean; +begin + Result := 0; + if not Assigned(ADoSomething) then + Exit; + + for LAttribute in AAttributes do + begin + if LAttribute.InheritsFrom(TClass(T)) then + begin + LContinue := ADoSomething(T(LAttribute)); + Inc(Result); + + if not LContinue then + Break; + end; + end; +end; + +class function TRttiHelper.ForEachAttribute(const AAttributes: TArray; + const ADoSomething: TProc): Integer; +var + LAttribute: TCustomAttribute; +begin + if Assigned(ADoSomething) then + for LAttribute in AAttributes do + begin + if LAttribute.InheritsFrom(TClass(T)) then + begin + ADoSomething(T(LAttribute)); + Inc(Result); + end; + end; +end; + +class function TRttiHelper.ForEachField(AInstance: TObject; + const ADoSomething: TFunc): Integer; +var + LContext: TRttiContext; + LField: TRttiField; + LType: TRttiType; + LBreak: Boolean; +begin + Result := 0; + LType := LContext.GetType(AInstance.ClassType); + for LField in LType.GetFields do + begin + LBreak := False; + + if Assigned(ADoSomething) then + begin + if not ADoSomething(LField) then + LBreak := True + else + Inc(Result); + end; + + if LBreak then + Break; + end; +end; + +class function TRttiHelper.ForEachFieldWithAttribute(AInstance: TObject; + const ADoSomething: TFunc): Integer; +var + LContext: TRttiContext; + LType: TRttiType; +begin + Result := 0; + LType := LContext.GetType(AInstance.ClassType); + if Assigned(LType) then + Result := LType.ForEachFieldWithAttribute(ADoSomething); +end; + +class function TRttiHelper.ForEachMethodWithAttribute( + const AMethods: TArray; + const ADoSomething: TFunc): Integer; +var + LMethod: TRttiMethod; + LBreak: Boolean; +begin + Result := 0; + if not Assigned(ADoSomething) then + Exit; + + for LMethod in AMethods do + begin + LBreak := False; + if LMethod.HasAttribute( + procedure (AAttrib: T) + begin + if not ADoSomething(LMethod, AAttrib) then + LBreak := True; + end + ) + then + Inc(Result); + + if LBreak then + Break; + end; +end; + +class function TRttiHelper.IfHasAttribute(AInstance: TObject): Boolean; +begin + Result := IfHasAttribute(AInstance, nil); +end; + +class function TRttiHelper.IfHasAttribute(AInstance: TObject; + const ADoSomething: TProc): Boolean; +var + LContext: TRttiContext; + LType: TRttiType; +begin + Result := False; + LType := LContext.GetType(AInstance.ClassType); + if Assigned(LType) then + Result := LType.HasAttribute(ADoSomething); +end; + + +{ TRecord } + +class function TRecord.DataSetToArray(const ADataSet: TDataSet): TArray; +var + LItem: R; +begin + if not ADataSet.Active then + ADataSet.Active := True + else + ADataSet.First; + +{$ifdef DelphiXE7_UP} + Result := []; +{$else} + SetLength(Result, 0); +{$endif} + + while not ADataSet.Eof do + begin + TRecord.FromDataSet(LItem, ADataSet); + +{$ifdef DelphiXE7_UP} + Result := Result + [LItem]; +{$else} + SetLength(Result, Length(Result) + 1); + Result[Length(Result)-1] := LItem; +{$endif} + + ADataSet.Next; + end; +end; + +class procedure TRecord.FromDataSet(var ARecord: R; + const ADataSet: TDataSet); +var + LRecordType: TRttiType; + LRecordField: TRttiField; + LDataSetField: TField; + LValue: TValue; +begin + if not ADataSet.Active then + ADataSet.Active := True; + + LRecordType := TRttiContext.Create.GetType(TypeInfo(R)); + for LRecordField in LRecordType.GetFields do + begin + LDataSetField := ADataSet.FindField(LRecordField.Name); + if Assigned(LDataSetField) then + begin + if LDataSetField.IsNull then + LRecordField.SetValue(@ARecord, TValue.Empty) + + else if (LDataSetField.DataType = ftBCD) then // MF 20170726 + LRecordField.SetValue(@ARecord, LDataSetField.AsFloat) + else if (LDataSetField.DataType = ftFMTBcd) then // MF 20170726 + LRecordField.SetValue(@ARecord, LDataSetField.AsFloat) + + else if LRecordField.FieldType.Handle = TypeInfo(Boolean) then + begin + if LDataSetField.DataType = ftBoolean then + LRecordField.SetValue(@ARecord, LDataSetField.AsBoolean) + else + LRecordField.SetValue(@ARecord, LDataSetField.AsInteger <> 0); + end + else if (LRecordField.FieldType.Handle = TypeInfo(TDateTime)) + or (LRecordField.FieldType.Handle = TypeInfo(TDate)) + or (LRecordField.FieldType.Handle = TypeInfo(TTime)) + then + begin + if (LDataSetField.DataType in [ftDate, ftDateTime, ftTime, ftTimeStamp]) then + LRecordField.SetValue(@ARecord, LDataSetField.AsDateTime) + else + LRecordField.SetValue(@ARecord, StrToDateTimeDef(LDataSetField.AsString, 0)); + end + else if LRecordField.FieldType is TRttiEnumerationType then + begin + if LDataSetField is TNumericField then + LRecordField.SetValue(@ARecord, TValue.FromOrdinal(LRecordField.FieldType.Handle, LDataSetField.AsInteger)) + else if LDataSetField is TStringField then + LRecordField.SetValue(@ARecord + , TValue.FromOrdinal( + LRecordField.FieldType.Handle + , GetEnumValue(LRecordField.FieldType.Handle, LDataSetField.AsString) + ) + ); + end + else + LRecordField.SetValue(@ARecord, TValue.FromVariant(LDataSetField.Value)); + end; + end; +end; + +class function TRecord.FromStrings(const AStrings: TStrings): R; +var + LRecordType: TRttiType; +begin + LRecordType := TRttiContext.Create.GetType(TypeInfo(R)); + Result := StringsToRecord(AStrings, LRecordType).AsType; +end; + +class function TRecord.GetFieldByName(var ARecord: R; + const AFieldName: string): TValue; +begin + Result := GetFieldByName(ARecord, AFieldName, TValue.Empty); +end; + +class function TRecord.GetFieldByName(var ARecord: R; + const AFieldName: string; const ADefault: TValue): TValue; +var + LRecordType: TRttiType; + LField: TRttiField; +begin + Result := ADefault; + LRecordType := TRttiContext.Create.GetType(TypeInfo(R)); + LField := LRecordType.GetField(AFieldName); + if Assigned(LField) then + Result := LField.GetValue(@ARecord); +end; + +class procedure TRecord.SetFieldByName(var ARecord: R; + const AFieldName: string; const AValue: TValue); +var + LRecordType: TRttiType; + LField: TRttiField; +begin + LRecordType := TRttiContext.Create.GetType(TypeInfo(R)); + + LField := LRecordType.GetField(AFieldName); + LField.SetValue(@ARecord, AValue); +end; + + +class function TRecord.ToArrayOfString(const ARecord: R): TArray; +var + LDummy: TStrings; +begin + LDummy := TStringList.Create; + try + ToStrings(ARecord, LDummy); + Result := LDummy.ToStringArray; + finally + LDummy.Free; + end; +end; + +class procedure TRecord.ToDataSet(const ARecord: R; const ADataSet: TDataSet; + const AAppend: Boolean); +var + LRecordType: TRttiType; + LRecordField: TRttiField; + LDataSetField: TField; + LValue: TValue; +begin + LRecordType := TRttiContext.Create.GetType(TypeInfo(R)); + + if AAppend then + ADataSet.Append + else + ADataSet.Edit; + try + for LRecordField in LRecordType.GetFields do + begin + LDataSetField := ADataSet.FindField(LRecordField.Name); + if Assigned(LDataSetField) and not (AAppend and (LDataSetField.DataType = ftAutoInc)) then + begin + LValue := LRecordField.GetValue(@ARecord); + if LValue.IsEmpty then + LDataSetField.Clear + else + LDataSetField.Value := LValue.AsVariant; + + // set NULL for 0.0 DateTime values + if (LDataSetField.DataType in [ftDate, ftDateTime, ftTime, ftTimeStamp]) and (LDataSetField.Value = 0.0) then + LDataSetField.Clear; + end; + end; + ADataSet.Post; + except + ADataSet.Cancel; + raise; + end; +end; + +class procedure TRecord.ToStrings(const ARecord: R; var AStrings: TStrings; + const AClear: Boolean); +var + LRecordType: TRttiType; + LField: TRttiField; + LFieldType: TRttiType; + LFieldValue: TValue; + LToStringMethod: TRttiMethod; +begin + Assert(Assigned(AStrings)); + + if AClear then + AStrings.Clear; + + LRecordType := TRttiContext.Create.GetType(TypeInfo(R)); + + for LField in LRecordType.GetFields do + begin + LFieldType := LField.FieldType; + LFieldValue := LField.GetValue(@ARecord); + + if LFieldType.IsRecord then + begin + LToStringMethod := LFieldType.GetMethod('ToString'); + if Assigned(LToStringMethod) then + AStrings.Values[LField.Name] := LToStringMethod.Invoke(LFieldValue, []).ToString + else + begin + //AM TODO recursion using ToStrings here + AStrings.Values[LField.Name] := LField.GetValue(@ARecord).ToString; + end; + end + else + AStrings.Values[LField.Name] := LField.GetValue(@ARecord).ToString; + end; +end; + +end. diff --git a/Source/MARS.Utils.Parameters.pas b/Source/MARS.Utils.Parameters.pas index 6599587b..6168b7b0 100644 --- a/Source/MARS.Utils.Parameters.pas +++ b/Source/MARS.Utils.Parameters.pas @@ -42,6 +42,9 @@ TMARSParametersSlice = class function CopyFrom(const ASource: TMARSParametersSlice; const ASliceName: string = ''): Integer; function ToString: string; override; + procedure AsStrings(var AStrings: TStrings; const AClearBefore: Boolean = True); overload; + function AsStrings: TStrings; overload; + function AsStringArray(const ANameValueSeparator: string = '='): TArray; property Count: Integer read GetCount; property IsEmpty: Boolean read GetIsEmpty; @@ -87,6 +90,33 @@ procedure TMARSParametersSlice.Assign(const ASource: TMARSParametersSlice); Fitems.Add(LItem.Key, LItem.Value); end; +function TMARSParametersSlice.AsStringArray( + const ANameValueSeparator: string): TArray; +var + LItem: TPair; +begin + Result := []; + for LItem in FItems do + begin + Result := Result + [LItem.Key + ANameValueSeparator + LItem.Value.ToString]; + end; +end; + +procedure TMARSParametersSlice.AsStrings(var AStrings: TStrings; + const AClearBefore: Boolean); +begin + if AClearBefore then + AStrings.Clear; + + AStrings.AddStrings(AsStringArray(AStrings.NameValueSeparator)); +end; + +function TMARSParametersSlice.AsStrings: TStrings; +begin + Result := TStringList.Create; + AsStrings(Result); +end; + function TMARSParametersSlice.ByName(const AName: string; const ADefault: TValue): TValue; var @@ -289,16 +319,8 @@ procedure TMARSParametersSlice.SetValue(AName: string; const Value: TValue); end; function TMARSParametersSlice.ToString: string; -var - LItem: TPair; begin - Result := ''; - for LItem in FItems do - begin - if Result <> '' then - Result := Result + sLineBreak; - Result := Result + LItem.Key +': ' + LItem.Value.ToString; - end; + Result := string.Join(sLineBreak, AsStringArray(': ')); end; end. diff --git a/Source/MARS.http.Server.DCS.pas b/Source/MARS.http.Server.DCS.pas new file mode 100644 index 00000000..8d2f346b --- /dev/null +++ b/Source/MARS.http.Server.DCS.pas @@ -0,0 +1,568 @@ +(* + Copyright 2016, MARS-Curiosity library + + Home: https://github.com/andrea-magni/MARS +*) +unit MARS.http.Server.DCS; + +{$I MARS.inc} + +interface + +uses + System.SysUtils, System.Classes, System.Generics.Collections, System.TimeSpan, DateUtils +, Net.CrossSocket.Base, Net.CrossSocket, Net.CrossHttpServer +, Net.CrossHttpMiddleware, Net.CrossHttpUtils +, MARS.Core.Engine, MARS.Core.Token +, MARS.Core.RequestAndResponse.Interfaces +; + +type + TMARSDCSRequest = class(TInterfacedObject, IMARSRequest) + private + FDCSRequest: ICrossHttpRequest; + public + // IMARSRequest ------------------------------------------------------------ + function AsObject: TObject; + function GetAccept: string; + function GetAuthorization: string; + function GetContent: string; + function GetCookieParamIndex(const AName: string): Integer; + function GetCookieParamValue(const AIndex: Integer): string; overload; + function GetCookieParamValue(const AName: string): string; overload; + function GetCookieParamCount: Integer; + function GetFilesCount: Integer; + function GetFormParamCount: Integer; + function GetFormParamIndex(const AName: string): Integer; + function GetFormParamName(const AIndex: Integer): string; + function GetFormParamValue(const AIndex: Integer): string; overload; + function GetFormParamValue(const AName: string): string; overload; + function GetFormFileParamIndex(const AName: string): Integer; + function GetFormFileParam(const AIndex: Integer; out AFieldName: string; + out AFileName: string; out ABytes: System.TArray; + out AContentType: string): Boolean; + function GetFormParams: string; + function GetHeaderParamValue(const AHeaderName: string): string; + function GetHostName: string; + function GetMethod: string; + function GetPort: Integer; + function GetQueryParamIndex(const AName: string): Integer; + function GetQueryParamValue(const AIndex: Integer): string; + function GetQueryParamCount: Integer; + function GetQueryString: string; + function GetRawContent: TBytes; + function GetRawPath: string; + procedure CheckWorkaroundForISAPI; + // ------------------------------------------------------------------------- + constructor Create(ADCSRequest: ICrossHttpRequest); virtual; + end; + + TMARSDCSResponse = class(TInterfacedObject, IMARSResponse) + private + FDCSResponse: ICrossHttpResponse; + public + // IMARSResponse ----------------------------------------------------------- + function GetContent: string; + function GetContentEncoding: string; + function GetContentStream: TStream; + function GetContentType: string; + function GetStatusCode: Integer; + procedure SetContent(const AContent: string); + procedure SetContentEncoding(const AContentEncoding: string); + procedure SetContentStream(const AContentStream: TStream); + procedure SetContentType(const AContentType: string); + procedure SetHeader(const AName: string; const AValue: string); + procedure SetStatusCode(const AStatusCode: Integer); + procedure SetCookie(const AName, AValue, ADomain, APath: string; const AExpiration: TDateTime; const ASecure: Boolean); + // ------------------------------------------------------------------------- + constructor Create(ADCSResponse: ICrossHttpResponse); virtual; + end; + + + TMARShttpServerDCS = class + private + FStoppedAt: TDateTime; + FEngine: TMARSEngine; + FStartedAt: TDateTime; + FActive: Boolean; + FDefaultPort: Integer; + FHttpServer: ICrossHttpServer; + function GetUpTime: TTimeSpan; + procedure SetActive(const Value: Boolean); + procedure SetDefaultPort(const Value: Integer); + protected + procedure Startup; virtual; + procedure Shutdown; virtual; + public + constructor Create(AEngine: TMARSEngine); virtual; + destructor Destroy; override; + + property Active: Boolean read FActive write SetActive; + property DefaultPort: Integer read FDefaultPort write SetDefaultPort; + property Engine: TMARSEngine read FEngine; + property StartedAt: TDateTime read FStartedAt; + property StoppedAt: TDateTime read FStoppedAt; + property UpTime: TTimeSpan read GetUpTime; + end; + +implementation + +uses + Net.CrossHttpParams; + +{ TMARShttpServerDCS } + +constructor TMARShttpServerDCS.Create(AEngine: TMARSEngine); +begin + inherited Create; + FEngine := AEngine; + FHttpServer := TCrossHttpServer.Create(0); +end; + +destructor TMARShttpServerDCS.Destroy; +begin + FHttpServer := nil; + inherited; +end; + +function TMARShttpServerDCS.GetUpTime: TTimeSpan; +begin + if Active then + Result := TTimeSpan.FromSeconds(SecondsBetween(FStartedAt, Now)) + else if StoppedAt > 0 then + Result := TTimeSpan.FromSeconds(SecondsBetween(FStartedAt, FStoppedAt)) + else + Result := TTimeSpan.Zero; +end; + +procedure TMARShttpServerDCS.SetActive(const Value: Boolean); +begin + if FActive <> Value then + begin + FActive := Value; + if FActive then + Startup + else + Shutdown; + end; +end; + +procedure TMARShttpServerDCS.SetDefaultPort(const Value: Integer); +begin + FDefaultPort := Value; +end; + +procedure TMARShttpServerDCS.Shutdown; +begin + FHttpServer.Stop; + + FStoppedAt := Now; +end; + +procedure TMARShttpServerDCS.Startup; +begin + FHttpServer.Addr := IPv4v6_ALL; // IPv4v6 + FHttpServer.Port := DefaultPort; + FHttpServer.Compressible := True; + + FHttpServer +// .Get('/hello', +// procedure(ARequest: ICrossHttpRequest; AResponse: ICrossHttpResponse) +// begin +// AResponse.Send('Hello World'); +// end) + .All(FEngine.BasePath + '*', + procedure(ARequest: ICrossHttpRequest; AResponse: ICrossHttpResponse; var AHandled: Boolean) + begin + AHandled := FEngine.HandleRequest(TMARSDCSRequest.Create(ARequest), TMARSDCSResponse.Create(AResponse)) + end + ); + + FHttpServer.Start; + + FStartedAt := Now; + FStoppedAt := 0; +end; + +{ TMARSDCSRequest } + +function TMARSDCSRequest.AsObject: TObject; +begin + Result := Self; +end; + +procedure TMARSDCSRequest.CheckWorkaroundForISAPI; +begin + // nothing to do +end; + +constructor TMARSDCSRequest.Create(ADCSRequest: ICrossHttpRequest); +begin + inherited Create; + FDCSRequest := ADCSRequest; +end; + +function TMARSDCSRequest.GetAccept: string; +begin + Result := FDCSRequest.Accept; +end; + +function TMARSDCSRequest.GetAuthorization: string; +begin + Result := FDCSRequest.Authorization; +end; + +function TMARSDCSRequest.GetContent: string; +begin +//AM TODO + Result := ''; +end; + +function TMARSDCSRequest.GetCookieParamCount: Integer; +begin + Result := FDCSRequest.Cookies.Count; +end; + +function TMARSDCSRequest.GetCookieParamIndex(const AName: string): Integer; +var + LIndex: Integer; +begin + Result := -1; + for LIndex := 0 to FDCSRequest.Cookies.Count -1 do + begin + if SameText(FDCSRequest.Cookies.Items[LIndex].Name, AName) then + begin + Result := LIndex; + Break; + end; + end; +end; + +function TMARSDCSRequest.GetCookieParamValue(const AName: string): string; +var + LIndex: Integer; + LCookie: TNameValue; +begin + Result := ''; + for LIndex := 0 to FDCSRequest.Cookies.Count -1 do + begin + LCookie := FDCSRequest.Cookies.Items[LIndex]; + if SameText(LCookie.Name, AName) then + begin + Result := LCookie.Value; + Break; + end; + end; +end; + +function TMARSDCSRequest.GetCookieParamValue(const AIndex: Integer): string; +begin + Result := FDCSRequest.Cookies.Items[AIndex].Value; +end; + +function TMARSDCSRequest.GetFilesCount: Integer; +begin +//AM TODO + Result := 0; +end; + +function TMARSDCSRequest.GetFormFileParam(const AIndex: Integer; out AFieldName, + AFileName: string; out ABytes: System.TArray; + out AContentType: string): Boolean; +var + LMultiPartBody: THttpMultiPartFormData; + LFile: TFormField; + +begin + Result := False; + if FDCSRequest.BodyType = btMultiPart then + begin + LMultiPartBody := FDCSRequest.Body as THttpMultiPartFormData; + Result := (AIndex >= 0) and (AIndex < LMultiPartBody.Count); + if Result then + begin + LFile := LMultiPartBody.Items[AIndex]; + AFieldName := LFile.Name; + AFileName := LFile.FileName; + ABytes := LFile.AsBytes; + AContentType := LFile.ContentType; + end; + end; +end; + +function TMARSDCSRequest.GetFormFileParamIndex(const AName: string): Integer; +var + LMultiPartBody: THttpMultiPartFormData; + LIndex: Integer; + LItem: TFormField; + LURLParamsBody: THttpUrlParams; + LURLParamItem: TNameValue; +begin + Result := -1; + if FDCSRequest.BodyType = btMultiPart then + begin + LMultiPartBody := FDCSRequest.Body as THttpMultiPartFormData; + + for LIndex := 0 to LMultiPartBody.Count - 1 do + begin + LItem := LMultiPartBody.Items[LIndex]; + + if SameText(LItem.Name, AName) then + begin + Result := LIndex; + Break; + end; + end; + end + else if FDCSRequest.BodyType = btUrlEncoded then + begin + LURLParamsBody := FDCSRequest.Body as THttpUrlParams; + + for LIndex := 0 to LURLParamsBody.Count-1 do + begin + LURLParamItem := LURLParamsBody.Items[LIndex]; + if SameText(LURLParamItem.Name, AName) then + begin + Result := LIndex; + Break; + end; + end; + end; +end; + +function TMARSDCSRequest.GetFormParamCount: Integer; +begin +//AM TODO + Result := 0; +end; + +function TMARSDCSRequest.GetFormParamIndex(const AName: string): Integer; +var + LMultiPartBody: THttpMultiPartFormData; + LIndex: Integer; + LItem: TFormField; + LURLParamsBody: THttpUrlParams; + LURLParamItem: TNameValue; +begin + Result := -1; + if FDCSRequest.BodyType = btMultiPart then + begin + LMultiPartBody := FDCSRequest.Body as THttpMultiPartFormData; + + for LIndex := 0 to LMultiPartBody.Count - 1 do + begin + LItem := LMultiPartBody.Items[LIndex]; + + if SameText(LItem.Name, AName) then + begin + Result := LIndex; + Break; + end; + end; + end + else if FDCSRequest.BodyType = btUrlEncoded then + begin + LURLParamsBody := FDCSRequest.Body as THttpUrlParams; + + for LIndex := 0 to LURLParamsBody.Count-1 do + begin + LURLParamItem := LURLParamsBody.Items[LIndex]; + if SameText(LURLParamItem.Name, AName) then + begin + Result := LIndex; + Break; + end; + end; + end; +end; + +function TMARSDCSRequest.GetFormParamName(const AIndex: Integer): string; +var + LMultiPartBody: THttpMultiPartFormData; + LURLParamsBody: THttpUrlParams; +begin + Result := ''; + if FDCSRequest.BodyType = btMultiPart then + begin + LMultiPartBody := FDCSRequest.Body as THttpMultiPartFormData; + Result := LMultiPartBody.Items[AIndex].Name; + end + else if FDCSRequest.BodyType = btUrlEncoded then + begin + LURLParamsBody := FDCSRequest.Body as THttpUrlParams; + Result := LURLParamsBody.Items[AIndex].Name; + end; +end; + + +function TMARSDCSRequest.GetFormParams: string; +begin +//AM TODO + Result := ''; +end; + +function TMARSDCSRequest.GetFormParamValue(const AName: string): string; +begin + Result := GetFormParamValue(GetFormParamIndex(AName)); +end; + +function TMARSDCSRequest.GetFormParamValue(const AIndex: Integer): string; +var + LMultiPartBody: THttpMultiPartFormData; + LURLParamsBody: THttpUrlParams; +begin + Result := ''; + if FDCSRequest.BodyType = btMultiPart then + begin + LMultiPartBody := FDCSRequest.Body as THttpMultiPartFormData; + Result := LMultiPartBody.Items[AIndex].AsString; + end + else if FDCSRequest.BodyType = btUrlEncoded then + begin + LURLParamsBody := FDCSRequest.Body as THttpUrlParams; + Result := LURLParamsBody.Items[AIndex].Value; + end; +end; + +function TMARSDCSRequest.GetHeaderParamValue(const AHeaderName: string): string; +begin + Result := ''; + FDCSRequest.Header.GetParamValue(AHeaderName, Result); +end; + +function TMARSDCSRequest.GetHostName: string; +begin + Result := FDCSRequest.HostName; +end; + +function TMARSDCSRequest.GetMethod: string; +begin + Result := FDCSRequest.Method; +end; + +function TMARSDCSRequest.GetPort: Integer; +begin + Result := FDCSRequest.HostPort; +end; + +function TMARSDCSRequest.GetQueryParamCount: Integer; +begin + Result := FDCSRequest.Query.Count; +end; + +function TMARSDCSRequest.GetQueryParamIndex(const AName: string): Integer; +var + LIndex: Integer; +begin + Result := -1; + for LIndex := 0 to FDCSRequest.Query.Count -1 do + begin + if SameText(FDCSRequest.Query.Items[LIndex].Name, AName) then + begin + Result := LIndex; + Break; + end; + end; +end; + +function TMARSDCSRequest.GetQueryParamValue(const AIndex: Integer): string; +begin + Result := FDCSRequest.Query.Items[AIndex].Value; +end; + +function TMARSDCSRequest.GetQueryString: string; +begin +//AM TODO controllare + Result := FDCSRequest.Query.ToString; +end; + +function TMARSDCSRequest.GetRawContent: TBytes; +begin + Result := []; + case FDCSRequest.BodyType of +// btNone: Result := []; +// btUrlEncoded: ; +// btMultiPart: Result := THttpMultiPartFormData(FDCSRequest.Body).Bytes; + btBinary: Result := TBytesStream(FDCSRequest.Body).Bytes; + end; +end; + +function TMARSDCSRequest.GetRawPath: string; +begin +//AM TODO controllare RawPathAndParams? + Result := FDCSRequest.Path; +end; + +{ TMARSDCSResponse } + +constructor TMARSDCSResponse.Create(ADCSResponse: ICrossHttpResponse); +begin + inherited Create; + FDCSResponse := ADCSResponse; +end; + +function TMARSDCSResponse.GetContent: string; +begin +//AM TODO + Result := ''; +end; + +function TMARSDCSResponse.GetContentEncoding: string; +begin +//AM TODO + Result := ''; +end; + +function TMARSDCSResponse.GetContentStream: TStream; +begin +//AM TODO + Result := nil; +end; + +function TMARSDCSResponse.GetContentType: string; +begin + Result := FDCSResponse.ContentType; +end; + +function TMARSDCSResponse.GetStatusCode: Integer; +begin + Result := FDCSResponse.StatusCode; +end; + +procedure TMARSDCSResponse.SetContent(const AContent: string); +begin + FDCSResponse.Send(AContent); +end; + +procedure TMARSDCSResponse.SetContentEncoding(const AContentEncoding: string); +begin +//AM TODO +end; + +procedure TMARSDCSResponse.SetContentStream(const AContentStream: TStream); +begin + FDCSResponse.Send(AContentStream); +end; + +procedure TMARSDCSResponse.SetContentType(const AContentType: string); +begin + FDCSResponse.ContentType := AContentType; +end; + +procedure TMARSDCSResponse.SetCookie(const AName, AValue, ADomain, + APath: string; const AExpiration: TDateTime; const ASecure: Boolean); +begin + FDCSResponse.Cookies.AddOrSet(AName, AValue, SecondsBetween(Now, AExpiration), APath, ADomain, False {AHttpOnly}, ASecure); +end; + +procedure TMARSDCSResponse.SetHeader(const AName, AValue: string); +begin + FDCSResponse.Header.Add(AName, AValue); +end; + +procedure TMARSDCSResponse.SetStatusCode(const AStatusCode: Integer); +begin + FDCSResponse.StatusCode := AStatusCode; +end; + +end. diff --git a/Source/MARS.http.Server.Indy.pas b/Source/MARS.http.Server.Indy.pas index 174fba7f..22a5fd1f 100644 --- a/Source/MARS.http.Server.Indy.pas +++ b/Source/MARS.http.Server.Indy.pas @@ -15,15 +15,76 @@ interface , IdContext, IdCustomHTTPServer, IdException, IdTCPServer, IdIOHandlerSocket , IdSchedulerOfThreadPool , idHTTPWebBrokerBridge - - , MARS.Core.Engine - , MARS.Core.Token + , Web.HttpApp + , MARS.Core.Engine, MARS.Core.Token, MARS.Core.RequestAndResponse.Interfaces ; type TBeforeCommandGetFunc = reference to function (AContext: TIdContext; ARequestInfo: TIdHTTPRequestInfo; AResponseInfo: TIdHTTPResponseInfo): Boolean; + TMARSWebRequest = class(TInterfacedObject, IMARSRequest) + private + FWebRequest: TWebRequest; + public + // IMARSRequest ------------------------------------------------------------ + function AsObject: TObject; inline; + function GetAccept: string; inline; + function GetAuthorization: string; inline; + function GetContent: string; inline; + function GetCookieParamIndex(const AName: string): Integer; inline; + function GetCookieParamValue(const AIndex: Integer): string; overload; inline; + function GetCookieParamValue(const AName: string): string; overload; inline; + function GetCookieParamCount: Integer; + function GetFilesCount: Integer; inline; + function GetFormParamCount: Integer; inline; + function GetFormParamIndex(const AName: string): Integer; inline; + function GetFormParamName(const AIndex: Integer): string; inline; + function GetFormParamValue(const AIndex: Integer): string; overload; inline; + function GetFormParamValue(const AName: string): string; overload; inline; + function GetFormFileParamIndex(const AName: string): Integer; inline; + function GetFormFileParam(const AIndex: Integer; out AFieldName, AFileName: string; + out ABytes: TBytes; out AContentType: string): Boolean; + function GetFormParams: string; inline; + function GetHeaderParamValue(const AHeaderName: string): string; inline; + function GetHostName: string; inline; + function GetMethod: string; inline; + function GetPort: Integer; inline; + function GetQueryParamIndex(const AName: string): Integer; inline; + function GetQueryParamValue(const AIndex: Integer): string; inline; + function GetQueryParamCount: Integer; + function GetQueryString: string; inline; + function GetRawContent: TBytes; inline; + function GetRawPath: string; inline; + procedure CheckWorkaroundForISAPI; + // ------------------------------------------------------------------------- + constructor Create(AWebRequest: TWebRequest); virtual; + + property WebRequest: TWebRequest read FWebRequest; + end; + + TMARSWebResponse = class(TInterfacedObject, IMARSResponse) + private + FWebResponse: TWebResponse; + public + // IMARSResponse ----------------------------------------------------------- + function GetContent: string; inline; + function GetContentEncoding: string; inline; + function GetContentStream: TStream; inline; + function GetContentType: string; inline; + function GetStatusCode: Integer; inline; + procedure SetContent(const AContent: string); inline; + procedure SetContentEncoding(const AContentEncoding: string); inline; + procedure SetContentStream(const AContentStream: TStream); inline; + procedure SetContentType(const AContentType: string); inline; + procedure SetHeader(const AName: string; const AValue: string); inline; + procedure SetStatusCode(const AStatusCode: Integer); inline; + procedure SetCookie(const AName, AValue, ADomain, APath: string; const AExpiration: TDateTime; const ASecure: Boolean); inline; + // ------------------------------------------------------------------------- + constructor Create(AWebResponse: TWebResponse); virtual; + end; + + TMARShttpServerIndy = class(TIdCustomHTTPServer) private FEngine: TMARSEngine; @@ -59,14 +120,9 @@ implementation uses StrUtils, DateUtils -{$ifdef DelphiXE7_UP} - , Web.HttpApp -{$else} - , HttpApp -{$endif} - , IdCookie - , MARS.Core.Utils - ; +, IdCookie +, MARS.Core.Utils +; { TMARShttpServerIndy } @@ -98,7 +154,7 @@ procedure TMARShttpServerIndy.DoCommandGet(AContext: TIdContext; LResponse.FreeContentStream := False; AResponseInfo.FreeContentStream := True; try - if not FEngine.HandleRequest(LRequest, LResponse) then + if not FEngine.HandleRequest(TMARSWebRequest.Create(LRequest), TMARSWebResponse.Create(LResponse)) then begin LResponse.ContentType := 'application/json'; LResponse.Content := @@ -209,4 +265,252 @@ procedure TMARShttpServerIndy.Startup; inherited; end; +{ TMARSWebRequest } + +function TMARSWebRequest.AsObject: TObject; +begin + Result := Self; +end; + +procedure TMARSWebRequest.CheckWorkaroundForISAPI; +begin + FWebRequest.ReadTotalContent; // workaround for https://quality.embarcadero.com/browse/RSP-14674 +end; + +constructor TMARSWebRequest.Create(AWebRequest: TWebRequest); +begin + inherited Create; + FWebRequest := AWebRequest; +end; + +function TMARSWebRequest.GetAccept: string; +begin + Result := FWebRequest.Accept; +end; + +function TMARSWebRequest.GetAuthorization: string; +begin + Result := FWebRequest.Authorization; +end; + +function TMARSWebRequest.GetContent: string; +begin + Result := FWebRequest.Content; +end; + +function TMARSWebRequest.GetCookieParamCount: Integer; +begin + Result := FWebRequest.CookieFields.Count; +end; + +function TMARSWebRequest.GetCookieParamIndex(const AName: string): Integer; +begin + Result := FWebRequest.CookieFields.IndexOfName(AName); +end; + +function TMARSWebRequest.GetCookieParamValue(const AName: string): string; +begin + Result := FWebRequest.CookieFields.Values[AName]; +end; + +function TMARSWebRequest.GetCookieParamValue(const AIndex: Integer): string; +begin + Result := FWebRequest.CookieFields.ValueFromIndex[AIndex]; +end; + +function TMARSWebRequest.GetFilesCount: Integer; +begin + Result := FWebRequest.Files.Count; +end; + +function TMARSWebRequest.GetFormFileParam(const AIndex: Integer; out AFieldName, + AFileName: string; out ABytes: TBytes; out AContentType: string): Boolean; +var + LFile: TAbstractWebRequestFile; +begin + Result := (AIndex >= 0) and (AIndex < FWebRequest.Files.Count); + if Result then + begin + LFile := FWebRequest.Files[AIndex]; + AFieldName := LFile.FieldName; + AFileName := LFile.FileName; + ABytes := StreamToBytes(LFile.Stream); + AContentType := LFile.ContentType; + end; +end; + +function TMARSWebRequest.GetFormFileParamIndex(const AName: string): Integer; +var + LFile: TAbstractWebRequestFile; + LIndex: Integer; +begin + Result := -1; + for LIndex := 0 to FWebRequest.Files.Count-1 do + begin + LFile := FWebRequest.Files[LIndex]; + if SameText(LFile.FieldName, AName) then + begin + Result := LIndex; + Break; + end; + end; +end; + +function TMARSWebRequest.GetFormParamCount: Integer; +begin + Result := FWebRequest.ContentFields.Count; +end; + +function TMARSWebRequest.GetFormParamIndex(const AName: string): Integer; +begin + Result := FWebRequest.ContentFields.IndexOfName(AName); +end; + +function TMARSWebRequest.GetFormParamName(const AIndex: Integer): string; +begin + Result := FWebRequest.ContentFields.Names[AIndex]; +end; + +function TMARSWebRequest.GetFormParams: string; +begin + Result := FWebRequest.ContentFields.Text; +end; + +function TMARSWebRequest.GetFormParamValue(const AIndex: Integer): string; +begin + Result := FWebRequest.ContentFields.ValueFromIndex[AIndex]; +end; + +function TMARSWebRequest.GetFormParamValue(const AName: string): string; +begin + Result := FWebRequest.ContentFields.Values[AName]; +end; + +function TMARSWebRequest.GetHeaderParamValue(const AHeaderName: string): string; +begin + Result := FWebRequest.GetFieldByName(AHeaderName); +end; + +function TMARSWebRequest.GetHostName: string; +begin + Result := FWebRequest.Host; +end; + +function TMARSWebRequest.GetMethod: string; +begin + Result := FWebRequest.Method; +end; + +function TMARSWebRequest.GetPort: Integer; +begin + Result := FWebRequest.ServerPort; +end; + +function TMARSWebRequest.GetQueryParamCount: Integer; +begin + Result := FWebRequest.QueryFields.Count; +end; + +function TMARSWebRequest.GetQueryParamIndex(const AName: string): Integer; +begin + Result := FWebRequest.QueryFields.IndexOfName(AName); +end; + +function TMARSWebRequest.GetQueryParamValue(const AIndex: Integer): string; +begin + Result := FWebRequest.QueryFields.ValueFromIndex[AIndex]; +end; + +function TMARSWebRequest.GetQueryString: string; +begin + Result := FWebRequest.Query; +end; + +function TMARSWebRequest.GetRawContent: TBytes; +begin + Result := FWebRequest.RawContent; +end; + +function TMARSWebRequest.GetRawPath: string; +begin + Result := FWebRequest.RawPathInfo; +end; + +{ TMARSWebResponse } + +constructor TMARSWebResponse.Create(AWebResponse: TWebResponse); +begin + inherited Create; + FWebResponse := AWebResponse; +end; + +function TMARSWebResponse.GetContent: string; +begin + Result := FWebResponse.Content; +end; + +function TMARSWebResponse.GetContentEncoding: string; +begin + Result := FWebResponse.ContentEncoding; +end; + +function TMARSWebResponse.GetContentStream: TStream; +begin + Result := FWebResponse.ContentStream; +end; + +function TMARSWebResponse.GetContentType: string; +begin + Result := FWebResponse.ContentType; +end; + +function TMARSWebResponse.GetStatusCode: Integer; +begin + Result := FWebResponse.StatusCode; +end; + +procedure TMARSWebResponse.SetContent(const AContent: string); +begin + FWebResponse.Content := AContent; +end; + +procedure TMARSWebResponse.SetContentEncoding(const AContentEncoding: string); +begin + FWebResponse.ContentEncoding := AContentEncoding; +end; + +procedure TMARSWebResponse.SetContentStream(const AContentStream: TStream); +begin + FWebResponse.ContentStream := AContentStream; +end; + +procedure TMARSWebResponse.SetContentType(const AContentType: string); +begin + FWebResponse.ContentType := AContentType; +end; + +procedure TMARSWebResponse.SetCookie(const AName, AValue, ADomain, + APath: string; const AExpiration: TDateTime; const ASecure: Boolean); +var + LSL: TStringList; +begin + LSL := TStringList.Create; + try + LSL.Values[AName] := AValue; + FWebResponse.SetCookieField(LSL, ADomain, APath, AExpiration, ASecure{, AHttpOnly}); + finally + LSL.Free; + end; +end; + +procedure TMARSWebResponse.SetHeader(const AName, AValue: string); +begin + FWebResponse.CustomHeaders.Values[AName] := AValue; +end; + +procedure TMARSWebResponse.SetStatusCode(const AStatusCode: Integer); +begin + FWebResponse.StatusCode := AStatusCode; +end; + end. diff --git a/Source/MARS.inc b/Source/MARS.inc index c5231212..68defed5 100644 --- a/Source/MARS.inc +++ b/Source/MARS.inc @@ -1,7 +1,5 @@ -// *** BEWARE *** -// if your Delphi edition/license does not include FireDAC, -// remove the following MARS_FIREDAC definition! -{$define MARS_FIREDAC} +{$define MARS_FIREDAC} // To enable MARS FireDAC support +{.$define MARS_UNIDAC} // To enable MARS Devart UniDAC support {$if compilerversion = 22} {$define DelphiXE} @@ -51,6 +49,10 @@ {$define Delphi10Rio} {$ENDIF} +{$if compilerversion = 34} + {$define Delphi10Sydney} +{$ENDIF} + {$if compilerversion >= 22} {$define DelphiXE_UP} {$ENDIF} @@ -164,3 +166,19 @@ {$define Delphi10Tokyo_UP} {$define Delphi10Rio_UP} {$ENDIF} + +{$if compilerversion >= 34} + {$define DelphiXE_UP} + {$define DelphiXE2_UP} + {$define DelphiXE3_UP} + {$define DelphiXE4_UP} + {$define DelphiXE5_UP} + {$define DelphiXE6_UP} + {$define DelphiXE7_UP} + {$define DelphiXE8_UP} + {$define Delphi10Seattle_UP} + {$define Delphi10Berlin_UP} + {$define Delphi10Tokyo_UP} + {$define Delphi10Rio_UP} + {$define Delphi10Sydney_UP} +{$ENDIF} diff --git a/ThirdParty/DCS/LICENSE b/ThirdParty/DCS/LICENSE new file mode 100644 index 00000000..65c5ca88 --- /dev/null +++ b/ThirdParty/DCS/LICENSE @@ -0,0 +1,165 @@ + GNU LESSER GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + + This version of the GNU Lesser General Public License incorporates +the terms and conditions of version 3 of the GNU General Public +License, supplemented by the additional permissions listed below. + + 0. Additional Definitions. + + As used herein, "this License" refers to version 3 of the GNU Lesser +General Public License, and the "GNU GPL" refers to version 3 of the GNU +General Public License. + + "The Library" refers to a covered work governed by this License, +other than an Application or a Combined Work as defined below. + + An "Application" is any work that makes use of an interface provided +by the Library, but which is not otherwise based on the Library. +Defining a subclass of a class defined by the Library is deemed a mode +of using an interface provided by the Library. + + A "Combined Work" is a work produced by combining or linking an +Application with the Library. The particular version of the Library +with which the Combined Work was made is also called the "Linked +Version". + + The "Minimal Corresponding Source" for a Combined Work means the +Corresponding Source for the Combined Work, excluding any source code +for portions of the Combined Work that, considered in isolation, are +based on the Application, and not on the Linked Version. + + The "Corresponding Application Code" for a Combined Work means the +object code and/or source code for the Application, including any data +and utility programs needed for reproducing the Combined Work from the +Application, but excluding the System Libraries of the Combined Work. + + 1. Exception to Section 3 of the GNU GPL. + + You may convey a covered work under sections 3 and 4 of this License +without being bound by section 3 of the GNU GPL. + + 2. Conveying Modified Versions. + + If you modify a copy of the Library, and, in your modifications, a +facility refers to a function or data to be supplied by an Application +that uses the facility (other than as an argument passed when the +facility is invoked), then you may convey a copy of the modified +version: + + a) under this License, provided that you make a good faith effort to + ensure that, in the event an Application does not supply the + function or data, the facility still operates, and performs + whatever part of its purpose remains meaningful, or + + b) under the GNU GPL, with none of the additional permissions of + this License applicable to that copy. + + 3. Object Code Incorporating Material from Library Header Files. + + The object code form of an Application may incorporate material from +a header file that is part of the Library. You may convey such object +code under terms of your choice, provided that, if the incorporated +material is not limited to numerical parameters, data structure +layouts and accessors, or small macros, inline functions and templates +(ten or fewer lines in length), you do both of the following: + + a) Give prominent notice with each copy of the object code that the + Library is used in it and that the Library and its use are + covered by this License. + + b) Accompany the object code with a copy of the GNU GPL and this license + document. + + 4. Combined Works. + + You may convey a Combined Work under terms of your choice that, +taken together, effectively do not restrict modification of the +portions of the Library contained in the Combined Work and reverse +engineering for debugging such modifications, if you also do each of +the following: + + a) Give prominent notice with each copy of the Combined Work that + the Library is used in it and that the Library and its use are + covered by this License. + + b) Accompany the Combined Work with a copy of the GNU GPL and this license + document. + + c) For a Combined Work that displays copyright notices during + execution, include the copyright notice for the Library among + these notices, as well as a reference directing the user to the + copies of the GNU GPL and this license document. + + d) Do one of the following: + + 0) Convey the Minimal Corresponding Source under the terms of this + License, and the Corresponding Application Code in a form + suitable for, and under terms that permit, the user to + recombine or relink the Application with a modified version of + the Linked Version to produce a modified Combined Work, in the + manner specified by section 6 of the GNU GPL for conveying + Corresponding Source. + + 1) Use a suitable shared library mechanism for linking with the + Library. A suitable mechanism is one that (a) uses at run time + a copy of the Library already present on the user's computer + system, and (b) will operate properly with a modified version + of the Library that is interface-compatible with the Linked + Version. + + e) Provide Installation Information, but only if you would otherwise + be required to provide such information under section 6 of the + GNU GPL, and only to the extent that such information is + necessary to install and execute a modified version of the + Combined Work produced by recombining or relinking the + Application with a modified version of the Linked Version. (If + you use option 4d0, the Installation Information must accompany + the Minimal Corresponding Source and Corresponding Application + Code. If you use option 4d1, you must provide the Installation + Information in the manner specified by section 6 of the GNU GPL + for conveying Corresponding Source.) + + 5. Combined Libraries. + + You may place library facilities that are a work based on the +Library side by side in a single library together with other library +facilities that are not Applications and are not covered by this +License, and convey such a combined library under terms of your +choice, if you do both of the following: + + a) Accompany the combined library with a copy of the same work based + on the Library, uncombined with any other library facilities, + conveyed under the terms of this License. + + b) Give prominent notice with the combined library that part of it + is a work based on the Library, and explaining where to find the + accompanying uncombined form of the same work. + + 6. Revised Versions of the GNU Lesser General Public License. + + The Free Software Foundation may publish revised and/or new versions +of the GNU Lesser General Public License from time to time. Such new +versions will be similar in spirit to the present version, but may +differ in detail to address new problems or concerns. + + Each version is given a distinguishing version number. If the +Library as you received it specifies that a certain numbered version +of the GNU Lesser General Public License "or any later version" +applies to it, you have the option of following the terms and +conditions either of that published version or of any later version +published by the Free Software Foundation. If the Library as you +received it does not specify a version number of the GNU Lesser +General Public License, you may choose any version of the GNU Lesser +General Public License ever published by the Free Software Foundation. + + If the Library as you received it specifies that a proxy can decide +whether future versions of the GNU Lesser General Public License shall +apply, that proxy's public statement of acceptance of any version is +permanent authorization for you to choose that version for the +Library. diff --git a/ThirdParty/DCS/Net/BSD.kqueue.pas b/ThirdParty/DCS/Net/BSD.kqueue.pas new file mode 100644 index 00000000..86813d46 --- /dev/null +++ b/ThirdParty/DCS/Net/BSD.kqueue.pas @@ -0,0 +1,114 @@ +{******************************************************************************} +{ } +{ Delphi cross platform socket library } +{ } +{ Copyright (c) 2017 WiNDDRiVER(soulawing@gmail.com) } +{ } +{ Homepage: https://github.com/winddriver/Delphi-Cross-Socket } +{ } +{******************************************************************************} +unit BSD.kqueue; + +interface + +uses + Posix.Base, Posix.Time; + +const + EVFILT_READ = -1; + EVFILT_WRITE = -2; + EVFILT_AIO = -3; { attached to aio requests } + EVFILT_VNODE = -4; { attached to vnodes } + EVFILT_PROC = -5; { attached to struct proc } + EVFILT_SIGNAL = -6; { attached to struct proc } + EVFILT_TIMER = -7; { timers } + EVFILT_NETDEV = -8; { network devices } + EVFILT_FS = -9; { filesystem events } + + EVFILT_SYSCOUNT = 9; + + EV_ADD = $0001; { add event to kq } + EV_DELETE = $0002; { delete event from kq } + EV_ENABLE = $0004; { enable event } + EV_DISABLE = $0008; { disable event (not reported) } + +{ flags } + EV_ONESHOT = $0010; { only report one occurrence } + EV_CLEAR = $0020; { clear event state after reporting } + EV_RECEIPT = $0040; { force EV_ERROR on success, data=0 } + EV_DISPATCH = $0080; { disable event after reporting } + EV_SYSFLAGS = $F000; { reserved by system } + EV_FLAG1 = $2000; { filter-specific flag } + +{ returned values } + EV_EOF = $8000; { EOF detected } + EV_ERROR = $4000; { error, data contains errno } + +{ data/hint flags for EVFILT_READ|WRITE, shared with userspace } + NOTE_LOWAT = $0001; { low water mark } + +{ data/hint flags for EVFILT_VNODE, shared with userspace } + NOTE_DELETE = $0001; { vnode was removed } + NOTE_WRITE = $0002; { data contents changed } + NOTE_EXTEND = $0004; { size increased } + NOTE_ATTRIB = $0008; { attributes changed } + NOTE_LINK = $0010; { link count changed } + NOTE_RENAME = $0020; { vnode was renamed } + NOTE_REVOKE = $0040; { vnode access was revoked } + +{ data/hint flags for EVFILT_PROC, shared with userspace } + NOTE_EXIT = $80000000; { process exited } + NOTE_FORK = $40000000; { process forked } + NOTE_EXEC = $20000000; { process exec'd } + NOTE_PCTRLMASK = $f0000000; { mask for hint bits } + NOTE_PDATAMASK = $000fffff; { mask for pid } + +{ additional flags for EVFILT_PROC } + NOTE_TRACK = $00000001; { follow across forks } + NOTE_TRACKERR = $00000002; { could not track child } + NOTE_CHILD = $00000004; { am a child process } + +{ data/hint flags for EVFILT_NETDEV, shared with userspace } + NOTE_LINKUP = $0001; { link is up } + NOTE_LINKDOWN = $0002; { link is down } + NOTE_LINKINV = $0004; { link state is invalid } + +type + PKEvent = ^TKEvent; + TKEvent = record + Ident : UIntPtr; { identifier for this event } + Filter : SmallInt; { filter for event } + Flags : Word; + FFlags : Cardinal; + Data : IntPtr; + uData : Pointer; { opaque user data identifier } + end; + +function kqueue: Integer; cdecl; + external libc name _PU + 'kqueue'; + {$EXTERNALSYM kqueue} + +function kevent(kq: Integer; ChangeList: PKEvent; nChanged: Integer; + EventList: PKevent; nEvents: Integer; Timeout: PTimeSpec): Integer; cdecl; + external libc name _PU + 'kevent'; + {$EXTERNALSYM kevent} + +procedure EV_SET(kevp: PKEvent; const aIdent: UIntPtr; const aFilter: SmallInt; + const aFlags: Word; const aFFlags: Cardinal; const aData: IntPtr; + const auData: Pointer); inline; + +implementation + +procedure EV_SET(kevp: PKEvent; const aIdent: UIntPtr; const aFilter: SmallInt; + const aFlags: Word; const aFFlags: Cardinal; const aData: IntPtr; + const auData: Pointer); inline; +begin + kevp^.Ident := aIdent; + kevp^.Filter := aFilter; + kevp^.Flags := aFlags; + kevp^.FFlags := aFFlags; + kevp^.Data := aData; + kevp^.uData := auData; +end; + +end. diff --git a/ThirdParty/DCS/Net/Linux.epoll.pas b/ThirdParty/DCS/Net/Linux.epoll.pas new file mode 100644 index 00000000..3ef74f62 --- /dev/null +++ b/ThirdParty/DCS/Net/Linux.epoll.pas @@ -0,0 +1,72 @@ +{******************************************************************************} +{ } +{ Delphi cross platform socket library } +{ } +{ Copyright (c) 2017 WiNDDRiVER(soulawing@gmail.com) } +{ } +{ Homepage: https://github.com/winddriver/Delphi-Cross-Socket } +{ } +{******************************************************************************} +unit Linux.epoll; + +interface + +uses + Posix.Base, Posix.Signal; + +const + EPOLLIN = $01; { The associated file is available for read(2) operations. } + EPOLLPRI = $02; { There is urgent data available for read(2) operations. } + EPOLLOUT = $04; { The associated file is available for write(2) operations. } + EPOLLERR = $08; { Error condition happened on the associated file descriptor. } + EPOLLHUP = $10; { Hang up happened on the associated file descriptor. } + EPOLLONESHOT = $40000000; { Sets the One-Shot behaviour for the associated file descriptor. } + EPOLLET = $80000000; { Sets the Edge Triggered behaviour for the associated file descriptor. } + + { Valid opcodes ( "op" parameter ) to issue to epoll_ctl } + EPOLL_CTL_ADD = 1; + EPOLL_CTL_DEL = 2; + EPOLL_CTL_MOD = 3; + +type + EPoll_Data = record + case integer of + 0: (ptr: pointer); + 1: (fd: Integer); + 2: (u32: Cardinal); + 3: (u64: UInt64); + end; + TEPoll_Data = Epoll_Data; + PEPoll_Data = ^Epoll_Data; + + EPoll_Event = {$IFDEF CPUX64}packed {$ENDIF}record + Events: Cardinal; + Data : TEpoll_Data; + end; + + TEPoll_Event = Epoll_Event; + PEpoll_Event = ^Epoll_Event; + +{ open an epoll file descriptor } +function epoll_create(size: Integer): Integer; cdecl; + external libc name _PU + 'epoll_create'; + {$EXTERNALSYM epoll_create} + +{ control interface for an epoll descriptor } +function epoll_ctl(epfd, op, fd: Integer; event: pepoll_event): Integer; cdecl; + external libc name _PU + 'epoll_ctl'; + {$EXTERNALSYM epoll_ctl} + +{ wait for an I/O event on an epoll file descriptor } +function epoll_wait(epfd: Integer; events: pepoll_event; maxevents, timeout: Integer): Integer; cdecl; + external libc name _PU + 'epoll_wait'; + {$EXTERNALSYM epoll_wait} + +{ create a file descriptor for event notification } +function eventfd(initval: Cardinal; flags: Integer): Integer; cdecl; + external libc name _PU + 'eventfd'; + {$EXTERNALSYM eventfd} + +implementation + +end. diff --git a/ThirdParty/DCS/Net/Net.CrossHttpMiddleware.pas b/ThirdParty/DCS/Net/Net.CrossHttpMiddleware.pas new file mode 100644 index 00000000..810d8398 --- /dev/null +++ b/ThirdParty/DCS/Net/Net.CrossHttpMiddleware.pas @@ -0,0 +1,222 @@ +{******************************************************************************} +{ } +{ Delphi cross platform socket library } +{ } +{ Copyright (c) 2017 WiNDDRiVER(soulawing@gmail.com) } +{ } +{ Homepage: https://github.com/winddriver/Delphi-Cross-Socket } +{ } +{******************************************************************************} +unit Net.CrossHttpMiddleware; + +interface + +uses + System.SysUtils, Net.CrossHttpServer; + +type + /// + /// HTTP֤ȡû + /// + TAuthGetPasswordProc = reference to procedure(ARequest: ICrossHttpRequest; const AUserName: string; var ACorrectPassword: string); + + /// + /// м + /// + /// + /// TCrossHttpServer.Use() + /// + TNetCrossMiddleware = class + public + /// + /// HTTP֤ + /// + /// + /// û֤ + /// + /// + /// + /// άٿ: HTTP֤ + /// + class function AuthenticateBasic(AAuthGetPasswordProc: TAuthGetPasswordProc; const ARealm: string = ''): TCrossHttpRouterProc2; static; + + /// + /// HTTPժҪ֤ + /// + /// + /// û֤ + /// + /// + /// + /// άٿ: HTTPժҪ֤ + /// + class function AuthenticateDigest(AAuthGetPasswordProc: TAuthGetPasswordProc; const ARealm: string = ''): TCrossHttpRouterProc2; static; + + /// + /// ԴԴ + /// + /// + /// + /// άٿ: ԴԴ + /// + class function CORS: TCrossHttpRouterProc2; static; + + /// + /// HTTPϸ䰲ȫ + /// + /// + /// + /// άٿ: HTTPϸ䰲ȫ + /// + class function HSTS: TCrossHttpRouterProc2; static; + end; + +implementation + +uses + System.Hash, System.NetEncoding, Utils.Utils, Net.CrossHttpParams; + +{ TNetCrossMiddleware } + +class function TNetCrossMiddleware.AuthenticateBasic(AAuthGetPasswordProc: TAuthGetPasswordProc; + const ARealm: string): TCrossHttpRouterProc2; +begin + Result := + procedure(ARequest: ICrossHttpRequest; AResponse: ICrossHttpResponse; var AHandled: Boolean) + var + LAuthStr: string; + LStrArr: TArray; + LCorrectPassword: string; + begin + // Authorization: Basic cm9vdDpyb290 + // base64ֽʽΪ "û:" + LAuthStr := ARequest.Header['Authorization']; + if (LAuthStr <> '') then + begin + if (LAuthStr.StartsWith('Basic')) then + LAuthStr := LAuthStr.Substring(6) + else + LAuthStr := ''; + end; + + LCorrectPassword := #0; + if (LAuthStr <> '') then + begin + LAuthStr := TNetEncoding.Base64.Decode(LAuthStr); + LStrArr := LAuthStr.Split([':']); + + // ȡûӦȷ + if Assigned(AAuthGetPasswordProc) and (Length(LStrArr) > 0) then + AAuthGetPasswordProc(ARequest, LStrArr[0], LCorrectPassword); + end; + + // ƥ + if (LAuthStr = '') or (Length(LStrArr) < 2) or (LStrArr[1] <> LCorrectPassword) then + begin + AHandled := True; + AResponse.Header['WWW-authenticate'] := Format('Basic Realm="%s"', [ARealm]); + AResponse.SendStatus(401); + Exit; + end; + + AHandled := False; + end; +end; + +class function TNetCrossMiddleware.AuthenticateDigest( + AAuthGetPasswordProc: TAuthGetPasswordProc; const ARealm: string): TCrossHttpRouterProc2; +begin + Result := + procedure(ARequest: ICrossHttpRequest; AResponse: ICrossHttpResponse; var AHandled: Boolean) + var + LUserName, LCorrectPassword: string; + LNonce, LUserResponse, LCorrectResponse: string; + LAuthStr: string; + A1, A2, HA1, HA2: string; + LAuthParams: TDelimitParams; + begin + // Authorization: Digest username="admin", realm="test realm", nonce="2468217498b46028705d401192459edd", uri="/login?key=value1", response="1d663058353e8f5831328728c29a6a1a", qop=auth, nc=00000006, cnonce="5d63a594e16feba2" + LAuthStr := ARequest.Header['Authorization']; + if (LAuthStr <> '') then + begin + if (LAuthStr.StartsWith('Digest')) then + LAuthStr := LAuthStr.Substring(7) + else + LAuthStr := ''; + end; + + LCorrectPassword := #0; + if (LAuthStr <> '') then + begin + LAuthParams := TDelimitParams.Create; + try + LAuthParams.Delimiter := ','; + LAuthParams.Decode(LAuthStr); + + LUserName := LAuthParams['username'].Replace('"', ''); + // ȡûӦȷ + if Assigned(AAuthGetPasswordProc) then + AAuthGetPasswordProc(ARequest, LUserName, LCorrectPassword); + + {$region 'ժҪ'} + A1 := Format('%s:%s:%s', [LUserName, ARealm, LCorrectPassword]); + A2 := Format('%s:%s', [ARequest.Method, LAuthParams['uri'].Replace('"', '')]); + + HA1 := TUtils.BytesToHex(THashMD5.GetHashBytes(A1)); + HA2 := TUtils.BytesToHex(THashMD5.GetHashBytes(A2)); + + LCorrectResponse := HA1 + + ':' + LAuthParams['nonce'].Replace('"', '') + + ':' + LAuthParams['nc'].Replace('"', '') + + ':' + LAuthParams['cnonce'].Replace('"', '') + + ':auth' + + ':' + HA2; + LCorrectResponse := TUtils.BytesToHex(THashMD5.GetHashBytes(LCorrectResponse)); + {$endregion} + + // ͻѼõժҪ + LUserResponse := LAuthParams['response'].Replace('"', ''); + finally + FreeAndNil(LAuthParams); + end; + end; + + // ȶԿͻ˵ժҪǷƥ + if (LAuthStr = '') or (LUserResponse <> LCorrectResponse) then + begin + AHandled := True; + LNonce := TUtils.BytesToHex(THashMD5.GetHashBytes(DateTimeToStr(Now))); + AResponse.Header['WWW-authenticate'] := Format( + 'Digest realm="%s", qop=auth, nonce="%s"', + [ARealm, LNonce]); + AResponse.SendStatus(401); + Exit; + end; + + AHandled := False; + end; +end; + +class function TNetCrossMiddleware.CORS: TCrossHttpRouterProc2; +begin + Result := + procedure(ARequest: ICrossHttpRequest; AResponse: ICrossHttpResponse; var AHandled: Boolean) + begin + AHandled := False; + AResponse.Header['Access-Control-Allow-Origin'] := '*'; + AResponse.Header['Access-Control-Allow-Methods'] := '*'; + AResponse.Header['Access-Control-Allow-Headers'] := '*'; + end; +end; + +class function TNetCrossMiddleware.HSTS: TCrossHttpRouterProc2; +begin + Result := + procedure(ARequest: ICrossHttpRequest; AResponse: ICrossHttpResponse; var AHandled: Boolean) + begin + AHandled := False; + AResponse.Header['Strict-Transport-Security'] := 'max-age=31536000; includeSubDomains'; + end; +end; + +end. diff --git a/ThirdParty/DCS/Net/Net.CrossHttpParams.pas b/ThirdParty/DCS/Net/Net.CrossHttpParams.pas new file mode 100644 index 00000000..42b03209 --- /dev/null +++ b/ThirdParty/DCS/Net/Net.CrossHttpParams.pas @@ -0,0 +1,2005 @@ +{******************************************************************************} +{ } +{ Delphi cross platform socket library } +{ } +{ Copyright (c) 2017 WiNDDRiVER(soulawing@gmail.com) } +{ } +{ Homepage: https://github.com/winddriver/Delphi-Cross-Socket } +{ } +{******************************************************************************} +unit Net.CrossHttpParams; + +interface + +uses + System.SysUtils, + System.Classes, + System.Generics.Collections, + System.Generics.Defaults, + System.NetEncoding, + System.IOUtils, + System.RegularExpressions, + System.SyncObjs, + System.Diagnostics, + System.DateUtils, + Net.CrossHttpUtils; + +type + TNameValue = record + Name, Value: string; + constructor Create(const AName, AValue: string); + end; + + /// + /// + /// + TBaseParams = class(TEnumerable) + private + FParams: TList; + + function GetParamIndex(const AName: string): Integer; + function GetParam(const AName: string): string; + procedure SetParam(const AName, AValue: string); + function GetCount: Integer; + function GetItem(AIndex: Integer): TNameValue; + procedure SetItem(AIndex: Integer; const AValue: TNameValue); + protected + function DoGetEnumerator: TEnumerator; override; + public type + TEnumerator = class(TEnumerator) + private + FList: TList; + FIndex: Integer; + protected + function DoGetCurrent: TNameValue; override; + function DoMoveNext: Boolean; override; + public + constructor Create(const AList: TList); + end; + public + constructor Create; overload; virtual; + constructor Create(const AEncodedParams: string); overload; virtual; + destructor Destroy; override; + + /// + /// Ӳ + /// + /// + /// + /// + /// + /// ֵ + /// + /// + /// Ƿ + /// + procedure Add(const AName, AValue: string; ADupAllowed: Boolean = False); overload; + + /// + /// ѱ + /// + /// + /// ѱַ + /// + procedure Add(const AEncodedParams: string); overload; + + /// + /// ɾָ + /// + /// + /// + /// + procedure Remove(const AName: string); overload; + + /// + /// ɾָ + /// + /// + /// + /// + procedure Remove(AIndex: Integer); overload; + + /// + /// в + /// + procedure Clear; + + /// + /// Բ + /// + procedure Sort(const AComparison: TComparison = nil); + + /// + /// ѱַн + /// + /// + /// ѱַ + /// + /// + /// Ƿ + /// + procedure Decode(const AEncodedParams: string; AClear: Boolean = True); virtual; abstract; + + /// + /// Ϊַ + /// + function Encode: string; virtual; abstract; + + /// + /// ȡֵ + /// + function GetParamValue(const AName: string; out AValue: string): Boolean; + + /// + /// Ʒʲ + /// + property Params[const AName: string]: string read GetParam write SetParam; default; + + /// + /// ŷʲ + /// + property Items[AIndex: Integer]: TNameValue read GetItem write SetItem; + + /// + /// + /// + property Count: Integer read GetCount; + end; + + /// + /// Url + /// + THttpUrlParams = class(TBaseParams) + private + FEncodeName: Boolean; + FEncodeValue: Boolean; + public + constructor Create; override; + + /// + /// ѱַн + /// + /// + /// ѱַ + /// + /// + /// Ƿ + /// + procedure Decode(const AEncodedParams: string; AClear: Boolean = True); override; + + /// + /// Ϊַ + /// + function Encode: string; override; + + /// + /// Ƿ + /// + property EncodeName: Boolean read FEncodeName write FEncodeName; + + /// + /// Ƿ + /// + property EncodeValue: Boolean read FEncodeValue write FEncodeValue; + end; + + /// + /// HTTPͷ + /// + THttpHeader = class(TBaseParams) + public + /// + /// ѱַн + /// + /// + /// ѱַ + /// + /// + /// Ƿ + /// + procedure Decode(const AEncodedParams: string; AClear: Boolean = True); override; + + /// + /// Ϊַ + /// + function Encode: string; override; + end; + + /// + /// ָIJ + /// + TDelimitParams = class(TBaseParams) + private + FDelimiter: Char; + public + /// + /// ѱַн + /// + /// + /// ѱַ + /// + /// + /// Ƿ + /// + procedure Decode(const AEncodedParams: string; AClear: Boolean = True); override; + + /// + /// Ϊַ + /// + function Encode: string; override; + + /// + /// ַָ + /// + property Delimiter: Char read FDelimiter write FDelimiter; + end; + + /// + /// ͻͷеCookies + /// + TRequestCookies = class(TBaseParams) + public + /// + /// ѱַн + /// + /// + /// ѱַ + /// + /// + /// Ƿ + /// + procedure Decode(const AEncodedParams: string; AClear: Boolean = True); override; + + /// + /// Ϊַ + /// + function Encode: string; override; + end; + + TResponseCookie = record + /// + /// Cookie + /// + Name: string; + + /// + /// Cookie + /// + Value: string; + + /// + /// CookieЧ, Ϊ0رպCookieʧЧ + /// + MaxAge: Integer; + + /// + /// + /// + /// + /// CookieЧ, ֻе·ͬʱʱ, ŻὫCookie͸Server. + /// ûDomainPathĻ, ǻᱻĬΪǰҳӦֵ + /// + Domain: string; + + /// + /// · + /// + /// + /// CookieЧ, ֻе·ͬʱʱ, ŻὫCookie͸Server. + /// ûDomainPathĻ, ǻᱻĬΪǰҳӦֵ + /// + Path: string; + + /// + /// Ƿ HttpOnly + /// + /// + /// HttpOnlyֶθ, ֻHTTPЭʹ, Ľűɼ, ԿվűʱҲᱻȡ + /// + HttpOnly: Boolean; + + /// + /// ǷSecure + /// + /// + /// Secureֶθhttpsͨʱ, Cookieаȫ, ʱкڿͼҲ޷ȡcookie + /// + Secure: Boolean; + + constructor Create(const AName, AValue: string; AMaxAge: Integer; + const APath: string = ''; const ADomain: string = ''; + AHttpOnly: Boolean = False; ASecure: Boolean = False); + + function Encode: string; + end; + + /// + /// Cookie + /// + TResponseCookies = class(TList) + private + function GetCookieIndex(const AName: string): Integer; + function GetCookie(const AName: string): TResponseCookie; + procedure SetCookie(const AName: string; const Value: TResponseCookie); + public + procedure AddOrSet(const AName, AValue: string; AMaxAge: Integer; + const APath: string = ''; const ADomain: string = ''; + AHttpOnly: Boolean = False; ASecure: Boolean = False); + procedure Remove(const AName: string); + + property Cookies[const AName: string]: TResponseCookie read GetCookie write SetCookie; + end; + + TFormField = class + private + FName: string; + FValue: TStream; + FFileName: string; + FFilePath: string; + FContentType: string; + FContentTransferEncoding: string; + public + constructor Create; + destructor Destroy; override; + + /// + /// תΪֽ + /// + function AsBytes: TBytes; + + /// + /// תΪַ + /// + /// + /// ַ + /// + function AsString(AEncoding: TEncoding = nil): string; + + /// + /// ͷ + /// + procedure FreeValue; + + /// + /// + /// + property Name: string read FName; + + /// + /// ԭʼ + /// + property Value: TStream read FValue; + + /// + /// ļֻļиԣ + /// + property FileName: string read FFileName; + + /// + /// ļ·ֻļиԣ + /// + property FilePath: string read FFilePath; + + /// + /// ֻͣļиԣ + /// + property ContentType: string read FContentType; + property ContentTransferEncoding: string read FContentTransferEncoding; + end; + + /// + /// MultiPartFormData + /// + THttpMultiPartFormData = class(TEnumerable) + public type + TDecodeState = (dsBoundary, dsDetect, dsPartHeader, dsPartData); + private const + DETECT_HEADER_BYTES: array [0..1] of Byte = (13, 10); // س + DETECT_END_BYTES: array [0..3] of Byte = (45, 45, 13, 10); // --س + MAX_PART_HEADER: Integer = 64 * 1024; + private + FBoundary, FStoragePath: string; + FBoundaryBytes, FLookbehind: TBytes; + FBoundaryIndex, FDetectHeaderIndex, FDetectEndIndex, FPartDataBegin: Integer; + FPrevIndex: Integer; + FDecodeState: TDecodeState; + CR, LF: Integer; + FPartFields: TObjectList; + FCurrentPartHeader: TBytesStream; + FCurrentPartField: TFormField; + FAutoDeleteFiles: Boolean; + + function GetItemIndex(const AName: string): Integer; + function GetItem(AIndex: Integer): TFormField; + function GetCount: Integer; + function GetDataSize: Integer; + function GetField(const AName: string): TFormField; + protected + function DoGetEnumerator: TEnumerator; override; + public type + TEnumerator = class(TEnumerator) + private + FList: TList; + FIndex: Integer; + protected + function DoGetCurrent: TFormField; override; + function DoMoveNext: Boolean; override; + public + constructor Create(const AList: TList); + end; + public + constructor Create; virtual; + destructor Destroy; override; + + /// + /// ʼBoundary(Decode֮ǰ) + /// + procedure InitWithBoundary(const ABoundary: string); + + /// + /// ڴн(ȵInitWithBoundary) + /// + /// + /// + /// + /// + /// ݳ + /// + function Decode(const ABuf: Pointer; ALen: Integer): Integer; + + /// + /// Items + /// + procedure Clear; + + /// + /// Boundaryַ(ֻ) + /// + property Boundary: string read FBoundary; + + /// + /// ϴļ· + /// + property StoragePath: string read FStoragePath write FStoragePath; + + /// + /// ŷʲ + /// + property Items[AIndex: Integer]: TFormField read GetItem; + + /// + /// Ʒʲ + /// + property Fields[const AName: string]: TFormField read GetField; + + /// + /// Items(ֻ) + /// + property Count: Integer read GetCount; + + /// + /// Itemsݵܳߴ(ֽ) + /// + property DataSize: Integer read GetDataSize; + + /// + /// ͷʱԶɾϴļ + /// + property AutoDeleteFiles: Boolean read FAutoDeleteFiles write FAutoDeleteFiles; + end; + + /// + /// SessionԱӿ + /// + ISession = interface + ['{A3D525A1-C534-4CE6-969B-53C5B8CB77C3}'] + function GetSessionID: string; + function GetCreateTime: TDateTime; + function GetLastAccessTime: TDateTime; + function GetExpiryTime: Integer; + function GetValue(const AName: string): string; + procedure SetSessionID(const ASessionID: string); + procedure SetCreateTime(const ACreateTime: TDateTime); + procedure SetLastAccessTime(const ALastAccessTime: TDateTime); + procedure SetExpiryTime(const Value: Integer); + procedure SetValue(const AName, AValue: string); + + /// + /// ʱ + /// + procedure Touch; + + /// + /// Ƿѹ + /// + function Expired: Boolean; + + /// + /// Session ID + /// + property SessionID: string read GetSessionID write SetSessionID; + + /// + /// ʱ + /// + property CreateTime: TDateTime read GetCreateTime write SetCreateTime; + + /// + /// ʱ + /// + property LastAccessTime: TDateTime read GetLastAccessTime write SetLastAccessTime; + + /// + /// Sessionʱ() + /// + /// + /// + /// + /// ֵ0ʱ, Session趨ֵûʹþͻᱻͷ; + /// + /// + /// ֵСڵ0ʱ, SessionɺһֱЧ + /// + /// + /// + property ExpiryTime: Integer read GetExpiryTime write SetExpiryTime; + + /// + /// SessionһKEY-VALUEṹ, ڷеijԱֵ + /// + property Values[const AName: string]: string read GetValue write SetValue; default; + end; + + TSessionBase = class abstract(TInterfacedObject, ISession) + protected + function GetSessionID: string; virtual; abstract; + function GetCreateTime: TDateTime; virtual; abstract; + function GetLastAccessTime: TDateTime; virtual; abstract; + function GetExpiryTime: Integer; virtual; abstract; + function GetValue(const AName: string): string; virtual; abstract; + procedure SetSessionID(const ASessionID: string); virtual; abstract; + procedure SetCreateTime(const ACreateTime: TDateTime); virtual; abstract; + procedure SetLastAccessTime(const ALastAccessTime: TDateTime); virtual; abstract; + procedure SetExpiryTime(const Value: Integer); virtual; abstract; + procedure SetValue(const AName, AValue: string); virtual; abstract; + public + constructor Create(const ASessionID: string); virtual; + + procedure Touch; virtual; + function Expired: Boolean; virtual; + + property SessionID: string read GetSessionID write SetSessionID; + property CreateTime: TDateTime read GetCreateTime write SetCreateTime; + property LastAccessTime: TDateTime read GetLastAccessTime write SetLastAccessTime; + property ExpiryTime: Integer read GetExpiryTime write SetExpiryTime; + property Values[const AName: string]: string read GetValue write SetValue; default; + end; + + TSession = class(TSessionBase) + protected + FSessionID: string; + FCreateTime: TDateTime; + FLastAccessTime: TDateTime; + FExpire: Integer; + FValues: TDictionary; + + function GetSessionID: string; override; + function GetCreateTime: TDateTime; override; + function GetLastAccessTime: TDateTime; override; + function GetExpiryTime: Integer; override; + function GetValue(const AName: string): string; override; + procedure SetSessionID(const ASessionID: string); override; + procedure SetCreateTime(const ACreateTime: TDateTime); override; + procedure SetLastAccessTime(const ALastAccessTime: TDateTime); override; + procedure SetExpiryTime(const AValue: Integer); override; + procedure SetValue(const AName, AValue: string); override; + public + constructor Create(const ASessionID: string); override; + destructor Destroy; override; + + property SessionID: string read GetSessionID write SetSessionID; + property CreateTime: TDateTime read GetCreateTime write SetCreateTime; + property LastAccessTime: TDateTime read GetLastAccessTime write SetLastAccessTime; + property Values[const AName: string]: string read GetValue write SetValue; default; + end; + + TSessionClass = class of TSessionBase; + + /// + /// Sessionӿ + /// + ISessions = interface + ['{5187CA76-4CC4-4986-B67B-BC3E76D6CD74}'] + function GetEnumerator: TEnumerator; + + function GetSessionClass: TSessionClass; + function GetCount: Integer; + function GetItem(const AIndex: Integer): ISession; + function GetSession(const ASessionID: string): ISession; + function GetExpiryTime: Integer; + procedure SetSessionClass(const Value: TSessionClass); + procedure SetExpiryTime(const Value: Integer); + + /// + /// ʼд(߳ͬ) + /// + procedure BeginWrite; + + /// + /// д(߳ͬ) + /// + procedure EndWrite; + + /// + /// ʼ(߳ͬ) + /// + procedure BeginRead; + + /// + /// (߳ͬ) + /// + procedure EndRead; + + /// + /// Session ID + /// + function NewSessionID: string; + + /// + /// ǷָIDSession + /// + /// + /// Session ID + /// + /// + /// ָSession ʵ浽ò + /// + function ExistsSession(const ASessionID: string; var ASession: ISession): Boolean; overload; + + /// + /// ǷָIDSession + /// + /// + /// Session ID + /// + function ExistsSession(const ASessionID: string): Boolean; overload; + + /// + /// Session + /// + /// + /// Session ID + /// + /// + /// Sessionʵ + /// + function AddSession(const ASessionID: string): ISession; overload; + + /// + /// Session + /// + /// + /// Sessionʵ + /// + function AddSession: ISession; overload; + + /// + /// Session + /// + /// + /// Session ID + /// + /// + /// Sessionʵ + /// + procedure AddSession(const ASessionID: string; ASession: ISession); overload; + + /// + /// ɾSession + /// + /// + /// Session ID + /// + procedure RemoveSession(const ASessionID: string); + + /// + /// Session + /// + property SessionClass: TSessionClass read GetSessionClass write SetSessionClass; + + /// + /// Session + /// + property Count: Integer read GetCount; + + /// + /// ȡָŵSession, 򷵻nil + /// + property Items[const AIndex: Integer]: ISession read GetItem; + + /// + /// ȡָIDSession, ½һ + /// + /// + /// Session ID + /// + property Sessions[const ASessionID: string]: ISession read GetSession; default; + + /// + /// Sessionʱ() + /// + /// + /// + /// + /// ֵ0ʱ, Session趨ֵûʹþͻᱻͷ; + /// + /// + /// ֵСڵ0ʱ, SessionɺһֱЧ + /// + /// + /// + property ExpiryTime: Integer read GetExpiryTime write SetExpiryTime; + end; + + TSessionsBase = class abstract(TInterfacedObject, ISessions) + protected + function GetSessionClass: TSessionClass; virtual; abstract; + function GetCount: Integer; virtual; abstract; + function GetItem(const AIndex: Integer): ISession; virtual; abstract; + function GetSession(const ASessionID: string): ISession; virtual; abstract; + function GetExpiryTime: Integer; virtual; abstract; + procedure SetSessionClass(const Value: TSessionClass); virtual; abstract; + procedure SetExpiryTime(const Value: Integer); virtual; abstract; + public + function GetEnumerator: TEnumerator; virtual; abstract; + + procedure BeginWrite; virtual; abstract; + procedure EndWrite; virtual; abstract; + + procedure BeginRead; virtual; abstract; + procedure EndRead; virtual; abstract; + + function NewSessionID: string; virtual; abstract; + function ExistsSession(const ASessionID: string; var ASession: ISession): Boolean; overload; virtual; abstract; + function ExistsSession(const ASessionID: string): Boolean; overload; virtual; + function AddSession(const ASessionID: string): ISession; overload; virtual; + function AddSession: ISession; overload; virtual; + procedure AddSession(const ASessionID: string; ASession: ISession); overload; virtual; abstract; + procedure RemoveSession(const ASessionID: string); virtual; abstract; + + property SessionClass: TSessionClass read GetSessionClass write SetSessionClass; + property Count: Integer read GetCount; + property Items[const AIndex: Integer]: ISession read GetItem; + property Sessions[const ASessionID: string]: ISession read GetSession; default; + property ExpiryTime: Integer read GetExpiryTime write SetExpiryTime; + end; + + TSessions = class(TSessionsBase) + private + FSessions: TDictionary; + FNewGUIDFunc: TFunc; + FLocker: TMultiReadExclusiveWriteSynchronizer; + FSessionClass: TSessionClass; + FExpire: Integer; + FShutdown, FExpiredProcRunning: Boolean; + protected + function GetSessionClass: TSessionClass; override; + function GetCount: Integer; override; + function GetItem(const AIndex: Integer): ISession; override; + function GetSession(const ASessionID: string): ISession; override; + function GetExpiryTime: Integer; override; + procedure SetSessionClass(const Value: TSessionClass); override; + procedure SetExpiryTime(const Value: Integer); override; + + procedure CreateExpiredProcThread; + public + constructor Create(ANewGUIDFunc: TFunc); overload; virtual; + constructor Create; overload; virtual; + destructor Destroy; override; + + function GetEnumerator: TEnumerator; override; + + procedure BeginWrite; override; + procedure EndWrite; override; + + procedure BeginRead; override; + procedure EndRead; override; + + function NewSessionID: string; override; + function ExistsSession(const ASessionID: string; var ASession: ISession): Boolean; override; + procedure AddSession(const ASessionID: string; ASession: ISession); override; + procedure RemoveSession(const ASessionID: string); override; + + property NewGUIDFunc: TFunc read FNewGUIDFunc write FNewGUIDFunc; + end; + +implementation + +uses + Utils.Utils, + Utils.DateTime; + +{ TNameValue } + +constructor TNameValue.Create(const AName, + AValue: string); +begin + Name := AName; + Value := AValue; +end; + +{ TBaseParams.TEnumerator } + +constructor TBaseParams.TEnumerator.Create(const AList: TList); +begin + inherited Create; + FList := AList; + FIndex := -1; +end; + +function TBaseParams.TEnumerator.DoGetCurrent: TNameValue; +begin + Result := FList[FIndex]; +end; + +function TBaseParams.TEnumerator.DoMoveNext: Boolean; +begin + if (FIndex >= FList.Count) then + Exit(False); + Inc(FIndex); + Result := (FIndex < FList.Count); +end; + +{ TBaseParams } + +constructor TBaseParams.Create; +begin + FParams := TList.Create(TComparer.Construct( + function(const Left, Right: TNameValue): Integer + begin + Result := CompareText(Left.Name, Right.Name, TLocaleOptions.loUserLocale); + end)); +end; + +constructor TBaseParams.Create(const AEncodedParams: string); +begin + Create; + Decode(AEncodedParams, True); +end; + +destructor TBaseParams.Destroy; +begin + FreeAndNil(FParams); + inherited; +end; + +procedure TBaseParams.Add(const AName, AValue: string; ADupAllowed: Boolean); +begin + if ADupAllowed then + FParams.Add(TNameValue.Create(AName, AValue)) + else + SetParam(AName, AValue); +end; + +procedure TBaseParams.Add(const AEncodedParams: string); +begin + Decode(AEncodedParams, False); +end; + +procedure TBaseParams.Clear; +begin + FParams.Clear; +end; + +function TBaseParams.GetParamIndex(const AName: string): Integer; +var + I: Integer; +begin + for I := 0 to FParams.Count - 1 do + if SameText(FParams[I].Name, AName) then Exit(I); + Result := -1; +end; + +function TBaseParams.GetParamValue(const AName: string; + out AValue: string): Boolean; +var + I: Integer; +begin + I := GetParamIndex(AName); + if (I >= 0) then + begin + AValue := FParams[I].Value; + Exit(True); + end; + + Result := False; +end; + +procedure TBaseParams.Remove(const AName: string); +var + I: Integer; +begin + I := GetParamIndex(AName); + if (I >= 0) then + FParams.Delete(I); +end; + +procedure TBaseParams.Remove(AIndex: Integer); +begin + FParams.Delete(AIndex); +end; + +function TBaseParams.GetCount: Integer; +begin + Result := FParams.Count; +end; + +function TBaseParams.GetItem(AIndex: Integer): TNameValue; +begin + Result := FParams.Items[AIndex]; +end; + +function TBaseParams.DoGetEnumerator: TEnumerator; +begin + Result := TEnumerator.Create(FParams); +end; + +function TBaseParams.GetParam(const AName: string): string; +var + I: Integer; +begin + I := GetParamIndex(AName); + if (I >= 0) then + Exit(FParams[I].Value); + Result := ''; +end; + +procedure TBaseParams.SetItem(AIndex: Integer; const AValue: TNameValue); +begin + FParams[AIndex] := AValue; +end; + +procedure TBaseParams.SetParam(const AName, AValue: string); +var + I: Integer; + LItem: TNameValue; +begin + I := GetParamIndex(AName); + if (I >= 0) then + begin + LItem := FParams[I]; + LItem.Value := AValue; + FParams[I] := LItem; + end else + FParams.Add(TNameValue.Create(AName, AValue)); +end; + +procedure TBaseParams.Sort(const AComparison: TComparison); +begin + if Assigned(AComparison) then + FParams.Sort(TComparer.Construct(AComparison)) + else + FParams.Sort(TComparer.Construct( + function(const Left, Right: TNameValue): Integer + begin + Result := CompareStr(Left.Name, Right.Name, TLocaleOptions.loInvariantLocale); + end)); +end; + +{ THttpUrlParams } + +constructor THttpUrlParams.Create; +begin + inherited Create; + + FEncodeName := False; + FEncodeValue := True; +end; + +procedure THttpUrlParams.Decode(const AEncodedParams: string; AClear: Boolean); +var + p, q: PChar; + LName, LValue: string; + LSize: Integer; +begin + if AClear then + FParams.Clear; + + p := PChar(AEncodedParams); + while (p^ <> #0) do + begin + q := p; + LSize := 0; + while (p^ <> #0) and (p^ <> '=') do + begin + Inc(LSize); + Inc(p); + end; + SetLength(LName, LSize); + Move(q^, Pointer(LName)^, LSize * SizeOf(Char)); + LName := TNetEncoding.URL.Decode(LName); + // '=' + while (p^ <> #0) and (p^ = '=') do + Inc(p); + + q := p; + LSize := 0; + while (p^ <> #0) and (p^ <> '&') do + begin + Inc(LSize); + Inc(p); + end; + SetLength(LValue, LSize); + Move(q^, Pointer(LValue)^, LSize * SizeOf(Char)); + LValue := TNetEncoding.URL.Decode(LValue); + // '&' + while (p^ <> #0) and (p^ = '&') do + Inc(p); + + Add(LName, LValue); + end; +end; + +function THttpUrlParams.Encode: string; +var + I: Integer; +begin + Result := ''; + for I := 0 to FParams.Count - 1 do + begin + if (I > 0) then + Result := Result + '&'; + if FEncodeName then + Result := Result + TNetEncoding.URL.Encode(FParams[I].Name) + else + Result := Result + FParams[I].Name; + if FEncodeValue then + Result := Result + '=' + TNetEncoding.URL.Encode(FParams[I].Value) + else + Result := Result + '=' + FParams[I].Value; + end; +end; + +{ THttpHeader } + +procedure THttpHeader.Decode(const AEncodedParams: string; AClear: Boolean); +var + p, q: PChar; + LName, LValue: string; + LSize: Integer; +begin + if AClear then + FParams.Clear; + + p := PChar(AEncodedParams); + while (p^ <> #0) do + begin + q := p; + LSize := 0; + while (p^ <> #0) and (p^ <> ':') do + begin + Inc(LSize); + Inc(p); + end; + SetLength(LName, LSize); + Move(q^, Pointer(LName)^, LSize * SizeOf(Char)); + // ':' + while (p^ <> #0) and ((p^ = ':') or (p^ = ' ')) do + Inc(p); + + q := p; + LSize := 0; + while (p^ <> #0) and (p^ <> #13) do + begin + Inc(LSize); + Inc(p); + end; + SetLength(LValue, LSize); + Move(q^, Pointer(LValue)^, LSize * SizeOf(Char)); + // #13#10 + while (p^ <> #0) and ((p^ = #13) or (p^ = #10)) do + Inc(p); + + Add(LName, LValue); + end; +end; + +function THttpHeader.Encode: string; +var + I: Integer; +begin + Result := ''; + for I := 0 to FParams.Count - 1 do + begin + Result := Result + FParams[I].Name; + Result := Result + ': ' + FParams[I].Value + #13#10; + end; + Result := Result + #13#10; +end; + +{ TDelimitParams } + +procedure TDelimitParams.Decode(const AEncodedParams: string; AClear: Boolean); +var + p, q: PChar; + LName, LValue: string; + LSize: Integer; +begin + if AClear then + FParams.Clear; + + p := PChar(AEncodedParams); + while (p^ <> #0) do + begin + q := p; + LSize := 0; + while (p^ <> #0) and (p^ <> '=') do + begin + Inc(LSize); + Inc(p); + end; + SetLength(LName, LSize); + Move(q^, Pointer(LName)^, LSize * SizeOf(Char)); + // '=' + while (p^ <> #0) and (p^ = '=') do + Inc(p); + + q := p; + LSize := 0; + while (p^ <> #0) and (p^ <> FDelimiter) do + begin + Inc(LSize); + Inc(p); + end; + SetLength(LValue, LSize); + Move(q^, Pointer(LValue)^, LSize * SizeOf(Char)); + LValue := TNetEncoding.URL.Decode(LValue); + // ';' + while (p^ <> #0) and ((p^ = FDelimiter) or (p^ = ' ')) do + Inc(p); + + Add(LName, LValue); + end; +end; + +function TDelimitParams.Encode: string; +var + I: Integer; +begin + Result := ''; + for I := 0 to FParams.Count - 1 do + begin + if (I > 0) then + Result := Result + FDelimiter + ' '; + Result := Result + FParams[I].Name + '=' + TNetEncoding.URL.Encode(FParams[I].Value); + end; +end; + +{ TRequestCookies } + +procedure TRequestCookies.Decode(const AEncodedParams: string; AClear: Boolean); +var + p, q: PChar; + LName, LValue: string; + LSize: Integer; +begin + if AClear then + FParams.Clear; + + p := PChar(AEncodedParams); + while (p^ <> #0) do + begin + q := p; + LSize := 0; + while (p^ <> #0) and (p^ <> '=') do + begin + Inc(LSize); + Inc(p); + end; + SetLength(LName, LSize); + Move(q^, Pointer(LName)^, LSize * SizeOf(Char)); + // '=' + while (p^ <> #0) and (p^ = '=') do + Inc(p); + + q := p; + LSize := 0; + while (p^ <> #0) and (p^ <> ';') do + begin + Inc(LSize); + Inc(p); + end; + SetLength(LValue, LSize); + Move(q^, Pointer(LValue)^, LSize * SizeOf(Char)); + LValue := TNetEncoding.URL.Decode(LValue); + // ';' + while (p^ <> #0) and ((p^ = ';') or (p^ = ' ')) do + Inc(p); + + Add(LName, LValue); + end; +end; + +function TRequestCookies.Encode: string; +var + I: Integer; +begin + Result := ''; + for I := 0 to FParams.Count - 1 do + begin + if (I > 0) then + Result := Result + '; '; + Result := Result + FParams[I].Name + '=' + TNetEncoding.URL.Encode(FParams[I].Value); + end; +end; + +{ TResponseCookie } + +constructor TResponseCookie.Create(const AName, AValue: string; + AMaxAge: Integer; const APath, ADomain: string; AHttpOnly, ASecure: Boolean); +begin + Name := AName; + Value := AValue; + MaxAge := AMaxAge; + Path := APath; + Domain := ADomain; + HttpOnly := AHttpOnly; + Secure := ASecure; +end; + +function TResponseCookie.Encode: string; +begin + Result := Name + '=' + TNetEncoding.URL.Encode(Value); + + if (MaxAge > 0) then + begin + Result := Result + '; Max-Age=' + MaxAge.ToString; + Result := Result + '; Expires=' + TCrossHttpUtils.RFC1123_DateToStr(Now.AddSeconds(MaxAge)); + end; + if (Path <> '') then + Result := Result + '; Path=' + Path; + if (Domain <> '') then + Result := Result + '; Domain=' + Domain; + if HttpOnly then + Result := Result + '; HttpOnly'; + if Secure then + Result := Result + '; Secure'; +end; + +{ TFormField } + +constructor TFormField.Create; +begin +end; + +destructor TFormField.Destroy; +begin + FreeValue; + + inherited; +end; + +procedure TFormField.FreeValue; +begin + if Assigned(FValue) then + FreeAndNil(FValue); +end; + +function TFormField.AsBytes: TBytes; +var + LBytesStream: TBytesStream; +begin + if (FValue = nil) or (FValue.Size <= 0) then Exit(nil); + + if (FValue is TBytesStream) then + begin + Result := TBytesStream(FValue).Bytes; + SetLength(Result, FValue.Size); + end else + begin + LBytesStream := TBytesStream.Create; + try + LBytesStream.CopyFrom(FValue, 0); + Result := LBytesStream.Bytes; + SetLength(Result, LBytesStream.Size); + finally + FreeAndNil(LBytesStream); + end; + end; +end; + +function TFormField.AsString(AEncoding: TEncoding): string; +begin + if (AEncoding = nil) then + AEncoding := TEncoding.UTF8; + + Result := AEncoding.GetString(AsBytes); +end; + +{ THttpMultiPartFormData.TEnumerator } + +constructor THttpMultiPartFormData.TEnumerator.Create( + const AList: TList); +begin + inherited Create; + FList := AList; + FIndex := -1; +end; + +function THttpMultiPartFormData.TEnumerator.DoGetCurrent: TFormField; +begin + Result := FList[FIndex]; +end; + +function THttpMultiPartFormData.TEnumerator.DoMoveNext: Boolean; +begin + if (FIndex >= FList.Count) then + Exit(False); + Inc(FIndex); + Result := (FIndex < FList.Count); +end; + +{ THttpMultiPartFormData } + +constructor THttpMultiPartFormData.Create; +begin + FDecodeState := dsBoundary; + FCurrentPartHeader := TBytesStream.Create(nil); + FPartFields := TObjectList.Create(True); +end; + +destructor THttpMultiPartFormData.Destroy; +begin + Clear; + FreeAndNil(FCurrentPartHeader); + FreeAndNil(FPartFields); + inherited; +end; + +procedure THttpMultiPartFormData.Clear; +var + LField: TFormField; +begin + for LField in FPartFields do + begin + if FAutoDeleteFiles and TFile.Exists(LField.FilePath) then + begin + LField.FreeValue; + TFile.Delete(LField.FilePath); + end; + end; + + FPartFields.Clear; +end; + +function THttpMultiPartFormData.DoGetEnumerator: TEnumerator; +begin + Result := TEnumerator.Create(FPartFields); +end; + +function THttpMultiPartFormData.GetItem(AIndex: Integer): TFormField; +begin + Result := FPartFields.Items[AIndex]; +end; + +function THttpMultiPartFormData.GetItemIndex(const AName: string): Integer; +var + I: Integer; +begin + for I := 0 to FPartFields.Count - 1 do + if SameText(FPartFields[I].Name, AName) then Exit(I); + Result := -1; +end; + +function THttpMultiPartFormData.GetCount: Integer; +begin + Result := FPartFields.Count; +end; + +function THttpMultiPartFormData.GetDataSize: Integer; +var + LPartField: TFormField; +begin + Result := 0; + for LPartField in FPartFields do + Inc(Result, LPartField.FValue.Size); +end; + +function THttpMultiPartFormData.GetField(const AName: string): TFormField; +var + I: Integer; +begin + I := GetItemIndex(AName); + if (I >= 0) then + Exit(FPartFields[I]); + Result := nil; +end; + +procedure THttpMultiPartFormData.InitWithBoundary(const ABoundary: string); +begin + Clear; + FBoundary := ABoundary; + FBoundaryBytes := TEncoding.ANSI.GetBytes(#13#10'--' + FBoundary); + FDecodeState := dsBoundary; + FBoundaryIndex := 0; + FCurrentPartHeader.Clear; + SetLength(FLookbehind, Length(FBoundaryBytes) + 8); +end; + +function THttpMultiPartFormData.Decode(const ABuf: Pointer; ALen: Integer): Integer; + function __NewFileID: string; + begin + Result := TUtils.GetGUID.ToLower; + end; + + procedure __InitFormFieldByHeader(AFormField: TFormField; const AHeader: string); + var + LFieldHeader: THttpHeader; + LContentDisposition: string; + LMatch: TMatch; + begin + LFieldHeader := THttpHeader.Create; + try + LFieldHeader.Decode(AHeader); + LContentDisposition := LFieldHeader['Content-Disposition']; + if (LContentDisposition = '') then Exit; + + LMatch := TRegEx.Match(LContentDisposition, '\bname="(.*?)"(?=;|$)', [TRegExOption.roIgnoreCase]); + if LMatch.Success then + AFormField.FName := LMatch.Groups[1].Value; + + LMatch := TRegEx.Match(LContentDisposition, '\bfilename="(.*?)"(?=;|$)', [TRegExOption.roIgnoreCase]); + if LMatch.Success then + begin + AFormField.FFileName := LMatch.Groups[1].Value; + AFormField.FFilePath := TPath.Combine(FStoragePath, + __NewFileID + TPath.GetExtension(AFormField.FFileName)); + if TFile.Exists(AFormField.FFilePath) then + TFile.Delete(AFormField.FFilePath); + AFormField.FValue := TFile.Open(AFormField.FFilePath, TFileMode.fmOpenOrCreate, TFileAccess.faReadWrite, TFileShare.fsRead); + end else + AFormField.FValue := TBytesStream.Create(nil); + + AFormField.FContentType := LFieldHeader['Content-Type']; + AFormField.FContentTransferEncoding := LFieldHeader['Content-Transfer-Encoding']; + finally + FreeAndNil(LFieldHeader); + end; + end; +var + C: Byte; + I: Integer; + P: PByteArray; + LPartHeader: string; +begin + if (FBoundaryBytes = nil) then Exit(0); + + P := ABuf; + I := 0; + while (I < ALen) do + begin + C := P[I]; + case FDecodeState of + // Boundary, ȷһ + dsBoundary: + begin + if (C = FBoundaryBytes[2 + FBoundaryIndex]) then + Inc(FBoundaryIndex) + else + FBoundaryIndex := 0; + // --Boundary + if (2 + FBoundaryIndex >= Length(FBoundaryBytes)) then + begin + FDecodeState := dsDetect; + CR := 0; + LF := 0; + FBoundaryIndex := 0; + FDetectHeaderIndex := 0; + FDetectEndIndex := 0; + end; + end; + + // ͨBoundary, ȷݻѵ + dsDetect: + begin + if (C = DETECT_HEADER_BYTES[FDetectHeaderIndex]) then + Inc(FDetectHeaderIndex) + else + FDetectHeaderIndex := 0; + + if (C = DETECT_END_BYTES[FDetectEndIndex]) then + Inc(FDetectEndIndex) + else + FDetectEndIndex := 0; + + // Ƿ + if (FDetectHeaderIndex = 0) and (FDetectEndIndex = 0) then Exit(I); + + // ⵽־ + // --Boundary--#13#10 + if (FDetectEndIndex >= Length(DETECT_END_BYTES)) then + begin + FDecodeState := dsBoundary; + CR := 0; + LF := 0; + FBoundaryIndex := 0; + FDetectEndIndex := 0; + end else + // 滹 + // --Boundary#13#10 + if (FDetectHeaderIndex >= Length(DETECT_HEADER_BYTES)) then + begin + FCurrentPartHeader.Clear; + FDecodeState := dsPartHeader; + CR := 0; + LF := 0; + FBoundaryIndex := 0; + FDetectHeaderIndex := 0; + end; + end; + + dsPartHeader: + begin + case C of + 13: Inc(CR); + 10: Inc(LF); + else + CR := 0; + LF := 0; + end; + + // ͷݵ, , ͻ˹, һ + // ޱȾ޴ͷ, ͻɻռùڴ, пڴ + // һͷߴ(MAX_PART_HEADER) + // ***ԽһŻ***: + // Բʹʱ, ֱӴABufнͷ, ͷݱ + // ABufʱȽ鷳 + FCurrentPartHeader.Write(C, 1); + // ͷ, ΪǷ + if (FCurrentPartHeader.Size > MAX_PART_HEADER) then Exit(I); + + // ͷ + // #13#10#13#10 + if (CR = 2) and (LF = 2) then + begin + // ͷͨUTF8 + LPartHeader := TEncoding.UTF8.GetString(FCurrentPartHeader.Bytes, 0, FCurrentPartHeader.Size - 4{#13#10#13#10}); + FCurrentPartHeader.Clear; + FCurrentPartField := TFormField.Create; + __InitFormFieldByHeader(FCurrentPartField, LPartHeader); + FPartFields.Add(FCurrentPartField); + + FDecodeState := dsPartData; + CR := 0; + LF := 0; + FPartDataBegin := -1; + FBoundaryIndex := 0; + FPrevIndex := 0; + end; + end; + + dsPartData: + begin + // һµݿ, Ҫݿʼλ + if (FPartDataBegin < 0) and (FPrevIndex = 0) then + FPartDataBegin := I; + + // Boundary + if (C = FBoundaryBytes[FBoundaryIndex]) then + Inc(FBoundaryIndex) + else + begin + if (FBoundaryIndex > 0) then + begin + Dec(I); + FBoundaryIndex := 0; + end; + + if (FPartDataBegin < 0) then + FPartDataBegin := I; + end; + + // һڴβвеBoundary, һж + if (FPrevIndex > 0) then + begin + // ǰֽȻܸBoundaryƥ, 䱣һ + if (FBoundaryIndex > 0) then + begin + FLookbehind[FPrevIndex] := C; + Inc(FPrevIndex); + end else + // ǰֽBoundaryƥ, ô˵֮ǰеBoundary + // Boundary, ݿе, Field + begin + FCurrentPartField.FValue.Write(FLookbehind[0], FPrevIndex); + FPrevIndex := 0; + end; + end; + + // ѵڴѾһݿ + if (I >= ALen - 1) or (FBoundaryIndex >= Length(FBoundaryBytes)) then + begin + // ڴݴField + if (FPartDataBegin >= 0) then + FCurrentPartField.FValue.Write(P[FPartDataBegin], I - FPartDataBegin - FBoundaryIndex + 1); + + // ѽһݿ + if (FBoundaryIndex >= Length(FBoundaryBytes)) then + begin + FCurrentPartField.FValue.Position := 0; + FDecodeState := dsDetect; + FBoundaryIndex := 0; + end else + // ѽڴβ, Ƿ˲еBoundary + // 䱣 + if (FPrevIndex = 0) and (FBoundaryIndex > 0) then + begin + FPrevIndex := FBoundaryIndex; + Move(P[I - FBoundaryIndex + 1], FLookbehind[0], FBoundaryIndex); + end; + + // ݿʼλҪ֮ + FPartDataBegin := -1; + end; + end; + end; + + Inc(I); + end; + + Result := ALen; +end; + +{ TResponseCookies } + +procedure TResponseCookies.AddOrSet(const AName, AValue: string; + AMaxAge: Integer; const APath, ADomain: string; AHttpOnly, ASecure: Boolean); +begin + SetCookie(AName, TResponseCookie.Create(AName, AValue, AMaxAge, APath, ADomain, AHttpOnly, ASecure)); +end; + +function TResponseCookies.GetCookieIndex(const AName: string): Integer; +var + I: Integer; +begin + for I := 0 to Count - 1 do + if SameText(Items[I].Name, AName) then Exit(I); + Result := -1; +end; + +procedure TResponseCookies.Remove(const AName: string); +var + I: Integer; +begin + I := GetCookieIndex(AName); + if (I >= 0) then + inherited Delete(I); +end; + +function TResponseCookies.GetCookie(const AName: string): TResponseCookie; +var + I: Integer; +begin + I := GetCookieIndex(AName); + if (I >= 0) then + Result := Items[I] + else + begin + Result := TResponseCookie.Create(AName, '', 0); + Add(Result); + end; +end; + +procedure TResponseCookies.SetCookie(const AName: string; + const Value: TResponseCookie); +var + I: Integer; +begin + I := GetCookieIndex(AName); + if (I >= 0) then + Items[I] := Value + else + Add(Value); +end; + +{ TSessionBase } + +constructor TSessionBase.Create(const ASessionID: string); +begin + SetSessionID(ASessionID); + SetCreateTime(Now); + SetLastAccessTime(Now); +end; + +function TSessionBase.Expired: Boolean; +begin + Result := (Now.SecondsDiffer(LastAccessTime) >= ExpiryTime); +end; + +procedure TSessionBase.Touch; +begin + LastAccessTime := Now; +end; + +{ TSession } + +constructor TSession.Create(const ASessionID: string); +begin + FValues := TDictionary.Create; + + inherited; +end; + +destructor TSession.Destroy; +begin + FreeAndNil(FValues); + inherited; +end; + +function TSession.GetCreateTime: TDateTime; +begin + Result := FCreateTime; +end; + +function TSession.GetExpiryTime: Integer; +begin + Result := FExpire; +end; + +function TSession.GetLastAccessTime: TDateTime; +begin + Result := FLastAccessTime; +end; + +function TSession.GetSessionID: string; +begin + Result := FSessionID; +end; + +function TSession.GetValue(const AName: string): string; +begin + if not FValues.TryGetValue(AName, Result) then + Result := ''; + FLastAccessTime := Now; +end; + +procedure TSession.SetCreateTime(const ACreateTime: TDateTime); +begin + FCreateTime := ACreateTime; +end; + +procedure TSession.SetExpiryTime(const AValue: Integer); +begin + FExpire := AValue; +end; + +procedure TSession.SetLastAccessTime(const ALastAccessTime: TDateTime); +begin + FLastAccessTime := ALastAccessTime; +end; + +procedure TSession.SetSessionID(const ASessionID: string); +begin + FSessionID := ASessionID; +end; + +procedure TSession.SetValue(const AName, AValue: string); +begin + if (AValue <> '') then + FValues.AddOrSetValue(AName, AValue) + else + FValues.Remove(AName); + FLastAccessTime := Now; +end; + +{ TSessionsBase } + +function TSessionsBase.AddSession(const ASessionID: string): ISession; +begin + Result := GetSessionClass.Create(ASessionID); + Result.ExpiryTime := ExpiryTime; + AddSession(ASessionID, Result); +end; + +function TSessionsBase.AddSession: ISession; +begin + Result := AddSession(NewSessionID); +end; + +function TSessionsBase.ExistsSession(const ASessionID: string): Boolean; +var + LStuff: ISession; +begin + Result := ExistsSession(ASessionID, LStuff); +end; + +{ TSessions } + +constructor TSessions.Create(ANewGUIDFunc: TFunc); +begin + FNewGUIDFunc := ANewGUIDFunc; + FSessions := TDictionary.Create; + FLocker := TMultiReadExclusiveWriteSynchronizer.Create; + FSessionClass := TSession; + CreateExpiredProcThread; +end; + +constructor TSessions.Create; +begin + Create(nil); +end; + +destructor TSessions.Destroy; +begin + FShutdown := True; + while FExpiredProcRunning do Sleep(10); + + BeginWrite; + FSessions.Clear; + EndWrite; + FreeAndNil(FLocker); + FreeAndNil(FSessions); + + inherited; +end; + +procedure TSessions.AddSession(const ASessionID: string; ASession: ISession); +begin + if (ASession.ExpiryTime <= 0) then + ASession.ExpiryTime := ExpiryTime; + FSessions.AddOrSetValue(ASessionID, ASession); +end; + +procedure TSessions.BeginRead; +begin + FLocker.BeginRead; +end; + +procedure TSessions.BeginWrite; +begin + FLocker.BeginWrite; +end; + +procedure TSessions.EndRead; +begin + FLocker.EndRead; +end; + +procedure TSessions.EndWrite; +begin + FLocker.EndWrite; +end; + +function TSessions.ExistsSession(const ASessionID: string; + var ASession: ISession): Boolean; +begin + Result := FSessions.TryGetValue(ASessionID, ASession); + if Result then + ASession.LastAccessTime := Now; +end; + +procedure TSessions.CreateExpiredProcThread; +begin + TThread.CreateAnonymousThread( + procedure + procedure _ClearExpiredSessions; + var + LPair: TPair; + begin + BeginWrite; + try + for LPair in FSessions do + begin + if FShutdown then Break; + + if LPair.Value.Expired then + RemoveSession(LPair.Key); + end; + finally + EndWrite; + end; + end; + var + LWatch: TStopwatch; + begin + FExpiredProcRunning := True; + try + LWatch := TStopwatch.StartNew; + while not FShutdown do + begin + // ÿ 5 һγʱ Session + if (FExpire > 0) and (LWatch.Elapsed.TotalMinutes >= 1) then + begin + _ClearExpiredSessions; + LWatch.Reset; + LWatch.Start; + end; + Sleep(10); + end; + finally + FExpiredProcRunning := False; + end; + end).Start; +end; + +function TSessions.NewSessionID: string; +begin + if Assigned(FNewGUIDFunc) then + Result := FNewGUIDFunc() + else + Result := TUtils.GetGUID.ToLower; +end; + +function TSessions.GetCount: Integer; +begin + Result := FSessions.Count; +end; + +function TSessions.GetEnumerator: TEnumerator; +begin + Result := TDictionary.TValueEnumerator.Create(FSessions); +end; + +function TSessions.GetExpiryTime: Integer; +begin + Result := FExpire; +end; + +function TSessions.GetItem(const AIndex: Integer): ISession; +var + LIndex: Integer; + LPair: TPair; +begin + LIndex := 0; + for LPair in FSessions do + begin + if (LIndex = AIndex) then Exit(LPair.Value); + Inc(LIndex); + end; + Result := nil; +end; + +function TSessions.GetSession(const ASessionID: string): ISession; +var + LSessionID: string; +begin + LSessionID := ASessionID; + BeginWrite; + try + if (LSessionID = '') then + LSessionID := NewSessionID; + if not FSessions.TryGetValue(LSessionID, Result) then + begin + Result := FSessionClass.Create(LSessionID); + Result.ExpiryTime := ExpiryTime; + AddSession(LSessionID, Result); + end; + finally + EndWrite; + end; + + Result.LastAccessTime := Now; +end; + +function TSessions.GetSessionClass: TSessionClass; +begin + Result := FSessionClass; +end; + +procedure TSessions.RemoveSession(const ASessionID: string); +begin + FSessions.Remove(ASessionID); +end; + +procedure TSessions.SetExpiryTime(const Value: Integer); +begin + FExpire := Value; +end; + +procedure TSessions.SetSessionClass(const Value: TSessionClass); +begin + FSessionClass := Value; +end; + +end. + diff --git a/ThirdParty/DCS/Net/Net.CrossHttpRouter.pas b/ThirdParty/DCS/Net/Net.CrossHttpRouter.pas new file mode 100644 index 00000000..7b7a9ac2 --- /dev/null +++ b/ThirdParty/DCS/Net/Net.CrossHttpRouter.pas @@ -0,0 +1,418 @@ +{******************************************************************************} +{ } +{ Delphi cross platform socket library } +{ } +{ Copyright (c) 2017 WiNDDRiVER(soulawing@gmail.com) } +{ } +{ Homepage: https://github.com/winddriver/Delphi-Cross-Socket } +{ } +{******************************************************************************} +unit Net.CrossHttpRouter; + +interface + +uses + Net.CrossHttpServer; + +type + /// + /// · + /// + /// + /// TCrossHttpServer.Route(), Get(), Post() + /// + TNetCrossRouter = class + public + /// + /// ̬ļ· + /// + /// + /// Ŀ¼ + /// + class function &Static(const ALocalDir, AFileParamName: string): TCrossHttpRouterProc2; static; + + /// + /// ļб· + /// + /// + /// ·, òΪĿ¼бҳжλ· + /// + /// + /// Ŀ¼ + /// + class function Dir(const APath, ALocalDir, ADirParamName: string): TCrossHttpRouterProc2; static; + + /// + /// Ĭҳļľ̬ļ· + /// + /// + /// ĬҳļıĿ¼ + /// + /// + /// Ĭϵҳļ,˳ѡ,ҵĸʹĸ + /// + class function Index(const ALocalDir, AFileParamName: string; const ADefIndexFiles: TArray): TCrossHttpRouterProc2; static; + end; + +implementation + +uses + System.SysUtils, System.Classes, System.IOUtils, System.NetEncoding; + +{ TNetCrossRouter } + +class function TNetCrossRouter.Index(const ALocalDir, AFileParamName: string; + const ADefIndexFiles: TArray): TCrossHttpRouterProc2; +var + LDefIndexFiles: TArray; +begin + if (ADefIndexFiles <> nil) then + LDefIndexFiles := ADefIndexFiles + else + LDefIndexFiles := [ + 'index.html', + 'main.html', + 'index.js', + 'main.js', + 'index.htm', + 'main.htm' + ]; + + Result := + procedure(ARequest: ICrossHttpRequest; AResponse: ICrossHttpResponse; var AHandled: Boolean) + var + LPath, LFile, LDefMainFile: string; + begin + LPath := ALocalDir; + LFile := ARequest.Params[AFileParamName]; + + if (LFile = '') then + begin + for LDefMainFile in LDefIndexFiles do + begin + LFile := TPath.Combine(LPath, LDefMainFile); + if TFile.Exists(LFile) then + begin + AResponse.SendFile(LFile); + AHandled := True; + Exit; + end; + end; + end else + begin + LFile := TPath.Combine(LPath, LFile); + if TFile.Exists(LFile) then + begin + AResponse.SendFile(LFile); + AHandled := True; + Exit; + end; + end; + + AHandled := False; + end; +end; + +class function TNetCrossRouter.Static( + const ALocalDir, AFileParamName: string): TCrossHttpRouterProc2; +begin + Result := + procedure(ARequest: ICrossHttpRequest; AResponse: ICrossHttpResponse; var AHandled: Boolean) + var + LFile: string; + begin + AHandled := True; + LFile := TPath.Combine(ALocalDir, ARequest.Params[AFileParamName]); + if (LFile = '') then + begin + AHandled := False; + Exit; + end; + LFile := TPath.GetFullPath(LFile); + AResponse.SendFile(LFile); + end; +end; + +{$region 'Dir'} +type + THttpFileEntry = class + Name: string; + Size: Int64; + Time: TDateTime; + Directory: Boolean; + ReadOnly: Boolean; + SysFile: Boolean; + Hidden: Boolean; + end; + +function BuildDirList(const ARealPath, ARequestPath, AHome: string): string; + function SmartSizeToStr(ABytes: Int64): string; + const + KBYTES = Int64(1024); + MBYTES = KBYTES * 1024; + GBYTES = MBYTES * 1024; + TBYTES = GBYTES * 1024; + PBYTES = TBYTES * 1024; + begin + if (ABytes < KBYTES) then + Result := Format('%dB', [ABytes]) + else if (ABytes < MBYTES) then + Result := Format('%.2fK ', [ABytes / KBYTES]) + else if (ABytes < GBYTES) then + Result := Format('%.2fM ', [ABytes / MBYTES]) + else if (ABytes < TBYTES) then + Result := Format('%.2fG ', [ABytes / GBYTES]) + else if (ABytes < PBYTES) then + Result := Format('%.2fT ', [ABytes / TBYTES]) + else + Result := Format('%.2fP ', [ABytes / PBYTES]); + end; + + function FormatDirEntry(const APath: string; F: THttpFileEntry): string; + var + Attr, Link, NameString, SizeString: string; + begin + if (F.Name = '.') or (F.Name = '..') then + begin + Result := ''; + Exit; + end; + + // drwsh + Attr := '-rw--'; + if F.Directory then + begin + Attr[1] := 'd'; + SizeString := ''; + NameString := '' + F.Name + ''; + end + else + begin + SizeString := SmartSizeToStr(F.Size); + NameString := F.Name; + end; + + if F.ReadOnly then + Attr[3] := '-'; + + if F.SysFile then + Attr[4] := 's'; + + if F.Hidden then + Attr[5] := 'h'; + + if (APath[Length(APath)] = '/') then + Link := TNetEncoding.URL.Encode(F.Name) + else + Link := APath + '/' + TNetEncoding.URL.Encode(F.Name); + + Result := + '' + NameString + '' + + '' + Attr + '' + + '' + SizeString + '' + + '' + + '' + FormatDateTime('YYYY-MM-DD HH:NN:SS', F.Time) + ''; + end; + + function PathToURL(const APath, AHome: string): string; + function _NormalizePath(const APathStr: string): string; + begin + Result := APathStr.Replace('\/', '/').Replace('\\', '/').Replace('//', '/'); + if (Result = '') then + Result := '/' + else + begin + if (Result.Chars[0] <> '/') then + Result := '/' + Result; + if (Result.Chars[Result.Length - 1] <> '/') then + Result := Result + '/'; + end; + end; + var + LPath, LHome, LSubPath: string; + LPathArr, LHomeArr: TArray; + I: Integer; + begin + LPath := _NormalizePath(APath); + LHome := _NormalizePath(AHome); + + LPathArr := LPath.Split(['/', '\'], TStringSplitOptions.ExcludeEmpty); + LHomeArr := LHome.Split(['/', '\'], TStringSplitOptions.ExcludeEmpty); + if Length(LHomeArr) > Length(LPathArr) then Exit(''); + + I := 0; + while True do + begin + if (I >= Length(LPathArr)) or (I >= Length(LHomeArr)) + or not SameText(LPathArr[I], LHomeArr[I]) then Break; + Inc(I); + end; + + Result := Format('Home / ', + [LHome]); + LSubPath := LHome; + + while True do + begin + if (I >= Length(LPathArr)) then Break; + + LSubPath := LSubPath + LPathArr[I] + '/'; + Result := Result + Format('%s / ', + [LSubPath, LPathArr[I]]); + Inc(I); + end; + end; +var + Status: Integer; + F: TSearchRec; + DirList: TStringList; + FileList: TStringList; + Data: THttpFileEntry; + i: Integer; + Total: Cardinal; + TotalBytes: Int64; + HTML: string; +begin + DirList := TStringList.Create; + FileList := TStringList.Create; + Status := FindFirst(TPath.Combine(ARealPath, '*.*'), faAnyFile, F); + while Status = 0 do + begin + if (F.Name <> '.') and (F.Name <> '..') then + begin + Data := THttpFileEntry.Create; + Data.Name := F.Name; + Data.Size := F.Size; + Data.Time := F.TimeStamp; + Data.Directory := ((F.Attr and faDirectory) <> 0); + Data.ReadOnly := ((F.Attr and faReadOnly) <> 0); + Data.SysFile := ((F.Attr and faSysFile) <> 0); + Data.Hidden := ((F.Attr and faHidden) <> 0); + + if ((F.Attr and faDirectory) <> 0) then + DirList.AddObject(Data.Name, Data) + else + FileList.AddObject(Data.Name, Data); + end; + + Status := FindNext(F); + end; + FindClose(F); + DirList.Sort; + FileList.Sort; + + HTML := + '' + + '' + + '' + + '' + + 'ļб' + + '' + + '' + + '' + + '' + + '
' + PathToURL(ARequestPath, AHome) + '

'; + + TotalBytes := 0; + Total := DirList.Count + FileList.Count; + if Total <= 0 then + HTML := HTML + '

Ŀ¼
' + else + begin + HTML := HTML + + // + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '
ļС޸ʱ
' + + + // һɫ + '' + + '' + + '
' + + + // ļб + ''; + + for i := 0 to DirList.Count - 1 do + begin + Data := THttpFileEntry(DirList.Objects[i]); + HTML := HTML + '' + FormatDirEntry(ARequestPath, Data) + ''; + FreeAndNil(Data); + end; + + for i := 0 to FileList.Count - 1 do + begin + Data := THttpFileEntry(FileList.Objects[i]); + HTML := HTML + '' + FormatDirEntry(ARequestPath, Data) + ''; + TotalBytes := TotalBytes + Data.Size; + FreeAndNil(Data); + end; + + HTML := HTML + '
' + + // һɫ + '' + + '' + + '
' + + + // ҳͳϢ + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '
' + Format('Ŀ¼: %d, ļ: %d', [DirList.Count, FileList.Count]) + '' + SmartSizeToStr(TotalBytes) + '
'; + end; + + FreeAndNil(DirList); + FreeAndNil(FileList); + + HTML := HTML + ''; + Result := HTML; +end; +{$endregion} + +class function TNetCrossRouter.Dir( + const APath, ALocalDir, ADirParamName: string): TCrossHttpRouterProc2; +begin + Result := + procedure(ARequest: ICrossHttpRequest; AResponse: ICrossHttpResponse; var AHandled: Boolean) + var + LFile: string; + begin + AHandled := True; + + LFile := TPath.Combine(ALocalDir, ARequest.Params[ADirParamName]); + if (LFile = '') then + begin + AHandled := False; + Exit; + end; + + LFile := TPath.GetFullPath(LFile); + if (TDirectory.Exists(LFile)) then + AResponse.Send(BuildDirList(LFile, ARequest.Path, APath)) + else if TFile.Exists(LFile) then + AResponse.SendFile(LFile) + else + AHandled := False; + end; +end; + +end. diff --git a/ThirdParty/DCS/Net/Net.CrossHttpServer.pas b/ThirdParty/DCS/Net/Net.CrossHttpServer.pas new file mode 100644 index 00000000..a065bd1f --- /dev/null +++ b/ThirdParty/DCS/Net/Net.CrossHttpServer.pas @@ -0,0 +1,4792 @@ +{******************************************************************************} +{ } +{ Delphi cross platform socket library } +{ } +{ Copyright (c) 2017 WiNDDRiVER(soulawing@gmail.com) } +{ } +{ Homepage: https://github.com/winddriver/Delphi-Cross-Socket } +{ } +{******************************************************************************} +unit Net.CrossHttpServer; + +{ + Linux下需要安装zlib1g-dev开发包 + sudo apt-get install zlib1g-dev +} + +interface + +uses + System.Classes, + System.SysUtils, + System.StrUtils, + System.Math, + System.IOUtils, + System.Generics.Collections, + System.RegularExpressions, + System.NetEncoding, + System.RegularExpressionsCore, + System.RegularExpressionsConsts, + System.ZLib, + System.Hash, + Net.SocketAPI, + Net.CrossSocket.Base, + Net.CrossSocket, + Net.CrossServer, + {$IFDEF __CROSS_SSL__} + Net.CrossSslSocket, + Net.CrossSslServer, + {$ENDIF} + Net.CrossHttpParams, + Net.CrossHttpUtils, + Utils.Logger; + +const + CROSS_HTTP_SERVER_NAME = 'CrossHttpServer/2.0'; + MIN_COMPRESS_SIZE = 512; + +type + ECrossHttpException = class(Exception) + private + FStatusCode: Integer; + public + constructor Create(const AMessage: string; AStatusCode: Integer = 400); reintroduce; virtual; + constructor CreateFmt(const AMessage: string; const AArgs: array of const; AStatusCode: Integer = 400); reintroduce; virtual; + + property StatusCode: Integer read FStatusCode write FStatusCode; + end; + + ICrossHttpServer = interface; + ICrossHttpRequest = interface; + ICrossHttpResponse = interface; + + /// + /// HTTP连接接口 + /// + ICrossHttpConnection = interface(ICrossConnection) + ['{72E9AC44-958C-4C6F-8769-02EA5EC3E9A8}'] + function GetRequest: ICrossHttpRequest; + function GetResponse: ICrossHttpResponse; + function GetServer: ICrossHttpServer; + + /// + /// 请求对象 + /// + property Request: ICrossHttpRequest read GetRequest; + + /// + /// 响应对象 + /// + property Response: ICrossHttpResponse read GetResponse; + + /// + /// Server对象 + /// + property Server: ICrossHttpServer read GetServer; + end; + + /// + /// 请求体类型 + /// + TBodyType = (btNone, btUrlEncoded, btMultiPart, btBinary); + + /// + /// HTTP请求接口 + /// + ICrossHttpRequest = interface + ['{B26B7E7B-6B24-4D86-AB58-EBC20722CFDD}'] + function GetConnection: ICrossHttpConnection; + function GetRawRequestText: string; + function GetRawPathAndParams: string; + function GetMethod: string; + function GetPath: string; + function GetVersion: string; + function GetHeader: THttpHeader; + function GetCookies: TRequestCookies; + function GetSession: ISession; + function GetParams: THttpUrlParams; + function GetQuery: THttpUrlParams; + function GetBody: TObject; + function GetBodyType: TBodyType; + function GetKeepAlive: Boolean; + function GetAccept: string; + function GetAcceptEncoding: string; + function GetAcceptLanguage: string; + function GetReferer: string; + function GetUserAgent: string; + function GetIfModifiedSince: TDateTime; + function GetIfNoneMatch: string; + function GetRange: string; + function GetIfRange: string; + function GetAuthorization: string; + function GetXForwardedFor: string; + function GetContentLength: Int64; + function GetHostName: string; + function GetHostPort: Word; + function GetContentType: string; + function GetContentEncoding: string; + function GetRequestBoundary: string; + function GetRequestCmdLine: string; + function GetRequestConnection: string; + function GetTransferEncoding: string; + function GetIsChunked: Boolean; + function GetIsMultiPartFormData: Boolean; + function GetIsUrlEncodedFormData: Boolean; + function GetPostDataSize: Int64; + + /// + /// HTTP连接对象 + /// + property Connection: ICrossHttpConnection read GetConnection; + + /// + /// 原始请求数据 + /// + property RawRequestText: string read GetRawRequestText; + + /// + /// 原始请求路径及参数 + /// + property RawPathAndParams: string read GetRawPathAndParams; + + /// + /// 请求方法 + /// + /// + /// GET + /// + /// + /// POST + /// + /// + /// PUT + /// + /// + /// DELETE + /// + /// + /// HEAD + /// + /// + /// OPTIONS + /// + /// + /// TRACE + /// + /// + /// CONNECT
+ ///
+ /// + /// PATCH
+ ///
+ /// + /// COPY
+ ///
+ /// + /// LINK
+ ///
+ /// + /// UNLINK
+ ///
+ /// + /// PURGE
+ ///
+ /// + /// LOCK
+ ///
+ /// + /// UNLOCK
+ ///
+ /// + /// PROPFIND + /// + ///
+ ///
+ property Method: string read GetMethod; + + /// + /// + /// 请求路径, 不包含参数部分 + /// + /// + /// 比如: /api/callapi1 + /// + /// + property Path: string read GetPath; + + /// + /// 请求版本: + /// + /// + /// HTTP/1.0 + /// + /// + /// HTTP/1.1 + /// + /// + /// + property Version: string read GetVersion; + + /// + /// HTTP请求头 + /// + property Header: THttpHeader read GetHeader; + + /// + /// 客户端传递过来的Cookies + /// + property Cookies: TRequestCookies read GetCookies; + + /// + /// Session对象 + /// + /// + /// + /// 只有在Server开启了Session支持的情况, 该属性才有效, 否则该属性为nil + /// + /// + /// 要开启Server的Session支持, 只需要设置Server.SessionIDCookieName不为空即可 + /// + /// + property Session: ISession read GetSession; + + /// + /// + /// 请求路径中定义的参数 + /// + /// + /// 比如定义了一个Get('/echo/:text', cb) 然后有一个请求为 /echo/hello, 那么 Params + /// 中就会有一个名为 'text', 值为 'hello' 的参数 + /// + /// + property Params: THttpUrlParams read GetParams; + + /// + /// 请求路径后形如?key1=value1&key2=value2的参数 + /// + property Query: THttpUrlParams read GetQuery; + + /// + /// Body数据, 通过检查BodyType可以知道数据类型: + /// + /// + /// btNone(nil) + /// + /// + /// btUrlEncoded(THttpUrlParams) + /// + /// + /// btMultiPart(THttpMultiPartFormData) + /// + /// + /// btBinary(TBytesStream) + /// + /// + /// + property Body: TObject read GetBody; + + /// + /// Body的类型, + /// + /// + /// btNone(nil) + /// + /// + /// btUrlEncoded(THttpUrlParams) + /// + /// + /// btMultiPart(THttpMultiPartFormData) + /// + /// + /// btBinary(TBytesStream) + /// + /// + /// + property BodyType: TBodyType read GetBodyType; + + /// + /// KeepAliv标志 + /// + property KeepAlive: Boolean read GetKeepAlive; + + /// + /// 客户端能接收的数据种类 + /// + /// + /// image/webp,image/*,*/*;q=0.8 + /// + property Accept: string read GetAccept; + + /// + /// 客户端能接收的编码 + /// + /// + /// gzip, deflate, sdch + /// + property AcceptEncoding: string read GetAcceptEncoding; + + /// + /// 客户端能接收的语言 + /// + /// + /// zh-CN,zh;q=0.8 + /// + property AcceptLanguage: string read GetAcceptLanguage; + + /// + /// 参考地址, 描述该请求由哪个页面发出 + /// + property Referer: string read GetReferer; + + /// + /// 用户代理 + /// + /// + /// Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like + /// Gecko) Chrome/50.0.2661.102 Safari/537.36 + /// + property UserAgent: string read GetUserAgent; + + /// + /// 请求内容在浏览器端的缓存时间 + /// + property IfModifiedSince: TDateTime read GetIfModifiedSince; + + /// + /// 请求内容在浏览器端的标记 + /// + property IfNoneMatch: string read GetIfNoneMatch; + + /// + /// 请求分块传输 + /// + property Range: string read GetRange; + + /// + /// 请求分块传输时传往服务器的标记, 用于服务器比较数据是否已发生变化 + /// + property IfRange: string read GetIfRange; + + /// + /// 简单认证信息 + /// + property Authorization: string read GetAuthorization; + + /// + /// 通过HTTP代理或负载均衡方式连接到Web服务器的客户端最原始的IP地址的HTTP请求头字段 + /// + property XForwardedFor: string read GetXForwardedFor; + + /// + /// 请求数据长度 + /// + property ContentLength: Int64 read GetContentLength; + + /// + /// 请求的主机名(域名、IP) + /// + property HostName: string read GetHostName; + + /// + /// 请求的主机端口 + /// + property HostPort: Word read GetHostPort; + + /// + /// 内容类型 + /// + property ContentType: string read GetContentType; + + /// + /// 请求命令行(也就是HTTP请求的第一行) + /// + property RequestCmdLine: string read GetRequestCmdLine; + + /// + /// 请求分界符 + /// + property RequestBoundary: string read GetRequestBoundary; + + /// + /// 传输编码 + /// + property TransferEncoding: string read GetTransferEncoding; + + /// + /// 内容编码 + /// + property ContentEncoding: string read GetContentEncoding; + + /// + /// 连接方式 + /// + property RequestConnection: string read GetRequestConnection; + + /// + /// 请求数据是否使用块编码 + /// + property IsChunked: Boolean read GetIsChunked; + + /// + /// 请求数据是使用 multipart/form-data 方式提交的 + /// + property IsMultiPartFormData: Boolean read GetIsMultiPartFormData; + + /// + /// 请求数据是使用 application/x-www-form-urlencoded 方式提交的 + /// + property IsUrlEncodedFormData: Boolean read GetIsUrlEncodedFormData; + + /// + /// 请求数据大小 + /// + property PostDataSize: Int64 read GetPostDataSize; + end; + + /// + /// 压缩类型 + /// + TCompressType = (ctGZip, ctDeflate); + + /// + /// HTTP应答接口 + /// + ICrossHttpResponse = interface + ['{5E15C20F-E221-4B10-90FC-222173A6F3E8}'] + function GetConnection: ICrossHttpConnection; + function GetRequest: ICrossHttpRequest; + function GetStatusCode: Integer; + procedure SetStatusCode(Value: Integer); + function GetContentType: string; + procedure SetContentType(const Value: string); + function GetLocation: string; + procedure SetLocation(const Value: string); + function GetHeader: THttpHeader; + function GetCookies: TResponseCookies; + function GetSent: Boolean; + + /// + /// 压缩发送块数据 + /// + /// + /// 产生块数据的匿名函数 + /// // AData: 数据指针 + /// // ACount: 数据大小 + /// // Result: 如果返回True, 则发送数据; 如果返回False, 则忽略AData和ACount并结束发送 + /// function(AData: PPointer; ACount: PNativeInt): Boolean + /// begin + /// end + /// + /// + /// 压缩方式 + /// + /// + /// 回调函数 + /// + /// + /// 本方法实现了一边压缩一边发送数据, 所以可以支持无限大的分块数据的压缩发送, 而不用占用太多的内存和CPU

+ /// zlib参考手册:
+ ///
+ procedure SendZCompress(const AChunkSource: TFunc; ACompressType: TCompressType; ACallback: TProc = nil); overload; + + /// + /// 压缩发送无类型数据 + /// + /// + /// 无类型数据 + /// + /// + /// 数据大小 + /// + /// + /// 压缩方式 + /// + /// + /// 回调函数 + /// + procedure SendZCompress(const ABody; ACount: NativeInt; ACompressType: TCompressType; ACallback: TProc = nil); overload; + + /// + /// 压缩发送字节数据 + /// + /// + /// 字节数据 + /// + /// + /// 偏移量 + /// + /// + /// 数据大小 + /// + /// + /// 压缩方式 + /// + /// + /// 回调函数 + /// + procedure SendZCompress(const ABody: TBytes; AOffset, ACount: NativeInt; ACompressType: TCompressType; ACallback: TProc = nil); overload; + + /// + /// 压缩发送字节数据 + /// + /// + /// 字节数据 + /// + /// + /// 压缩方式 + /// + /// + /// 回调函数 + /// + procedure SendZCompress(const ABody: TBytes; ACompressType: TCompressType; ACallback: TProc = nil); overload; + + /// + /// 压缩发送流数据 + /// + /// + /// 流数据 + /// + /// + /// 偏移量 + /// + /// + /// 数据大小 + /// + /// + /// 压缩方式 + /// + /// + /// 回调函数 + /// + /// + /// 必须保证发送过程中流对象的有效性, 要释放流对象可以放到回调函数中进行 + /// + procedure SendZCompress(const ABody: TStream; const AOffset, ACount: Int64; ACompressType: TCompressType; ACallback: TProc = nil); overload; + + /// + /// 压缩发送流数据 + /// + /// + /// 流数据 + /// + /// + /// 压缩方式 + /// + /// + /// 回调函数 + /// + /// + /// 必须保证发送过程中流对象的有效性, 要释放流对象可以放到回调函数中进行 + /// + procedure SendZCompress(const ABody: TStream; ACompressType: TCompressType; ACallback: TProc = nil); overload; + + /// + /// 压缩发送字符串数据 + /// + /// + /// 字符串数据 + /// + /// + /// 压缩方式 + /// + /// + /// 回调函数 + /// + procedure SendZCompress(const ABody: string; ACompressType: TCompressType; ACallback: TProc = nil); overload; + + /// + /// 不压缩发送块数据 + /// + /// + /// 产生块数据的匿名函数 + /// // AData: 数据指针 + /// // ACount: 数据大小 + /// // Result: 如果返回True, 则发送数据; 如果返回False, 则忽略AData和ACount并结束发送 + /// function(AData: PPointer; ACount: PNativeInt): Boolean + /// begin + /// end + /// + /// + /// 回调函数 + /// + /// + /// 使用该方法可以一边生成数据一边发送, 无需等待数据全部准备完成 + /// + procedure SendNoCompress(const AChunkSource: TFunc; ACallback: TProc = nil); overload; + + /// + /// 不压缩发送无类型数据 + /// + /// + /// 无类型数据 + /// + /// + /// 数据大小 + /// + /// + /// 回调函数 + /// + procedure SendNoCompress(const ABody; ACount: NativeInt; ACallback: TProc = nil); overload; + + /// + /// 不压缩发送字节数据 + /// + /// + /// 字节数据 + /// + /// + /// 偏移量 + /// + /// + /// 数据大小 + /// + /// + /// 回调函数 + /// + procedure SendNoCompress(const ABody: TBytes; AOffset, ACount: NativeInt; ACallback: TProc = nil); overload; + + /// + /// 不压缩发送字节数据 + /// + /// + /// 字节数据 + /// + /// + /// 回调函数 + /// + procedure SendNoCompress(const ABody: TBytes; ACallback: TProc = nil); overload; + + /// + /// 不压缩发送流数据 + /// + /// + /// 流数据 + /// + /// + /// 偏移量 + /// + /// + /// 数据大小 + /// + /// + /// 回调函数 + /// + /// + /// 必须保证发送过程中流对象的有效性, 要释放流对象可以放到回调函数中进行 + /// + procedure SendNoCompress(const ABody: TStream; const AOffset, ACount: Int64; ACallback: TProc = nil); overload; + + /// + /// 不压缩发送流数据 + /// + /// + /// 流数据 + /// + /// + /// 回调函数 + /// + /// + /// 必须保证发送过程中流对象的有效性, 要释放流对象可以放到回调函数中进行 + /// + procedure SendNoCompress(const ABody: TStream; ACallback: TProc = nil); overload; + + /// + /// 不压缩发送字符串数据 + /// + /// + /// 字符串数据 + /// + /// + /// 回调函数 + /// + procedure SendNoCompress(const ABody: string; ACallback: TProc = nil); overload; + + /// + /// 发送无类型数据 + /// + /// + /// 无类型数据 + /// + /// + /// 数据大小 + /// + /// + /// 回调函数
+ /// + procedure Send(const ABody; ACount: NativeInt; ACallback: TProc = nil); overload; + + /// + /// 发送字节数据 + /// + /// + /// 字节数据 + /// + /// + /// 偏移量 + /// + /// + /// 数据大小 + /// + /// + /// 回调函数
+ /// + procedure Send(const ABody: TBytes; AOffset, ACount: NativeInt; ACallback: TProc = nil); overload; + + /// + /// 发送字节数据 + /// + /// + /// 字节数据 + /// + /// + /// 回调函数
+ /// + procedure Send(const ABody: TBytes; ACallback: TProc = nil); overload; + + /// + /// 发送流数据 + /// + /// + /// 流数据 + /// + /// + /// 偏移量 + /// + /// + /// 数据大小 + /// + /// + /// 回调函数 + /// + /// + /// 必须保证发送过程中流对象的有效性, 要释放流对象可以放到回调函数中进行 + /// + procedure Send(const ABody: TStream; const AOffset, ACount: Int64; ACallback: TProc = nil); overload; + + /// + /// 发送流数据 + /// + /// + /// 流数据 + /// + /// + /// 回调函数 + /// + /// + /// 必须保证发送过程中流对象的有效性, 要释放流对象可以放到回调函数中进行 + /// + procedure Send(const ABody: TStream; ACallback: TProc = nil); overload; + + /// + /// 发送字符串数据 + /// + /// + /// 字符串数据 + /// + /// + /// 回调函数
+ /// + procedure Send(const ABody: string; ACallback: TProc = nil); overload; + + /// + /// 发送Json字符串数据 + /// + /// + /// Json字符串数据 + /// + /// + /// 回调函数
+ /// + procedure Json(const AJson: string; ACallback: TProc = nil); + + /// + /// 发送文件内容 + /// + /// + /// 文件名 + /// + /// + /// 回调函数 + /// + procedure SendFile(const AFileName: string; ACallback: TProc = nil); + + /// + /// 将文件以下载形式发送 + /// + /// + /// 文件名 + /// + /// + /// 回调函数 + /// + procedure Download(const AFileName: string; ACallback: TProc = nil); + + /// + /// 发送状态码 + /// + /// + /// 状态码 + /// + /// + /// 描述信息(body) + /// + /// + /// 回调函数 + /// + /// + /// 描述信息即是body数据, 如果设置为空, 则body也为空 + /// + procedure SendStatus(AStatusCode: Integer; const ADescription: string; + ACallback: TProc = nil); overload; + + /// + /// 发送状态码 + /// + /// + /// 状态码 + /// + /// + /// 回调函数 + /// + /// + /// 该方法根据状态码生成默认的body数据 + /// + procedure SendStatus(AStatusCode: Integer; + ACallback: TProc = nil); overload; + + /// + /// 发送重定向Url命令 + /// + /// + /// 新的Url + /// + /// + /// 回调函数 + /// + procedure Redirect(const AUrl: string; ACallback: TProc = nil); + + /// + /// 设置Content-Disposition, 令客户端将收到的数据作为文件下载处理 + /// + /// + /// 文件名 + /// + procedure Attachment(const AFileName: string); + + /// + /// HTTP连接对象 + /// + property Connection: ICrossHttpConnection read GetConnection; + + /// + /// 请求对象 + /// + property Request: ICrossHttpRequest read GetRequest; + + /// + /// 状态码 + /// + property StatusCode: Integer read GetStatusCode write SetStatusCode; + + /// + /// 内容类型 + /// + property ContentType: string read GetContentType write SetContentType; + + /// + /// 重定向Url + /// + property Location: string read GetLocation write SetLocation; + + /// + /// HTTP响应头 + /// + property Header: THttpHeader read GetHeader; + + /// + /// 设置Cookies + /// + property Cookies: TResponseCookies read GetCookies; + + /// + /// 是否已经发送数据 + /// + property Sent: Boolean read GetSent; + end; + + /// + /// 路由接口 + /// + ICrossHttpRouter = interface + ['{2B095450-6A5D-450F-8DCD-6911526C733F}'] + function GetMethod: string; + function GetPath: string; + function IsMatch(ARequest: ICrossHttpRequest): Boolean; + procedure Execute(ARequest: ICrossHttpRequest; AResponse: ICrossHttpResponse; var AHandled: Boolean); + + property Method: string read GetMethod; + property Path: string read GetPath; + end; + TCrossHttpRouters = TList; + + TCrossHttpRouterProc = reference to procedure(ARequest: ICrossHttpRequest; AResponse: ICrossHttpResponse); + TCrossHttpRouterMethod = procedure(ARequest: ICrossHttpRequest; AResponse: ICrossHttpResponse) of object; + TCrossHttpRouterProc2 = reference to procedure(ARequest: ICrossHttpRequest; AResponse: ICrossHttpResponse; var AHandled: Boolean); + TCrossHttpRouterMethod2 = procedure(ARequest: ICrossHttpRequest; AResponse: ICrossHttpResponse; var AHandled: Boolean) of object; + + TCrossHttpConnEvent = procedure(Sender: TObject; AConnection: ICrossHttpConnection) of object; + TCrossHttpRequestEvent = procedure(Sender: TObject; ARequest: ICrossHttpRequest; AResponse: ICrossHttpResponse; var AHandled: Boolean) of object; + TCrossHttpRequestExceptionEvent = procedure(Sender: TObject; ARequest: ICrossHttpRequest; AResponse: ICrossHttpResponse; AException: Exception) of object; + TCrossHttpDataEvent = procedure(Sender: TObject; AClient: ICrossHttpConnection; ABuf: Pointer; ALen: Integer) of object; + + /// + /// + /// 跨平台HTTP服务器接口 + /// + /// + /// 路由定义方式: + /// + /// + /// Route(AMehod, APath, ARouter) + /// + /// + /// Get(APath, ARouter) + /// + /// + /// Put(APath, ARouter) + /// + /// + /// Post(APath, ARouter)
+ ///
+ /// + /// Delete(APath, ARouter)
+ ///
+ /// + /// All(APath, ARouter)
+ ///
+ /// + /// 其中AMehod和APath都支持正则表达式, ARouter可以是一个对象方法也可以是匿名函数
+ ///
+ ///
+ /// + /// + /// 这里偷了下懒, 没将HTTP和HTTPS分开实现两个不同的接口, 需要通过编译开关选择使用HTTP还是HTTP + /// + /// + /// 通过接口引用计数保证连接的有效性,所以可以在路由函数中调用线程池来处理业务逻辑,而不用担心处理过程中连接对象被释放 + /// + /// + /// 每个请求的响应流程大致为: + /// + /// + /// + /// 执行匹配的中间件; + /// + /// + /// 执行匹配的路由 + /// + /// + /// + /// + /// // 在线程池中处理业务逻辑 + /// FCrossHttpServer.Route('GET', '/runtask/:name', + /// procedure(ARequest: ICrossHttpRequest; AResponse: ICrossHttpResponse) + /// begin + /// System.Threading.TTask.Run( + /// procedure + /// begin + /// CallTask(ARequest.Params['name']); + /// end); + /// end); + /// // 正则表达式 + /// FCrossHttpServer.Route('GET', '/query/:count(\d+)', + /// procedure(ARequest: ICrossHttpRequest; AResponse: ICrossHttpResponse) + /// begin + /// System.Threading.TTask.Run( + /// procedure + /// begin + /// CallQuery(ARequest.Params['count'].ToInteger); + /// end); + /// end); + /// + ICrossHttpServer = interface({$IFDEF __CROSS_SSL__}ICrossSslServer{$ELSE}ICrossServer{$ENDIF}) + ['{224D16AA-317C-435E-9C2E-92868E578DB3}'] + function GetStoragePath: string; + function GetAutoDeleteFiles: Boolean; + function GetMaxHeaderSize: Int64; + function GetMaxPostDataSize: Int64; + function GetCompressible: Boolean; + function GetMinCompressSize: Int64; + function GetSessions: ISessions; + function GetSessionIDCookieName: string; + function GetOnRequest: TCrossHttpRequestEvent; + function GetOnRequestException: TCrossHttpRequestExceptionEvent; + function GetOnPostDataBegin: TCrossHttpConnEvent; + function GetOnPostData: TCrossHttpDataEvent; + function GetOnPostDataEnd: TCrossHttpConnEvent; + + procedure SetStoragePath(const Value: string); + procedure SetAutoDeleteFiles(const Value: Boolean); + procedure SetMaxHeaderSize(const Value: Int64); + procedure SetMaxPostDataSize(const Value: Int64); + procedure SetCompressible(const Value: Boolean); + procedure SetMinCompressSize(const Value: Int64); + procedure SetSessions(const Value: ISessions); + procedure SetSessionIDCookieName(const Value: string); + procedure SetOnRequest(const Value: TCrossHttpRequestEvent); + procedure SetOnRequestException(const Value: TCrossHttpRequestExceptionEvent); + procedure SetOnPostDataBegin(const Value: TCrossHttpConnEvent); + procedure SetOnPostData(const Value: TCrossHttpDataEvent); + procedure SetOnPostDataEnd(const Value: TCrossHttpConnEvent); + + /// + /// 创建路由对象 + /// + /// + /// 请求方式 + /// + /// + /// 请求路径 + /// + /// + /// 路由匿名函数 + /// + /// + /// 路由方法 + /// + /// + /// 路由匿名函数 + /// + /// + /// 路由方法 + /// + function CreateRouter(const AMethod, APath: string; + ARouterProc: TCrossHttpRouterProc; ARouterMethod: TCrossHttpRouterMethod; + ARouterProc2: TCrossHttpRouterProc2; ARouterMethod2: TCrossHttpRouterMethod2): ICrossHttpRouter; + + /// + /// 注册中间件 + /// + /// + /// 请求方式 + /// + /// + /// 请求路径 + /// + /// + /// 中间件处理匿名函数, 执行完处理函数之后还会继续执行后续匹配的中间件及路由 + /// + /// + /// + /// + /// 中间件严格按照注册时的顺序被调用 + /// + /// + /// 中间件先于路由执行 + /// + /// + /// + function Use(const AMethod, APath: string; + AMiddlewareProc: TCrossHttpRouterProc): ICrossHttpServer; overload; + + /// + /// 注册中间件 + /// + /// + /// 请求方式 + /// + /// + /// 请求路径 + /// + /// + /// 中间件处理匿名函数, 执行完处理函数之后, 如果AHandled=False则会继续执行后续匹配的中间件及路由, + /// 否则后续匹配的中间件及路由不会被执行 + /// + /// + /// + /// + /// 中间件严格按照注册时的顺序被调用 + /// + /// + /// 中间件先于路由执行 + /// + /// + /// + function Use(const AMethod, APath: string; + AMiddlewareProc2: TCrossHttpRouterProc2): ICrossHttpServer; overload; + + /// + /// 注册中间件 + /// + /// + /// 请求方式 + /// + /// + /// 请求路径 + /// + /// + /// 中间件处理匿名方法, 执行完处理方法之后还会继续执行后续匹配的中间件及路由 + /// + /// + /// + /// + /// 中间件严格按照注册时的顺序被调用 + /// + /// + /// 中间件先于路由执行 + /// + /// + /// + function Use(const AMethod, APath: string; + AMiddlewareMethod: TCrossHttpRouterMethod): ICrossHttpServer; overload; + + /// + /// 注册中间件 + /// + /// + /// 请求方式 + /// + /// + /// 请求路径 + /// + /// + /// 中间件处理匿名方法, 执行完处理方法之后, 如果AHandled=False则会继续执行后续匹配的中间件及路由, + /// 否则后续匹配的中间件及路由不会被执行 + /// + /// + /// + /// + /// 中间件严格按照注册时的顺序被调用 + /// + /// + /// 中间件先于路由执行 + /// + /// + /// + function Use(const AMethod, APath: string; + AMiddlewareMethod2: TCrossHttpRouterMethod2): ICrossHttpServer; overload; + + /// + /// 注册中间件 + /// + /// + /// 请求路径 + /// + /// + /// 中间件处理匿名函数, 执行完处理函数之后还会继续执行后续匹配的中间件及路由 + /// + /// + /// + /// + /// 中间件严格按照注册时的顺序被调用 + /// + /// + /// 中间件先于路由执行 + /// + /// + /// + function Use(const APath: string; + AMiddlewareProc: TCrossHttpRouterProc): ICrossHttpServer; overload; + + /// + /// 注册中间件 + /// + /// + /// 请求路径 + /// + /// + /// 中间件处理匿名函数, 执行完处理函数之后, 如果AHandled=False则会继续执行后续匹配的中间件及路由, + /// 否则后续匹配的中间件及路由不会被执行 + /// + /// + /// + /// + /// 中间件严格按照注册时的顺序被调用 + /// + /// + /// 中间件先于路由执行 + /// + /// + /// + function Use(const APath: string; + AMiddlewareProc2: TCrossHttpRouterProc2): ICrossHttpServer; overload; + + /// + /// 注册中间件 + /// + /// + /// 请求路径 + /// + /// + /// 中间件处理匿名方法, 执行完处理方法之后还会继续执行后续匹配的中间件及路由 + /// + /// + /// + /// + /// 中间件严格按照注册时的顺序被调用 + /// + /// + /// 中间件先于路由执行 + /// + /// + /// + function Use(const APath: string; + AMiddlewareMethod: TCrossHttpRouterMethod): ICrossHttpServer; overload; + + /// + /// 注册中间件 + /// + /// + /// 请求路径 + /// + /// + /// 中间件处理匿名方法, 执行完处理方法之后, 如果AHandled=False则会继续执行后续匹配的中间件及路由, + /// 否则后续匹配的中间件及路由不会被执行 + /// + /// + /// + /// + /// 中间件严格按照注册时的顺序被调用 + /// + /// + /// 中间件先于路由执行 + /// + /// + /// + function Use(const APath: string; + AMiddlewareMethod2: TCrossHttpRouterMethod2): ICrossHttpServer; overload; + + /// + /// 注册中间件 + /// + /// + /// 中间件处理匿名函数, 执行完处理函数之后还会继续执行后续匹配的中间件及路由 + /// + /// + /// + /// + /// 中间件严格按照注册时的顺序被调用 + /// + /// + /// 中间件先于路由执行 + /// + /// + /// + function Use(AMiddlewareProc: TCrossHttpRouterProc): ICrossHttpServer; overload; + function Use(AMiddlewareProc2: TCrossHttpRouterProc2): ICrossHttpServer; overload; + + /// + /// 注册中间件 + /// + /// + /// 中间件处理方法, 执行完处理方法之后还会继续执行后续匹配的中间件及路由 + /// + /// + /// + /// + /// 中间件严格按照注册时的顺序被调用 + /// + /// + /// 中间件先于路由执行 + /// + /// + /// + function Use(AMiddlewareMethod: TCrossHttpRouterMethod): ICrossHttpServer; overload; + function Use(AMiddlewareMethod2: TCrossHttpRouterMethod2): ICrossHttpServer; overload; + + /// + /// 注册路由(请求处理函数) + /// + /// + /// 请求方式, GET/POST/PUT/DELETE等, 支持正则表达式, * 表示处理全部请求方式 + /// + /// + /// 请求路径, 支持正则表达式, * 表示处理全部请求路径,
例如: + /// /path/:param1/:param2(\d+)|/path/:param
+ /// + /// + /// 路由处理匿名函数 + /// + /// + /// + /// + /// 路由严格按照注册时的顺序被调用, 所以如果在注册了AMethod=*, + /// APath=*的路由之后,再注册的其它路由将不会被调用. 所以强烈建议把 "* 路由" 放到最后注册. + /// + /// + /// 路由中的正则表达式用法与node.js express相同 + /// + /// + /// + function Route(const AMethod, APath: string; ARouterProc: TCrossHttpRouterProc): ICrossHttpServer; overload; + function Route(const AMethod, APath: string; ARouterProc2: TCrossHttpRouterProc2): ICrossHttpServer; overload; + + /// + /// 注册路由(请求处理函数) + /// + /// + /// 请求方式, GET/POST/PUT/DELETE等, 支持正则表达式, * 表示处理全部请求方式 + /// + /// + /// 请求路径, 支持正则表达式, * 表示处理全部请求路径,
例如: + /// /path/:param1/:param2(\d+)|/path/:param + /// + /// + /// 路由处理方法 + /// + /// + /// + /// + /// 路由严格按照注册时的顺序被调用, 所以如果在注册了AMethod=*, + /// APath=*的路由之后,再注册的其它路由将不会被调用. 所以强烈建议把 "* 路由" 放到最后注册. + /// + /// + /// 路由中的正则表达式用法与node.js express相同 + /// + /// + /// + function Route(const AMethod, APath: string; ARouterMethod: TCrossHttpRouterMethod): ICrossHttpServer; overload; + function Route(const AMethod, APath: string; ARouterMethod2: TCrossHttpRouterMethod2): ICrossHttpServer; overload; + + /// + /// 注册GET路由(请求处理函数) + /// + /// + /// 请求路径, 支持正则表达式, * 表示处理全部请求路径,
例如: + /// /path/:param1/:param2(\d+)|/path/:param
+ /// + /// + /// 路由处理匿名函数 + /// + /// + /// + /// + /// 路由严格按照注册时的顺序被调用, 所以如果在注册了AMethod=*, + /// APath=*的路由之后,再注册的其它路由将不会被调用. 所以强烈建议把 "* 路由" 放到最后注册. + /// + /// + /// 路由中的正则表达式用法与node.js express相同 + /// + /// + /// + function Get(const APath: string; ARouterProc: TCrossHttpRouterProc): ICrossHttpServer; overload; + function Get(const APath: string; ARouterProc2: TCrossHttpRouterProc2): ICrossHttpServer; overload; + + /// + /// 注册GET路由(请求处理函数) + /// + /// + /// 请求路径, 支持正则表达式, * 表示处理全部请求路径,
例如: + /// /path/:param1/:param2(\d+)|/path/:param
+ /// + /// + /// 路由处理方法 + /// + /// + /// + /// + /// 路由严格按照注册时的顺序被调用, 所以如果在注册了AMethod=*, + /// APath=*的路由之后,再注册的其它路由将不会被调用. 所以强烈建议把 "* 路由" 放到最后注册. + /// + /// + /// 路由中的正则表达式用法与node.js express相同 + /// + /// + /// + function Get(const APath: string; ARouterMethod: TCrossHttpRouterMethod): ICrossHttpServer; overload; + function Get(const APath: string; ARouterMethod2: TCrossHttpRouterMethod2): ICrossHttpServer; overload; + + /// + /// 注册PUT路由(请求处理函数) + /// + /// + /// 请求路径, 支持正则表达式, * 表示处理全部请求路径,
例如: + /// /path/:param1/:param2(\d+)|/path/:param
+ /// + /// + /// 路由处理匿名函数 + /// + /// + /// + /// + /// 路由严格按照注册时的顺序被调用, 所以如果在注册了AMethod=*, + /// APath=*的路由之后,再注册的其它路由将不会被调用. 所以强烈建议把 "* 路由" 放到最后注册. + /// + /// + /// 路由中的正则表达式用法与node.js express相同 + /// + /// + /// + function Put(const APath: string; ARouterProc: TCrossHttpRouterProc): ICrossHttpServer; overload; + function Put(const APath: string; ARouterProc2: TCrossHttpRouterProc2): ICrossHttpServer; overload; + + /// + /// 注册PUT路由(请求处理函数) + /// + /// + /// 请求路径, 支持正则表达式, * 表示处理全部请求路径,
例如: + /// /path/:param1/:param2(\d+)|/path/:param
+ /// + /// + /// 路由处理方法 + /// + /// + /// + /// + /// 路由严格按照注册时的顺序被调用, 所以如果在注册了AMethod=*, + /// APath=*的路由之后,再注册的其它路由将不会被调用. 所以强烈建议把 "* 路由" 放到最后注册. + /// + /// + /// 路由中的正则表达式用法与node.js express相同 + /// + /// + /// + function Put(const APath: string; ARouterMethod: TCrossHttpRouterMethod): ICrossHttpServer; overload; + function Put(const APath: string; ARouterMethod2: TCrossHttpRouterMethod2): ICrossHttpServer; overload; + + /// + /// 注册POST路由(请求处理函数) + /// + /// + /// 请求路径, 支持正则表达式, * 表示处理全部请求路径,
例如: + /// /path/:param1/:param2(\d+)|/path/:param + /// + /// + /// 路由处理匿名函数 + /// + /// + /// + /// + /// 路由严格按照注册时的顺序被调用, 所以如果在注册了AMethod=*, + /// APath=*的路由之后,再注册的其它路由将不会被调用. 所以强烈建议把 "* 路由" 放到最后注册. + /// + /// + /// 路由中的正则表达式用法与node.js express相同 + /// + /// + /// + function Post(const APath: string; ARouterProc: TCrossHttpRouterProc): ICrossHttpServer; overload; + function Post(const APath: string; ARouterProc2: TCrossHttpRouterProc2): ICrossHttpServer; overload; + + /// + /// 注册POST路由(请求处理函数) + /// + /// + /// 请求路径, 支持正则表达式, * 表示处理全部请求路径,
例如: + /// /path/:param1/:param2(\d+)|/path/:param
+ /// + /// + /// 路由处理方法 + /// + /// + /// + /// + /// 路由严格按照注册时的顺序被调用, 所以如果在注册了AMethod=*, + /// APath=*的路由之后,再注册的其它路由将不会被调用. 所以强烈建议把 "* 路由" 放到最后注册. + /// + /// + /// 路由中的正则表达式用法与node.js express相同 + /// + /// + /// + function Post(const APath: string; ARouterMethod: TCrossHttpRouterMethod): ICrossHttpServer; overload; + function Post(const APath: string; ARouterMethod2: TCrossHttpRouterMethod2): ICrossHttpServer; overload; + + /// + /// 注册DELETE路由(请求处理函数) + /// + /// + /// 请求路径, 支持正则表达式, * 表示处理全部请求路径,
例如: + /// /path/:param1/:param2(\d+)|/path/:param
+ /// + /// + /// 路由处理匿名函数 + /// + /// + /// + /// + /// 路由严格按照注册时的顺序被调用, 所以如果在注册了AMethod=*, + /// APath=*的路由之后,再注册的其它路由将不会被调用. 所以强烈建议把 "* 路由" 放到最后注册. + /// + /// + /// 路由中的正则表达式用法与node.js express相同 + /// + /// + /// + function Delete(const APath: string; ARouterProc: TCrossHttpRouterProc): ICrossHttpServer; overload; + function Delete(const APath: string; ARouterProc2: TCrossHttpRouterProc2): ICrossHttpServer; overload; + + /// + /// 注册DELETE路由(请求处理函数) + /// + /// + /// 请求路径, 支持正则表达式, * 表示处理全部请求路径,
例如: + /// /path/:param1/:param2(\d+)|/path/:param + /// + /// + /// 路由处理方法 + /// + /// + /// + /// + /// 路由严格按照注册时的顺序被调用, 所以如果在注册了AMethod=*, + /// APath=*的路由之后,再注册的其它路由将不会被调用. 所以强烈建议把 "* 路由" 放到最后注册. + /// + /// + /// 路由中的正则表达式用法与node.js express相同 + /// + /// + /// + function Delete(const APath: string; ARouterMethod: TCrossHttpRouterMethod): ICrossHttpServer; overload; + function Delete(const APath: string; ARouterMethod2: TCrossHttpRouterMethod2): ICrossHttpServer; overload; + + /// + /// 注册全部请求方式路由(请求处理函数) + /// + /// + /// 请求路径, 支持正则表达式, * 表示处理全部请求路径,
例如: + /// /path/:param1/:param2(\d+)|/path/:param + /// + /// + /// 路由处理匿名函数 + /// + /// + /// + /// + /// 路由严格按照注册时的顺序被调用, 所以如果在注册了AMethod=*, + /// APath=*的路由之后,再注册的其它路由将不会被调用. 所以强烈建议把 "* 路由" 放到最后注册. + /// + /// + /// 路由中的正则表达式用法与node.js express相同 + /// + /// + /// + function All(const APath: string; ARouterProc: TCrossHttpRouterProc): ICrossHttpServer; overload; + function All(const APath: string; ARouterProc2: TCrossHttpRouterProc2): ICrossHttpServer; overload; + + /// + /// 注册全部请求方式路由(请求处理函数) + /// + /// + /// 请求路径, 支持正则表达式, * 表示处理全部请求路径,
例如: + /// /path/:param1/:param2(\d+)|/path/:param + /// + /// + /// 路由处理方法 + /// + /// + /// + /// + /// 路由严格按照注册时的顺序被调用, 所以如果在注册了AMethod=*, + /// APath=*的路由之后,再注册的其它路由将不会被调用. 所以强烈建议把 "* 路由" 放到最后注册. + /// + /// + /// 路由中的正则表达式用法与node.js express相同 + /// + /// + /// + function All(const APath: string; ARouterMethod: TCrossHttpRouterMethod): ICrossHttpServer; overload; + function All(const APath: string; ARouterMethod2: TCrossHttpRouterMethod2): ICrossHttpServer; overload; + + /// + /// 注册静态文件路由 + /// + /// + /// 请求路径 + /// + /// + /// 静态文件目录, 该目录及子目录下的文件都将作为静态文件返回 + /// + function &Static(const APath, ALocalStaticDir: string): ICrossHttpServer; + + /// + /// 注册文件列表路由 + /// + /// + /// 请求路径 + /// + /// + /// 本地文件目录 + /// + function Dir(const APath, ALocalDir: string): ICrossHttpServer; + + /// + /// 注册含有默认首页文件的静态文件路由 + /// + /// + /// 请求路径 + /// + /// + /// 含有默认首页文件的本地目录 + /// + /// + /// 默认的首页文件,按顺序选择,先找到哪个就使用哪个 + /// + function Index(const APath, ALocalDir: string; const ADefIndexFiles: TArray): ICrossHttpServer; + + /// + /// 删除指定路由 + /// + function RemoveRouter(const AMethod, APath: string): ICrossHttpServer; + + /// + /// 清除所有路由 + /// + function ClearRouter: ICrossHttpServer; + + /// + /// 锁定并返回路由列表 + /// + function LockRouters: TCrossHttpRouters; + + /// + /// 解锁路由列表 + /// + procedure UnlockRouters; + + /// + /// 锁定并返回中间件列表 + /// + function LockMiddlewares: TCrossHttpRouters; + + /// + /// 解锁中间件列表 + /// + procedure UnlockMiddlewares; + + /// + /// 上传文件保存路径 + /// + /// + /// 用于保存multipart/form-data上传的文件 + /// + property StoragePath: string read GetStoragePath write SetStoragePath; + + /// + /// 对象释放时自动删除上传的文件 + /// + property AutoDeleteFiles: Boolean read GetAutoDeleteFiles write SetAutoDeleteFiles; + + /// + /// 最大允许HEADER的数据尺寸 + /// + /// + /// > 0, 限制HEADER尺寸 + /// + /// + /// <= 0, 不限制 + /// + /// + /// + property MaxHeaderSize: Int64 read GetMaxHeaderSize write SetMaxHeaderSize; + + /// + /// 最大允许POST的数据尺寸 + /// + /// + /// > 0, 限制上传数据尺寸 + /// + /// + /// <= 0, 不限制 + /// + /// + /// + property MaxPostDataSize: Int64 read GetMaxPostDataSize write SetMaxPostDataSize; + + /// + /// 是否开启压缩 + /// + /// + /// 开启压缩后, 发往客户端的数据将会进行压缩处理 + /// + property Compressible: Boolean read GetCompressible write SetCompressible; + + /// + /// 最小允许压缩的数据尺寸 + /// + /// + /// + /// + /// 如果设置值大于0, 则只有Body数据尺寸大于等于该值才会进行压缩 + /// + /// + /// 如果设置值小于等于0, 则无视Body数据尺寸, 始终进行压缩 + /// + /// + /// 由于数据是分块压缩发送, 所以数据无论多大都不会占用更多的资源, 也就不需要限制最大压缩尺寸了 + /// + /// + /// 目前支持的压缩方式: gzip, deflate + /// + /// + /// + property MinCompressSize: Int64 read GetMinCompressSize write SetMinCompressSize; + + /// + /// Sessions接口对象 + /// + /// + /// 通过它管理所有Session, 如果不设置则Session功能将不会被启用 + /// + property Sessions: ISessions read GetSessions write SetSessions; + + /// + /// + /// SessionID在Cookie中存储的名称 + /// + /// + /// + /// 如果设置为空, 则Session功能将不会被启用 + /// + property SessionIDCookieName: string read GetSessionIDCookieName write SetSessionIDCookieName; + + property OnRequest: TCrossHttpRequestEvent read GetOnRequest write SetOnRequest; + property OnRequestException: TCrossHttpRequestExceptionEvent read GetOnRequestException write SetOnRequestException; + property OnPostDataBegin: TCrossHttpConnEvent read GetOnPostDataBegin write SetOnPostDataBegin; + property OnPostData: TCrossHttpDataEvent read GetOnPostData write SetOnPostData; + property OnPostDataEnd: TCrossHttpConnEvent read GetOnPostDataEnd write SetOnPostDataEnd; + end; + + TCrossHttpConnection = class({$IFDEF __CROSS_SSL__}TCrossSslConnection{$ELSE}TCrossConnection{$ENDIF}, ICrossHttpConnection) + private + FRequest: ICrossHttpRequest; + FResponse: ICrossHttpResponse; + + function GetRequest: ICrossHttpRequest; + function GetResponse: ICrossHttpResponse; + function GetServer: ICrossHttpServer; + public + constructor Create(AOwner: ICrossSocket; AClientSocket: THandle; + AConnectType: TConnectType); override; + + property Request: ICrossHttpRequest read GetRequest; + property Response: ICrossHttpResponse read GetResponse; + property Server: ICrossHttpServer read GetServer; + end; + + TCrossHttpRequest = class(TInterfacedObject, ICrossHttpRequest) + private + function GetConnection: ICrossHttpConnection; + function GetRawRequestText: string; + function GetRawPathAndParams: string; + function GetMethod: string; + function GetPath: string; + function GetVersion: string; + function GetHeader: THttpHeader; + function GetCookies: TRequestCookies; + function GetSession: ISession; + function GetParams: THttpUrlParams; + function GetQuery: THttpUrlParams; + function GetBody: TObject; + function GetBodyType: TBodyType; + function GetKeepAlive: Boolean; + function GetAccept: string; + function GetAcceptEncoding: string; + function GetAcceptLanguage: string; + function GetReferer: string; + function GetUserAgent: string; + function GetIfModifiedSince: TDateTime; + function GetIfNoneMatch: string; + function GetRange: string; + function GetIfRange: string; + function GetAuthorization: string; + function GetXForwardedFor: string; + function GetContentLength: Int64; + function GetHostName: string; + function GetHostPort: Word; + function GetContentType: string; + function GetContentEncoding: string; + function GetRequestBoundary: string; + function GetRequestCmdLine: string; + function GetRequestConnection: string; + function GetTransferEncoding: string; + function GetIsChunked: Boolean; + function GetIsMultiPartFormData: Boolean; + function GetIsUrlEncodedFormData: Boolean; + function GetPostDataSize: Int64; + private type + TCrossHttpParseState = (psHeader, psPostData, psChunkSize, psChunkData, psChunkEnd, psDone); + private + FParseState: TCrossHttpParseState; + CR, LF: Integer; + FChunkSizeStream: TBytesStream; + FChunkSize, FChunkLeftSize: Integer; + + FRawRequest: TBytesStream; + FRawRequestText: string; + FMethod, FPath, FVersion: string; + FRawPath, FRawParamsText, FRawPathAndParams: string; + FHttpVerNum: Integer; + FKeepAlive: Boolean; + FAccept: string; + FReferer: string; + FAcceptLanguage: string; + FAcceptEncoding: string; + FUserAgent: string; + FIfModifiedSince: TDateTime; + FIfNoneMatch: string; + FRange: string; + FIfRange: string; + FAuthorization: string; + FXForwardedFor: string; + FContentLength: Int64; + FHostName: string; + FHostPort: Word; + + FPostDataSize: Int64; + + FRequestCmdLine: string; + FContentType: string; + FRequestBoundary: string; + FTransferEncoding: string; + FContentEncoding: string; + FRequestCookies: string; + FRequestHost: string; + FRequestConnection: string; + protected + function ParseRequestData: Boolean; virtual; + private + // Request 是 Connection 的子对象, 它的生命周期跟随 Connection + // 这里使用弱引用, 不增加 Connection 的引用计数, 避免循环引用造成接口对象无法自动释放 + [unsafe]FConnection: ICrossHttpConnection; + FHeader: THttpHeader; + FCookies: TRequestCookies; + FSession: ISession; + FParams: THttpUrlParams; + FQuery: THttpUrlParams; + FBody: TObject; + FBodyType: TBodyType; + public + constructor Create(AConnection: ICrossHttpConnection); + destructor Destroy; override; + + procedure Reset; + + property Connection: ICrossHttpConnection read GetConnection; + property RawRequestText: string read GetRawRequestText; + property RawPathAndParams: string read GetRawPathAndParams; + property Method: string read GetMethod; + property Path: string read GetPath; + property Version: string read GetVersion; + property Header: THttpHeader read GetHeader; + property Cookies: TRequestCookies read GetCookies; + property Session: ISession read GetSession; + property Params: THttpUrlParams read GetParams; + property Query: THttpUrlParams read GetQuery; + property Body: TObject read GetBody; + property BodyType: TBodyType read GetBodyType; + property KeepAlive: Boolean read GetKeepAlive; + property Accept: string read GetAccept; + property AcceptEncoding: string read GetAcceptEncoding; + property AcceptLanguage: string read GetAcceptLanguage; + property Referer: string read GetReferer; + property UserAgent: string read GetUserAgent; + property IfModifiedSince: TDateTime read GetIfModifiedSince; + property IfNoneMatch: string read GetIfNoneMatch; + property Range: string read GetRange; + property IfRange: string read GetIfRange; + property Authorization: string read GetAuthorization; + property XForwardedFor: string read GetXForwardedFor; + property ContentLength: Int64 read GetContentLength; + property HostName: string read GetHostName; + property HostPort: Word read GetHostPort; + property ContentType: string read GetContentType; + + property RequestCmdLine: string read GetRequestCmdLine; + + property RequestBoundary: string read GetRequestBoundary; + property TransferEncoding: string read GetTransferEncoding; + property ContentEncoding: string read GetContentEncoding; + property RequestConnection: string read GetRequestConnection; + property IsChunked: Boolean read GetIsChunked; + property IsMultiPartFormData: Boolean read GetIsMultiPartFormData; + property IsUrlEncodedFormData: Boolean read GetIsUrlEncodedFormData; + property PostDataSize: Int64 read GetPostDataSize; + end; + + TCrossHttpResponse = class(TInterfacedObject, ICrossHttpResponse) + public const + SND_BUF_SIZE = TCrossConnection.SND_BUF_SIZE; + private + // Response 是 Connection 的子对象, 它的生命周期跟随 Connection + // 这里使用弱引用, 不增加 Connection 的引用计数, 避免循环引用造成接口对象无法自动释放 + [unsafe]FConnection: ICrossHttpConnection; + FStatusCode: Integer; + FHeader: THttpHeader; + FCookies: TResponseCookies; + FSendStatus: Integer; + + procedure Reset; + + function GetConnection: ICrossHttpConnection; + function GetRequest: ICrossHttpRequest; + function GetStatusCode: Integer; + procedure SetStatusCode(Value: Integer); + function GetContentType: string; + procedure SetContentType(const Value: string); + function GetLocation: string; + procedure SetLocation(const Value: string); + function GetHeader: THttpHeader; + function GetCookies: TResponseCookies; + function GetSent: Boolean; + + function _CreateHeader(const ABodySize: Int64; AChunked: Boolean): TBytes; + + {$region '内部: 基础发送方法'} + procedure _Send(ASource: TFunc; ACallback: TProc = nil); overload; + procedure _Send(AHeaderSource, ABodySource: TFunc; ACallback: TProc = nil); overload; + {$endregion} + + function _CheckCompress(const ABodySize: Int64; var ACompressType: TCompressType): Boolean; + procedure _AdjustOffsetCount(const ABodySize: NativeInt; var AOffset, ACount: NativeInt); overload; + procedure _AdjustOffsetCount(const ABodySize: Int64; var AOffset, ACount: Int64); overload; + + {$region '压缩发送'} + procedure SendZCompress(const AChunkSource: TFunc; ACompressType: TCompressType; ACallback: TProc = nil); overload; + procedure SendZCompress(const ABody; ACount: NativeInt; ACompressType: TCompressType; ACallback: TProc = nil); overload; + procedure SendZCompress(const ABody: TBytes; AOffset, ACount: NativeInt; ACompressType: TCompressType; ACallback: TProc = nil); overload; + procedure SendZCompress(const ABody: TBytes; ACompressType: TCompressType; ACallback: TProc = nil); overload; + procedure SendZCompress(const ABody: TStream; const AOffset, ACount: Int64; ACompressType: TCompressType; ACallback: TProc = nil); overload; + procedure SendZCompress(const ABody: TStream; ACompressType: TCompressType; ACallback: TProc = nil); overload; + procedure SendZCompress(const ABody: string; ACompressType: TCompressType; ACallback: TProc = nil); overload; + {$endregion} + + {$region '不压缩发送'} + procedure SendNoCompress(const AChunkSource: TFunc; ACallback: TProc = nil); overload; + procedure SendNoCompress(const ABody; ACount: NativeInt; ACallback: TProc = nil); overload; + procedure SendNoCompress(const ABody: TBytes; AOffset, ACount: NativeInt; ACallback: TProc = nil); overload; + procedure SendNoCompress(const ABody: TBytes; ACallback: TProc = nil); overload; + procedure SendNoCompress(const ABody: TStream; const AOffset, ACount: Int64; ACallback: TProc = nil); overload; + procedure SendNoCompress(const ABody: TStream; ACallback: TProc = nil); overload; + procedure SendNoCompress(const ABody: string; ACallback: TProc = nil); overload; + {$endregion} + + {$region '常规方法'} + procedure Send(const ABody; ACount: NativeInt; ACallback: TProc = nil); overload; + procedure Send(const ABody: TBytes; AOffset, ACount: NativeInt; ACallback: TProc = nil); overload; + procedure Send(const ABody: TBytes; ACallback: TProc = nil); overload; + procedure Send(const ABody: TStream; const AOffset, ACount: Int64; ACallback: TProc = nil); overload; + procedure Send(const ABody: TStream; ACallback: TProc = nil); overload; + procedure Send(const ABody: string; ACallback: TProc = nil); overload; + + procedure Json(const AJson: string; ACallback: TProc = nil); + + procedure SendFile(const AFileName: string; ACallback: TProc = nil); + procedure Download(const AFileName: string; ACallback: TProc = nil); + procedure SendStatus(AStatusCode: Integer; const ADescription: string; + ACallback: TProc = nil); overload; + procedure SendStatus(AStatusCode: Integer; + ACallback: TProc = nil); overload; + procedure Redirect(const AUrl: string; ACallback: TProc = nil); + procedure Attachment(const AFileName: string); + {$endregion} + public + constructor Create(AConnection: ICrossHttpConnection); + destructor Destroy; override; + end; + + TCrossHttpRouter = class(TInterfacedObject, ICrossHttpRouter) + private + FMethod, FPath: string; + FRouterProc: TCrossHttpRouterProc; + FRouterMethod: TCrossHttpRouterMethod; + FRouterProc2: TCrossHttpRouterProc2; + FRouterMethod2: TCrossHttpRouterMethod2; + FMethodPattern, FPathPattern: string; + FPathParamKeys: TArray; + FMethodRegEx, FPathRegEx: TPerlRegEx; // 直接使用TPerlRegEx比使用TRegEx速度快1倍 + FRegExLock: TObject; + + function MakeMethodPattern(const AMethod: string): string; + function MakePathPattern(const APath: string; var AKeys: TArray): string; + procedure RemakePattern; + + function GetMethod: string; + function GetPath: string; + public + constructor Create(const AMethod, APath: string; + ARouterProc: TCrossHttpRouterProc; + ARouterMethod: TCrossHttpRouterMethod; + ARouterProc2: TCrossHttpRouterProc2; + ARouterMethod2: TCrossHttpRouterMethod2); + destructor Destroy; override; + + function IsMatch(ARequest: ICrossHttpRequest): Boolean; + procedure Execute(ARequest: ICrossHttpRequest; AResponse: ICrossHttpResponse; var AHandled: Boolean); + end; + + TCrossHttpServer = class({$IFDEF __CROSS_SSL__}TCrossSslServer{$ELSE}TCrossServer{$ENDIF}, ICrossHttpServer) + private const + HTTP_METHOD_COUNT = 16; + HTTP_METHODS: array [0..HTTP_METHOD_COUNT-1] of string = ( + 'GET', 'POST', 'PUT', 'DELETE', + 'HEAD', 'OPTIONS', 'TRACE', 'CONNECT', + 'PATCH', 'COPY', 'LINK', 'UNLINK', + 'PURGE', 'LOCK', 'UNLOCK', 'PROPFIND'); + SESSIONID_COOKIE_NAME = 'cross_sessionid'; + private + FMethodTags: array [0..HTTP_METHOD_COUNT-1] of TBytes; + FStoragePath: string; + FAutoDeleteFiles: Boolean; + FMaxPostDataSize: Int64; + FMaxHeaderSize: Int64; + FMinCompressSize: Integer; + FSessionIDCookieName: string; + FRouters: TCrossHttpRouters; + FRoutersLock: TMultiReadExclusiveWriteSynchronizer; + FMiddlewares: TCrossHttpRouters; + FMiddlewaresLock: TMultiReadExclusiveWriteSynchronizer; + FSessions: ISessions; + FOnRequest: TCrossHttpRequestEvent; + FOnRequestException: TCrossHttpRequestExceptionEvent; + FOnPostDataBegin: TCrossHttpConnEvent; + FOnPostData: TCrossHttpDataEvent; + FOnPostDataEnd: TCrossHttpConnEvent; + FCompressible: Boolean; + + function IsValidHttpRequest(ABuf: Pointer; ALen: Integer): Boolean; + procedure ParseRecvData(AConnection: ICrossConnection; ABuf: Pointer; ALen: Integer); + + function RegisterRouter(const AMethod, APath: string; + ARouterProc: TCrossHttpRouterProc; + ARouterMethod: TCrossHttpRouterMethod; + ARouterProc2: TCrossHttpRouterProc2; + ARouterMethod2: TCrossHttpRouterMethod2): TCrossHttpServer; + function RegisterMiddleware(const AMethod, APath: string; + AMiddlewareProc: TCrossHttpRouterProc; + AMiddlewareMethod: TCrossHttpRouterMethod; + AMiddlewareProc2: TCrossHttpRouterProc2; + AMiddlewareMethod2: TCrossHttpRouterMethod2): TCrossHttpServer; + private + function GetStoragePath: string; + function GetAutoDeleteFiles: Boolean; + function GetMaxHeaderSize: Int64; + function GetMaxPostDataSize: Int64; + function GetCompressible: Boolean; + function GetMinCompressSize: Int64; + function GetSessions: ISessions; + function GetSessionIDCookieName: string; + function GetOnRequest: TCrossHttpRequestEvent; + function GetOnRequestException: TCrossHttpRequestExceptionEvent; + function GetOnPostDataBegin: TCrossHttpConnEvent; + function GetOnPostData: TCrossHttpDataEvent; + function GetOnPostDataEnd: TCrossHttpConnEvent; + + procedure SetStoragePath(const Value: string); + procedure SetAutoDeleteFiles(const Value: Boolean); + procedure SetMaxHeaderSize(const Value: Int64); + procedure SetMaxPostDataSize(const Value: Int64); + procedure SetCompressible(const Value: Boolean); + procedure SetMinCompressSize(const Value: Int64); + procedure SetSessions(const Value: ISessions); + procedure SetSessionIDCookieName(const Value: string); + procedure SetOnRequest(const Value: TCrossHttpRequestEvent); + procedure SetOnRequestException(const Value: TCrossHttpRequestExceptionEvent); + procedure SetOnPostDataBegin(const Value: TCrossHttpConnEvent); + procedure SetOnPostData(const Value: TCrossHttpDataEvent); + procedure SetOnPostDataEnd(const Value: TCrossHttpConnEvent); + protected + function CreateConnection(AOwner: ICrossSocket; AClientSocket: THandle; + AConnectType: TConnectType): ICrossConnection; override; + + function CreateRouter(const AMethod, APath: string; + ARouterProc: TCrossHttpRouterProc; ARouterMethod: TCrossHttpRouterMethod; + ARouterProc2: TCrossHttpRouterProc2; ARouterMethod2: TCrossHttpRouterMethod2): ICrossHttpRouter; virtual; + + procedure LogicReceived(AConnection: ICrossConnection; ABuf: Pointer; ALen: Integer); override; + protected + procedure TriggerPostDataBegin(AConnection: ICrossHttpConnection); virtual; + procedure TriggerPostData(AConnection: ICrossHttpConnection; + const ABuf: Pointer; ALen: Integer); virtual; + procedure TriggerPostDataEnd(AConnection: ICrossHttpConnection); virtual; + + // 处理请求 + procedure DoOnRequest(AConnection: ICrossHttpConnection); virtual; + public + constructor Create(AIoThreads: Integer); override; + destructor Destroy; override; + + function Use(const AMethod, APath: string; + AMiddlewareProc: TCrossHttpRouterProc): ICrossHttpServer; overload; + + function Use(const AMethod, APath: string; + AMiddlewareProc2: TCrossHttpRouterProc2): ICrossHttpServer; overload; + + function Use(const AMethod, APath: string; + AMiddlewareMethod: TCrossHttpRouterMethod): ICrossHttpServer; overload; + + function Use(const AMethod, APath: string; + AMiddlewareMethod2: TCrossHttpRouterMethod2): ICrossHttpServer; overload; + + function Use(const APath: string; + AMiddlewareProc: TCrossHttpRouterProc): ICrossHttpServer; overload; + + function Use(const APath: string; + AMiddlewareProc2: TCrossHttpRouterProc2): ICrossHttpServer; overload; + + function Use(const APath: string; + AMiddlewareMethod: TCrossHttpRouterMethod): ICrossHttpServer; overload; + + function Use(const APath: string; + AMiddlewareMethod2: TCrossHttpRouterMethod2): ICrossHttpServer; overload; + + function Use(AMiddlewareProc: TCrossHttpRouterProc): ICrossHttpServer; overload; + function Use(AMiddlewareProc2: TCrossHttpRouterProc2): ICrossHttpServer; overload; + function Use(AMiddlewareMethod: TCrossHttpRouterMethod): ICrossHttpServer; overload; + function Use(AMiddlewareMethod2: TCrossHttpRouterMethod2): ICrossHttpServer; overload; + + function Route(const AMethod, APath: string; ARouterProc: TCrossHttpRouterProc): ICrossHttpServer; overload; + function Route(const AMethod, APath: string; ARouterProc2: TCrossHttpRouterProc2): ICrossHttpServer; overload; + function Route(const AMethod, APath: string; ARouterMethod: TCrossHttpRouterMethod): ICrossHttpServer; overload; + function Route(const AMethod, APath: string; ARouterMethod2: TCrossHttpRouterMethod2): ICrossHttpServer; overload; + + function Get(const APath: string; ARouterProc: TCrossHttpRouterProc): ICrossHttpServer; overload; + function Get(const APath: string; ARouterProc2: TCrossHttpRouterProc2): ICrossHttpServer; overload; + function Get(const APath: string; ARouterMethod: TCrossHttpRouterMethod): ICrossHttpServer; overload; + function Get(const APath: string; ARouterMethod2: TCrossHttpRouterMethod2): ICrossHttpServer; overload; + + function Put(const APath: string; ARouterProc: TCrossHttpRouterProc): ICrossHttpServer; overload; + function Put(const APath: string; ARouterProc2: TCrossHttpRouterProc2): ICrossHttpServer; overload; + function Put(const APath: string; ARouterMethod: TCrossHttpRouterMethod): ICrossHttpServer; overload; + function Put(const APath: string; ARouterMethod2: TCrossHttpRouterMethod2): ICrossHttpServer; overload; + + function Post(const APath: string; ARouterProc: TCrossHttpRouterProc): ICrossHttpServer; overload; + function Post(const APath: string; ARouterProc2: TCrossHttpRouterProc2): ICrossHttpServer; overload; + function Post(const APath: string; ARouterMethod: TCrossHttpRouterMethod): ICrossHttpServer; overload; + function Post(const APath: string; ARouterMethod2: TCrossHttpRouterMethod2): ICrossHttpServer; overload; + + function Delete(const APath: string; ARouterProc: TCrossHttpRouterProc): ICrossHttpServer; overload; + function Delete(const APath: string; ARouterProc2: TCrossHttpRouterProc2): ICrossHttpServer; overload; + function Delete(const APath: string; ARouterMethod: TCrossHttpRouterMethod): ICrossHttpServer; overload; + function Delete(const APath: string; ARouterMethod2: TCrossHttpRouterMethod2): ICrossHttpServer; overload; + + function All(const APath: string; ARouterProc: TCrossHttpRouterProc): ICrossHttpServer; overload; + function All(const APath: string; ARouterProc2: TCrossHttpRouterProc2): ICrossHttpServer; overload; + function All(const APath: string; ARouterMethod: TCrossHttpRouterMethod): ICrossHttpServer; overload; + function All(const APath: string; ARouterMethod2: TCrossHttpRouterMethod2): ICrossHttpServer; overload; + + function &Static(const APath, ALocalStaticDir: string): ICrossHttpServer; + function Dir(const APath, ALocalDir: string): ICrossHttpServer; + function Index(const APath, ALocalDir: string; const ADefIndexFiles: TArray): ICrossHttpServer; + + function RemoveRouter(const AMethod, APath: string): ICrossHttpServer; + function ClearRouter: ICrossHttpServer; + + function LockRouters: TCrossHttpRouters; + procedure UnlockRouters; + + function LockMiddlewares: TCrossHttpRouters; + procedure UnlockMiddlewares; + + property StoragePath: string read GetStoragePath write SetStoragePath; + property AutoDeleteFiles: Boolean read GetAutoDeleteFiles write SetAutoDeleteFiles; + property MaxHeaderSize: Int64 read GetMaxHeaderSize write SetMaxHeaderSize; + property MaxPostDataSize: Int64 read GetMaxPostDataSize write SetMaxPostDataSize; + property Compressible: Boolean read GetCompressible write SetCompressible; + property MinCompressSize: Int64 read GetMinCompressSize write SetMinCompressSize; + property Sessions: ISessions read GetSessions write SetSessions; + property SessionIDCookieName: string read GetSessionIDCookieName write SetSessionIDCookieName; + + property OnRequest: TCrossHttpRequestEvent read GetOnRequest write SetOnRequest; + property OnRequestException: TCrossHttpRequestExceptionEvent read GetOnRequestException write SetOnRequestException; + property OnPostDataBegin: TCrossHttpConnEvent read GetOnPostDataBegin write SetOnPostDataBegin; + property OnPostData: TCrossHttpDataEvent read GetOnPostData write SetOnPostData; + property OnPostDataEnd: TCrossHttpConnEvent read GetOnPostDataEnd write SetOnPostDataEnd; + end; + +implementation + +uses + {$IFDEF MSWINDOWS} + Winapi.Windows, + {$ENDIF} + Utils.RegEx, Utils.Utils, + Net.CrossHttpRouter; + + +{ ECrossHttpException } + +constructor ECrossHttpException.Create(const AMessage: string; + AStatusCode: Integer); +begin + inherited Create(AMessage); + FStatusCode := AStatusCode; +end; + +constructor ECrossHttpException.CreateFmt(const AMessage: string; + const AArgs: array of const; AStatusCode: Integer); +begin + inherited CreateFmt(AMessage, AArgs); + FStatusCode := AStatusCode; +end; + +{ TCrossHttpConnection } + +constructor TCrossHttpConnection.Create(AOwner: ICrossSocket; + AClientSocket: THandle; AConnectType: TConnectType); +var + LConnection: ICrossHttpConnection; +begin + inherited; + + LConnection := Self; + + FRequest := TCrossHttpRequest.Create(LConnection); + FResponse := TCrossHttpResponse.Create(LConnection); +end; + +function TCrossHttpConnection.GetRequest: ICrossHttpRequest; +begin + Result := FRequest; +end; + +function TCrossHttpConnection.GetResponse: ICrossHttpResponse; +begin + Result := FResponse; +end; + +function TCrossHttpConnection.GetServer: ICrossHttpServer; +begin + Result := Owner as ICrossHttpServer; +end; + +{ TCrossHttpRouter } + +constructor TCrossHttpRouter.Create(const AMethod, APath: string; + ARouterProc: TCrossHttpRouterProc; ARouterMethod: TCrossHttpRouterMethod; + ARouterProc2: TCrossHttpRouterProc2; ARouterMethod2: TCrossHttpRouterMethod2); +begin + FMethod := AMethod; + FPath := APath; + FRouterProc := ARouterProc; + FRouterMethod := ARouterMethod; + FRouterProc2 := ARouterProc2; + FRouterMethod2 := ARouterMethod2; + + FMethodRegEx := TPerlRegEx.Create; + FMethodRegEx.Options := [preCaseLess]; + + FPathRegEx := TPerlRegEx.Create; + FPathRegEx.Options := [preCaseLess]; + + FRegExLock := TObject.Create; + + RemakePattern; +end; + +destructor TCrossHttpRouter.Destroy; +begin + TMonitor.Enter(FRegExLock); + try + FreeAndNil(FMethodRegEx); + FreeAndNil(FPathRegEx); + finally + TMonitor.Exit(FRegExLock); + end; + FreeAndNil(FRegExLock); + + inherited; +end; + +function TCrossHttpRouter.MakeMethodPattern(const AMethod: string): string; +var + LPattern: string; +begin + LPattern := AMethod; + + // 通配符*转正则表达式 + LPattern := TRegEx.Replace(LPattern, '(?): string; +var + LPattern: string; + LKeys: TArray; +begin + LKeys := []; + LPattern := APath; + + // 最后增加 /? + if APath.EndsWith('/') then + LPattern := LPattern + '?' + else + LPattern := LPattern + '/?'; + + // 将 /( 替换成 /(?: + LPattern := TRegEx.Replace(LPattern, '\/\(', '/(?:'); + + // 将 / . 替换成 \/ \. + LPattern := TRegEx.Replace(LPattern, '([\/\.])', '\\$1'); + + // 提取形如 :keyname 的参数名称 + // 可以在参数后面增加正则限定参数 :number(\d+), :word(\w+) + LPattern := TRegEx.Replace(LPattern, '(\\\/)?(\\\.)?:(\w+)(\(.*?\))?(\*)?(\?)?', + function(const Match: TMatch): string + var + LSlash, LFormat, LKey, LCapture, LStar, LOptional: string; + begin + if not Match.Success then Exit(''); + + if (Match.Groups.Count > 1) then + LSlash := Match.Groups[1].Value + else + LSlash := ''; + if (Match.Groups.Count > 2) then + LFormat := Match.Groups[2].Value + else + LFormat := ''; + if (Match.Groups.Count > 3) then + LKey := Match.Groups[3].Value + else + LKey := ''; + if (Match.Groups.Count > 4) then + LCapture := Match.Groups[4].Value + else + LCapture := ''; + if (Match.Groups.Count > 5) then + LStar := Match.Groups[5].Value + else + LStar := ''; + if (Match.Groups.Count > 6) then + LOptional := Match.Groups[6].Value + else + LOptional := ''; + + if (LCapture = '') then + LCapture := '([^\\/' + LFormat + ']+?)'; + + Result := ''; + if (LOptional = '') then + Result := Result + LSlash; + Result := Result + '(?:' + LFormat; + if (LOptional <> '') then + Result := Result + LSlash; + Result := Result + LCapture; + if (LStar <> '') then + Result := Result + '((?:[\\/' + LFormat + '].+?)?)'; + Result := Result + ')' + LOptional; + + LKeys := LKeys + [LKey]; + end); + + // 通配符*转正则表达式 + LPattern := TRegEx.Replace(LPattern, '(? '/') then + FPath := '/' + FPath; + + FMethodPattern := MakeMethodPattern(FMethod); + FPathPattern := MakePathPattern(FPath, FPathParamKeys); + + TMonitor.Enter(FRegExLock); + try + FMethodRegEx.RegEx := FMethodPattern; + FPathRegEx.RegEx := FPathPattern; + finally + TMonitor.Exit(FRegExLock); + end; +end; + +function TCrossHttpRouter.IsMatch(ARequest: ICrossHttpRequest): Boolean; + function _IsMatchMethod: Boolean; + begin + // Method中不包括参数, 使用SameText辅助加速 + if (FMethod = '*') or SameText(ARequest.Method, FMethod) then Exit(True); + + FMethodRegEx.Subject := ARequest.Method; + Result := FMethodRegEx.Match; + end; + + function _IsMatchPath: Boolean; + var + I: Integer; + begin + // Path中不包括参数时, 使用SameText辅助加速 + if (FPath = '*') or ((Length(FPathParamKeys) = 0) and SameText(ARequest.Path, FPath)) then Exit(True); + + FPathRegEx.Subject := ARequest.Path; + Result := FPathRegEx.Match; + if not Result then Exit; + + // 将Path中的参数解析出来保存到Request.Params中 + // 注意: TPerlRegEx.GroupCount 是实际 GroupCount - 1 + for I := 1 to FPathRegEx.GroupCount do + ARequest.Params[FPathParamKeys[I - 1]] := FPathRegEx.Groups[I]; + end; +begin + ARequest.Params.Clear; + + TMonitor.Enter(FRegExLock); + try + Result := _IsMatchMethod and _IsMatchPath; + finally + TMonitor.Exit(FRegExLock); + end; +end; + +procedure TCrossHttpRouter.Execute(ARequest: ICrossHttpRequest; AResponse: ICrossHttpResponse; var AHandled: Boolean); +begin + if Assigned(FRouterProc) then + begin + FRouterProc(ARequest, AResponse); + end else + if Assigned(FRouterMethod) then + begin + FRouterMethod(ARequest, AResponse); + end else + if Assigned(FRouterProc2) then + begin + FRouterProc2(ARequest, AResponse, AHandled); + end else + if Assigned(FRouterMethod2) then + begin + FRouterMethod2(ARequest, AResponse, AHandled); + end; +end; + +function TCrossHttpRouter.GetMethod: string; +begin + Result := FMethod; +end; + +function TCrossHttpRouter.GetPath: string; +begin + Result := FPath; +end; + +{ TCrossHttpServer } + +function TCrossHttpServer.All(const APath: string; + ARouterProc: TCrossHttpRouterProc): ICrossHttpServer; +begin + Result := Route('*', APath, ARouterProc); +end; + +function TCrossHttpServer.All(const APath: string; + ARouterProc2: TCrossHttpRouterProc2): ICrossHttpServer; +begin + Result := Route('*', APath, ARouterProc2); +end; + +function TCrossHttpServer.All(const APath: string; + ARouterMethod: TCrossHttpRouterMethod): ICrossHttpServer; +begin + Result := Route('*', APath, ARouterMethod); +end; + +function TCrossHttpServer.All(const APath: string; + ARouterMethod2: TCrossHttpRouterMethod2): ICrossHttpServer; +begin + Result := Route('*', APath, ARouterMethod2); +end; + +constructor TCrossHttpServer.Create(AIoThreads: Integer); +var + I: Integer; +begin + inherited Create(AIoThreads); + + FRouters := TCrossHttpRouters.Create; + FRoutersLock := TMultiReadExclusiveWriteSynchronizer.Create; + + FMiddlewares := TCrossHttpRouters.Create; + FMiddlewaresLock := TMultiReadExclusiveWriteSynchronizer.Create; + + for I := Low(FMethodTags) to High(FMethodTags) do + FMethodTags[I] := TEncoding.ANSI.GetBytes(HTTP_METHODS[I]); + + Port := 80; + Addr := ''; + + FCompressible := True; + FMinCompressSize := MIN_COMPRESS_SIZE; + FStoragePath := TPath.Combine(TUtils.AppPath, 'temp') + TPath.DirectorySeparatorChar; + FSessionIDCookieName := SESSIONID_COOKIE_NAME; +end; + +function TCrossHttpServer.CreateConnection(AOwner: ICrossSocket; + AClientSocket: THandle; AConnectType: TConnectType): ICrossConnection; +begin + Result := TCrossHttpConnection.Create(AOwner, AClientSocket, AConnectType); +end; + +function TCrossHttpServer.CreateRouter(const AMethod, APath: string; + ARouterProc: TCrossHttpRouterProc; ARouterMethod: TCrossHttpRouterMethod; + ARouterProc2: TCrossHttpRouterProc2; + ARouterMethod2: TCrossHttpRouterMethod2): ICrossHttpRouter; +begin + Result := TCrossHttpRouter.Create(AMethod, APath, + ARouterProc, ARouterMethod, + ARouterProc2, ARouterMethod2); +end; + +destructor TCrossHttpServer.Destroy; +begin + Stop; + + FRoutersLock.BeginWrite; + FreeAndNil(FRouters); + FRoutersLock.EndWrite; + FreeAndNil(FRoutersLock); + + FMiddlewaresLock.BeginWrite; + FreeAndNil(FMiddlewares); + FMiddlewaresLock.EndWrite; + FreeAndNil(FMiddlewaresLock); + + inherited Destroy; +end; + +function TCrossHttpServer.Dir(const APath, ALocalDir: string): ICrossHttpServer; +var + LReqPath: string; +begin + LReqPath := APath; + if not LReqPath.EndsWith('/') then + LReqPath := LReqPath + '/'; + LReqPath := LReqPath + '?:dir(*)'; + Result := Get(LReqPath, TNetCrossRouter.Dir(APath, ALocalDir, 'dir')); +end; + +function TCrossHttpServer.Delete(const APath: string; + ARouterProc: TCrossHttpRouterProc): ICrossHttpServer; +begin + Result := Route('DELETE', APath, ARouterProc); +end; + +function TCrossHttpServer.Delete(const APath: string; + ARouterProc2: TCrossHttpRouterProc2): ICrossHttpServer; +begin + Result := Route('DELETE', APath, ARouterProc2); +end; + +function TCrossHttpServer.Delete(const APath: string; + ARouterMethod: TCrossHttpRouterMethod): ICrossHttpServer; +begin + Result := Route('DELETE', APath, ARouterMethod); +end; + +function TCrossHttpServer.Delete(const APath: string; + ARouterMethod2: TCrossHttpRouterMethod2): ICrossHttpServer; +begin + Result := Route('DELETE', APath, ARouterMethod2); +end; + +procedure TCrossHttpServer.DoOnRequest(AConnection: ICrossHttpConnection); +var + LRequest: ICrossHttpRequest; + LResponse: ICrossHttpResponse; + LSessionID: string; + LHandled: Boolean; + LRouter, LMiddleware: ICrossHttpRouter; + LMiddlewares, LRouters: TArray; +begin + LRequest := AConnection.Request; + LResponse := AConnection.Response; + + try + {$region 'Session'} + if (FSessions <> nil) and (FSessionIDCookieName <> '') then + begin + LSessionID := LRequest.Cookies[FSessionIDCookieName]; + (LRequest as TCrossHttpRequest).FSession := FSessions.Sessions[LSessionID]; + if (LRequest.Session <> nil) and (LRequest.Session.SessionID <> LSessionID) then + begin + LSessionID := LRequest.Session.SessionID; + LResponse.Cookies.AddOrSet(FSessionIDCookieName, LSessionID, 0); + end; + end; + {$endregion} + + {$region '中间件'} + FMiddlewaresLock.BeginRead; + try + // 先将中间件保存到临时数组中 + // 然后再执行中间件 + // 这样做是为了减少加锁的时间 + LMiddlewares := FMiddlewares.ToArray; + finally + FMiddlewaresLock.EndRead; + end; + for LMiddleware in LMiddlewares do + begin + // 执行匹配的中间件 + if LMiddleware.IsMatch(LRequest) then + begin + // 中间件通常用于请求的预处理 + // 所以默认将 LHandled 置为 False, 以保证后续路由能被执行 + // 除非用户在中间件中明确指定了 LHandled := True, 表明该请求无需后续路由响应了 + LHandled := False; + LMiddleware.Execute(LRequest, LResponse, LHandled); + + // 如果已经发送了数据, 则后续的事件和路由响应都不需要执行了 + if LHandled or LResponse.Sent then Exit; + end; + end; + {$endregion} + + {$region '响应请求事件'} + if Assigned(FOnRequest) then + begin + LHandled := False; + FOnRequest(Self, LRequest, LResponse, LHandled); + + // 如果已经发送了数据, 则后续的事件和路由响应都不需要执行了 + if LHandled or LResponse.Sent then Exit; + end; + {$endregion} + + {$region '路由'} + FRoutersLock.BeginRead; + try + // 先将路由保存到临时数组中 + // 然后再执行路由 + // 这样做是为了减少加锁时间 + LRouters := FRouters.ToArray; + finally + FRoutersLock.EndRead; + end; + for LRouter in LRouters do + begin + // 执行匹配的路由 + if LRouter.IsMatch(LRequest) then + begin + // 路由用于响应请求 + // 所以默认将 LHandled 置为 True, 以保证不会有多个匹配的路由被执行 + // 除非用户在路由中明确指定了 LHandled := False, 表明该路由并没有 + // 完成请求响应, 还需要后续路由继续进行响应 + LHandled := True; + LRouter.Execute(LRequest, LResponse, LHandled); + + // 如果已经发送了数据, 则后续的事件和路由响应都不需要执行了 + if LHandled or LResponse.Sent then Exit; + end; + end; + {$endregion} + + // 如果该请求没有被任何中间件、事件、路由响应, 返回 404 + if not (LHandled or LResponse.Sent) then + LResponse.SendStatus(404); + except + on e: Exception do + begin + if Assigned(FOnRequestException) then + FOnRequestException(Self, LRequest, LResponse, e) + else if (e is ECrossHttpException) then + LResponse.SendStatus(ECrossHttpException(e).StatusCode, ECrossHttpException(e).Message) + else + LResponse.SendStatus(500, e.Message); + end; + end; +end; + +function TCrossHttpServer.Get(const APath: string; + ARouterProc: TCrossHttpRouterProc): ICrossHttpServer; +begin + Result := Route('GET', APath, ARouterProc); +end; + +function TCrossHttpServer.Get(const APath: string; + ARouterProc2: TCrossHttpRouterProc2): ICrossHttpServer; +begin + Result := Route('GET', APath, ARouterProc2); +end; + +function TCrossHttpServer.Get(const APath: string; + ARouterMethod: TCrossHttpRouterMethod): ICrossHttpServer; +begin + Result := Route('GET', APath, ARouterMethod); +end; + +function TCrossHttpServer.Get(const APath: string; + ARouterMethod2: TCrossHttpRouterMethod2): ICrossHttpServer; +begin + Result := Route('GET', APath, ARouterMethod2); +end; + +function TCrossHttpServer.GetAutoDeleteFiles: Boolean; +begin + Result := FAutoDeleteFiles; +end; + +function TCrossHttpServer.GetCompressible: Boolean; +begin + Result := FCompressible; +end; + +function TCrossHttpServer.GetMaxHeaderSize: Int64; +begin + Result := FMaxHeaderSize; +end; + +function TCrossHttpServer.GetMaxPostDataSize: Int64; +begin + Result := FMaxPostDataSize; +end; + +function TCrossHttpServer.GetMinCompressSize: Int64; +begin + Result := FMinCompressSize; +end; + +function TCrossHttpServer.GetOnPostData: TCrossHttpDataEvent; +begin + Result := FOnPostData; +end; + +function TCrossHttpServer.GetOnPostDataBegin: TCrossHttpConnEvent; +begin + Result := FOnPostDataBegin; +end; + +function TCrossHttpServer.GetOnPostDataEnd: TCrossHttpConnEvent; +begin + Result := FOnPostDataEnd; +end; + +function TCrossHttpServer.GetOnRequest: TCrossHttpRequestEvent; +begin + Result := FOnRequest; +end; + +function TCrossHttpServer.GetOnRequestException: TCrossHttpRequestExceptionEvent; +begin + Result := FOnRequestException; +end; + +function TCrossHttpServer.GetSessionIDCookieName: string; +begin + Result := FSessionIDCookieName; +end; + +function TCrossHttpServer.GetSessions: ISessions; +begin + Result := FSessions; +end; + +function TCrossHttpServer.GetStoragePath: string; +begin + Result := FStoragePath; +end; + +procedure TCrossHttpServer.SetAutoDeleteFiles(const Value: Boolean); +begin + FAutoDeleteFiles := Value; +end; + +procedure TCrossHttpServer.SetCompressible(const Value: Boolean); +begin + FCompressible := Value; +end; + +procedure TCrossHttpServer.SetMaxHeaderSize(const Value: Int64); +begin + FMaxHeaderSize := Value; +end; + +procedure TCrossHttpServer.SetMaxPostDataSize(const Value: Int64); +begin + FMaxPostDataSize := Value; +end; + +procedure TCrossHttpServer.SetMinCompressSize(const Value: Int64); +begin + FMinCompressSize := Value; +end; + +procedure TCrossHttpServer.SetOnPostData(const Value: TCrossHttpDataEvent); +begin + FOnPostData := Value; +end; + +procedure TCrossHttpServer.SetOnPostDataBegin(const Value: TCrossHttpConnEvent); +begin + FOnPostDataBegin := Value; +end; + +procedure TCrossHttpServer.SetOnPostDataEnd(const Value: TCrossHttpConnEvent); +begin + FOnPostDataEnd := Value; +end; + +procedure TCrossHttpServer.SetOnRequest(const Value: TCrossHttpRequestEvent); +begin + FOnRequest := Value; +end; + +procedure TCrossHttpServer.SetOnRequestException( + const Value: TCrossHttpRequestExceptionEvent); +begin + FOnRequestException := Value; +end; + +procedure TCrossHttpServer.SetSessionIDCookieName(const Value: string); +begin + FSessionIDCookieName := Value; +end; + +procedure TCrossHttpServer.SetSessions(const Value: ISessions); +begin + FSessions := Value; +end; + +procedure TCrossHttpServer.SetStoragePath(const Value: string); +begin + FStoragePath := Value; +end; + +function TCrossHttpServer.Static(const APath, + ALocalStaticDir: string): ICrossHttpServer; +var + LReqPath: string; +begin + LReqPath := APath; + if not LReqPath.EndsWith('/') then + LReqPath := LReqPath + '/'; + LReqPath := LReqPath + ':file(*)'; + Result := Get(LReqPath, TNetCrossRouter.Static(ALocalStaticDir, 'file')); +end; + +function TCrossHttpServer.Index(const APath, ALocalDir: string; + const ADefIndexFiles: TArray): ICrossHttpServer; +var + LReqPath: string; +begin + LReqPath := APath; + if not LReqPath.EndsWith('/') then + LReqPath := LReqPath + '/'; + LReqPath := LReqPath + ':file(*)'; + Result := Get(LReqPath, TNetCrossRouter.Index(ALocalDir, 'file', ADefIndexFiles)); +end; + +function TCrossHttpServer.IsValidHttpRequest(ABuf: Pointer; + ALen: Integer): Boolean; +var + LBytes: TBytes; + I: Integer; +begin + for I := Low(FMethodTags) to High(FMethodTags) do + begin + LBytes := FMethodTags[I]; + if (ALen >= Length(LBytes)) and + CompareMem(ABuf, Pointer(LBytes), Length(LBytes)) then Exit(True); + end; + Result := False; +end; + +procedure TCrossHttpServer.ParseRecvData(AConnection: ICrossConnection; + ABuf: Pointer; ALen: Integer); +var + LHttpConnection: ICrossHttpConnection; + LRequest: TCrossHttpRequest; + LResponse: TCrossHttpResponse; + pch: PByte; + LChunkSize: Integer; + LLineStr: string; + + procedure _Error(AStatusCode: Integer; const AErrMsg: string); + begin + LHttpConnection.Response.SendStatus(AStatusCode, AErrMsg); + end; + +begin + LHttpConnection := AConnection as ICrossHttpConnection; + LRequest := LHttpConnection.Request as TCrossHttpRequest; + LResponse := LHttpConnection.Response as TCrossHttpResponse; + + // 在这里解析客户端浏览器发送过来的请求数据 + if (LRequest.FParseState = psDone) then + begin + LRequest.Reset; + LResponse.Reset; + end; + + pch := ABuf; + while (ALen > 0) do + begin + case LRequest.FParseState of + psHeader: + begin + case pch^ of + 13{\r}: Inc(LRequest.CR); + 10{\n}: Inc(LRequest.LF); + else + LRequest.CR := 0; + LRequest.LF := 0; + end; + + // Header尺寸超标 + if (FMaxHeaderSize > 0) and (LRequest.FRawRequest.Size + 1 > FMaxHeaderSize) then + begin + _Error(400, 'Request header too large.'); + Exit; + end; + + // 写入请求数据 + LRequest.FRawRequest.Write(pch^, 1); + Dec(ALen); + Inc(pch); + + // 如果不是有效的Http请求直接断开 + // HTTP 请求命令中最长的命令是 PROPFIND, 长度为8个字符 + // 所以在收到8个字节的时候进行检测 + if (LRequest.FRawRequest.Size = 8) and + not IsValidHttpRequest(LRequest.FRawRequest.Memory, LRequest.FRawRequest.Size) then + begin + _Error(400, 'Request method invalid.'); + Exit; + end; + + // HTTP头已接收完毕(\r\n\r\n是HTTP头结束的标志) + if (LRequest.CR = 2) and (LRequest.LF = 2) then + begin + LRequest.CR := 0; + LRequest.LF := 0; + + if not LRequest.ParseRequestData then + begin + _Error(400, 'Request data invalid.'); + Exit; + end; + + // Post数据尺寸超标, 直接断开连接 + if (FMaxPostDataSize > 0) and (LRequest.FContentLength > FMaxPostDataSize) then + begin + _Error(400, 'Post data too large.'); + Exit; + end; + + // 如果 RequestContentLength 大于 0, 或者是 Chunked 编码, 则还需要接收 post 数据 + if (LRequest.FContentLength > 0) or LRequest.IsChunked then + begin + LRequest.FPostDataSize := 0; + + if LRequest.IsChunked then + begin + LRequest.FParseState := psChunkSize; + LRequest.FChunkSizeStream := TBytesStream.Create(nil); + end else + LRequest.FParseState := psPostData; + + TriggerPostDataBegin(LHttpConnection); + end else + begin + LRequest.FBodyType := btNone; + LRequest.FParseState := psDone; + Break; + end; + end; + end; + + // 非Chunked编码的Post数据(有RequestContentLength) + psPostData: + begin + LChunkSize := Min((LRequest.ContentLength - LRequest.FPostDataSize), ALen); + // Post数据尺寸超标, 直接断开连接 + if (FMaxPostDataSize > 0) and (LRequest.FPostDataSize + LChunkSize > FMaxPostDataSize) then + begin + _Error(400, 'Post data too large.'); + Exit; + end; + TriggerPostData(LHttpConnection, pch, LChunkSize); + + Inc(LRequest.FPostDataSize, LChunkSize); + Inc(pch, LChunkSize); + Dec(ALen, LChunkSize); + + if (LRequest.FPostDataSize >= LRequest.ContentLength) then + begin + LRequest.FParseState := psDone; + TriggerPostDataEnd(LHttpConnection); + Break; + end; + end; + + // Chunked编码: 块尺寸 + psChunkSize: + begin + case pch^ of + 13{\r}: Inc(LRequest.CR); + 10{\n}: Inc(LRequest.LF); + else + LRequest.CR := 0; + LRequest.LF := 0; + LRequest.FChunkSizeStream.Write(pch^, 1); + end; + Dec(ALen); + Inc(pch); + + if (LRequest.CR = 1) and (LRequest.LF = 1) then + begin + SetString(LLineStr, MarshaledAString(LRequest.FChunkSizeStream.Memory), LRequest.FChunkSizeStream.Size); + LRequest.FParseState := psChunkData; + LRequest.FChunkSize := StrToIntDef('$' + Trim(LLineStr), -1); + LRequest.FChunkLeftSize := LRequest.FChunkSize; + end; + end; + + // Chunked编码: 块数据 + psChunkData: + begin + if (LRequest.FChunkLeftSize > 0) then + begin + LChunkSize := Min(LRequest.FChunkLeftSize, ALen); + // Post数据尺寸超标, 直接断开连接 + if (FMaxPostDataSize > 0) and (LRequest.FPostDataSize + LChunkSize > FMaxPostDataSize) then + begin + _Error(400, 'Post data too large.'); + Exit; + end; + TriggerPostData(LHttpConnection, pch, LChunkSize); + + Inc(LRequest.FPostDataSize, LChunkSize); + Dec(LRequest.FChunkLeftSize, LChunkSize); + Inc(pch, LChunkSize); + Dec(ALen, LChunkSize); + end; + + if (LRequest.FChunkLeftSize <= 0) then + begin + LRequest.FParseState := psChunkEnd; + LRequest.CR := 0; + LRequest.LF := 0; + end; + end; + + // Chunked编码: 块结束符\r\n + psChunkEnd: + begin + case pch^ of + 13{\r}: Inc(LRequest.CR); + 10{\n}: Inc(LRequest.LF); + else + LRequest.CR := 0; + LRequest.LF := 0; + end; + Dec(ALen); + Inc(pch); + + if (LRequest.CR = 1) and (LRequest.LF = 1) then + begin + // 最后一块的ChunSize为0 + if (LRequest.FChunkSize > 0) then + begin + LRequest.FParseState := psChunkSize; + LRequest.FChunkSizeStream.Clear; + LRequest.CR := 0; + LRequest.LF := 0; + end else + begin + LRequest.FParseState := psDone; + FreeAndNil(LRequest.FChunkSizeStream); + TriggerPostDataEnd(LHttpConnection); + Break; + end; + end; + end; + end; + end; + + // 处理请求 + if (LRequest.FParseState = psDone) then + DoOnRequest(LHttpConnection); + + // 处理粘包 + if (ALen > 0) then + ParseRecvData(LHttpConnection, pch, ALen); +end; + +function TCrossHttpServer.Post(const APath: string; + ARouterProc: TCrossHttpRouterProc): ICrossHttpServer; +begin + Result := Route('POST', APath, ARouterProc); +end; + +function TCrossHttpServer.Post(const APath: string; + ARouterProc2: TCrossHttpRouterProc2): ICrossHttpServer; +begin + Result := Route('POST', APath, ARouterProc2); +end; + +function TCrossHttpServer.Post(const APath: string; + ARouterMethod: TCrossHttpRouterMethod): ICrossHttpServer; +begin + Result := Route('POST', APath, ARouterMethod); +end; + +function TCrossHttpServer.Post(const APath: string; + ARouterMethod2: TCrossHttpRouterMethod2): ICrossHttpServer; +begin + Result := Route('POST', APath, ARouterMethod2); +end; + +function TCrossHttpServer.Put(const APath: string; + ARouterMethod: TCrossHttpRouterMethod): ICrossHttpServer; +begin + Result := Route('PUT', APath, ARouterMethod); +end; + +function TCrossHttpServer.Put(const APath: string; + ARouterMethod2: TCrossHttpRouterMethod2): ICrossHttpServer; +begin + Result := Route('PUT', APath, ARouterMethod2); +end; + +function TCrossHttpServer.Put(const APath: string; + ARouterProc: TCrossHttpRouterProc): ICrossHttpServer; +begin + Result := Route('PUT', APath, ARouterProc); +end; + +function TCrossHttpServer.Put(const APath: string; + ARouterProc2: TCrossHttpRouterProc2): ICrossHttpServer; +begin + Result := Route('PUT', APath, ARouterProc2); +end; + +function TCrossHttpServer.RegisterMiddleware(const AMethod, APath: string; + AMiddlewareProc: TCrossHttpRouterProc; + AMiddlewareMethod: TCrossHttpRouterMethod; + AMiddlewareProc2: TCrossHttpRouterProc2; + AMiddlewareMethod2: TCrossHttpRouterMethod2): TCrossHttpServer; +var + LMiddleware: ICrossHttpRouter; +begin + LMiddleware := CreateRouter(AMethod, APath, + AMiddlewareProc, AMiddlewareMethod, + AMiddlewareProc2, AMiddlewareMethod2); + FMiddlewaresLock.BeginWrite; + try + FMiddlewares.Add(LMiddleware); + finally + FMiddlewaresLock.EndWrite; + end; + Result := Self; +end; + +function TCrossHttpServer.RegisterRouter(const AMethod, APath: string; + ARouterProc: TCrossHttpRouterProc; ARouterMethod: TCrossHttpRouterMethod; + ARouterProc2: TCrossHttpRouterProc2; ARouterMethod2: TCrossHttpRouterMethod2): TCrossHttpServer; +var + LRouter: ICrossHttpRouter; +begin + LRouter := CreateRouter(AMethod, APath, + ARouterProc, ARouterMethod, + ARouterProc2, ARouterMethod2); + FRoutersLock.BeginWrite; + try + FRouters.Add(LRouter); + finally + FRoutersLock.EndWrite; + end; + Result := Self; +end; + +function TCrossHttpServer.Route(const AMethod, APath: string; + ARouterProc: TCrossHttpRouterProc): ICrossHttpServer; +begin + Result := RegisterRouter(AMethod, APath, ARouterProc, nil, nil, nil); +end; + +function TCrossHttpServer.Route(const AMethod, APath: string; + ARouterProc2: TCrossHttpRouterProc2): ICrossHttpServer; +begin + Result := RegisterRouter(AMethod, APath, nil, nil, ARouterProc2, nil); +end; + +function TCrossHttpServer.Route(const AMethod, APath: string; + ARouterMethod: TCrossHttpRouterMethod): ICrossHttpServer; +begin + Result := RegisterRouter(AMethod, APath, nil, ARouterMethod, nil, nil); +end; + +function TCrossHttpServer.Route(const AMethod, APath: string; + ARouterMethod2: TCrossHttpRouterMethod2): ICrossHttpServer; +begin + Result := RegisterRouter(AMethod, APath, nil, nil, nil, ARouterMethod2); +end; + +function TCrossHttpServer.RemoveRouter(const AMethod, APath: string): ICrossHttpServer; +var + I: Integer; +begin + FRoutersLock.BeginWrite; + try + for I := FRouters.Count - 1 downto 0 do + if SameText(FRouters[I].Method, AMethod) and SameText(FRouters[I].Path, APath) then + FRouters.Delete(I); + finally + FRoutersLock.EndWrite; + end; + Result := Self; +end; + +function TCrossHttpServer.ClearRouter: ICrossHttpServer; +begin + FRoutersLock.BeginWrite; + try + FRouters.Clear; + finally + FRoutersLock.EndWrite; + end; + Result := Self; +end; + +procedure TCrossHttpServer.TriggerPostDataBegin( + AConnection: ICrossHttpConnection); +var + LRequest: TCrossHttpRequest; + LMultiPart: THttpMultiPartFormData; + LStream: TStream; +begin + LRequest := AConnection.Request as TCrossHttpRequest; + case LRequest.BodyType of + btMultiPart: + begin + if (FStoragePath <> '') and not TDirectory.Exists(FStoragePath) then + TDirectory.CreateDirectory(FStoragePath); + + LMultiPart := THttpMultiPartFormData.Create; + LMultiPart.StoragePath := FStoragePath; + LMultiPart.AutoDeleteFiles := FAutoDeleteFiles; + LMultiPart.InitWithBoundary(LRequest.RequestBoundary); + FreeAndNil(LRequest.FBody); + LRequest.FBody := LMultiPart; + end; + + btUrlEncoded: + begin + LStream := TBytesStream.Create; + FreeAndNil(LRequest.FBody); + LRequest.FBody := LStream; + end; + + btBinary: + begin + LStream := TBytesStream.Create(nil); + FreeAndNil(LRequest.FBody); + LRequest.FBody := LStream; + end; + end; + + if Assigned(FOnPostDataBegin) then + FOnPostDataBegin(Self, AConnection); +end; + +procedure TCrossHttpServer.TriggerPostData(AConnection: ICrossHttpConnection; + const ABuf: Pointer; ALen: Integer); +var + LRequest: TCrossHttpRequest; +begin + LRequest := AConnection.Request as TCrossHttpRequest; + + case LRequest.GetBodyType of + btMultiPart: (LRequest.Body as THttpMultiPartFormData).Decode(ABuf, ALen); + btUrlEncoded: (LRequest.Body as TStream).Write(ABuf^, ALen); + btBinary: (LRequest.Body as TStream).Write(ABuf^, ALen); + end; + + if Assigned(FOnPostData) then + FOnPostData(Self, AConnection, ABuf, ALen); +end; + +procedure TCrossHttpServer.TriggerPostDataEnd( + AConnection: ICrossHttpConnection); +var + LRequest: TCrossHttpRequest; + LUrlEncodedStr: string; + LUrlEncodedBody: THttpUrlParams; +begin + LRequest := AConnection.Request as TCrossHttpRequest; + + case LRequest.GetBodyType of + btUrlEncoded: + begin + SetString(LUrlEncodedStr, + MarshaledAString((LRequest.Body as TBytesStream).Memory), + (LRequest.Body as TBytesStream).Size); + LUrlEncodedBody := THttpUrlParams.Create; + LUrlEncodedBody.Decode(LUrlEncodedStr); + FreeAndNil(LRequest.FBody); + LRequest.FBody := LUrlEncodedBody; + end; + + btBinary: + begin + (LRequest.Body as TStream).Position := 0; + end; + end; + + if Assigned(FOnPostDataEnd) then + FOnPostDataEnd(Self, AConnection); +end; + +function TCrossHttpServer.LockMiddlewares: TCrossHttpRouters; +begin + Result := FMiddlewares; + FMiddlewaresLock.BeginWrite; +end; + +function TCrossHttpServer.LockRouters: TCrossHttpRouters; +begin + Result := FRouters; + FRoutersLock.BeginWrite; +end; + +procedure TCrossHttpServer.LogicReceived(AConnection: ICrossConnection; + ABuf: Pointer; ALen: Integer); +begin + ParseRecvData(AConnection as ICrossHttpConnection, ABuf, ALen); +end; + +function TCrossHttpServer.Use(const AMethod, APath: string; + AMiddlewareMethod: TCrossHttpRouterMethod): ICrossHttpServer; +begin + Result := RegisterMiddleware(AMethod, APath, nil, AMiddlewareMethod, nil, nil); +end; + +function TCrossHttpServer.Use(const AMethod, APath: string; + AMiddlewareProc: TCrossHttpRouterProc): ICrossHttpServer; +begin + Result := RegisterMiddleware(AMethod, APath, AMiddlewareProc, nil, nil, nil); +end; + +function TCrossHttpServer.Use(const APath: string; + AMiddlewareMethod: TCrossHttpRouterMethod): ICrossHttpServer; +begin + Result := Use('*', APath, AMiddlewareMethod); +end; + +function TCrossHttpServer.Use(const APath: string; + AMiddlewareProc: TCrossHttpRouterProc): ICrossHttpServer; +begin + Result := Use('*', APath, AMiddlewareProc); +end; + +function TCrossHttpServer.Use( + AMiddlewareMethod: TCrossHttpRouterMethod): ICrossHttpServer; +begin + Result := Use('*', '*', AMiddlewareMethod); +end; + +procedure TCrossHttpServer.UnlockMiddlewares; +begin + FMiddlewaresLock.EndWrite; +end; + +procedure TCrossHttpServer.UnlockRouters; +begin + FRoutersLock.EndWrite; +end; + +function TCrossHttpServer.Use( + AMiddlewareMethod2: TCrossHttpRouterMethod2): ICrossHttpServer; +begin + Result := Use('*', '*', AMiddlewareMethod2); +end; + +function TCrossHttpServer.Use( + AMiddlewareProc2: TCrossHttpRouterProc2): ICrossHttpServer; +begin + Result := Use('*', '*', AMiddlewareProc2); +end; + +function TCrossHttpServer.Use(const AMethod, APath: string; + AMiddlewareMethod2: TCrossHttpRouterMethod2): ICrossHttpServer; +begin + Result := RegisterMiddleware(AMethod, APath, nil, nil, nil, AMiddlewareMethod2); +end; + +function TCrossHttpServer.Use(const AMethod, APath: string; + AMiddlewareProc2: TCrossHttpRouterProc2): ICrossHttpServer; +begin + Result := RegisterMiddleware(AMethod, APath, nil, nil, AMiddlewareProc2, nil); +end; + +function TCrossHttpServer.Use(const APath: string; + AMiddlewareMethod2: TCrossHttpRouterMethod2): ICrossHttpServer; +begin + Result := Use('*', APath, AMiddlewareMethod2); +end; + +function TCrossHttpServer.Use(const APath: string; + AMiddlewareProc2: TCrossHttpRouterProc2): ICrossHttpServer; +begin + Result := Use('*', APath, AMiddlewareProc2); +end; + +function TCrossHttpServer.Use( + AMiddlewareProc: TCrossHttpRouterProc): ICrossHttpServer; +begin + Result := Use('*', '*', AMiddlewareProc); +end; + +{ TCrossHttpRequest } + +constructor TCrossHttpRequest.Create(AConnection: ICrossHttpConnection); +begin + FConnection := AConnection; + + FRawRequest := TBytesStream.Create(nil); + FHeader := THttpHeader.Create; + FCookies := TRequestCookies.Create; + FParams := THttpUrlParams.Create; + FQuery := THttpUrlParams.Create; + + Reset; +end; + +destructor TCrossHttpRequest.Destroy; +begin + FreeAndNil(FRawRequest); + FreeAndNil(FHeader); + FreeAndNil(FCookies); + FreeAndNil(FParams); + FreeAndNil(FQuery); + FreeAndNil(FBody); + inherited; +end; + +function TCrossHttpRequest.GetAccept: string; +begin + Result := FAccept; +end; + +function TCrossHttpRequest.GetAcceptEncoding: string; +begin + Result := FAcceptEncoding; +end; + +function TCrossHttpRequest.GetAcceptLanguage: string; +begin + Result := FAcceptLanguage; +end; + +function TCrossHttpRequest.GetAuthorization: string; +begin + Result := FAuthorization; +end; + +function TCrossHttpRequest.GetBody: TObject; +begin + Result := FBody; +end; + +function TCrossHttpRequest.GetBodyType: TBodyType; +begin + Result := FBodyType; +end; + +function TCrossHttpRequest.GetConnection: ICrossHttpConnection; +begin + Result := FConnection; +end; + +function TCrossHttpRequest.GetContentEncoding: string; +begin + Result := FContentEncoding; +end; + +function TCrossHttpRequest.GetContentLength: Int64; +begin + Result := FContentLength; +end; + +function TCrossHttpRequest.GetContentType: string; +begin + Result := FContentType; +end; + +function TCrossHttpRequest.GetCookies: TRequestCookies; +begin + Result := FCookies; +end; + +function TCrossHttpRequest.GetHeader: THttpHeader; +begin + Result := FHeader; +end; + +function TCrossHttpRequest.GetHostName: string; +begin + Result := FHostName; +end; + +function TCrossHttpRequest.GetHostPort: Word; +begin + Result := FHostPort; +end; + +function TCrossHttpRequest.GetIfModifiedSince: TDateTime; +begin + Result := FIfModifiedSince; +end; + +function TCrossHttpRequest.GetIfNoneMatch: string; +begin + Result := FIfNoneMatch; +end; + +function TCrossHttpRequest.GetIfRange: string; +begin + Result := FIfRange; +end; + +function TCrossHttpRequest.GetIsChunked: Boolean; +begin + Result := SameText(FTransferEncoding, 'chunked'); +end; + +function TCrossHttpRequest.GetIsMultiPartFormData: Boolean; +begin + Result := SameText(FContentType, 'multipart/form-data'); +end; + +function TCrossHttpRequest.GetIsUrlEncodedFormData: Boolean; +begin + Result := SameText(FContentType, 'application/x-www-form-urlencoded'); +end; + +function TCrossHttpRequest.GetKeepAlive: Boolean; +begin + Result := FKeepAlive; +end; + +function TCrossHttpRequest.GetMethod: string; +begin + Result := FMethod; +end; + +function TCrossHttpRequest.GetParams: THttpUrlParams; +begin + Result := FParams; +end; + +function TCrossHttpRequest.GetPath: string; +begin + Result := FPath; +end; + +function TCrossHttpRequest.GetPostDataSize: Int64; +begin + Result := FPostDataSize; +end; + +function TCrossHttpRequest.GetQuery: THttpUrlParams; +begin + Result := FQuery; +end; + +function TCrossHttpRequest.GetRange: string; +begin + Result := FRange; +end; + +function TCrossHttpRequest.GetRawPathAndParams: string; +begin + Result := FRawPathAndParams; +end; + +function TCrossHttpRequest.GetRawRequestText: string; +begin + Result := FRawRequestText; +end; + +function TCrossHttpRequest.GetReferer: string; +begin + Result := FReferer; +end; + +function TCrossHttpRequest.GetRequestBoundary: string; +begin + Result := FRequestBoundary; +end; + +function TCrossHttpRequest.GetRequestCmdLine: string; +begin + Result := FRequestCmdLine; +end; + +function TCrossHttpRequest.GetRequestConnection: string; +begin + Result := FRequestConnection; +end; + +function TCrossHttpRequest.GetSession: ISession; +begin + Result := FSession; +end; + +function TCrossHttpRequest.GetTransferEncoding: string; +begin + Result := FTransferEncoding; +end; + +function TCrossHttpRequest.GetUserAgent: string; +begin + Result := FUserAgent; +end; + +function TCrossHttpRequest.GetVersion: string; +begin + Result := FVersion; +end; + +function TCrossHttpRequest.GetXForwardedFor: string; +begin + Result := FXForwardedFor; +end; + +function TCrossHttpRequest.ParseRequestData: Boolean; +var + LRequestHeader: string; + I, J: Integer; +begin + SetString(FRawRequestText, MarshaledAString(FRawRequest.Memory), FRawRequest.Size); + I := FRawRequestText.IndexOf(#13#10); + // 第一行是请求命令行 + // GET /home?param=123 HTTP/1.1 + FRequestCmdLine := FRawRequestText.Substring(0, I); + // 第二行起是请求头 + LRequestHeader := FRawRequestText.Substring(I + 2); + // 解析请求头 + FHeader.Decode(LRequestHeader); + + // 请求方法(GET, POST, PUT, DELETE...) + I := FRequestCmdLine.IndexOf(' '); + FMethod := FRequestCmdLine.Substring(0, I).ToUpper; + + // 路径及参数(/home?param=123) + J := FRequestCmdLine.IndexOf(' ', I + 1); + FRawPathAndParams := FRequestCmdLine.Substring(I + 1, J - I - 1); + + // 请求的HTTP版本(HTTP/1.1) + FVersion := FRequestCmdLine.Substring(J + 1).ToUpper; + + // 解析?key1=value1&key2=value2参数 + J := FRawPathAndParams.IndexOf('?'); + if (J < 0) then + begin + FRawPath := FRawPathAndParams; + FRawParamsText := ''; + end else + begin + FRawPath := FRawPathAndParams.Substring(0, J); + FRawParamsText := FRawPathAndParams.Substring(J + 1); + end; + FPath := TNetEncoding.URL.Decode(FRawPath); + FQuery.Decode(FRawParamsText); + + // HTTP协议版本 + if (FVersion = '') then + FVersion := 'HTTP/1.0'; + if (FVersion = 'HTTP/1.0') then + FHttpVerNum := 10 + else + FHttpVerNum := 11; + FKeepAlive := (FHttpVerNum = 11); + + // 解析Cookies + FCookies.Decode(FHeader['Cookie'], True); + + FContentType := FHeader['Content-Type']; + FRequestBoundary := ''; + J := FContentType.IndexOf(';'); + if (J >= 0) then + begin + FRequestBoundary := FContentType.Substring(J + 1); + if FRequestBoundary.StartsWith(' boundary=', True) then + FRequestBoundary := FRequestBoundary.Substring(10); + + FContentType := FContentType.Substring(0, J); + end; + + FContentLength := StrToInt64Def(FHeader['Content-Length'], -1); + + FRequestHost := FHeader['Host']; + J := FRequestHost.IndexOf(':'); + if (J >= 0) then + begin + FHostName := FRequestHost.Substring(0, J); + FHostPort := FRequestHost.Substring(J + 1).ToInteger; + end else + begin + FHostName := FRequestHost; + FHostPort := TCrossHttpServer(FConnection.Owner).Port; + end; + + FRequestConnection := FHeader['Connection']; + // HTTP/1.0 默认KeepAlive=False,只有显示指定了Connection: keep-alive才认为KeepAlive=True + // HTTP/1.1 默认KeepAlive=True,只有显示指定了Connection: close才认为KeepAlive=False + if FHttpVerNum = 10 then + FKeepAlive := SameText(FRequestConnection, 'keep-alive') + else if SameText(FRequestConnection, 'close') then + FKeepAlive := False; + + FTransferEncoding:= FHeader['Transfer-Encoding']; + FContentEncoding:= FHeader['Content-Encoding']; + FAccept:= FHeader['Accept']; + FReferer:= FHeader['Referer']; + FAcceptLanguage:= FHeader['Accept-Language']; + FAcceptEncoding:= FHeader['Accept-Encoding']; + FUserAgent:= FHeader['User-Agent']; + FAuthorization:= FHeader['Authorization']; + FRequestCookies:= FHeader['Cookie']; + FIfModifiedSince := TCrossHttpUtils.RFC1123_StrToDate(FHeader['If-Modified-Since']); + FIfNoneMatch := FHeader['If-None-Match']; + FRange := FHeader['Range']; + FIfRange := FHeader['If-Range']; + FXForwardedFor:= FHeader['X-Forwarded-For']; + + if IsMultiPartFormData then + FBodyType := btMultiPart + else if IsUrlEncodedFormData then + FBodyType := btUrlEncoded + else + FBodyType := btBinary; + + Result := True; +end; + +procedure TCrossHttpRequest.Reset; +begin + FRawRequest.Clear; + + FParseState := psHeader; + CR := 0; + LF := 0; + FPostDataSize := 0; + FreeAndNil(FBody); +end; + +{ TCrossHttpResponse } + +constructor TCrossHttpResponse.Create(AConnection: ICrossHttpConnection); +begin + FConnection := AConnection; + FHeader := THttpHeader.Create; + FCookies := TResponseCookies.Create; + FStatusCode := 200; +end; + +destructor TCrossHttpResponse.Destroy; +begin + FreeAndNil(FHeader); + FreeAndNil(FCookies); + inherited; +end; + +procedure TCrossHttpResponse.Download(const AFileName: string; + ACallback: TProc); +begin + Attachment(AFileName); + SendFile(AFileName, ACallback); +end; + +function TCrossHttpResponse.GetConnection: ICrossHttpConnection; +begin + Result := FConnection; +end; + +function TCrossHttpResponse.GetContentType: string; +begin + Result := FHeader['Content-Type']; +end; + +function TCrossHttpResponse.GetCookies: TResponseCookies; +begin + Result := FCookies; +end; + +function TCrossHttpResponse.GetHeader: THttpHeader; +begin + Result := FHeader; +end; + +function TCrossHttpResponse.GetLocation: string; +begin + Result := FHeader['Location']; +end; + +function TCrossHttpResponse.GetRequest: ICrossHttpRequest; +begin + Result := FConnection.Request; +end; + +function TCrossHttpResponse.GetSent: Boolean; +begin + Result := (AtomicCmpExchange(FSendStatus, 0, 0) > 0); +end; + +function TCrossHttpResponse.GetStatusCode: Integer; +begin + Result := FStatusCode; +end; + +procedure TCrossHttpResponse.Json(const AJson: string; + ACallback: TProc); +begin + SetContentType(TMediaType.APPLICATION_JSON_UTF8); + Send(AJson, ACallback); +end; + +procedure TCrossHttpResponse.Redirect(const AUrl: string; ACallback: TProc); +begin + SetLocation(AUrl); + SendStatus(302, '', ACallback); +end; + +procedure TCrossHttpResponse.Reset; +begin + FStatusCode := 200; + FHeader.Clear; + FCookies.Clear; + FSendStatus := 0; +end; + +procedure TCrossHttpResponse.Attachment(const AFileName: string); +begin + if (GetContentType = '') then + SetContentType(TCrossHttpUtils.GetFileMIMEType(AFileName)); + FHeader['Content-Disposition'] := 'attachment; filename="' + + TNetEncoding.URL.Encode(TPath.GetFileName(AFileName)) + '"'; +end; + +procedure TCrossHttpResponse.Send(const ABody; ACount: NativeInt; + ACallback: TProc); +var + LCompressType: TCompressType; +begin + if _CheckCompress(ACount, LCompressType) then + SendZCompress(ABody, ACount, LCompressType, ACallback) + else + SendNoCompress(ABody, ACount, ACallback); +end; + +procedure TCrossHttpResponse.Send(const ABody: TBytes; AOffset, ACount: NativeInt; + ACallback: TProc); +var + LBody: TBytes; + LOffset, LCount: NativeInt; +begin + // 增加其引用计数 + LBody := ABody; + + LOffset := AOffset; + LCount := ACount; + _AdjustOffsetCount(Length(ABody), LOffset, LCount); + + Send(LBody[LOffset], LCount, + // CALLBACK + procedure(AConnection: ICrossConnection; ASuccess: Boolean) + begin + // 减少引用计数 + LBody := nil; + + if Assigned(ACallback) then + ACallback(AConnection, ASuccess); + end); +end; + +procedure TCrossHttpResponse.Send(const ABody: TBytes; + ACallback: TProc); +begin + Send(ABody, 0, Length(ABody), ACallback); +end; + +procedure TCrossHttpResponse.Send(const ABody: TStream; const AOffset, + ACount: Int64; ACallback: TProc); +var + LCompressType: TCompressType; +begin + if _CheckCompress(ABody.Size, LCompressType) then + SendZCompress(ABody, AOffset, ACount, LCompressType, ACallback) + else + SendNoCompress(ABody, AOffset, ACount, ACallback); +end; + +procedure TCrossHttpResponse.Send(const ABody: TStream; ACallback: TProc); +begin + Send(ABody, 0, 0, ACallback); +end; + +procedure TCrossHttpResponse.Send(const ABody: string; + ACallback: TProc); +var + LBody: TBytes; +begin + LBody := TEncoding.UTF8.GetBytes(ABody); + if (GetContentType = '') then + SetContentType(TMediaType.TEXT_HTML_UTF8); + + Send(LBody, ACallback); +end; + +procedure TCrossHttpResponse.SendNoCompress( + const AChunkSource: TFunc; + ACallback: TProc); +{ +HTTP头\r\n\r\n +块尺寸\r\n +块内容 +\r\n块尺寸\r\n +块内容 +\r\n0\r\n\r\n +} +type + TChunkState = (csHead, csBody, csDone); +const + CHUNK_END: array [0..6] of Byte = (13, 10, 48, 13, 10, 13, 10); // \r\n0\r\n\r\n +var + LHeaderBytes, LChunkHeader: TBytes; + LIsFirstChunk: Boolean; + LChunkState: TChunkState; + LChunkData: Pointer; + LChunkSize: NativeInt; +begin + LIsFirstChunk := True; + LChunkState := csHead; + + _Send( + // HEADER + function(AData: PPointer; ACount: PNativeInt): Boolean + begin + LHeaderBytes := _CreateHeader(0, True); + + AData^ := @LHeaderBytes[0]; + ACount^ := Length(LHeaderBytes); + + Result := (ACount^ > 0); + end, + // BODY + function(AData: PPointer; ACount: PNativeInt): Boolean + begin + case LChunkState of + csHead: + begin + LChunkData := nil; + LChunkSize := 0; + if not Assigned(AChunkSource) + or not AChunkSource(@LChunkData, @LChunkSize) + or (LChunkData = nil) + or (LChunkSize <= 0) then + begin + LChunkState := csDone; + + AData^ := @CHUNK_END[0]; + ACount^ := Length(CHUNK_END); + + Result := (ACount^ > 0); + + Exit; + end; + + LChunkHeader := TEncoding.ANSI.GetBytes(IntToHex(LChunkSize, 0)) + [13, 10]; + if LIsFirstChunk then + LIsFirstChunk := False + else + LChunkHeader := [13, 10] + LChunkHeader; + + LChunkState := csBody; + + AData^ := @LChunkHeader[0]; + ACount^ := Length(LChunkHeader); + + Result := (ACount^ > 0); + end; + + csBody: + begin + LChunkState := csHead; + + AData^ := LChunkData; + ACount^ := LChunkSize; + + Result := (ACount^ > 0); + end; + + csDone: + begin + Result := False; + end; + else + Result := False; + end; + end, + // CALLBACK + procedure(AConnection: ICrossConnection; ASuccess: Boolean) + begin + LHeaderBytes := nil; + LChunkHeader := nil; + + if Assigned(ACallback) then + ACallback(AConnection, ASuccess); + end); +end; + +procedure TCrossHttpResponse.SendFile(const AFileName: string; + ACallback: TProc); +var + LStream: TFileStream; + LLastModified: TDateTime; + LRequest: TCrossHttpRequest; + LLastModifiedStr, LETag: string; + LRangeStr: string; + LRangeStrArr: TArray; + LRangeBegin, LRangeEnd, LOffset, LCount: Int64; +begin + if not TFile.Exists(AFileName) then + begin + FHeader.Remove('Content-Disposition'); + SendStatus(404, ACallback); + Exit; + end; + + if (GetContentType = '') then + SetContentType(TCrossHttpUtils.GetFileMIMEType(AFileName)); + + try + // 根据请求头中的时间戳决定是否需要发送文件数据 + // 当请求头中的时间戳与文件时间一致时, 浏览器会自动从本地加载文件数据 + // 服务端无需发送文件数据 + LRequest := GetRequest as TCrossHttpRequest; + LLastModified := TFile.GetLastWriteTime(AFileName); + + if (LRequest.IfModifiedSince > 0) and (LRequest.IfModifiedSince >= (LLastModified - (1 / SecsPerDay))) then + begin + // 304不要带任何body数据, 否则部分浏览器会报告无效的RESPONSE + SendStatus(304, '', ACallback); + Exit; + end; + + LLastModifiedStr := TCrossHttpUtils.RFC1123_DateToStr(LLastModified); + + LETag := '"' + TUtils.BytesToHex(THashMD5.GetHashBytes(AFileName + LLastModifiedStr)) + '"'; + if (LRequest.IfNoneMatch = LETag) then + begin + // 304不要带任何body数据, 否则部分浏览器会报告无效的RESPONSE + SendStatus(304, '', ACallback); + Exit; + end; + + LStream := TFileStream.Create(AFileName, fmOpenRead, fmShareDenyNone); + except + on e: Exception do + begin + FHeader.Remove('Content-Disposition'); + SendStatus(404, Format('%s, %s', [e.ClassName, e.Message]), ACallback); + Exit; + end; + end; + + // 在响应头中加入文件时间戳 + // 浏览器会根据该时间戳决定是否从本地缓存中直接加载数据 + FHeader['Last-Modified'] := LLastModifiedStr; + FHeader['ETag'] := LETag; + + // 告诉浏览器支持分块传输 + FHeader['Accept-Ranges'] := 'bytes'; + + // 收到分块取数据头 + // Range: bytes=[x]-[y] + LRangeStr := LRequest.Range; + if (LRangeStr <> '') + and ((LRequest.IfRange = '') or (LRequest.IfRange = LETag)) then + begin + LRangeStr := LRangeStr.Substring(LRangeStr.IndexOf('=') + 1); + LRangeStrArr := LRangeStr.Split(['-']); + if (Length(LRangeStrArr) >= 2) then + begin + LRangeBegin := StrToInt64Def(LRangeStrArr[0], 0); + LRangeEnd := StrToInt64Def(LRangeStrArr[1], 0); + end else + if (Length(LRangeStrArr) >= 1) then + begin + LRangeBegin := StrToInt64Def(LRangeStrArr[0], 0); + LRangeEnd := LStream.Size - 1; + end else + begin + LRangeBegin := 0; + LRangeEnd := LStream.Size - 1; + end; + + LOffset := LRangeBegin; + LCount := LRangeEnd - LRangeBegin + 1; + + // 返回分块信息 + // Content-Range: bytes [x]-[y]/file-size + FHeader['Content-Range'] := Format('bytes %d-%d/%d', + [LRangeBegin, LRangeEnd, LStream.Size]); + + // 断点续传需要返回206状态码, 而不是200 + FStatusCode := 206; + end else + begin + LOffset := 0; + LCount := LStream.Size; + end; + + Send(LStream, LOffset, LCount, + procedure(AConnection: ICrossConnection; ASuccess: Boolean) + begin + FreeAndNil(LStream); + + if Assigned(ACallback) then + ACallback(AConnection, ASuccess); + end); +end; + +procedure TCrossHttpResponse.SetContentType(const Value: string); +begin + FHeader['Content-Type'] := Value; +end; + +procedure TCrossHttpResponse.SetLocation(const Value: string); +begin + FHeader['Location'] := Value; +end; + +procedure TCrossHttpResponse.SetStatusCode(Value: Integer); +begin + FStatusCode := Value; +end; + +procedure TCrossHttpResponse._AdjustOffsetCount(const ABodySize: NativeInt; + var AOffset, ACount: NativeInt); +begin + {$region '修正 AOffset'} + // 偏移为正数, 从头部开始计算偏移 + if (AOffset >= 0) then + begin + AOffset := AOffset; + if (AOffset >= ABodySize) then + AOffset := ABodySize - 1; + end else + // 偏移为负数, 从尾部开始计算偏移 + begin + AOffset := ABodySize + AOffset; + if (AOffset < 0) then + AOffset := 0; + end; + {$endregion} + + {$region '修正 ACount'} + // ACount<=0表示需要处理所有数据 + if (ACount <= 0) then + ACount := ABodySize; + + if (ABodySize - AOffset < ACount) then + ACount := ABodySize - AOffset; + {$endregion} +end; + +procedure TCrossHttpResponse._AdjustOffsetCount(const ABodySize: Int64; + var AOffset, ACount: Int64); +begin + {$region '修正 AOffset'} + // 偏移为正数, 从头部开始计算偏移 + if (AOffset >= 0) then + begin + AOffset := AOffset; + if (AOffset >= ABodySize) then + AOffset := ABodySize - 1; + end else + // 偏移为负数, 从尾部开始计算偏移 + begin + AOffset := ABodySize + AOffset; + if (AOffset < 0) then + AOffset := 0; + end; + {$endregion} + + {$region '修正 ACount'} + // ACount<=0表示需要处理所有数据 + if (ACount <= 0) then + ACount := ABodySize; + + if (ABodySize - AOffset < ACount) then + ACount := ABodySize - AOffset; + {$endregion} +end; + +function TCrossHttpResponse._CheckCompress(const ABodySize: Int64; + var ACompressType: TCompressType): Boolean; +var + LContType, LRequestAcceptEncoding: string; + LServer: ICrossHttpServer; +begin + LContType := GetContentType; + LServer := FConnection.Server; + + if LServer.Compressible + and (ABodySize > 0) + and ((LServer.MinCompressSize <= 0) or (ABodySize >= LServer.MinCompressSize)) + and ((Pos('text/', LContType) > 0) + or (Pos('application/json', LContType) > 0) + or (Pos('javascript', LContType) > 0) + or (Pos('xml', LContType) > 0) + ) then + begin + LRequestAcceptEncoding := GetRequest.AcceptEncoding; + + if (Pos('gzip', LRequestAcceptEncoding) > 0) then + begin + ACompressType := ctGZip; + Exit(True); + end else + if (Pos('deflate', LRequestAcceptEncoding) > 0) then + begin + ACompressType := ctDeflate; + Exit(True); + end; + end; + + Result := False; +end; + +function TCrossHttpResponse._CreateHeader(const ABodySize: Int64; + AChunked: Boolean): TBytes; +var + LHeaderStr: string; + LCookie: TResponseCookie; +begin + if (GetContentType = '') then + SetContentType(TMediaType.APPLICATION_OCTET_STREAM); + if (FHeader['Connection'] = '') then + begin + if FConnection.Request.KeepAlive then + FHeader['Connection'] := 'keep-alive' + else + FHeader['Connection'] := 'close'; + end; + + if AChunked then + FHeader['Transfer-Encoding'] := 'chunked' + else + FHeader['Content-Length'] := ABodySize.ToString; + + if (FHeader['Server'] = '') then + FHeader['Server'] := CROSS_HTTP_SERVER_NAME; + + LHeaderStr := FConnection.Request.Version + ' ' + FStatusCode.ToString + ' ' + + TCrossHttpUtils.GetHttpStatusText(FStatusCode) + #13#10; + + for LCookie in FCookies do + LHeaderStr := LHeaderStr + 'Set-Cookie: ' + LCookie.Encode + #13#10; + + LHeaderStr := LHeaderStr + FHeader.Encode; + + Result := TEncoding.ANSI.GetBytes(LHeaderStr); +end; + +procedure TCrossHttpResponse._Send(ASource: TFunc; + ACallback: TProc); +var + LSender: TProc; + LKeepAlive: Boolean; + LStatusCode: Integer; +begin + AtomicIncrement(FSendStatus); + + LKeepAlive := FConnection.Request.KeepAlive; + LStatusCode := FStatusCode; + + LSender := + procedure(AConnection: ICrossConnection; ASuccess: Boolean) + var + LData: Pointer; + LCount: NativeInt; + begin + if not ASuccess then + begin + if Assigned(ACallback) then + ACallback(AConnection, False); + + AConnection.Close; + + LSender := nil; + + Exit; + end; + + LData := nil; + LCount := 0; + if not Assigned(ASource) + or not ASource(@LData, @LCount) + or (LData = nil) + or (LCount <= 0) then + begin + if Assigned(ACallback) then + ACallback(AConnection, True); + + if not LKeepAlive + or (LStatusCode >= 400{如果发送的是出错状态码, 则发送完成之后断开连接}) then + AConnection.Disconnect; + + LSender := nil; + + Exit; + end; + + AConnection.SendBuf(LData^, LCount, LSender); + end; + + LSender(FConnection, True); +end; + +procedure TCrossHttpResponse._Send(AHeaderSource, + ABodySource: TFunc; + ACallback: TProc); +var + LHeaderDone: Boolean; +begin + LHeaderDone := False; + + _Send( + function(AData: PPointer; ACount: PNativeInt): Boolean + begin + if not LHeaderDone then + begin + LHeaderDone := True; + Result := Assigned(AHeaderSource) and AHeaderSource(AData, ACount); + end else + begin + Result := Assigned(ABodySource) and ABodySource(AData, ACount); + end; + end, + ACallback); +end; + +procedure TCrossHttpResponse.SendNoCompress(const ABody; ACount: NativeInt; + ACallback: TProc); +var + P: PByte; + LSize: NativeInt; + LHeaderBytes: TBytes; +begin + P := @ABody; + LSize := ACount; + + _Send( + // HEADER + function(AData: PPointer; ACount: PNativeInt): Boolean + begin + LHeaderBytes := _CreateHeader(LSize, False); + + AData^ := @LHeaderBytes[0]; + ACount^ := Length(LHeaderBytes); + + Result := (ACount^ > 0); + end, + // BODY + function(AData: PPointer; ACount: PNativeInt): Boolean + begin + AData^ := P; + ACount^ := Min(LSize, SND_BUF_SIZE); + Result := (ACount^ > 0); + + if (LSize > SND_BUF_SIZE) then + begin + Inc(P, SND_BUF_SIZE); + Dec(LSize, SND_BUF_SIZE); + end else + begin + LSize := 0; + P := nil; + end; + end, + // CALLBACK + procedure(AConnection: ICrossConnection; ASuccess: Boolean) + begin + LHeaderBytes := nil; + + if Assigned(ACallback) then + ACallback(AConnection, ASuccess); + end); +end; + +procedure TCrossHttpResponse.SendNoCompress(const ABody: TBytes; AOffset, + ACount: NativeInt; ACallback: TProc); +var + LBody: TBytes; + LOffset, LCount: NativeInt; +begin + // 增加其引用计数 + LBody := ABody; + + LOffset := AOffset; + LCount := ACount; + _AdjustOffsetCount(Length(ABody), LOffset, LCount); + + SendNoCompress(LBody[LOffset], LCount, + // CALLBACK + procedure(AConnection: ICrossConnection; ASuccess: Boolean) + begin + // 减少引用计数 + LBody := nil; + + if Assigned(ACallback) then + ACallback(AConnection, ASuccess); + end); +end; + +procedure TCrossHttpResponse.SendNoCompress(const ABody: TBytes; + ACallback: TProc); +begin + SendNoCompress(ABody, 0, Length(ABody), ACallback); +end; + +procedure TCrossHttpResponse.SendNoCompress(const ABody: TStream; const AOffset, + ACount: Int64; ACallback: TProc); +var + LOffset, LCount: Int64; + LBody: TStream; + LHeaderBytes, LBuffer: TBytes; +begin + LOffset := AOffset; + LCount := ACount; + _AdjustOffsetCount(ABody.Size, LOffset, LCount); + + if (ABody is TCustomMemoryStream) then + begin + SendNoCompress(Pointer(IntPtr(TCustomMemoryStream(ABody).Memory) + LOffset)^, LCount, ACallback); + Exit; + end; + + LBody := ABody; + LBody.Position := LOffset; + + SetLength(LBuffer, SND_BUF_SIZE); + + _Send( + // HEADER + function(AData: PPointer; ACount: PNativeInt): Boolean + begin + LHeaderBytes := _CreateHeader(LCount, False); + + AData^ := @LHeaderBytes[0]; + ACount^ := Length(LHeaderBytes); + + Result := (ACount^ > 0); + end, + // BODY + function(AData: PPointer; ACount: PNativeInt): Boolean + begin + if (LCount <= 0) then Exit(False); + + AData^ := @LBuffer[0]; + ACount^ := LBody.Read(LBuffer[0], Min(LCount, SND_BUF_SIZE)); + + Result := (ACount^ > 0); + + if Result then + Dec(LCount, ACount^); + end, + // CALLBACK + procedure(AConnection: ICrossConnection; ASuccess: Boolean) + begin + LHeaderBytes := nil; + LBuffer := nil; + + if Assigned(ACallback) then + ACallback(AConnection, ASuccess); + end); +end; + +procedure TCrossHttpResponse.SendNoCompress(const ABody: TStream; + ACallback: TProc); +begin + SendNoCompress(ABody, 0, 0, ACallback); +end; + +procedure TCrossHttpResponse.SendNoCompress(const ABody: string; + ACallback: TProc); +var + LBody: TBytes; +begin + LBody := TEncoding.UTF8.GetBytes(ABody); + if (GetContentType = '') then + SetContentType(TMediaType.TEXT_HTML_UTF8); + + SendNoCompress(LBody, ACallback); +end; + +procedure TCrossHttpResponse.SendStatus(AStatusCode: Integer; + const ADescription: string; ACallback: TProc); +begin + FStatusCode := AStatusCode; + Send(ADescription, ACallback); +end; + +procedure TCrossHttpResponse.SendStatus(AStatusCode: Integer; + ACallback: TProc); +begin + SendStatus(AStatusCode, TCrossHttpUtils.GetHttpStatusText(AStatusCode), ACallback); +end; + +procedure TCrossHttpResponse.SendZCompress( + const AChunkSource: TFunc; + ACompressType: TCompressType; ACallback: TProc); +{ + 本方法实现了一边压缩一边发送数据, 所以可以支持无限大的分块数据的压缩发送, + 而不用占用太多的内存和CPU + + zlib参考手册: http://www.zlib.net/zlib_how.html +} +const + WINDOW_BITS: array [TCompressType] of Integer = (15 + 16{gzip}, 15{deflate}); + CONTENT_ENCODING: array [TCompressType] of string = ('gzip', 'deflate'); +var + LZStream: TZStreamRec; + LZFlush: Integer; + LZResult: Integer; + LOutSize: Integer; + LBuffer: TBytes; +begin + // 返回压缩方式 + FHeader['Content-Encoding'] := CONTENT_ENCODING[ACompressType]; + + // 明确告知缓存服务器按照 Accept-Encoding 字段的内容, 分别缓存不同的版本 + FHeader['Vary'] := 'Accept-Encoding'; + + SetLength(LBuffer, SND_BUF_SIZE); + + FillChar(LZStream, SizeOf(TZStreamRec), 0); + LZResult := Z_OK; + LZFlush := Z_NO_FLUSH; + + if (deflateInit2(LZStream, Z_DEFAULT_COMPRESSION, + Z_DEFLATED, WINDOW_BITS[ACompressType], 8, Z_DEFAULT_STRATEGY) <> Z_OK) then + begin + if Assigned(ACallback) then + ACallback(FConnection, False); + Exit; + end; + + SendNoCompress( + // CHUNK + function(AData: PPointer; ACount: PNativeInt): Boolean + var + LChunkData: Pointer; + LChunkSize: NativeInt; + begin + repeat + // 当 deflate(LZStream, Z_FINISH) 被调用后 + // 返回 Z_STREAM_END 表示所有数据处理完毕 + if (LZResult = Z_STREAM_END) then + begin + AData^ := nil; + ACount^ := 0; + Exit(False); + end; + + // 输入缓冲区已经处理完毕 + // 需要填入新数据 + if (LZStream.avail_in = 0) then + begin + LChunkData := nil; + LChunkSize := 0; + if not Assigned(AChunkSource) + or not AChunkSource(@LChunkData, @LChunkSize) + or (LChunkData = nil) + or (LChunkSize <= 0) then + LZFlush := Z_FINISH // 如果没有后续数据了, 准备结束压缩 + else + LZFlush := Z_NO_FLUSH; + + // 压缩数据输入缓冲区 + LZStream.avail_in := LChunkSize; + LZStream.next_in := LChunkData; + end; + + // 压缩数据输出缓冲区 + LZStream.avail_out := SND_BUF_SIZE; + LZStream.next_out := @LBuffer[0]; + + // 进行压缩处理 + // 输入缓冲区数据可以大于输出缓冲区 + // 这种情况可以多次调用 deflate 分批压缩, + // 直到 avail_in=0 表示当前输入缓冲区数据已压缩完毕 + LZResult := deflate(LZStream, LZFlush); + + // 压缩出错之后直接结束 + // 这里也可能会返回 Z_STREAM_END(1) + // 返回 Z_STREAM_END(1) 这一次还是有数据的 + // 所以要到下次 CHUNK 函数被调用的时候再结束 + if (LZResult < 0) then + begin + AData^ := nil; + ACount^ := 0; + Exit(False); + end; + + // 已压缩完成的数据大小 + LOutSize := SND_BUF_SIZE - LZStream.avail_out; + until (LOutSize > 0); + + // 已压缩的数据 + AData^ := @LBuffer[0]; + ACount^ := LOutSize; + + Result := (ACount^ > 0); + end, + // CALLBACK + procedure(AConnection: ICrossConnection; ASuccess: Boolean) + begin + LBuffer := nil; + deflateEnd(LZStream); + + if Assigned(ACallback) then + ACallback(AConnection, ASuccess); + end); +end; + +procedure TCrossHttpResponse.SendZCompress(const ABody; ACount: NativeInt; + ACompressType: TCompressType; ACallback: TProc); +var + P: PByte; + LSize: NativeInt; +begin + P := @ABody; + LSize := ACount; + + SendZCompress( + // CHUNK + function(AData: PPointer; ACount: PNativeInt): Boolean + begin + AData^ := P; + ACount^ := Min(LSize, SND_BUF_SIZE); + Result := (ACount^ > 0); + + if (LSize > SND_BUF_SIZE) then + begin + Inc(P, SND_BUF_SIZE); + Dec(LSize, SND_BUF_SIZE); + end else + begin + LSize := 0; + P := nil; + end; + end, + ACompressType, + ACallback); +end; + +procedure TCrossHttpResponse.SendZCompress(const ABody: TBytes; AOffset, + ACount: NativeInt; ACompressType: TCompressType; + ACallback: TProc); +var + LBody: TBytes; + LOffset, LCount: NativeInt; +begin + // 增加其引用计数 + LBody := ABody; + + LOffset := AOffset; + LCount := ACount; + _AdjustOffsetCount(Length(ABody), LOffset, LCount); + + SendZCompress(LBody[LOffset], LCount, ACompressType, + // CALLBACK + procedure(AConnection: ICrossConnection; ASuccess: Boolean) + begin + // 减少引用计数 + LBody := nil; + + if Assigned(ACallback) then + ACallback(AConnection, ASuccess); + end); +end; + +procedure TCrossHttpResponse.SendZCompress(const ABody: TBytes; + ACompressType: TCompressType; ACallback: TProc); +begin + SendZCompress(ABody, 0, Length(ABody), ACompressType, ACallback); +end; + +procedure TCrossHttpResponse.SendZCompress(const ABody: TStream; const AOffset, + ACount: Int64; ACompressType: TCompressType; + ACallback: TProc); +var + LOffset, LCount: Int64; + LBody: TStream; + LBuffer: TBytes; +begin + LOffset := AOffset; + LCount := ACount; + _AdjustOffsetCount(ABody.Size, LOffset, LCount); + + if (ABody is TCustomMemoryStream) then + begin + SendZCompress(Pointer(IntPtr(TCustomMemoryStream(ABody).Memory) + LOffset)^, LCount, ACompressType, ACallback); + Exit; + end; + + LBody := ABody; + LBody.Position := LOffset; + + SetLength(LBuffer, SND_BUF_SIZE); + + SendZCompress( + // CHUNK + function(AData: PPointer; ACount: PNativeInt): Boolean + begin + if (LCount <= 0) then Exit(False); + + ACount^ := LBody.Read(LBuffer, Min(LCount, SND_BUF_SIZE)); + AData^ := @LBuffer[0]; + + Result := (ACount^ > 0); + + if Result then + Dec(LCount, ACount^); + end, + ACompressType, + // CALLBACK + procedure(AConnection: ICrossConnection; ASuccess: Boolean) + begin + LBuffer := nil; + + if Assigned(ACallback) then + ACallback(AConnection, ASuccess); + end); +end; + +procedure TCrossHttpResponse.SendZCompress(const ABody: TStream; + ACompressType: TCompressType; ACallback: TProc); +begin + SendZCompress(ABody, 0, 0, ACompressType, ACallback); +end; + +procedure TCrossHttpResponse.SendZCompress(const ABody: string; + ACompressType: TCompressType; ACallback: TProc); +var + LBody: TBytes; +begin + LBody := TEncoding.UTF8.GetBytes(ABody); + if (GetContentType = '') then + SetContentType(TMediaType.TEXT_HTML_UTF8); + + SendZCompress(LBody, ACompressType, ACallback); +end; + +end. diff --git a/ThirdParty/DCS/Net/Net.CrossHttpUtils.pas b/ThirdParty/DCS/Net/Net.CrossHttpUtils.pas new file mode 100644 index 00000000..470fdb97 --- /dev/null +++ b/ThirdParty/DCS/Net/Net.CrossHttpUtils.pas @@ -0,0 +1,1203 @@ +{******************************************************************************} +{ } +{ Delphi cross platform socket library } +{ } +{ Copyright (c) 2017 WiNDDRiVER(soulawing@gmail.com) } +{ } +{ Homepage: https://github.com/winddriver/Delphi-Cross-Socket } +{ } +{******************************************************************************} +unit Net.CrossHttpUtils; + +interface + +uses + System.SysUtils; + +type + THttpStatus = record + Code: Integer; + Text: string; + end; + + TMimeValue = record + Key: string; + Value: string; + end; + +const + {$REGION 'STATUS CODE CONST'} + STATUS_CODES: array [0..56] of THttpStatus = ( + (Code: 100; Text: 'Continue'), + (Code: 101; Text: 'Switching Protocols'), + (Code: 102; Text: 'Processing'), // RFC 2518, obsoleted by RFC 4918 + (Code: 200; Text: 'OK'), + (Code: 201; Text: 'Created'), + (Code: 202; Text: 'Accepted'), + (Code: 203; Text: 'Non-Authoritative Information'), + (Code: 204; Text: 'No Content'), + (Code: 205; Text: 'Reset Content'), + (Code: 206; Text: 'Partial Content'), + (Code: 207; Text: 'Multi-Status'), // RFC 4918 + (Code: 300; Text: 'Multiple Choices'), + (Code: 301; Text: 'Moved Permanently'), + (Code: 302; Text: 'Moved Temporarily'), + (Code: 303; Text: 'See Other'), + (Code: 304; Text: 'Not Modified'), + (Code: 305; Text: 'Use Proxy'), + (Code: 307; Text: 'Temporary Redirect'), + (Code: 308; Text: 'Permanent Redirect'), // RFC 7238 + (Code: 400; Text: 'Bad Request'), + (Code: 401; Text: 'Unauthorized'), + (Code: 402; Text: 'Payment Required'), + (Code: 403; Text: 'Forbidden'), + (Code: 404; Text: 'Not Found'), + (Code: 405; Text: 'Method Not Allowed'), + (Code: 406; Text: 'Not Acceptable'), + (Code: 407; Text: 'Proxy Authentication Required'), + (Code: 408; Text: 'Request Time-out'), + (Code: 409; Text: 'Conflict'), + (Code: 410; Text: 'Gone'), + (Code: 411; Text: 'Length Required'), + (Code: 412; Text: 'Precondition Failed'), + (Code: 413; Text: 'Request Entity Too Large'), + (Code: 414; Text: 'Request-URI Too Large'), + (Code: 415; Text: 'Unsupported Media Type'), + (Code: 416; Text: 'Requested Range Not Satisfiable'), + (Code: 417; Text: 'Expectation Failed'), + (Code: 418; Text: 'I''m a teapot'), // RFC 2324 + (Code: 422; Text: 'Unprocessable Entity'), // RFC 4918 + (Code: 423; Text: 'Locked'), // RFC 4918 + (Code: 424; Text: 'Failed Dependency'), // RFC 4918 + (Code: 425; Text: 'Unordered Collection'), // RFC 4918 + (Code: 426; Text: 'Upgrade Required'), // RFC 2817 + (Code: 428; Text: 'Precondition Required'), // RFC 6585 + (Code: 429; Text: 'Too Many Requests'), // RFC 6585 + (Code: 431; Text: 'Request Header Fields Too Large'), // RFC 6585 + (Code: 500; Text: 'Internal Server Error'), + (Code: 501; Text: 'Not Implemented'), + (Code: 502; Text: 'Bad Gateway'), + (Code: 503; Text: 'Service Unavailable'), + (Code: 504; Text: 'Gateway Time-out'), + (Code: 505; Text: 'HTTP Version Not Supported'), + (Code: 506; Text: 'Variant Also Negotiates'), // RFC 2295 + (Code: 507; Text: 'Insufficient Storage'), // RFC 4918 + (Code: 509; Text: 'Bandwidth Limit Exceeded'), + (Code: 510; Text: 'Not Extended'), // RFC 2774 + (Code: 511; Text: 'Network Authentication Required') // RFC 6585 + ); + {$ENDREGION} + + {$REGION 'MIME CONST'} + MIME_TYPES: array[0..987] of TMimeValue = ( + (Key: 'ez'; Value: 'application/andrew-inset'), // do not localize + (Key: 'aw'; Value: 'application/applixware'), // do not localize + (Key: 'atom'; Value: 'application/atom+xml'), // do not localize + (Key: 'atomcat'; Value: 'application/atomcat+xml'), // do not localize + (Key: 'atomsvc'; Value: 'application/atomsvc+xml'), // do not localize + (Key: 'ccxml'; Value: 'application/ccxml+xml'), // do not localize + (Key: 'cdmia'; Value: 'application/cdmi-capability'), // do not localize + (Key: 'cdmic'; Value: 'application/cdmi-container'), // do not localize + (Key: 'cdmid'; Value: 'application/cdmi-domain'), // do not localize + (Key: 'cdmio'; Value: 'application/cdmi-object'), // do not localize + (Key: 'cdmiq'; Value: 'application/cdmi-queue'), // do not localize + (Key: 'cu'; Value: 'application/cu-seeme'), // do not localize + (Key: 'davmount'; Value: 'application/davmount+xml'), // do not localize + (Key: 'dbk'; Value: 'application/docbook+xml'), // do not localize + (Key: 'dssc'; Value: 'application/dssc+der'), // do not localize + (Key: 'xdssc'; Value: 'application/dssc+xml'), // do not localize + (Key: 'ecma'; Value: 'application/ecmascript'), // do not localize + (Key: 'emma'; Value: 'application/emma+xml'), // do not localize + (Key: 'epub'; Value: 'application/epub+zip'), // do not localize + (Key: 'exi'; Value: 'application/exi'), // do not localize + (Key: 'pfr'; Value: 'application/font-tdpfr'), // do not localize + (Key: 'gml'; Value: 'application/gml+xml'), // do not localize + (Key: 'gpx'; Value: 'application/gpx+xml'), // do not localize + (Key: 'gxf'; Value: 'application/gxf'), // do not localize + (Key: 'stk'; Value: 'application/hyperstudio'), // do not localize + (Key: 'ink'; Value: 'application/inkml+xml'), // do not localize + (Key: 'inkml'; Value: 'application/inkml+xml'), // do not localize + (Key: 'ipfix'; Value: 'application/ipfix'), // do not localize + (Key: 'jar'; Value: 'application/java-archive'), // do not localize + (Key: 'ser'; Value: 'application/java-serialized-object'), // do not localize + (Key: 'class'; Value: 'application/java-vm'), // do not localize + (Key: 'js'; Value: 'application/javascript'), // do not localize + (Key: 'json'; Value: 'application/json'), // do not localize + (Key: 'jsonml'; Value: 'application/jsonml+json'), // do not localize + (Key: 'lostxml'; Value: 'application/lost+xml'), // do not localize + (Key: 'hqx'; Value: 'application/mac-binhex40'), // do not localize + (Key: 'cpt'; Value: 'application/mac-compactpro'), // do not localize + (Key: 'mads'; Value: 'application/mads+xml'), // do not localize + (Key: 'mrc'; Value: 'application/marc'), // do not localize + (Key: 'mrcx'; Value: 'application/marcxml+xml'), // do not localize + (Key: 'ma'; Value: 'application/mathematica'), // do not localize + (Key: 'nb'; Value: 'application/mathematica'), // do not localize + (Key: 'mb'; Value: 'application/mathematica'), // do not localize + (Key: 'mathml'; Value: 'application/mathml+xml'), // do not localize + (Key: 'mbox'; Value: 'application/mbox'), // do not localize + (Key: 'mscml'; Value: 'application/mediaservercontrol+xml'), // do not localize + (Key: 'metalink'; Value: 'application/metalink+xml'), // do not localize + (Key: 'meta4'; Value: 'application/metalink4+xml'), // do not localize + (Key: 'mets'; Value: 'application/mets+xml'), // do not localize + (Key: 'mods'; Value: 'application/mods+xml'), // do not localize + (Key: 'm21'; Value: 'application/mp21'), // do not localize + (Key: 'mp21'; Value: 'application/mp21'), // do not localize + (Key: 'mp4s'; Value: 'application/mp4'), // do not localize + (Key: 'doc'; Value: 'application/msword'), // do not localize + (Key: 'dot'; Value: 'application/msword'), // do not localize + (Key: 'mxf'; Value: 'application/mxf'), // do not localize + (Key: 'bin'; Value: 'application/octet-stream'), // do not localize + (Key: 'bpk'; Value: 'application/octet-stream'), // do not localize + (Key: 'class'; Value: 'application/octet-stream'), // do not localize + (Key: 'deploy'; Value: 'application/octet-stream'), // do not localize + (Key: 'dist'; Value: 'application/octet-stream'), // do not localize + (Key: 'distz'; Value: 'application/octet-stream'), // do not localize + (Key: 'dmg'; Value: 'application/octet-stream'), // do not localize + (Key: 'dms'; Value: 'application/octet-stream'), // do not localize + (Key: 'dump'; Value: 'application/octet-stream'), // do not localize + (Key: 'elc'; Value: 'application/octet-stream'), // do not localize + (Key: 'iso'; Value: 'application/octet-stream'), // do not localize + (Key: 'lha'; Value: 'application/octet-stream'), // do not localize + (Key: 'lrf'; Value: 'application/octet-stream'), // do not localize + (Key: 'lzh'; Value: 'application/octet-stream'), // do not localize + (Key: 'mar'; Value: 'application/octet-stream'), // do not localize + (Key: 'pkg'; Value: 'application/octet-stream'), // do not localize + (Key: 'so'; Value: 'application/octet-stream'), // do not localize + (Key: 'oda'; Value: 'application/oda'), // do not localize + (Key: 'opf'; Value: 'application/oebps-package+xml'), // do not localize + (Key: 'ogx'; Value: 'application/ogg'), // do not localize + (Key: 'omdoc'; Value: 'application/omdoc+xml'), // do not localize + (Key: 'onetoc'; Value: 'application/onenote'), // do not localize + (Key: 'onetoc2'; Value: 'application/onenote'), // do not localize + (Key: 'onetmp'; Value: 'application/onenote'), // do not localize + (Key: 'onepkg'; Value: 'application/onenote'), // do not localize + (Key: 'oxps'; Value: 'application/oxps'), // do not localize + (Key: 'xer'; Value: 'application/patch-ops-error+xml'), // do not localize + (Key: 'pdf'; Value: 'application/pdf'), // do not localize + (Key: 'pgp'; Value: 'application/pgp-encrypted'), // do not localize + (Key: 'asc'; Value: 'application/pgp-signature'), // do not localize + (Key: 'sig'; Value: 'application/pgp-signature'), // do not localize + (Key: 'prf'; Value: 'application/pics-rules'), // do not localize + (Key: 'p10'; Value: 'application/pkcs10'), // do not localize + (Key: 'p7m'; Value: 'application/pkcs7-mime'), // do not localize + (Key: 'p7c'; Value: 'application/pkcs7-mime'), // do not localize + (Key: 'p7s'; Value: 'application/pkcs7-signature'), // do not localize + (Key: 'p8'; Value: 'application/pkcs8'), // do not localize + (Key: 'ac'; Value: 'application/pkix-attr-cert'), // do not localize + (Key: 'cer'; Value: 'application/pkix-cert'), // do not localize + (Key: 'crl'; Value: 'application/pkix-crl'), // do not localize + (Key: 'pkipath'; Value: 'application/pkix-pkipath'), // do not localize + (Key: 'pki'; Value: 'application/pkixcmp'), // do not localize + (Key: 'pls'; Value: 'application/pls+xml'), // do not localize + (Key: 'ai'; Value: 'application/postscript'), // do not localize + (Key: 'eps'; Value: 'application/postscript'), // do not localize + (Key: 'ps'; Value: 'application/postscript'), // do not localize + (Key: 'cww'; Value: 'application/prs.cww'), // do not localize + (Key: 'pskcxml'; Value: 'application/pskc+xml'), // do not localize + (Key: 'rdf'; Value: 'application/rdf+xml'), // do not localize + (Key: 'rif'; Value: 'application/reginfo+xml'), // do not localize + (Key: 'rnc'; Value: 'application/relax-ng-compact-syntax'), // do not localize + (Key: 'rl'; Value: 'application/resource-lists+xml'), // do not localize + (Key: 'rld'; Value: 'application/resource-lists-diff+xml'), // do not localize + (Key: 'rs'; Value: 'application/rls-services+xml'), // do not localize + (Key: 'gbr'; Value: 'application/rpki-ghostbusters'), // do not localize + (Key: 'mft'; Value: 'application/rpki-manifest'), // do not localize + (Key: 'roa'; Value: 'application/rpki-roa'), // do not localize + (Key: 'rsd'; Value: 'application/rsd+xml'), // do not localize + (Key: 'rss'; Value: 'application/rss+xml'), // do not localize + (Key: 'rtf'; Value: 'application/rtf'), // do not localize + (Key: 'sbml'; Value: 'application/sbml+xml'), // do not localize + (Key: 'scq'; Value: 'application/scvp-cv-request'), // do not localize + (Key: 'scs'; Value: 'application/scvp-cv-response'), // do not localize + (Key: 'spq'; Value: 'application/scvp-vp-request'), // do not localize + (Key: 'spp'; Value: 'application/scvp-vp-response'), // do not localize + (Key: 'sdp'; Value: 'application/sdp'), // do not localize + (Key: 'setpay'; Value: 'application/set-payment-initiation'), // do not localize + (Key: 'setreg'; Value: 'application/set-registration-initiation'), // do not localize + (Key: 'shf'; Value: 'application/shf+xml'), // do not localize + (Key: 'smi'; Value: 'application/smil+xml'), // do not localize + (Key: 'smil'; Value: 'application/smil+xml'), // do not localize + (Key: 'rq'; Value: 'application/sparql-query'), // do not localize + (Key: 'srx'; Value: 'application/sparql-results+xml'), // do not localize + (Key: 'gram'; Value: 'application/srgs'), // do not localize + (Key: 'grxml'; Value: 'application/srgs+xml'), // do not localize + (Key: 'sru'; Value: 'application/sru+xml'), // do not localize + (Key: 'ssdl'; Value: 'application/ssdl+xml'), // do not localize + (Key: 'ssml'; Value: 'application/ssml+xml'), // do not localize + (Key: 'tei'; Value: 'application/tei+xml'), // do not localize + (Key: 'teicorpus'; Value: 'application/tei+xml'), // do not localize + (Key: 'tfi'; Value: 'application/thraud+xml'), // do not localize + (Key: 'tsd'; Value: 'application/timestamped-data'), // do not localize + (Key: 'plb'; Value: 'application/vnd.3gpp.pic-bw-large'), // do not localize + (Key: 'psb'; Value: 'application/vnd.3gpp.pic-bw-small'), // do not localize + (Key: 'pvb'; Value: 'application/vnd.3gpp.pic-bw-var'), // do not localize + (Key: 'tcap'; Value: 'application/vnd.3gpp2.tcap'), // do not localize + (Key: 'pwn'; Value: 'application/vnd.3m.post-it-notes'), // do not localize + (Key: 'aso'; Value: 'application/vnd.accpac.simply.aso'), // do not localize + (Key: 'imp'; Value: 'application/vnd.accpac.simply.imp'), // do not localize + (Key: 'acu'; Value: 'application/vnd.acucobol'), // do not localize + (Key: 'atc'; Value: 'application/vnd.acucorp'), // do not localize + (Key: 'acutc'; Value: 'application/vnd.acucorp'), // do not localize + (Key: 'air'; Value: 'application/vnd.adobe.air-application-installer-package+zip'), // do not localize + (Key: 'fcdt'; Value: 'application/vnd.adobe.formscentral.fcdt'), // do not localize + (Key: 'fxp'; Value: 'application/vnd.adobe.fxp'), // do not localize + (Key: 'fxpl'; Value: 'application/vnd.adobe.fxp'), // do not localize + (Key: 'xdp'; Value: 'application/vnd.adobe.xdp+xml'), // do not localize + (Key: 'xfdf'; Value: 'application/vnd.adobe.xfdf'), // do not localize + (Key: 'ahead'; Value: 'application/vnd.ahead.space'), // do not localize + (Key: 'azf'; Value: 'application/vnd.airzip.filesecure.azf'), // do not localize + (Key: 'azs'; Value: 'application/vnd.airzip.filesecure.azs'), // do not localize + (Key: 'azw'; Value: 'application/vnd.amazon.ebook'), // do not localize + (Key: 'acc'; Value: 'application/vnd.americandynamics.acc'), // do not localize + (Key: 'ami'; Value: 'application/vnd.amiga.ami'), // do not localize + (Key: 'apk'; Value: 'application/vnd.android.package-archive'), // do not localize + (Key: 'cii'; Value: 'application/vnd.anser-web-certificate-issue-initiation'), // do not localize + (Key: 'fti'; Value: 'application/vnd.anser-web-funds-transfer-initiation'), // do not localize + (Key: 'atx'; Value: 'application/vnd.antix.game-component'), // do not localize + (Key: 'mpkg'; Value: 'application/vnd.apple.installer+xml'), // do not localize + (Key: 'm3u8'; Value: 'application/vnd.apple.mpegurl'), // do not localize + (Key: 'swi'; Value: 'application/vnd.aristanetworks.swi'), // do not localize + (Key: 'iota'; Value: 'application/vnd.astraea-software.iota'), // do not localize + (Key: 'aep'; Value: 'application/vnd.audiograph'), // do not localize + (Key: 'mpm'; Value: 'application/vnd.blueice.multipass'), // do not localize + (Key: 'bmi'; Value: 'application/vnd.bmi'), // do not localize + (Key: 'rep'; Value: 'application/vnd.businessobjects'), // do not localize + (Key: 'cdxml'; Value: 'application/vnd.chemdraw+xml'), // do not localize + (Key: 'mmd'; Value: 'application/vnd.chipnuts.karaoke-mmd'), // do not localize + (Key: 'cdy'; Value: 'application/vnd.cinderella'), // do not localize + (Key: 'cla'; Value: 'application/vnd.claymore'), // do not localize + (Key: 'rp9'; Value: 'application/vnd.cloanto.rp9'), // do not localize + (Key: 'c4g'; Value: 'application/vnd.clonk.c4group'), // do not localize + (Key: 'c4d'; Value: 'application/vnd.clonk.c4group'), // do not localize + (Key: 'c4f'; Value: 'application/vnd.clonk.c4group'), // do not localize + (Key: 'c4p'; Value: 'application/vnd.clonk.c4group'), // do not localize + (Key: 'c4u'; Value: 'application/vnd.clonk.c4group'), // do not localize + (Key: 'c11amc'; Value: 'application/vnd.cluetrust.cartomobile-config'), // do not localize + (Key: 'c11amz'; Value: 'application/vnd.cluetrust.cartomobile-config-pkg'), // do not localize + (Key: 'csp'; Value: 'application/vnd.commonspace'), // do not localize + (Key: 'cdbcmsg'; Value: 'application/vnd.contact.cmsg'), // do not localize + (Key: 'cmc'; Value: 'application/vnd.cosmocaller'), // do not localize + (Key: 'clkx'; Value: 'application/vnd.crick.clicker'), // do not localize + (Key: 'clkk'; Value: 'application/vnd.crick.clicker.keyboard'), // do not localize + (Key: 'clkp'; Value: 'application/vnd.crick.clicker.palette'), // do not localize + (Key: 'clkt'; Value: 'application/vnd.crick.clicker.template'), // do not localize + (Key: 'clkw'; Value: 'application/vnd.crick.clicker.wordbank'), // do not localize + (Key: 'wbs'; Value: 'application/vnd.criticaltools.wbs+xml'), // do not localize + (Key: 'pml'; Value: 'application/vnd.ctc-posml'), // do not localize + (Key: 'ppd'; Value: 'application/vnd.cups-ppd'), // do not localize + (Key: 'car'; Value: 'application/vnd.curl.car'), // do not localize + (Key: 'pcurl'; Value: 'application/vnd.curl.pcurl'), // do not localize + (Key: 'dart'; Value: 'application/vnd.dart'), // do not localize + (Key: 'rdz'; Value: 'application/vnd.data-vision.rdz'), // do not localize + (Key: 'uvf'; Value: 'application/vnd.dece.data'), // do not localize + (Key: 'uvvf'; Value: 'application/vnd.dece.data'), // do not localize + (Key: 'uvd'; Value: 'application/vnd.dece.data'), // do not localize + (Key: 'uvvd'; Value: 'application/vnd.dece.data'), // do not localize + (Key: 'uvt'; Value: 'application/vnd.dece.ttml+xml'), // do not localize + (Key: 'uvvt'; Value: 'application/vnd.dece.ttml+xml'), // do not localize + (Key: 'uvx'; Value: 'application/vnd.dece.unspecified'), // do not localize + (Key: 'uvvx'; Value: 'application/vnd.dece.unspecified'), // do not localize + (Key: 'uvz'; Value: 'application/vnd.dece.zip'), // do not localize + (Key: 'uvvz'; Value: 'application/vnd.dece.zip'), // do not localize + (Key: 'fe_launch'; Value: 'application/vnd.denovo.fcselayout-link'), // do not localize + (Key: 'dna'; Value: 'application/vnd.dna'), // do not localize + (Key: 'mlp'; Value: 'application/vnd.dolby.mlp'), // do not localize + (Key: 'dpg'; Value: 'application/vnd.dpgraph'), // do not localize + (Key: 'dfac'; Value: 'application/vnd.dreamfactory'), // do not localize + (Key: 'kpxx'; Value: 'application/vnd.ds-keypoint'), // do not localize + (Key: 'ait'; Value: 'application/vnd.dvb.ait'), // do not localize + (Key: 'svc'; Value: 'application/vnd.dvb.service'), // do not localize + (Key: 'geo'; Value: 'application/vnd.dynageo'), // do not localize + (Key: 'mag'; Value: 'application/vnd.ecowin.chart'), // do not localize + (Key: 'nml'; Value: 'application/vnd.enliven'), // do not localize + (Key: 'esf'; Value: 'application/vnd.epson.esf'), // do not localize + (Key: 'msf'; Value: 'application/vnd.epson.msf'), // do not localize + (Key: 'qam'; Value: 'application/vnd.epson.quickanime'), // do not localize + (Key: 'slt'; Value: 'application/vnd.epson.salt'), // do not localize + (Key: 'ssf'; Value: 'application/vnd.epson.ssf'), // do not localize + (Key: 'es3'; Value: 'application/vnd.eszigno3+xml'), // do not localize + (Key: 'et3'; Value: 'application/vnd.eszigno3+xml'), // do not localize + (Key: 'ez2'; Value: 'application/vnd.ezpix-album'), // do not localize + (Key: 'ez3'; Value: 'application/vnd.ezpix-package'), // do not localize + (Key: 'fdf'; Value: 'application/vnd.fdf'), // do not localize + (Key: 'mseed'; Value: 'application/vnd.fdsn.mseed'), // do not localize + (Key: 'seed'; Value: 'application/vnd.fdsn.seed'), // do not localize + (Key: 'dataless'; Value: 'application/vnd.fdsn.seed'), // do not localize + (Key: 'gph'; Value: 'application/vnd.flographit'), // do not localize + (Key: 'ftc'; Value: 'application/vnd.fluxtime.clip'), // do not localize + (Key: 'fm'; Value: 'application/vnd.framemaker'), // do not localize + (Key: 'frame'; Value: 'application/vnd.framemaker'), // do not localize + (Key: 'maker'; Value: 'application/vnd.framemaker'), // do not localize + (Key: 'book'; Value: 'application/vnd.framemaker'), // do not localize + (Key: 'fnc'; Value: 'application/vnd.frogans.fnc'), // do not localize + (Key: 'ltf'; Value: 'application/vnd.frogans.ltf'), // do not localize + (Key: 'fsc'; Value: 'application/vnd.fsc.weblaunch'), // do not localize + (Key: 'oas'; Value: 'application/vnd.fujitsu.oasys'), // do not localize + (Key: 'oa2'; Value: 'application/vnd.fujitsu.oasys2'), // do not localize + (Key: 'oa3'; Value: 'application/vnd.fujitsu.oasys3'), // do not localize + (Key: 'fg5'; Value: 'application/vnd.fujitsu.oasysgp'), // do not localize + (Key: 'bh2'; Value: 'application/vnd.fujitsu.oasysprs'), // do not localize + (Key: 'ddd'; Value: 'application/vnd.fujixerox.ddd'), // do not localize + (Key: 'xdw'; Value: 'application/vnd.fujixerox.docuworks'), // do not localize + (Key: 'xbd'; Value: 'application/vnd.fujixerox.docuworks.binder'), // do not localize + (Key: 'fzs'; Value: 'application/vnd.fuzzysheet'), // do not localize + (Key: 'txd'; Value: 'application/vnd.genomatix.tuxedo'), // do not localize + (Key: 'ggb'; Value: 'application/vnd.geogebra.file'), // do not localize + (Key: 'ggt'; Value: 'application/vnd.geogebra.tool'), // do not localize + (Key: 'gex'; Value: 'application/vnd.geometry-explorer'), // do not localize + (Key: 'gre'; Value: 'application/vnd.geometry-explorer'), // do not localize + (Key: 'gxt'; Value: 'application/vnd.geonext'), // do not localize + (Key: 'g2w'; Value: 'application/vnd.geoplan'), // do not localize + (Key: 'g3w'; Value: 'application/vnd.geospace'), // do not localize + (Key: 'gmx'; Value: 'application/vnd.gmx'), // do not localize + (Key: 'kml'; Value: 'application/vnd.google-earth.kml+xml'), // do not localize + (Key: 'kmz'; Value: 'application/vnd.google-earth.kmz'), // do not localize + (Key: 'gqf'; Value: 'application/vnd.grafeq'), // do not localize + (Key: 'gqs'; Value: 'application/vnd.grafeq'), // do not localize + (Key: 'gac'; Value: 'application/vnd.groove-account'), // do not localize + (Key: 'ghf'; Value: 'application/vnd.groove-help'), // do not localize + (Key: 'gim'; Value: 'application/vnd.groove-identity-message'), // do not localize + (Key: 'grv'; Value: 'application/vnd.groove-injector'), // do not localize + (Key: 'gtm'; Value: 'application/vnd.groove-tool-message'), // do not localize + (Key: 'tpl'; Value: 'application/vnd.groove-tool-template'), // do not localize + (Key: 'vcg'; Value: 'application/vnd.groove-vcard'), // do not localize + (Key: 'hal'; Value: 'application/vnd.hal+xml'), // do not localize + (Key: 'zmm'; Value: 'application/vnd.handheld-entertainment+xml'), // do not localize + (Key: 'hbci'; Value: 'application/vnd.hbci'), // do not localize + (Key: 'les'; Value: 'application/vnd.hhe.lesson-player'), // do not localize + (Key: 'hpgl'; Value: 'application/vnd.hp-hpgl'), // do not localize + (Key: 'hpid'; Value: 'application/vnd.hp-hpid'), // do not localize + (Key: 'hps'; Value: 'application/vnd.hp-hps'), // do not localize + (Key: 'jlt'; Value: 'application/vnd.hp-jlyt'), // do not localize + (Key: 'pcl'; Value: 'application/vnd.hp-pcl'), // do not localize + (Key: 'pclxl'; Value: 'application/vnd.hp-pclxl'), // do not localize + (Key: 'sfd-hdstx'; Value: 'application/vnd.hydrostatix.sof-data'), // do not localize + (Key: 'mpy'; Value: 'application/vnd.ibm.minipay'), // do not localize + (Key: 'afp'; Value: 'application/vnd.ibm.modcap'), // do not localize + (Key: 'listafp'; Value: 'application/vnd.ibm.modcap'), // do not localize + (Key: 'list3820'; Value: 'application/vnd.ibm.modcap'), // do not localize + (Key: 'irm'; Value: 'application/vnd.ibm.rights-management'), // do not localize + (Key: 'sc'; Value: 'application/vnd.ibm.secure-container'), // do not localize + (Key: 'icc'; Value: 'application/vnd.iccprofile'), // do not localize + (Key: 'icm'; Value: 'application/vnd.iccprofile'), // do not localize + (Key: 'igl'; Value: 'application/vnd.igloader'), // do not localize + (Key: 'ivp'; Value: 'application/vnd.immervision-ivp'), // do not localize + (Key: 'ivu'; Value: 'application/vnd.immervision-ivu'), // do not localize + (Key: 'igm'; Value: 'application/vnd.insors.igm'), // do not localize + (Key: 'xpw'; Value: 'application/vnd.intercon.formnet'), // do not localize + (Key: 'xpx'; Value: 'application/vnd.intercon.formnet'), // do not localize + (Key: 'i2g'; Value: 'application/vnd.intergeo'), // do not localize + (Key: 'qbo'; Value: 'application/vnd.intu.qbo'), // do not localize + (Key: 'qfx'; Value: 'application/vnd.intu.qfx'), // do not localize + (Key: 'rcprofile'; Value: 'application/vnd.ipunplugged.rcprofile'), // do not localize + (Key: 'irp'; Value: 'application/vnd.irepository.package+xml'), // do not localize + (Key: 'xpr'; Value: 'application/vnd.is-xpr'), // do not localize + (Key: 'fcs'; Value: 'application/vnd.isac.fcs'), // do not localize + (Key: 'jam'; Value: 'application/vnd.jam'), // do not localize + (Key: 'rms'; Value: 'application/vnd.jcp.javame.midlet-rms'), // do not localize + (Key: 'jisp'; Value: 'application/vnd.jisp'), // do not localize + (Key: 'joda'; Value: 'application/vnd.joost.joda-archive'), // do not localize + (Key: 'ktz'; Value: 'application/vnd.kahootz'), // do not localize + (Key: 'ktr'; Value: 'application/vnd.kahootz'), // do not localize + (Key: 'karbon'; Value: 'application/vnd.kde.karbon'), // do not localize + (Key: 'chrt'; Value: 'application/vnd.kde.kchart'), // do not localize + (Key: 'kfo'; Value: 'application/vnd.kde.kformula'), // do not localize + (Key: 'flw'; Value: 'application/vnd.kde.kivio'), // do not localize + (Key: 'kon'; Value: 'application/vnd.kde.kontour'), // do not localize + (Key: 'kpr'; Value: 'application/vnd.kde.kpresenter'), // do not localize + (Key: 'kpt'; Value: 'application/vnd.kde.kpresenter'), // do not localize + (Key: 'ksp'; Value: 'application/vnd.kde.kspread'), // do not localize + (Key: 'kwd'; Value: 'application/vnd.kde.kword'), // do not localize + (Key: 'kwt'; Value: 'application/vnd.kde.kword'), // do not localize + (Key: 'htke'; Value: 'application/vnd.kenameaapp'), // do not localize + (Key: 'kia'; Value: 'application/vnd.kidspiration'), // do not localize + (Key: 'kne'; Value: 'application/vnd.kinar'), // do not localize + (Key: 'knp'; Value: 'application/vnd.kinar'), // do not localize + (Key: 'skp'; Value: 'application/vnd.koan'), // do not localize + (Key: 'skd'; Value: 'application/vnd.koan'), // do not localize + (Key: 'skt'; Value: 'application/vnd.koan'), // do not localize + (Key: 'skm'; Value: 'application/vnd.koan'), // do not localize + (Key: 'sse'; Value: 'application/vnd.kodak-descriptor'), // do not localize + (Key: 'lasxml'; Value: 'application/vnd.las.las+xml'), // do not localize + (Key: 'lbd'; Value: 'application/vnd.llamagraphics.life-balance.desktop'), // do not localize + (Key: 'lbe'; Value: 'application/vnd.llamagraphics.life-balance.exchange+xml'), // do not localize + (Key: '123'; Value: 'application/vnd.lotus-1-2-3'), // do not localize + (Key: 'apr'; Value: 'application/vnd.lotus-approach'), // do not localize + (Key: 'pre'; Value: 'application/vnd.lotus-freelance'), // do not localize + (Key: 'nsf'; Value: 'application/vnd.lotus-notes'), // do not localize + (Key: 'org'; Value: 'application/vnd.lotus-organizer'), // do not localize + (Key: 'scm'; Value: 'application/vnd.lotus-screencam'), // do not localize + (Key: 'lwp'; Value: 'application/vnd.lotus-wordpro'), // do not localize + (Key: 'portpkg'; Value: 'application/vnd.macports.portpkg'), // do not localize + (Key: 'mcd'; Value: 'application/vnd.mcd'), // do not localize + (Key: 'mc1'; Value: 'application/vnd.medcalcdata'), // do not localize + (Key: 'cdkey'; Value: 'application/vnd.mediastation.cdkey'), // do not localize + (Key: 'mwf'; Value: 'application/vnd.mfer'), // do not localize + (Key: 'mfm'; Value: 'application/vnd.mfmp'), // do not localize + (Key: 'flo'; Value: 'application/vnd.micrografx.flo'), // do not localize + (Key: 'igx'; Value: 'application/vnd.micrografx.igx'), // do not localize + (Key: 'mif'; Value: 'application/vnd.mif'), // do not localize + (Key: 'daf'; Value: 'application/vnd.mobius.daf'), // do not localize + (Key: 'dis'; Value: 'application/vnd.mobius.dis'), // do not localize + (Key: 'mbk'; Value: 'application/vnd.mobius.mbk'), // do not localize + (Key: 'mqy'; Value: 'application/vnd.mobius.mqy'), // do not localize + (Key: 'msl'; Value: 'application/vnd.mobius.msl'), // do not localize + (Key: 'plc'; Value: 'application/vnd.mobius.plc'), // do not localize + (Key: 'txf'; Value: 'application/vnd.mobius.txf'), // do not localize + (Key: 'mpn'; Value: 'application/vnd.mophun.application'), // do not localize + (Key: 'mpc'; Value: 'application/vnd.mophun.certificate'), // do not localize + (Key: 'xul'; Value: 'application/vnd.mozilla.xul+xml'), // do not localize + (Key: 'cil'; Value: 'application/vnd.ms-artgalry'), // do not localize + (Key: 'cab'; Value: 'application/vnd.ms-cab-compressed'), // do not localize + (Key: 'xls'; Value: 'application/vnd.ms-excel'), // do not localize + (Key: 'xlm'; Value: 'application/vnd.ms-excel'), // do not localize + (Key: 'xla'; Value: 'application/vnd.ms-excel'), // do not localize + (Key: 'xlc'; Value: 'application/vnd.ms-excel'), // do not localize + (Key: 'xlt'; Value: 'application/vnd.ms-excel'), // do not localize + (Key: 'xlw'; Value: 'application/vnd.ms-excel'), // do not localize + (Key: 'xlam'; Value: 'application/vnd.ms-excel.addin.macroenabled.12'), // do not localize + (Key: 'xlsb'; Value: 'application/vnd.ms-excel.sheet.binary.macroenabled.12'), // do not localize + (Key: 'xlsm'; Value: 'application/vnd.ms-excel.sheet.macroenabled.12'), // do not localize + (Key: 'xltm'; Value: 'application/vnd.ms-excel.template.macroenabled.12'), // do not localize + (Key: 'eot'; Value: 'application/vnd.ms-fontobject'), // do not localize + (Key: 'chm'; Value: 'application/vnd.ms-htmlhelp'), // do not localize + (Key: 'ims'; Value: 'application/vnd.ms-ims'), // do not localize + (Key: 'lrm'; Value: 'application/vnd.ms-lrm'), // do not localize + (Key: 'thmx'; Value: 'application/vnd.ms-officetheme'), // do not localize + (Key: 'cat'; Value: 'application/vnd.ms-pki.seccat'), // do not localize + (Key: 'stl'; Value: 'application/vnd.ms-pki.stl'), // do not localize + (Key: 'ppt'; Value: 'application/vnd.ms-powerpoint'), // do not localize + (Key: 'pps'; Value: 'application/vnd.ms-powerpoint'), // do not localize + (Key: 'pot'; Value: 'application/vnd.ms-powerpoint'), // do not localize + (Key: 'ppam'; Value: 'application/vnd.ms-powerpoint.addin.macroenabled.12'), // do not localize + (Key: 'pptm'; Value: 'application/vnd.ms-powerpoint.presentation.macroenabled.12'), // do not localize + (Key: 'sldm'; Value: 'application/vnd.ms-powerpoint.slide.macroenabled.12'), // do not localize + (Key: 'ppsm'; Value: 'application/vnd.ms-powerpoint.slideshow.macroenabled.12'), // do not localize + (Key: 'potm'; Value: 'application/vnd.ms-powerpoint.template.macroenabled.12'), // do not localize + (Key: 'mpp'; Value: 'application/vnd.ms-project'), // do not localize + (Key: 'mpt'; Value: 'application/vnd.ms-project'), // do not localize + (Key: 'docm'; Value: 'application/vnd.ms-word.document.macroenabled.12'), // do not localize + (Key: 'dotm'; Value: 'application/vnd.ms-word.template.macroenabled.12'), // do not localize + (Key: 'wps'; Value: 'application/vnd.ms-works'), // do not localize + (Key: 'wks'; Value: 'application/vnd.ms-works'), // do not localize + (Key: 'wcm'; Value: 'application/vnd.ms-works'), // do not localize + (Key: 'wdb'; Value: 'application/vnd.ms-works'), // do not localize + (Key: 'wpl'; Value: 'application/vnd.ms-wpl'), // do not localize + (Key: 'xps'; Value: 'application/vnd.ms-xpsdocument'), // do not localize + (Key: 'mseq'; Value: 'application/vnd.mseq'), // do not localize + (Key: 'mus'; Value: 'application/vnd.musician'), // do not localize + (Key: 'msty'; Value: 'application/vnd.muvee.style'), // do not localize + (Key: 'taglet'; Value: 'application/vnd.mynfc'), // do not localize + (Key: 'nlu'; Value: 'application/vnd.neurolanguage.nlu'), // do not localize + (Key: 'ntf'; Value: 'application/vnd.nitf'), // do not localize + (Key: 'nitf'; Value: 'application/vnd.nitf'), // do not localize + (Key: 'nnd'; Value: 'application/vnd.noblenet-directory'), // do not localize + (Key: 'nns'; Value: 'application/vnd.noblenet-sealer'), // do not localize + (Key: 'nnw'; Value: 'application/vnd.noblenet-web'), // do not localize + (Key: 'ngdat'; Value: 'application/vnd.nokia.n-gage.data'), // do not localize + (Key: 'n-gage'; Value: 'application/vnd.nokia.n-gage.symbian.install'), // do not localize + (Key: 'rpst'; Value: 'application/vnd.nokia.radio-preset'), // do not localize + (Key: 'rpss'; Value: 'application/vnd.nokia.radio-presets'), // do not localize + (Key: 'edm'; Value: 'application/vnd.novadigm.edm'), // do not localize + (Key: 'edx'; Value: 'application/vnd.novadigm.edx'), // do not localize + (Key: 'ext'; Value: 'application/vnd.novadigm.ext'), // do not localize + (Key: 'odc'; Value: 'application/vnd.oasis.opendocument.chart'), // do not localize + (Key: 'otc'; Value: 'application/vnd.oasis.opendocument.chart-template'), // do not localize + (Key: 'odb'; Value: 'application/vnd.oasis.opendocument.database'), // do not localize + (Key: 'odf'; Value: 'application/vnd.oasis.opendocument.formula'), // do not localize + (Key: 'odft'; Value: 'application/vnd.oasis.opendocument.formula-template'), // do not localize + (Key: 'odg'; Value: 'application/vnd.oasis.opendocument.graphics'), // do not localize + (Key: 'otg'; Value: 'application/vnd.oasis.opendocument.graphics-template'), // do not localize + (Key: 'odi'; Value: 'application/vnd.oasis.opendocument.image'), // do not localize + (Key: 'oti'; Value: 'application/vnd.oasis.opendocument.image-template'), // do not localize + (Key: 'odp'; Value: 'application/vnd.oasis.opendocument.presentation'), // do not localize + (Key: 'otp'; Value: 'application/vnd.oasis.opendocument.presentation-template'), // do not localize + (Key: 'ods'; Value: 'application/vnd.oasis.opendocument.spreadsheet'), // do not localize + (Key: 'ots'; Value: 'application/vnd.oasis.opendocument.spreadsheet-template'), // do not localize + (Key: 'odt'; Value: 'application/vnd.oasis.opendocument.text'), // do not localize + (Key: 'odm'; Value: 'application/vnd.oasis.opendocument.text-master'), // do not localize + (Key: 'ott'; Value: 'application/vnd.oasis.opendocument.text-template'), // do not localize + (Key: 'oth'; Value: 'application/vnd.oasis.opendocument.text-web'), // do not localize + (Key: 'xo'; Value: 'application/vnd.olpc-sugar'), // do not localize + (Key: 'dd2'; Value: 'application/vnd.oma.dd2+xml'), // do not localize + (Key: 'oxt'; Value: 'application/vnd.openofficeorg.extension'), // do not localize + (Key: 'pptx'; Value: 'application/vnd.openxmlformats-officedocument.presentationml.presentation'), // do not localize + (Key: 'sldx'; Value: 'application/vnd.openxmlformats-officedocument.presentationml.slide'), // do not localize + (Key: 'ppsx'; Value: 'application/vnd.openxmlformats-officedocument.presentationml.slideshow'), // do not localize + (Key: 'potx'; Value: 'application/vnd.openxmlformats-officedocument.presentationml.template'), // do not localize + (Key: 'xlsx'; Value: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'), // do not localize + (Key: 'xltx'; Value: 'application/vnd.openxmlformats-officedocument.spreadsheetml.template'), // do not localize + (Key: 'docx'; Value: 'application/vnd.openxmlformats-officedocument.wordprocessingml.document'), // do not localize + (Key: 'dotx'; Value: 'application/vnd.openxmlformats-officedocument.wordprocessingml.template'), // do not localize + (Key: 'mgp'; Value: 'application/vnd.osgeo.mapguide.package'), // do not localize + (Key: 'dp'; Value: 'application/vnd.osgi.dp'), // do not localize + (Key: 'esa'; Value: 'application/vnd.osgi.subsystem'), // do not localize + (Key: 'pdb'; Value: 'application/vnd.palm'), // do not localize + (Key: 'pqa'; Value: 'application/vnd.palm'), // do not localize + (Key: 'oprc'; Value: 'application/vnd.palm'), // do not localize + (Key: 'paw'; Value: 'application/vnd.pawaafile'), // do not localize + (Key: 'str'; Value: 'application/vnd.pg.format'), // do not localize + (Key: 'ei6'; Value: 'application/vnd.pg.osasli'), // do not localize + (Key: 'efif'; Value: 'application/vnd.picsel'), // do not localize + (Key: 'wg'; Value: 'application/vnd.pmi.widget'), // do not localize + (Key: 'plf'; Value: 'application/vnd.pocketlearn'), // do not localize + (Key: 'pbd'; Value: 'application/vnd.powerbuilder6'), // do not localize + (Key: 'box'; Value: 'application/vnd.previewsystems.box'), // do not localize + (Key: 'mgz'; Value: 'application/vnd.proteus.magazine'), // do not localize + (Key: 'qps'; Value: 'application/vnd.publishare-delta-tree'), // do not localize + (Key: 'ptid'; Value: 'application/vnd.pvi.ptid1'), // do not localize + (Key: 'qxd'; Value: 'application/vnd.quark.quarkxpress'), // do not localize + (Key: 'qxt'; Value: 'application/vnd.quark.quarkxpress'), // do not localize + (Key: 'qwd'; Value: 'application/vnd.quark.quarkxpress'), // do not localize + (Key: 'qwt'; Value: 'application/vnd.quark.quarkxpress'), // do not localize + (Key: 'qxl'; Value: 'application/vnd.quark.quarkxpress'), // do not localize + (Key: 'qxb'; Value: 'application/vnd.quark.quarkxpress'), // do not localize + (Key: 'bed'; Value: 'application/vnd.realvnc.bed'), // do not localize + (Key: 'mxl'; Value: 'application/vnd.recordare.musicxml'), // do not localize + (Key: 'musicxml'; Value: 'application/vnd.recordare.musicxml+xml'), // do not localize + (Key: 'cryptonote'; Value: 'application/vnd.rig.cryptonote'), // do not localize + (Key: 'cod'; Value: 'application/vnd.rim.cod'), // do not localize + (Key: 'rm'; Value: 'application/vnd.rn-realmedia'), // do not localize + (Key: 'rmvb'; Value: 'application/vnd.rn-realmedia-vbr'), // do not localize + (Key: 'link66'; Value: 'application/vnd.route66.link66+xml'), // do not localize + (Key: 'st'; Value: 'application/vnd.sailingtracker.track'), // do not localize + (Key: 'see'; Value: 'application/vnd.seemail'), // do not localize + (Key: 'sema'; Value: 'application/vnd.sema'), // do not localize + (Key: 'semd'; Value: 'application/vnd.semd'), // do not localize + (Key: 'semf'; Value: 'application/vnd.semf'), // do not localize + (Key: 'ifm'; Value: 'application/vnd.shana.informed.formdata'), // do not localize + (Key: 'itp'; Value: 'application/vnd.shana.informed.formtemplate'), // do not localize + (Key: 'iif'; Value: 'application/vnd.shana.informed.interchange'), // do not localize + (Key: 'ipk'; Value: 'application/vnd.shana.informed.package'), // do not localize + (Key: 'twd'; Value: 'application/vnd.simtech-mindmapper'), // do not localize + (Key: 'twds'; Value: 'application/vnd.simtech-mindmapper'), // do not localize + (Key: 'mmf'; Value: 'application/vnd.smaf'), // do not localize + (Key: 'teacher'; Value: 'application/vnd.smart.teacher'), // do not localize + (Key: 'sdkm'; Value: 'application/vnd.solent.sdkm+xml'), // do not localize + (Key: 'sdkd'; Value: 'application/vnd.solent.sdkm+xml'), // do not localize + (Key: 'dxp'; Value: 'application/vnd.spotfire.dxp'), // do not localize + (Key: 'sfs'; Value: 'application/vnd.spotfire.sfs'), // do not localize + (Key: 'sdc'; Value: 'application/vnd.stardivision.calc'), // do not localize + (Key: 'sda'; Value: 'application/vnd.stardivision.draw'), // do not localize + (Key: 'sdd'; Value: 'application/vnd.stardivision.impress'), // do not localize + (Key: 'smf'; Value: 'application/vnd.stardivision.math'), // do not localize + (Key: 'sdw'; Value: 'application/vnd.stardivision.writer'), // do not localize + (Key: 'vor'; Value: 'application/vnd.stardivision.writer'), // do not localize + (Key: 'sgl'; Value: 'application/vnd.stardivision.writer-global'), // do not localize + (Key: 'smzip'; Value: 'application/vnd.stepmania.package'), // do not localize + (Key: 'sm'; Value: 'application/vnd.stepmania.stepchart'), // do not localize + (Key: 'sxc'; Value: 'application/vnd.sun.xml.calc'), // do not localize + (Key: 'stc'; Value: 'application/vnd.sun.xml.calc.template'), // do not localize + (Key: 'sxd'; Value: 'application/vnd.sun.xml.draw'), // do not localize + (Key: 'std'; Value: 'application/vnd.sun.xml.draw.template'), // do not localize + (Key: 'sxi'; Value: 'application/vnd.sun.xml.impress'), // do not localize + (Key: 'sti'; Value: 'application/vnd.sun.xml.impress.template'), // do not localize + (Key: 'sxm'; Value: 'application/vnd.sun.xml.math'), // do not localize + (Key: 'sxw'; Value: 'application/vnd.sun.xml.writer'), // do not localize + (Key: 'sxg'; Value: 'application/vnd.sun.xml.writer.global'), // do not localize + (Key: 'stw'; Value: 'application/vnd.sun.xml.writer.template'), // do not localize + (Key: 'sus'; Value: 'application/vnd.sus-calendar'), // do not localize + (Key: 'susp'; Value: 'application/vnd.sus-calendar'), // do not localize + (Key: 'svd'; Value: 'application/vnd.svd'), // do not localize + (Key: 'sis'; Value: 'application/vnd.symbian.install'), // do not localize + (Key: 'sisx'; Value: 'application/vnd.symbian.install'), // do not localize + (Key: 'xsm'; Value: 'application/vnd.syncml+xml'), // do not localize + (Key: 'bdm'; Value: 'application/vnd.syncml.dm+wbxml'), // do not localize + (Key: 'xdm'; Value: 'application/vnd.syncml.dm+xml'), // do not localize + (Key: 'tao'; Value: 'application/vnd.tao.intent-module-archive'), // do not localize + (Key: 'pcap'; Value: 'application/vnd.tcpdump.pcap'), // do not localize + (Key: 'cap'; Value: 'application/vnd.tcpdump.pcap'), // do not localize + (Key: 'dmp'; Value: 'application/vnd.tcpdump.pcap'), // do not localize + (Key: 'tmo'; Value: 'application/vnd.tmobile-livetv'), // do not localize + (Key: 'tpt'; Value: 'application/vnd.trid.tpt'), // do not localize + (Key: 'mxs'; Value: 'application/vnd.triscape.mxs'), // do not localize + (Key: 'tra'; Value: 'application/vnd.trueapp'), // do not localize + (Key: 'ufd'; Value: 'application/vnd.ufdl'), // do not localize + (Key: 'ufdl'; Value: 'application/vnd.ufdl'), // do not localize + (Key: 'utz'; Value: 'application/vnd.uiq.theme'), // do not localize + (Key: 'umj'; Value: 'application/vnd.umajin'), // do not localize + (Key: 'unityweb'; Value: 'application/vnd.unity'), // do not localize + (Key: 'uoml'; Value: 'application/vnd.uoml+xml'), // do not localize + (Key: 'vcx'; Value: 'application/vnd.vcx'), // do not localize + (Key: 'vsd'; Value: 'application/vnd.visio'), // do not localize + (Key: 'vst'; Value: 'application/vnd.visio'), // do not localize + (Key: 'vss'; Value: 'application/vnd.visio'), // do not localize + (Key: 'vsw'; Value: 'application/vnd.visio'), // do not localize + (Key: 'vis'; Value: 'application/vnd.visionary'), // do not localize + (Key: 'vsf'; Value: 'application/vnd.vsf'), // do not localize + (Key: 'wbxml'; Value: 'application/vnd.wap.wbxml'), // do not localize + (Key: 'wmlc'; Value: 'application/vnd.wap.wmlc'), // do not localize + (Key: 'wmlsc'; Value: 'application/vnd.wap.wmlscriptc'), // do not localize + (Key: 'wtb'; Value: 'application/vnd.webturbo'), // do not localize + (Key: 'nbp'; Value: 'application/vnd.wolfram.player'), // do not localize + (Key: 'wpd'; Value: 'application/vnd.wordperfect'), // do not localize + (Key: 'wqd'; Value: 'application/vnd.wqd'), // do not localize + (Key: 'stf'; Value: 'application/vnd.wt.stf'), // do not localize + (Key: 'xar'; Value: 'application/vnd.xara'), // do not localize + (Key: 'xfdl'; Value: 'application/vnd.xfdl'), // do not localize + (Key: 'hvd'; Value: 'application/vnd.yamaha.hv-dic'), // do not localize + (Key: 'hvs'; Value: 'application/vnd.yamaha.hv-script'), // do not localize + (Key: 'hvp'; Value: 'application/vnd.yamaha.hv-voice'), // do not localize + (Key: 'osf'; Value: 'application/vnd.yamaha.openscoreformat'), // do not localize + (Key: 'osfpvg'; Value: 'application/vnd.yamaha.openscoreformat.osfpvg+xml'), // do not localize + (Key: 'saf'; Value: 'application/vnd.yamaha.smaf-audio'), // do not localize + (Key: 'spf'; Value: 'application/vnd.yamaha.smaf-phrase'), // do not localize + (Key: 'cmp'; Value: 'application/vnd.yellowriver-custom-menu'), // do not localize + (Key: 'zir'; Value: 'application/vnd.zul'), // do not localize + (Key: 'zirz'; Value: 'application/vnd.zul'), // do not localize + (Key: 'zaz'; Value: 'application/vnd.zzazz.deck+xml'), // do not localize + (Key: 'vxml'; Value: 'application/voicexml+xml'), // do not localize + (Key: 'wgt'; Value: 'application/widget'), // do not localize + (Key: 'hlp'; Value: 'application/winhlp'), // do not localize + (Key: 'wsdl'; Value: 'application/wsdl+xml'), // do not localize + (Key: 'wspolicy'; Value: 'application/wspolicy+xml'), // do not localize + (Key: '7z'; Value: 'application/x-7z-compressed'), // do not localize + (Key: 'abw'; Value: 'application/x-abiword'), // do not localize + (Key: 'ace'; Value: 'application/x-ace-compressed'), // do not localize + (Key: 'dmg'; Value: 'application/x-apple-diskimage'), // do not localize + (Key: 'aab'; Value: 'application/x-authorware-bin'), // do not localize + (Key: 'x32'; Value: 'application/x-authorware-bin'), // do not localize + (Key: 'u32'; Value: 'application/x-authorware-bin'), // do not localize + (Key: 'vox'; Value: 'application/x-authorware-bin'), // do not localize + (Key: 'aam'; Value: 'application/x-authorware-map'), // do not localize + (Key: 'aas'; Value: 'application/x-authorware-seg'), // do not localize + (Key: 'bcpio'; Value: 'application/x-bcpio'), // do not localize + (Key: 'torrent'; Value: 'application/x-bittorrent'), // do not localize + (Key: 'blb'; Value: 'application/x-blorb'), // do not localize + (Key: 'blorb'; Value: 'application/x-blorb'), // do not localize + (Key: 'bz'; Value: 'application/x-bzip'), // do not localize + (Key: 'bz2'; Value: 'application/x-bzip2'), // do not localize + (Key: 'boz'; Value: 'application/x-bzip2'), // do not localize + (Key: 'cbr'; Value: 'application/x-cbr'), // do not localize + (Key: 'cba'; Value: 'application/x-cbr'), // do not localize + (Key: 'cbt'; Value: 'application/x-cbr'), // do not localize + (Key: 'cbz'; Value: 'application/x-cbr'), // do not localize + (Key: 'cb7'; Value: 'application/x-cbr'), // do not localize + (Key: 'vcd'; Value: 'application/x-cdlink'), // do not localize + (Key: 'cfs'; Value: 'application/x-cfs-compressed'), // do not localize + (Key: 'chat'; Value: 'application/x-chat'), // do not localize + (Key: 'pgn'; Value: 'application/x-chess-pgn'), // do not localize + (Key: 'nsc'; Value: 'application/x-conference'), // do not localize + (Key: 'cpio'; Value: 'application/x-cpio'), // do not localize + (Key: 'csh'; Value: 'application/x-csh'), // do not localize + (Key: 'deb'; Value: 'application/x-debian-package'), // do not localize + (Key: 'udeb'; Value: 'application/x-debian-package'), // do not localize + (Key: 'dgc'; Value: 'application/x-dgc-compressed'), // do not localize + (Key: 'dir'; Value: 'application/x-director'), // do not localize + (Key: 'dcr'; Value: 'application/x-director'), // do not localize + (Key: 'dxr'; Value: 'application/x-director'), // do not localize + (Key: 'cst'; Value: 'application/x-director'), // do not localize + (Key: 'cct'; Value: 'application/x-director'), // do not localize + (Key: 'cxt'; Value: 'application/x-director'), // do not localize + (Key: 'w3d'; Value: 'application/x-director'), // do not localize + (Key: 'fgd'; Value: 'application/x-director'), // do not localize + (Key: 'swa'; Value: 'application/x-director'), // do not localize + (Key: 'wad'; Value: 'application/x-doom'), // do not localize + (Key: 'ncx'; Value: 'application/x-dtbncx+xml'), // do not localize + (Key: 'dtb'; Value: 'application/x-dtbook+xml'), // do not localize + (Key: 'res'; Value: 'application/x-dtbresource+xml'), // do not localize + (Key: 'dvi'; Value: 'application/x-dvi'), // do not localize + (Key: 'evy'; Value: 'application/x-envoy'), // do not localize + (Key: 'eva'; Value: 'application/x-eva'), // do not localize + (Key: 'bdf'; Value: 'application/x-font-bdf'), // do not localize + (Key: 'gsf'; Value: 'application/x-font-ghostscript'), // do not localize + (Key: 'psf'; Value: 'application/x-font-linux-psf'), // do not localize + (Key: 'otf'; Value: 'application/x-font-otf'), // do not localize + (Key: 'pcf'; Value: 'application/x-font-pcf'), // do not localize + (Key: 'snf'; Value: 'application/x-font-snf'), // do not localize + (Key: 'ttf'; Value: 'application/x-font-ttf'), // do not localize + (Key: 'ttc'; Value: 'application/x-font-ttf'), // do not localize + (Key: 'pfa'; Value: 'application/x-font-type1'), // do not localize + (Key: 'pfb'; Value: 'application/x-font-type1'), // do not localize + (Key: 'pfm'; Value: 'application/x-font-type1'), // do not localize + (Key: 'afm'; Value: 'application/x-font-type1'), // do not localize + (Key: 'woff'; Value: 'application/x-font-woff'), // do not localize + (Key: 'arc'; Value: 'application/x-freearc'), // do not localize + (Key: 'spl'; Value: 'application/x-futuresplash'), // do not localize + (Key: 'gca'; Value: 'application/x-gca-compressed'), // do not localize + (Key: 'ulx'; Value: 'application/x-glulx'), // do not localize + (Key: 'gnumeric'; Value: 'application/x-gnumeric'), // do not localize + (Key: 'gramps'; Value: 'application/x-gramps-xml'), // do not localize + (Key: 'gtar'; Value: 'application/x-gtar'), // do not localize + (Key: 'hdf'; Value: 'application/x-hdf'), // do not localize + (Key: 'install'; Value: 'application/x-install-instructions'), // do not localize + (Key: 'iso'; Value: 'application/x-iso9660-image'), // do not localize + (Key: 'jnlp'; Value: 'application/x-java-jnlp-file'), // do not localize + (Key: 'latex'; Value: 'application/x-latex'), // do not localize + (Key: 'lzh'; Value: 'application/x-lzh-compressed'), // do not localize + (Key: 'lha'; Value: 'application/x-lzh-compressed'), // do not localize + (Key: 'mie'; Value: 'application/x-mie'), // do not localize + (Key: 'prc'; Value: 'application/x-mobipocket-ebook'), // do not localize + (Key: 'mobi'; Value: 'application/x-mobipocket-ebook'), // do not localize + (Key: 'application'; Value: 'application/x-ms-application'), // do not localize + (Key: 'lnk'; Value: 'application/x-ms-shortcut'), // do not localize + (Key: 'wmd'; Value: 'application/x-ms-wmd'), // do not localize + (Key: 'wmz'; Value: 'application/x-ms-wmz'), // do not localize + (Key: 'xbap'; Value: 'application/x-ms-xbap'), // do not localize + (Key: 'mdb'; Value: 'application/x-msaccess'), // do not localize + (Key: 'obd'; Value: 'application/x-msbinder'), // do not localize + (Key: 'crd'; Value: 'application/x-mscardfile'), // do not localize + (Key: 'clp'; Value: 'application/x-msclip'), // do not localize + (Key: 'exe'; Value: 'application/x-msdownload'), // do not localize + (Key: 'dll'; Value: 'application/x-msdownload'), // do not localize + (Key: 'com'; Value: 'application/x-msdownload'), // do not localize + (Key: 'bat'; Value: 'application/x-msdownload'), // do not localize + (Key: 'msi'; Value: 'application/x-msdownload'), // do not localize + (Key: 'mvb'; Value: 'application/x-msmediaview'), // do not localize + (Key: 'm13'; Value: 'application/x-msmediaview'), // do not localize + (Key: 'm14'; Value: 'application/x-msmediaview'), // do not localize + (Key: 'wmf'; Value: 'application/x-msmetafile'), // do not localize + (Key: 'wmz'; Value: 'application/x-msmetafile'), // do not localize + (Key: 'emf'; Value: 'application/x-msmetafile'), // do not localize + (Key: 'emz'; Value: 'application/x-msmetafile'), // do not localize + (Key: 'mny'; Value: 'application/x-msmoney'), // do not localize + (Key: 'pub'; Value: 'application/x-mspublisher'), // do not localize + (Key: 'scd'; Value: 'application/x-msschedule'), // do not localize + (Key: 'trm'; Value: 'application/x-msterminal'), // do not localize + (Key: 'wri'; Value: 'application/x-mswrite'), // do not localize + (Key: 'nc'; Value: 'application/x-netcdf'), // do not localize + (Key: 'cdf'; Value: 'application/x-netcdf'), // do not localize + (Key: 'nzb'; Value: 'application/x-nzb'), // do not localize + (Key: 'p12'; Value: 'application/x-pkcs12'), // do not localize + (Key: 'pfx'; Value: 'application/x-pkcs12'), // do not localize + (Key: 'p7b'; Value: 'application/x-pkcs7-certificates'), // do not localize + (Key: 'spc'; Value: 'application/x-pkcs7-certificates'), // do not localize + (Key: 'p7r'; Value: 'application/x-pkcs7-certreqresp'), // do not localize + (Key: 'rar'; Value: 'application/x-rar-compressed'), // do not localize + (Key: 'ris'; Value: 'application/x-research-info-systems'), // do not localize + (Key: 'sh'; Value: 'application/x-sh'), // do not localize + (Key: 'shar'; Value: 'application/x-shar'), // do not localize + (Key: 'swf'; Value: 'application/x-shockwave-flash'), // do not localize + (Key: 'xap'; Value: 'application/x-silverlight-app'), // do not localize + (Key: 'sql'; Value: 'application/x-sql'), // do not localize + (Key: 'sit'; Value: 'application/x-stuffit'), // do not localize + (Key: 'sitx'; Value: 'application/x-stuffitx'), // do not localize + (Key: 'srt'; Value: 'application/x-subrip'), // do not localize + (Key: 'sv4cpio'; Value: 'application/x-sv4cpio'), // do not localize + (Key: 'sv4crc'; Value: 'application/x-sv4crc'), // do not localize + (Key: 't3'; Value: 'application/x-t3vm-image'), // do not localize + (Key: 'gam'; Value: 'application/x-tads'), // do not localize + (Key: 'tar'; Value: 'application/x-tar'), // do not localize + (Key: 'tcl'; Value: 'application/x-tcl'), // do not localize + (Key: 'tex'; Value: 'application/x-tex'), // do not localize + (Key: 'tfm'; Value: 'application/x-tex-tfm'), // do not localize + (Key: 'texinfo'; Value: 'application/x-texinfo'), // do not localize + (Key: 'texi'; Value: 'application/x-texinfo'), // do not localize + (Key: 'obj'; Value: 'application/x-tgif'), // do not localize + (Key: 'ustar'; Value: 'application/x-ustar'), // do not localize + (Key: 'src'; Value: 'application/x-wais-source'), // do not localize + (Key: 'der'; Value: 'application/x-x509-ca-cert'), // do not localize + (Key: 'crt'; Value: 'application/x-x509-ca-cert'), // do not localize + (Key: 'fig'; Value: 'application/x-xfig'), // do not localize + (Key: 'xlf'; Value: 'application/x-xliff+xml'), // do not localize + (Key: 'xpi'; Value: 'application/x-xpinstall'), // do not localize + (Key: 'xz'; Value: 'application/x-xz'), // do not localize + (Key: 'z1'; Value: 'application/x-zmachine'), // do not localize + (Key: 'z2'; Value: 'application/x-zmachine'), // do not localize + (Key: 'z3'; Value: 'application/x-zmachine'), // do not localize + (Key: 'z4'; Value: 'application/x-zmachine'), // do not localize + (Key: 'z5'; Value: 'application/x-zmachine'), // do not localize + (Key: 'z6'; Value: 'application/x-zmachine'), // do not localize + (Key: 'z7'; Value: 'application/x-zmachine'), // do not localize + (Key: 'z8'; Value: 'application/x-zmachine'), // do not localize + (Key: 'xaml'; Value: 'application/xaml+xml'), // do not localize + (Key: 'xdf'; Value: 'application/xcap-diff+xml'), // do not localize + (Key: 'xenc'; Value: 'application/xenc+xml'), // do not localize + (Key: 'xhtml'; Value: 'application/xhtml+xml'), // do not localize + (Key: 'xht'; Value: 'application/xhtml+xml'), // do not localize + (Key: 'xml'; Value: 'application/xml'), // do not localize + (Key: 'xsl'; Value: 'application/xml'), // do not localize + (Key: 'dtd'; Value: 'application/xml-dtd'), // do not localize + (Key: 'xop'; Value: 'application/xop+xml'), // do not localize + (Key: 'xpl'; Value: 'application/xproc+xml'), // do not localize + (Key: 'xslt'; Value: 'application/xslt+xml'), // do not localize + (Key: 'xspf'; Value: 'application/xspf+xml'), // do not localize + (Key: 'mxml'; Value: 'application/xv+xml'), // do not localize + (Key: 'xhvml'; Value: 'application/xv+xml'), // do not localize + (Key: 'xvml'; Value: 'application/xv+xml'), // do not localize + (Key: 'xvm'; Value: 'application/xv+xml'), // do not localize + (Key: 'yang'; Value: 'application/yang'), // do not localize + (Key: 'yin'; Value: 'application/yin+xml'), // do not localize + (Key: 'zip'; Value: 'application/zip'), // do not localize + (Key: 'adp'; Value: 'audio/adpcm'), // do not localize + (Key: 'au'; Value: 'audio/basic'), // do not localize + (Key: 'snd'; Value: 'audio/basic'), // do not localize + (Key: 'mid'; Value: 'audio/midi'), // do not localize + (Key: 'midi'; Value: 'audio/midi'), // do not localize + (Key: 'kar'; Value: 'audio/midi'), // do not localize + (Key: 'rmi'; Value: 'audio/midi'), // do not localize + (Key: 'mp4a'; Value: 'audio/mp4'), // do not localize + (Key: 'mpga'; Value: 'audio/mpeg'), // do not localize + (Key: 'mp2'; Value: 'audio/mpeg'), // do not localize + (Key: 'mp2a'; Value: 'audio/mpeg'), // do not localize + (Key: 'mp3'; Value: 'audio/mpeg'), // do not localize + (Key: 'm2a'; Value: 'audio/mpeg'), // do not localize + (Key: 'm3a'; Value: 'audio/mpeg'), // do not localize + (Key: 'oga'; Value: 'audio/ogg'), // do not localize + (Key: 'ogg'; Value: 'audio/ogg'), // do not localize + (Key: 'spx'; Value: 'audio/ogg'), // do not localize + (Key: 's3m'; Value: 'audio/s3m'), // do not localize + (Key: 'sil'; Value: 'audio/silk'), // do not localize + (Key: 'uva'; Value: 'audio/vnd.dece.audio'), // do not localize + (Key: 'uvva'; Value: 'audio/vnd.dece.audio'), // do not localize + (Key: 'eol'; Value: 'audio/vnd.digital-winds'), // do not localize + (Key: 'dra'; Value: 'audio/vnd.dra'), // do not localize + (Key: 'dts'; Value: 'audio/vnd.dts'), // do not localize + (Key: 'dtshd'; Value: 'audio/vnd.dts.hd'), // do not localize + (Key: 'lvp'; Value: 'audio/vnd.lucent.voice'), // do not localize + (Key: 'pya'; Value: 'audio/vnd.ms-playready.media.pya'), // do not localize + (Key: 'ecelp4800'; Value: 'audio/vnd.nuera.ecelp4800'), // do not localize + (Key: 'ecelp7470'; Value: 'audio/vnd.nuera.ecelp7470'), // do not localize + (Key: 'ecelp9600'; Value: 'audio/vnd.nuera.ecelp9600'), // do not localize + (Key: 'rip'; Value: 'audio/vnd.rip'), // do not localize + (Key: 'weba'; Value: 'audio/webm'), // do not localize + (Key: 'aac'; Value: 'audio/x-aac'), // do not localize + (Key: 'aif'; Value: 'audio/x-aiff'), // do not localize + (Key: 'aiff'; Value: 'audio/x-aiff'), // do not localize + (Key: 'aifc'; Value: 'audio/x-aiff'), // do not localize + (Key: 'caf'; Value: 'audio/x-caf'), // do not localize + (Key: 'flac'; Value: 'audio/x-flac'), // do not localize + (Key: 'mka'; Value: 'audio/x-matroska'), // do not localize + (Key: 'm3u'; Value: 'audio/x-mpegurl'), // do not localize + (Key: 'wax'; Value: 'audio/x-ms-wax'), // do not localize + (Key: 'wma'; Value: 'audio/x-ms-wma'), // do not localize + (Key: 'ram'; Value: 'audio/x-pn-realaudio'), // do not localize + (Key: 'ra'; Value: 'audio/x-pn-realaudio'), // do not localize + (Key: 'rmp'; Value: 'audio/x-pn-realaudio-plugin'), // do not localize + (Key: 'wav'; Value: 'audio/x-wav'), // do not localize + (Key: 'xm'; Value: 'audio/xm'), // do not localize + (Key: 'cdx'; Value: 'chemical/x-cdx'), // do not localize + (Key: 'cif'; Value: 'chemical/x-cif'), // do not localize + (Key: 'cmdf'; Value: 'chemical/x-cmdf'), // do not localize + (Key: 'cml'; Value: 'chemical/x-cml'), // do not localize + (Key: 'csml'; Value: 'chemical/x-csml'), // do not localize + (Key: 'xyz'; Value: 'chemical/x-xyz'), // do not localize + (Key: 'bmp'; Value: 'image/bmp'), // do not localize + (Key: 'cgm'; Value: 'image/cgm'), // do not localize + (Key: 'g3'; Value: 'image/g3fax'), // do not localize + (Key: 'gif'; Value: 'image/gif'), // do not localize + (Key: 'ief'; Value: 'image/ief'), // do not localize + (Key: 'jpeg'; Value: 'image/jpeg'), // do not localize + (Key: 'jpg'; Value: 'image/jpeg'), // do not localize + (Key: 'jpe'; Value: 'image/jpeg'), // do not localize + (Key: 'ktx'; Value: 'image/ktx'), // do not localize + (Key: 'png'; Value: 'image/png'), // do not localize + (Key: 'btif'; Value: 'image/prs.btif'), // do not localize + (Key: 'sgi'; Value: 'image/sgi'), // do not localize + (Key: 'svg'; Value: 'image/svg+xml'), // do not localize + (Key: 'svgz'; Value: 'image/svg+xml'), // do not localize + (Key: 'tiff'; Value: 'image/tiff'), // do not localize + (Key: 'tif'; Value: 'image/tiff'), // do not localize + (Key: 'psd'; Value: 'image/vnd.adobe.photoshop'), // do not localize + (Key: 'uvi'; Value: 'image/vnd.dece.graphic'), // do not localize + (Key: 'uvvi'; Value: 'image/vnd.dece.graphic'), // do not localize + (Key: 'uvg'; Value: 'image/vnd.dece.graphic'), // do not localize + (Key: 'uvvg'; Value: 'image/vnd.dece.graphic'), // do not localize + (Key: 'sub'; Value: 'image/vnd.dvb.subtitle'), // do not localize + (Key: 'djvu'; Value: 'image/vnd.djvu'), // do not localize + (Key: 'djv'; Value: 'image/vnd.djvu'), // do not localize + (Key: 'dwg'; Value: 'image/vnd.dwg'), // do not localize + (Key: 'dxf'; Value: 'image/vnd.dxf'), // do not localize + (Key: 'fbs'; Value: 'image/vnd.fastbidsheet'), // do not localize + (Key: 'fpx'; Value: 'image/vnd.fpx'), // do not localize + (Key: 'fst'; Value: 'image/vnd.fst'), // do not localize + (Key: 'mmr'; Value: 'image/vnd.fujixerox.edmics-mmr'), // do not localize + (Key: 'rlc'; Value: 'image/vnd.fujixerox.edmics-rlc'), // do not localize + (Key: 'mdi'; Value: 'image/vnd.ms-modi'), // do not localize + (Key: 'wdp'; Value: 'image/vnd.ms-photo'), // do not localize + (Key: 'npx'; Value: 'image/vnd.net-fpx'), // do not localize + (Key: 'wbmp'; Value: 'image/vnd.wap.wbmp'), // do not localize + (Key: 'xif'; Value: 'image/vnd.xiff'), // do not localize + (Key: 'webp'; Value: 'image/webp'), // do not localize + (Key: '3ds'; Value: 'image/x-3ds'), // do not localize + (Key: 'ras'; Value: 'image/x-cmu-raster'), // do not localize + (Key: 'cmx'; Value: 'image/x-cmx'), // do not localize + (Key: 'fh'; Value: 'image/x-freehand'), // do not localize + (Key: 'fhc'; Value: 'image/x-freehand'), // do not localize + (Key: 'fh4'; Value: 'image/x-freehand'), // do not localize + (Key: 'fh5'; Value: 'image/x-freehand'), // do not localize + (Key: 'fh7'; Value: 'image/x-freehand'), // do not localize + (Key: 'ico'; Value: 'image/x-icon'), // do not localize + (Key: 'sid'; Value: 'image/x-mrsid-image'), // do not localize + (Key: 'pcx'; Value: 'image/x-pcx'), // do not localize + (Key: 'pic'; Value: 'image/x-pict'), // do not localize + (Key: 'pct'; Value: 'image/x-pict'), // do not localize + (Key: 'pnm'; Value: 'image/x-portable-anymap'), // do not localize + (Key: 'pbm'; Value: 'image/x-portable-bitmap'), // do not localize + (Key: 'pgm'; Value: 'image/x-portable-graymap'), // do not localize + (Key: 'ppm'; Value: 'image/x-portable-pixmap'), // do not localize + (Key: 'rgb'; Value: 'image/x-rgb'), // do not localize + (Key: 'tga'; Value: 'image/x-tga'), // do not localize + (Key: 'xbm'; Value: 'image/x-xbitmap'), // do not localize + (Key: 'xpm'; Value: 'image/x-xpixmap'), // do not localize + (Key: 'xwd'; Value: 'image/x-xwindowdump'), // do not localize + (Key: 'eml'; Value: 'message/rfc822'), // do not localize + (Key: 'mime'; Value: 'message/rfc822'), // do not localize + (Key: 'igs'; Value: 'model/iges'), // do not localize + (Key: 'iges'; Value: 'model/iges'), // do not localize + (Key: 'msh'; Value: 'model/mesh'), // do not localize + (Key: 'mesh'; Value: 'model/mesh'), // do not localize + (Key: 'silo'; Value: 'model/mesh'), // do not localize + (Key: 'dae'; Value: 'model/vnd.collada+xml'), // do not localize + (Key: 'dwf'; Value: 'model/vnd.dwf'), // do not localize + (Key: 'gdl'; Value: 'model/vnd.gdl'), // do not localize + (Key: 'gtw'; Value: 'model/vnd.gtw'), // do not localize + (Key: 'mts'; Value: 'model/vnd.mts'), // do not localize + (Key: 'vtu'; Value: 'model/vnd.vtu'), // do not localize + (Key: 'wrl'; Value: 'model/vrml'), // do not localize + (Key: 'vrml'; Value: 'model/vrml'), // do not localize + (Key: 'x3db'; Value: 'model/x3d+binary'), // do not localize + (Key: 'x3dbz'; Value: 'model/x3d+binary'), // do not localize + (Key: 'x3dv'; Value: 'model/x3d+vrml'), // do not localize + (Key: 'x3dvz'; Value: 'model/x3d+vrml'), // do not localize + (Key: 'x3d'; Value: 'model/x3d+xml'), // do not localize + (Key: 'x3dz'; Value: 'model/x3d+xml'), // do not localize + (Key: 'appcache'; Value: 'text/cache-manifest'), // do not localize + (Key: 'ics'; Value: 'text/calendar'), // do not localize + (Key: 'ifb'; Value: 'text/calendar'), // do not localize + (Key: 'css'; Value: 'text/css'), // do not localize + (Key: 'csv'; Value: 'text/csv'), // do not localize + (Key: 'html'; Value: 'text/html'), // do not localize + (Key: 'htm'; Value: 'text/html'), // do not localize + (Key: 'n3'; Value: 'text/n3'), // do not localize + (Key: 'txt'; Value: 'text/plain'), // do not localize + (Key: 'text'; Value: 'text/plain'), // do not localize + (Key: 'conf'; Value: 'text/plain'), // do not localize + (Key: 'def'; Value: 'text/plain'), // do not localize + (Key: 'list'; Value: 'text/plain'), // do not localize + (Key: 'log'; Value: 'text/plain'), // do not localize + (Key: 'in'; Value: 'text/plain'), // do not localize + (Key: 'dsc'; Value: 'text/prs.lines.tag'), // do not localize + (Key: 'rtx'; Value: 'text/richtext'), // do not localize + (Key: 'sgml'; Value: 'text/sgml'), // do not localize + (Key: 'sgm'; Value: 'text/sgml'), // do not localize + (Key: 'tsv'; Value: 'text/tab-separated-values'), // do not localize + (Key: 't'; Value: 'text/troff'), // do not localize + (Key: 'tr'; Value: 'text/troff'), // do not localize + (Key: 'roff'; Value: 'text/troff'), // do not localize + (Key: 'man'; Value: 'text/troff'), // do not localize + (Key: 'me'; Value: 'text/troff'), // do not localize + (Key: 'ms'; Value: 'text/troff'), // do not localize + (Key: 'ttl'; Value: 'text/turtle'), // do not localize + (Key: 'uri'; Value: 'text/uri-list'), // do not localize + (Key: 'uris'; Value: 'text/uri-list'), // do not localize + (Key: 'urls'; Value: 'text/uri-list'), // do not localize + (Key: 'vcard'; Value: 'text/vcard'), // do not localize + (Key: 'curl'; Value: 'text/vnd.curl'), // do not localize + (Key: 'dcurl'; Value: 'text/vnd.curl.dcurl'), // do not localize + (Key: 'scurl'; Value: 'text/vnd.curl.scurl'), // do not localize + (Key: 'mcurl'; Value: 'text/vnd.curl.mcurl'), // do not localize + (Key: 'sub'; Value: 'text/vnd.dvb.subtitle'), // do not localize + (Key: 'fly'; Value: 'text/vnd.fly'), // do not localize + (Key: 'flx'; Value: 'text/vnd.fmi.flexstor'), // do not localize + (Key: 'gv'; Value: 'text/vnd.graphviz'), // do not localize + (Key: '3dml'; Value: 'text/vnd.in3d.3dml'), // do not localize + (Key: 'spot'; Value: 'text/vnd.in3d.spot'), // do not localize + (Key: 'jad'; Value: 'text/vnd.sun.j2me.app-descriptor'), // do not localize + (Key: 'wml'; Value: 'text/vnd.wap.wml'), // do not localize + (Key: 'wmls'; Value: 'text/vnd.wap.wmlscript'), // do not localize + (Key: 's'; Value: 'text/x-asm'), // do not localize + (Key: 'asm'; Value: 'text/x-asm'), // do not localize + (Key: 'c'; Value: 'text/x-c'), // do not localize + (Key: 'cc'; Value: 'text/x-c'), // do not localize + (Key: 'cxx'; Value: 'text/x-c'), // do not localize + (Key: 'cpp'; Value: 'text/x-c'), // do not localize + (Key: 'h'; Value: 'text/x-c'), // do not localize + (Key: 'hh'; Value: 'text/x-c'), // do not localize + (Key: 'dic'; Value: 'text/x-c'), // do not localize + (Key: 'f'; Value: 'text/x-fortran'), // do not localize + (Key: 'for'; Value: 'text/x-fortran'), // do not localize + (Key: 'f77'; Value: 'text/x-fortran'), // do not localize + (Key: 'f90'; Value: 'text/x-fortran'), // do not localize + (Key: 'java'; Value: 'text/x-java-source'), // do not localize + (Key: 'opml'; Value: 'text/x-opml'), // do not localize + (Key: 'p'; Value: 'text/x-pascal'), // do not localize + (Key: 'pas'; Value: 'text/x-pascal'), // do not localize + (Key: 'nfo'; Value: 'text/x-nfo'), // do not localize + (Key: 'etx'; Value: 'text/x-setext'), // do not localize + (Key: 'sfv'; Value: 'text/x-sfv'), // do not localize + (Key: 'uu'; Value: 'text/x-uuencode'), // do not localize + (Key: 'vcs'; Value: 'text/x-vcalendar'), // do not localize + (Key: 'vcf'; Value: 'text/x-vcard'), // do not localize + (Key: '3gp'; Value: 'video/3gpp'), // do not localize + (Key: '3g2'; Value: 'video/3gpp2'), // do not localize + (Key: 'h261'; Value: 'video/h261'), // do not localize + (Key: 'h263'; Value: 'video/h263'), // do not localize + (Key: 'h264'; Value: 'video/h264'), // do not localize + (Key: 'jpgv'; Value: 'video/jpeg'), // do not localize + (Key: 'jpm'; Value: 'video/jpm'), // do not localize + (Key: 'jpgm'; Value: 'video/jpm'), // do not localize + (Key: 'mj2'; Value: 'video/mj2'), // do not localize + (Key: 'mjp2'; Value: 'video/mj2'), // do not localize + (Key: 'mp4'; Value: 'video/mp4'), // do not localize + (Key: 'mp4v'; Value: 'video/mp4'), // do not localize + (Key: 'mpg4'; Value: 'video/mp4'), // do not localize + (Key: 'mpeg'; Value: 'video/mpeg'), // do not localize + (Key: 'mpg'; Value: 'video/mpeg'), // do not localize + (Key: 'mpe'; Value: 'video/mpeg'), // do not localize + (Key: 'm1v'; Value: 'video/mpeg'), // do not localize + (Key: 'm2v'; Value: 'video/mpeg'), // do not localize + (Key: 'ogv'; Value: 'video/ogg'), // do not localize + (Key: 'qt'; Value: 'video/quicktime'), // do not localize + (Key: 'mov'; Value: 'video/quicktime'), // do not localize + (Key: 'uvh'; Value: 'video/vnd.dece.hd'), // do not localize + (Key: 'uvvh'; Value: 'video/vnd.dece.hd'), // do not localize + (Key: 'uvm'; Value: 'video/vnd.dece.mobile'), // do not localize + (Key: 'uvvm'; Value: 'video/vnd.dece.mobile'), // do not localize + (Key: 'uvp'; Value: 'video/vnd.dece.pd'), // do not localize + (Key: 'uvvp'; Value: 'video/vnd.dece.pd'), // do not localize + (Key: 'uvs'; Value: 'video/vnd.dece.sd'), // do not localize + (Key: 'uvvs'; Value: 'video/vnd.dece.sd'), // do not localize + (Key: 'uvv'; Value: 'video/vnd.dece.video'), // do not localize + (Key: 'uvvv'; Value: 'video/vnd.dece.video'), // do not localize + (Key: 'dvb'; Value: 'video/vnd.dvb.file'), // do not localize + (Key: 'fvt'; Value: 'video/vnd.fvt'), // do not localize + (Key: 'mxu'; Value: 'video/vnd.mpegurl'), // do not localize + (Key: 'm4u'; Value: 'video/vnd.mpegurl'), // do not localize + (Key: 'pyv'; Value: 'video/vnd.ms-playready.media.pyv'), // do not localize + (Key: 'uvu'; Value: 'video/vnd.uvvu.mp4'), // do not localize + (Key: 'uvvu'; Value: 'video/vnd.uvvu.mp4'), // do not localize + (Key: 'viv'; Value: 'video/vnd.vivo'), // do not localize + (Key: 'webm'; Value: 'video/webm'), // do not localize + (Key: 'f4v'; Value: 'video/x-f4v'), // do not localize + (Key: 'fli'; Value: 'video/x-fli'), // do not localize + (Key: 'flv'; Value: 'video/x-flv'), // do not localize + (Key: 'm4v'; Value: 'video/x-m4v'), // do not localize + (Key: 'mkv'; Value: 'video/x-matroska'), // do not localize + (Key: 'mk3d'; Value: 'video/x-matroska'), // do not localize + (Key: 'mks'; Value: 'video/x-matroska'), // do not localize + (Key: 'mng'; Value: 'video/x-mng'), // do not localize + (Key: 'asf'; Value: 'video/x-ms-asf'), // do not localize + (Key: 'asx'; Value: 'video/x-ms-asf'), // do not localize + (Key: 'vob'; Value: 'video/x-ms-vob'), // do not localize + (Key: 'wm'; Value: 'video/x-ms-wm'), // do not localize + (Key: 'wmv'; Value: 'video/x-ms-wmv'), // do not localize + (Key: 'wmx'; Value: 'video/x-ms-wmx'), // do not localize + (Key: 'wvx'; Value: 'video/x-ms-wvx'), // do not localize + (Key: 'avi'; Value: 'video/x-msvideo'), // do not localize + (Key: 'movie'; Value: 'video/x-sgi-movie'), // do not localize + (Key: 'smv'; Value: 'video/x-smv'), // do not localize + (Key: 'ice'; Value: 'x-conference/x-cooltalk') // do not localize + ); + {$ENDREGION} + +type + TMediaType = class + public const + DELIM_PARAMS = ';'; + CHARSET_NAME = 'charset'; + CHARSET_UTF8 = 'utf-8'; + CHARSET_UTF8_DEF = CHARSET_NAME + '=' + CHARSET_UTF8; + + TEXT_PLAIN = 'text/plain'; + TEXT_PLAIN_UTF8 = TEXT_PLAIN + DELIM_PARAMS + CHARSET_UTF8_DEF; + + TEXT_XML = 'text/xml'; + TEXT_XML_UTF8 = TEXT_XML + DELIM_PARAMS + CHARSET_UTF8_DEF; + + TEXT_HTML = 'text/html'; + TEXT_HTML_UTF8 = TEXT_HTML + DELIM_PARAMS + CHARSET_UTF8_DEF; + + APPLICATION_JSON = 'application/json'; + APPLICATION_JSON_UTF8 = APPLICATION_JSON + DELIM_PARAMS + CHARSET_UTF8_DEF; + + APPLICATION_XML = 'application/xml'; + APPLICATION_XML_UTF8 = APPLICATION_XML + DELIM_PARAMS + CHARSET_UTF8_DEF; + + APPLICATION_OCTET_STREAM = 'application/octet-stream'; + APPLICATION_FORM_URLENCODED_TYPE = 'application/x-www-form-urlencoded'; + MULTIPART_FORM_DATA = 'multipart/form-data'; + WILDCARD = '*/*'; + end; + + TCrossHttpUtils = class + private const + RFC1123_StrWeekDay: string = 'MonTueWedThuFriSatSun'; + RFC1123_StrMonth : string = 'JanFebMarAprMayJunJulAugSepOctNovDec'; + public + class function GetHttpStatusText(const AStatusCode: Integer): string; static; + class function GetFileMIMEType(const AFileName: string): string; static; + class function RFC1123_DateToStr(const ADate: TDateTime): string; static; + class function RFC1123_StrToDate(const ADateStr: string): TDateTime; static; + class function CombinePath(const APath1, APath2: string): string; static; + end; + +implementation + +{ TCrossHttpUtils } + +class function TCrossHttpUtils.GetHttpStatusText(const AStatusCode: Integer): string; +var + LItem: THttpStatus; +begin + for LItem in STATUS_CODES do + if (LItem.Code = AStatusCode) then Exit(LItem.Text); + Result := AStatusCode.ToString; +end; + +class function TCrossHttpUtils.CombinePath(const APath1, + APath2: string): string; +var + LPath1Ends, LPath2Starts: string; +begin + if (APath1 = '') then Exit(APath2); + if (APath2 = '') then Exit(APath1); + + LPath1Ends := APath1.Substring(APath1.Length - 1, 1); + LPath2Starts := APath2.Substring(0, 1); + if (LPath1Ends = '/') and (LPath2Starts = '/') then + Result := APath1 + APath2.Substring(1) + else if (LPath1Ends = '/') and (LPath2Starts <> '/') then + Result := APath1 + APath2 + else if (LPath1Ends <> '/') and (LPath2Starts = '/') then + Result := APath1 + APath2 + else + Result := APath1 + '/' + APath2; +end; + +class function TCrossHttpUtils.GetFileMIMEType(const AFileName: string): string; +var + I: Integer; + LExt: string; +begin + LExt := ExtractFileExt(AFileName).Substring(1); + for I := 0 to High(MIME_TYPES) do + if (CompareText(MIME_TYPES[I].Key, LExt) = 0) then + Exit(MIME_TYPES[I].Value); + Result := TMediaType.APPLICATION_OCTET_STREAM; +end; + +class function TCrossHttpUtils.RFC1123_DateToStr(const ADate: TDateTime): string; +var + Year, Month, Day : Word; + Hour, Min, Sec, MSec : Word; + DayOfWeek : Word; +begin + DecodeDate(ADate, Year, Month, Day); + DecodeTime(ADate, Hour, Min, Sec, MSec); + DayOfWeek := ((Trunc(aDate) - 2) mod 7); + Result := Copy(RFC1123_StrWeekDay, 1 + DayOfWeek * 3, 3) + ', ' + + Format('%2.2d %s %4.4d %2.2d:%2.2d:%2.2d GMT', + [Day, Copy(RFC1123_StrMonth, 1 + 3 * (Month - 1), 3), + Year, Hour, Min, Sec]); +end; + +class function TCrossHttpUtils.RFC1123_StrToDate(const ADateStr: string) : TDateTime; +var + Year, Month, Day : Word; + Hour, Min, Sec : Word; +begin + if (ADateStr = '') then Exit(0); + + { Fri, 30 Jul 2004 10:10:35 GMT } + Day := StrToIntDef(Copy(ADateStr, 6, 2), 0); + Month := (Pos(Copy(ADateStr, 9, 3), RFC1123_StrMonth) + 2) div 3; + Year := StrToIntDef(Copy(ADateStr, 13, 4), 0); + Hour := StrToIntDef(Copy(ADateStr, 18, 2), 0); + Min := StrToIntDef(Copy(ADateStr, 21, 2), 0); + Sec := StrToIntDef(Copy(ADateStr, 24, 2), 0); + Result := EncodeDate(Year, Month, Day); + Result := Result + EncodeTime(Hour, Min, Sec, 0); +end; + +end. diff --git a/ThirdParty/DCS/Net/Net.CrossServer.pas b/ThirdParty/DCS/Net/Net.CrossServer.pas new file mode 100644 index 00000000..8c0b1b10 --- /dev/null +++ b/ThirdParty/DCS/Net/Net.CrossServer.pas @@ -0,0 +1,133 @@ +{******************************************************************************} +{ } +{ Delphi cross platform socket library } +{ } +{ Copyright (c) 2017 WiNDDRiVER(soulawing@gmail.com) } +{ } +{ Homepage: https://github.com/winddriver/Delphi-Cross-Socket } +{ } +{******************************************************************************} +unit Net.CrossServer; + +interface + +uses + System.SysUtils, + Net.CrossSocket.Base, + Net.CrossSocket; + +type + ICrossServer = interface(ICrossSocket) + ['{78865955-48EE-4354-9B03-389025839B67}'] + function GetAddr: string; + function GetPort: Word; + function GetActive: Boolean; + + procedure SetAddr(const Value: string); + procedure SetPort(const Value: Word); + procedure SetActive(const Value: Boolean); + + procedure Start(const ACallback: TProc = nil); + procedure Stop; + + property Addr: string read GetAddr write SetAddr; + property Port: Word read GetPort write SetPort; + + property Active: Boolean read GetActive write SetActive; + end; + + TCrossServer = class(TCrossSocket, ICrossServer) + private + FPort: Word; + FAddr: string; + FStarted: Integer; + + function GetAddr: string; + function GetPort: Word; + function GetActive: Boolean; + + procedure SetAddr(const Value: string); + procedure SetPort(const Value: Word); + procedure SetActive(const Value: Boolean); + public + procedure Start(const ACallback: TProc = nil); + procedure Stop; + + property Addr: string read GetAddr write SetAddr; + property Port: Word read GetPort write SetPort; + property Active: Boolean read GetActive write SetActive; + end; + +implementation + +{ TCrossServer } + +function TCrossServer.GetActive: Boolean; +begin + Result := (AtomicCmpExchange(FStarted, 0, 0) = 1); +end; + +function TCrossServer.GetAddr: string; +begin + Result := FAddr; +end; + +function TCrossServer.GetPort: Word; +begin + Result := FPort; +end; + +procedure TCrossServer.SetActive(const Value: Boolean); +begin + if Value then + Start + else + Stop; +end; + +procedure TCrossServer.SetAddr(const Value: string); +begin + FAddr := Value; +end; + +procedure TCrossServer.SetPort(const Value: Word); +begin + FPort := Value; +end; + +procedure TCrossServer.Start(const ACallback: TProc); +begin + if (AtomicExchange(FStarted, 1) = 1) then + begin + if Assigned(ACallback) then + ACallback(False); + + Exit; + end; + + StartLoop; + + Listen(FAddr, FPort, + procedure(AListen: ICrossListen; ASuccess: Boolean) + begin + if not ASuccess then + AtomicExchange(FStarted, 0); + + // Ǽ˿ + // ڼɹ֮ʵʵĶ˿ȡ + if (FPort = 0) then + FPort := AListen.LocalPort; + + if Assigned(ACallback) then + ACallback(ASuccess); + end); +end; + +procedure TCrossServer.Stop; +begin + CloseAll; + StopLoop; + AtomicExchange(FStarted, 0); +end; + +end. diff --git a/ThirdParty/DCS/Net/Net.CrossSocket.Base.pas b/ThirdParty/DCS/Net/Net.CrossSocket.Base.pas new file mode 100644 index 00000000..baef176d --- /dev/null +++ b/ThirdParty/DCS/Net/Net.CrossSocket.Base.pas @@ -0,0 +1,1757 @@ +{******************************************************************************} +{ } +{ Delphi cross platform socket library } +{ } +{ Copyright (c) 2017 WiNDDRiVER(soulawing@gmail.com) } +{ } +{ Homepage: https://github.com/winddriver/Delphi-Cross-Socket } +{ } +{******************************************************************************} +unit Net.CrossSocket.Base; + +// 是否将大块数据分成小块发送(仅IOCP下有效) +// 注意: 开启该开关的情况下, 同一个连接不要在一次发送尚未结束时开始另一次发送 +// 否则会导致两块数据被分成小块后出现交错 +{.$DEFINE __LITTLE_PIECE__} + +interface + +uses + System.SysUtils, + System.Classes, + System.Math, + System.Generics.Collections, + Net.SocketAPI; + +const + // 唯一编号类别 + // 唯一编号共64位, 高2位用于表示类别 + UID_RAW = $0; + UID_LISTEN = $1; + UID_CONNECTION = $2; + + // 最大唯一编号(62个1) + UID_MASK = UInt64($3FFFFFFFFFFFFFFF); + + IPv4_ALL = '0.0.0.0'; + IPv6_ALL = '::'; + IPv4v6_ALL = ''; + IPv4_LOCAL = '127.0.0.1'; + IPv6_LOCAL = '::1'; + +type + ECrossSocket = class(Exception); + + ICrossSocket = interface; + TAbstractCrossSocket = class; + TIoEventThread = class; + + /// + /// 连接类型 + /// + TConnectType = ( + /// + /// 未知 + /// + ctUnknown, + /// + /// 由监听Accept生成的连接 + /// + ctAccept, + /// + /// 由Connect调用生成的连接 + /// + ctConnect); + + /// + /// 连接状态 + /// + TConnectStatus = ( + /// + /// 未知 + /// + csUnknown, + /// + /// 正在连接 + /// + csConnecting, + /// + /// 正在握手(SSL) + /// + csHandshaking, + /// + /// 已连接 + /// + csConnected, + /// + /// 已断开 + /// + csDisconnected, + /// + /// 已关闭 + /// + csClosed); + + /// + /// 基础数据接口 + /// + ICrossData = interface + ['{988404A3-D297-4C6D-9A76-16E50553596E}'] + function GetOwner: ICrossSocket; + function GetUID: UInt64; + function GetSocket: THandle; + function GetLocalAddr: string; + function GetLocalPort: Word; + function GetIsClosed: Boolean; + function GetUserData: Pointer; + function GetUserObject: TObject; + function GetUserInterface: IInterface; + + procedure SetUserData(const AValue: Pointer); + procedure SetUserObject(const AValue: TObject); + procedure SetUserInterface(const AValue: IInterface); + + /// + /// 更新套接字地址信息 + /// + /// + /// LocalAddr, LocalPort, PeerAddr, PeerPort 都依赖于该方法 + /// + procedure UpdateAddr; + + /// + /// 关闭套接字 + /// + procedure Close; + + /// + /// 宿主对象 + /// + property Owner: ICrossSocket read GetOwner; + + /// + /// 唯一编号 + /// + property UID: UInt64 read GetUID; + + /// + /// 套接字句柄 + /// + property Socket: THandle read GetSocket; + + /// + /// 本地IP地址 + /// + property LocalAddr: string read GetLocalAddr; + + /// + /// 本地端口 + /// + property LocalPort: Word read GetLocalPort; + + /// + /// 是否已关闭 + /// + property IsClosed: Boolean read GetIsClosed; + + /// + /// 用户数据(可以用于存储用户自定义的数据结构) + /// + property UserData: Pointer read GetUserData write SetUserData; + + /// + /// 用户数据(可以用于存储用户自定义的数据结构) + /// + property UserObject: TObject read GetUserObject write SetUserObject; + + /// + /// 用户数据(可以用于存储用户自定义的数据结构) + /// + property UserInterface: IInterface read GetUserInterface write SetUserInterface; + end; + TCrossDatas = TDictionary; + + /// + /// 监听接口 + /// + ICrossListen = interface(ICrossData) + ['{4008919E-8F16-4BBD-A68D-2FD1DE630702}'] + function GetFamily: Integer; + function GetSockType: Integer; + function GetProtocol: Integer; + + /// + /// PF_xxx + /// + property Family: Integer read GetFamily; + + /// + /// SOCK_xxx + /// + property SockType: Integer read GetSockType; + + /// + /// IPPROTO_xxx + /// + property Protocol: Integer read GetProtocol; + end; + TCrossListens = TDictionary; + + /// + /// 连接接口 + /// + ICrossConnection = interface(ICrossData) + ['{13C2A39E-C918-49B9-BBD3-A99110F94D1B}'] + function GetPeerAddr: string; + function GetPeerPort: Word; + function GetConnectType: TConnectType; + function GetConnectStatus: TConnectStatus; + + procedure SetConnectStatus(const Value: TConnectStatus); + + /// + /// 优雅关闭 + /// + procedure Disconnect; + + /// + /// 发送内存块数据 + /// + /// + /// 内存块指针 + /// + /// + /// 数据大小 + /// + /// + /// 全部数据发送完成或者出错时调用的回调函数 + /// + procedure SendBuf(ABuffer: Pointer; ACount: Integer; + const ACallback: TProc = nil); overload; + + /// + /// 发送无类型数据 + /// + /// + /// 无类型数据 + /// + /// + /// 数据大小 + /// + /// + /// 全部数据发送完成或者出错时调用的回调函数 + /// + procedure SendBuf(const ABuffer; ACount: Integer; + const ACallback: TProc = nil); overload; + + /// + /// 发送字节数据 + /// + /// + /// 字节数据 + /// + /// + /// 偏移量 + /// + /// + /// 数据大小 + /// + /// + /// 全部数据发送完成或者出错时调用的回调函数 + /// + procedure SendBytes(const ABytes: TBytes; AOffset, ACount: Integer; + const ACallback: TProc = nil); overload; + + /// + /// 发送字节数据 + /// + /// + /// 字节数据 + /// + /// + /// 全部数据发送完成或者出错时调用的回调函数 + /// + procedure SendBytes(const ABytes: TBytes; + const ACallback: TProc = nil); overload; + + /// + /// 发送数据流(用于发送较大的数据) + /// + /// + /// 流数据 + /// + /// + /// 全部数据发送完成或者出错时调用的回调函数 + /// + /// + /// 由于是纯异步发送, 所以务必保证发送过程中 AStream 的有效性, 将 AStream 的释放放到回调函数中去
+ ///
+ procedure SendStream(const AStream: TStream; + const ACallback: TProc = nil); + + /// + /// 连接IP地址 + /// + property PeerAddr: string read GetPeerAddr; + + /// + /// 连接端口 + /// + property PeerPort: Word read GetPeerPort; + + /// + /// 连接类型 + /// + /// + /// + /// + /// ctAccept, 由监听Accept生成的连接; + /// + /// + /// ctConnect, 由Connect调用生成的连接 + /// + /// + /// + property ConnectType: TConnectType read GetConnectType; + + /// + /// 连接状态 + /// + property ConnectStatus: TConnectStatus read GetConnectStatus write SetConnectStatus; + end; + TCrossConnections = TDictionary; + + TCrossIoThreadEvent = procedure(Sender: TObject; AIoThread: TIoEventThread) of object; + TCrossListenEvent = procedure(Sender: TObject; AListen: ICrossListen) of object; + TCrossConnectEvent = procedure(Sender: TObject; AConnection: ICrossConnection) of object; + TCrossDataEvent = procedure(Sender: TObject; AConnection: ICrossConnection; ABuf: Pointer; ALen: Integer) of object; + + /// + /// 跨平台Socket接口 + /// + ICrossSocket = interface + ['{2371CC3F-EB38-4C5D-8FA9-C913B9CD37A0}'] + function GetIoThreads: Integer; + function GetConnectionsCount: Integer; + function GetListensCount: Integer; + + function GetOnIoThreadBegin: TCrossIoThreadEvent; + function GetOnIoThreadEnd: TCrossIoThreadEvent; + function GetOnConnected: TCrossConnectEvent; + function GetOnDisconnected: TCrossConnectEvent; + function GetOnListened: TCrossListenEvent; + function GetOnListenEnd: TCrossListenEvent; + function GetOnReceived: TCrossDataEvent; + function GetOnSent: TCrossDataEvent; + + procedure SetOnIoThreadBegin(const Value: TCrossIoThreadEvent); + procedure SetOnIoThreadEnd(const Value: TCrossIoThreadEvent); + procedure SetOnConnected(const Value: TCrossConnectEvent); + procedure SetOnDisconnected(const Value: TCrossConnectEvent); + procedure SetOnListened(const Value: TCrossListenEvent); + procedure SetOnListenEnd(const Value: TCrossListenEvent); + procedure SetOnReceived(const Value: TCrossDataEvent); + procedure SetOnSent(const Value: TCrossDataEvent); + + /// + /// 启动IO循环 + /// + procedure StartLoop; + + /// + /// 停止IO循环 + /// + procedure StopLoop; + + /// + /// 处理IO事件 + /// + function ProcessIoEvent: Boolean; + + /// + /// 监听端口 + /// + /// + /// 监听地址: + /// + /// + /// 要监听IPv4和IPv6所有地址, 请设置为空 + /// + /// + /// 要单独监听IPv4, 请设置为 '0.0.0.0' + /// + /// + /// 要单独监听IPv6, 请设置为 '::' + /// + /// + /// 要监听IPv4环路地址, 请设置为 '127.0.0.1' + /// + /// + /// 要监听IPv6环路地址, 请设置为 '::1' + /// + /// + /// + /// + /// 监听端口, 设置为0则随机监听一个可用的端口 + /// + /// + /// 回调匿名函数 + /// + procedure Listen(const AHost: string; APort: Word; + const ACallback: TProc = nil); + + /// + /// 连接到主机 + /// + /// + /// 主机地址 + /// + /// + /// 主机端口 + /// + /// + /// 回调匿名函数 + /// + procedure Connect(const AHost: string; APort: Word; + const ACallback: TProc = nil); + + /// + /// 发送数据 + /// + /// + /// 连接对象 + /// + /// + /// 数据指针 + /// + /// + /// 数据尺寸 + /// + /// + /// 回调匿名函数 + /// + /// + /// 由于发送是异步的, 所以需要调用者保证发送完成之前数据的有效性 + /// + procedure Send(AConnection: ICrossConnection; ABuf: Pointer; ALen: Integer; + const ACallback: TProc = nil); + + /// + /// 关闭所有连接 + /// + /// + /// 正在发送中的数据将会丢失 + /// + procedure CloseAllConnections; + + /// + /// 关闭所有监听 + /// + procedure CloseAllListens; + + /// + /// 关闭所有监听及连接 + /// + procedure CloseAll; + + /// + /// 断开所有连接 + /// + /// + /// 正在发送中的数据会被送达 + /// + procedure DisconnectAll; + + /// + /// 加锁并返回所有连接 + /// + function LockConnections: TCrossConnections; + + /// + /// 解锁连接 + /// + procedure UnlockConnections; + + /// + /// 加锁并返回所有监听 + /// + function LockListens: TCrossListens; + + /// + /// 解锁监听 + /// + procedure UnlockListens; + + /// + /// 创建连接对象(内部使用) + /// + function CreateConnection(AOwner: ICrossSocket; AClientSocket: THandle; + AConnectType: TConnectType): ICrossConnection; + + /// + /// 创建监听对象(内部使用) + /// + function CreateListen(AOwner: ICrossSocket; AListenSocket: THandle; + AFamily, ASockType, AProtocol: Integer): ICrossListen; + + {$region '物理事件'} + /// + /// 监听成功后触发(内部使用) + /// + /// + /// 监听对象 + /// + procedure TriggerListened(AListen: ICrossListen); + + /// + /// 监听结束后触发(内部使用) + /// + /// + /// 监听对象 + /// + procedure TriggerListenEnd(AListen: ICrossListen); + + /// + /// 正在连接(内部使用) + /// + /// + /// 连接对象 + /// + procedure TriggerConnecting(AConnection: ICrossConnection); + + /// + /// 连接成功后触发(内部使用) + /// + /// + /// 连接对象 + /// + procedure TriggerConnected(AConnection: ICrossConnection); + + /// + /// 连接断开后触发(内部使用) + /// + /// + /// 连接对象 + /// + procedure TriggerDisconnected(AConnection: ICrossConnection); + {$endregion} + + /// + /// IO线程开始时触发 + /// + procedure TriggerIoThreadBegin(AIoThread: TIoEventThread); + + /// + /// IO线程结束时触发 + /// + procedure TriggerIoThreadEnd(AIoThread: TIoEventThread); + + /// + /// IO线程数 + /// + property IoThreads: Integer read GetIoThreads; + + /// + /// 连接数 + /// + property ConnectionsCount: Integer read GetConnectionsCount; + + /// + /// 监听数 + /// + property ListensCount: Integer read GetListensCount; + + /// + /// IO线程开始事件 + /// + property OnIoThreadBegin: TCrossIoThreadEvent read GetOnIoThreadBegin write SetOnIoThreadBegin; + + /// + /// IO线程结束事件 + /// + property OnIoThreadEnd: TCrossIoThreadEvent read GetOnIoThreadEnd write SetOnIoThreadEnd; + + /// + /// 监听成功事件 + /// + property OnListened: TCrossListenEvent read GetOnListened write SetOnListened; + + /// + /// 监听结束事件 + /// + property OnListenEnd: TCrossListenEvent read GetOnListenEnd write SetOnListenEnd; + + /// + /// 连接成功事件 + /// + property OnConnected: TCrossConnectEvent read GetOnConnected write SetOnConnected; + + /// + /// 连接断开事件 + /// + property OnDisconnected: TCrossConnectEvent read GetOnDisconnected write SetOnDisconnected; + + /// + /// 收到数据事件 + /// + property OnReceived: TCrossDataEvent read GetOnReceived write SetOnReceived; + + /// + /// 发出数据事件 + /// + property OnSent: TCrossDataEvent read GetOnSent write SetOnSent; + end; + + TCrossData = class abstract(TInterfacedObject, ICrossData) + private + class var FCrossUID: UInt64; + private + [unsafe]FOwner: ICrossSocket; + FUID: UInt64; + FSocket: THandle; + FLocalAddr: string; + FLocalPort: Word; + FUserData: Pointer; + FUserObject: TObject; + FUserInterface: IInterface; + protected + function GetOwner: ICrossSocket; + function GetUIDTag: Byte; virtual; + function GetUID: UInt64; + function GetSocket: THandle; + function GetLocalAddr: string; + function GetLocalPort: Word; + function GetIsClosed: Boolean; virtual; abstract; + function GetUserData: Pointer; + function GetUserObject: TObject; + function GetUserInterface: IInterface; + + procedure SetUserData(const AValue: Pointer); + procedure SetUserObject(const AValue: TObject); + procedure SetUserInterface(const AValue: IInterface); + public + constructor Create(AOwner: ICrossSocket; ASocket: THandle); virtual; + destructor Destroy; override; + + procedure UpdateAddr; virtual; + procedure Close; virtual; abstract; + + property Owner: ICrossSocket read GetOwner; + property UID: UInt64 read GetUID; + property Socket: THandle read GetSocket; + property LocalAddr: string read GetLocalAddr; + property LocalPort: Word read GetLocalPort; + property IsClosed: Boolean read GetIsClosed; + property UserData: Pointer read GetUserData write SetUserData; + property UserObject: TObject read GetUserObject write SetUserObject; + property UserInterface: IInterface read GetUserInterface write SetUserInterface; + end; + + TAbstractCrossListen = class(TCrossData, ICrossListen) + private + FFamily: Integer; + FSockType: Integer; + FProtocol: Integer; + FClosed: Integer; + protected + function GetUIDTag: Byte; override; + function GetFamily: Integer; + function GetSockType: Integer; + function GetProtocol: Integer; + function GetIsClosed: Boolean; override; + public + constructor Create(AOwner: ICrossSocket; AListenSocket: THandle; + AFamily, ASockType, AProtocol: Integer); reintroduce; virtual; + + procedure Close; override; + + property Owner: ICrossSocket read GetOwner; + property Socket: THandle read GetSocket; + property LocalAddr: string read GetLocalAddr; + property LocalPort: Word read GetLocalPort; + property IsClosed: Boolean read GetIsClosed; + end; + + TAbstractCrossConnection = class(TCrossData, ICrossConnection) + public const + SND_BUF_SIZE = 32768; + private + FPeerAddr: string; + FPeerPort: Word; + FConnectType: TConnectType; + FConnectStatus: Integer; + protected + function GetUIDTag: Byte; override; + function GetPeerAddr: string; + function GetPeerPort: Word; + function GetConnectType: TConnectType; + function GetConnectStatus: TConnectStatus; + function GetIsClosed: Boolean; override; + + function _SetConnectStatus(const AStatus: TConnectStatus): TConnectStatus; inline; + procedure SetConnectStatus(const Value: TConnectStatus); + + procedure DirectSend(ABuffer: Pointer; ACount: Integer; + const ACallback: TProc = nil); virtual; + public + constructor Create(AOwner: ICrossSocket; AClientSocket: THandle; + AConnectType: TConnectType); reintroduce; virtual; + + procedure UpdateAddr; override; + procedure Close; override; + procedure Disconnect; virtual; + + procedure SendBuf(ABuffer: Pointer; ACount: Integer; + const ACallback: TProc = nil); overload; + procedure SendBuf(const ABuffer; ACount: Integer; + const ACallback: TProc = nil); overload; inline; + procedure SendBytes(const ABytes: TBytes; AOffset, ACount: Integer; + const ACallback: TProc = nil); overload; + procedure SendBytes(const ABytes: TBytes; + const ACallback: TProc = nil); overload; inline; + procedure SendStream(const AStream: TStream; + const ACallback: TProc = nil); + + property Owner: ICrossSocket read GetOwner; + property Socket: THandle read GetSocket; + property LocalAddr: string read GetLocalAddr; + property LocalPort: Word read GetLocalPort; + property IsClosed: Boolean read GetIsClosed; + + property PeerAddr: string read GetPeerAddr; + property PeerPort: Word read GetPeerPort; + property ConnectType: TConnectType read GetConnectType; + property ConnectStatus: TConnectStatus read GetConnectStatus write SetConnectStatus; + end; + + TIoEventThread = class(TThread) + private + [unsafe]FCrossSocket: ICrossSocket; + protected + procedure Execute; override; + public + constructor Create(ACrossSocket: ICrossSocket); reintroduce; + end; + + TAbstractCrossSocket = class abstract(TInterfacedObject, ICrossSocket) + protected const + RCV_BUF_SIZE = 32768; + protected class threadvar + FRecvBuf: array [0..RCV_BUF_SIZE-1] of Byte; + protected + FIoThreads: Integer; + + // 设置套接字心跳参数, 用于处理异常断线(拔网线, 主机异常掉电等造成的网络异常) + function SetKeepAlive(ASocket: THandle): Integer; + private + FConnections: TCrossConnections; + FConnectionsLock: TObject; + + FListens: TCrossListens; + FListensLock: TObject; + + FOnIoThreadBegin: TCrossIoThreadEvent; + FOnIoThreadEnd: TCrossIoThreadEvent; + FOnListened: TCrossListenEvent; + FOnListenEnd: TCrossListenEvent; + FOnConnected: TCrossConnectEvent; + FOnDisconnected: TCrossConnectEvent; + FOnReceived: TCrossDataEvent; + FOnSent: TCrossDataEvent; + + procedure _LockConnections; inline; + procedure _UnlockConnections; inline; + + procedure _LockListens; inline; + procedure _UnlockListens; inline; + + function GetConnectionsCount: Integer; + function GetListensCount: Integer; + + function GetOnIoThreadBegin: TCrossIoThreadEvent; + function GetOnIoThreadEnd: TCrossIoThreadEvent; + function GetOnConnected: TCrossConnectEvent; + function GetOnDisconnected: TCrossConnectEvent; + function GetOnListened: TCrossListenEvent; + function GetOnListenEnd: TCrossListenEvent; + function GetOnReceived: TCrossDataEvent; + function GetOnSent: TCrossDataEvent; + + procedure SetOnIoThreadBegin(const Value: TCrossIoThreadEvent); + procedure SetOnIoThreadEnd(const Value: TCrossIoThreadEvent); + procedure SetOnConnected(const Value: TCrossConnectEvent); + procedure SetOnDisconnected(const Value: TCrossConnectEvent); + procedure SetOnListened(const Value: TCrossListenEvent); + procedure SetOnListenEnd(const Value: TCrossListenEvent); + procedure SetOnReceived(const Value: TCrossDataEvent); + procedure SetOnSent(const Value: TCrossDataEvent); + protected + FConnectionsCount: Integer; + FListensCount: Integer; + + function ProcessIoEvent: Boolean; virtual; abstract; + function GetIoThreads: Integer; virtual; + + // 创建连接对象 + function CreateConnection(AOwner: ICrossSocket; AClientSocket: THandle; + AConnectType: TConnectType): ICrossConnection; virtual; abstract; + + // 创建监听对象 + function CreateListen(AOwner: ICrossSocket; AListenSocket: THandle; + AFamily, ASockType, AProtocol: Integer): ICrossListen; virtual; abstract; + + {$region '物理事件'} + procedure TriggerListened(AListen: ICrossListen); virtual; + procedure TriggerListenEnd(AListen: ICrossListen); virtual; + + procedure TriggerConnecting(AConnection: ICrossConnection); virtual; + procedure TriggerConnected(AConnection: ICrossConnection); virtual; + procedure TriggerDisconnected(AConnection: ICrossConnection); virtual; + + procedure TriggerReceived(AConnection: ICrossConnection; ABuf: Pointer; ALen: Integer); virtual; + procedure TriggerSent(AConnection: ICrossConnection; ABuf: Pointer; ALen: Integer); virtual; + {$endregion} + + {$region '逻辑事件'} + // 这几个虚方法用于在派生类中使用 + // 比如SSL中网络端口收到的是加密数据, 可能要几次接收才会收到一个完整的 + // 已加密数据包, 然后才能解密出数据, 也就是说可能几次网络端口的接收才 + // 会对应到一次实际的数据接收, 所以设计了以下接口, 以下接口是实际数据 + // 发生时才会被触发的 + procedure LogicConnected(AConnection: ICrossConnection); virtual; + procedure LogicDisconnected(AConnection: ICrossConnection); virtual; + procedure LogicReceived(AConnection: ICrossConnection; ABuf: Pointer; ALen: Integer); virtual; + procedure LogicSent(AConnection: ICrossConnection; ABuf: Pointer; ALen: Integer); virtual; + {$endregion} + + procedure TriggerIoThreadBegin(AIoThread: TIoEventThread); virtual; + procedure TriggerIoThreadEnd(AIoThread: TIoEventThread); virtual; + + procedure StartLoop; virtual; abstract; + procedure StopLoop; virtual; abstract; + + procedure Listen(const AHost: string; APort: Word; + const ACallback: TProc = nil); virtual; abstract; + + procedure Connect(const AHost: string; APort: Word; + const ACallback: TProc = nil); virtual; abstract; + + procedure Send(AConnection: ICrossConnection; ABuf: Pointer; ALen: Integer; + const ACallback: TProc = nil); virtual; abstract; + + procedure CloseAllConnections; virtual; + procedure CloseAllListens; virtual; + procedure CloseAll; virtual; + procedure DisconnectAll; virtual; + public + constructor Create(AIoThreads: Integer); virtual; + destructor Destroy; override; + + procedure AfterConstruction; override; + procedure BeforeDestruction; override; + + function LockConnections: TCrossConnections; + procedure UnlockConnections; + + function LockListens: TCrossListens; + procedure UnlockListens; + + property IoThreads: Integer read GetIoThreads; + property ConnectionsCount: Integer read GetConnectionsCount; + property ListensCount: Integer read GetListensCount; + + property OnIoThreadBegin: TCrossIoThreadEvent read GetOnIoThreadBegin write SetOnIoThreadBegin; + property OnIoThreadEnd: TCrossIoThreadEvent read GetOnIoThreadEnd write SetOnIoThreadEnd; + property OnListened: TCrossListenEvent read GetOnListened write SetOnListened; + property OnListenEnd: TCrossListenEvent read GetOnListenEnd write SetOnListenEnd; + property OnConnected: TCrossConnectEvent read GetOnConnected write SetOnConnected; + property OnDisconnected: TCrossConnectEvent read GetOnDisconnected write SetOnDisconnected; + property OnReceived: TCrossDataEvent read GetOnReceived write SetOnReceived; + property OnSent: TCrossDataEvent read GetOnSent write SetOnSent; + end; + + function GetTagByUID(const AUID: UInt64): Byte; + + procedure _LogLastOsError(const ATag: string = ''); + procedure _Log(const S: string); overload; + procedure _Log(const Fmt: string; const Args: array of const); overload; + +implementation + +uses + Utils.Logger; + +function GetTagByUID(const AUID: UInt64): Byte; +begin + // 取最高 2 位 + Result := (AUID shr 62) and $03; +end; + +procedure _Log(const S: string); overload; +begin + if IsConsole then + Writeln(S) + else + AppendLog(S); +end; + +procedure _Log(const Fmt: string; const Args: array of const); overload; +begin + _Log(Format(Fmt, Args)); +end; + +procedure _LogLastOsError(const ATag: string); +{$IFDEF DEBUG} +var + LError: Integer; + LErrMsg: string; +{$ENDIF} +begin + {$IFDEF DEBUG} + LError := GetLastError; + if (ATag <> '') then + LErrMsg := ATag + ' : ' + else + LErrMsg := ''; + LErrMsg := LErrMsg + Format('System Error. Code: %0:d(%0:.4x), %1:s', + [LError, SysErrorMessage(LError)]); + _Log(LErrMsg); + {$ENDIF} +end; + +{ TIoEventThread } + +constructor TIoEventThread.Create(ACrossSocket: ICrossSocket); +begin + inherited Create(True); + FCrossSocket := ACrossSocket; + Suspended := False; +end; + +procedure TIoEventThread.Execute; +var + {$IFDEF DEBUG} + LRunCount: Int64; + {$ENDIF} + LCrossSocketObj: TAbstractCrossSocket; +begin + LCrossSocketObj := FCrossSocket as TAbstractCrossSocket; + try + LCrossSocketObj.TriggerIoThreadBegin(Self); + {$IFDEF DEBUG} + LRunCount := 0; + {$ENDIF} + while not Terminated do + begin + try + if not LCrossSocketObj.ProcessIoEvent then Break; + except + {$IFDEF DEBUG} + on e: Exception do + _Log('%s Io线程ID %d, 异常 %s, %s', [TAbstractCrossSocket(FCrossSocket).ClassName, Self.ThreadID, e.ClassName, e.Message]); + {$ENDIF} + end; + {$IFDEF DEBUG} + Inc(LRunCount) + {$ENDIF}; + end; + {$IFDEF DEBUG} + // _Log('%s Io线程ID %d, 被调用了 %d 次', [TAbstractCrossSocket(FCrossSocket).ClassName, Self.ThreadID, LRunCount]); + {$ENDIF} + finally + LCrossSocketObj.TriggerIoThreadEnd(Self); + end; +end; + +{ TAbstractCrossSocket } + +procedure TAbstractCrossSocket.CloseAll; +begin + CloseAllListens; + CloseAllConnections; +end; + +procedure TAbstractCrossSocket.CloseAllConnections; +var + LLConnectionArr: TArray; + LConnection: ICrossConnection; +begin + _LockConnections; + try + LLConnectionArr := FConnections.Values.ToArray; + finally + _UnlockConnections; + end; + + for LConnection in LLConnectionArr do + LConnection.Close; +end; + +procedure TAbstractCrossSocket.CloseAllListens; +var + LListenArr: TArray; + LListen: ICrossListen; +begin + _LockListens; + try + LListenArr := FListens.Values.ToArray; + finally + _UnlockListens; + end; + + for LListen in LListenArr do + LListen.Close; +end; + +constructor TAbstractCrossSocket.Create(AIoThreads: Integer); +begin + FIoThreads := AIoThreads; + + FListens := TCrossListens.Create; + FListensLock := TObject.Create; + + FConnections := TCrossConnections.Create; + FConnectionsLock := TObject.Create; +end; + +destructor TAbstractCrossSocket.Destroy; +begin + FreeAndNil(FListens); + FreeAndNil(FListensLock); + + FreeAndNil(FConnections); + FreeAndNil(FConnectionsLock); + + inherited; +end; + +procedure TAbstractCrossSocket.DisconnectAll; +var + LLConnectionArr: TArray; + LConnection: ICrossConnection; +begin + _LockConnections; + try + LLConnectionArr := FConnections.Values.ToArray; + finally + _UnlockConnections; + end; + + for LConnection in LLConnectionArr do + LConnection.Disconnect; +end; + +procedure TAbstractCrossSocket.AfterConstruction; +begin + StartLoop; + inherited AfterConstruction; +end; + +procedure TAbstractCrossSocket.BeforeDestruction; +begin + StopLoop; + inherited BeforeDestruction; +end; + +function TAbstractCrossSocket.GetConnectionsCount: Integer; +begin + Result := FConnectionsCount; +end; + +function TAbstractCrossSocket.GetIoThreads: Integer; +begin + if (FIoThreads > 0) then + Result := FIoThreads + else + Result := CPUCount * 2 + 1; +end; + +function TAbstractCrossSocket.GetListensCount: Integer; +begin + Result := FListensCount; +end; + +function TAbstractCrossSocket.GetOnConnected: TCrossConnectEvent; +begin + Result := FOnConnected; +end; + +function TAbstractCrossSocket.GetOnDisconnected: TCrossConnectEvent; +begin + Result := FOnDisconnected; +end; + +function TAbstractCrossSocket.GetOnIoThreadBegin: TCrossIoThreadEvent; +begin + Result := FOnIoThreadBegin; +end; + +function TAbstractCrossSocket.GetOnIoThreadEnd: TCrossIoThreadEvent; +begin + Result := FOnIoThreadEnd; +end; + +function TAbstractCrossSocket.GetOnListened: TCrossListenEvent; +begin + Result := FOnListened; +end; + +function TAbstractCrossSocket.GetOnListenEnd: TCrossListenEvent; +begin + Result := FOnListenEnd; +end; + +function TAbstractCrossSocket.GetOnReceived: TCrossDataEvent; +begin + Result := FOnReceived; +end; + +function TAbstractCrossSocket.GetOnSent: TCrossDataEvent; +begin + Result := FOnSent; +end; + +function TAbstractCrossSocket.LockConnections: TCrossConnections; +begin + _LockConnections; + Result := FConnections; +end; + +function TAbstractCrossSocket.LockListens: TCrossListens; +begin + _LockListens; + Result := FListens; +end; + +procedure TAbstractCrossSocket.LogicConnected(AConnection: ICrossConnection); +begin + +end; + +procedure TAbstractCrossSocket.LogicDisconnected(AConnection: ICrossConnection); +begin + +end; + +procedure TAbstractCrossSocket.LogicReceived(AConnection: ICrossConnection; + ABuf: Pointer; ALen: Integer); +begin + +end; + +procedure TAbstractCrossSocket.LogicSent(AConnection: ICrossConnection; + ABuf: Pointer; ALen: Integer); +begin + +end; + +function TAbstractCrossSocket.SetKeepAlive(ASocket: THandle): Integer; +begin + Result := TSocketAPI.SetKeepAlive(ASocket, 5, 3, 5); +end; + +procedure TAbstractCrossSocket.SetOnConnected(const Value: TCrossConnectEvent); +begin + FOnConnected := Value; +end; + +procedure TAbstractCrossSocket.SetOnDisconnected(const Value: TCrossConnectEvent); +begin + FOnDisconnected := Value; +end; + +procedure TAbstractCrossSocket.SetOnIoThreadBegin( + const Value: TCrossIoThreadEvent); +begin + FOnIoThreadBegin := Value; +end; + +procedure TAbstractCrossSocket.SetOnIoThreadEnd( + const Value: TCrossIoThreadEvent); +begin + FOnIoThreadEnd := Value; +end; + +procedure TAbstractCrossSocket.SetOnListened(const Value: TCrossListenEvent); +begin + FOnListened := Value; +end; + +procedure TAbstractCrossSocket.SetOnListenEnd(const Value: TCrossListenEvent); +begin + FOnListenEnd := Value; +end; + +procedure TAbstractCrossSocket.SetOnReceived(const Value: TCrossDataEvent); +begin + FOnReceived := Value; +end; + +procedure TAbstractCrossSocket.SetOnSent(const Value: TCrossDataEvent); +begin + FOnSent := Value; +end; + +procedure TAbstractCrossSocket.TriggerConnecting(AConnection: ICrossConnection); +begin + AConnection.ConnectStatus := csConnecting; + + _LockConnections; + try + if not FConnections.ContainsKey(AConnection.UID) then + Inc(FConnectionsCount); + + FConnections.AddOrSetValue(AConnection.UID, AConnection); + finally + _UnlockConnections; + end; +end; + +procedure TAbstractCrossSocket.TriggerConnected(AConnection: ICrossConnection); +begin + AConnection.UpdateAddr; + AConnection.ConnectStatus := csConnected; + + LogicConnected(AConnection); + + if Assigned(FOnConnected) then + FOnConnected(Self, AConnection); +end; + +procedure TAbstractCrossSocket.TriggerDisconnected(AConnection: ICrossConnection); +begin + AConnection.ConnectStatus := csClosed; + + _LockConnections; + try + if not FConnections.ContainsKey(AConnection.UID) then Exit; + + FConnections.Remove(AConnection.UID); + Dec(FConnectionsCount); + finally + _UnlockConnections; + end; + + LogicDisconnected(AConnection); + + if Assigned(FOnDisconnected) then + FOnDisconnected(Self, AConnection); +end; + +procedure TAbstractCrossSocket.TriggerIoThreadBegin(AIoThread: TIoEventThread); +begin + if Assigned(FOnIoThreadBegin) then + FOnIoThreadBegin(Self, AIoThread); +end; + +procedure TAbstractCrossSocket.TriggerIoThreadEnd(AIoThread: TIoEventThread); +begin + if Assigned(FOnIoThreadEnd) then + FOnIoThreadEnd(Self, AIoThread); +end; + +procedure TAbstractCrossSocket.TriggerListened(AListen: ICrossListen); +begin + AListen.UpdateAddr; + + _LockListens; + try + FListens.AddOrSetValue(AListen.UID, AListen); + FListensCount := FListens.Count; + finally + _UnlockListens; + end; + + if Assigned(FOnListened) then + FOnListened(Self, AListen); +end; + +procedure TAbstractCrossSocket.TriggerListenEnd(AListen: ICrossListen); +begin + _LockListens; + try + if not FListens.ContainsKey(AListen.UID) then Exit; + FListens.Remove(AListen.UID); + FListensCount := FListens.Count; + finally + _UnlockListens; + end; + + if Assigned(FOnListenEnd) then + FOnListenEnd(Self, AListen); +end; + +procedure TAbstractCrossSocket.TriggerReceived(AConnection: ICrossConnection; + ABuf: Pointer; ALen: Integer); +begin + LogicReceived(AConnection, ABuf, ALen); + + if Assigned(FOnReceived) then + FOnReceived(Self, AConnection, ABuf, ALen); +end; + +procedure TAbstractCrossSocket.TriggerSent(AConnection: ICrossConnection; + ABuf: Pointer; ALen: Integer); +begin + LogicSent(AConnection, ABuf, ALen); + + if Assigned(FOnSent) then + FOnSent(Self, AConnection, ABuf, ALen); +end; + +procedure TAbstractCrossSocket.UnlockConnections; +begin + _UnlockConnections; +end; + +procedure TAbstractCrossSocket.UnlockListens; +begin + _UnlockListens; +end; + +procedure TAbstractCrossSocket._LockConnections; +begin + System.TMonitor.Enter(FConnectionsLock); +end; + +procedure TAbstractCrossSocket._LockListens; +begin + System.TMonitor.Enter(FListensLock); +end; + +procedure TAbstractCrossSocket._UnlockConnections; +begin + System.TMonitor.Exit(FConnectionsLock); +end; + +procedure TAbstractCrossSocket._UnlockListens; +begin + System.TMonitor.Exit(FListensLock); +end; + +{ TCrossData } + +constructor TCrossData.Create(AOwner: ICrossSocket; ASocket: THandle); +begin + // 理论上说62位的唯一编号永远也不可能用完 + // 所以也就不用考虑编号重置的问题了 + FUID := + // 高2位 标志位 + (UInt64(GetUIDTag and $03) shl 62) or + // 低62位 编号位 + (UID_MASK and AtomicIncrement(FCrossUID)); + + FOwner := AOwner; + FSocket := ASocket; +end; + +destructor TCrossData.Destroy; +begin + if (FSocket <> INVALID_HANDLE_VALUE) then + begin + TSocketAPI.CloseSocket(FSocket); + {$IFDEF DEBUG} +// _Log('close result %d', [GetLastError]); + {$ENDIF} + FSocket := INVALID_HANDLE_VALUE; + end; + + inherited; +end; + +function TCrossData.GetLocalAddr: string; +begin + Result := FLocalAddr; +end; + +function TCrossData.GetLocalPort: Word; +begin + Result := FLocalPort; +end; + +function TCrossData.GetOwner: ICrossSocket; +begin + Result := FOwner; +end; + +function TCrossData.GetSocket: THandle; +begin + Result := FSocket; +end; + +function TCrossData.GetUID: UInt64; +begin + Result := FUID; +end; + +function TCrossData.GetUIDTag: Byte; +begin + Result := UID_RAW; +end; + +function TCrossData.GetUserData: Pointer; +begin + Result := FUserData; +end; + +function TCrossData.GetUserInterface: IInterface; +begin + Result := FUserInterface; +end; + +function TCrossData.GetUserObject: TObject; +begin + Result := FUserObject; +end; + +procedure TCrossData.SetUserData(const AValue: Pointer); +begin + FUserData := AValue; +end; + +procedure TCrossData.SetUserInterface(const AValue: IInterface); +begin + FUserInterface := AValue; +end; + +procedure TCrossData.SetUserObject(const AValue: TObject); +begin + FUserObject := AValue; +end; + +procedure TCrossData.UpdateAddr; +var + LAddr: TRawSockAddrIn; +begin + {$region '本地地址信息'} + FillChar(LAddr, SizeOf(TRawSockAddrIn), 0); + LAddr.AddrLen := SizeOf(LAddr.Addr6); + if (TSocketAPI.GetSockName(FSocket, @LAddr.Addr, LAddr.AddrLen) = 0) then + TSocketAPI.ExtractAddrInfo(@LAddr.Addr, LAddr.AddrLen, + FLocalAddr, FLocalPort); + {$endregion} +end; + +{ TAbstractCrossListen } + +constructor TAbstractCrossListen.Create(AOwner: ICrossSocket; AListenSocket: THandle; + AFamily, ASockType, AProtocol: Integer); +begin + inherited Create(AOwner, AListenSocket); + + FFamily := AFamily; + FSockType := ASockType; + FProtocol := AProtocol; + + FClosed := 0; +end; + +procedure TAbstractCrossListen.Close; +begin + if (AtomicExchange(FClosed, 1) = 1) then Exit; + + if (FSocket <> INVALID_HANDLE_VALUE) then + begin + TSocketAPI.CloseSocket(FSocket); + FOwner.TriggerListenEnd(Self); + FSocket := INVALID_HANDLE_VALUE; + end; +end; + +function TAbstractCrossListen.GetFamily: Integer; +begin + Result := FFamily; +end; + +function TAbstractCrossListen.GetIsClosed: Boolean; +begin + Result := (FClosed = 1); +end; + +function TAbstractCrossListen.GetProtocol: Integer; +begin + Result := FProtocol; +end; + +function TAbstractCrossListen.GetSockType: Integer; +begin + Result := FSockType; +end; + +function TAbstractCrossListen.GetUIDTag: Byte; +begin + Result := UID_LISTEN; +end; + +{ TAbstractCrossConnection } + +constructor TAbstractCrossConnection.Create(AOwner: ICrossSocket; + AClientSocket: THandle; AConnectType: TConnectType); +begin + inherited Create(AOwner, AClientSocket); + + FConnectType := AConnectType; +end; + +procedure TAbstractCrossConnection.SetConnectStatus(const Value: TConnectStatus); +begin + _SetConnectStatus(Value); +end; + +procedure TAbstractCrossConnection.Close; +begin + if (_SetConnectStatus(csClosed) = csClosed) then Exit; + + if (FSocket <> INVALID_HANDLE_VALUE) then + begin + TSocketAPI.CloseSocket(FSocket); + FOwner.TriggerDisconnected(Self); + FSocket := INVALID_HANDLE_VALUE; + end; +end; + +procedure TAbstractCrossConnection.DirectSend(ABuffer: Pointer; ACount: Integer; + const ACallback: TProc); +var + LConnection: ICrossConnection; + LBuffer: Pointer; +begin + LConnection := Self as ICrossConnection; + + if (FSocket = INVALID_HANDLE_VALUE) + or IsClosed then + begin + if Assigned(ACallback) then + ACallback(LConnection, False); + Exit; + end; + + LBuffer := ABuffer; + FOwner.Send(LConnection, LBuffer, ACount, + procedure(AConnection: ICrossConnection; ASuccess: Boolean) + begin + if ASuccess then + (FOwner as TAbstractCrossSocket).TriggerSent(AConnection, LBuffer, ACount); + + if Assigned(ACallback) then + ACallback(AConnection, ASuccess); + end); +end; + +procedure TAbstractCrossConnection.Disconnect; +begin + if (_SetConnectStatus(csDisconnected) in [csDisconnected, csClosed]) then Exit; + + TSocketAPI.Shutdown(FSocket, 2); +end; + +function TAbstractCrossConnection.GetConnectStatus: TConnectStatus; +begin + Result := TConnectStatus(AtomicCmpExchange(FConnectStatus, 0, 0)); +end; + +function TAbstractCrossConnection.GetConnectType: TConnectType; +begin + Result := FConnectType; +end; + +function TAbstractCrossConnection.GetIsClosed: Boolean; +begin + Result := (GetConnectStatus = csClosed); +end; + +function TAbstractCrossConnection.GetPeerAddr: string; +begin + Result := FPeerAddr; +end; + +function TAbstractCrossConnection.GetPeerPort: Word; +begin + Result := FPeerPort; +end; + +function TAbstractCrossConnection.GetUIDTag: Byte; +begin + Result := UID_CONNECTION; +end; + +procedure TAbstractCrossConnection.SendBuf(ABuffer: Pointer; ACount: Integer; + const ACallback: TProc); +{$IF defined(POSIX) or not defined(__LITTLE_PIECE__)} +begin + DirectSend(ABuffer, ACount, ACallback); +end; +{$ELSE} // MSWINDOWS +// Windows下 iocp 发送数据会锁定非页面内存, 为了减少非页面内存的占用 +// 采用将大数据分小块发送的策略, 一个小块发送完之后再发送下一个 +var + LConnection: ICrossConnection; + P: PByte; + LSize: Integer; + LSender: TProc; +begin + LConnection := Self; + P := ABuffer; + LSize := ACount; + + LSender := + procedure(AConnection: ICrossConnection; ASuccess: Boolean) + var + LData: Pointer; + LCount: Integer; + begin + if not ASuccess then + begin + LSender := nil; + + if Assigned(ACallback) then + ACallback(AConnection, False); + + AConnection.Close; + + Exit; + end; + + LData := P; + LCount := Min(LSize, SND_BUF_SIZE); + + if (LSize > LCount) then + begin + Inc(P, LCount); + Dec(LSize, LCount); + end else + begin + LSize := 0; + P := nil; + end; + + if (LData = nil) or (LCount <= 0) then + begin + LSender := nil; + + if Assigned(ACallback) then + ACallback(AConnection, True); + + Exit; + end; + + TAbstractCrossConnection(AConnection).DirectSend(LData, LCount, LSender); + end; + + LSender(LConnection, True); +end; +{$ENDIF} + +procedure TAbstractCrossConnection.SendBuf(const ABuffer; ACount: Integer; + const ACallback: TProc); +begin + SendBuf(@ABuffer, ACount, ACallback); +end; + +procedure TAbstractCrossConnection.SendBytes(const ABytes: TBytes; AOffset, + ACount: Integer; const ACallback: TProc); +var + LBytes: TBytes; +begin + // 增加引用计数 + // 由于 SendBuf 的 ABuffer 参数是直接传递的内存地址 + // 所以并不会增加 ABytes 的引用计数, 这里为了保证发送过程中数据的有效性 + // 需要定义一个局部变量用来引用 ABytes, 以维持其引用计数 + LBytes := ABytes; + SendBuf(@LBytes[AOffset], ACount, + procedure(AConnection: ICrossConnection; ASuccess: Boolean) + begin + // 减少引用计数 + LBytes := nil; + + if Assigned(ACallback) then + ACallback(AConnection, ASuccess); + end); +end; + +procedure TAbstractCrossConnection.SendBytes(const ABytes: TBytes; + const ACallback: TProc); +begin + SendBytes(ABytes, 0, Length(ABytes), ACallback); +end; + +procedure TAbstractCrossConnection.SendStream(const AStream: TStream; + const ACallback: TProc); +var + LConnection: ICrossConnection; + LBuffer: TBytes; + LSender: TProc; +begin + if (AStream is TBytesStream) then + begin + SendBytes( + TBytesStream(AStream).Bytes, + TBytesStream(AStream).Position, + TBytesStream(AStream).Size - TBytesStream(AStream).Position, + ACallback); + Exit; + end; + + LConnection := Self; + SetLength(LBuffer, SND_BUF_SIZE); + + LSender := + procedure(AConnection: ICrossConnection; ASuccess: Boolean) + var + LData: Pointer; + LCount: Integer; + begin + if not ASuccess then + begin + LSender := nil; + LBuffer := nil; + + if Assigned(ACallback) then + ACallback(AConnection, False); + + AConnection.Close; + + Exit; + end; + + LData := @LBuffer[0]; + LCount := AStream.Read(LBuffer[0], SND_BUF_SIZE); + + if (LData = nil) or (LCount <= 0) then + begin + LSender := nil; + LBuffer := nil; + + if Assigned(ACallback) then + ACallback(AConnection, True); + + Exit; + end; + + TAbstractCrossConnection(AConnection).DirectSend(LData, LCount, LSender); + end; + + LSender(LConnection, True); +end; + +procedure TAbstractCrossConnection.UpdateAddr; +var + LAddr: TRawSockAddrIn; +begin + inherited; + + {$region '远端地址信息'} + FillChar(LAddr, SizeOf(TRawSockAddrIn), 0); + LAddr.AddrLen := SizeOf(LAddr.Addr6); + if (TSocketAPI.GetPeerName(FSocket, @LAddr.Addr, LAddr.AddrLen) = 0) then + TSocketAPI.ExtractAddrInfo(@LAddr.Addr, LAddr.AddrLen, FPeerAddr, FPeerPort); + {$endregion} +end; + +function TAbstractCrossConnection._SetConnectStatus( + const AStatus: TConnectStatus): TConnectStatus; +begin + Result := TConnectStatus(AtomicExchange(FConnectStatus, Integer(AStatus))); +end; + +end. diff --git a/ThirdParty/DCS/Net/Net.CrossSocket.Epoll.pas b/ThirdParty/DCS/Net/Net.CrossSocket.Epoll.pas new file mode 100644 index 00000000..22e30021 --- /dev/null +++ b/ThirdParty/DCS/Net/Net.CrossSocket.Epoll.pas @@ -0,0 +1,986 @@ +{******************************************************************************} +{ } +{ Delphi cross platform socket library } +{ } +{ Copyright (c) 2017 WiNDDRiVER(soulawing@gmail.com) } +{ } +{ Homepage: https://github.com/winddriver/Delphi-Cross-Socket } +{ } +{******************************************************************************} +unit Net.CrossSocket.Epoll; + +interface + +uses + System.SysUtils, + System.Classes, + System.Generics.Collections, + Posix.SysSocket, + Posix.NetinetIn, + Posix.UniStd, + Posix.NetDB, + Posix.Pthread, + Posix.Errno, + Linux.epoll, + Net.SocketAPI, + Net.CrossSocket.Base; + +type + TIoEvent = (ieRead, ieWrite); + TIoEvents = set of TIoEvent; + + TEpollListen = class(TAbstractCrossListen) + private + FLock: TObject; + FIoEvents: TIoEvents; + FOpCode: Integer; + + procedure _Lock; inline; + procedure _Unlock; inline; + + function _ReadEnabled: Boolean; inline; + function _UpdateIoEvent(const AIoEvents: TIoEvents): Boolean; + public + constructor Create(AOwner: ICrossSocket; AListenSocket: THandle; + AFamily, ASockType, AProtocol: Integer); override; + destructor Destroy; override; + end; + + PSendItem = ^TSendItem; + TSendItem = record + Data: PByte; + Size: Integer; + Callback: TProc; + end; + + TSendQueue = class(TList) + protected + procedure Notify(const Value: PSendItem; Action: TCollectionNotification); override; + end; + + TEpollConnection = class(TAbstractCrossConnection) + private + FLock: TObject; + FSendQueue: TSendQueue; + FIoEvents: TIoEvents; + FConnectCallback: TProc; // 用于 Connect 回调 + FOpCode: Integer; + + procedure _Lock; inline; + procedure _Unlock; inline; + + function _ReadEnabled: Boolean; inline; + function _WriteEnabled: Boolean; inline; + function _UpdateIoEvent(const AIoEvents: TIoEvents): Boolean; + public + constructor Create(AOwner: ICrossSocket; AClientSocket: THandle; + AConnectType: TConnectType); override; + destructor Destroy; override; + end; + + // KQUEUE 与 EPOLL 队列的差异 + // KQUEUE的队列中, 一个Socket句柄可以有多条记录, 每个事件一条, + // 这一点和 EPOLL 不一样, EPOLL中每个Socket句柄只会有一条记录 + // 要监测多个事件时, 只需要将多个事件做位运算加在一起调用 epoll_ctl 即可 + // + // EPOLLONESHOT 是令 epoll 支持线程池的关键 + // 该参数可以令事件触发后就立即被禁用, 避免让同一个Socket的同一个事件 + // 同时被多个工作线程触发, 由于 epoll 中每个 socket 只有一条记录, 所以 + // 一定要注意带上 EPOLLONESHOT 参数的 epoll_ctl, 在 epoll_wait 之后一定要再次 + // 调用 epoll_ctl 增加要监视的事件 + // + // EPOLL 发送数据 + // 最好的做法是将实际发送数据的动作放到 EPOLLOUT 触发时进行, 该 + // 事件触发表明 Socket 发送缓存有空闲空间了。IOCP 可以直接将待发送的数据及 + // 回调同时扔给 WSASend, 发送完成后去调用回调即可; EPOLL 机制不一样, 在 EPOLL + // 中没有类似 WSASend 的函数, 只能自行维护发送数据及回调的队列 + // EPOLL要支持多线程并发发送数据必须创建发送队列, 否则同一个 Socket 的并发发送 + // 极有可能有一部分会被其它发送覆盖掉 + // + // 由于 EPOLL 中每个套接字在队列中只有一条记录, 也就是说改写套接字的监视事件时 + // 后一次修改会修改之前的, 这就很难使用接口的引用计数机制来保持连接有效性了 + // 这里使用连接UID作为 epoll_ctl 的参数, 在事件触发时通过UID查找连接对象, 这样 + // 同样可以保证事件触发时访问到有效的连接对象, 而且不需要引用计数保证 + TEpollCrossSocket = class(TAbstractCrossSocket) + private const + MAX_EVENT_COUNT = 2048; + SHUTDOWN_FLAG = UInt64(-1); + private class threadvar + FEventList: array [0..MAX_EVENT_COUNT-1] of TEPoll_Event; + private + FEpollHandle: THandle; + FIoThreads: TArray; + FIdleHandle: THandle; + FIdleLock: TObject; + FStopHandle: THandle; + + // 利用 eventfd 唤醒并退出IO线程 + procedure _OpenStopHandle; inline; + procedure _PostStopCommand; inline; + procedure _CloseStopHandle; inline; + + procedure _OpenIdleHandle; inline; + procedure _CloseIdleHandle; inline; + + procedure _HandleAccept(AListen: ICrossListen); + procedure _HandleConnect(AConnection: ICrossConnection); + procedure _HandleRead(AConnection: ICrossConnection); + procedure _HandleWrite(AConnection: ICrossConnection); + protected + function CreateConnection(AOwner: ICrossSocket; AClientSocket: THandle; + AConnectType: TConnectType): ICrossConnection; override; + function CreateListen(AOwner: ICrossSocket; AListenSocket: THandle; + AFamily, ASockType, AProtocol: Integer): ICrossListen; override; + + procedure StartLoop; override; + procedure StopLoop; override; + + procedure Listen(const AHost: string; APort: Word; + const ACallback: TProc = nil); override; + + procedure Connect(const AHost: string; APort: Word; + const ACallback: TProc = nil); override; + + procedure Send(AConnection: ICrossConnection; ABuf: Pointer; ALen: Integer; + const ACallback: TProc = nil); override; + + function ProcessIoEvent: Boolean; override; + public + constructor Create(AIoThreads: Integer); override; + destructor Destroy; override; + end; + +implementation + +{$I Net.Posix.inc} + +{ TEpollListen } + +constructor TEpollListen.Create(AOwner: ICrossSocket; AListenSocket: THandle; + AFamily, ASockType, AProtocol: Integer); +begin + inherited; + + FLock := TObject.Create; + FOpCode := EPOLL_CTL_ADD; +end; + +destructor TEpollListen.Destroy; +begin + FreeAndNil(FLock); + + inherited; +end; + +procedure TEpollListen._Lock; +begin + System.TMonitor.Enter(FLock); +end; + +function TEpollListen._ReadEnabled: Boolean; +begin + Result := (ieRead in FIoEvents); +end; + +procedure TEpollListen._Unlock; +begin + System.TMonitor.Exit(FLock); +end; + +function TEpollListen._UpdateIoEvent(const AIoEvents: TIoEvents): Boolean; +var + LOwner: TEpollCrossSocket; + LEvent: TEPoll_Event; +begin + FIoEvents := AIoEvents; + + if (FIoEvents = []) or IsClosed then Exit(False); + + LOwner := TEpollCrossSocket(Owner); + + LEvent.Events := EPOLLET or EPOLLONESHOT; + LEvent.Data.u64 := Self.UID; + + if _ReadEnabled then + LEvent.Events := LEvent.Events or EPOLLIN; + + Result := (epoll_ctl(LOwner.FEpollHandle, FOpCode, Socket, @LEvent) >= 0); + FOpCode := EPOLL_CTL_MOD; + + {$IFDEF DEBUG} + if not Result then + _Log('listen %d epoll_ctl error %d', [UID, GetLastError]); + {$ENDIF} +end; + +{ TSendQueue } + +procedure TSendQueue.Notify(const Value: PSendItem; + Action: TCollectionNotification); +begin + inherited; + + if (Action = TCollectionNotification.cnRemoved) then + begin + if (Value <> nil) then + begin + Value.Callback := nil; + FreeMem(Value, SizeOf(TSendItem)); + end; + end; +end; + +{ TEpollConnection } + +constructor TEpollConnection.Create(AOwner: ICrossSocket; + AClientSocket: THandle; AConnectType: TConnectType); +begin + inherited; + + FSendQueue := TSendQueue.Create; + FLock := TObject.Create; + + FOpCode := EPOLL_CTL_ADD; +end; + +destructor TEpollConnection.Destroy; +var + LConnection: ICrossConnection; + LSendItem: PSendItem; +begin + LConnection := Self; + + _Lock; + try + // 连接释放时, 调用连接回调, 告知连接失败 + // 连接成功后 FConnectCallback 会被置为 nil, + // 所以如果这里 FConnectCallback 不等于 nil, 则表示连接释放时仍未连接成功 + if Assigned(FConnectCallback) then + begin + FConnectCallback(LConnection, False); + FConnectCallback := nil; + end; + + // 连接释放时, 调用所有发送队列的回调, 告知发送失败 + if (FSendQueue.Count > 0) then + begin + for LSendItem in FSendQueue do + if Assigned(LSendItem.Callback) then + LSendItem.Callback(LConnection, False); + + FSendQueue.Clear; + end; + + FreeAndNil(FSendQueue); + finally + _Unlock; + end; + + FreeAndNil(FLock); + + inherited; +end; + +procedure TEpollConnection._Lock; +begin + System.TMonitor.Enter(FLock); +end; + +function TEpollConnection._ReadEnabled: Boolean; +begin + Result := (ieRead in FIoEvents); +end; + +procedure TEpollConnection._Unlock; +begin + System.TMonitor.Exit(FLock); +end; + +function TEpollConnection._UpdateIoEvent(const AIoEvents: TIoEvents): Boolean; +var + LOwner: TEpollCrossSocket; + LEvent: TEPoll_Event; +begin + FIoEvents := AIoEvents; + + if (FIoEvents = []) or IsClosed then Exit(False); + + LOwner := TEpollCrossSocket(Owner); + + LEvent.Events := EPOLLET or EPOLLONESHOT; + LEvent.Data.u64 := Self.UID; + + if _ReadEnabled then + LEvent.Events := LEvent.Events or EPOLLIN; + + if _WriteEnabled then + LEvent.Events := LEvent.Events or EPOLLOUT; + + Result := (epoll_ctl(LOwner.FEpollHandle, FOpCode, Socket, @LEvent) >= 0); + FOpCode := EPOLL_CTL_MOD; + + {$IFDEF DEBUG} + if not Result then + _Log('connection %.16x epoll_ctl socket=%d events=0x%.8x error %d', + [UID, LEvent.Events, Socket, GetLastError]); + {$ENDIF} +end; + + +function TEpollConnection._WriteEnabled: Boolean; +begin + Result := (ieWrite in FIoEvents); +end; + +{ TEpollCrossSocket } + +constructor TEpollCrossSocket.Create(AIoThreads: Integer); +begin + inherited; + + FIdleLock := TObject.Create; +end; + +destructor TEpollCrossSocket.Destroy; +begin + FreeAndNil(FIdleLock); + + inherited; +end; + +procedure TEpollCrossSocket._CloseIdleHandle; +begin + FileClose(FIdleHandle); +end; + +procedure TEpollCrossSocket._CloseStopHandle; +begin + FileClose(FStopHandle); +end; + +procedure TEpollCrossSocket._HandleAccept(AListen: ICrossListen); +var + LListen: ICrossListen; + LConnection: ICrossConnection; + LEpConnection: TEpollConnection; + LSocket, LError: Integer; + LListenSocket, LClientSocket: THandle; + LSuccess: Boolean; +begin + LListen := AListen; + LListenSocket := LListen.Socket; + + while True do + begin + LSocket := TSocketAPI.Accept(LListenSocket, nil, nil); + + // Accept失败 + // EAGAIN 所有就绪的连接都已处理完毕 + // EMFILE 进程的文件句柄已经用完了 + if (LSocket < 0) then + begin + LError := GetLastError; + + // 当句柄用完了的时候, 释放事先占用的临时句柄 + // 然后再次 accept, 然后将 accept 的句柄关掉 + // 这样可以保证在文件句柄耗尽的时候依然能响应连接请求 + // 并立即将新到的连接关闭 + if (LError = EMFILE) then + begin + System.TMonitor.Enter(FIdleLock); + try + _CloseIdleHandle; + LSocket := TSocketAPI.Accept(LListenSocket, nil, nil); + TSocketAPI.CloseSocket(LSocket); + _OpenIdleHandle; + finally + System.TMonitor.Exit(FIdleLock); + end; + end; + + Break; + end; + + LClientSocket := LSocket; + TSocketAPI.SetNonBlock(LClientSocket, True); + SetKeepAlive(LClientSocket); + + LConnection := CreateConnection(Self, LClientSocket, ctAccept); + TriggerConnecting(LConnection); + TriggerConnected(LConnection); + + // 连接建立后监视Socket的读事件 + LEpConnection := LConnection as TEpollConnection; + LEpConnection._Lock; + try + LSuccess := LEpConnection._UpdateIoEvent([ieRead]); + finally + LEpConnection._Unlock; + end; + + if not LSuccess then + LConnection.Close; + end; +end; + +procedure TEpollCrossSocket._HandleConnect(AConnection: ICrossConnection); +var + LConnection: ICrossConnection; + LEpConnection: TEpollConnection; + LConnectCallback: TProc; +begin + LConnection := AConnection; + + // Connect失败 + if (TSocketAPI.GetError(LConnection.Socket) <> 0) then + begin + {$IFDEF DEBUG} + _LogLastOsError; + {$ENDIF} + LConnection.Close; + Exit; + end; + + LEpConnection := LConnection as TEpollConnection; + + LEpConnection._Lock; + try + LConnectCallback := LEpConnection.FConnectCallback; + LEpConnection.FConnectCallback := nil; + finally + LEpConnection._Unlock; + end; + + TriggerConnected(LConnection); + + if Assigned(LConnectCallback) then + LConnectCallback(LConnection, True); +end; + +procedure TEpollCrossSocket._HandleRead(AConnection: ICrossConnection); +var + LConnection: ICrossConnection; + LRcvd, LError: Integer; +begin + LConnection := AConnection; + + while True do + begin + LRcvd := TSocketAPI.Recv(LConnection.Socket, FRecvBuf[0], RCV_BUF_SIZE); + + // 对方主动断开连接 + if (LRcvd = 0) then + begin +// _Log('connection=%.16x socket=%d read 0', [LConnection.UID, LConnection.Socket]); + LConnection.Close; + Exit; + end; + + if (LRcvd < 0) then + begin + LError := GetLastError; + + // 被系统信号中断, 可以重新recv + if (LError = EINTR) then + Continue + // 接收缓冲区中数据已经被取完了 + else if (LError = EAGAIN) or (LError = EWOULDBLOCK) then + Break + else + // 接收出错 + begin +// _Log('connection=%.16x socket=%d read error %d', [LConnection.UID, LConnection.Socket, GetLastError]); + LConnection.Close; + Exit; + end; + end; + + TriggerReceived(LConnection, @FRecvBuf[0], LRcvd); + + if (LRcvd < RCV_BUF_SIZE) then Break; + end; +end; + +procedure TEpollCrossSocket._HandleWrite(AConnection: ICrossConnection); +var + LConnection: ICrossConnection; + LEpConnection: TEpollConnection; + LSendItem: PSendItem; + LCallback: TProc; + LSent: Integer; +begin + LConnection := AConnection; + LEpConnection := LConnection as TEpollConnection; + + LEpConnection._Lock; + try + // 队列中没有数据了, 清除 ioWrite 标志 + if (LEpConnection.FSendQueue.Count <= 0) then + begin + LEpConnection._UpdateIoEvent([]); + Exit; + end; + + // 获取Socket发送队列中的第一条数据 + LSendItem := LEpConnection.FSendQueue.Items[0]; + + // 发送数据 + LSent := PosixSend(LConnection.Socket, LSendItem.Data, LSendItem.Size); + + {$region '全部发送完成'} + if (LSent >= LSendItem.Size) then + begin + // 先保存回调函数, 避免后面删除队列后将其释放 + LCallback := LSendItem.Callback; + + // 发送成功, 移除已发送成功的数据 + if (LEpConnection.FSendQueue.Count > 0) then + LEpConnection.FSendQueue.Delete(0); + + // 队列中没有数据了, 清除 ioWrite 标志 + if (LEpConnection.FSendQueue.Count <= 0) then + LEpConnection._UpdateIoEvent([]); + + if Assigned(LCallback) then + LCallback(LConnection, True); + + Exit; + end; + {$endregion} + + {$region '连接断开或发送错误'} + // 发送失败的回调会在连接对象的destroy方法中被调用 + if (LSent < 0) then Exit; + {$endregion} + + {$region '部分发送成功,在下一次唤醒发送线程时继续处理剩余部分'} + Dec(LSendItem.Size, LSent); + Inc(LSendItem.Data, LSent); + {$endregion} + finally + LEpConnection._Unlock; + end; +end; + + +procedure TEpollCrossSocket._OpenIdleHandle; +begin + FIdleHandle := FileOpen('/dev/null', fmOpenRead); +end; + +procedure TEpollCrossSocket._OpenStopHandle; +var + LEvent: TEPoll_Event; +begin + FStopHandle := eventfd(0, 0); + // 这里不使用 EPOLLET + // 这样可以保证通知退出的命令发出后, 所有IO线程都会收到 + LEvent.Events := EPOLLIN; + LEvent.Data.u64 := SHUTDOWN_FLAG; + epoll_ctl(FEpollHandle, EPOLL_CTL_ADD, FStopHandle, @LEvent); +end; + +procedure TEpollCrossSocket._PostStopCommand; +var + LStuff: UInt64; +begin + LStuff := 1; + // 往 FStopHandle 写入任意数据, 唤醒工作线程 + Posix.UniStd.__write(FStopHandle, @LStuff, SizeOf(LStuff)); +end; + +procedure TEpollCrossSocket.StartLoop; +var + I: Integer; + LCrossSocket: ICrossSocket; +begin + if (FIoThreads <> nil) then Exit; + + _OpenIdleHandle; + + // epoll_create(size) + // 这个 size 只要传递大于0的任何值都可以 + // 并不是说队列的大小会受限于该值 + // http://man7.org/linux/man-pages/man2/epoll_create.2.html + FEpollHandle := epoll_create(MAX_EVENT_COUNT); + LCrossSocket := Self; + SetLength(FIoThreads, GetIoThreads); + for I := 0 to Length(FIoThreads) - 1 do + FIoThreads[I] := TIoEventThread.Create(LCrossSocket); + + _OpenStopHandle; +end; + +procedure TEpollCrossSocket.StopLoop; +var + I: Integer; + LCurrentThreadID: TThreadID; +begin + if (FIoThreads = nil) then Exit; + + CloseAll; + + while (FListensCount > 0) or (FConnectionsCount > 0) do Sleep(1); + + _PostStopCommand; + + LCurrentThreadID := GetCurrentThreadId; + for I := 0 to Length(FIoThreads) - 1 do + begin + if (FIoThreads[I].ThreadID = LCurrentThreadID) then + raise ECrossSocket.Create('不能在IO线程中执行StopLoop!'); + + FIoThreads[I].WaitFor; + FreeAndNil(FIoThreads[I]); + end; + FIoThreads := nil; + + FileClose(FEpollHandle); + _CloseIdleHandle; + _CloseStopHandle; +end; + +procedure TEpollCrossSocket.Connect(const AHost: string; APort: Word; + const ACallback: TProc); + + procedure _Failed1; + begin + if Assigned(ACallback) then + ACallback(nil, False); + end; + + function _Connect(ASocket: THandle; AAddr: PRawAddrInfo): Boolean; + var + LConnection: ICrossConnection; + LEpConnection: TEpollConnection; + begin + if (TSocketAPI.Connect(ASocket, AAddr.ai_addr, AAddr.ai_addrlen) = 0) + or (GetLastError = EINPROGRESS) then + begin + LConnection := CreateConnection(Self, ASocket, ctConnect); + TriggerConnecting(LConnection); + LEpConnection := LConnection as TEpollConnection; + + LEpConnection._Lock; + try + LEpConnection.ConnectStatus := csConnecting; + LEpConnection.FConnectCallback := ACallback; + if not LEpConnection._UpdateIoEvent([ieWrite]) then + begin + if Assigned(ACallback) then + ACallback(LConnection, False); + LConnection.Close; + Exit(False); + end; + finally + LEpConnection._Unlock; + end; + end else + begin + if Assigned(ACallback) then + ACallback(nil, False); + TSocketAPI.CloseSocket(ASocket); + Exit(False); + end; + + Result := True; + end; + +var + LHints: TRawAddrInfo; + P, LAddrInfo: PRawAddrInfo; + LSocket: THandle; +begin + FillChar(LHints, SizeOf(TRawAddrInfo), 0); + LHints.ai_family := AF_UNSPEC; + LHints.ai_socktype := SOCK_STREAM; + LHints.ai_protocol := IPPROTO_TCP; + LAddrInfo := TSocketAPI.GetAddrInfo(AHost, APort, LHints); + if (LAddrInfo = nil) then + begin + _Failed1; + Exit; + end; + + P := LAddrInfo; + try + while (LAddrInfo <> nil) do + begin + LSocket := TSocketAPI.NewSocket(LAddrInfo.ai_family, LAddrInfo.ai_socktype, + LAddrInfo.ai_protocol); + if (LSocket = INVALID_HANDLE_VALUE) then + begin + _Failed1; + Exit; + end; + + TSocketAPI.SetNonBlock(LSocket, True); + SetKeepAlive(LSocket); + + if _Connect(LSocket, LAddrInfo) then Exit; + + LAddrInfo := PRawAddrInfo(LAddrInfo.ai_next); + end; + finally + TSocketAPI.FreeAddrInfo(P); + end; + + _Failed1; +end; + +function TEpollCrossSocket.CreateConnection(AOwner: ICrossSocket; + AClientSocket: THandle; AConnectType: TConnectType): ICrossConnection; +begin + Result := TEpollConnection.Create(AOwner, AClientSocket, AConnectType); +end; + +function TEpollCrossSocket.CreateListen(AOwner: ICrossSocket; + AListenSocket: THandle; AFamily, ASockType, AProtocol: Integer): ICrossListen; +begin + Result := TEpollListen.Create(AOwner, AListenSocket, AFamily, ASockType, AProtocol); +end; + +procedure TEpollCrossSocket.Listen(const AHost: string; APort: Word; + const ACallback: TProc); +var + LHints: TRawAddrInfo; + P, LAddrInfo: PRawAddrInfo; + LListenSocket: THandle; + LListen: ICrossListen; + LEpListen: TEpollListen; + LSuccess: Boolean; + + procedure _Failed; + begin + if Assigned(ACallback) then + ACallback(nil, False); + end; + +begin + FillChar(LHints, SizeOf(TRawAddrInfo), 0); + + LHints.ai_flags := AI_PASSIVE; + LHints.ai_family := AF_UNSPEC; + LHints.ai_socktype := SOCK_STREAM; + LHints.ai_protocol := IPPROTO_TCP; + LAddrInfo := TSocketAPI.GetAddrInfo(AHost, APort, LHints); + if (LAddrInfo = nil) then + begin + _Failed; + Exit; + end; + + P := LAddrInfo; + try + while (LAddrInfo <> nil) do + begin + LListenSocket := TSocketAPI.NewSocket(LAddrInfo.ai_family, LAddrInfo.ai_socktype, + LAddrInfo.ai_protocol); + if (LListenSocket = INVALID_HANDLE_VALUE) then + begin + _Failed; + Exit; + end; + + TSocketAPI.SetNonBlock(LListenSocket, True); + TSocketAPI.SetReUseAddr(LListenSocket, True); + + if (LAddrInfo.ai_family = AF_INET6) then + TSocketAPI.SetSockOpt(LListenSocket, IPPROTO_IPV6, IPV6_V6ONLY, 1); + + if (TSocketAPI.Bind(LListenSocket, LAddrInfo.ai_addr, LAddrInfo.ai_addrlen) < 0) + or (TSocketAPI.Listen(LListenSocket) < 0) then + begin + _Failed; + Exit; + end; + + LListen := CreateListen(Self, LListenSocket, LAddrInfo.ai_family, + LAddrInfo.ai_socktype, LAddrInfo.ai_protocol); + LEpListen := LListen as TEpollListen; + + // 监听套接字的读事件 + // 读事件到达表明有新连接 + LEpListen._Lock; + try + LSuccess := LEpListen._UpdateIoEvent([ieRead]); + finally + LEpListen._Unlock; + end; + + if not LSuccess then + begin + _Failed; + + Exit; + end; + + // 监听成功 + TriggerListened(LListen); + if Assigned(ACallback) then + ACallback(LListen, True); + + // 如果端口传入0,让所有地址统一用首个分配到的端口 + if (APort = 0) and (LAddrInfo.ai_next <> nil) then + Psockaddr_in(LAddrInfo.ai_next.ai_addr).sin_port := LListen.LocalPort; + + LAddrInfo := PRawAddrInfo(LAddrInfo.ai_next); + end; + finally + TSocketAPI.FreeAddrInfo(P); + end; +end; + +procedure TEpollCrossSocket.Send(AConnection: ICrossConnection; ABuf: Pointer; + ALen: Integer; const ACallback: TProc); +var + LEpConnection: TEpollConnection; + LSendItem: PSendItem; +begin + // 测试过先发送, 然后将剩余部分放入发送队列的做法 + // 发现会引起内存访问异常, 放到队列里到IO线程中发送则不会有问题 + {$region '放入发送队列'} + GetMem(LSendItem, SizeOf(TSendItem)); + FillChar(LSendItem^, SizeOf(TSendItem), 0); + LSendItem.Data := ABuf; + LSendItem.Size := ALen; + LSendItem.Callback := ACallback; + + LEpConnection := AConnection as TEpollConnection; + + LEpConnection._Lock; + try + // 将数据放入队列 + LEpConnection.FSendQueue.Add(LSendItem); + + // 由于epoll队列中每个套接字只有一条记录, 为了避免监视发送数据的时候 + // 无法接收数据, 这里必须同时监视读和写 + if not LEpConnection._WriteEnabled then + LEpConnection._UpdateIoEvent([ieRead, ieWrite]); + finally + LEpConnection._Unlock; + end; + {$endregion} +end; + +function TEpollCrossSocket.ProcessIoEvent: Boolean; +var + LRet, I: Integer; + LEvent: TEPoll_Event; + LCrossUID: UInt64; + LCrossTag: Byte; + LListens: TCrossListens; + LConnections: TCrossConnections; + LListen: ICrossListen; + LEpListen: TEpollListen; + LConnection: ICrossConnection; + LEpConnection: TEpollConnection; + LSuccess: Boolean; + LIoEvents: TIoEvents; +begin + // 被系统信号打断或者出错会返回-1, 具体需要根据错误代码判断 + LRet := epoll_wait(FEpollHandle, @FEventList[0], MAX_EVENT_COUNT, -1); + if (LRet < 0) then + begin + LRet := GetLastError; + // EINTR, epoll_wait 调用被系统信号打断, 可以进行重试 + Exit(LRet = EINTR); + end; + + for I := 0 to LRet - 1 do + begin + LEvent := FEventList[I]; + + // 收到退出命令 + if (LEvent.Data.u64 = SHUTDOWN_FLAG) then Exit(False); + + {$region '获取连接或监听对象'} + LCrossUID := LEvent.Data.u64; + LCrossTag := GetTagByUID(LCrossUID); + LListen := nil; + LConnection := nil; + + {$IFDEF DEBUG} +// _Log('epoll events %.8x, uid %.16x, tag %d', [LEvent.Events, LEvent.Data.u64, LCrossTag]); + {$ENDIF} + case LCrossTag of + UID_LISTEN: + begin + LListens := LockListens; + try + if not LListens.TryGetValue(LCrossUID, LListen) then + Continue; + finally + UnlockListens; + end; + end; + + UID_CONNECTION: + begin + LConnections := LockConnections; + try + if not LConnections.TryGetValue(LCrossUID, LConnection) + or (LConnection = nil) then + Continue; + finally + UnlockConnections; + end; + end; + else + Continue; + end; + {$endregion} + + {$region 'IO事件处理'} + if (LListen <> nil) then + begin + if (LEvent.Events and EPOLLIN <> 0) then + _HandleAccept(LListen); + + // 继续接收新连接 + LEpListen := LListen as TEpollListen; + LEpListen._Lock; + LEpListen._UpdateIoEvent([ieRead]); + LEpListen._Unlock; + end else + if (LConnection <> nil) then + begin + // epoll的读写事件同一时间可能两个同时触发 + if (LEvent.Events and EPOLLIN <> 0) then + _HandleRead(LConnection); + + if (LEvent.Events and EPOLLOUT <> 0) then + begin + if (LConnection.ConnectStatus = csConnecting) then + _HandleConnect(LConnection) + else + _HandleWrite(LConnection); + end; + + // 把更新连接的IO事件放到这里统一处理 + // 当读写同时触发的情况, 可以节省一次IO事件更新 + if not LConnection.IsClosed then + begin + LEpConnection := LConnection as TEpollConnection; + LEpConnection._Lock; + try + if (LEpConnection.FSendQueue.Count > 0) then + LIoEvents := [ieRead, ieWrite] + else + LIoEvents := [ieRead]; + LSuccess := LEpConnection._UpdateIoEvent(LIoEvents); + finally + LEpConnection._Unlock; + end; + + if not LSuccess then + LConnection.Close; + end; + end; + {$endregion} + end; + + Result := True; +end; + +end. diff --git a/ThirdParty/DCS/Net/Net.CrossSocket.Iocp.pas b/ThirdParty/DCS/Net/Net.CrossSocket.Iocp.pas new file mode 100644 index 00000000..998c0aae --- /dev/null +++ b/ThirdParty/DCS/Net/Net.CrossSocket.Iocp.pas @@ -0,0 +1,765 @@ +{******************************************************************************} +{ } +{ Delphi cross platform socket library } +{ } +{ Copyright (c) 2017 WiNDDRiVER(soulawing@gmail.com) } +{ } +{ Homepage: https://github.com/winddriver/Delphi-Cross-Socket } +{ } +{******************************************************************************} +unit Net.CrossSocket.Iocp; + +interface + +uses + System.SysUtils, + System.Classes, + Winapi.Windows, + Net.Winsock2, + Net.Wship6, + Net.SocketAPI, + Net.CrossSocket.Base; + +type + TIocpListen = class(TAbstractCrossListen) + end; + + TIocpConnection = class(TAbstractCrossConnection) + end; + + TIocpCrossSocket = class(TAbstractCrossSocket) + private const + SHUTDOWN_FLAG = ULONG_PTR(-1); + SO_UPDATE_CONNECT_CONTEXT = $7010; + IPV6_V6ONLY = 27; + ERROR_ABANDONED_WAIT_0 = $02DF; + private type + TAddrUnion = record + case Integer of + 0: (IPv4: TSockAddrIn); + 1: (IPv6: TSockAddrIn6); + end; + + TAddrBuffer = record + Addr: TAddrUnion; + Extra: array [0..15] of Byte; + end; + + TAcceptExBuffer = array[0..SizeOf(TAddrBuffer) * 2 - 1] of Byte; + + TPerIoBufUnion = record + case Integer of + 0: (DataBuf: WSABUF); + // 这个Buffer只用于AcceptEx保存终端地址数据,大小为2倍地址结构 + 1: (AcceptExBuffer: TAcceptExBuffer); + end; + + TIocpAction = (ioAccept, ioConnect, ioRead, ioWrite); + + PPerIoData = ^TPerIoData; + TPerIoData = record + Overlapped: TWSAOverlapped; + Buffer: TPerIoBufUnion; + Action: TIocpAction; + Socket: THandle; + CrossData: ICrossData; + Callback: TProc; + end; + private + FIocpHandle: THandle; + FIoThreads: TArray; + FPerIoDataCount: NativeInt; + + function _NewIoData: PPerIoData; inline; + procedure _FreeIoData(P: PPerIoData); inline; + + procedure _NewAccept(AListen: ICrossListen); + function _NewReadZero(AConnection: ICrossConnection): Boolean; + + procedure _HandleAccept(APerIoData: PPerIoData); + procedure _HandleConnect(APerIoData: PPerIoData); + procedure _HandleRead(APerIoData: PPerIoData); + procedure _HandleWrite(APerIoData: PPerIoData); + protected + function CreateListen(AOwner: ICrossSocket; AListenSocket: THandle; + AFamily, ASockType, AProtocol: Integer): ICrossListen; override; + function CreateConnection(AOwner: ICrossSocket; AClientSocket: THandle; + AConnectType: TConnectType): ICrossConnection; override; + + procedure StartLoop; override; + procedure StopLoop; override; + + procedure Listen(const AHost: string; APort: Word; + const ACallback: TProc = nil); override; + + procedure Connect(const AHost: string; APort: Word; + const ACallback: TProc = nil); override; + + procedure Send(AConnection: ICrossConnection; ABuf: Pointer; ALen: Integer; + const ACallback: TProc = nil); override; + + function ProcessIoEvent: Boolean; override; + end; + +implementation + +{ TIocpCrossSocket } + +function TIocpCrossSocket._NewIoData: PPerIoData; +begin + GetMem(Result, SizeOf(TPerIoData)); + FillChar(Result^, SizeOf(TPerIoData), 0); + + AtomicIncrement(FPerIoDataCount); +end; + +procedure TIocpCrossSocket._FreeIoData(P: PPerIoData); +begin + if (P = nil) then Exit; + + P.CrossData := nil; + P.Callback := nil; + FreeMem(P, SizeOf(TPerIoData)); + + AtomicDecrement(FPerIoDataCount); +end; + +procedure TIocpCrossSocket._NewAccept(AListen: ICrossListen); +var + LClientSocket: THandle; + LPerIoData: PPerIoData; + LBytes: Cardinal; +begin + LClientSocket := WSASocket(AListen.Family, AListen.SockType, AListen.Protocol, + nil, 0, WSA_FLAG_OVERLAPPED); + if (LClientSocket = INVALID_SOCKET) then + begin + {$IFDEF DEBUG} + _LogLastOsError('TIocpCrossSocket._NewAccept.WSASocket'); + {$ENDIF} + Exit; + end; + + TSocketAPI.SetNonBlock(LClientSocket, True); + SetKeepAlive(LClientSocket); + + LPerIoData := _NewIoData; + LPerIoData.Action := ioAccept; + LPerIoData.Socket := LClientSocket; + LPerIoData.CrossData := AListen; + + if (not AcceptEx(AListen.Socket, LClientSocket, @LPerIoData.Buffer.AcceptExBuffer, 0, + SizeOf(TAddrBuffer), SizeOf(TAddrBuffer), LBytes, POverlapped(LPerIoData))) + and (WSAGetLastError <> WSA_IO_PENDING) then + begin + {$IFDEF DEBUG} + _LogLastOsError('TIocpCrossSocket._NewAccept.AcceptEx'); + {$ENDIF} + TSocketAPI.CloseSocket(LClientSocket); + _FreeIoData(LPerIoData); + end; +end; + +function TIocpCrossSocket._NewReadZero(AConnection: ICrossConnection): Boolean; +var + LPerIoData: PPerIoData; + LBytes, LFlags: Cardinal; +begin + LPerIoData := _NewIoData; + LPerIoData.Buffer.DataBuf.buf := nil; + LPerIoData.Buffer.DataBuf.len := 0; + LPerIoData.Action := ioRead; + LPerIoData.Socket := AConnection.Socket; + LPerIoData.CrossData := AConnection; + + LFlags := 0; + LBytes := 0; + if (WSARecv(AConnection.Socket, @LPerIoData.Buffer.DataBuf, 1, LBytes, LFlags, PWSAOverlapped(LPerIoData), nil) < 0) + and (WSAGetLastError <> WSA_IO_PENDING) then + begin + {$IFDEF DEBUG} + _LogLastOsError('TIocpCrossSocket._NewReadZero.WSARecv'); + {$ENDIF} + _FreeIoData(LPerIoData); + Exit(False); + end; + + Result := True; +end; + +procedure TIocpCrossSocket._HandleAccept(APerIoData: PPerIoData); +var + LListen: ICrossListen; + LConnection: ICrossConnection; + LClientSocket, LListenSocket: THandle; +begin + if (APerIoData.CrossData = nil) then Exit; + + LListen := APerIoData.CrossData as ICrossListen; + + _NewAccept(LListen); + + LClientSocket := APerIoData.Socket; + LListenSocket := LListen.Socket; + + // 不设置该参数, 会导致 getpeername 调用失败 + if (TSocketAPI.SetSockOpt(LClientSocket, SOL_SOCKET, + SO_UPDATE_ACCEPT_CONTEXT, LListenSocket) < 0) then + begin + {$IFDEF DEBUG} + _LogLastOsError('TIocpCrossSocket._HandleAccept.SetSockOpt'); + {$ENDIF} + TSocketAPI.CloseSocket(LClientSocket); + Exit; + end; + + if (CreateIoCompletionPort(LClientSocket, FIocpHandle, ULONG_PTR(LClientSocket), 0) = 0) then + begin + {$IFDEF DEBUG} + _LogLastOsError('TIocpCrossSocket._HandleAccept.CreateIoCompletionPort'); + {$ENDIF} + TSocketAPI.CloseSocket(LClientSocket); + Exit; + end; + + LConnection := CreateConnection(Self, LClientSocket, ctAccept); + TriggerConnecting(LConnection); + TriggerConnected(LConnection); + + if not _NewReadZero(LConnection) then + LConnection.Close; +end; + +procedure TIocpCrossSocket._HandleConnect(APerIoData: PPerIoData); +var + LClientSocket: THandle; + LConnection: ICrossConnection; + LSuccess: Boolean; + + procedure _Failed1; + begin + {$IFDEF DEBUG} + _LogLastOsError('TIocpCrossSocket._HandleConnect'); + {$ENDIF} + + if Assigned(APerIoData.Callback) then + APerIoData.Callback(nil, False); + + TSocketAPI.CloseSocket(LClientSocket); + end; +begin + LClientSocket := APerIoData.Socket; + + if (TSocketAPI.GetError(LClientSocket) <> 0) then + begin + _Failed1; + Exit; + end; + + // 不设置该参数, 会导致 getpeername 调用失败 + if (TSocketAPI.SetSockOpt(LClientSocket, SOL_SOCKET, + SO_UPDATE_CONNECT_CONTEXT, 1) < 0) then + begin + _Failed1; + Exit; + end; + + LConnection := CreateConnection(Self, LClientSocket, ctConnect); + TriggerConnecting(LConnection); + + LSuccess := _NewReadZero(LConnection); + if LSuccess then + TriggerConnected(LConnection); + + if Assigned(APerIoData.Callback) then + APerIoData.Callback(LConnection, LSuccess); + + if not LSuccess then + LConnection.Close; +end; + +procedure TIocpCrossSocket._HandleRead(APerIoData: PPerIoData); +var + LConnection: ICrossConnection; + LRcvd, LError: Integer; +begin + if (APerIoData.CrossData = nil) then + begin + if Assigned(APerIoData.Callback) then + APerIoData.Callback(nil, False); + Exit; + end; + + LConnection := APerIoData.CrossData as ICrossConnection; + + while True do + begin + LRcvd := TSocketAPI.Recv(LConnection.Socket, FRecvBuf[0], RCV_BUF_SIZE); + + // 对方主动断开连接 + if (LRcvd = 0) then + begin + LConnection.Close; + Exit; + end; + + if (LRcvd < 0) then + begin + LError := GetLastError; + + // 被系统信号中断, 可以重新recv + if (LError = WSAEINTR) then + Continue + // 接收缓冲区中数据已经被取完了 + else if (LError = WSAEWOULDBLOCK) or (LError = WSAEINPROGRESS) then + Break + // 接收出错 + else + begin + LConnection.Close; + Exit; + end; + end; + + TriggerReceived(LConnection, @FRecvBuf[0], LRcvd); + + if (LRcvd < RCV_BUF_SIZE) then Break; + end; + + if not _NewReadZero(LConnection) then + LConnection.Close; +end; + +procedure TIocpCrossSocket._HandleWrite(APerIoData: PPerIoData); +begin + if Assigned(APerIoData.Callback) then + APerIoData.Callback(APerIoData.CrossData as ICrossConnection, True); +end; + +procedure TIocpCrossSocket.StartLoop; +var + I: Integer; + LCrossSocket: ICrossSocket; +begin + if (FIoThreads <> nil) then Exit; + + FIocpHandle := CreateIoCompletionPort(INVALID_HANDLE_VALUE, 0, 0, 0); + LCrossSocket := Self; + SetLength(FIoThreads, GetIoThreads); + for I := 0 to Length(FIoThreads) - 1 do + FIoThreads[I] := TIoEventThread.Create(LCrossSocket); +end; + +procedure TIocpCrossSocket.StopLoop; + + // IO 线程在收到 SHUTDOWN_FLAG 标记之后就会退出 + // 而这时候有可能还有部分操作未完成, 其对应的 PerIoData 结构就无法释放 + // 只需要在这里再次接收完成端口的消息, 就能等到这部分未完成的操作超时或失败 + // 从而释放其对应的 PerIoData 结构 + procedure _FreeMissingPerIoDatas; + var + LBytes: Cardinal; + LSocket: THandle; + LPerIoData: PPerIoData; + LConnection: ICrossConnection; + begin + while (AtomicCmpExchange(FPerIoDataCount, 0, 0) > 0) do + begin + GetQueuedCompletionStatus(FIocpHandle, LBytes, ULONG_PTR(LSocket), POverlapped(LPerIoData), 10); + + if (LPerIoData <> nil) then + begin + if Assigned(LPerIoData.Callback) then + begin + if (LPerIoData.CrossData <> nil) + and (LPerIoData.CrossData is TIocpConnection) then + LConnection := LPerIoData.CrossData as ICrossConnection + else + LConnection := nil; + + LPerIoData.Callback(LConnection, False); + end; + + if (LPerIoData.CrossData <> nil) then + LPerIoData.CrossData.Close + else + TSocketAPI.CloseSocket(LPerIoData.Socket); + + _FreeIoData(LPerIoData); + end; + end; + end; + +var + I: Integer; + LCurrentThreadID: TThreadID; +begin + if (FIoThreads = nil) then Exit; + + CloseAll; + + while (ListensCount > 0) or (ConnectionsCount > 0) do Sleep(1); + + for I := 0 to Length(FIoThreads) - 1 do + PostQueuedCompletionStatus(FIocpHandle, 0, 0, POverlapped(SHUTDOWN_FLAG)); + + LCurrentThreadID := GetCurrentThreadId; + for I := 0 to Length(FIoThreads) - 1 do + begin + if (FIoThreads[I].ThreadID = LCurrentThreadID) then + raise ECrossSocket.Create('不能在IO线程中执行StopLoop!'); + + FIoThreads[I].WaitFor; + FreeAndNil(FIoThreads[I]); + end; + FIoThreads := nil; + + _FreeMissingPerIoDatas; + CloseHandle(FIocpHandle); +end; + +procedure TIocpCrossSocket.Connect(const AHost: string; APort: Word; + const ACallback: TProc); +var + LHints: TRawAddrInfo; + P, LAddrInfo: PRawAddrInfo; + LSocket: THandle; + + procedure _Failed1; + begin + if Assigned(ACallback) then + ACallback(nil, False); + end; + + function _Connect(ASocket: THandle; AAddr: PRawAddrInfo): Boolean; + procedure _Failed2; + begin + if Assigned(ACallback) then + ACallback(nil, False); + TSocketAPI.CloseSocket(ASocket); + end; + var + LSockAddr: TRawSockAddrIn; + LPerIoData: PPerIoData; + LBytes: Cardinal; + begin + LSockAddr.AddrLen := AAddr.ai_addrlen; + Move(AAddr.ai_addr^, LSockAddr.Addr, AAddr.ai_addrlen); + if (AAddr.ai_family = AF_INET6) then + begin + LSockAddr.Addr6.sin6_addr := in6addr_any; + LSockAddr.Addr6.sin6_port := 0; + end else + begin + LSockAddr.Addr.sin_addr.S_addr := INADDR_ANY; + LSockAddr.Addr.sin_port := 0; + end; + if (TSocketAPI.Bind(ASocket, @LSockAddr.Addr, LSockAddr.AddrLen) < 0) then + begin + _Failed2; + Exit(False); + end; + + if (CreateIoCompletionPort(ASocket, FIocpHandle, ULONG_PTR(ASocket), 0) = 0) then + begin + _Failed2; + Exit(False); + end; + + LPerIoData := _NewIoData; + LPerIoData.Action := ioConnect; + LPerIoData.Socket := ASocket; + LPerIoData.Callback := ACallback; + if not ConnectEx(ASocket, AAddr.ai_addr, AAddr.ai_addrlen, nil, 0, LBytes, PWSAOverlapped(LPerIoData)) and + (WSAGetLastError <> WSA_IO_PENDING) then + begin + _FreeIoData(LPerIoData); + _Failed2; + Exit(False); + end; + + Result := True; + end; + +begin + FillChar(LHints, SizeOf(TRawAddrInfo), 0); + LHints.ai_family := AF_UNSPEC; + LHints.ai_socktype := SOCK_STREAM; + LHints.ai_protocol := IPPROTO_TCP; + LAddrInfo := TSocketAPI.GetAddrInfo(AHost, APort, LHints); + if (LAddrInfo = nil) then + begin + _Failed1; + Exit; + end; + + P := LAddrInfo; + try + while (LAddrInfo <> nil) do + begin + LSocket := WSASocket(LAddrInfo.ai_family, LAddrInfo.ai_socktype, + LAddrInfo.ai_protocol, nil, 0, WSA_FLAG_OVERLAPPED); + if (LSocket = INVALID_SOCKET) then + begin + _Failed1; + Exit; + end; + + TSocketAPI.SetNonBlock(LSocket, True); + SetKeepAlive(LSocket); + + if _Connect(LSocket, LAddrInfo) then Exit; + + LAddrInfo := PRawAddrInfo(LAddrInfo.ai_next); + end; + finally + TSocketAPI.FreeAddrInfo(P); + end; + + _Failed1; +end; + +function TIocpCrossSocket.CreateConnection(AOwner: ICrossSocket; + AClientSocket: THandle; AConnectType: TConnectType): ICrossConnection; +begin + Result := TIocpConnection.Create(AOwner, AClientSocket, AConnectType); +end; + +function TIocpCrossSocket.CreateListen(AOwner: ICrossSocket; + AListenSocket: THandle; AFamily, ASockType, AProtocol: Integer): ICrossListen; +begin + Result := TIocpListen.Create(AOwner, AListenSocket, AFamily, ASockType, AProtocol); +end; + +procedure TIocpCrossSocket.Listen(const AHost: string; APort: Word; + const ACallback: TProc); +var + LHints: TRawAddrInfo; + P, LAddrInfo: PRawAddrInfo; + LListenSocket: THandle; + LListen: ICrossListen; + I: Integer; + + procedure _Failed; + begin + if Assigned(ACallback) then + ACallback(LListen, False); + + if (LListen <> nil) then + LListen.Close; + end; + + procedure _Success; + begin + TriggerListened(LListen); + + if Assigned(ACallback) then + ACallback(LListen, True); + end; +begin + LListen := nil; + FillChar(LHints, SizeOf(TRawAddrInfo), 0); + + LHints.ai_flags := AI_PASSIVE; + LHints.ai_family := AF_UNSPEC; + LHints.ai_socktype := SOCK_STREAM; + LHints.ai_protocol := IPPROTO_TCP; + LAddrInfo := TSocketAPI.GetAddrInfo(AHost, APort, LHints); + if (LAddrInfo = nil) then + begin + {$IFDEF DEBUG} + _LogLastOsError('TIocpCrossSocket.Listen.GetAddrInfo'); + {$ENDIF} + _Failed; + Exit; + end; + + P := LAddrInfo; + try + while (LAddrInfo <> nil) do + begin + LListenSocket := WSASocket(LAddrInfo.ai_family, LAddrInfo.ai_socktype, + LAddrInfo.ai_protocol, nil, 0, WSA_FLAG_OVERLAPPED); + if (LListenSocket = INVALID_SOCKET) then + begin + {$IFDEF DEBUG} + _LogLastOsError('TIocpCrossSocket.Listen.WSASocket'); + {$ENDIF} + _Failed; + Exit; + end; + + TSocketAPI.SetNonBlock(LListenSocket, True); + TSocketAPI.SetReUseAddr(LListenSocket, True); + + if (LAddrInfo.ai_family = AF_INET6) then + TSocketAPI.SetSockOpt(LListenSocket, IPPROTO_IPV6, IPV6_V6ONLY, 1); + + if (TSocketAPI.Bind(LListenSocket, LAddrInfo.ai_addr, LAddrInfo.ai_addrlen) < 0) + or (TSocketAPI.Listen(LListenSocket) < 0) then + begin + {$IFDEF DEBUG} + _LogLastOsError('TIocpCrossSocket.Listen.Bind'); + {$ENDIF} + _Failed; + Exit; + end; + + LListen := CreateListen(Self, LListenSocket, LAddrInfo.ai_family, + LAddrInfo.ai_socktype, LAddrInfo.ai_protocol); + + if (CreateIoCompletionPort(LListenSocket, FIocpHandle, ULONG_PTR(LListenSocket), 0) = 0) then + begin + {$IFDEF DEBUG} + _LogLastOsError('TIocpCrossSocket.Listen.CreateIoCompletionPort'); + {$ENDIF} + _Failed; + Exit; + end; + + // 给每个IO线程投递一个AcceptEx + for I := 1 to GetIoThreads do + _NewAccept(LListen); + + _Success; + + // 如果端口传入0,让所有地址统一用首个分配到的端口 + if (APort = 0) and (LAddrInfo.ai_next <> nil) then + LAddrInfo.ai_next.ai_addr.sin_port := LListen.LocalPort; + + LAddrInfo := PRawAddrInfo(LAddrInfo.ai_next); + end; + finally + TSocketAPI.FreeAddrInfo(P); + end; +end; + +procedure TIocpCrossSocket.Send(AConnection: ICrossConnection; ABuf: Pointer; + ALen: Integer; const ACallback: TProc); +var + LPerIoData: PPerIoData; + LBytes, LFlags: Cardinal; +begin + LPerIoData := _NewIoData; + LPerIoData.Buffer.DataBuf.buf := ABuf; + LPerIoData.Buffer.DataBuf.len := ALen; + LPerIoData.Action := ioWrite; + LPerIoData.Socket := AConnection.Socket; + LPerIoData.CrossData := AConnection; + LPerIoData.Callback := ACallback; + + LFlags := 0; + LBytes := 0; + // WSASend 不会出现部分发送的情况, 要么全部失败, 要么全部成功 + // 所以不需要像 kqueue 或 epoll 中调用 send 那样调用完之后还得检查实际发送了多少 + // 唯一需要注意的是: WSASend 会将待发送的数据锁定到非页面内存, 非页面内存资源 + // 是非常紧张的, 所以不要无节制的调用 WSASend, 最好通过回调发送完一批数据再继 + // 续发送下一批 + if (WSASend(AConnection.Socket, @LPerIoData.Buffer.DataBuf, 1, LBytes, LFlags, PWSAOverlapped(LPerIoData), nil) < 0) + and (WSAGetLastError <> WSA_IO_PENDING) then + begin + // 出错多半是 WSAENOBUFS, 也就是投递的 WSASend 过多, 来不及发送 + // 导致非页面内存资源全部被锁定, 要避免这种情况必须上层发送逻辑 + // 保证不能无节制的调用Send发送大量数据, 最好发送完一个再继续下 + // 一个, 本函数提供了发送结果的回调函数, 在回调函数报告发送成功 + // 之后就可以继续下一块数据发送了 + _FreeIoData(LPerIoData); + + if Assigned(ACallback) then + ACallback(AConnection, False); + + AConnection.Close; + end; +end; + +function TIocpCrossSocket.ProcessIoEvent: Boolean; +var + LBytes: Cardinal; + LSocket: THandle; + LPerIoData: PPerIoData; + LConnection: ICrossConnection; + {$IFDEF DEBUG} + LErrNo: Cardinal; + {$ENDIF} +begin + if not GetQueuedCompletionStatus(FIocpHandle, LBytes, ULONG_PTR(LSocket), POverlapped(LPerIoData), INFINITE) then + begin + // 出错了, 并且完成数据也都是空的, + // 这种情况即便重试, 应该也会继续出错, 最好立即终止IO线程 + if (LPerIoData = nil) then + begin + {$IFDEF DEBUG} + LErrNo := GetLastError; + // 完成端口被关闭时可能会触发 ERROR_INVALID_HANDLE 和 ERROR_ABANDONED_WAIT_0 + if (LErrNo <> ERROR_INVALID_HANDLE) + and (LErrNo <> ERROR_ABANDONED_WAIT_0) + then + _LogLastOsError('TIocpCrossSocket.ProcessIoEvent.GetQueuedCompletionStatus'); + {$ENDIF} + Exit(False); + end; + + try + // WSA_OPERATION_ABORTED, 995, 由于线程退出或应用程序请求,已中止 I/O 操作。 + // WSAENOTSOCK, 10038, 在一个非套接字上尝试了一个操作。 + // ERROR_NETNAME_DELETED, 64, 指定的网络名不再可用 + // ERROR_CONNECTION_REFUSED, 1225, 远程计算机拒绝网络连接。 + if (LPerIoData.CrossData <> nil) then + begin + // AcceptEx虽然成功, 但是Socket句柄耗尽了, 再次投递AcceptEx + if (LPerIoData.Action = ioAccept) then + begin + // 关闭监听后会触发该错误, 这种情况不应该继续投递 + if (GetLastError <> WSA_OPERATION_ABORTED) then + _NewAccept(LPerIoData.CrossData as ICrossListen); + end else + begin + if Assigned(LPerIoData.Callback) then + begin + if (LPerIoData.CrossData is TIocpConnection) then + LConnection := LPerIoData.CrossData as ICrossConnection + else + LConnection := nil; + + LPerIoData.Callback(LConnection, False); + end; + + LPerIoData.CrossData.Close; + end; + end else + begin + if Assigned(LPerIoData.Callback) then + LPerIoData.Callback(nil, False); + + TSocketAPI.CloseSocket(LPerIoData.Socket); + end; + finally + _FreeIoData(LPerIoData); + end; + + // 出错了, 但是完成数据不是空的, 需要重试 + Exit(True); + end; + + // 主动调用了 StopLoop + if (LBytes = 0) and (ULONG_PTR(LPerIoData) = SHUTDOWN_FLAG) then Exit(False); + + // 由于未知原因未获取到完成数据, 但是返回的错误代码又是正常 + // 这种情况需要进行重试(返回True之后IO线程会再次调用ProcessIoEvent) + if (LPerIoData = nil) then Exit(True); + + try + case LPerIoData.Action of + ioAccept : _HandleAccept(LPerIoData); + ioConnect : _HandleConnect(LPerIoData); + ioRead : _HandleRead(LPerIoData); + ioWrite : _HandleWrite(LPerIoData); + end; + finally + _FreeIoData(LPerIoData); + end; + + Result := True; +end; + +end. diff --git a/ThirdParty/DCS/Net/Net.CrossSocket.Kqueue.pas b/ThirdParty/DCS/Net/Net.CrossSocket.Kqueue.pas new file mode 100644 index 00000000..447e5a08 --- /dev/null +++ b/ThirdParty/DCS/Net/Net.CrossSocket.Kqueue.pas @@ -0,0 +1,1030 @@ +{******************************************************************************} +{ } +{ Delphi cross platform socket library } +{ } +{ Copyright (c) 2017 WiNDDRiVER(soulawing@gmail.com) } +{ } +{ Homepage: https://github.com/winddriver/Delphi-Cross-Socket } +{ } +{******************************************************************************} +unit Net.CrossSocket.Kqueue; + +interface + +uses + System.SysUtils, + System.Classes, + System.Generics.Collections, + Posix.SysSocket, + Posix.NetinetIn, + Posix.UniStd, + Posix.NetDB, + Posix.Pthread, + Posix.Errno, + BSD.kqueue, + Net.SocketAPI, + Net.CrossSocket.Base; + +type + TIoEvent = (ieRead, ieWrite); + TIoEvents = set of TIoEvent; + + TKqueueListen = class(TAbstractCrossListen) + private + FLock: TObject; + FIoEvents: TIoEvents; + + procedure _Lock; inline; + procedure _Unlock; inline; + + function _ReadEnabled: Boolean; inline; + function _UpdateIoEvent(const AIoEvents: TIoEvents): Boolean; + public + constructor Create(AOwner: ICrossSocket; AListenSocket: THandle; + AFamily, ASockType, AProtocol: Integer); override; + destructor Destroy; override; + end; + + PSendItem = ^TSendItem; + TSendItem = record + Data: PByte; + Size: Integer; + Callback: TProc; + end; + + TSendQueue = class(TList) + protected + procedure Notify(const Value: PSendItem; Action: TCollectionNotification); override; + end; + + TKqueueConnection = class(TAbstractCrossConnection) + private + FLock: TObject; + FSendQueue: TSendQueue; + FIoEvents: TIoEvents; + FConnectCallback: TProc; // 用于 Connect 回调 + + procedure _Lock; inline; + procedure _Unlock; inline; + + function _ReadEnabled: Boolean; inline; + function _WriteEnabled: Boolean; inline; + function _UpdateIoEvent(const AIoEvents: TIoEvents): Boolean; + public + constructor Create(AOwner: ICrossSocket; AClientSocket: THandle; + AConnectType: TConnectType); override; + destructor Destroy; override; + + procedure Close; override; + end; + + // KQUEUE 与 EPOLL 队列的差异 + // KQUEUE的队列中, 一个Socket句柄可以有多条记录, 每个事件一条, + // 这一点和 EPOLL 不一样, EPOLL中每个Socket句柄只会有一条记录 + // 要监测多个事件时, 只需要将多个事件做位运算加在一起调用 epoll_ctl 即可 + // + // EV_DISPATCH 和 EV_CLEAR 是令 kqueue 支持线程池的关键 + // 该参数组合可以令事件触发后就立即被禁用, 避免让同一个Socket的同一个事件 + // 同时被多个工作线程触发 + // + // EVFILT_READ + // 用于监测接收缓冲区是否可读了 + // 对于监听Socket来说,表示有新的连接到来 + // 对于已连接的Socket来说,表示有数据到达接收缓冲区 + // 为了支持线程池, 必须带上参数 EV_CLEAR or EV_DISPATCH + // 该参数组合表示, 该事件一旦触发立即清除该事件的状态并禁用它 + // 处理完连接或者读取数据之后再投递一次 EVFILT_READ, 带上参数 + // EV_ENABLE or EV_CLEAR or EV_DISPATCH, 让事件继续监测 + // + // EVFILT_WRITE + // 用于监测发送缓冲区是否可写了 + // 对于Connect中的Socket,投递EV_ENABLE,等到事件触发时表示连接已建立 + // 对于已连接的Socket,在Send之后立即投递EVFILT_WRITE,等到事件触发时表示发送完成 + // 对于EVFILT_WRITE都应该带上EV_ONESHOT参数,让该事件只会被触发一次 + // 否则,只要发送缓冲区是空的,该事件就会一直触发,这并没有什么意义 + // 我们只需要用EVFILT_WRITE去监测连接或者发送是否成功 + // + // KQUEUE 发送数据 + // 最好的做法是将实际发送数据的动作放到 EVFILT_WRITE 触发时进行, 该 + // 事件触发表明 Socket 发送缓存有空闲空间了。IOCP可以直接将待发送的数据及 + // 回调同时扔给 WSASend, 发送完成后去调用回调即可; KQUEUE 机制不一样, 在 KQUEUE + // 中没有类似 WSASend 的函数, 只能自行维护发送数据及回调的队列 + // EPOLL要支持多线程并发发送数据必须创建发送队列, 否则同一个 Socket 的并发发送 + // 极有可能有一部分会被其它发送覆盖掉 + // + // 由于 KQUEUE 中每个套接字在队列中的 EV_WRITE 和 EV_READ 是分开的两条记录 + // 所以修改套接字的监听事件时不会互相覆盖, 也就是说每个事件都会对应到一次 + // 触发, 这样就可以方便的使用接口的引用计数机制保持连接的有效性, 也不会出现 + // 内存泄漏 + TKqueueCrossSocket = class(TAbstractCrossSocket) + private const + MAX_EVENT_COUNT = 2048; + SHUTDOWN_FLAG = Pointer(-1); + private class threadvar + FEventList: array [0..MAX_EVENT_COUNT-1] of TKEvent; + private + FKqueueHandle: THandle; + FIoThreads: TArray; + FIdleHandle: THandle; + FIdleLock: TObject; + FStopHandle: TPipeDescriptors; + + // 利用 pipe 唤醒并退出IO线程 + procedure _OpenStopHandle; inline; + procedure _PostStopCommand; inline; + procedure _CloseStopHandle; inline; + + procedure _OpenIdleHandle; inline; + procedure _CloseIdleHandle; inline; + + // 在向一个已经关闭的套接字发送数据时系统会直接抛出EPIPE异常导致程序非正常退出 + // LINUX下可以在send时带上MSG_NOSIGNAL参数就能避免这种情况的发生 + // OSX中可以通过设置套接字的SO_NOSIGPIPE参数达到同样的目的 + procedure _SetNoSigPipe(ASocket: THandle); inline; + + procedure _HandleAccept(AListen: ICrossListen); + procedure _HandleConnect(AConnection: ICrossConnection); + procedure _HandleRead(AConnection: ICrossConnection); + procedure _HandleWrite(AConnection: ICrossConnection); + protected + function CreateConnection(AOwner: ICrossSocket; AClientSocket: THandle; + AConnectType: TConnectType): ICrossConnection; override; + function CreateListen(AOwner: ICrossSocket; AListenSocket: THandle; + AFamily, ASockType, AProtocol: Integer): ICrossListen; override; + + procedure StartLoop; override; + procedure StopLoop; override; + + procedure Listen(const AHost: string; APort: Word; + const ACallback: TProc = nil); override; + + procedure Connect(const AHost: string; APort: Word; + const ACallback: TProc = nil); override; + + procedure Send(AConnection: ICrossConnection; ABuf: Pointer; ALen: Integer; + const ACallback: TProc = nil); override; + + function ProcessIoEvent: Boolean; override; + public + constructor Create(AIoThreads: Integer); override; + destructor Destroy; override; + end; + +implementation + +{$I Net.Posix.inc} + +{ TKqueueListen } + +constructor TKqueueListen.Create(AOwner: ICrossSocket; AListenSocket: THandle; + AFamily, ASockType, AProtocol: Integer); +begin + inherited; + + FLock := TObject.Create; +end; + +destructor TKqueueListen.Destroy; +begin + FreeAndNil(FLock); + + inherited; +end; + +procedure TKqueueListen._Lock; +begin + System.TMonitor.Enter(FLock); +end; + +function TKqueueListen._ReadEnabled: Boolean; +begin + Result := (ieRead in FIoEvents); +end; + +procedure TKqueueListen._Unlock; +begin + System.TMonitor.Exit(FLock); +end; + +function TKqueueListen._UpdateIoEvent(const AIoEvents: TIoEvents): Boolean; +var + LOwner: TKqueueCrossSocket; + LCrossData: Pointer; + LEvents: array [0..1] of TKEvent; + N: Integer; +begin + FIoEvents := AIoEvents; + + if (FIoEvents = []) or IsClosed then Exit(False); + + LOwner := TKqueueCrossSocket(Owner); + LCrossData := Pointer(Self); + N := 0; + + if _ReadEnabled then + begin + EV_SET(@LEvents[N], Socket, EVFILT_READ, + EV_ADD or EV_ONESHOT or EV_CLEAR or EV_DISPATCH, 0, 0, Pointer(LCrossData)); + + Inc(N); + end; + + if (N <= 0) then Exit(False); + + Result := (kevent(LOwner.FKqueueHandle, @LEvents, N, nil, 0, nil) >= 0); + + {$IFDEF DEBUG} + if not Result then + _Log('listen %d kevent error %d', [Socket, GetLastError]); + {$ENDIF} +end; + +{ TSendQueue } + +procedure TSendQueue.Notify(const Value: PSendItem; + Action: TCollectionNotification); +begin + inherited; + + if (Action = TCollectionNotification.cnRemoved) then + begin + if (Value <> nil) then + begin + Value.Callback := nil; + FreeMem(Value, SizeOf(TSendItem)); + end; + end; +end; + +{ TKqueueConnection } + +constructor TKqueueConnection.Create(AOwner: ICrossSocket; + AClientSocket: THandle; AConnectType: TConnectType); +begin + inherited; + + FSendQueue := TSendQueue.Create; + FLock := TObject.Create; +end; + +destructor TKqueueConnection.Destroy; +var + LConnection: ICrossConnection; + LSendItem: PSendItem; +begin + LConnection := Self; + + _Lock; + try + // 连接释放时, 调用连接回调, 告知连接失败 + // 连接成功后 FConnectCallback 会被置为 nil, + // 所以如果这里 FConnectCallback 不等于 nil, 则表示连接释放时仍未连接成功 + if Assigned(FConnectCallback) then + begin + FConnectCallback(LConnection, False); + FConnectCallback := nil; + end; + + // 连接释放时, 调用所有发送队列的回调, 告知发送失败 + if (FSendQueue.Count > 0) then + begin + for LSendItem in FSendQueue do + if Assigned(LSendItem.Callback) then + LSendItem.Callback(LConnection, False); + + FSendQueue.Clear; + end; + + FreeAndNil(FSendQueue); + finally + _Unlock; + end; + + FreeAndNil(FLock); + + inherited; +end; + +procedure TKqueueConnection.Close; +begin + if (_SetConnectStatus(csClosed) = csClosed) then Exit; + + // shutdown可以触发套接字在kqueue中的事件 + // 而直接close会将套接字从kqueue队列中移除, 不会触发任何事件 + // 这就会导致连接对象在放入kqueue队列时增加的引用计数无法回收, 导致内存泄漏 + // 使用shutdown触发事件后再释放连接可以确保不会产生内存泄露 + TSocketAPI.Shutdown(Socket, 2); +end; + +procedure TKqueueConnection._Lock; +begin + System.TMonitor.Enter(FLock); +end; + +function TKqueueConnection._ReadEnabled: Boolean; +begin + Result := (ieRead in FIoEvents); +end; + +procedure TKqueueConnection._Unlock; +begin + System.TMonitor.Exit(FLock); +end; + +function TKqueueConnection._UpdateIoEvent(const AIoEvents: TIoEvents): Boolean; +var + LOwner: TKqueueCrossSocket; + LCrossData: Pointer; + LEvents: array [0..1] of TKEvent; + N: Integer; +begin + FIoEvents := AIoEvents; + + if (FIoEvents = []) or IsClosed then Exit(False); + + LOwner := TKqueueCrossSocket(Owner); + LCrossData := Pointer(Self); + N := 0; + + // kqueue中同一个套接字的EVFILT_READ和EVFILT_WRITE事件在队列中会有两条记录 + // 并且可能会在不同的线程中同时被触发, 如果其中一个线程关闭了连接, 在没有 + // 引用计数保护的情况下, 就会导致连接对象被释放, 另一个线程再访问连接对象 + // 就会引起异常, 这里为了保证连接对象的有效性, 在添加事件时手动增加连接对象 + // 的引用计数, 到事件触发时再减少引用计数 + // 注意关闭连接一定要使用shutdown而不能直接close, 否则无法触发kqueue事件, + // 导致引用计数无法回收 + + if _ReadEnabled then + begin + Self._AddRef; + + EV_SET(@LEvents[N], Socket, EVFILT_READ, + EV_ADD or EV_ONESHOT or EV_CLEAR or EV_DISPATCH, 0, 0, Pointer(LCrossData)); + + Inc(N); + end; + + if _WriteEnabled then + begin + Self._AddRef; + + EV_SET(@LEvents[N], Socket, EVFILT_WRITE, + EV_ADD or EV_ONESHOT or EV_CLEAR or EV_DISPATCH, 0, 0, Pointer(LCrossData)); + + Inc(N); + end; + + if (N <= 0) then Exit(False); + + Result := (kevent(LOwner.FKqueueHandle, @LEvents, N, nil, 0, nil) >= 0); + + if not Result then + begin + {$IFDEF DEBUG} + _Log('connection %d kevent error %d', [Socket, GetLastError]); + {$ENDIF} + + while (N > 0) do + begin + Self._Release; + Dec(N); + end; + end; +end; + +function TKqueueConnection._WriteEnabled: Boolean; +begin + Result := (ieWrite in FIoEvents); +end; + +{ TKqueueCrossSocket } + +constructor TKqueueCrossSocket.Create(AIoThreads: Integer); +begin + inherited; + + FIdleLock := TObject.Create; +end; + +destructor TKqueueCrossSocket.Destroy; +begin + FreeAndNil(FIdleLock); + + inherited; +end; + +procedure TKqueueCrossSocket._CloseIdleHandle; +begin + FileClose(FIdleHandle); +end; + +procedure TKqueueCrossSocket._CloseStopHandle; +begin + FileClose(FStopHandle.ReadDes); + FileClose(FStopHandle.WriteDes); +end; + +procedure TKqueueCrossSocket._HandleAccept(AListen: ICrossListen); +var + LListen: ICrossListen; + LKqListen: TKqueueListen; + LConnection: ICrossConnection; + LKqConnection: TKqueueConnection; + LSocket, LError: Integer; + LListenSocket, LClientSocket: THandle; + LSuccess: Boolean; +begin + LListen := AListen; + LListenSocket := LListen.Socket; + + while True do + begin + LSocket := TSocketAPI.Accept(LListenSocket, nil, nil); + + // Accept失败 + // EAGAIN 所有就绪的连接都已处理完毕 + // EMFILE 进程的文件句柄已经用完了 + if (LSocket < 0) then + begin + LError := GetLastError; + + // 当句柄用完了的时候, 释放事先占用的临时句柄 + // 然后再次 accept, 然后将 accept 的句柄关掉 + // 这样可以保证在文件句柄耗尽的时候依然能响应连接请求 + // 并立即将新到的连接关闭 + if (LError = EMFILE) then + begin + System.TMonitor.Enter(FIdleLock); + try + _CloseIdleHandle; + LSocket := TSocketAPI.Accept(LListenSocket, nil, nil); + TSocketAPI.CloseSocket(LSocket); + _OpenIdleHandle; + finally + System.TMonitor.Exit(FIdleLock); + end; + end; + + Break; + end; + + LClientSocket := LSocket; + TSocketAPI.SetNonBlock(LClientSocket, True); + SetKeepAlive(LClientSocket); + _SetNoSigPipe(LClientSocket); + + LConnection := CreateConnection(Self, LClientSocket, ctAccept); + TriggerConnecting(LConnection); + TriggerConnected(LConnection); + + // 连接建立后监视Socket的读事件 + LKqConnection := LConnection as TKqueueConnection; + LKqConnection._Lock; + try + LSuccess := LKqConnection._UpdateIoEvent([ieRead]); + finally + LKqConnection._Unlock; + end; + + if not LSuccess then + TriggerDisconnected(LConnection); + end; + + // 继续接收新连接 + LKqListen := LListen as TKqueueListen; + LKqListen._Lock; + LKqListen._UpdateIoEvent([ieRead]); + LKqListen._Unlock; +end; + +procedure TKqueueCrossSocket._HandleConnect(AConnection: ICrossConnection); +var + LConnection: ICrossConnection; + LKqConnection: TKqueueConnection; + LConnectCallback: TProc; + LSuccess: Boolean; +begin + LConnection := AConnection; + + // Connect失败 + if (TSocketAPI.GetError(LConnection.Socket) <> 0) then + begin + {$IFDEF DEBUG} + _LogLastOsError; + {$ENDIF} + TriggerDisconnected(LConnection); + Exit; + end; + + LKqConnection := LConnection as TKqueueConnection; + + LKqConnection._Lock; + try + LConnectCallback := LKqConnection.FConnectCallback; + LKqConnection.FConnectCallback := nil; + LSuccess := LKqConnection._UpdateIoEvent([ieRead]); + finally + LKqConnection._Unlock; + end; + + if LSuccess then + TriggerConnected(LConnection); + + if Assigned(LConnectCallback) then + LConnectCallback(LConnection, LSuccess); + + if not LSuccess then + TriggerDisconnected(LConnection); +end; + +procedure TKqueueCrossSocket._HandleRead(AConnection: ICrossConnection); +var + LConnection: ICrossConnection; + LRcvd, LError: Integer; + LKqConnection: TKqueueConnection; + LSuccess: Boolean; +begin + LConnection := AConnection; + + while True do + begin + LRcvd := TSocketAPI.Recv(LConnection.Socket, FRecvBuf[0], RCV_BUF_SIZE); + + // 对方主动断开连接 + if (LRcvd = 0) then + begin +// _Log('%d close on read 0, ref %d', [LConnection.Socket, TInterfacedObject(LConnection).RefCount]); + TriggerDisconnected(LConnection); + Exit; + end; + + if (LRcvd < 0) then + begin + LError := GetLastError; + + // 被系统信号中断, 可以重新recv + if (LError = EINTR) then + Continue + // 接收缓冲区中数据已经被取完了 + else if (LError = EAGAIN) or (LError = EWOULDBLOCK) then + Break + else + // 接收出错 + begin +// _Log('%d close on read error %d, ref %d', [LConnection.Socket, GetLastError, TInterfacedObject(LConnection).RefCount]); + TriggerDisconnected(LConnection); + Exit; + end; + end; + + TriggerReceived(LConnection, @FRecvBuf[0], LRcvd); + + if (LRcvd < RCV_BUF_SIZE) then Break; + end; + + LKqConnection := LConnection as TKqueueConnection; + LKqConnection._Lock; + try + LSuccess := LKqConnection._UpdateIoEvent([ieRead]); + finally + LKqConnection._Unlock; + end; + + if not LSuccess then + TriggerDisconnected(LConnection); +end; + +procedure TKqueueCrossSocket._HandleWrite(AConnection: ICrossConnection); +var + LConnection: ICrossConnection; + LKqConnection: TKqueueConnection; + LSendItem: PSendItem; + LCallback: TProc; + LSent: Integer; +begin + LConnection := AConnection; + LKqConnection := LConnection as TKqueueConnection; + + LKqConnection._Lock; + try + // 队列中没有数据了, 清除 ioWrite 标志 + if (LKqConnection.FSendQueue.Count <= 0) then + begin + LKqConnection._UpdateIoEvent([]); + Exit; + end; + + // 获取Socket发送队列中的第一条数据 + LSendItem := LKqConnection.FSendQueue.Items[0]; + + // 发送数据 + LSent := PosixSend(LConnection.Socket, LSendItem.Data, LSendItem.Size); + + {$region '全部发送完成'} + if (LSent >= LSendItem.Size) then + begin + // 先保存回调函数, 避免后面删除队列后将其释放 + LCallback := LSendItem.Callback; + + // 发送成功, 移除已发送成功的数据 + if (LKqConnection.FSendQueue.Count > 0) then + LKqConnection.FSendQueue.Delete(0); + + // 队列中没有数据了, 清除 ioWrite 标志 + if (LKqConnection.FSendQueue.Count <= 0) then + LKqConnection._UpdateIoEvent([]); + + if Assigned(LCallback) then + LCallback(LConnection, True); + + Exit; + end; + {$endregion} + + {$region '连接断开或发送错误'} + // 发送失败的回调会在连接对象的destroy方法中被调用 + if (LSent < 0) then Exit; + {$endregion} + + {$region '部分发送成功,在下一次唤醒发送线程时继续处理剩余部分'} + Dec(LSendItem.Size, LSent); + Inc(LSendItem.Data, LSent); + {$endregion} + + LKqConnection._UpdateIoEvent([ieWrite]); + finally + LKqConnection._Unlock; + end; +end; + + +procedure TKqueueCrossSocket._OpenIdleHandle; +begin + FIdleHandle := FileOpen('/dev/null', fmOpenRead); +end; + +procedure TKqueueCrossSocket._OpenStopHandle; +var + LEvent: TKEvent; +begin + pipe(FStopHandle); + + // 这里不使用 EV_ONESHOT + // 这样可以保证通知退出的命令发出后, 所有IO线程都会收到 + EV_SET(@LEvent, FStopHandle.ReadDes, EVFILT_READ, + EV_ADD, 0, 0, SHUTDOWN_FLAG); + kevent(FKqueueHandle, @LEvent, 1, nil, 0, nil); +end; + +procedure TKqueueCrossSocket._PostStopCommand; +var + LStuff: UInt64; +begin + LStuff := 1; + // 往 FStopHandle.WriteDes 写入任意数据, 唤醒工作线程 + Posix.UniStd.__write(FStopHandle.WriteDes, @LStuff, SizeOf(LStuff)); +end; + +procedure TKqueueCrossSocket._SetNoSigPipe(ASocket: THandle); +begin + TSocketAPI.SetSockOpt(ASocket, SOL_SOCKET, SO_NOSIGPIPE, 1); +end; + +procedure TKqueueCrossSocket.StartLoop; +var + I: Integer; + LCrossSocket: ICrossSocket; +begin + if (FIoThreads <> nil) then Exit; + + _OpenIdleHandle; + + FKqueueHandle := kqueue(); + LCrossSocket := Self; + SetLength(FIoThreads, GetIoThreads); + for I := 0 to Length(FIoThreads) - 1 do + FIoThreads[i] := TIoEventThread.Create(LCrossSocket); + + _OpenStopHandle; +end; + +procedure TKqueueCrossSocket.StopLoop; +var + I: Integer; + LCurrentThreadID: TThreadID; +begin + if (FIoThreads = nil) then Exit; + + CloseAll; + + while (ListensCount > 0) or (ConnectionsCount > 0) do Sleep(1); + + _PostStopCommand; + + LCurrentThreadID := GetCurrentThreadId; + for I := 0 to Length(FIoThreads) - 1 do + begin + if (FIoThreads[I].ThreadID = LCurrentThreadID) then + raise ECrossSocket.Create('不能在IO线程中执行StopLoop!'); + + FIoThreads[I].WaitFor; + FreeAndNil(FIoThreads[I]); + end; + FIoThreads := nil; + + FileClose(FKqueueHandle); + _CloseIdleHandle; + _CloseStopHandle; +end; + +procedure TKqueueCrossSocket.Connect(const AHost: string; APort: Word; + const ACallback: TProc); + + procedure _Failed1; + begin + if Assigned(ACallback) then + ACallback(nil, False); + end; + + function _Connect(ASocket: THandle; AAddr: PRawAddrInfo): Boolean; + var + LConnection: ICrossConnection; + LKqConnection: TKqueueConnection; + begin + if (TSocketAPI.Connect(ASocket, AAddr.ai_addr, AAddr.ai_addrlen) = 0) + or (GetLastError = EINPROGRESS) then + begin + LConnection := CreateConnection(Self, ASocket, ctConnect); + TriggerConnecting(LConnection); + LKqConnection := LConnection as TKqueueConnection; + + LKqConnection._Lock; + try + LKqConnection.ConnectStatus := csConnecting; + LKqConnection.FConnectCallback := ACallback; + if not LKqConnection._UpdateIoEvent([ieWrite]) then + begin + TriggerDisconnected(LConnection); + if Assigned(ACallback) then + ACallback(LConnection, False); + Exit(False); + end; + finally + LKqConnection._Unlock; + end; + end else + begin + if Assigned(ACallback) then + ACallback(nil, False); + TSocketAPI.CloseSocket(ASocket); + Exit(False); + end; + + Result := True; + end; + +var + LHints: TRawAddrInfo; + P, LAddrInfo: PRawAddrInfo; + LSocket: THandle; +begin + FillChar(LHints, SizeOf(TRawAddrInfo), 0); + LHints.ai_family := AF_UNSPEC; + LHints.ai_socktype := SOCK_STREAM; + LHints.ai_protocol := IPPROTO_TCP; + LAddrInfo := TSocketAPI.GetAddrInfo(AHost, APort, LHints); + if (LAddrInfo = nil) then + begin + _Failed1; + Exit; + end; + + P := LAddrInfo; + try + while (LAddrInfo <> nil) do + begin + LSocket := TSocketAPI.NewSocket(LAddrInfo.ai_family, LAddrInfo.ai_socktype, + LAddrInfo.ai_protocol); + if (LSocket = INVALID_HANDLE_VALUE) then + begin + _Failed1; + Exit; + end; + + TSocketAPI.SetNonBlock(LSocket, True); + SetKeepAlive(LSocket); + _SetNoSigPipe(LSocket); + + if _Connect(LSocket, LAddrInfo) then Exit; + + LAddrInfo := PRawAddrInfo(LAddrInfo.ai_next); + end; + finally + TSocketAPI.FreeAddrInfo(P); + end; + + _Failed1; +end; + +function TKqueueCrossSocket.CreateConnection(AOwner: ICrossSocket; + AClientSocket: THandle; AConnectType: TConnectType): ICrossConnection; +begin + Result := TKqueueConnection.Create(AOwner, AClientSocket, AConnectType); +end; + +function TKqueueCrossSocket.CreateListen(AOwner: ICrossSocket; + AListenSocket: THandle; AFamily, ASockType, AProtocol: Integer): ICrossListen; +begin + Result := TKqueueListen.Create(AOwner, AListenSocket, AFamily, ASockType, AProtocol); +end; + +procedure TKqueueCrossSocket.Listen(const AHost: string; APort: Word; + const ACallback: TProc); +var + LHints: TRawAddrInfo; + P, LAddrInfo: PRawAddrInfo; + LListenSocket: THandle; + LListen: ICrossListen; + LKqListen: TKqueueListen; + LSuccess: Boolean; + + procedure _Failed; + begin + if Assigned(ACallback) then + ACallback(nil, False); + end; + +begin + FillChar(LHints, SizeOf(TRawAddrInfo), 0); + + LHints.ai_flags := AI_PASSIVE; + LHints.ai_family := AF_UNSPEC; + LHints.ai_socktype := SOCK_STREAM; + LHints.ai_protocol := IPPROTO_TCP; + LAddrInfo := TSocketAPI.GetAddrInfo(AHost, APort, LHints); + if (LAddrInfo = nil) then + begin + _Failed; + Exit; + end; + + P := LAddrInfo; + try + while (LAddrInfo <> nil) do + begin + LListenSocket := TSocketAPI.NewSocket(LAddrInfo.ai_family, LAddrInfo.ai_socktype, + LAddrInfo.ai_protocol); + if (LListenSocket = INVALID_HANDLE_VALUE) then + begin + _Failed; + Exit; + end; + + TSocketAPI.SetNonBlock(LListenSocket, True); + TSocketAPI.SetReUseAddr(LListenSocket, True); + + if (LAddrInfo.ai_family = AF_INET6) then + TSocketAPI.SetSockOpt(LListenSocket, IPPROTO_IPV6, IPV6_V6ONLY, 1); + + if (TSocketAPI.Bind(LListenSocket, LAddrInfo.ai_addr, LAddrInfo.ai_addrlen) < 0) + or (TSocketAPI.Listen(LListenSocket) < 0) then + begin + _Failed; + Exit; + end; + + LListen := CreateListen(Self, LListenSocket, LAddrInfo.ai_family, + LAddrInfo.ai_socktype, LAddrInfo.ai_protocol); + LKqListen := LListen as TKqueueListen; + + // 监听套接字的读事件 + // 读事件到达表明有新连接 + LKqListen._Lock; + try + LSuccess := LKqListen._UpdateIoEvent([ieRead]); + finally + LKqListen._Unlock; + end; + + if not LSuccess then + begin + _Failed; + + Exit; + end; + + // 监听成功 + TriggerListened(LListen); + if Assigned(ACallback) then + ACallback(LListen, True); + + // 如果端口传入0,让所有地址统一用首个分配到的端口 + if (APort = 0) and (LAddrInfo.ai_next <> nil) then + Psockaddr_in(LAddrInfo.ai_next.ai_addr).sin_port := LListen.LocalPort; + + LAddrInfo := PRawAddrInfo(LAddrInfo.ai_next); + end; + finally + TSocketAPI.FreeAddrInfo(P); + end; +end; + +procedure TKqueueCrossSocket.Send(AConnection: ICrossConnection; ABuf: Pointer; + ALen: Integer; const ACallback: TProc); +var + LKqConnection: TKqueueConnection; + LSendItem: PSendItem; +begin + // 测试过先发送, 然后将剩余部分放入发送队列的做法 + // 发现会引起内存访问异常, 放到队列里到IO线程中发送则不会有问题 + {$region '放入发送队列'} + GetMem(LSendItem, SizeOf(TSendItem)); + FillChar(LSendItem^, SizeOf(TSendItem), 0); + LSendItem.Data := ABuf; + LSendItem.Size := ALen; + LSendItem.Callback := ACallback; + + LKqConnection := AConnection as TKqueueConnection; + + LKqConnection._Lock; + try + // 将数据放入队列 + LKqConnection.FSendQueue.Add(LSendItem); + + // 由于 kqueue 队列中每个套接字的读写事件是分开的两条记录 + // 所以发送只需要添加写事件即可, 不用管读事件, 否则反而会引起引用计数异常 + if not LKqConnection._WriteEnabled then + LKqConnection._UpdateIoEvent([ieWrite]); + finally + LKqConnection._Unlock; + end; + {$endregion} +end; + +function TKqueueCrossSocket.ProcessIoEvent: Boolean; +var + LRet, I: Integer; + LEvent: TKEvent; + LCrossData: TCrossData; + LListen: ICrossListen; + LConnection: ICrossConnection; +begin + LRet := kevent(FKqueueHandle, nil, 0, @FEventList[0], MAX_EVENT_COUNT, nil); + if (LRet < 0) then + begin + LRet := GetLastError; + // EINTR, kevent 调用被系统信号打断, 可以进行重试 + Exit(LRet = EINTR); + end; + + for I := 0 to LRet - 1 do + begin + LEvent := FEventList[I]; + + // 收到退出命令 + if (LEvent.uData = SHUTDOWN_FLAG) then Exit(False); + + if (LEvent.uData = nil) then Continue; + + {$region '获取连接或监听对象'} + LCrossData := TCrossData(LEvent.uData); + + if (LCrossData is TKqueueListen) then + LListen := LCrossData as ICrossListen + else + LListen := nil; + + if (LCrossData is TKqueueConnection) then + LConnection := LCrossData as ICrossConnection + else + LConnection := nil; + {$endregion} + + {$region 'IO事件处理'} + if (LListen <> nil) then + begin + if (LEvent.Filter = EVFILT_READ) then + _HandleAccept(LListen); + end else + if (LConnection <> nil) then + begin + LConnection._Release; + + // kqueue的读写事件同一时间只可能触发一个 + if (LEvent.Filter = EVFILT_READ) then + _HandleRead(LConnection) + else if (LEvent.Filter = EVFILT_WRITE) then + begin + if (LConnection.ConnectStatus = csConnecting) then + _HandleConnect(LConnection) + else + _HandleWrite(LConnection); + end; + end; + {$endregion} + end; + + Result := True; +end; + +end. diff --git a/ThirdParty/DCS/Net/Net.CrossSocket.pas b/ThirdParty/DCS/Net/Net.CrossSocket.pas new file mode 100644 index 00000000..5f55d0fe --- /dev/null +++ b/ThirdParty/DCS/Net/Net.CrossSocket.pas @@ -0,0 +1,55 @@ +{******************************************************************************} +{ } +{ Delphi cross platform socket library } +{ } +{ Copyright (c) 2017 WiNDDRiVER(soulawing@gmail.com) } +{ } +{ Homepage: https://github.com/winddriver/Delphi-Cross-Socket } +{ } +{******************************************************************************} +unit Net.CrossSocket; + +interface + +uses + Net.CrossSocket.Base, + {$IFDEF MSWINDOWS} + Net.CrossSocket.Iocp + {$ELSEIF defined(MACOS) or defined(IOS)} + Net.CrossSocket.Kqueue + {$ELSEIF defined(LINUX) or defined(ANDROID)} + Net.CrossSocket.Epoll + {$ENDIF}; + +type + TCrossListen = + {$IFDEF MSWINDOWS} + TIocpListen + {$ELSEIF defined(MACOS) or defined(IOS)} + TKqueueListen + {$ELSEIF defined(LINUX) or defined(ANDROID)} + TEpollListen + {$ENDIF}; + + TCrossConnection = + {$IFDEF MSWINDOWS} + TIocpConnection + {$ELSEIF defined(MACOS) or defined(IOS)} + TKqueueConnection + {$ELSEIF defined(LINUX) or defined(ANDROID)} + TEpollConnection + {$ENDIF}; + + TCrossSocket = + {$IFDEF MSWINDOWS} + TIocpCrossSocket + {$ELSEIF defined(MACOS) or defined(IOS)} + TKqueueCrossSocket + {$ELSEIF defined(LINUX) or defined(ANDROID)} + TEpollCrossSocket + {$ENDIF}; + +implementation + +end. + diff --git a/ThirdParty/DCS/Net/Net.CrossSslDemoCert.pas b/ThirdParty/DCS/Net/Net.CrossSslDemoCert.pas new file mode 100644 index 00000000..1abba68a --- /dev/null +++ b/ThirdParty/DCS/Net/Net.CrossSslDemoCert.pas @@ -0,0 +1,69 @@ +{******************************************************************************} +{ } +{ Delphi cross platform socket library } +{ } +{ Copyright (c) 2017 WiNDDRiVER(soulawing@gmail.com) } +{ } +{ Homepage: https://github.com/winddriver/Delphi-Cross-Socket } +{ } +{******************************************************************************} +unit Net.CrossSslDemoCert; + +interface + +const + SSL_SERVER_CERT: string = + '-----BEGIN CERTIFICATE-----' + sLineBreak + + 'MIIDRDCCAiwCAf8wDQYJKoZIhvcNAQEFBQAwaDELMAkGA1UEBhMCVVMxCzAJBgNV' + sLineBreak + + 'BAgMAkNBMQswCQYDVQQHDAJMQTEVMBMGA1UECgwMVGVzdCBSb290IENBMQswCQYD' + sLineBreak + + 'VQQLDAJJVDEbMBkGA1UEAwwSd3d3LnRlc3Ryb290Y2EuY29tMB4XDTEzMDIyNzE4' + sLineBreak + + 'MjE1NloXDTIzMDIyNTE4MjE1NlowaDELMAkGA1UEBhMCVVMxCzAJBgNVBAgMAkNB' + sLineBreak + + 'MQswCQYDVQQHDAJMQTEVMBMGA1UECgwMVGVzdCBDb21wYW55MQswCQYDVQQLDAJJ' + sLineBreak + + 'VDEbMBkGA1UEAwwSd3d3LnRlc3RzZXJ2ZXIuY29tMIIBIjANBgkqhkiG9w0BAQEF' + sLineBreak + + 'AAOCAQ8AMIIBCgKCAQEAzkHv+S30g5Dc+F1RJ1PUq9Hbh1YkEUJdYEj7ti+UfONV' + sLineBreak + + 'NOT24hXzg8zaNSVO2Bhm+l8vzOVYMnjK9xcGSq5R5I633+lEeFdxURfsSJv9Vymq' + sLineBreak + + 'tHUj5eNkmjzWBVrf4HvnZTJtRJljs941zYUgyJT9tkQXaerGFKJ6sfdXYfhGrkuK' + sLineBreak + + 'gA1e71TwpRFYcfyYbQ3htENTh2CFBv7l5gjrITcmEJwpcU3U4nx4ZTr0IPLmV2kr' + sLineBreak + + 'K8IJysY4dqgRcmduEI5ZgbYGkdG8L7QjggFXA6QNDPu8DfmXeeqS0gIffEm22bk7' + sLineBreak + + 'b2fMnPfnFsJLsDdyhgrdYktnWhtZNij0y80tV4YCTwIDAQABMA0GCSqGSIb3DQEB' + sLineBreak + + 'BQUAA4IBAQDMLn9VnUQt6BWx73J1lExYO/LWulMOnMR/WSVFy9dSwry+E807ekMY' + sLineBreak + + 'WC8b3gpgDIqfkZjmttE9VtAdss2Baten+oBW+K13339sxHvcn30OxOs/Bln0yvaZ' + sLineBreak + + 'Be+Zir7iE450b1IdYI98PMTSKgrK2e3vx/uUOCgG2yvs6/1v5rz5er/M1SQNzdMS' + sLineBreak + + 'blelHWRQ1/ExwoUWBfIBkx/A4lTPmLgoC9fnXSiLhHKbZdfCJD8KLzEV0Se+ocn/' + sLineBreak + + 'vl+6tlcUznap0TsRQpC67T/NGUimxdAhb6G1/U6z9bq0QQIuDxpOIpvwIgLvfRFx' + sLineBreak + + 'qZQxmxOcK28fejHngmek7ZJNYKQbNewP' + sLineBreak + + '-----END CERTIFICATE-----' + sLineBreak; + + SSL_SERVER_PKEY: string = + '-----BEGIN PRIVATE KEY-----' + sLineBreak + + 'MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQDOQe/5LfSDkNz4' + sLineBreak + + 'XVEnU9Sr0duHViQRQl1gSPu2L5R841U05PbiFfODzNo1JU7YGGb6Xy/M5VgyeMr3' + sLineBreak + + 'FwZKrlHkjrff6UR4V3FRF+xIm/1XKaq0dSPl42SaPNYFWt/ge+dlMm1EmWOz3jXN' + sLineBreak + + 'hSDIlP22RBdp6sYUonqx91dh+EauS4qADV7vVPClEVhx/JhtDeG0Q1OHYIUG/uXm' + sLineBreak + + 'COshNyYQnClxTdTifHhlOvQg8uZXaSsrwgnKxjh2qBFyZ24QjlmBtgaR0bwvtCOC' + sLineBreak + + 'AVcDpA0M+7wN+Zd56pLSAh98SbbZuTtvZ8yc9+cWwkuwN3KGCt1iS2daG1k2KPTL' + sLineBreak + + 'zS1XhgJPAgMBAAECggEAIT83s27Y7yw2skI4hqJYsamOPW6BOdb8vjyFdoSM5uSu' + sLineBreak + + 'I2yU7zSioCgxNEfjQaoNT2ZwihKd+OTHsrSfawJWaQUoVot/YfaWaX/1sm6Sk64/' + sLineBreak + + 'uf733mKdIM+VoB9Z3xGZ5xIN0vT2wVOcUJiZBDwf+XVYYNZbP5BBPtaj20LuAcIZ' + sLineBreak + + 'OmW9uigdXQkQ1dylUkRPitjJ92bbysrTr621JTBSmvKnF7ctcF/Ql6VfS5RcqzYI' + sLineBreak + + '6U1vozoFkjmUnExlYZHC6qKCFG73Z+IcC7ojdMpzMp4/EqiveV/9EVdFlLRB1YAa' + sLineBreak + + 'tND93xU9mo7L26XQzy79Xf2dWRUgUvaJ/7EvLA1RoQKBgQD2ZhJ9ogqfQ0ahq0D6' + sLineBreak + + '5neZo6bPbckEKshv1GKR5ixnYpPp1kCIxM8oIzb9fOvTX4MOMeRzPJyrJNwhVgfY' + sLineBreak + + 'otWLrvkNviGHXN0frmkdj/Y/WSWh7clzzwXmGbB/8NPG4yzREvQ8vhKBkAmZln6K' + sLineBreak + + 'ICl8J5NxOxF6GgYJ793GcsfZVQKBgQDWS3DYMVQ3eRgFajkQ/8+Gacgdu+8/SyM1' + sLineBreak + + 'WptHOlPvKfqg3nZYPlAjMnVmk0Q7l/d2EtFBPP07/Jz0IvC/pMz0S8XfW/NigcRn' + sLineBreak + + '0R5Nci3BXbmQEjxNGt0m0sX4C4/Bx8ei8pugipX96OemT/bWP05RskL6tWsofGsb' + sLineBreak + + '8zgIQcldEwKBgCyx90iyzBp3qahJ2E+q3qcP+IJH9965pAIlFHxCtGtMhmg0ZSBq' + sLineBreak + + 'EunE+YSh1GVTPgKlKjt9Ey44UXX6lRHG99WOt762bn6Pac0FZivmoVR8Z0coSxKm' + sLineBreak + + 'yvsiTdHnbYL2UnraZVNfZxv5dMRXeDy1+NB8nVI81L7BWbcTu7bzuyzBAoGAY0j4' + sLineBreak + + 's3HHbxwvwPKCFhovcDs6eGxGYLDTUzjzkIC5uqlccYQgmKnmPyh1tFyu1F2ITbBS' + sLineBreak + + 'O0OioFRd887sdB5KxzUELIRRs2YkNWVyALfR8zEVdGa+gYrcw8wL5OyWYlXJbPmy' + sLineBreak + + 'mSMcc1OhYDDUUFdsVfWdisLbLxrWFVEOuOSiAvkCgYEA2viHsxoFxOrhnZQOhaLT' + sLineBreak + + 'RPrgaSojv9pooHQ6fJwplewt91tb1OchDIeZk9Sl1hqPAXB0167of43GDOw2vfnq' + sLineBreak + + 'Ust7RtiyJhQhSkz0qp4aH4P9l+dZJIWnpgjcyWkcz893br9gEuVnQgh13V/lcxOn' + sLineBreak + + 'JtpaCFuHNTU3PcFiuQW+cN0=' + sLineBreak + + '-----END PRIVATE KEY-----' + sLineBreak; + +implementation + +end. diff --git a/ThirdParty/DCS/Net/Net.CrossSslServer.pas b/ThirdParty/DCS/Net/Net.CrossSslServer.pas new file mode 100644 index 00000000..fb4e0bf1 --- /dev/null +++ b/ThirdParty/DCS/Net/Net.CrossSslServer.pas @@ -0,0 +1,142 @@ +{******************************************************************************} +{ } +{ Delphi cross platform socket library } +{ } +{ Copyright (c) 2017 WiNDDRiVER(soulawing@gmail.com) } +{ } +{ Homepage: https://github.com/winddriver/Delphi-Cross-Socket } +{ } +{******************************************************************************} +unit Net.CrossSslServer; + +interface + +uses + System.SysUtils, + Net.SocketAPI, + Net.CrossSocket.Base, + Net.CrossSslSocket.Base, + Net.CrossSslSocket; + +type + ICrossSslServer = interface(ICrossSslSocket) + ['{DAEB2898-1EC4-4BCF-9BEB-078B582173AB}'] + function GetAddr: string; + function GetPort: Word; + function GetActive: Boolean; + + procedure SetAddr(const Value: string); + procedure SetPort(const Value: Word); + procedure SetActive(const Value: Boolean); + + procedure Start(const ACallback: TProc = nil); + procedure Stop; + + property Addr: string read GetAddr write SetAddr; + property Port: Word read GetPort write SetPort; + + property Active: Boolean read GetActive write SetActive; + end; + + TCrossSslServer = class(TCrossSslSocket, ICrossSslServer) + private + FPort: Word; + FAddr: string; + FStarted: Integer; + + function GetAddr: string; + function GetPort: Word; + function GetActive: Boolean; + + procedure SetAddr(const Value: string); + procedure SetPort(const Value: Word); + procedure SetActive(const Value: Boolean); + public + constructor Create(AIoThreads: Integer); override; + + procedure Start(const ACallback: TProc = nil); + procedure Stop; + + property Addr: string read GetAddr write SetAddr; + property Port: Word read GetPort write SetPort; + property Active: Boolean read GetActive write SetActive; + end; + +implementation + +{ TCrossSslServer } + +constructor TCrossSslServer.Create(AIoThreads: Integer); +begin + inherited; +end; + +function TCrossSslServer.GetActive: Boolean; +begin + Result := (AtomicCmpExchange(FStarted, 0, 0) = 1); +end; + +function TCrossSslServer.GetAddr: string; +begin + Result := FAddr; +end; + +function TCrossSslServer.GetPort: Word; +begin + Result := FPort; +end; + +procedure TCrossSslServer.SetActive(const Value: Boolean); +begin + if Value then + Start + else + Stop; +end; + +procedure TCrossSslServer.SetAddr(const Value: string); +begin + FAddr := Value; +end; + +procedure TCrossSslServer.SetPort(const Value: Word); +begin + FPort := Value; +end; + +procedure TCrossSslServer.Start(const ACallback: TProc); +begin + if (AtomicExchange(FStarted, 1) = 1) then + begin + if Assigned(ACallback) then + ACallback(False); + + Exit; + end; + + StartLoop; + + Listen(FAddr, FPort, + procedure(AListen: ICrossListen; ASuccess: Boolean) + begin + if not ASuccess then + AtomicExchange(FStarted, 0); + + // Ǽ˿ + // ڼɹ֮ʵʵĶ˿ȡ + if (FPort = 0) then + FPort := AListen.LocalPort; + + if Assigned(ACallback) then + ACallback(ASuccess); + end); +end; + +procedure TCrossSslServer.Stop; +begin + CloseAll; + StopLoop; + AtomicExchange(FStarted, 0); +end; + +end. diff --git a/ThirdParty/DCS/Net/Net.CrossSslSocket.Base.pas b/ThirdParty/DCS/Net/Net.CrossSslSocket.Base.pas new file mode 100644 index 00000000..fdb67c65 --- /dev/null +++ b/ThirdParty/DCS/Net/Net.CrossSslSocket.Base.pas @@ -0,0 +1,85 @@ +unit Net.CrossSslSocket.Base; + +interface + +uses + Net.CrossSocket.Base; + +type + /// + /// SSL Socket + /// + /// + /// ȷʹò: + /// + /// + /// SetCertificateificate SetCertificateificateFile + /// + /// + /// SetPrivateKey SetPrivateKeyFile, ͻ˲Ҫһ + /// + /// + /// Connect / Listen + /// + /// + /// + ICrossSslSocket = interface(ICrossSocket) + ['{A4765486-A0F1-4EFD-BC39-FA16AED21A6A}'] + /// + /// ڴ֤ + /// + /// + /// ֤黺 + /// + /// + /// ֤黺С + /// + procedure SetCertificate(ACertBuf: Pointer; ACertBufSize: Integer); overload; + + /// + /// ַ֤ + /// + /// + /// ַ֤ + /// + procedure SetCertificate(const ACertStr: string); overload; + + /// + /// ļ֤ + /// + /// + /// ֤ļ + /// + procedure SetCertificateFile(const ACertFile: string); + + /// + /// ڴ˽Կ + /// + /// + /// ˽Կ + /// + /// + /// ˽ԿС + /// + procedure SetPrivateKey(APKeyBuf: Pointer; APKeyBufSize: Integer); overload; + + /// + /// ַ˽Կ + /// + /// + /// ˽Կַ + /// + procedure SetPrivateKey(const APKeyStr: string); overload; + + /// + /// ļ˽Կ + /// + /// + /// ˽Կļ + /// + procedure SetPrivateKeyFile(const APKeyFile: string); + end; + +implementation + +end. diff --git a/ThirdParty/DCS/Net/Net.CrossSslSocket.MbedTls.pas b/ThirdParty/DCS/Net/Net.CrossSslSocket.MbedTls.pas new file mode 100644 index 00000000..1e785061 --- /dev/null +++ b/ThirdParty/DCS/Net/Net.CrossSslSocket.MbedTls.pas @@ -0,0 +1,540 @@ +{******************************************************************************} +{ } +{ Delphi cross platform socket library } +{ } +{ Copyright (c) 2017 WiNDDRiVER(soulawing@gmail.com) } +{ } +{ Homepage: https://github.com/winddriver/Delphi-Cross-Socket } +{ } +{******************************************************************************} +unit Net.CrossSslSocket.MbedTls; + +{ + SSL通讯基本流程: + 1. 当连接建立时进行 SSL 握手, 收到数据时也要检查握手状态 + 2. 发送数据: 用 SSL_write 写入原数据, BIO_read 读取加密后的数据进行发送 + 3. 接收数据: 用 BIO_write 写入收到的数据, 用 SSL_read 读取解密后的数据 + + 传输层安全协议: + https://zh.wikipedia.org/wiki/%E5%82%B3%E8%BC%B8%E5%B1%A4%E5%AE%89%E5%85%A8%E5%8D%94%E8%AD%B0 +} + +interface + +uses + System.SysUtils, + System.Classes, + System.IOUtils, + Net.CrossSocket.Base, + Net.CrossSocket, + Net.CrossSslSocket.Base, + Net.MbedTls, + Net.MbedBIO; + +const + DEFAULT_CIPHERSUITES_SERVER: array [0..12] of Integer = ( + MBEDTLS_TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256, + MBEDTLS_TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384, + MBEDTLS_TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA, + MBEDTLS_TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA, + + MBEDTLS_TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256, + MBEDTLS_TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384, + MBEDTLS_TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA, + MBEDTLS_TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA, + + MBEDTLS_TLS_RSA_WITH_AES_128_GCM_SHA256, + MBEDTLS_TLS_RSA_WITH_AES_256_GCM_SHA384, + MBEDTLS_TLS_RSA_WITH_AES_128_CBC_SHA, + MBEDTLS_TLS_RSA_WITH_AES_256_CBC_SHA, + + 0); + + DEFAULT_CIPHERSUITES_CLIENT: array [0..18] of Integer = ( + MBEDTLS_TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256, + MBEDTLS_TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384, + MBEDTLS_TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA, + MBEDTLS_TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA, + + MBEDTLS_TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256, + MBEDTLS_TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384, + MBEDTLS_TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA, + MBEDTLS_TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA, + + MBEDTLS_TLS_DHE_RSA_WITH_AES_128_GCM_SHA256, + MBEDTLS_TLS_DHE_RSA_WITH_AES_256_GCM_SHA384, + MBEDTLS_TLS_DHE_RSA_WITH_AES_128_CBC_SHA, + MBEDTLS_TLS_DHE_RSA_WITH_AES_256_CBC_SHA, + + MBEDTLS_TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA, + + MBEDTLS_TLS_RSA_WITH_AES_128_GCM_SHA256, + MBEDTLS_TLS_RSA_WITH_AES_256_GCM_SHA384, + MBEDTLS_TLS_RSA_WITH_AES_128_CBC_SHA, + MBEDTLS_TLS_RSA_WITH_AES_256_CBC_SHA, + + MBEDTLS_TLS_RSA_WITH_3DES_EDE_CBC_SHA, + + 0); + +type + EMbedTls = class(Exception) + private + FCode: Integer; + public + constructor Create(const ACode: Integer; const AMessage: string); reintroduce; overload; + constructor Create(const ACode: Integer; const AFmt: string; const AArgs: array of const); reintroduce; overload; + + property Code: Integer read FCode; + end; + + TCrossMbedTlsConnection = class(TCrossConnection) + private + FSsl: TMbedtls_SSL_Context; + FSslBIO, FAppBIO: PBIO; + + procedure _Lock; inline; + procedure _Unlock; inline; + + function _SslHandshake: Boolean; + procedure _SendBIOPendingData(const ACallback: TProc = nil); + protected + procedure DirectSend(ABuffer: Pointer; ACount: Integer; + const ACallback: TProc = nil); override; + public + constructor Create(AOwner: ICrossSocket; AClientSocket: THandle; + AConnectType: TConnectType); override; + destructor Destroy; override; + end; + + /// + /// 若要继承该类, 请重载 LogicXXX, 而不是 TriggerXXX + /// + TCrossMbedTlsSocket = class(TCrossSocket, ICrossSslSocket) + private const + SSL_BUF_SIZE = 32768; + private class threadvar + FSslInBuf: array [0..SSL_BUF_SIZE-1] of Byte; + private + FSrvConf, FCliConf: TMbedtls_SSL_Config; + FEntropy: TMbedtls_Entropy_Context; + FCtrDrbg: TMbedtls_CTR_DRBG_Context ; + FCert: TMbedtls_X509_CRT; + FPKey: TMbedtls_PK_Context; + FCache: TMbedtls_SSL_Cache_Context; + + procedure _InitSslConf; + procedure _FreeSslConf; + + function _MbedCert(const ACertBytes: TBytes): TBytes; inline; + procedure _UpdateCert; + protected + procedure TriggerConnected(AConnection: ICrossConnection); override; + procedure TriggerReceived(AConnection: ICrossConnection; ABuf: Pointer; ALen: Integer); override; + + function CreateConnection(AOwner: ICrossSocket; AClientSocket: THandle; + AConnectType: TConnectType): ICrossConnection; override; + public + constructor Create(AIoThreads: Integer); override; + destructor Destroy; override; + + procedure SetCertificate(ACertBuf: Pointer; ACertBufSize: Integer); overload; + procedure SetCertificate(const ACertStr: string); overload; + procedure SetCertificateFile(const ACertFile: string); + + procedure SetPrivateKey(APKeyBuf: Pointer; APKeyBufSize: Integer); overload; + procedure SetPrivateKey(const APKeyStr: string); overload; + procedure SetPrivateKeyFile(const APKeyFile: string); + end; + +implementation + +{ EMbedTls } + +constructor EMbedTls.Create(const ACode: Integer; const AMessage: string); +var + LMessage: string; +begin + FCode := ACode; + + if (AMessage <> '') then + LMessage := AMessage + MbedErrToStr(ACode) + else + LMessage := MbedErrToStr(ACode); + + inherited Create(LMessage); +end; + +constructor EMbedTls.Create(const ACode: Integer; const AFmt: string; + const AArgs: array of const); +begin + Create(ACode, Format(AFmt, AArgs)); +end; + +function MbedCheck(const ACode: Integer; const AErrMsg: string = ''): Integer; +begin + Result := ACode; + + if (ACode >= 0) then Exit; + + case ACode of + MBEDTLS_ERR_SSL_WANT_READ, MBEDTLS_ERR_SSL_WANT_WRITE:; + else + raise EMbedTls.Create(ACode, AErrMsg); + end; +end; + +{ TCrossMbedTlsConnection } + +constructor TCrossMbedTlsConnection.Create(AOwner: ICrossSocket; + AClientSocket: THandle; AConnectType: TConnectType); +begin + inherited; + + mbedtls_ssl_init(@FSsl); + + if (ConnectType = ctAccept) then + MbedCheck(mbedtls_ssl_setup(@Fssl, @TCrossMbedTlsSocket(Owner).FSrvConf), 'mbedtls_ssl_setup Accept:') + else + MbedCheck(mbedtls_ssl_setup(@Fssl, @TCrossMbedTlsSocket(Owner).FCliConf), 'mbedtls_ssl_setup Connect:'); + + FSslBIO := SSL_BIO_new(BIO_BIO); + FAppBIO := SSL_BIO_new(BIO_BIO); + BIO_make_bio_pair(FSslBIO, FAppBIO); + + mbedtls_ssl_set_bio(@FSsl, FSslBIO, BIO_net_send, BIO_net_recv, nil); +end; + +destructor TCrossMbedTlsConnection.Destroy; +begin + mbedtls_ssl_free(@FSsl); + BIO_free_all(FSslBIO); + BIO_free_all(FAppBIO); + + inherited; +end; + +procedure TCrossMbedTlsConnection._Lock; +begin + // mbedtls 的多线程支持比 openssl 完善 + // 调用 mbedtls_threading_set_alt 设置了相应的线程同步函数之后不用再自己 + // _Lock _Unlock 了 +// System.TMonitor.Enter(Self); +end; + +procedure TCrossMbedTlsConnection._SendBIOPendingData( + const ACallback: TProc); +var + LConnection: ICrossConnection; + LRetCode: Integer; + LBuffer: TBytesStream; + + procedure _Success; + begin + if (LBuffer <> nil) then + FreeAndNil(LBuffer); + if Assigned(ACallback) then + ACallback(LConnection, True); + end; + +begin + LConnection := Self; + LBuffer := nil; + + {$region '将BIO中已加密的数据全部读到缓存中'} + // 检查 BIO 中是否有数据 + LRetCode := BIO_ctrl_pending(FAppBIO); + if (LRetCode <= 0) then + begin + _Success; + Exit; + end; + + LBuffer := TBytesStream.Create(nil); + while (LRetCode > 0) do + begin + LBuffer.Size := LBuffer.Size + LRetCode; + + // 读取加密后的数据 + LRetCode := BIO_read(FAppBIO, PByte(LBuffer.Memory) + LBuffer.Position, LRetCode); + if (LRetCode <= 0) then Break; + + LBuffer.Position := LBuffer.Position + LRetCode; + + // 检查 BIO 中是否还有数据 + LRetCode := BIO_ctrl_pending(FAppBIO); + end; + + if (LBuffer.Memory = nil) or (LBuffer.Size <= 0) then + begin + _Success; + Exit; + end; + {$endregion} + + {$region '发送缓存中已加密的数据'} + inherited DirectSend(LBuffer.Memory, LBuffer.Size, + procedure(AConnection: ICrossConnection; ASuccess: Boolean) + begin + FreeAndNil(LBuffer); + if Assigned(ACallback) then + ACallback(AConnection, ASuccess); + end); + {$endregion} +end; + +procedure TCrossMbedTlsConnection.DirectSend(ABuffer: Pointer; ACount: Integer; + const ACallback: TProc); +var + LRetCode: Integer; +begin + LRetCode := mbedtls_ssl_write(@FSsl, ABuffer, ACount); + if (LRetCode <> ACount) then + begin + _Log('mbedtls_ssl_write, %d / %d', [LRetCode, ACount]); + end; + + // 将待发送数据加密后发送 + if (MbedCheck(LRetCode, 'mbedtls_ssl_write DirectSend:') > 0) then + _SendBIOPendingData(ACallback); +end; + +function TCrossMbedTlsConnection._SslHandshake: Boolean; +begin + // 开始握手 + Result := (MbedCheck(mbedtls_ssl_handshake(@FSsl), 'mbedtls_ssl_handshake _SslHandshake:') = 0); + _SendBIOPendingData; +end; + +procedure TCrossMbedTlsConnection._Unlock; +begin +// System.TMonitor.Exit(Self); +end; + +{ TCrossMbedTlsSocket } + +constructor TCrossMbedTlsSocket.Create(AIoThreads: Integer); +begin + inherited; + + _InitSslConf; +end; + +destructor TCrossMbedTlsSocket.Destroy; +begin + inherited; + + _FreeSslConf; +end; + +function TCrossMbedTlsSocket.CreateConnection(AOwner: ICrossSocket; + AClientSocket: THandle; AConnectType: TConnectType): ICrossConnection; +begin + Result := TCrossMbedTlsConnection.Create(AOwner, AClientSocket, AConnectType); +end; + +procedure TCrossMbedTlsSocket.SetCertificate(ACertBuf: Pointer; ACertBufSize: Integer); +begin + MbedCheck(mbedtls_x509_crt_parse(@FCert, ACertBuf, ACertBufSize), 'mbedtls_x509_crt_parse SetCertificate:'); + + _UpdateCert; +end; + +procedure TCrossMbedTlsSocket.SetCertificate(const ACertStr: string); +var + LCertBytes: TBytes; +begin + LCertBytes := TEncoding.ANSI.GetBytes(ACertStr); + LCertBytes := _MbedCert(LCertBytes); + + SetCertificate(Pointer(LCertBytes), Length(LCertBytes)); +end; + +procedure TCrossMbedTlsSocket.SetCertificateFile(const ACertFile: string); +var + LCertBytes: TBytes; +begin + LCertBytes := TFile.ReadAllBytes(ACertFile); + LCertBytes := _MbedCert(LCertBytes); + + SetCertificate(Pointer(LCertBytes), Length(LCertBytes)); +end; + +procedure TCrossMbedTlsSocket.SetPrivateKey(APKeyBuf: Pointer; APKeyBufSize: Integer); +begin + MbedCheck(mbedtls_pk_parse_key(@FPKey, APKeyBuf, APKeyBufSize, nil, 0), 'mbedtls_pk_parse_key SetPrivateKey:'); + + _UpdateCert; +end; + +procedure TCrossMbedTlsSocket.SetPrivateKey(const APKeyStr: string); +var + LPKeyBytes: TBytes; +begin + LPKeyBytes := TEncoding.ANSI.GetBytes(APKeyStr); + LPKeyBytes := _MbedCert(LPKeyBytes); + + SetPrivateKey(Pointer(LPKeyBytes), Length(LPKeyBytes)); +end; + +procedure TCrossMbedTlsSocket.SetPrivateKeyFile(const APKeyFile: string); +var + LPKeyBytes: TBytes; +begin + LPKeyBytes := TFile.ReadAllBytes(APKeyFile); + LPKeyBytes := _MbedCert(LPKeyBytes); + + SetPrivateKey(Pointer(LPKeyBytes), Length(LPKeyBytes)); +end; + +procedure TCrossMbedTlsSocket.TriggerConnected(AConnection: ICrossConnection); +var + LConnection: TCrossMbedTlsConnection; +begin + LConnection := AConnection as TCrossMbedTlsConnection; + + // 网络连接已建立, 等待握手 + LConnection.ConnectStatus := csHandshaking; + + if LConnection._SslHandshake then + begin + LConnection.ConnectStatus := csConnected; + inherited TriggerConnected(AConnection); + end; +end; + +procedure TCrossMbedTlsSocket.TriggerReceived(AConnection: ICrossConnection; + ABuf: Pointer; ALen: Integer); +var + LConnection: TCrossMbedTlsConnection; + LRetCode: Integer; +begin + LConnection := AConnection as TCrossMbedTlsConnection; + + LConnection._Lock; + try + // 将收到的加密数据写入 BIO + LRetCode := BIO_write(LConnection.FAppBIO, ABuf, ALen); + if (LRetCode <= 0) then + begin + _Log('BIO_write, error: %d', [LRetCode]); + LConnection.Close; + Exit; + end; + + if (LRetCode <> ALen) then + begin + _Log('BIO_write, %d / %d', [LRetCode, ALen]); + end; + + // 握手 + if (LConnection.ConnectStatus = csHandshaking) then + begin + // 已完成握手才视为连接真正建立 + if LConnection._SslHandshake then + begin + LConnection.ConnectStatus := csConnected; + inherited TriggerConnected(AConnection); + end else + Exit; + end; + + while True do + begin + // 读取解密后的数据 + LRetCode := mbedtls_ssl_read(@LConnection.FSsl, @FSslInBuf, SSL_BUF_SIZE); + + if (LRetCode > 0) then + begin + inherited TriggerReceived(AConnection, @FSslInBuf, LRetCode); + end else + begin + case LRetCode of + MBEDTLS_ERR_SSL_WANT_READ, MBEDTLS_ERR_SSL_WANT_WRITE:; + else + _Log('mbedtls_ssl_read, error: %d', [LRetCode]); + LConnection.Close; + end; + Break; + end; + end; + finally + LConnection._Unlock; + end; +end; + +procedure TCrossMbedTlsSocket._FreeSslConf; +begin + mbedtls_ssl_config_free(@FSrvConf); + mbedtls_ssl_config_free(@FCliConf); + + mbedtls_x509_crt_free(@FCert); + mbedtls_pk_free(@FPKey); + + mbedtls_ctr_drbg_free(@FCtrDrbg); + mbedtls_entropy_free(@FEntropy); + mbedtls_ssl_cache_free(@FCache); +end; + +procedure TCrossMbedTlsSocket._InitSslConf; +begin + mbedtls_x509_crt_init(@FCert); + mbedtls_pk_init(@FPKey); + mbedtls_ctr_drbg_init(@FCtrDrbg); + mbedtls_entropy_init(@FEntropy); + mbedtls_ssl_cache_init(@FCache); + + MbedCheck(mbedtls_ctr_drbg_seed(@FCtrDrbg, mbedtls_entropy_func, @FEntropy, nil, 0), 'mbedtls_ctr_drbg_seed:'); + + {$region '服务端SSL配置'} + mbedtls_ssl_config_init(@FSrvConf); + mbedtls_ssl_conf_rng(@FSrvConf, mbedtls_ctr_drbg_random, @FCtrDrbg); + mbedtls_ssl_conf_authmode(@FSrvConf, MBEDTLS_SSL_VERIFY_OPTIONAL); + mbedtls_ssl_conf_session_cache(@FSrvConf, @FCache, mbedtls_ssl_cache_get, mbedtls_ssl_cache_set); // 仅服务端有效 + mbedtls_ssl_conf_ciphersuites(@FSrvConf, PInteger(@DEFAULT_CIPHERSUITES_SERVER)); + mbedtls_ssl_conf_min_version(@FSrvConf, MBEDTLS_SSL_MAJOR_VERSION_3, MBEDTLS_SSL_MINOR_VERSION_3); // TLS v1.2 + MbedCheck(mbedtls_ssl_config_defaults(@FSrvConf, + MBEDTLS_SSL_IS_SERVER, + MBEDTLS_SSL_TRANSPORT_STREAM, + MBEDTLS_SSL_PRESET_DEFAULT), 'mbedtls_ssl_config_defaults FSrvConf:'); + {$endregion} + + {$region '客户端SSL配置'} + mbedtls_ssl_config_init(@FCliConf); + mbedtls_ssl_conf_rng(@FCliConf, mbedtls_ctr_drbg_random, @FCtrDrbg); + mbedtls_ssl_conf_authmode(@FCliConf, MBEDTLS_SSL_VERIFY_OPTIONAL); + MbedCheck(mbedtls_ssl_config_defaults(@FCliConf, + MBEDTLS_SSL_IS_CLIENT, + MBEDTLS_SSL_TRANSPORT_STREAM, + MBEDTLS_SSL_PRESET_DEFAULT), 'mbedtls_ssl_config_defaults FCliConf:'); + mbedtls_ssl_conf_ciphersuites(@FCliConf, PInteger(@DEFAULT_CIPHERSUITES_CLIENT)); + {$endregion} +end; + +function TCrossMbedTlsSocket._MbedCert(const ACertBytes: TBytes): TBytes; +begin + // PEM格式的证书需要以#0结尾 + if (ACertBytes = nil) + or (ACertBytes[High(ACertBytes)] = 0) then + Result := ACertBytes + else + Result := ACertBytes + [0]; +end; + +procedure TCrossMbedTlsSocket._UpdateCert; +begin + // 尚未加载证书 + if (FCert.version = 0) then Exit; + + mbedtls_ssl_conf_ca_chain(@FCliConf, @FCert, nil); + + // 尚未加载私钥 + if (FPKey.pk_info = nil) then Exit; + + if (FCert.next <> nil) then + mbedtls_ssl_conf_ca_chain(@FSrvConf, FCert.next, nil); + + MbedCheck(mbedtls_ssl_conf_own_cert(@FSrvConf, @FCert, @FPKey), 'mbedtls_ssl_conf_own_cert:'); +end; + +end. diff --git a/ThirdParty/DCS/Net/Net.CrossSslSocket.OpenSSL.pas b/ThirdParty/DCS/Net/Net.CrossSslSocket.OpenSSL.pas new file mode 100644 index 00000000..a54007f1 --- /dev/null +++ b/ThirdParty/DCS/Net/Net.CrossSslSocket.OpenSSL.pas @@ -0,0 +1,507 @@ +{******************************************************************************} +{ } +{ Delphi cross platform socket library } +{ } +{ Copyright (c) 2017 WiNDDRiVER(soulawing@gmail.com) } +{ } +{ Homepage: https://github.com/winddriver/Delphi-Cross-Socket } +{ } +{******************************************************************************} +unit Net.CrossSslSocket.OpenSSL; + +{ + SSL通讯基本流程: + 1. 当连接建立时进行 SSL 握手, 收到数据时也要检查握手状态 + 2. 发送数据: 用 SSL_write 写入原数据, BIO_read 读取加密后的数据进行发送 + 3. 接收数据: 用 BIO_write 写入收到的数据, 用 SSL_read 读取解密后的数据 + + OpenSSL 的 SSL 对象不是线程安全的!!!!!!!!!!!!!!!!! + 即便初始化 OpenSSL 时设置了那几个线程相关的回调也一样, + 一定要保证同一时间不能有两个或以上的线程访问到同一个 SSL 对象 + 或者与其绑定的 BIO 对象 + + 由于收发数据需要解密及加密, 还需要用临界区保护 SSL 对象, + 所以效率比不使用 SSL 时会下降很多, 这是无法避免的 + + 传输层安全协议: + https://zh.wikipedia.org/wiki/%E5%82%B3%E8%BC%B8%E5%B1%A4%E5%AE%89%E5%85%A8%E5%8D%94%E8%AD%B0 +} + +interface + +uses + System.SysUtils, + System.Classes, + Net.CrossSocket.Base, + Net.CrossSocket, + Net.CrossSslSocket.Base, + Net.OpenSSL; + +type + TCrossOpenSslConnection = class(TCrossConnection) + private + FSsl: PSSL; + FBIOIn, FBIOOut: PBIO; + FSslLock: TObject; + + procedure _SslLock; inline; + procedure _SslUnlock; inline; + + function _SslHandshake: Boolean; + procedure _WriteBioToSocket(const ACallback: TProc = nil); + protected + procedure DirectSend(ABuffer: Pointer; ACount: Integer; + const ACallback: TProc = nil); override; + public + constructor Create(AOwner: ICrossSocket; AClientSocket: THandle; + AConnectType: TConnectType); override; + destructor Destroy; override; + end; + + /// + /// 若要继承该类, 请重载 LogicXXX, 而不是 TriggerXXX + /// + TCrossOpenSslSocket = class(TCrossSocket, ICrossSslSocket) + private const + SSL_BUF_SIZE = 32768; + private class threadvar + FSslInBuf: array [0..SSL_BUF_SIZE-1] of Byte; + private + FSslCtx: PSSL_CTX; + + procedure _InitSslCtx; + procedure _FreeSslCtx; + protected + procedure TriggerConnected(AConnection: ICrossConnection); override; + procedure TriggerReceived(AConnection: ICrossConnection; ABuf: Pointer; ALen: Integer); override; + + function CreateConnection(AOwner: ICrossSocket; AClientSocket: THandle; + AConnectType: TConnectType): ICrossConnection; override; + public + constructor Create(AIoThreads: Integer); override; + destructor Destroy; override; + + procedure SetCertificate(ACertBuf: Pointer; ACertBufSize: Integer); overload; + procedure SetCertificate(const ACertStr: string); overload; + procedure SetCertificateFile(const ACertFile: string); + + procedure SetPrivateKey(APKeyBuf: Pointer; APKeyBufSize: Integer); overload; + procedure SetPrivateKey(const APKeyStr: string); overload; + procedure SetPrivateKeyFile(const APKeyFile: string); + end; + +implementation + +{ TCrossOpenSslConnection } + +constructor TCrossOpenSslConnection.Create(AOwner: ICrossSocket; + AClientSocket: THandle; AConnectType: TConnectType); +begin + inherited; + + FSslLock := TObject.Create; + + FSsl := SSL_new(TCrossOpenSslSocket(Owner).FSslCtx); + FBIOIn := BIO_new(BIO_s_mem()); + FBIOOut := BIO_new(BIO_s_mem()); + SSL_set_bio(FSsl, FBIOIn, FBIOOut); + + if (ConnectType = ctAccept) then + SSL_set_accept_state(FSsl) // 服务端连接 + else + SSL_set_connect_state(FSsl); // 客户端连接 +end; + +destructor TCrossOpenSslConnection.Destroy; +begin + _SslLock; + try + if (SSL_shutdown(FSsl) = 0) then + SSL_shutdown(FSsl); + SSL_free(FSsl); + finally + _SslUnlock; + end; + FreeAndNil(FSslLock); + + inherited; +end; + +procedure TCrossOpenSslConnection._WriteBioToSocket( + const ACallback: TProc); +var + LConnection: ICrossConnection; + ret, error: Integer; + LBuffer: TBytesStream; + + procedure _Success; + begin + if (LBuffer <> nil) then + FreeAndNil(LBuffer); + if Assigned(ACallback) then + ACallback(LConnection, True); + end; + + procedure _Failed; + begin + if (LBuffer <> nil) then + FreeAndNil(LBuffer); + LConnection.Close; + if Assigned(ACallback) then + ACallback(LConnection, False); + end; + +begin + LConnection := Self; + LBuffer := nil; + + {$region '将BIO中已加密的数据全部读到缓存中'} + // 从BIO中读取数据这一段必须全读出来再发送 + // 因为SSL对象本身并不是线程安全的, 如果读取数据的同时, 另一个线程尝试操作SSL对象 + // 就会引起异常, 所以这里将读取数据和发送数据分成两部分, 将数据全读出来之后 + // 再调用异步发送, 方便在外层包裹加锁 + ret := BIO_pending(FBIOOut); + if (ret <= 0) then + begin + _Success; + Exit; + end; + + LBuffer := TBytesStream.Create(nil); + while (ret > 0) do + begin + LBuffer.Size := LBuffer.Size + ret; + + // 读取加密后的数据 + ret := BIO_read(FBIOOut, PByte(LBuffer.Memory) + LBuffer.Position, ret); + error := SSL_get_error(FSsl, ret); + if ssl_is_fatal_error(error) then + begin + _Failed; + Exit; + end; + + if (ret <= 0) then Break; + + LBuffer.Position := LBuffer.Position + ret; + + // 检查 BIO 中是否有数据 + ret := BIO_pending(FBIOOut); + end; + + if (LBuffer.Memory = nil) or (LBuffer.Size <= 0) then + begin + _Success; + Exit; + end; + {$endregion} + + {$region '发送缓存中已加密的数据'} + inherited DirectSend(LBuffer.Memory, LBuffer.Size, + procedure(AConnection: ICrossConnection; ASuccess: Boolean) + begin + FreeAndNil(LBuffer); + if Assigned(ACallback) then + ACallback(AConnection, ASuccess); + end); + {$endregion} +end; + +procedure TCrossOpenSslConnection.DirectSend(ABuffer: Pointer; ACount: Integer; + const ACallback: TProc); +var + LConnection: ICrossConnection; + ret, error: Integer; + + procedure _Failed; + begin + if Assigned(ACallback) then + ACallback(LConnection, False); + end; + +begin + LConnection := Self; + + // 将待发送数据加密 + // SSL_write 默认会将全部数据写入成功才返回, + // 除非调用 SSL_CTX_set_mode 设置了 SSL_MODE_ENABLE_PARTIAL_WRITE 参数 + // 才会出现部分写入成功即返回的情况。这里并没有设置该参数,所以无需做 + // 部分数据的处理,只需要一次 SSL_Write 调用即可。 + _SslLock; + try + ret := SSL_write(FSsl, ABuffer, ACount); + if (ret > 0) then + _WriteBioToSocket(ACallback) + else + begin + error := SSL_get_error(FSsl, ret); + _Log('SSL_write error %d %s', [error, ssl_error_message(error)]); + case error of + SSL_ERROR_WANT_READ:; + SSL_ERROR_WANT_WRITE: _WriteBioToSocket; + else + _Failed; + end; + end; + finally + _SslUnlock; + end; +end; + +procedure TCrossOpenSslConnection._SslLock; +begin + TMonitor.Enter(FSslLock); +end; + +procedure TCrossOpenSslConnection._SslUnlock; +begin + TMonitor.Exit(FSslLock); +end; + +function TCrossOpenSslConnection._SslHandshake: Boolean; +var + ret, error: Integer; +begin + Result := False; + + _SslLock; + try + // 开始握手 + ret := SSL_do_handshake(FSsl); + if (ret = 1) then + begin + _WriteBioToSocket; + Exit(True); + end; + + error := SSL_get_error(FSsl, ret); + if ssl_is_fatal_error(error) then + begin + {$IFDEF DEBUG} + _Log('SSL_do_handshake error %s', [ssl_error_message(error)]); + {$ENDIF} + Close; + end else + _WriteBioToSocket; + finally + _SslUnlock; + end; +end; + +{ TCrossOpenSslSocket } + +constructor TCrossOpenSslSocket.Create(AIoThreads: Integer); +begin + inherited; + + TSSLTools.LoadSSL; + _InitSslCtx; +end; + +destructor TCrossOpenSslSocket.Destroy; +begin + inherited; + + _FreeSslCtx; + TSSLTools.UnloadSSL; +end; + +function TCrossOpenSslSocket.CreateConnection(AOwner: ICrossSocket; + AClientSocket: THandle; AConnectType: TConnectType): ICrossConnection; +begin + Result := TCrossOpenSslConnection.Create(AOwner, AClientSocket, AConnectType); +end; + +procedure TCrossOpenSslSocket._InitSslCtx; +var + LEcdh: PEC_KEY; +begin + if (FSslCtx <> nil) then Exit; + + FSslCtx := TSSLTools.NewCTX(SSLv23_method()); + + SSL_CTX_set_verify(FSslCtx, SSL_VERIFY_NONE, nil); + + SSL_CTX_set_mode(FSslCtx, SSL_MODE_AUTO_RETRY); + + {$region '采用新型加密套件进行加密'} + SSL_CTX_set_options(FSslCtx, + // 不使用已经不安全的 SSLv2 和 SSv3 + SSL_OP_NO_SSLv2 or SSL_OP_NO_SSLv3 or + // 启用各种漏洞解决方案(适用于 0.9.7 之前的版本) + SSL_OP_ALL or + // 总是使用 SSL_CTX_set_tmp_ecdh/SSL_set_tmp_ecdh 设置的参数创建新 KEY + SSL_OP_SINGLE_ECDH_USE or + // 根据服务器偏好选择加密套件 + SSL_OP_CIPHER_SERVER_PREFERENCE + ); + + // 设置加密套件的使用顺序 + SSL_CTX_set_cipher_list(FSslCtx, + // from nodejs(node_constants.h) + // #define DEFAULT_CIPHER_LIST_CORE + 'ECDHE-ECDSA-AES128-GCM-SHA256:' + + 'ECDHE-RSA-AES128-GCM-SHA256:' + + 'ECDHE-RSA-AES256-GCM-SHA384:' + + 'ECDHE-ECDSA-AES256-GCM-SHA384:' + + 'DHE-RSA-AES128-GCM-SHA256:' + + 'ECDHE-RSA-AES128-SHA256:' + + 'DHE-RSA-AES128-SHA256:' + + 'ECDHE-RSA-AES256-SHA384:' + + 'DHE-RSA-AES256-SHA384:' + + 'ECDHE-RSA-AES256-SHA256:' + + 'DHE-RSA-AES256-SHA256:' + + 'HIGH:' + + '!aNULL:' + + '!eNULL:' + + '!EXPORT:' + + '!DES:' + + '!RC4:' + + '!MD5:' + + '!PSK:' + + '!SRP:' + + '!CAMELLIA' + ); + + LEcdh := EC_KEY_new_by_curve_name(NID_X9_62_prime256v1); + if (LEcdh <> nil) then + begin + SSL_CTX_set_tmp_ecdh(FSslCtx, LEcdh); + EC_KEY_free(LEcdh); + end; + {$endregion} +end; + +procedure TCrossOpenSslSocket._FreeSslCtx; +begin + if (FSslCtx = nil) then Exit; + + TSSLTools.FreeCTX(FSslCtx); +end; + +procedure TCrossOpenSslSocket.SetCertificate(ACertBuf: Pointer; + ACertBufSize: Integer); +begin + TSSLTools.SetCertificate(FSslCtx, ACertBuf, ACertBufSize); +end; + +procedure TCrossOpenSslSocket.SetCertificate(const ACertStr: string); +begin + TSSLTools.SetCertificate(FSslCtx, ACertStr); +end; + +procedure TCrossOpenSslSocket.SetCertificateFile(const ACertFile: string); +begin + TSSLTools.SetCertificateFile(FSslCtx, ACertFile); +end; + +procedure TCrossOpenSslSocket.SetPrivateKey(APKeyBuf: Pointer; + APKeyBufSize: Integer); +begin + TSSLTools.SetPrivateKey(FSslCtx, APKeyBuf, APKeyBufSize); +end; + +procedure TCrossOpenSslSocket.SetPrivateKey(const APKeyStr: string); +begin + TSSLTools.SetPrivateKey(FSslCtx, APKeyStr); +end; + +procedure TCrossOpenSslSocket.SetPrivateKeyFile(const APKeyFile: string); +begin + TSSLTools.SetPrivateKeyFile(FSslCtx, APKeyFile); +end; + +procedure TCrossOpenSslSocket.TriggerConnected(AConnection: ICrossConnection); +var + LConnection: TCrossOpenSslConnection; +begin + LConnection := AConnection as TCrossOpenSslConnection; + + LConnection._SslLock; + try + // 网络连接已建立, 等待握手 + LConnection.ConnectStatus := csHandshaking; + + // 已完成握手才视为连接真正建立 + if LConnection._SslHandshake then + begin + LConnection.ConnectStatus := csConnected; + inherited TriggerConnected(AConnection); + end; + finally + LConnection._SslUnlock; + end; +end; + +procedure TCrossOpenSslSocket.TriggerReceived(AConnection: ICrossConnection; + ABuf: Pointer; ALen: Integer); +var + LConnection: TCrossOpenSslConnection; + ret, error: Integer; + LBuffer: TBytesStream; +begin + LConnection := AConnection as TCrossOpenSslConnection; + LConnection._SslLock; + try + // 将收到的加密数据写入 BIO, 让 OpenSSL 对其解密 + while True do + begin + ret := BIO_write(LConnection.FBIOIn, ABuf, ALen); +// _Log('recv %d, bio_write %d', [ALen, ret]); + if (ret > 0) then Break; + + if not BIO_should_retry(LConnection.FBIOIn) then + begin + LConnection.Close; + Exit; + end; + end; + + // 未完成初始化, 继续握手 + if not SSL_is_init_finished(LConnection.FSsl) then + begin + // 已完成握手才视为连接真正建立 + if LConnection._SslHandshake + and (LConnection.ConnectStatus = csHandshaking) then + begin + LConnection.ConnectStatus := csConnected; + inherited TriggerConnected(AConnection); + end; + Exit; + end; + + LBuffer := TBytesStream.Create(nil); + try + while True do + begin + // 貌似每次读出来的数据都不会超过 16K + ret := SSL_read(LConnection.FSsl, @FSslInBuf[0], SSL_BUF_SIZE); + if (ret > 0) then + LBuffer.Write(FSslInBuf[0], ret) + else + begin + error := SSL_get_error(LConnection.FSsl, ret); +// _Log('SSL_read error %d %s', [error, ssl_error_message(error)]); + + if ssl_is_fatal_error(error) then + begin + {$IFDEF DEBUG} + _Log('SSL_read error %d %s', [error, ssl_error_message(error)]); + {$ENDIF} + LConnection.Close; + end; + Break; + end; + end; + + if (LBuffer.Size > 0) then + inherited TriggerReceived(AConnection, LBuffer.Memory, LBuffer.Size); + finally + FreeAndNil(LBuffer); + end; + finally + LConnection._SslUnlock; + end; +end; + +end. diff --git a/ThirdParty/DCS/Net/Net.CrossSslSocket.pas b/ThirdParty/DCS/Net/Net.CrossSslSocket.pas new file mode 100644 index 00000000..de277ba5 --- /dev/null +++ b/ThirdParty/DCS/Net/Net.CrossSslSocket.pas @@ -0,0 +1,31 @@ +unit Net.CrossSslSocket; + +interface + +uses + Net.CrossSocket.Base, + Net.CrossSslSocket.Base + {$IFDEF __MBED_TLS__} + ,Net.CrossSslSocket.MbedTls + {$ELSE} + ,Net.CrossSslSocket.OpenSSL + {$ENDIF}; + +type + TCrossSslConnection = + {$IFDEF __MBED_TLS__} + TCrossMbedTlsConnection + {$ELSE} + TCrossOpenSslConnection + {$ENDIF}; + + TCrossSslSocket = + {$IFDEF __MBED_TLS__} + TCrossMbedTlsSocket + {$ELSE} + TCrossOpenSslSocket + {$ENDIF}; + +implementation + +end. diff --git a/ThirdParty/DCS/Net/Net.CrossWebSocketServer.pas b/ThirdParty/DCS/Net/Net.CrossWebSocketServer.pas new file mode 100644 index 00000000..71a44f8e --- /dev/null +++ b/ThirdParty/DCS/Net/Net.CrossWebSocketServer.pas @@ -0,0 +1,1067 @@ +{******************************************************************************} +{ } +{ Delphi cross platform socket library } +{ } +{ Copyright (c) 2017 WiNDDRiVER(soulawing@gmail.com) } +{ } +{ Homepage: https://github.com/winddriver/Delphi-Cross-Socket } +{ } +{******************************************************************************} +unit Net.CrossWebSocketServer; + +{ + The WebSocket Protocol + https://tools.ietf.org/html/rfc6455 + + 0 1 2 3 + 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + +-+-+-+-+-------+-+-------------+-------------------------------+ + |F|R|R|R| opcode|M| Payload len | Extended payload length | + |I|S|S|S| (4) |A| (7) | (16/64) | + |N|V|V|V| |S| | (if payload len==126/127) | + | |1|2|3| |K| | | + +-+-+-+-+-------+-+-------------+ - - - - - - - - - - - - - - - + + | Extended payload length continued, if payload len == 127 | + + - - - - - - - - - - - - - - - +-------------------------------+ + | |Masking-key, if MASK set to 1 | + +-------------------------------+-------------------------------+ + | Masking-key (continued) | Payload Data | + +-------------------------------- - - - - - - - - - - - - - - - + + : Payload Data continued ... : + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + | Payload Data continued ... | + +---------------------------------------------------------------+ + opcode: + * %x0 denotes a continuation frame + * %x1 denotes a text frame + * %x2 denotes a binary frame + * %x3-7 are reserved for further non-control frames + * %x8 denotes a connection close + * %x9 denotes a ping + * %xA denotes a pong + * %xB-F are reserved for further control frames + Payload length: 7 bits, 7+16 bits, or 7+64 bits + Masking-key: 0 or 4 bytes +} + +interface + +uses + System.SysUtils, + System.Classes, + System.Hash, + System.NetEncoding, + System.Math, + System.Generics.Collections, + Net.CrossSocket.Base, + Net.CrossHttpServer; + +const + WS_OP_CONTINUATION = $00; + WS_OP_TEXT = $01; + WS_OP_BINARY = $02; + WS_OP_CLOSE = $08; + WS_OP_PING = $09; + WS_OP_PONG = $0A; + + WS_MAGIC_STR = '258EAFA5-E914-47DA-95CA-C5AB0DC85B11'; + +type + /// + /// WebSocket连接接口 + /// + ICrossWebSocketConnection = interface(ICrossHttpConnection) + ['{15AAA55B-1671-43A1-A9CF-D3EF08D377A6}'] + /// + /// 是否WEB SOCKET + /// + function IsWebSocket: Boolean; + + /// + /// 发送关闭握手 + /// + procedure WsClose; + + /// + /// 发送无类型数据 + /// + /// + /// 无类型数据 + /// + /// + /// 数据大小 + /// + /// + /// 回调函数 + /// + procedure WsSend(const AData; ACount: NativeInt; ACallback: TProc = nil); overload; + + /// + /// 发送字节数据 + /// + /// + /// 字节数据 + /// + /// + /// 偏移量 + /// + /// + /// 数据大小 + /// + /// + /// 回调函数 + /// + procedure WsSend(const AData: TBytes; AOffset, ACount: NativeInt; ACallback: TProc = nil); overload; + + /// + /// 发送字节数据 + /// + /// + /// 字节数据 + /// + /// + /// 回调函数 + /// + procedure WsSend(const AData: TBytes; ACallback: TProc = nil); overload; + + /// + /// 发送字符串数据 + /// + /// + /// 字符串数据 + /// + /// + /// 回调函数 + /// + procedure WsSend(const AData: string; ACallback: TProc = nil); overload; + + /// + /// 发送碎片化数据 + /// + /// + /// 碎片化数据源 + /// + /// + /// 回调函数 + /// + /// + /// 回调函数说明: + /// + /// + /// 第一个参数: 待发送数据指针 + /// + /// + /// 第二个参数: 待发送数据长度 + /// + /// + /// 返回值: 数据是否有效 + /// + /// + /// 当待发送数据指针为nil或者长度小于等于0或者返回值为false时, 表示没有更多的数据需要发送了 + /// + /// + /// + procedure WsSend(const AData: TFunc; ACallback: TProc = nil); overload; + + /// + /// 发送流数据 + /// + /// + /// 流数据 + /// + /// + /// 偏移量 + /// + /// + /// 数据大小 + /// + /// + /// 回调函数 + /// + /// + /// 必须保证发送过程中流对象的有效性, 要释放流对象可以放到回调函数中进行 + /// + procedure WsSend(const AData: TStream; const AOffset, ACount: Int64; ACallback: TProc = nil); overload; + + /// + /// 发送流数据 + /// + /// + /// 流数据 + /// + /// + /// 回调函数 + /// + /// + /// 必须保证发送过程中流对象的有效性, 要释放流对象可以放到回调函数中进行 + /// + procedure WsSend(const AData: TStream; ACallback: TProc = nil); overload; + end; + + TWsRequestType = (wsrtUnknown, wsrtText, wsrtBinary); + TWsOnOpen = TProc; + TWsOnMessage = reference to procedure(AConnection: ICrossWebSocketConnection; + ARequestType: TWsRequestType; const ARequestData: TBytes); + TWsOnClose = TProc; + + /// + /// 跨平台WebSocket服务器 + /// + ICrossWebSocketServer = interface(ICrossHttpServer) + ['{FF008E22-9938-4DC4-9421-083DA9EFFCDC}'] + /// + /// WebSocket连接建立时触发 + /// + /// + /// 回调函数 + /// + function OnOpen(ACallback: TWsOnOpen): ICrossWebSocketServer; + + /// + /// 收到WebSocket消息时触发 + /// + /// + /// 回调函数 + /// + function OnMessage(ACallback: TWsOnMessage): ICrossWebSocketServer; + + /// + /// WebSocket连接关闭时触发 + /// + /// + /// hui'diaohanshu + /// + function OnClose(ACallback: TWsOnClose): ICrossWebSocketServer; + end; + + TCrossWebSocketConnection = class(TCrossHttpConnection, ICrossWebSocketConnection) + private type + TWsFrameParseState = (wsHeader, wsBody, wsDone); + private + FIsWebSocket: Boolean; + FWsFrameState: TWsFrameParseState; + FWsFrameHeader, FWsRequestBody: TBytesStream; + FWsFIN: Boolean; + FWsOpCode: Byte; + FWsMask: Boolean; + FWsMaskKey: Cardinal; + FWsMaskKeyShift: Integer; + FWsPayload: Byte; + FWsHeaderSize: Byte; + FWsBodySize: UInt64; + FWsSendClose: Integer; + + procedure _WebSocketRecv(ABuf: Pointer; ALen: Integer); + procedure _ResetFrameHeader; + procedure _ResetRequest; + + procedure _AdjustOffsetCount(const ABodySize: NativeInt; var AOffset, ACount: NativeInt); overload; + procedure _AdjustOffsetCount(const ABodySize: Int64; var AOffset, ACount: Int64); overload; + + {$region '内部发送方法'} + procedure _WsSend(AOpCode: Byte; AFin: Boolean; AData: Pointer; ACount: NativeInt; + ACallback: TProc = nil); overload; + + procedure _WsSend(AOpCode: Byte; AFin: Boolean; const AData: TBytes; AOffset, ACount: NativeInt; + ACallback: TProc = nil); overload; + + procedure _WsSend(AOpCode: Byte; AFin: Boolean; const AData: TBytes; + ACallback: TProc = nil); overload; + {$endregion} + + procedure _RespondPong(const AData: TBytes); + procedure _RespondClose; + + function _OpCodeToReqType(AOpCode: Byte): TWsRequestType; + protected + procedure TriggerWsRequest(ARequestType: TWsRequestType; + const ARequestData: TBytes); virtual; + public + constructor Create(AOwner: ICrossSocket; AClientSocket: THandle; + AConnectType: TConnectType); override; + destructor Destroy; override; + + class function _MakeFrameHeader(AOpCode: Byte; AFin: Boolean; AMaskKey: Cardinal; ADataSize: UInt64): TBytes; static; + + function IsWebSocket: Boolean; + procedure WsClose; + + procedure WsSend(const AData; ACount: NativeInt; ACallback: TProc = nil); overload; + procedure WsSend(const AData: TBytes; AOffset, ACount: NativeInt; ACallback: TProc = nil); overload; + procedure WsSend(const AData: TBytes; ACallback: TProc = nil); overload; + procedure WsSend(const AData: string; ACallback: TProc = nil); overload; + + procedure WsSend(const AData: TFunc; ACallback: TProc = nil); overload; + procedure WsSend(const AData: TStream; const AOffset, ACount: Int64; ACallback: TProc = nil); overload; + procedure WsSend(const AData: TStream; ACallback: TProc = nil); overload; + end; + + TNetCrossWebSocketServer = class(TCrossHttpServer, ICrossWebSocketServer) + private + FOnOpenEvents: TList; + FOnMessageEvents: TList; + FOnCloseEvents: TList; + + procedure _WebSocketHandshake(AConnection: ICrossWebSocketConnection; + ACallback: TProc); + + procedure _OnOpen(AConnection: ICrossWebSocketConnection); + procedure _OnMessage(AConnection: ICrossWebSocketConnection; + ARequestType: TWsRequestType; const ARequestData: TBytes); + procedure _OnClose(AConnection: ICrossWebSocketConnection); + protected + function CreateConnection(AOwner: ICrossSocket; AClientSocket: THandle; + AConnectType: TConnectType): ICrossConnection; override; + procedure LogicReceived(AConnection: ICrossConnection; ABuf: Pointer; ALen: Integer); override; + procedure LogicDisconnected(AConnection: ICrossConnection); override; + + procedure DoOnRequest(AConnection: ICrossHttpConnection); override; + + procedure TriggerWsRequest(AConnection: ICrossWebSocketConnection; + ARequestType: TWsRequestType; const ARequestData: TBytes); virtual; + public + constructor Create(AIoThreads: Integer); override; + destructor Destroy; override; + + function OnOpen(ACallback: TWsOnOpen): ICrossWebSocketServer; + function OnMessage(ACallback: TWsOnMessage): ICrossWebSocketServer; + function OnClose(ACallback: TWsOnClose): ICrossWebSocketServer; + end; + +implementation + +uses + System.StrUtils; + +{ TCrossWebSocketConnection } + +constructor TCrossWebSocketConnection.Create(AOwner: ICrossSocket; + AClientSocket: THandle; AConnectType: TConnectType); +begin + inherited; + + FWsFrameHeader := TBytesStream.Create(nil); + FWsRequestBody := TBytesStream.Create(nil); + _ResetRequest; +end; + +destructor TCrossWebSocketConnection.Destroy; +begin + FreeAndNil(FWsFrameHeader); + FreeAndNil(FWsRequestBody); + inherited; +end; + +function TCrossWebSocketConnection.IsWebSocket: Boolean; +begin + Result := FIsWebSocket; +end; + +procedure TCrossWebSocketConnection.TriggerWsRequest( + ARequestType: TWsRequestType; const ARequestData: TBytes); +begin + TNetCrossWebSocketServer(Owner).TriggerWsRequest(Self, ARequestType, ARequestData); +end; + +procedure TCrossWebSocketConnection.WsSend(const AData; ACount: NativeInt; + ACallback: TProc); +begin + _WsSend(WS_OP_BINARY, True, @AData, ACount, ACallback); +end; + +procedure TCrossWebSocketConnection.WsSend(const AData: TBytes; AOffset, + ACount: NativeInt; ACallback: TProc); +begin + _WsSend(WS_OP_BINARY, True, AData, AOffset, ACount, ACallback); +end; + +procedure TCrossWebSocketConnection.WsSend(const AData: TBytes; + ACallback: TProc); +begin + WsSend(AData, 0, Length(AData), ACallback); +end; + +procedure TCrossWebSocketConnection.WsSend(const AData: string; + ACallback: TProc); +begin + _WsSend(WS_OP_TEXT, True, TEncoding.UTF8.GetBytes(AData), ACallback); +end; + +procedure TCrossWebSocketConnection.WsSend( + const AData: TFunc; + ACallback: TProc); +var + LConnection: ICrossWebSocketConnection; + LOpCode: Byte; + LSender: TProc; +begin + LConnection := Self; + LOpCode := WS_OP_BINARY; + + LSender := + procedure(AConnection: ICrossWebSocketConnection; ASuccess: Boolean) + var + LData: Pointer; + LCount: NativeInt; + begin + if not ASuccess then + begin + if Assigned(ACallback) then + ACallback(AConnection, False); + + AConnection.Close; + + LSender := nil; + + Exit; + end; + + LData := nil; + LCount := 0; + if not Assigned(AData) + or not AData(@LData, @LCount) + or (LData = nil) + or (LCount <= 0) then + begin + LSender := nil; + + // 结束帧 + // opcode 为 WS_OP_CONTINUATION + // FIN 为 1 + // 结束帧只有一个头, 因为结束帧是流无数据可读时才生成的 + TCrossWebSocketConnection(AConnection)._WsSend(LOpCode, + True, nil, 0, + procedure(AConnection: ICrossWebSocketConnection; ASuccess: Boolean) + begin + if Assigned(ACallback) then + ACallback(AConnection, ASuccess); + end); + + Exit; + end; + + // 第一帧及中间帧 + // 第一帧 opcode 为 WS_OP_BINARY, FIN 为 0 + // 中间帧 opcode 为 WS_OP_CONTINUATION, FIN 为 0 + TCrossWebSocketConnection(AConnection)._WsSend(LOpCode, + False, LData, LCount, LSender); + + LOpCode := WS_OP_CONTINUATION; + end; + + LSender(LConnection, True); +end; + +procedure TCrossWebSocketConnection.WsSend(const AData: TStream; const AOffset, + ACount: Int64; ACallback: TProc); +var + LOffset, LCount: Int64; + LBody: TStream; + LBuffer: TBytes; +begin + LOffset := AOffset; + LCount := ACount; + _AdjustOffsetCount(AData.Size, LOffset, LCount); + + if (AData is TCustomMemoryStream) then + begin + WsSend(Pointer(IntPtr(TCustomMemoryStream(AData).Memory) + LOffset)^, LCount, ACallback); + Exit; + end; + + LBody := AData; + LBody.Position := LOffset; + + SetLength(LBuffer, SND_BUF_SIZE); + + WsSend( + // BODY + function(AData: PPointer; ACount: PNativeInt): Boolean + begin + if (LCount <= 0) then Exit(False); + + AData^ := @LBuffer[0]; + ACount^ := LBody.Read(LBuffer[0], Min(LCount, SND_BUF_SIZE)); + + Result := (ACount^ > 0); + + if Result then + Dec(LCount, ACount^); + end, + // CALLBACK + procedure(AConnection: ICrossWebSocketConnection; ASuccess: Boolean) + begin + LBuffer := nil; + + if Assigned(ACallback) then + ACallback(AConnection, ASuccess); + end); +end; + +procedure TCrossWebSocketConnection.WsSend(const AData: TStream; + ACallback: TProc); +begin + WsSend(AData, 0, 0, ACallback); +end; + +procedure TCrossWebSocketConnection.WsClose; +begin + if (AtomicExchange(FWsSendClose, 1) = 1) then Exit; + + _WsSend(WS_OP_CLOSE, True, nil, 0); +end; + +procedure TCrossWebSocketConnection._ResetRequest; +begin + FWsFrameState := wsHeader; + FWsFrameHeader.Clear; + FWsRequestBody.Clear; + FWsMaskKeyShift := 0; +end; + +procedure TCrossWebSocketConnection._AdjustOffsetCount( + const ABodySize: NativeInt; var AOffset, ACount: NativeInt); +begin + {$region '修正 AOffset'} + // 偏移为正数, 从头部开始计算偏移 + if (AOffset >= 0) then + begin + AOffset := AOffset; + if (AOffset >= ABodySize) then + AOffset := ABodySize - 1; + end else + // 偏移为负数, 从尾部开始计算偏移 + begin + AOffset := ABodySize + AOffset; + if (AOffset < 0) then + AOffset := 0; + end; + {$endregion} + + {$region '修正 ACount'} + // ACount<=0表示需要处理所有数据 + if (ACount <= 0) then + ACount := ABodySize; + + if (ABodySize - AOffset < ACount) then + ACount := ABodySize - AOffset; + {$endregion} +end; + +procedure TCrossWebSocketConnection._AdjustOffsetCount(const ABodySize: Int64; + var AOffset, ACount: Int64); +begin + {$region '修正 AOffset'} + // 偏移为正数, 从头部开始计算偏移 + if (AOffset >= 0) then + begin + AOffset := AOffset; + if (AOffset >= ABodySize) then + AOffset := ABodySize - 1; + end else + // 偏移为负数, 从尾部开始计算偏移 + begin + AOffset := ABodySize + AOffset; + if (AOffset < 0) then + AOffset := 0; + end; + {$endregion} + + {$region '修正 ACount'} + // ACount<=0表示需要处理所有数据 + if (ACount <= 0) then + ACount := ABodySize; + + if (ABodySize - AOffset < ACount) then + ACount := ABodySize - AOffset; + {$endregion} +end; + +class function TCrossWebSocketConnection._MakeFrameHeader(AOpCode: Byte; + AFin: Boolean; AMaskKey: Cardinal; ADataSize: UInt64): TBytes; +var + LPayload: Byte; + LHeaderSize: Integer; +begin + LHeaderSize := 2; + if (ADataSize < 126) then + LPayload := ADataSize + else if (ADataSize <= $FFFF) then + begin + LPayload := 126; + Inc(LHeaderSize, 2); + end else + begin + LPayload := 127; + Inc(LHeaderSize, 8); + end; + if (AMaskKey <> 0) then + Inc(LHeaderSize, 4); + + SetLength(Result, LHeaderSize); + FillChar(Result[0], LHeaderSize, 0); + + if AFin then + Result[0] := Result[0] or $80; + Result[0] := Result[0] or (AOpCode and $0F); + + if (AMaskKey <> 0) then + Result[1] := Result[1] or $80; + Result[1] := Result[1] or (LPayload and $7F); + + if (LPayload = 126) then + begin + Result[2] := PByte(@ADataSize)[1]; + Result[3] := PByte(@ADataSize)[0]; + end else + if (LPayload = 127) then + begin + Result[2] := PByte(@ADataSize)[7]; + Result[3] := PByte(@ADataSize)[6]; + Result[4] := PByte(@ADataSize)[5]; + Result[5] := PByte(@ADataSize)[4]; + Result[6] := PByte(@ADataSize)[3]; + Result[7] := PByte(@ADataSize)[2]; + Result[8] := PByte(@ADataSize)[1]; + Result[9] := PByte(@ADataSize)[0]; + end; + + if (AMaskKey <> 0) then + Move(AMaskKey, Result[LHeaderSize - 4], 4); +end; + +function TCrossWebSocketConnection._OpCodeToReqType( + AOpCode: Byte): TWsRequestType; +begin + case AOpCode of + WS_OP_TEXT: Exit(wsrtText); + WS_OP_BINARY: Exit(wsrtBinary); + else + Exit(wsrtUnknown); + end; +end; + +procedure TCrossWebSocketConnection._ResetFrameHeader; +begin + FWsFrameState := wsHeader; + FWsFrameHeader.Clear; + FWsMaskKeyShift := 0; +end; + +procedure TCrossWebSocketConnection._WebSocketRecv(ABuf: Pointer; + ALen: Integer); +var + PBuf: PByte; + LByte: Byte; + LReqData: TBytes; +begin + PBuf := ABuf; + while (ALen > 0) do + begin + // 使用循环处理粘包, 比递归调用节省资源 + while (ALen > 0) and (FWsFrameState <> wsDone) do + begin + case FWsFrameState of + wsHeader: + begin + FWsFrameHeader.Write(PBuf^, 1); + Dec(ALen); + Inc(PBuf); + + if (FWsFrameHeader.Size = 2) then + begin + // 第1个字节最高位为 FIN 状态 + FWsFIN := (FWsFrameHeader.Bytes[0] and $80 <> 0); + + // 第1个字节低4位为 opcode 状态 + LByte := FWsFrameHeader.Bytes[0] and $0F; + if (LByte <> WS_OP_CONTINUATION) then + FWsOpCode := LByte; + + // 第2个字节最高位为 MASK 状态 + FWsMask := (FWsFrameHeader.Bytes[1] and $80 <> 0); + + // 第2个字节低7位为 payload len + FWsPayload := FWsFrameHeader.Bytes[1] and $7F; + + FWsHeaderSize := 2; + if (FWsPayload < 126) then + FWsBodySize := FWsPayload + else if (FWsPayload = 126) then + Inc(FWsHeaderSize, 2) + else if (FWsPayload = 127) then + Inc(FWsHeaderSize, 8); + if FWsMask then + Inc(FWsHeaderSize, 4); + end else + if (FWsFrameHeader.Size = FWsHeaderSize) then + begin + FWsFrameState := wsBody; + + // 保存 mask key + if FWsMask then + Move(PCardinal(UIntPtr(FWsFrameHeader.Memory) + FWsHeaderSize - 4)^, FWsMaskKey, 4); + + if (FWsPayload = 126) then + FWsBodySize := FWsFrameHeader.Bytes[3] + + Word(FWsFrameHeader.Bytes[2]) shl 8 + else if (FWsPayload = 127) then + FWsBodySize := FWsFrameHeader.Bytes[9] + + UInt64(FWsFrameHeader.Bytes[8]) shl 8 + + UInt64(FWsFrameHeader.Bytes[7]) shl 16 + + UInt64(FWsFrameHeader.Bytes[6]) shl 24 + + UInt64(FWsFrameHeader.Bytes[5]) shl 32 + + UInt64(FWsFrameHeader.Bytes[4]) shl 40 + + UInt64(FWsFrameHeader.Bytes[3]) shl 48 + + UInt64(FWsFrameHeader.Bytes[2]) shl 56 + ; + + // 接收完一帧 + if (FWsBodySize <= 0) then + begin + // 如果这是一个独立帧或者连续帧的最后一帧 + // 则表示一次请求数据接收完成 + if FWsFIN then + begin + FWsFrameState := wsDone; + Break; + // 否则继续接收下一帧 + end else + _ResetFrameHeader; + end; + end; + end; + + wsBody: + begin + LByte := PBuf^; + // 如果 MASK 状态为 1, 则将收到的数据与 mask key 做异或处理 + if FWsMask then + begin + LByte := LByte xor PByte(@FWsMaskKey)[FWsMaskKeyShift]; + FWsMaskKeyShift := (FWsMaskKeyShift + 1) mod 4; + end; + FWsRequestBody.Write(LByte, 1); + Dec(ALen); + Inc(PBuf); + Dec(FWsBodySize); + + // 接收完一帧 + if (FWsBodySize <= 0) then + begin + // 如果这是一个独立帧或者连续帧的最后一帧 + // 则表示一次请求数据接收完成 + if FWsFIN then + begin + FWsFrameState := wsDone; + Break; + // 否则继续接收下一帧 + end else + _ResetFrameHeader; + end; + end; + end; + end; + + // 一个完整的 WebSocket 数据帧接收完毕 + if (FWsFrameState = wsDone) then + begin + case FWsOpCode of + WS_OP_CLOSE: + begin + // 关闭帧 + // 收到关闭帧, 如果已经发送关闭帧, 直接关闭连接 + // 否则, 需要发送关闭帧, 发送完成之后关闭连接 + _RespondClose; + Exit; + end; + + WS_OP_PING: + begin + // pong 帧必须将 ping 帧发来的数据原封不动地返回 + LReqData := FWsRequestBody.Bytes; + SetLength(LReqData, FWsRequestBody.Size); + _RespondPong(LReqData); + end; + + WS_OP_TEXT, WS_OP_BINARY: + begin + // 收到请求数据 + LReqData := FWsRequestBody.Bytes; + SetLength(LReqData, FWsRequestBody.Size); + TriggerWsRequest(_OpCodeToReqType(FWsOpCode), LReqData); + end; + end; + + _ResetRequest; + end; + end; +end; + +procedure TCrossWebSocketConnection._RespondClose; +begin + if (AtomicExchange(FWsSendClose, 1) = 1) then + Disconnect + else + begin + _WsSend(WS_OP_CLOSE, True, nil, 0, + procedure(AConnection: ICrossWebSocketConnection; ASuccess: Boolean) + begin + AConnection.Disconnect; + end); + end; +end; + +procedure TCrossWebSocketConnection._RespondPong(const AData: TBytes); +begin + _WsSend(WS_OP_PONG, True, AData); +end; + +procedure TCrossWebSocketConnection._WsSend(AOpCode: Byte; AFin: Boolean; + AData: Pointer; ACount: NativeInt; + ACallback: TProc); +var + LWsFrameHeader: TBytes; +begin + LWsFrameHeader := _MakeFrameHeader(AOpCode, AFin, 0, ACount); + inherited SendBytes(LWsFrameHeader, + procedure(AConnection: ICrossConnection; ASuccess: Boolean) + begin + if not ASuccess then + begin + if Assigned(ACallback) then + ACallback(AConnection as ICrossWebSocketConnection, ASuccess); + Exit; + end; + + if (AData = nil) or (ACount <= 0) then + begin + if Assigned(ACallback) then + ACallback(AConnection as ICrossWebSocketConnection, ASuccess); + Exit; + end; + + inherited SendBuf(AData^, ACount, + procedure(AConnection: ICrossConnection; ASuccess: Boolean) + begin + if Assigned(ACallback) then + ACallback(AConnection as ICrossWebSocketConnection, ASuccess); + end); + end); +end; + +procedure TCrossWebSocketConnection._WsSend(AOpCode: Byte; AFin: Boolean; + const AData: TBytes; AOffset, ACount: NativeInt; + ACallback: TProc); +var + LData: TBytes; + LOffset, LCount: NativeInt; +begin + LData := AData; + LOffset := AOffset; + LCount := ACount; + _AdjustOffsetCount(Length(AData), LOffset, LCount); + + _WsSend(AOpCode, AFin, @LData[LOffset], LCount, + procedure(AConnection: ICrossWebSocketConnection; ASuccess: Boolean) + begin + LData := nil; + if Assigned(ACallback) then + ACallback(AConnection as ICrossWebSocketConnection, ASuccess); + end); +end; + +procedure TCrossWebSocketConnection._WsSend(AOpCode: Byte; AFin: Boolean; + const AData: TBytes; ACallback: TProc); +begin + _WsSend(AOpCode, AFin, AData, 0, Length(AData), ACallback); +end; + +{ TNetCrossWebSocketServer } + +constructor TNetCrossWebSocketServer.Create(AIoThreads: Integer); +begin + inherited; + + FOnOpenEvents := TList.Create; + FOnMessageEvents := TList.Create; + FOnCloseEvents := TList.Create; +end; + +destructor TNetCrossWebSocketServer.Destroy; +begin + FreeAndNil(FOnOpenEvents); + FreeAndNil(FOnMessageEvents); + FreeAndNil(FOnCloseEvents); + + inherited; +end; + +procedure TNetCrossWebSocketServer.LogicDisconnected( + AConnection: ICrossConnection); +var + LConnection: ICrossWebSocketConnection; +begin + LConnection := AConnection as ICrossWebSocketConnection; + if LConnection.IsWebSocket then + _OnClose(LConnection) + else + inherited; +end; + +procedure TNetCrossWebSocketServer.LogicReceived(AConnection: ICrossConnection; + ABuf: Pointer; ALen: Integer); +var + LConnection: ICrossWebSocketConnection; +begin + LConnection := AConnection as ICrossWebSocketConnection; + if LConnection.IsWebSocket then + TCrossWebSocketConnection(LConnection)._WebSocketRecv(ABuf, ALen) + else + inherited; +end; + +function TNetCrossWebSocketServer.OnClose(ACallback: TWsOnClose): ICrossWebSocketServer; +begin + System.TMonitor.Enter(FOnCloseEvents); + try + FOnCloseEvents.Add(ACallback); + finally + System.TMonitor.Exit(FOnCloseEvents); + end; + + Result := Self; +end; + +function TNetCrossWebSocketServer.OnMessage(ACallback: TWsOnMessage): ICrossWebSocketServer; +begin + System.TMonitor.Enter(FOnMessageEvents); + try + FOnMessageEvents.Add(ACallback); + finally + System.TMonitor.Exit(FOnMessageEvents); + end; + + Result := Self; +end; + +function TNetCrossWebSocketServer.OnOpen(ACallback: TWsOnOpen): ICrossWebSocketServer; +begin + System.TMonitor.Enter(FOnOpenEvents); + try + FOnOpenEvents.Add(ACallback); + finally + System.TMonitor.Exit(FOnOpenEvents); + end; + + Result := Self; +end; + +procedure TNetCrossWebSocketServer.TriggerWsRequest( + AConnection: ICrossWebSocketConnection; ARequestType: TWsRequestType; + const ARequestData: TBytes); +begin + _OnMessage(AConnection, ARequestType, ARequestData); +end; + +procedure TNetCrossWebSocketServer._OnClose( + AConnection: ICrossWebSocketConnection); +var + LOnCloseEvents: TArray; + LOnCloseEvent: TWsOnClose; +begin + System.TMonitor.Enter(FOnCloseEvents); + try + LOnCloseEvents := FOnCloseEvents.ToArray; + finally + System.TMonitor.Exit(FOnCloseEvents); + end; + + for LOnCloseEvent in LOnCloseEvents do + if Assigned(LOnCloseEvent) then + LOnCloseEvent(AConnection); +end; + +procedure TNetCrossWebSocketServer._OnMessage( + AConnection: ICrossWebSocketConnection; ARequestType: TWsRequestType; + const ARequestData: TBytes); +var + LOnMessageEvents: TArray; + LOnMessageEvent: TWsOnMessage; +begin + System.TMonitor.Enter(FOnMessageEvents); + try + LOnMessageEvents := FOnMessageEvents.ToArray; + finally + System.TMonitor.Exit(FOnMessageEvents); + end; + + for LOnMessageEvent in LOnMessageEvents do + if Assigned(LOnMessageEvent) then + LOnMessageEvent(AConnection, ARequestType, ARequestData); +end; + +procedure TNetCrossWebSocketServer._OnOpen( + AConnection: ICrossWebSocketConnection); +var + LOnOpenEvents: TArray; + LOnOpenEvent: TWsOnOpen; +begin + System.TMonitor.Enter(FOnOpenEvents); + try + LOnOpenEvents := FOnOpenEvents.ToArray; + finally + System.TMonitor.Exit(FOnOpenEvents); + end; + + for LOnOpenEvent in LOnOpenEvents do + if Assigned(LOnOpenEvent) then + LOnOpenEvent(AConnection); +end; + +procedure TNetCrossWebSocketServer._WebSocketHandshake( + AConnection: ICrossWebSocketConnection; + ACallback: TProc); +begin + AConnection.Response.Header['Upgrade'] := 'websocket'; + AConnection.Response.Header['Connection'] := 'Upgrade'; + AConnection.Response.Header['Sec-WebSocket-Accept'] := + TNetEncoding.Base64.EncodeBytesToString( + THashSHA1.GetHashBytes( + AConnection.Request.Header['Sec-WebSocket-Key'] + WS_MAGIC_STR + ) + ); + AConnection.Response.SendStatus(101, '', + procedure(AConnection: ICrossConnection; ASuccess: Boolean) + begin + ACallback(AConnection as ICrossWebSocketConnection, ASuccess); + end); +end; + +function TNetCrossWebSocketServer.CreateConnection(AOwner: ICrossSocket; + AClientSocket: THandle; AConnectType: TConnectType): ICrossConnection; +begin + Result := TCrossWebSocketConnection.Create(AOwner, AClientSocket, AConnectType); +end; + +procedure TNetCrossWebSocketServer.DoOnRequest( + AConnection: ICrossHttpConnection); +var + LConnection: ICrossWebSocketConnection; +begin + LConnection := AConnection as ICrossWebSocketConnection; + if ContainsText(AConnection.Request.Header['Connection'], 'Upgrade') + and ContainsText(AConnection.Request.Header['Upgrade'], 'websocket') then + begin + TCrossWebSocketConnection(LConnection).FIsWebSocket := True; + _WebSocketHandshake(LConnection, + procedure(AConnection: ICrossWebSocketConnection; ASuccess: Boolean) + begin + if ASuccess then + _OnOpen(AConnection); + end); + end else + inherited; +end; + +end. diff --git a/ThirdParty/DCS/Net/Net.MbedBIO.pas b/ThirdParty/DCS/Net/Net.MbedBIO.pas new file mode 100644 index 00000000..2607ff65 --- /dev/null +++ b/ThirdParty/DCS/Net/Net.MbedBIO.pas @@ -0,0 +1,390 @@ +{******************************************************************************} +{ } +{ Delphi cross platform socket library } +{ } +{ Copyright (c) 2017 WiNDDRiVER(soulawing@gmail.com) } +{ } +{ Homepage: https://github.com/winddriver/Delphi-Cross-Socket } +{ } +{******************************************************************************} +unit Net.MbedBIO; + +interface + +uses + System.SysUtils, + System.IOUtils, + Net.MbedTls; + +const + SSL_BIO_ERROR = -1; + SSL_BIO_UNSET = -2; + SSL_BIO_SIZE = 16384; // default BIO write size if not set + + SSL_FAILED = -1; + SSL_SUCCESS = 0; + + // BIO_TYPE + BIO_BUFFER = 1; + BIO_SOCKET = 2; + BIO_SSL = 3; + BIO_MEMORY = 4; + BIO_BIO = 5; + BIO_FILE = 6; + +type + PBIO = ^BIO; + + BIO = record + prev: PBIO; // previous in chain + next: PBIO; // next in chain + pair: PBIO; // BIO paired with + mem: PByte; // memory buffer + wrSz: Integer; // write buffer size (mem) + wrIdx: Integer; // current index for write buffer + rdIdx: Integer; // current read index + readRq: Integer; // read request + memLen: Integer; // memory buffer length + &type: Integer; // method type + end; + + // 抽象 IO API +function SSL_BIO_new(&type: Integer): PBIO; +function BIO_make_bio_pair(b1, b2: PBIO): Integer; + +function BIO_ctrl_pending(BIO: PBIO): Integer; +function BIO_set_write_buf_size(BIO: PBIO; size: Integer): Integer; + +function BIO_read(BIO: PBIO; buf: Pointer; size: Integer): Integer; +function BIO_write(BIO: PBIO; buf: Pointer; size: Integer): Integer; + +function BIO_free(BIO: PBIO): Integer; +function BIO_free_all(BIO: PBIO): Integer; + +function BIO_net_recv(ctx: Pointer; buf: Pointer; size: Size_T): Integer; cdecl; +function BIO_net_send(ctx: Pointer; buf: Pointer; size: Size_T): Integer; cdecl; + +implementation + +// Return the number of pending bytes in read and write buffers +function BIO_ctrl_pending(BIO: PBIO): Integer; +var + pair: PBIO; +begin + if (BIO = nil) then + Exit(0); + + if (BIO.&type = BIO_MEMORY) then + Exit(BIO.memLen); + + // type BIO_BIO then check paired buffer + if (BIO.&type = BIO_BIO) and (BIO.pair <> nil) then + begin + pair := BIO.pair; + if (pair.wrIdx > 0) and (pair.wrIdx <= pair.rdIdx) then + // in wrap around state where begining of buffer is being overwritten + Exit(pair.wrSz - pair.rdIdx + pair.wrIdx) + else + // simple case where has not wrapped around + Exit(pair.wrIdx - pair.rdIdx); + end; + + Result := 0; +end; + +function BIO_set_write_buf_size(BIO: PBIO; size: Integer): Integer; +begin + if (BIO = nil) or (BIO.&type <> BIO_BIO) or (size < 0) then + Exit(SSL_FAILED); + + // if already in pair then do not change size + if BIO.pair <> nil then + Exit(SSL_FAILED); + + BIO.wrSz := size; + if (BIO.wrSz < 0) then + Exit(SSL_FAILED); + + if (BIO.mem <> nil) then + FreeMem(BIO.mem); + + GetMem(BIO.mem, BIO.wrSz); + if (BIO.mem = nil) then + Exit(SSL_FAILED); + + BIO.wrIdx := 0; + BIO.rdIdx := 0; + Result := SSL_SUCCESS; +end; + +{ * Joins two BIO_BIO types. The write of b1 goes to the read of b2 and vise + * versa. Creating something similar to a two way pipe. + * Reading and writing between the two BIOs is not thread safe, they are + * expected to be used by the same thread. + * } +function BIO_make_bio_pair(b1, b2: PBIO): Integer; +begin + if (b1 = nil) or (b2 = nil) then + Exit(SSL_FAILED); + + // both are expected to be of type BIO and not already paired + if (b1.&type <> BIO_BIO) + or (b2.&type <> BIO_BIO) or (b1.pair <> nil) or (b2.pair <> nil) then + Exit(SSL_FAILED); + + // set default write size if not already set + if (b1.mem = nil) + and (BIO_set_write_buf_size(b1, SSL_BIO_SIZE) <> SSL_SUCCESS) then + Exit(SSL_FAILED); + + if (b2.mem = nil) + and (BIO_set_write_buf_size(b2, SSL_BIO_SIZE) <> SSL_SUCCESS) then + Exit(SSL_FAILED); + + b1.pair := b2; + b2.pair := b1; + Result := SSL_SUCCESS; +end; + +// Does not advance read index pointer +function BIO_nread0(BIO: PBIO; buf: PPointer): Integer; +var + pair: PBIO; +begin + if (BIO = nil) or (buf = nil) then + Exit(0); + + // if paired read from pair + if (BIO.pair <> nil) then + begin + pair := BIO.pair; + + // case where have wrapped around write buffer + buf^ := pair.mem + pair.rdIdx; + if (pair.wrIdx > 0) and (pair.rdIdx >= pair.wrIdx) then + Exit(pair.wrSz - pair.rdIdx) + else + Exit(pair.wrIdx - pair.rdIdx); + end; + + Result := 0; +end; + +function BIO_nread(BIO: PBIO; buf: PPointer; num: Integer): Integer; +var + sz: Integer; +begin + sz := SSL_BIO_UNSET; + if (BIO = nil) or (buf = nil) then + Exit(SSL_FAILED); + + if (BIO.pair <> nil) then + begin + // special case if asking to read 0 bytes + if (num = 0) then + begin + buf^ := BIO.pair.mem + BIO.pair.rdIdx; + Exit(0); + end; + + // get amount able to read and set buffer pointer + sz := BIO_nread0(BIO, buf); + if (sz = 0) then + Exit(SSL_BIO_ERROR); + + if (num < sz) then + sz := num; + + BIO.pair.rdIdx := BIO.pair.rdIdx + sz; + + // check if have read to the end of the buffer and need to reset + if (BIO.pair.rdIdx = BIO.pair.wrSz) then + begin + BIO.pair.rdIdx := 0; + if (BIO.pair.wrIdx = BIO.pair.wrSz) then + BIO.pair.wrIdx := 0; + end; + + // check if read up to write index, if so then reset indexs + if (BIO.pair.rdIdx = BIO.pair.wrIdx) then + begin + BIO.pair.rdIdx := 0; + BIO.pair.wrIdx := 0; + end; + end; + + Result := sz; +end; + +function BIO_nwrite(BIO: PBIO; buf: PPointer; num: Integer): Integer; +var + sz: Integer; +begin + sz := SSL_BIO_UNSET; + if (BIO = nil) or (buf = nil) then + Exit(0); + + if (BIO.pair <> nil) then + begin + if num = 0 then + begin + buf^ := BIO.mem + BIO.wrIdx; + Exit(0); + end; + if (BIO.wrIdx < BIO.rdIdx) then + // if wrapped around only write up to read index. In this case + // rdIdx is always greater then wrIdx so sz will not be negative. + sz := BIO.rdIdx - BIO.wrIdx + else if (BIO.rdIdx > 0) and (BIO.wrIdx = BIO.rdIdx) then + Exit(SSL_BIO_ERROR) // no more room to write + else + begin + // write index is past read index so write to end of buffer + sz := BIO.wrSz - BIO.wrIdx; + if (sz <= 0) then + begin + // either an error has occured with write index or it is at the + // end of the write buffer. + if (BIO.rdIdx = 0) then + // no more room, nothing has been read + Exit(SSL_BIO_ERROR); + + BIO.wrIdx := 0; + + // check case where read index is not at 0 + if (BIO.rdIdx > 0) then + sz := BIO.rdIdx // can write up to the read index + else + sz := BIO.wrSz; // no restriction other then buffer size + end; + end; + + if (num < sz) then + sz := num; + + buf^ := BIO.mem + BIO.wrIdx; + BIO.wrIdx := BIO.wrIdx + sz; + + // if at the end of the buffer and space for wrap around then set + // write index back to 0 + if (BIO.wrIdx = BIO.wrSz) and (BIO.rdIdx > 0) then + BIO.wrIdx := 0; + end; + + Result := sz; +end; + +// Reset BIO to initial state +function BIO_reset(BIO: PBIO): Integer; +begin + if (BIO = nil) then + // -1 is consistent failure even for FILE type + Exit(SSL_BIO_ERROR); + + case BIO.&type of + BIO_BIO: + begin + BIO.rdIdx := 0; + BIO.wrIdx := 0; + Exit(0); + end; + end; + + Result := SSL_BIO_ERROR; +end; + +function BIO_read(BIO: PBIO; buf: Pointer; size: Integer): Integer; +var + sz: Integer; + pt: PPointer; +begin + sz := BIO_nread(BIO, @pt, size); + if (sz > 0) then + Move(pt^, buf^, sz); + + Result := sz; +end; + +function BIO_write(BIO: PBIO; buf: Pointer; size: Integer): Integer; +var + sz: Integer; + data: Pointer; +begin + // internal function where arguments have already been sanity checked + sz := BIO_nwrite(BIO, @data, size); + + // test space for write + if (sz <= 0) then + Exit(sz); + + Move(buf^, data^, sz); + Result := sz; +end; + +// support bio type only +function SSL_BIO_new(&type: Integer): PBIO; +begin + GetMem(Result, SizeOf(BIO)); + FillChar(Result^, SizeOf(BIO), 0); + Result.&type := &type; +end; + +function BIO_free(BIO: PBIO): Integer; +begin + // unchain?, doesn't matter in goahead since from free all + if (BIO <> nil) then + begin + // remove from pair by setting the paired bios pair to nil + if (BIO.pair <> nil) then + BIO.pair.pair := nil; + + if (BIO.mem <> nil) then + FreeMem(BIO.mem); + + FreeMem(BIO); + end; + + Result := 0; +end; + +function BIO_free_all(BIO: PBIO): Integer; +var + next: PBIO; +begin + while (BIO <> nil) do + begin + next := BIO.next; + BIO_free(BIO); + BIO := next; + end; + + Result := 0; +end; + +function BIO_net_send(ctx: Pointer; buf: Pointer; size: Size_T): Integer; +var + BIO: PBIO; + sz: Integer; +begin + BIO := PBIO(ctx); + sz := BIO_write(BIO, buf, size); + if (sz <= 0) then + Exit(MBEDTLS_ERR_SSL_WANT_WRITE); + + Result := sz; +end; + +function BIO_net_recv(ctx: Pointer; buf: Pointer; size: Size_T): Integer; +var + BIO: PBIO; + sz: Integer; +begin + BIO := PBIO(ctx); + sz := BIO_read(BIO, buf, size); + if (sz <= 0) then + Exit(MBEDTLS_ERR_SSL_WANT_READ); + + Result := sz; +end; + +end. diff --git a/ThirdParty/DCS/Net/Net.MbedTls.pas b/ThirdParty/DCS/Net/Net.MbedTls.pas new file mode 100644 index 00000000..168acc55 --- /dev/null +++ b/ThirdParty/DCS/Net/Net.MbedTls.pas @@ -0,0 +1,1641 @@ +{ ****************************************************************************** } +{ } +{ Delphi cross platform socket library } +{ } +{ Copyright (c) 2017 WiNDDRiVER(soulawing@gmail.com) } +{ } +{ Homepage: https://github.com/winddriver/Delphi-Cross-Socket } +{ } +{ ****************************************************************************** } +unit Net.MbedTls; + +{ + MbedTls 库编译说明 + + > Windows + 用 C++ Builder 打开 mbedtls.cbproj 工程文件 + Win32编译时一定要设置 Use 'classic' Borland compiler 为 true + Win64编译的 Release 目标文件链接到 Delphi 中时, aes.o 会报告错误的文件格式 + 使用 Debug 版本的 aes.o 代替则不会报错, 测试了 C++ Builder 10.2.3 和 10.3 生成的 aes.o 文件均有该问题 + + > Linux iOS Android + 使用 Make 命令进行编译 +} + +interface + +uses +{$IFDEF MSWINDOWS} + Winapi.Windows, + System.Win.Crtl, +{$ENDIF} + System.SysUtils; + +// C 语言中枚举类型大小为 4 字节 +{$Z4} + +const +{$IF defined(WIN32)} + _PU = '_'; +{$ELSE} + _PU = ''; +{$ENDIF} + +{$IF defined(WIN32)} + {$DEFINE __HAS_MBED_TLS_OBJ__} +{$ELSEIF defined(WIN64)} + {$DEFINE __HAS_MBED_TLS_O__} +{$ELSE} // POSIX + {$DEFINE __HAS_MBED_TLS_LIB__} +{$ENDIF} + +{$IFDEF __HAS_MBED_TLS_LIB__} + {$IF defined(IOS) or defined(ANDROID)} + LIB_MBED_CRYPTO = 'libmbedtls.a'; + LIB_MBED_TLS = 'libmbedtls.a'; + LIB_MBED_X509 = 'libmbedtls.a'; + {$ELSEIF defined(OSX)} + LIB_MBED_CRYPTO = 'libmbedcrypto.dylib'; + LIB_MBED_TLS = 'libmbedtls.dylib'; + LIB_MBED_X509 = 'libmbedx509.dylib'; + {$ELSE} // LINUX + LIB_MBED_CRYPTO = 'libmbedcrypto.so'; + LIB_MBED_TLS = 'libmbedtls.so'; + LIB_MBED_X509 = 'libmbedx509.so'; + {$ENDIF} +{$ENDIF} + +{$REGION 'MbedTls定义'} +const + MBEDTLS_SSL_VERIFY_DATA_MAX_LEN = 36; + MBEDTLS_ENTROPY_MAX_SOURCES = 20; // *< Maximum number of sources supported + MBEDTLS_HAVEGE_COLLECT_SIZE = 1024; + DEBUG_LEVEL = 0; + + MBEDTLS_TLS_RSA_WITH_NULL_MD5 = $01; + MBEDTLS_TLS_RSA_WITH_NULL_SHA = $02; + MBEDTLS_TLS_RSA_WITH_RC4_128_MD5 = $04; + MBEDTLS_TLS_RSA_WITH_RC4_128_SHA = $05; + MBEDTLS_TLS_RSA_WITH_DES_CBC_SHA = $09; + MBEDTLS_TLS_RSA_WITH_3DES_EDE_CBC_SHA = $0A; + MBEDTLS_TLS_DHE_RSA_WITH_DES_CBC_SHA = $15; + MBEDTLS_TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA = $16; + MBEDTLS_TLS_PSK_WITH_NULL_SHA = $2C; + MBEDTLS_TLS_DHE_PSK_WITH_NULL_SHA = $2D; + MBEDTLS_TLS_RSA_PSK_WITH_NULL_SHA = $2E; + MBEDTLS_TLS_RSA_WITH_AES_128_CBC_SHA = $2F; + MBEDTLS_TLS_DHE_RSA_WITH_AES_128_CBC_SHA = $33; + MBEDTLS_TLS_RSA_WITH_AES_256_CBC_SHA = $35; + MBEDTLS_TLS_DHE_RSA_WITH_AES_256_CBC_SHA = $39; + MBEDTLS_TLS_RSA_WITH_NULL_SHA256 = $3B; + MBEDTLS_TLS_RSA_WITH_AES_128_CBC_SHA256 = $3C; + MBEDTLS_TLS_RSA_WITH_AES_256_CBC_SHA256 = $3D; + MBEDTLS_TLS_RSA_WITH_CAMELLIA_128_CBC_SHA = $41; + MBEDTLS_TLS_DHE_RSA_WITH_CAMELLIA_128_CBC_SHA = $45; + MBEDTLS_TLS_DHE_RSA_WITH_AES_128_CBC_SHA256 = $67; + MBEDTLS_TLS_DHE_RSA_WITH_AES_256_CBC_SHA256 = $6B; + MBEDTLS_TLS_RSA_WITH_CAMELLIA_256_CBC_SHA = $84; + MBEDTLS_TLS_DHE_RSA_WITH_CAMELLIA_256_CBC_SHA = $88; + MBEDTLS_TLS_PSK_WITH_RC4_128_SHA = $8A; + MBEDTLS_TLS_PSK_WITH_3DES_EDE_CBC_SHA = $8B; + MBEDTLS_TLS_PSK_WITH_AES_128_CBC_SHA = $8C; + MBEDTLS_TLS_PSK_WITH_AES_256_CBC_SHA = $8D; + MBEDTLS_TLS_DHE_PSK_WITH_RC4_128_SHA = $8E; + MBEDTLS_TLS_DHE_PSK_WITH_3DES_EDE_CBC_SHA = $8F; + MBEDTLS_TLS_DHE_PSK_WITH_AES_128_CBC_SHA = $90; + MBEDTLS_TLS_DHE_PSK_WITH_AES_256_CBC_SHA = $91; + MBEDTLS_TLS_RSA_PSK_WITH_RC4_128_SHA = $92; + MBEDTLS_TLS_RSA_PSK_WITH_3DES_EDE_CBC_SHA = $93; + MBEDTLS_TLS_RSA_PSK_WITH_AES_128_CBC_SHA = $94; + MBEDTLS_TLS_RSA_PSK_WITH_AES_256_CBC_SHA = $95; + MBEDTLS_TLS_RSA_WITH_AES_128_GCM_SHA256 = $9C; + MBEDTLS_TLS_RSA_WITH_AES_256_GCM_SHA384 = $9D; + MBEDTLS_TLS_DHE_RSA_WITH_AES_128_GCM_SHA256 = $9E; + MBEDTLS_TLS_DHE_RSA_WITH_AES_256_GCM_SHA384 = $9F; + MBEDTLS_TLS_PSK_WITH_AES_128_GCM_SHA256 = $A8; + MBEDTLS_TLS_PSK_WITH_AES_256_GCM_SHA384 = $A9; + MBEDTLS_TLS_DHE_PSK_WITH_AES_128_GCM_SHA256 = $AA; + MBEDTLS_TLS_DHE_PSK_WITH_AES_256_GCM_SHA384 = $AB; + MBEDTLS_TLS_RSA_PSK_WITH_AES_128_GCM_SHA256 = $AC; + MBEDTLS_TLS_RSA_PSK_WITH_AES_256_GCM_SHA384 = $AD; + MBEDTLS_TLS_PSK_WITH_AES_128_CBC_SHA256 = $AE; + MBEDTLS_TLS_PSK_WITH_AES_256_CBC_SHA384 = $AF; + MBEDTLS_TLS_PSK_WITH_NULL_SHA256 = $B0; + MBEDTLS_TLS_PSK_WITH_NULL_SHA384 = $B1; + MBEDTLS_TLS_DHE_PSK_WITH_AES_128_CBC_SHA256 = $B2; + MBEDTLS_TLS_DHE_PSK_WITH_AES_256_CBC_SHA384 = $B3; + MBEDTLS_TLS_DHE_PSK_WITH_NULL_SHA256 = $B4; + MBEDTLS_TLS_DHE_PSK_WITH_NULL_SHA384 = $B5; + MBEDTLS_TLS_RSA_PSK_WITH_AES_128_CBC_SHA256 = $B6; + MBEDTLS_TLS_RSA_PSK_WITH_AES_256_CBC_SHA384 = $B7; + MBEDTLS_TLS_RSA_PSK_WITH_NULL_SHA256 = $B8; + MBEDTLS_TLS_RSA_PSK_WITH_NULL_SHA384 = $B9; + MBEDTLS_TLS_RSA_WITH_CAMELLIA_128_CBC_SHA256 = $BA; + MBEDTLS_TLS_DHE_RSA_WITH_CAMELLIA_128_CBC_SHA256 = $BE; + MBEDTLS_TLS_RSA_WITH_CAMELLIA_256_CBC_SHA256 = $C0; + MBEDTLS_TLS_DHE_RSA_WITH_CAMELLIA_256_CBC_SHA256 = $C4; + MBEDTLS_TLS_ECDH_ECDSA_WITH_NULL_SHA = $C001; + MBEDTLS_TLS_ECDH_ECDSA_WITH_RC4_128_SHA = $C002; + MBEDTLS_TLS_ECDH_ECDSA_WITH_3DES_EDE_CBC_SHA = $C003; + MBEDTLS_TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA = $C004; + MBEDTLS_TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA = $C005; + MBEDTLS_TLS_ECDHE_ECDSA_WITH_NULL_SHA = $C006; + MBEDTLS_TLS_ECDHE_ECDSA_WITH_RC4_128_SHA = $C007; + MBEDTLS_TLS_ECDHE_ECDSA_WITH_3DES_EDE_CBC_SHA = $C008; + MBEDTLS_TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA = $C009; + MBEDTLS_TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA = $C00A; + MBEDTLS_TLS_ECDH_RSA_WITH_NULL_SHA = $C00B; + MBEDTLS_TLS_ECDH_RSA_WITH_RC4_128_SHA = $C00C; + MBEDTLS_TLS_ECDH_RSA_WITH_3DES_EDE_CBC_SHA = $C00D; + MBEDTLS_TLS_ECDH_RSA_WITH_AES_128_CBC_SHA = $C00E; + MBEDTLS_TLS_ECDH_RSA_WITH_AES_256_CBC_SHA = $C00F; + MBEDTLS_TLS_ECDHE_RSA_WITH_NULL_SHA = $C010; + MBEDTLS_TLS_ECDHE_RSA_WITH_RC4_128_SHA = $C011; + MBEDTLS_TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA = $C012; + MBEDTLS_TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA = $C013; + MBEDTLS_TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA = $C014; + MBEDTLS_TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256 = $C023; + MBEDTLS_TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384 = $C024; + MBEDTLS_TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA256 = $C025; + MBEDTLS_TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA384 = $C026; + MBEDTLS_TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256 = $C027; + MBEDTLS_TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384 = $C028; + MBEDTLS_TLS_ECDH_RSA_WITH_AES_128_CBC_SHA256 = $C029; + MBEDTLS_TLS_ECDH_RSA_WITH_AES_256_CBC_SHA384 = $C02A; + MBEDTLS_TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256 = $C02B; + MBEDTLS_TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384 = $C02C; + MBEDTLS_TLS_ECDH_ECDSA_WITH_AES_128_GCM_SHA256 = $C02D; + MBEDTLS_TLS_ECDH_ECDSA_WITH_AES_256_GCM_SHA384 = $C02E; + MBEDTLS_TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256 = $C02F; + MBEDTLS_TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 = $C030; + MBEDTLS_TLS_ECDH_RSA_WITH_AES_128_GCM_SHA256 = $C031; + MBEDTLS_TLS_ECDH_RSA_WITH_AES_256_GCM_SHA384 = $C032; + MBEDTLS_TLS_ECDHE_PSK_WITH_RC4_128_SHA = $C033; + MBEDTLS_TLS_ECDHE_PSK_WITH_3DES_EDE_CBC_SHA = $C034; + MBEDTLS_TLS_ECDHE_PSK_WITH_AES_128_CBC_SHA = $C035; + MBEDTLS_TLS_ECDHE_PSK_WITH_AES_256_CBC_SHA = $C036; + MBEDTLS_TLS_ECDHE_PSK_WITH_AES_128_CBC_SHA256 = $C037; + MBEDTLS_TLS_ECDHE_PSK_WITH_AES_256_CBC_SHA384 = $C038; + MBEDTLS_TLS_ECDHE_PSK_WITH_NULL_SHA = $C039; + MBEDTLS_TLS_ECDHE_PSK_WITH_NULL_SHA256 = $C03A; + MBEDTLS_TLS_ECDHE_PSK_WITH_NULL_SHA384 = $C03B; + MBEDTLS_TLS_RSA_WITH_ARIA_128_CBC_SHA256 = $C03C; + MBEDTLS_TLS_RSA_WITH_ARIA_256_CBC_SHA384 = $C03D; + MBEDTLS_TLS_DHE_RSA_WITH_ARIA_128_CBC_SHA256 = $C044; + MBEDTLS_TLS_DHE_RSA_WITH_ARIA_256_CBC_SHA384 = $C045; + MBEDTLS_TLS_ECDHE_ECDSA_WITH_ARIA_128_CBC_SHA256 = $C048; + MBEDTLS_TLS_ECDHE_ECDSA_WITH_ARIA_256_CBC_SHA384 = $C049; + MBEDTLS_TLS_ECDH_ECDSA_WITH_ARIA_128_CBC_SHA256 = $C04A; + MBEDTLS_TLS_ECDH_ECDSA_WITH_ARIA_256_CBC_SHA384 = $C04B; + MBEDTLS_TLS_ECDHE_RSA_WITH_ARIA_128_CBC_SHA256 = $C04C; + MBEDTLS_TLS_ECDHE_RSA_WITH_ARIA_256_CBC_SHA384 = $C04D; + MBEDTLS_TLS_ECDH_RSA_WITH_ARIA_128_CBC_SHA256 = $C04E; + MBEDTLS_TLS_ECDH_RSA_WITH_ARIA_256_CBC_SHA384 = $C04F; + MBEDTLS_TLS_RSA_WITH_ARIA_128_GCM_SHA256 = $C050; + MBEDTLS_TLS_RSA_WITH_ARIA_256_GCM_SHA384 = $C051; + MBEDTLS_TLS_DHE_RSA_WITH_ARIA_128_GCM_SHA256 = $C052; + MBEDTLS_TLS_DHE_RSA_WITH_ARIA_256_GCM_SHA384 = $C053; + MBEDTLS_TLS_ECDHE_ECDSA_WITH_ARIA_128_GCM_SHA256 = $C05C; + MBEDTLS_TLS_ECDHE_ECDSA_WITH_ARIA_256_GCM_SHA384 = $C05D; + MBEDTLS_TLS_ECDH_ECDSA_WITH_ARIA_128_GCM_SHA256 = $C05E; + MBEDTLS_TLS_ECDH_ECDSA_WITH_ARIA_256_GCM_SHA384 = $C05F; + MBEDTLS_TLS_ECDHE_RSA_WITH_ARIA_128_GCM_SHA256 = $C060; + MBEDTLS_TLS_ECDHE_RSA_WITH_ARIA_256_GCM_SHA384 = $C061; + MBEDTLS_TLS_ECDH_RSA_WITH_ARIA_128_GCM_SHA256 = $C062; + MBEDTLS_TLS_ECDH_RSA_WITH_ARIA_256_GCM_SHA384 = $C063; + MBEDTLS_TLS_PSK_WITH_ARIA_128_CBC_SHA256 = $C064; + MBEDTLS_TLS_PSK_WITH_ARIA_256_CBC_SHA384 = $C065; + MBEDTLS_TLS_DHE_PSK_WITH_ARIA_128_CBC_SHA256 = $C066; + MBEDTLS_TLS_DHE_PSK_WITH_ARIA_256_CBC_SHA384 = $C067; + MBEDTLS_TLS_RSA_PSK_WITH_ARIA_128_CBC_SHA256 = $C068; + MBEDTLS_TLS_RSA_PSK_WITH_ARIA_256_CBC_SHA384 = $C069; + MBEDTLS_TLS_PSK_WITH_ARIA_128_GCM_SHA256 = $C06A; + MBEDTLS_TLS_PSK_WITH_ARIA_256_GCM_SHA384 = $C06B; + MBEDTLS_TLS_DHE_PSK_WITH_ARIA_128_GCM_SHA256 = $C06C; + MBEDTLS_TLS_DHE_PSK_WITH_ARIA_256_GCM_SHA384 = $C06D; + MBEDTLS_TLS_RSA_PSK_WITH_ARIA_128_GCM_SHA256 = $C06E; + MBEDTLS_TLS_RSA_PSK_WITH_ARIA_256_GCM_SHA384 = $C06F; + MBEDTLS_TLS_ECDHE_PSK_WITH_ARIA_128_CBC_SHA256 = $C070; + MBEDTLS_TLS_ECDHE_PSK_WITH_ARIA_256_CBC_SHA384 = $C071; + MBEDTLS_TLS_ECDHE_ECDSA_WITH_CAMELLIA_128_CBC_SHA256 = $C072; + MBEDTLS_TLS_ECDHE_ECDSA_WITH_CAMELLIA_256_CBC_SHA384 = $C073; + MBEDTLS_TLS_ECDH_ECDSA_WITH_CAMELLIA_128_CBC_SHA256 = $C074; + MBEDTLS_TLS_ECDH_ECDSA_WITH_CAMELLIA_256_CBC_SHA384 = $C075; + MBEDTLS_TLS_ECDHE_RSA_WITH_CAMELLIA_128_CBC_SHA256 = $C076; + MBEDTLS_TLS_ECDHE_RSA_WITH_CAMELLIA_256_CBC_SHA384 = $C077; + MBEDTLS_TLS_ECDH_RSA_WITH_CAMELLIA_128_CBC_SHA256 = $C078; + MBEDTLS_TLS_ECDH_RSA_WITH_CAMELLIA_256_CBC_SHA384 = $C079; + MBEDTLS_TLS_RSA_WITH_CAMELLIA_128_GCM_SHA256 = $C07A; + MBEDTLS_TLS_RSA_WITH_CAMELLIA_256_GCM_SHA384 = $C07B; + MBEDTLS_TLS_DHE_RSA_WITH_CAMELLIA_128_GCM_SHA256 = $C07C; + MBEDTLS_TLS_DHE_RSA_WITH_CAMELLIA_256_GCM_SHA384 = $C07D; + MBEDTLS_TLS_ECDHE_ECDSA_WITH_CAMELLIA_128_GCM_SHA256 = $C086; + MBEDTLS_TLS_ECDHE_ECDSA_WITH_CAMELLIA_256_GCM_SHA384 = $C087; + MBEDTLS_TLS_ECDH_ECDSA_WITH_CAMELLIA_128_GCM_SHA256 = $C088; + MBEDTLS_TLS_ECDH_ECDSA_WITH_CAMELLIA_256_GCM_SHA384 = $C089; + MBEDTLS_TLS_ECDHE_RSA_WITH_CAMELLIA_128_GCM_SHA256 = $C08A; + MBEDTLS_TLS_ECDHE_RSA_WITH_CAMELLIA_256_GCM_SHA384 = $C08B; + MBEDTLS_TLS_ECDH_RSA_WITH_CAMELLIA_128_GCM_SHA256 = $C08C; + MBEDTLS_TLS_ECDH_RSA_WITH_CAMELLIA_256_GCM_SHA384 = $C08D; + MBEDTLS_TLS_PSK_WITH_CAMELLIA_128_GCM_SHA256 = $C08E; + MBEDTLS_TLS_PSK_WITH_CAMELLIA_256_GCM_SHA384 = $C08F; + MBEDTLS_TLS_DHE_PSK_WITH_CAMELLIA_128_GCM_SHA256 = $C090; + MBEDTLS_TLS_DHE_PSK_WITH_CAMELLIA_256_GCM_SHA384 = $C091; + MBEDTLS_TLS_RSA_PSK_WITH_CAMELLIA_128_GCM_SHA256 = $C092; + MBEDTLS_TLS_RSA_PSK_WITH_CAMELLIA_256_GCM_SHA384 = $C093; + MBEDTLS_TLS_PSK_WITH_CAMELLIA_128_CBC_SHA256 = $C094; + MBEDTLS_TLS_PSK_WITH_CAMELLIA_256_CBC_SHA384 = $C095; + MBEDTLS_TLS_DHE_PSK_WITH_CAMELLIA_128_CBC_SHA256 = $C096; + MBEDTLS_TLS_DHE_PSK_WITH_CAMELLIA_256_CBC_SHA384 = $C097; + MBEDTLS_TLS_RSA_PSK_WITH_CAMELLIA_128_CBC_SHA256 = $C098; + MBEDTLS_TLS_RSA_PSK_WITH_CAMELLIA_256_CBC_SHA384 = $C099; + MBEDTLS_TLS_ECDHE_PSK_WITH_CAMELLIA_128_CBC_SHA256 = $C09A; + MBEDTLS_TLS_ECDHE_PSK_WITH_CAMELLIA_256_CBC_SHA384 = $C09B; + MBEDTLS_TLS_RSA_WITH_AES_128_CCM = $C09C; + MBEDTLS_TLS_RSA_WITH_AES_256_CCM = $C09D; + MBEDTLS_TLS_DHE_RSA_WITH_AES_128_CCM = $C09E; + MBEDTLS_TLS_DHE_RSA_WITH_AES_256_CCM = $C09F; + MBEDTLS_TLS_RSA_WITH_AES_128_CCM_8 = $C0A0; + MBEDTLS_TLS_RSA_WITH_AES_256_CCM_8 = $C0A1; + MBEDTLS_TLS_DHE_RSA_WITH_AES_128_CCM_8 = $C0A2; + MBEDTLS_TLS_DHE_RSA_WITH_AES_256_CCM_8 = $C0A3; + MBEDTLS_TLS_PSK_WITH_AES_128_CCM = $C0A4; + MBEDTLS_TLS_PSK_WITH_AES_256_CCM = $C0A5; + MBEDTLS_TLS_DHE_PSK_WITH_AES_128_CCM = $C0A6; + MBEDTLS_TLS_DHE_PSK_WITH_AES_256_CCM = $C0A7; + MBEDTLS_TLS_PSK_WITH_AES_128_CCM_8 = $C0A8; + MBEDTLS_TLS_PSK_WITH_AES_256_CCM_8 = $C0A9; + MBEDTLS_TLS_DHE_PSK_WITH_AES_128_CCM_8 = $C0AA; + MBEDTLS_TLS_DHE_PSK_WITH_AES_256_CCM_8 = $C0AB; + MBEDTLS_TLS_ECDHE_ECDSA_WITH_AES_128_CCM = $C0AC; + MBEDTLS_TLS_ECDHE_ECDSA_WITH_AES_256_CCM = $C0AD; + MBEDTLS_TLS_ECDHE_ECDSA_WITH_AES_128_CCM_8 = $C0AE; + MBEDTLS_TLS_ECDHE_ECDSA_WITH_AES_256_CCM_8 = $C0AF; + MBEDTLS_TLS_ECJPAKE_WITH_AES_128_CCM_8 = $C0FF; + MBEDTLS_TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256 = $CCA8; + MBEDTLS_TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256 = $CCA9; + MBEDTLS_TLS_DHE_RSA_WITH_CHACHA20_POLY1305_SHA256 = $CCAA; + MBEDTLS_TLS_PSK_WITH_CHACHA20_POLY1305_SHA256 = $CCAB; + MBEDTLS_TLS_ECDHE_PSK_WITH_CHACHA20_POLY1305_SHA256 = $CCAC; + MBEDTLS_TLS_DHE_PSK_WITH_CHACHA20_POLY1305_SHA256 = $CCAD; + MBEDTLS_TLS_RSA_PSK_WITH_CHACHA20_POLY1305_SHA256 = $CCAE; + +type + TMbedtls_SSL_States = Integer; + // 定义完整的 TMbedtls_SSL_States 枚举类型在编译 Win64 执行文件时会报错 + // [dcc64 Error] Net.MbedTls.pas(1638): E2068 Illegal reference to symbol 'MBEDTLS_SSL_HANDSHAKE_WRAPUP' in object file 'D:\Design\Delphi\VclFmxRtl\zLib\Net\MbedTls\Win64\Release\ssl_cli.o' + // 这显然是编译器的问题, 经测试 Delphi 10.2.3 和 Delphi 10.3 均有该问题 +// TMbedtls_SSL_States = ( +// MBEDTLS_SSL_HELLO_REQUEST = 0, +// MBEDTLS_SSL_CLIENT_HELLO, +// MBEDTLS_SSL_SERVER_HELLO, +// MBEDTLS_SSL_SERVER_CERTIFICATE, +// MBEDTLS_SSL_SERVER_KEY_EXCHANGE, +// MBEDTLS_SSL_CERTIFICATE_REQUEST, +// MBEDTLS_SSL_SERVER_HELLO_DONE, +// MBEDTLS_SSL_CLIENT_CERTIFICATE, +// MBEDTLS_SSL_CLIENT_KEY_EXCHANGE, +// MBEDTLS_SSL_CERTIFICATE_VERIFY, +// MBEDTLS_SSL_CLIENT_CHANGE_CIPHER_SPEC, +// MBEDTLS_SSL_CLIENT_FINISHED, +// MBEDTLS_SSL_SERVER_CHANGE_CIPHER_SPEC, +// MBEDTLS_SSL_SERVER_FINISHED, +// MBEDTLS_SSL_FLUSH_BUFFERS, +// MBEDTLS_SSL_HANDSHAKE_WRAPUP, +// MBEDTLS_SSL_HANDSHAKE_OVER, +// MBEDTLS_SSL_SERVER_NEW_SESSION_TICKET, +// MBEDTLS_SSL_SERVER_HELLO_VERIFY_REQUEST_SENT +// ); + + Size_T = NativeUInt; + PSize_T = ^Size_T; + UInt32_T = UInt32; + PUint32_T = ^UInt32_T; + UInt16_T = UInt16; + UInt64_T = UInt64; + UInt8_T = UInt8; + + PMbedtls_SSL_Session = Pointer; + PMbedtls_SSL_Transform = Pointer; + PMbedtls_SSL_Handshake_Params = Pointer; + PMbedtls_MD_Info = Pointer; // opaque + PMbedtls_X509_Crl = Pointer; + PMbedtls_Cipher_Info = Pointer; + + PMbedtls_SSL_Send = Pointer; + PMbedtls_SSL_Recv = Pointer; + PMbedtls_SSL_Recv_Timeout = Pointer; + PMbedtls_SSL_Set_Timer = Pointer; + PMbedtls_SSL_Get_Timer = Pointer; + + PMbedtls_SSL_Cache_Entry = Pointer; + + PTlsMutex = ^TTlsMutex; + TTlsMutex = record + Lock: TObject; + IsValid: Byte; + end; + + TMbedtls_MPI = record + s: Integer; // integer sign + n: Size_T; // total # of limbs + p: Pointer; // pointer to limbs + end; + + // + // SSL/TLS configuration to be shared between mbedtls_ssl_context structures. + // + PMbedtls_SSL_Config = ^TMbedtls_SSL_Config; + TMbedtls_SSL_Config = record + // Group items by size (largest first) to minimize padding overhead + + // + // Pointers + // + + ciphersuite_list: array [0 .. 3] of PInteger; + // allowed ciphersuites per version + + // Callback for printing debug output + f_dbg: Pointer; + p_dbg: Pointer; // !< context for the debug function + + // ** Callback for getting (pseudo-)random numbers + f_rng: Pointer; + p_rng: Pointer; // !< context for the RNG function + + // ** Callback to retrieve a session from the cache + f_get_cache: Pointer; + // ** Callback to store a session into the cache + f_set_cache: Pointer; + p_cache: Pointer; // !< context for cache callbacks + + // ** Callback for setting cert according to SNI extension + f_sni: Pointer; + p_sni: Pointer; // !< context for SNI callback + + // ** Callback to customize X.509 certificate chain verification + f_vrfy: Pointer; + p_vrfy: Pointer; // !< context for X.509 verify calllback + + // ** Callback to retrieve PSK key from identity + f_psk: Pointer; + p_psk: Pointer; // !< context for PSK callback + + // ** Callback to create & write a cookie for ClientHello veirifcation + f_cookie_write: Pointer; + // ** Callback to verify validity of a ClientHello cookie + f_cookie_check: Pointer; + p_cookie: Pointer; // !< context for the cookie callbacks + + // ** Callback to create & write a session ticket + f_ticket_write: Pointer; + // ** Callback to parse a session ticket into a session structure + f_ticket_parse: Pointer; + p_ticket: Pointer; // !< context for the ticket callbacks + + // ** Callback to export key block and master secret + f_export_keys: Pointer; + p_export_keys: Pointer; // !< context for key export callback + + cert_profile: Pointer; // !< verification profile + key_cert: Pointer; // !< own certificate/key pair(s) + ca_chain: Pointer; // !< trusted CAs + ca_crl: Pointer; // !< trusted CAs CRLs + + f_async_sign_start: Pointer; // !< start asynchronous signature operation + f_async_decrypt_start: Pointer; // !< start asynchronous decryption operation + f_async_resume: Pointer; // !< resume asynchronous operation + f_async_cancel: Pointer; // !< cancel asynchronous operation + p_async_config_data: Pointer; // !< Configuration data set by mbedtls_ssl_conf_async_private_cb(). + + sig_hashes: PInteger; // !< allowed signature hashes + + curve_list: Pointer; // !< allowed curves + + dhm_P: TMbedtls_MPI; // !< prime modulus for DHM + dhm_G: TMbedtls_MPI; // !< generator for DHM + + psk: PByte; // !< pre-shared key. This field should + // only be set via + // mbedtls_ssl_conf_psk() + psk_len: Size_T; // !< length of the pre-shared key. This + // field should only be set via + // mbedtls_ssl_conf_psk() + psk_identity: PByte; // !< identity for PSK negotiation. This + // field should only be set via + // mbedtls_ssl_conf_psk() + psk_identity_len: Size_T; // !< length of identity. This field should + // only be set via + // mbedtls_ssl_conf_psk() + + alpn_list: PMarshaledAString; // !< ordered list of protocols + + // + // * Numerical settings (int then char) + // + + read_timeout: UInt32_T; // !< timeout for mbedtls_ssl_read (ms) + + hs_timeout_min: UInt32_T; // !< initial value of the handshake + // retransmission timeout (ms) + hs_timeout_max: UInt32_T; // !< maximum value of the handshake + // retransmission timeout (ms) + + renego_max_records: Integer; // !< grace period for renegotiation + renego_period: array [0 .. 7] of UInt8_T; // !< value of the record counters + // that triggers renegotiation + + badmac_limit: UInt32_T; // !< limit of records with a bad MAC + + dhm_min_bitlen: UInt32_T; // !< min. bit length of the DHM prime + + max_major_ver: UInt8_T; // !< max. major version used + max_minor_ver: UInt8_T; // !< max. minor version used + min_major_ver: UInt8_T; // !< min. major version used + min_minor_ver: UInt8_T; // !< min. minor version used + + // + // * Flags (bitfields) + // + Flag: UInt32_T; + end; + + PMbedtls_SSL_Context = ^TMbedtls_SSL_Context; + TMbedtls_SSL_Context = record + conf: PMbedtls_SSL_Config; // !< configuration information + + // + // * Miscellaneous + // + state: Integer; // !< SSL handshake: current state + renego_status: Integer; // !< Initial, in progress, pending? + renego_records_seen: Integer; // !< Records since renego request, or with DTLS, + // number of retransmissions of request if + // renego_max_records is < 0 + + major_ver: Integer; // !< equal to MBEDTLS_SSL_MAJOR_VERSION_3 + minor_ver: Integer; // !< either 0 (SSL3) or 1 (TLS1.0) + + badmac_seen: Cardinal; // !< records with a bad MAC received + + f_send: PMbedtls_SSL_Send; // !< Callback for network send + f_recv: PMbedtls_SSL_Recv; // !< Callback for network receive + f_recv_timeout: PMbedtls_SSL_Recv_Timeout; // !< Callback for network receive with timeout + + p_bio: Pointer; // !< context for I/O operations + + // + // * Session layer + // + session_in: PMbedtls_SSL_Session; // !< current session data (in) + session_out: PMbedtls_SSL_Session; // !< current session data (out) + session: PMbedtls_SSL_Session; // !< negotiated session data + session_negotiate: PMbedtls_SSL_Session; // !< session data in negotiation + + handshake: PMbedtls_SSL_Handshake_Params; // !< params required only during + // the handshake process + + // + // * Record layer transformations + // + transform_in: PMbedtls_SSL_Transform; // !< current transform params (in) + transform_out: PMbedtls_SSL_Transform; // !< current transform params (in) + transform: PMbedtls_SSL_Transform; // !< negotiated transform params + transform_negotiate: PMbedtls_SSL_Transform; // !< transform params in negotiation + + // + // * Timers + // + p_timer: Pointer; // !< context for the timer callbacks + + f_set_timer: PMbedtls_SSL_Set_Timer; // !< set timer callback + f_get_timer: PMbedtls_SSL_Get_Timer; // !< get timer callback + + // + // * Record layer (incoming data) + // + in_buf: PByte; // !< input buffer + in_ctr: PByte; // !< 64-bit incoming message counter + // TLS: maintained by us + // DTLS: read from peer + in_hdr: PByte; // !< start of record header + in_len: PByte; // !< two-bytes message length field + in_iv: PByte; // !< ivlen-byte IV + in_msg: PByte; // !< message contents (in_iv+ivlen) + in_offt: PByte; // !< read offset in application data + + in_msgtype: Integer; // !< record header: message type + in_msglen: Size_T; // !< record header: message length + in_left: Size_T; // !< amount of data read so far + in_epoch: UInt16_T; // !< DTLS epoch for incoming records + next_record_offset: Size_T; // !< offset of the next record in datagram + // (equal to in_left if none) + in_window_top: UInt64_T; // !< last validated record seq_num + in_window: UInt64_T; // !< bitmask for replay detection + + in_hslen: Size_T; // !< current handshake message length, + // including the handshake header + nb_zero: Integer; // !< # of 0-length encrypted messages + + keep_current_message: Integer; // !< drop or reuse current message + // on next call to record layer? + + disable_datagram_packing: UInt8_T; // !< Disable packing multiple records + // within a single datagram. + + // + // * Record layer (outgoing data) + // + out_buf: PByte; // !< output buffer + out_ctr: PByte; // !< 64-bit outgoing message counter + out_hdr: PByte; // !< start of record header + out_len: PByte; // !< two-bytes message length field + out_iv: PByte; // !< ivlen-byte IV + out_msg: PByte; // !< message contents (out_iv+ivlen) + + out_msgtype: Integer; // !< record header: message type + out_msglen: Size_T; // !< record header: message length + out_left: Size_T; // !< amount of data not yet written + + cur_out_ctr: array [0 .. 7] of Byte; // !< Outgoing record sequence number. + + mtu: UInt16_T; // !< path mtu, used to fragment outgoing messages + + compress_buf: PByte; // !< zlib data buffer + split_done: Byte; // !< current record already splitted? + + // + // * PKI layer + // + client_auth: Integer; // !< flag for client auth. + + // + // * User settings + // + hostname: MarshaledAString; // !< expected peer CN for verification + // (and SNI if available) + + alpn_chosen: MarshaledAString; // !< negotiated protocol + + // + // * Information for DTLS hello verify + // + cli_id: PByte; // !< transport-level ID of the client + cli_id_len: Size_T; // !< length of cli_id + + // + // * Secure renegotiation + // + // needed to know when to send extension on server + secure_renegotiation: Integer; // !< does peer support legacy or + // secure renegotiation + verify_data_len: Size_T; // !< length of verify data stored + own_verify_data: array [0 .. MBEDTLS_SSL_VERIFY_DATA_MAX_LEN - 1] of Byte; // !< previous handshake verify data + peer_verify_data: array [0 .. MBEDTLS_SSL_VERIFY_DATA_MAX_LEN - 1] of Byte; // !< previous handshake verify data + end; + + PMbedtls_SSL_Cache_Context = ^TMbedtls_SSL_Cache_Context; + TMbedtls_SSL_Cache_Context = record + chain: PMbedtls_SSL_Cache_Entry; // !< start of the chain + timeout: Integer; // !< cache entry timeout + max_entries: Integer; // !< maximum entries + mutex: TTlsMutex; // !< mutex + end; + + TMbedtls_Asn1_Buf = record + tag: Integer; // **< ASN1 type, e.g. MBEDTLS_ASN1_UTF8_STRING. + len: Size_T; // **< ASN1 length, in octets. + p: PByte; // **< ASN1 data, e.g. in ASCII. + end; + + TMbedtls_X509_Buf = TMbedtls_Asn1_Buf; + + PMbedtls_Asn1_Named_Data = ^TMbedtls_Asn1_Named_Data; + TMbedtls_Asn1_Named_Data = record + oid: TMbedtls_Asn1_Buf; // **< The object identifier. + val: TMbedtls_Asn1_Buf; // **< The named value. + next: PMbedtls_Asn1_Named_Data; // **< The next entry in the sequence. + next_merged: Byte; // **< Merge next item into the current one? + end; + + TMbedtls_X509_Name = TMbedtls_Asn1_Named_Data; + + TMbedtls_X509_Time = record + year, mon, day: Integer; // **< Date. + hour, min, sec: Integer; // **< Time. + end; + + TMbedtls_PK_Type = ( + MBEDTLS_PK_NONE = 0, + MBEDTLS_PK_RSA, + MBEDTLS_PK_ECKEY, + MBEDTLS_PK_ECKEY_DH, + MBEDTLS_PK_ECDSA, + MBEDTLS_PK_RSA_ALT, + MBEDTLS_PK_RSASSA_PSS + ); + + PMbedtls_PK_Info = ^TMbedtls_PK_Info; + TMbedtls_PK_Info = record + // ** Public key type + &type: TMbedtls_PK_Type; + + // ** Type name + name: MarshaledAString; + + // ** Get key size in bits + get_bitlen: Pointer; + + // ** Tell if the context implements this type (e.g. ECKEY can do ECDSA) + can_do: Pointer; + + // ** Verify signature + verify_func: Pointer; + + // ** Make signature + sign_func: Pointer; + + // ** Decrypt message + decrypt_func: Pointer; + + // ** Encrypt message + encrypt_func: Pointer; + + // ** Check public-private key pair + check_pair_func: Pointer; + + // ** Allocate a new context + ctx_alloc_func: Pointer; + + // ** Free the given context + ctx_free_func: Pointer; + + // ** Interface with the debug module + debug_func: Pointer; + end; + + PMbedtls_PK_Context = ^TMbedtls_PK_Context; + TMbedtls_PK_Context = record + pk_info: PMbedtls_PK_Info; // **< Public key informations + pk_ctx: Pointer; // **< Underlying public key context + end; + + PMbedtls_Asn1_Sequence = ^TMbedtls_Asn1_Sequence; + TMbedtls_Asn1_Sequence = record + buf: TMbedtls_Asn1_Buf; // **< Buffer containing the given ASN.1 item. + next: PMbedtls_Asn1_Sequence; // **< The next entry in the sequence. + end; + + TMbedtls_X509_Sequence = TMbedtls_Asn1_Sequence; + + TMbedtls_MD_Type = ( + MBEDTLS_MD_NONE = 0, // **< None. + MBEDTLS_MD_MD2, // **< The MD2 message digest. + MBEDTLS_MD_MD4, // **< The MD4 message digest. + MBEDTLS_MD_MD5, // **< The MD5 message digest. + MBEDTLS_MD_SHA1, // **< The SHA-1 message digest. + MBEDTLS_MD_SHA224, // **< The SHA-224 message digest. + MBEDTLS_MD_SHA256, // **< The SHA-256 message digest. + MBEDTLS_MD_SHA384, // **< The SHA-384 message digest. + MBEDTLS_MD_SHA512, // **< The SHA-512 message digest. + MBEDTLS_MD_RIPEMD160 // **< The RIPEMD-160 message digest. + ); + + PMbedtls_X509_CRT = ^TMbedtls_X509_CRT; + TMbedtls_X509_CRT = record + raw: TMbedtls_X509_Buf; // **< The raw certificate data (DER). + tbs: TMbedtls_X509_Buf; // **< The raw certificate body (DER). The part that is To Be Signed. + + version: Integer; // **< The X.509 version. (1=v1, 2=v2, 3=v3) + serial: TMbedtls_X509_Buf; // **< Unique id for certificate issued by a specific CA. + sig_oid: TMbedtls_X509_Buf; // **< Signature algorithm, e.g. sha1RSA + + issuer_raw: TMbedtls_X509_Buf; // **< The raw issuer data (DER). Used for quick comparison. + subject_raw: TMbedtls_X509_Buf; // **< The raw subject data (DER). Used for quick comparison. + + issuer: TMbedtls_X509_Name; // **< The parsed issuer data (named information object). + subject: TMbedtls_X509_Name; // **< The parsed subject data (named information object). + + valid_from: TMbedtls_X509_Time; // **< Start time of certificate validity. + valid_to: TMbedtls_X509_Time; // **< End time of certificate validity. + + pk: TMbedtls_PK_Context; // **< Container for the public key context. + + issuer_id: TMbedtls_X509_Buf; // **< Optional X.509 v2/v3 issuer unique identifier. + subject_id: TMbedtls_X509_Buf; // **< Optional X.509 v2/v3 subject unique identifier. + v3_ext: TMbedtls_X509_Buf; // **< Optional X.509 v3 extensions. + subject_alt_names: TMbedtls_X509_Sequence; // **< Optional list of Subject Alternative Names (Only dNSName supported). + + ext_types: Integer; // **< Bit string containing detected and parsed extensions + ca_istrue: Integer; // **< Optional Basic Constraint extension value: 1 if this certificate belongs to a CA, 0 otherwise. + max_pathlen: Integer; // **< Optional Basic Constraint extension value: The maximum path length to the root certificate. Path length is 1 higher than RFC 5280 'meaning', so 1+ + + key_usage: UInt32_T; // **< Optional key usage extension value: See the values in x509.h + + ext_key_usage: TMbedtls_X509_Sequence; // **< Optional list of extended key usage OIDs. + + ns_cert_type: Byte; // **< Optional Netscape certificate type extension value: See the values in x509.h + + sig: TMbedtls_X509_Buf; // **< Signature: hash of the tbs part signed with the private key. + sig_md: TMbedtls_MD_Type; // **< Internal representation of the MD algorithm of the signature algorithm, e.g. MBEDTLS_MD_SHA256 + sig_pk: TMbedtls_PK_Type; // **< Internal representation of the Public Key algorithm of the signature algorithm, e.g. MBEDTLS_PK_RSA + sig_opts: Pointer; // **< Signature options to be passed to mbedtls_pk_verify_ext(), e.g. for RSASSA-PSS + + next: PMbedtls_X509_CRT; // **< Next certificate in the CA-chain. + end; + + TMbedtls_SHA512_Context = record + total: array [0 .. 1] of UInt64_T; // !< The number of Bytes processed. + state: array [0 .. 7] of UInt64_T; // !< The intermediate digest state. + buffer: array [0 .. 127] of Byte; // !< The data block being processed. + is384: Integer; // !< Determines which function to use: + // 0: Use SHA-512, or 1: Use SHA-384. + end; + + TMbedtls_Entropy_Source_State = record + f_source: Pointer; // **< The entropy source callback + p_source: Pointer; // **< The callback data pointer + size: Size_T; // **< Amount received in bytes + threshold: Size_T; // **< Minimum bytes required before release + strong: Integer; // **< Is the source strong? + end; + + TMbedtls_Havege_State = record + PT1: Integer; + PT2: Integer; + offset: array [0 .. 1] of Integer; + pool: array [0 .. MBEDTLS_HAVEGE_COLLECT_SIZE - 1] of Integer; + WALK: array [0 .. 8191] of Integer; + end; + + PMbedtls_Entropy_Context = ^TMbedtls_Entropy_Context; + TMbedtls_Entropy_Context = record + accumulator_started: Integer; + accumulator: TMbedtls_SHA512_Context; + source_count: Integer; + source: array [0 .. MBEDTLS_ENTROPY_MAX_SOURCES - 1] of TMbedtls_Entropy_Source_State; + havege_data: TMbedtls_Havege_State; + mutex: TTlsMutex; // !< mutex + initial_entropy_run: Integer; + end; + + TMbedtls_AES_Context = record + nr: Integer; // !< The number of rounds. + rk: PUint32_T; // !< AES round keys. + buf: array [0 .. 67] of UInt32_T; // !< Unaligned data buffer. This buffer can + // hold 32 extra Bytes, which can be used for + // one of the following purposes: + //
  • Alignment if VIA padlock is + // used.
  • + //
  • Simplifying key expansion in the 256-bit + // case by generating an extra round key. + //
+ end; + + PMbedtls_CTR_DRBG_Context = ^TMbedtls_CTR_DRBG_Context; + TMbedtls_CTR_DRBG_Context = record + counter: array [0 .. 15] of Byte; // !< The counter (V). + reseed_counter: Integer; // !< The reseed counter. + prediction_resistance: Integer; // !< This determines whether prediction + // resistance is enabled, that is + // whether to systematically reseed before + // each random generation. + entropy_len: Size_T; // !< The amount of entropy grabbed on each + // seed or reseed operation. + reseed_interval: Integer; // !< The reseed interval. + + aes_ctx: TMbedtls_AES_Context; // !< The AES context. + + // + // * Callbacks (Entropy) + // + f_entropy: Pointer; // !< The entropy callback function. + + p_entropy: Pointer; // !< The context for the entropy function. + + mutex: TTlsMutex; + end; + + TEntropyFunc = function(data: Pointer; output: MarshaledAString; len: Size_T): Integer; cdecl; + PEntropyFunc = ^TEntropyFunc; + TrngFunc = function(data: Pointer; output: MarshaledAString; len: Size_T): Integer; cdecl; + TdbgFunc = procedure(data: Pointer; i: Integer; c: MarshaledAString; i2: Integer; c2: MarshaledAString); cdecl; + TNetSendFunc = function(ctx: Pointer; buf: Pointer; len: Size_T): Integer; cdecl; + TNetRecvFunc = function(ctx: Pointer; buf: Pointer; len: Size_T): Integer; cdecl; + TNetRecvTimeoutFunc = function(ctx: Pointer; buf: Pointer; len: Size_T; timeout: UInt32_T): Integer; cdecl; + TGetTimerFunc = function(ctx: Pointer): Integer; cdecl; + TSetTimerFunc = procedure(ctx: Pointer; int_ms: UInt32_T; fin_ms: UInt32_T); cdecl; + + TGetCacheFunc = function(data: Pointer; session: PMbedtls_SSL_Session): Integer; cdecl; + TSetCacheFunc = function(data: Pointer; const session: PMbedtls_SSL_Session): Integer; cdecl; + +const + // SSL Error codes - actually negative of these + MBEDTLS_ERR_MPI_FILE_IO_ERROR = -$0002; // An error occurred while reading from or writing to a file. + MBEDTLS_ERR_MPI_BAD_INPUT_DATA = -$0004; // Bad input parameters to function. + MBEDTLS_ERR_MPI_INVALID_CHARACTER = -$0006; // There is an invalid character in the digit string. + MBEDTLS_ERR_MPI_BUFFER_TOO_SMALL = -$0008; // The buffer is too small to write to. + MBEDTLS_ERR_MPI_NEGATIVE_VALUE = -$000A; // The input arguments are negative or result in illegal output. + MBEDTLS_ERR_MPI_DIVISION_BY_ZERO = -$000C; // The input argument for division is zero, which is not allowed. + MBEDTLS_ERR_MPI_NOT_ACCEPTABLE = -$000E; // The input arguments are not acceptable. + MBEDTLS_ERR_MPI_ALLOC_FAILED = -$0010; // Memory allocation failed. + + MBEDTLS_ERR_HMAC_DRBG_REQUEST_TOO_BIG = -$0003; // Too many random requested in single call. + MBEDTLS_ERR_HMAC_DRBG_INPUT_TOO_BIG = -$0005; // Input too large (Entropy + additional). + MBEDTLS_ERR_HMAC_DRBG_FILE_IO_ERROR = -$0007; // Read/write error in file. + MBEDTLS_ERR_HMAC_DRBG_ENTROPY_SOURCE_FAILED = -$0009; // The entropy source failed. + + MBEDTLS_ERR_CCM_BAD_INPUT = -$000D; // Bad input parameters to the function. + MBEDTLS_ERR_CCM_AUTH_FAILED = -$000F; // Authenticated decryption failed. + MBEDTLS_ERR_CCM_HW_ACCEL_FAILED = -$0011; // CCM hardware accelerator failed. + + MBEDTLS_ERR_GCM_AUTH_FAILED = -$0012; // Authenticated decryption failed. + MBEDTLS_ERR_GCM_HW_ACCEL_FAILED = -$0013; // GCM hardware accelerator failed. + MBEDTLS_ERR_GCM_BAD_INPUT = -$0014; // Bad input parameters to function. + + MBEDTLS_ERR_BLOWFISH_INVALID_KEY_LENGTH = -$0016; // Invalid key length. + MBEDTLS_ERR_BLOWFISH_HW_ACCEL_FAILED = -$0017; // Blowfish hardware accelerator failed. + MBEDTLS_ERR_BLOWFISH_INVALID_INPUT_LENGTH = -$0018; // Invalid data input length. + + MBEDTLS_ERR_ARC4_HW_ACCEL_FAILED = -$0019; // ARC4 hardware accelerator failed. + + MBEDTLS_ERR_THREADING_FEATURE_UNAVAILABLE = -$001A; // The selected feature is not available. + MBEDTLS_ERR_THREADING_BAD_INPUT_DATA = -$001C; // Bad input parameters to function. + MBEDTLS_ERR_THREADING_MUTEX_ERROR = -$001E; // Locking / unlocking / free failed with error code. + + MBEDTLS_ERR_AES_INVALID_KEY_LENGTH = -$0020; // Invalid key length. + MBEDTLS_ERR_AES_INVALID_INPUT_LENGTH = -$0022; // Invalid data input length. + + MBEDTLS_ERR_AES_FEATURE_UNAVAILABLE = -$0023; // Feature not available. For example, an unsupported AES key size. + MBEDTLS_ERR_AES_HW_ACCEL_FAILED = -$0025; // AES hardware accelerator failed. + + MBEDTLS_ERR_CAMELLIA_INVALID_KEY_LENGTH = -$0024; // Invalid key length. + MBEDTLS_ERR_CAMELLIA_INVALID_INPUT_LENGTH = -$0026; // Invalid data input length. + MBEDTLS_ERR_CAMELLIA_HW_ACCEL_FAILED = -$0027; // Camellia hardware accelerator failed. + + MBEDTLS_ERR_XTEA_INVALID_INPUT_LENGTH = -$0028; // The data input has an invalid length. + MBEDTLS_ERR_XTEA_HW_ACCEL_FAILED = -$0029; // XTEA hardware accelerator failed. + + MBEDTLS_ERR_BASE64_BUFFER_TOO_SMALL = -$002A; // Output buffer too small. + MBEDTLS_ERR_BASE64_INVALID_CHARACTER = -$002C; // Invalid character in input. + + MBEDTLS_ERR_MD2_HW_ACCEL_FAILED = -$002B; // MD2 hardware accelerator failed + MBEDTLS_ERR_MD4_HW_ACCEL_FAILED = -$002D; // MD4 hardware accelerator failed + MBEDTLS_ERR_MD5_HW_ACCEL_FAILED = -$002F; // MD5 hardware accelerator failed + + MBEDTLS_ERR_OID_NOT_FOUND = -$002E; // OID is not found. + MBEDTLS_ERR_OID_BUF_TOO_SMALL = -$000B; // output buffer is too small + + MBEDTLS_ERR_PADLOCK_DATA_MISALIGNED = -$0030; // Input data should be aligned. + + MBEDTLS_ERR_RIPEMD160_HW_ACCEL_FAILED = -$0031; // RIPEMD160 hardware accelerator failed + + MBEDTLS_ERR_DES_INVALID_INPUT_LENGTH = -$0032; // The data input has an invalid length. + MBEDTLS_ERR_DES_HW_ACCEL_FAILED = -$0033; // DES hardware accelerator failed. + + MBEDTLS_ERR_CTR_DRBG_ENTROPY_SOURCE_FAILED = -$0034; // The entropy source failed. + MBEDTLS_ERR_CTR_DRBG_REQUEST_TOO_BIG = -$0036; // The requested random buffer length is too big. + MBEDTLS_ERR_CTR_DRBG_INPUT_TOO_BIG = -$0038; // The input (entropy + additional data) is too large. + MBEDTLS_ERR_CTR_DRBG_FILE_IO_ERROR = -$003A; // Read or write error in file. + + MBEDTLS_ERR_SHA1_HW_ACCEL_FAILED = -$0035; // SHA-1 hardware accelerator failed + MBEDTLS_ERR_SHA256_HW_ACCEL_FAILED = -$0037; // SHA-256 hardware accelerator failed + MBEDTLS_ERR_SHA512_HW_ACCEL_FAILED = -$0039; // SHA-512 hardware accelerator failed + + MBEDTLS_ERR_ENTROPY_SOURCE_FAILED = -$003C; // Critical entropy source failure. + MBEDTLS_ERR_ENTROPY_MAX_SOURCES = -$003E; // No more sources can be added. + MBEDTLS_ERR_ENTROPY_NO_SOURCES_DEFINED = -$0040; // No sources have been added to poll. + MBEDTLS_ERR_ENTROPY_NO_STRONG_SOURCE = -$003D; // No strong sources have been added to poll. + MBEDTLS_ERR_ENTROPY_FILE_IO_ERROR = -$003F; // Read/write error in file. + + MBEDTLS_ERR_NET_SOCKET_FAILED = -$0042; // Failed to open a socket. + MBEDTLS_ERR_NET_CONNECT_FAILED = -$0044; // The connection to the given server / port failed. + MBEDTLS_ERR_NET_BIND_FAILED = -$0046; // Binding of the socket failed. + MBEDTLS_ERR_NET_LISTEN_FAILED = -$0048; // Could not listen on the socket. + MBEDTLS_ERR_NET_ACCEPT_FAILED = -$004A; // Could not accept the incoming connection. + MBEDTLS_ERR_NET_RECV_FAILED = -$004C; // Reading information from the socket failed. + MBEDTLS_ERR_NET_SEND_FAILED = -$004E; // Sending information through the socket failed. + MBEDTLS_ERR_NET_CONN_RESET = -$0050; // Connection was reset by peer. + MBEDTLS_ERR_NET_UNKNOWN_HOST = -$0052; // Failed to get an IP address for the given hostname. + MBEDTLS_ERR_NET_BUFFER_TOO_SMALL = -$0043; // Buffer is too small to hold the data. + MBEDTLS_ERR_NET_INVALID_CONTEXT = -$0045; // The context is invalid, eg because it was free()ed. + + MBEDTLS_ERR_ASN1_OUT_OF_DATA = -$0060; // Out of data when parsing an ASN1 data structure. + MBEDTLS_ERR_ASN1_UNEXPECTED_TAG = -$0062; // ASN1 tag was of an unexpected value. + MBEDTLS_ERR_ASN1_INVALID_LENGTH = -$0064; // Error when trying to determine the length or invalid length. + MBEDTLS_ERR_ASN1_LENGTH_MISMATCH = -$0066; // Actual length differs from expected length. + MBEDTLS_ERR_ASN1_INVALID_DATA = -$0068; // Data is invalid. (not used) + MBEDTLS_ERR_ASN1_ALLOC_FAILED = -$006A; // Memory allocation failed + MBEDTLS_ERR_ASN1_BUF_TOO_SMALL = -$006C; // Buffer too small when writing ASN.1 data structure. + + MBEDTLS_ERR_CMAC_HW_ACCEL_FAILED = -$007A; // CMAC hardware accelerator failed. + + MBEDTLS_ERR_PEM_NO_HEADER_FOOTER_PRESENT = -$1080; // No PEM header or footer found. + MBEDTLS_ERR_PEM_INVALID_DATA = -$1100; // PEM string is not as expected. + MBEDTLS_ERR_PEM_ALLOC_FAILED = -$1180; // Failed to allocate memory. + MBEDTLS_ERR_PEM_INVALID_ENC_IV = -$1200; // RSA IV is not in hex-format. + MBEDTLS_ERR_PEM_UNKNOWN_ENC_ALG = -$1280; // Unsupported key encryption algorithm. + MBEDTLS_ERR_PEM_PASSWORD_REQUIRED = -$1300; // Private key password can't be empty. + MBEDTLS_ERR_PEM_PASSWORD_MISMATCH = -$1380; // Given private key password does not allow for correct decryption. + MBEDTLS_ERR_PEM_FEATURE_UNAVAILABLE = -$1400; // Unavailable feature, e.g. hashing/encryption combination. + MBEDTLS_ERR_PEM_BAD_INPUT_DATA = -$1480; // Bad input parameters to function. + + MBEDTLS_ERR_PKCS12_BAD_INPUT_DATA = -$1F80; // Bad input parameters to function. + MBEDTLS_ERR_PKCS12_FEATURE_UNAVAILABLE = -$1F00; // Feature not available, e.g. unsupported encryption scheme. + MBEDTLS_ERR_PKCS12_PBE_INVALID_FORMAT = -$1E80; // PBE ASN.1 data not as expected. + MBEDTLS_ERR_PKCS12_PASSWORD_MISMATCH = -$1E00; // Given private key password does not allow for correct decryption. + + MBEDTLS_ERR_X509_FEATURE_UNAVAILABLE = -$2080; // Unavailable feature, e.g. RSA hashing/encryption combination. + MBEDTLS_ERR_X509_UNKNOWN_OID = -$2100; // Requested OID is unknown. + MBEDTLS_ERR_X509_INVALID_FORMAT = -$2180; // The CRT/CRL/CSR format is invalid, e.g. different type expected. + MBEDTLS_ERR_X509_INVALID_VERSION = -$2200; // The CRT/CRL/CSR version element is invalid. + MBEDTLS_ERR_X509_INVALID_SERIAL = -$2280; // The serial tag or value is invalid. + MBEDTLS_ERR_X509_INVALID_ALG = -$2300; // The algorithm tag or value is invalid. + MBEDTLS_ERR_X509_INVALID_NAME = -$2380; // The name tag or value is invalid. + MBEDTLS_ERR_X509_INVALID_DATE = -$2400; // The date tag or value is invalid. + MBEDTLS_ERR_X509_INVALID_SIGNATURE = -$2480; // The signature tag or value invalid. + MBEDTLS_ERR_X509_INVALID_EXTENSIONS = -$2500; // The extension tag or value is invalid. + MBEDTLS_ERR_X509_UNKNOWN_VERSION = -$2580; // CRT/CRL/CSR has an unsupported version number. + MBEDTLS_ERR_X509_UNKNOWN_SIG_ALG = -$2600; // Signature algorithm (oid) is unsupported. + MBEDTLS_ERR_X509_SIG_MISMATCH = -$2680; // Signature algorithms do not match. (see \c ::mbedtls_x509_crt sig_oid) + MBEDTLS_ERR_X509_CERT_VERIFY_FAILED = -$2700; // Certificate verification failed, e.g. CRL, CA or signature check failed. + MBEDTLS_ERR_X509_CERT_UNKNOWN_FORMAT = -$2780; // Format not recognized as DER or PEM. + MBEDTLS_ERR_X509_BAD_INPUT_DATA = -$2800; // Input invalid. + MBEDTLS_ERR_X509_ALLOC_FAILED = -$2880; // Allocation of memory failed. + MBEDTLS_ERR_X509_FILE_IO_ERROR = -$2900; // Read/write of file failed. + MBEDTLS_ERR_X509_BUFFER_TOO_SMALL = -$2980; // Destination buffer is too small. + MBEDTLS_ERR_X509_FATAL_ERROR = -$3000; // A fatal error occured, eg the chain is too long or the vrfy callback failed. + + MBEDTLS_ERR_PKCS5_BAD_INPUT_DATA = -$2F80; // Bad input parameters to function. + MBEDTLS_ERR_PKCS5_INVALID_FORMAT = -$2F00; // Unexpected ASN.1 data. + MBEDTLS_ERR_PKCS5_FEATURE_UNAVAILABLE = -$2E80; // Requested encryption or digest alg not available. + MBEDTLS_ERR_PKCS5_PASSWORD_MISMATCH = -$2E00; // Given private key password does not allow for correct decryption. + + MBEDTLS_ERR_DHM_BAD_INPUT_DATA = -$3080; // Bad input parameters. + MBEDTLS_ERR_DHM_READ_PARAMS_FAILED = -$3100; // Reading of the DHM parameters failed. + MBEDTLS_ERR_DHM_MAKE_PARAMS_FAILED = -$3180; // Making of the DHM parameters failed. + MBEDTLS_ERR_DHM_READ_PUBLIC_FAILED = -$3200; // Reading of the public values failed. + MBEDTLS_ERR_DHM_MAKE_PUBLIC_FAILED = -$3280; // Making of the public value failed. + MBEDTLS_ERR_DHM_CALC_SECRET_FAILED = -$3300; // Calculation of the DHM secret failed. + MBEDTLS_ERR_DHM_INVALID_FORMAT = -$3380; // The ASN.1 data is not formatted correctly. + MBEDTLS_ERR_DHM_ALLOC_FAILED = -$3400; // Allocation of memory failed. + MBEDTLS_ERR_DHM_FILE_IO_ERROR = -$3480; // Read or write of file failed. + MBEDTLS_ERR_DHM_HW_ACCEL_FAILED = -$3500; // DHM hardware accelerator failed. + MBEDTLS_ERR_DHM_SET_GROUP_FAILED = -$3580; // Setting the modulus and generator failed. + + MBEDTLS_ERR_PK_ALLOC_FAILED = -$3F80; // Memory allocation failed. + MBEDTLS_ERR_PK_TYPE_MISMATCH = -$3F00; // Type mismatch, eg attempt to encrypt with an ECDSA key + MBEDTLS_ERR_PK_BAD_INPUT_DATA = -$3E80; // Bad input parameters to function. + MBEDTLS_ERR_PK_FILE_IO_ERROR = -$3E00; // Read/write of file failed. + MBEDTLS_ERR_PK_KEY_INVALID_VERSION = -$3D80; // Unsupported key version + MBEDTLS_ERR_PK_KEY_INVALID_FORMAT = -$3D00; // Invalid key tag or value. + MBEDTLS_ERR_PK_UNKNOWN_PK_ALG = -$3C80; // Key algorithm is unsupported (only RSA and EC are supported). + MBEDTLS_ERR_PK_PASSWORD_REQUIRED = -$3C00; // Private key password can't be empty. + MBEDTLS_ERR_PK_PASSWORD_MISMATCH = -$3B80; // Given private key password does not allow for correct decryption. + MBEDTLS_ERR_PK_INVALID_PUBKEY = -$3B00; // The pubkey tag or value is invalid (only RSA and EC are supported). + MBEDTLS_ERR_PK_INVALID_ALG = -$3A80; // The algorithm tag or value is invalid. + MBEDTLS_ERR_PK_UNKNOWN_NAMED_CURVE = -$3A00; // Elliptic curve is unsupported (only NIST curves are supported). + MBEDTLS_ERR_PK_FEATURE_UNAVAILABLE = -$3980; // Unavailable feature, e.g. RSA disabled for RSA key. + MBEDTLS_ERR_PK_SIG_LEN_MISMATCH = -$3900; // The signature is valid but its length is less than expected. + MBEDTLS_ERR_PK_HW_ACCEL_FAILED = -$3880; // PK hardware accelerator failed. + + MBEDTLS_ERR_RSA_BAD_INPUT_DATA = -$4080; // Bad input parameters to function. + MBEDTLS_ERR_RSA_INVALID_PADDING = -$4100; // Input data contains invalid padding and is rejected. + MBEDTLS_ERR_RSA_KEY_GEN_FAILED = -$4180; // Something failed during generation of a key. + MBEDTLS_ERR_RSA_KEY_CHECK_FAILED = -$4200; // Key failed to pass the validity check of the library. + MBEDTLS_ERR_RSA_PUBLIC_FAILED = -$4280; // The public key operation failed. + MBEDTLS_ERR_RSA_PRIVATE_FAILED = -$4300; // The private key operation failed. + MBEDTLS_ERR_RSA_VERIFY_FAILED = -$4380; // The PKCS#1 verification failed. + MBEDTLS_ERR_RSA_OUTPUT_TOO_LARGE = -$4400; // The output buffer for decryption is not large enough. + MBEDTLS_ERR_RSA_RNG_FAILED = -$4480; // The random generator failed to generate non-zeros. + MBEDTLS_ERR_RSA_UNSUPPORTED_OPERATION = -$4500; // The implementation does not offer the requested operation, for example, because of security violations or lack of functionality. + MBEDTLS_ERR_RSA_HW_ACCEL_FAILED = -$4580; // RSA hardware accelerator failed. + + MBEDTLS_ERR_ECP_BAD_INPUT_DATA = -$4F80; // Bad input parameters to function. + MBEDTLS_ERR_ECP_BUFFER_TOO_SMALL = -$4F00; // The buffer is too small to write to. + MBEDTLS_ERR_ECP_FEATURE_UNAVAILABLE = -$4E80; // Requested curve not available. + MBEDTLS_ERR_ECP_VERIFY_FAILED = -$4E00; // The signature is not valid. + MBEDTLS_ERR_ECP_ALLOC_FAILED = -$4D80; // Memory allocation failed. + MBEDTLS_ERR_ECP_RANDOM_FAILED = -$4D00; // Generation of random value, such as (ephemeral) key, failed. + MBEDTLS_ERR_ECP_INVALID_KEY = -$4C80; // Invalid private or public key. + MBEDTLS_ERR_ECP_SIG_LEN_MISMATCH = -$4C00; // Signature is valid but shorter than the user-supplied length. + MBEDTLS_ERR_ECP_HW_ACCEL_FAILED = -$4B80; // ECP hardware accelerator failed. + + MBEDTLS_ERR_MD_FEATURE_UNAVAILABLE = -$5080; // The selected feature is not available. + MBEDTLS_ERR_MD_BAD_INPUT_DATA = -$5100; // Bad input parameters to function. + MBEDTLS_ERR_MD_ALLOC_FAILED = -$5180; // Failed to allocate memory. + MBEDTLS_ERR_MD_FILE_IO_ERROR = -$5200; // Opening or reading of file failed. + MBEDTLS_ERR_MD_HW_ACCEL_FAILED = -$5280; // MD hardware accelerator failed. + + MBEDTLS_ERR_CIPHER_FEATURE_UNAVAILABLE = -$6080; // The selected feature is not available. + MBEDTLS_ERR_CIPHER_BAD_INPUT_DATA = -$6100; // Bad input parameters. + MBEDTLS_ERR_CIPHER_ALLOC_FAILED = -$6180; // Failed to allocate memory. + MBEDTLS_ERR_CIPHER_INVALID_PADDING = -$6200; // Input data contains invalid padding and is rejected. + MBEDTLS_ERR_CIPHER_FULL_BLOCK_EXPECTED = -$6280; // Decryption of block requires a full block. + MBEDTLS_ERR_CIPHER_AUTH_FAILED = -$6300; // Authentication failed (for AEAD modes). + MBEDTLS_ERR_CIPHER_INVALID_CONTEXT = -$6380; // The context is invalid. For example, because it was freed. + MBEDTLS_ERR_CIPHER_HW_ACCEL_FAILED = -$6400; // Cipher hardware accelerator failed. + + MBEDTLS_ERR_SSL_FEATURE_UNAVAILABLE = -$7080; // The requested feature is not available. + MBEDTLS_ERR_SSL_BAD_INPUT_DATA = -$7100; // Bad input parameters to function. + MBEDTLS_ERR_SSL_INVALID_MAC = -$7180; // Verification of the message MAC failed. + MBEDTLS_ERR_SSL_INVALID_RECORD = -$7200; // An invalid SSL record was received. + MBEDTLS_ERR_SSL_CONN_EOF = -$7280; // The connection indicated an EOF. + MBEDTLS_ERR_SSL_UNKNOWN_CIPHER = -$7300; // An unknown cipher was received. + MBEDTLS_ERR_SSL_NO_CIPHER_CHOSEN = -$7380; // The server has no ciphersuites in common with the client. + MBEDTLS_ERR_SSL_NO_RNG = -$7400; // No RNG was provided to the SSL module. + MBEDTLS_ERR_SSL_NO_CLIENT_CERTIFICATE = -$7480; // No client certification received from the client, but required by the authentication mode. + MBEDTLS_ERR_SSL_CERTIFICATE_TOO_LARGE = -$7500; // Our own certificate(s) is/are too large to send in an SSL message. + MBEDTLS_ERR_SSL_CERTIFICATE_REQUIRED = -$7580; // The own certificate is not set, but needed by the server. + MBEDTLS_ERR_SSL_PRIVATE_KEY_REQUIRED = -$7600; // The own private key or pre-shared key is not set, but needed. + MBEDTLS_ERR_SSL_CA_CHAIN_REQUIRED = -$7680; // No CA Chain is set, but required to operate. + MBEDTLS_ERR_SSL_UNEXPECTED_MESSAGE = -$7700; // An unexpected message was received from our peer. + MBEDTLS_ERR_SSL_FATAL_ALERT_MESSAGE = -$7780; // A fatal alert message was received from our peer. + MBEDTLS_ERR_SSL_PEER_VERIFY_FAILED = -$7800; // Verification of our peer failed. + MBEDTLS_ERR_SSL_PEER_CLOSE_NOTIFY = -$7880; // The peer notified us that the connection is going to be closed. + MBEDTLS_ERR_SSL_BAD_HS_CLIENT_HELLO = -$7900; // Processing of the ClientHello handshake message failed. + MBEDTLS_ERR_SSL_BAD_HS_SERVER_HELLO = -$7980; // Processing of the ServerHello handshake message failed. + MBEDTLS_ERR_SSL_BAD_HS_CERTIFICATE = -$7A00; // Processing of the Certificate handshake message failed. + MBEDTLS_ERR_SSL_BAD_HS_CERTIFICATE_REQUEST = -$7A80; // Processing of the CertificateRequest handshake message failed. + MBEDTLS_ERR_SSL_BAD_HS_SERVER_KEY_EXCHANGE = -$7B00; // Processing of the ServerKeyExchange handshake message failed. + MBEDTLS_ERR_SSL_BAD_HS_SERVER_HELLO_DONE = -$7B80; // Processing of the ServerHelloDone handshake message failed. + MBEDTLS_ERR_SSL_BAD_HS_CLIENT_KEY_EXCHANGE = -$7C00; // Processing of the ClientKeyExchange handshake message failed. + MBEDTLS_ERR_SSL_BAD_HS_CLIENT_KEY_EXCHANGE_RP = -$7C80; // Processing of the ClientKeyExchange handshake message failed in DHM / ECDH Read Public. + MBEDTLS_ERR_SSL_BAD_HS_CLIENT_KEY_EXCHANGE_CS = -$7D00; // Processing of the ClientKeyExchange handshake message failed in DHM / ECDH Calculate Secret. + MBEDTLS_ERR_SSL_BAD_HS_CERTIFICATE_VERIFY = -$7D80; // Processing of the CertificateVerify handshake message failed. + MBEDTLS_ERR_SSL_BAD_HS_CHANGE_CIPHER_SPEC = -$7E00; // Processing of the ChangeCipherSpec handshake message failed. + MBEDTLS_ERR_SSL_BAD_HS_FINISHED = -$7E80; // Processing of the Finished handshake message failed. + MBEDTLS_ERR_SSL_ALLOC_FAILED = -$7F00; // Memory allocation failed + MBEDTLS_ERR_SSL_HW_ACCEL_FAILED = -$7F80; // Hardware acceleration function returned with error + MBEDTLS_ERR_SSL_HW_ACCEL_FALLTHROUGH = -$6F80; // Hardware acceleration function skipped / left alone data + MBEDTLS_ERR_SSL_COMPRESSION_FAILED = -$6F00; // Processing of the compression / decompression failed + MBEDTLS_ERR_SSL_BAD_HS_PROTOCOL_VERSION = -$6E80; // Handshake protocol not within min/max boundaries + MBEDTLS_ERR_SSL_BAD_HS_NEW_SESSION_TICKET = -$6E00; // Processing of the NewSessionTicket handshake message failed. + MBEDTLS_ERR_SSL_SESSION_TICKET_EXPIRED = -$6D80; // Session ticket has expired. + MBEDTLS_ERR_SSL_PK_TYPE_MISMATCH = -$6D00; // Public key type mismatch (eg, asked for RSA key exchange and presented EC key) + MBEDTLS_ERR_SSL_UNKNOWN_IDENTITY = -$6C80; // Unknown identity received (eg, PSK identity) + MBEDTLS_ERR_SSL_INTERNAL_ERROR = -$6C00; // Internal error (eg, unexpected failure in lower-level module) + MBEDTLS_ERR_SSL_COUNTER_WRAPPING = -$6B80; // A counter would wrap (eg, too many messages exchanged). + MBEDTLS_ERR_SSL_WAITING_SERVER_HELLO_RENEGO = -$6B00; // Unexpected message at ServerHello in renegotiation. + MBEDTLS_ERR_SSL_HELLO_VERIFY_REQUIRED = -$6A80; // DTLS client must retry for hello verification + MBEDTLS_ERR_SSL_BUFFER_TOO_SMALL = -$6A00; // A buffer is too small to receive or write a message + MBEDTLS_ERR_SSL_NO_USABLE_CIPHERSUITE = -$6980; // None of the common ciphersuites is usable (eg, no suitable certificate, see debug messages). + MBEDTLS_ERR_SSL_WANT_READ = -$6900; // Connection requires a read call. + MBEDTLS_ERR_SSL_WANT_WRITE = -$6880; // Connection requires a write call. + MBEDTLS_ERR_SSL_TIMEOUT = -$6800; // The operation timed out. + MBEDTLS_ERR_SSL_CLIENT_RECONNECT = -$6780; // The client initiated a reconnect from the same port. + MBEDTLS_ERR_SSL_UNEXPECTED_RECORD = -$6700; // Record header looks valid but is not expected. + MBEDTLS_ERR_SSL_NON_FATAL = -$6680; // The alert message received indicates a non-fatal error. + MBEDTLS_ERR_SSL_INVALID_VERIFY_HASH = -$6600; // Couldn't set the hash for verifying CertificateVerify + + // Various constants + + MBEDTLS_SSL_MAJOR_VERSION_3 = 3; + MBEDTLS_SSL_MINOR_VERSION_0 = 0; // SSL v3.0 + MBEDTLS_SSL_MINOR_VERSION_1 = 1; // TLS v1.0 + MBEDTLS_SSL_MINOR_VERSION_2 = 2; // TLS v1.1 + MBEDTLS_SSL_MINOR_VERSION_3 = 3; // TLS v1.2 + + MBEDTLS_SSL_TRANSPORT_STREAM = 0; // TLS + MBEDTLS_SSL_TRANSPORT_DATAGRAM = 1; // DTLS + + MBEDTLS_SSL_MAX_HOST_NAME_LEN = 255; // Maximum host name defined in RFC 1035 + + // RFC 6066 section 4, see also mfl_code_to_length in ssl_tls.c + // NONE must be zero so that memset()ing structure to zero works + + MBEDTLS_SSL_MAX_FRAG_LEN_NONE = 0; // don't use this extension + MBEDTLS_SSL_MAX_FRAG_LEN_512 = 1; // MaxFragmentLength 2^9 + MBEDTLS_SSL_MAX_FRAG_LEN_1024 = 2; // MaxFragmentLength 2^10 + MBEDTLS_SSL_MAX_FRAG_LEN_2048 = 3; // MaxFragmentLength 2^11 + MBEDTLS_SSL_MAX_FRAG_LEN_4096 = 4; // MaxFragmentLength 2^12 + MBEDTLS_SSL_MAX_FRAG_LEN_INVALID = 5; // first invalid value + + MBEDTLS_SSL_IS_CLIENT = 0; + MBEDTLS_SSL_IS_SERVER = 1; + + MBEDTLS_SSL_IS_NOT_FALLBACK = 0; + MBEDTLS_SSL_IS_FALLBACK = 1; + + MBEDTLS_SSL_EXTENDED_MS_DISABLED = 0; + MBEDTLS_SSL_EXTENDED_MS_ENABLED = 1; + + MBEDTLS_SSL_ETM_DISABLED = 0; + MBEDTLS_SSL_ETM_ENABLED = 1; + + MBEDTLS_SSL_COMPRESS_NULL = 0; + MBEDTLS_SSL_COMPRESS_DEFLATE = 1; + + MBEDTLS_SSL_VERIFY_NONE = 0; + MBEDTLS_SSL_VERIFY_OPTIONAL = 1; + MBEDTLS_SSL_VERIFY_REQUIRED = 2; + MBEDTLS_SSL_VERIFY_UNSET = 3; // Used only for sni_authmode + + MBEDTLS_SSL_LEGACY_RENEGOTIATION = 0; + MBEDTLS_SSL_SECURE_RENEGOTIATION = 1; + + MBEDTLS_SSL_RENEGOTIATION_DISABLED = 0; + MBEDTLS_SSL_RENEGOTIATION_ENABLED = 1; + + MBEDTLS_SSL_ANTI_REPLAY_DISABLED = 0; + MBEDTLS_SSL_ANTI_REPLAY_ENABLED = 1; + + MBEDTLS_SSL_RENEGOTIATION_NOT_ENFORCED = -1; + MBEDTLS_SSL_RENEGO_MAX_RECORDS_DEFAULT = 16; + + MBEDTLS_SSL_LEGACY_NO_RENEGOTIATION = 0; + MBEDTLS_SSL_LEGACY_ALLOW_RENEGOTIATION = 1; + MBEDTLS_SSL_LEGACY_BREAK_HANDSHAKE = 2; + + MBEDTLS_SSL_TRUNC_HMAC_DISABLED = 0; + MBEDTLS_SSL_TRUNC_HMAC_ENABLED = 1; + MBEDTLS_SSL_TRUNCATED_HMAC_LEN = 10; // 80 bits, rfc 6066 section 7 + + MBEDTLS_SSL_SESSION_TICKETS_DISABLED = 0; + MBEDTLS_SSL_SESSION_TICKETS_ENABLED = 1; + + MBEDTLS_SSL_CBC_RECORD_SPLITTING_DISABLED = 0; + MBEDTLS_SSL_CBC_RECORD_SPLITTING_ENABLED = 1; + + MBEDTLS_SSL_ARC4_ENABLED = 0; + MBEDTLS_SSL_ARC4_DISABLED = 1; + + MBEDTLS_SSL_PRESET_DEFAULT = 0; + MBEDTLS_SSL_PRESET_SUITEB = 2; + + MBEDTLS_SSL_CERT_REQ_CA_LIST_ENABLED = 1; + MBEDTLS_SSL_CERT_REQ_CA_LIST_DISABLED = 0; + + // Default range for DTLS retransmission timer value, in milliseconds. + // RFC 6347 4.2.4.1 says from 1 second to 60 seconds. + + MBEDTLS_SSL_DTLS_TIMEOUT_DFL_MIN = 1000; + MBEDTLS_SSL_DTLS_TIMEOUT_DFL_MAX = 60000; + + MBEDTLS_SSL_INITIAL_HANDSHAKE = 0; + MBEDTLS_SSL_RENEGOTIATION_IN_PROGRESS = 1; // In progress + MBEDTLS_SSL_RENEGOTIATION_DONE = 2; // Done or aborted + MBEDTLS_SSL_RENEGOTIATION_PENDING = 3; // Requested (server only) + + MBEDTLS_SSL_RETRANS_PREPARING = 0; + MBEDTLS_SSL_RETRANS_SENDING = 1; + MBEDTLS_SSL_RETRANS_WAITING = 2; + MBEDTLS_SSL_RETRANS_FINISHED = 3; + + MBEDTLS_SSL_MSG_CHANGE_CIPHER_SPEC = 20; + MBEDTLS_SSL_MSG_ALERT = 21; + MBEDTLS_SSL_MSG_HANDSHAKE = 22; + MBEDTLS_SSL_MSG_APPLICATION_DATA = 23; + + MBEDTLS_SSL_ALERT_LEVEL_WARNING = 1; + MBEDTLS_SSL_ALERT_LEVEL_FATAL = 2; + + MBEDTLS_SSL_ALERT_MSG_CLOSE_NOTIFY = 0; // 0x00 + MBEDTLS_SSL_ALERT_MSG_UNEXPECTED_MESSAGE = 10; // 0x0A + MBEDTLS_SSL_ALERT_MSG_BAD_RECORD_MAC = 20; // 0x14 + MBEDTLS_SSL_ALERT_MSG_DECRYPTION_FAILED = 21; // 0x15 + MBEDTLS_SSL_ALERT_MSG_RECORD_OVERFLOW = 22; // 0x16 + MBEDTLS_SSL_ALERT_MSG_DECOMPRESSION_FAILURE = 30; // 0x1E + MBEDTLS_SSL_ALERT_MSG_HANDSHAKE_FAILURE = 40; // 0x28 + MBEDTLS_SSL_ALERT_MSG_NO_CERT = 41; // 0x29 + MBEDTLS_SSL_ALERT_MSG_BAD_CERT = 42; // 0x2A + MBEDTLS_SSL_ALERT_MSG_UNSUPPORTED_CERT = 43; // 0x2B + MBEDTLS_SSL_ALERT_MSG_CERT_REVOKED = 44; // 0x2C + MBEDTLS_SSL_ALERT_MSG_CERT_EXPIRED = 45; // 0x2D + MBEDTLS_SSL_ALERT_MSG_CERT_UNKNOWN = 46; // 0x2E + MBEDTLS_SSL_ALERT_MSG_ILLEGAL_PARAMETER = 47; // 0x2F + MBEDTLS_SSL_ALERT_MSG_UNKNOWN_CA = 48; // 0x30 + MBEDTLS_SSL_ALERT_MSG_ACCESS_DENIED = 49; // 0x31 + MBEDTLS_SSL_ALERT_MSG_DECODE_ERROR = 50; // 0x32 + MBEDTLS_SSL_ALERT_MSG_DECRYPT_ERROR = 51; // 0x33 + MBEDTLS_SSL_ALERT_MSG_EXPORT_RESTRICTION = 60; // 0x3C + MBEDTLS_SSL_ALERT_MSG_PROTOCOL_VERSION = 70; // 0x46 + MBEDTLS_SSL_ALERT_MSG_INSUFFICIENT_SECURITY = 71; // 0x47 + MBEDTLS_SSL_ALERT_MSG_INTERNAL_ERROR = 80; // 0x50 + MBEDTLS_SSL_ALERT_MSG_INAPROPRIATE_FALLBACK = 86; // 0x56 + MBEDTLS_SSL_ALERT_MSG_USER_CANCELED = 90; // 0x5A + MBEDTLS_SSL_ALERT_MSG_NO_RENEGOTIATION = 100; // 0x64 + MBEDTLS_SSL_ALERT_MSG_UNSUPPORTED_EXT = 110; // 0x6E + MBEDTLS_SSL_ALERT_MSG_UNRECOGNIZED_NAME = 112; // 0x70 + MBEDTLS_SSL_ALERT_MSG_UNKNOWN_PSK_IDENTITY = 115; // 0x73 + MBEDTLS_SSL_ALERT_MSG_NO_APPLICATION_PROTOCOL = 120; // 0x78 + + MBEDTLS_SSL_HS_HELLO_REQUEST = 0; + MBEDTLS_SSL_HS_CLIENT_HELLO = 1; + MBEDTLS_SSL_HS_SERVER_HELLO = 2; + MBEDTLS_SSL_HS_HELLO_VERIFY_REQUEST = 3; + MBEDTLS_SSL_HS_NEW_SESSION_TICKET = 4; + MBEDTLS_SSL_HS_CERTIFICATE = 11; + MBEDTLS_SSL_HS_SERVER_KEY_EXCHANGE = 12; + MBEDTLS_SSL_HS_CERTIFICATE_REQUEST = 13; + MBEDTLS_SSL_HS_SERVER_HELLO_DONE = 14; + MBEDTLS_SSL_HS_CERTIFICATE_VERIFY = 15; + MBEDTLS_SSL_HS_CLIENT_KEY_EXCHANGE = 16; + MBEDTLS_SSL_HS_FINISHED = 20; + + // TLS extensions + MBEDTLS_TLS_EXT_SERVERNAME = 0; + MBEDTLS_TLS_EXT_SERVERNAME_HOSTNAME = 0; + MBEDTLS_TLS_EXT_MAX_FRAGMENT_LENGTH = 1; + MBEDTLS_TLS_EXT_TRUNCATED_HMAC = 4; + MBEDTLS_TLS_EXT_SUPPORTED_ELLIPTIC_CURVES = 10; + MBEDTLS_TLS_EXT_SUPPORTED_POINT_FORMATS = 11; + MBEDTLS_TLS_EXT_SIG_ALG = 13; + MBEDTLS_TLS_EXT_ALPN = 16; + MBEDTLS_TLS_EXT_ENCRYPT_THEN_MAC = 22; // 0x16 + MBEDTLS_TLS_EXT_EXTENDED_MASTER_SECRET = $0017; // 23 + MBEDTLS_TLS_EXT_SESSION_TICKET = 35; + MBEDTLS_TLS_EXT_ECJPAKE_KKPP = 256; // experimental + MBEDTLS_TLS_EXT_RENEGOTIATION_INFO = $FF01; + + MBEDTLS_ENTROPY_SOURCE_STRONG = 1; // Entropy source is strong + MBEDTLS_ENTROPY_SOURCE_WEAK = 0; // Entropy source is weak + +{$REGION 'libmbedcrypto'} +function mbedtls_entropy_add_source(ctx: PMbedtls_Entropy_Context; f_source: TEntropyFunc; p_source: Pointer; threshold: Size_T; strong: Integer): Integer; cdecl; external {$IFDEF __HAS_MBED_TLS_LIB__}LIB_MBED_CRYPTO{$ENDIF} name _PU + 'mbedtls_entropy_add_source'; +procedure mbedtls_entropy_free(ctx: PMbedtls_Entropy_Context); cdecl; external {$IFDEF __HAS_MBED_TLS_LIB__}LIB_MBED_CRYPTO{$ENDIF} name _PU + 'mbedtls_entropy_free'; +function mbedtls_entropy_func(data: Pointer; output: MarshaledAString; len: Size_T): Integer; cdecl; external {$IFDEF __HAS_MBED_TLS_LIB__}LIB_MBED_CRYPTO{$ENDIF} name _PU + 'mbedtls_entropy_func'; +procedure mbedtls_entropy_init(ctx: PMbedtls_Entropy_Context); cdecl; external {$IFDEF __HAS_MBED_TLS_LIB__}LIB_MBED_CRYPTO{$ENDIF} name _PU + 'mbedtls_entropy_init'; + +procedure mbedtls_ctr_drbg_init(ctx: PMbedtls_CTR_DRBG_Context); cdecl; external {$IFDEF __HAS_MBED_TLS_LIB__}LIB_MBED_CRYPTO{$ENDIF} name _PU + 'mbedtls_ctr_drbg_init'; +function mbedtls_ctr_drbg_seed(ctx: PMbedtls_CTR_DRBG_Context; f_entropy: TEntropyFunc; p_entropy: Pointer; custom: MarshaledAString; len: Size_T): Integer; cdecl; external {$IFDEF __HAS_MBED_TLS_LIB__}LIB_MBED_CRYPTO{$ENDIF} name _PU + 'mbedtls_ctr_drbg_seed'; +function mbedtls_ctr_drbg_random_with_add(p_rng: Pointer; output: MarshaledAString; output_len: Size_T; additional: MarshaledAString; add_len: Size_T): Integer; cdecl; external {$IFDEF __HAS_MBED_TLS_LIB__}LIB_MBED_CRYPTO{$ENDIF} name _PU + 'mbedtls_ctr_drbg_random_with_add'; +function mbedtls_ctr_drbg_random(p_rng: Pointer; output: MarshaledAString; output_len: Size_T): Integer; cdecl; external {$IFDEF __HAS_MBED_TLS_LIB__}LIB_MBED_CRYPTO{$ENDIF} name _PU + 'mbedtls_ctr_drbg_random'; +procedure mbedtls_ctr_drbg_free(ctx: PMbedtls_CTR_DRBG_Context); cdecl; external {$IFDEF __HAS_MBED_TLS_LIB__}LIB_MBED_CRYPTO{$ENDIF} name _PU + 'mbedtls_ctr_drbg_free'; + +function mbedtls_pk_parse_key(pk: PMbedtls_PK_Context; key: PByte; keylen: Size_T; pwd: PByte; pwdlen: Size_T): Integer; cdecl; external {$IFDEF __HAS_MBED_TLS_LIB__}LIB_MBED_CRYPTO{$ENDIF} name _PU + 'mbedtls_pk_parse_key'; + +procedure mbedtls_pk_init(ctx: PMbedtls_PK_Context); cdecl; external {$IFDEF __HAS_MBED_TLS_LIB__}LIB_MBED_CRYPTO{$ENDIF} name _PU + 'mbedtls_pk_init'; +procedure mbedtls_pk_free(ctx: PMbedtls_PK_Context); cdecl; external {$IFDEF __HAS_MBED_TLS_LIB__}LIB_MBED_CRYPTO{$ENDIF} name _PU + 'mbedtls_pk_free'; + +function mbedtls_version_get_number: Integer; cdecl; external {$IFDEF __HAS_MBED_TLS_LIB__}LIB_MBED_CRYPTO{$ENDIF} name _PU + 'mbedtls_version_get_number'; +procedure mbedtls_version_get_string(string_: MarshaledAString); cdecl; external {$IFDEF __HAS_MBED_TLS_LIB__}LIB_MBED_CRYPTO{$ENDIF} name _PU + 'mbedtls_version_get_string'; +procedure mbedtls_version_get_string_full(string_: MarshaledAString); cdecl; external {$IFDEF __HAS_MBED_TLS_LIB__}LIB_MBED_CRYPTO{$ENDIF} name _PU + 'mbedtls_version_get_string_full'; + +procedure mbedtls_strerror(errnum: Integer; buffer: MarshaledAString; buflen: Size_T); cdecl; external {$IFDEF __HAS_MBED_TLS_LIB__}LIB_MBED_CRYPTO{$ENDIF} name _PU + 'mbedtls_strerror'; + +procedure mbedtls_threading_set_alt(_mutex_init: Pointer; _mutex_free: Pointer; _mutex_lock: Pointer; _mutex_unlock: Pointer); cdecl; external {$IFDEF __HAS_MBED_TLS_LIB__}LIB_MBED_CRYPTO{$ENDIF} name _PU + 'mbedtls_threading_set_alt'; +procedure mbedtls_threading_free_alt; cdecl; external {$IFDEF __HAS_MBED_TLS_LIB__}LIB_MBED_CRYPTO{$ENDIF} name _PU + 'mbedtls_threading_free_alt'; +{$ENDREGION} + +{$REGION 'libmbedtls'} +function mbedtls_ssl_close_notify(ssl: PMbedtls_SSL_Context): Integer; cdecl; external {$IFDEF __HAS_MBED_TLS_LIB__}LIB_MBED_TLS{$ENDIF} name _PU + 'mbedtls_ssl_close_notify'; +procedure mbedtls_ssl_free(ssl: PMbedtls_SSL_Context); cdecl; external {$IFDEF __HAS_MBED_TLS_LIB__}LIB_MBED_TLS{$ENDIF} name _PU + 'mbedtls_ssl_free'; +function mbedtls_ssl_get_verify_result(ssl: PMbedtls_SSL_Context): UInt32_T; cdecl; external {$IFDEF __HAS_MBED_TLS_LIB__}LIB_MBED_TLS{$ENDIF} name _PU + 'mbedtls_ssl_get_verify_result'; +function mbedtls_ssl_get_ciphersuite(const ssl: PMbedtls_SSL_Context): MarshaledAString; cdecl; external {$IFDEF __HAS_MBED_TLS_LIB__}LIB_MBED_TLS{$ENDIF} name _PU + 'mbedtls_ssl_get_ciphersuite'; +function mbedtls_ssl_get_version(const ssl: PMbedtls_SSL_Context): MarshaledAString; cdecl; external {$IFDEF __HAS_MBED_TLS_LIB__}LIB_MBED_TLS{$ENDIF} name _PU + 'mbedtls_ssl_get_version'; +function mbedtls_ssl_get_max_frag_len(const ssl: PMbedtls_SSL_Context): Size_T; cdecl; external {$IFDEF __HAS_MBED_TLS_LIB__}LIB_MBED_TLS{$ENDIF} name _PU + 'mbedtls_ssl_get_max_frag_len'; +function mbedtls_ssl_get_peer_cert(const ssl: PMbedtls_SSL_Context): PMbedtls_X509_CRT; cdecl; external {$IFDEF __HAS_MBED_TLS_LIB__}LIB_MBED_TLS{$ENDIF} name _PU + 'mbedtls_ssl_get_peer_cert'; +function mbedtls_ssl_get_session(const ssl: PMbedtls_SSL_Context; session: PMbedtls_SSL_Session): Integer; cdecl; external {$IFDEF __HAS_MBED_TLS_LIB__}LIB_MBED_TLS{$ENDIF} name _PU + 'mbedtls_ssl_get_session'; +function mbedtls_ssl_get_ciphersuite_name(const ciphersuite_id: Integer): MarshaledAString; cdecl; external {$IFDEF __HAS_MBED_TLS_LIB__}LIB_MBED_TLS{$ENDIF} name _PU + 'mbedtls_ssl_get_ciphersuite_name'; +function mbedtls_ssl_get_ciphersuite_id(const ciphersuite_name: MarshaledAString): Integer; cdecl; external {$IFDEF __HAS_MBED_TLS_LIB__}LIB_MBED_TLS{$ENDIF} name _PU + 'mbedtls_ssl_get_ciphersuite_id'; +function mbedtls_ssl_handshake(ssl: PMbedtls_SSL_Context): Integer; cdecl; external {$IFDEF __HAS_MBED_TLS_LIB__}LIB_MBED_TLS{$ENDIF} name _PU + 'mbedtls_ssl_handshake'; +procedure mbedtls_ssl_init(ssl: PMbedtls_SSL_Context); cdecl; external {$IFDEF __HAS_MBED_TLS_LIB__}LIB_MBED_TLS{$ENDIF} name _PU + 'mbedtls_ssl_init'; +function mbedtls_ssl_list_ciphersuites: PInteger; cdecl; external {$IFDEF __HAS_MBED_TLS_LIB__}LIB_MBED_TLS{$ENDIF} name _PU + 'mbedtls_ssl_list_ciphersuites'; +function mbedtls_ssl_read(ssl: PMbedtls_SSL_Context; buf: Pointer; len: Size_T): Integer; cdecl; external {$IFDEF __HAS_MBED_TLS_LIB__}LIB_MBED_TLS{$ENDIF} name _PU + 'mbedtls_ssl_read'; +function mbedtls_ssl_set_hostname(ssl: PMbedtls_SSL_Context; hostname: MarshaledAString): Integer; cdecl; external {$IFDEF __HAS_MBED_TLS_LIB__}LIB_MBED_TLS{$ENDIF} name _PU + 'mbedtls_ssl_set_hostname'; +procedure mbedtls_ssl_set_bio(ssl: PMbedtls_SSL_Context; p_bio: Pointer; f_send: TNetSendFunc; f_recv: TNetRecvFunc; f_recv_timeout: TNetRecvTimeoutFunc); cdecl; external {$IFDEF __HAS_MBED_TLS_LIB__}LIB_MBED_TLS{$ENDIF} name _PU + 'mbedtls_ssl_set_bio'; +procedure mbedtls_ssl_set_timer_cb(ssl: PMbedtls_SSL_Context; p_timer: Pointer; f_set_timer: TSetTimerFunc; f_get_timer: TGetTimerFunc); cdecl; external {$IFDEF __HAS_MBED_TLS_LIB__}LIB_MBED_TLS{$ENDIF} name _PU + 'mbedtls_ssl_set_timer_cb'; +function mbedtls_ssl_setup(ssl: PMbedtls_SSL_Context; conf: PMbedtls_SSL_Config): Integer; cdecl; external {$IFDEF __HAS_MBED_TLS_LIB__}LIB_MBED_TLS{$ENDIF} name _PU + 'mbedtls_ssl_setup'; +function mbedtls_ssl_write(ssl: PMbedtls_SSL_Context; const buf: Pointer; len: Size_T): Integer; cdecl; external {$IFDEF __HAS_MBED_TLS_LIB__}LIB_MBED_TLS{$ENDIF} name _PU + 'mbedtls_ssl_write'; + +procedure mbedtls_ssl_cache_init(cache: PMbedtls_SSL_Cache_Context); cdecl; external {$IFDEF __HAS_MBED_TLS_LIB__}LIB_MBED_TLS{$ENDIF} name _PU + 'mbedtls_ssl_cache_init'; + +procedure mbedtls_ssl_config_init(conf: PMbedtls_SSL_Config); cdecl; external {$IFDEF __HAS_MBED_TLS_LIB__}LIB_MBED_TLS{$ENDIF} name _PU + 'mbedtls_ssl_config_init'; +function mbedtls_ssl_config_defaults(conf: PMbedtls_SSL_Config; endpoint: Integer; transport: Integer; preset: Integer): Integer; cdecl; external {$IFDEF __HAS_MBED_TLS_LIB__}LIB_MBED_TLS{$ENDIF} name _PU + 'mbedtls_ssl_config_defaults'; +procedure mbedtls_ssl_conf_authmode(conf: PMbedtls_SSL_Config; authmode: Integer); cdecl; external {$IFDEF __HAS_MBED_TLS_LIB__}LIB_MBED_TLS{$ENDIF} name _PU + 'mbedtls_ssl_conf_authmode'; +procedure mbedtls_ssl_conf_ca_chain(conf: PMbedtls_SSL_Config; ca_chain: PMbedtls_X509_CRT; ca_crl: PMbedtls_X509_Crl); cdecl; external {$IFDEF __HAS_MBED_TLS_LIB__}LIB_MBED_TLS{$ENDIF} name _PU + 'mbedtls_ssl_conf_ca_chain'; +procedure mbedtls_ssl_conf_transport(conf: PMbedtls_SSL_Config; transport: Integer); cdecl; external {$IFDEF __HAS_MBED_TLS_LIB__}LIB_MBED_TLS{$ENDIF} name _PU + 'mbedtls_ssl_conf_transport'; +procedure mbedtls_ssl_conf_rng(conf: PMbedtls_SSL_Config; f_rng: TrngFunc; p_rng: Pointer); cdecl; external {$IFDEF __HAS_MBED_TLS_LIB__}LIB_MBED_TLS{$ENDIF} name _PU + 'mbedtls_ssl_conf_rng'; +procedure mbedtls_ssl_conf_dbg(conf: PMbedtls_SSL_Config; f_dbg: TdbgFunc; p_dbg: Pointer); cdecl; external {$IFDEF __HAS_MBED_TLS_LIB__}LIB_MBED_TLS{$ENDIF} name _PU + 'mbedtls_ssl_conf_dbg'; +procedure mbedtls_ssl_config_free(conf: PMbedtls_SSL_Config); cdecl; external {$IFDEF __HAS_MBED_TLS_LIB__}LIB_MBED_TLS{$ENDIF} name _PU + 'mbedtls_ssl_config_free'; + +procedure mbedtls_ssl_conf_ciphersuites(conf: PMbedtls_SSL_Config; ciphersuites: PInteger); cdecl; external {$IFDEF __HAS_MBED_TLS_LIB__}LIB_MBED_TLS{$ENDIF} name _PU + 'mbedtls_ssl_conf_ciphersuites'; + +function mbedtls_ssl_session_reset(ssl: PMbedtls_SSL_Context): Integer; cdecl; external {$IFDEF __HAS_MBED_TLS_LIB__}LIB_MBED_TLS{$ENDIF} name _PU + 'mbedtls_ssl_session_reset'; +function mbedtls_ssl_set_session(ssl: PMbedtls_SSL_Context; const session: PMbedtls_SSL_Session): Integer; cdecl; external {$IFDEF __HAS_MBED_TLS_LIB__}LIB_MBED_TLS{$ENDIF} name _PU + 'mbedtls_ssl_set_session'; +procedure mbedtls_ssl_conf_max_version(conf: PMbedtls_SSL_Config; major, minor: Integer); cdecl; external {$IFDEF __HAS_MBED_TLS_LIB__}LIB_MBED_TLS{$ENDIF} name _PU + 'mbedtls_ssl_conf_max_version'; +procedure mbedtls_ssl_conf_min_version(conf: PMbedtls_SSL_Config; major, minor: Integer); cdecl; external {$IFDEF __HAS_MBED_TLS_LIB__}LIB_MBED_TLS{$ENDIF} name _PU + 'mbedtls_ssl_conf_min_version'; +function mbedtls_ssl_get_bytes_avail(ssl: PMbedtls_SSL_Context): Size_T; cdecl; external {$IFDEF __HAS_MBED_TLS_LIB__}LIB_MBED_TLS{$ENDIF} name _PU + 'mbedtls_ssl_get_bytes_avail'; + +procedure mbedtls_ssl_conf_session_cache(conf: PMbedtls_SSL_Config; p_cache: Pointer; f_get_cache: TGetCacheFunc; f_set_cache: TSetCacheFunc); cdecl; external {$IFDEF __HAS_MBED_TLS_LIB__}LIB_MBED_TLS{$ENDIF} name _PU + 'mbedtls_ssl_conf_session_cache'; + +function mbedtls_ssl_cache_get(data: Pointer; session: PMbedtls_SSL_Session): Integer; cdecl; external {$IFDEF __HAS_MBED_TLS_LIB__}LIB_MBED_TLS{$ENDIF} name _PU + 'mbedtls_ssl_cache_get'; +function mbedtls_ssl_cache_set(data: Pointer; const session: PMbedtls_SSL_Session): Integer; cdecl; external {$IFDEF __HAS_MBED_TLS_LIB__}LIB_MBED_TLS{$ENDIF} name _PU + 'mbedtls_ssl_cache_set'; + +function mbedtls_ssl_conf_own_cert(conf: PMbedtls_SSL_Config; own_cert: PMbedtls_X509_CRT; pk_key: PMbedtls_PK_Context): Integer; cdecl; external {$IFDEF __HAS_MBED_TLS_LIB__}LIB_MBED_TLS{$ENDIF} name _PU + 'mbedtls_ssl_conf_own_cert'; + +procedure mbedtls_ssl_cache_free(cache: PMbedtls_SSL_Cache_Context); cdecl; external {$IFDEF __HAS_MBED_TLS_LIB__}LIB_MBED_TLS{$ENDIF} name _PU + 'mbedtls_ssl_cache_free'; +{$ENDREGION} + +{$REGION 'libmbedx509'} +procedure mbedtls_x509_crt_init(crt: PMbedtls_X509_CRT); cdecl; external {$IFDEF __HAS_MBED_TLS_LIB__}LIB_MBED_X509{$ENDIF} name _PU + 'mbedtls_x509_crt_init'; +procedure mbedtls_x509_crt_free(crt: PMbedtls_X509_CRT); cdecl; external {$IFDEF __HAS_MBED_TLS_LIB__}LIB_MBED_X509{$ENDIF} name _PU + 'mbedtls_x509_crt_free'; +function mbedtls_x509_crt_parse(chain: PMbedtls_X509_CRT; buf: PByte; buflen: Size_T): Integer; cdecl; external {$IFDEF __HAS_MBED_TLS_LIB__}LIB_MBED_X509{$ENDIF} name _PU + 'mbedtls_x509_crt_parse'; +function mbedtls_x509_crt_verify_info(buf: MarshaledAString; size_: Size_T; const prefix: MarshaledAString; flags: UInt32_T): Integer; cdecl; external {$IFDEF __HAS_MBED_TLS_LIB__}LIB_MBED_X509{$ENDIF} name _PU + 'mbedtls_x509_crt_verify_info'; +{$ENDREGION} + +type + TMbedtls_Cipher_ID = ( + MBEDTLS_CIPHER_ID_NONE = 0, // **< Placeholder to mark the end of cipher ID lists. + MBEDTLS_CIPHER_ID_NULL, // **< The identity cipher, treated as a stream cipher. + MBEDTLS_CIPHER_ID_AES, // **< The AES cipher. + MBEDTLS_CIPHER_ID_DES, // **< The DES cipher. + MBEDTLS_CIPHER_ID_3DES, // **< The Triple DES cipher. + MBEDTLS_CIPHER_ID_CAMELLIA, // **< The Camellia cipher. + MBEDTLS_CIPHER_ID_BLOWFISH, // **< The Blowfish cipher. + MBEDTLS_CIPHER_ID_ARC4, // **< The RC4 cipher. + MBEDTLS_CIPHER_ID_ARIA, // **< The Aria cipher. + MBEDTLS_CIPHER_ID_CHACHA20 // **< The ChaCha20 cipher. + ); + + TMbedtls_Cipher_Mode = ( + MBEDTLS_MODE_NONE = 0, // **< None. + MBEDTLS_MODE_ECB, // **< The ECB cipher mode. + MBEDTLS_MODE_CBC, // **< The CBC cipher mode. + MBEDTLS_MODE_CFB, // **< The CFB cipher mode. + MBEDTLS_MODE_OFB, // **< The OFB cipher mode. + MBEDTLS_MODE_CTR, // **< The CTR cipher mode. + MBEDTLS_MODE_GCM, // **< The GCM cipher mode. + MBEDTLS_MODE_STREAM, // **< The stream cipher mode. + MBEDTLS_MODE_CCM, // **< The CCM cipher mode. + MBEDTLS_MODE_XTS, // **< The XTS cipher mode. + MBEDTLS_MODE_CHACHAPOLY // **< The ChaCha-Poly cipher mode. + ); + +function mbedtls_ssl_handshake_client_step(ssl: PMbedtls_SSL_Context): Integer; cdecl; external {$IFDEF __HAS_MBED_TLS_LIB__}LIB_MBED_TLS{$ENDIF} name _PU + 'mbedtls_ssl_handshake_client_step'; + +function mbedtls_ssl_handshake_server_step(ssl: PMbedtls_SSL_Context): Integer; cdecl; external {$IFDEF __HAS_MBED_TLS_LIB__}LIB_MBED_TLS{$ENDIF} name _PU + 'mbedtls_ssl_handshake_server_step'; + +function mbedtls_cipher_info_from_values(const cipher_id: TMbedtls_Cipher_ID; key_bitlen: Integer; const mode: TMbedtls_Cipher_Mode): PMbedtls_Cipher_Info; cdecl; external {$IFDEF __HAS_MBED_TLS_LIB__}LIB_MBED_TLS{$ENDIF} name _PU + 'mbedtls_cipher_info_from_values'; +{$ENDREGION} + +function MbedErrToStr(AErrCode: Integer): string; + +implementation + +{$IFDEF __HAS_MBED_TLS_OBJ__} + {$L aesni.obj} + {$L aria.obj} + {$L cmac.obj} + {$L ctr_drbg.obj} + {$L entropy.obj} + {$L entropy_poll.obj} + {$L error.obj} + {$L havege.obj} + {$L hkdf.obj} + {$L md2.obj} + {$L md4.obj} + {$L memory_buffer_alloc.obj} + {$L nist_kw.obj} + {$L pkcs11.obj} + {$L ssl_cache.obj} + {$L ssl_cookie.obj} + {$L ssl_ticket.obj} + {$L threading.obj} + {$L timing.obj} + {$L version.obj} + {$L version_features.obj} + {$L x509write_crt.obj} + {$L x509write_csr.obj} + {$L x509_create.obj} + {$L x509_crl.obj} + {$L x509_csr.obj} + {$L xtea.obj} + {$L asn1write.obj} + {$L pem.obj} + {$L platform.obj} + {$L pkwrite.obj} + {$L pkparse.obj} + {$L base64.obj} + {$L pkcs12.obj} + {$L pkcs5.obj} + {$L arc4.obj} + {$L ssl_cli.obj} + {$L ssl_srv.obj} + {$L ssl_tls.obj} + {$L ssl_ciphersuites.obj} + {$L debug.obj} + {$L x509_crt.obj} + {$L pk.obj} + {$L pk_wrap.obj} + {$L x509.obj} + {$L platform_util.obj} + {$L rsa.obj} + {$L rsa_internal.obj} + {$L oid.obj} + {$L ecjpake.obj} + {$L ecdsa.obj} + {$L ecdh.obj} + {$L ecp.obj} + {$L hmac_drbg.obj} + {$L dhm.obj} + {$L ecp_curves.obj} + {$L asn1parse.obj} + {$L md.obj} + {$L md_wrap.obj} + {$L cipher.obj} + {$L cipher_wrap.obj} + {$L ccm.obj} + {$L gcm.obj} + {$L aes.obj} + {$L camellia.obj} + {$L des.obj} + {$L blowfish.obj} + {$L chachapoly.obj} + {$L chacha20.obj} + {$L md5.obj} + {$L ripemd160.obj} + {$L sha1.obj} + {$L sha256.obj} + {$L sha512.obj} + {$L poly1305.obj} + {$L bignum.obj} +{$ENDIF} + +{$IFDEF __HAS_MBED_TLS_O__} + {$L aesni.o} + {$L aria.o} + {$L cmac.o} + {$L ctr_drbg.o} + {$L entropy.o} + {$L entropy_poll.o} + {$L error.o} + {$L havege.o} + {$L hkdf.o} + {$L md2.o} + {$L md4.o} + {$L memory_buffer_alloc.o} + {$L nist_kw.o} + {$L pkcs11.o} + {$L ssl_cache.o} + {$L ssl_cookie.o} + {$L ssl_ticket.o} + {$L threading.o} + {$L timing.o} + {$L version.o} + {$L version_features.o} + {$L x509write_crt.o} + {$L x509write_csr.o} + {$L x509_create.o} + {$L x509_crl.o} + {$L x509_csr.o} + {$L xtea.o} + {$L asn1write.o} + {$L pem.o} + {$L platform.o} + {$L pkwrite.o} + {$L pkparse.o} + {$L base64.o} + {$L pkcs12.o} + {$L pkcs5.o} + {$L arc4.o} + {$L ssl_cli.o} + {$L ssl_srv.o} + {$L ssl_tls.o} + {$L ssl_ciphersuites.o} + {$L debug.o} + {$L x509_crt.o} + {$L pk.o} + {$L pk_wrap.o} + {$L x509.o} + {$L platform_util.o} + {$L rsa.o} + {$L rsa_internal.o} + {$L oid.o} + {$L ecjpake.o} + {$L ecdsa.o} + {$L ecdh.o} + {$L ecp.o} + {$L hmac_drbg.o} + {$L dhm.o} + {$L ecp_curves.o} + {$L asn1parse.o} + {$L md.o} + {$L md_wrap.o} + {$L cipher.o} + {$L cipher_wrap.o} + {$L ccm.o} + {$L gcm.o} + {$L aes.o} + {$L camellia.o} + {$L des.o} + {$L blowfish.o} + {$L chachapoly.o} + {$L chacha20.o} + {$L md5.o} + {$L ripemd160.o} + {$L sha1.o} + {$L sha256.o} + {$L sha512.o} + {$L poly1305.o} + {$L bignum.o} +{$ENDIF} + +{$IFDEF MSWINDOWS} +type + HCRYPTPROV = ULONG_PTR; + +function CryptAcquireContextA(var phProv: HCRYPTPROV; pszContainer, pszProvider: LPCSTR; dwProvType, dwFlags: DWORD): BOOL; stdcall; external advapi32; + +function CryptGenRandom(hProv: HCRYPTPROV; dwLen: DWORD; pbBuffer: LPBYTE): BOOL; stdcall; external advapi32; + +function CryptReleaseContext(hProv: HCRYPTPROV; dwFlags: ULONG_PTR): BOOL; stdcall; external advapi32; +{$ENDIF} + +procedure _mutex_init(mutex: PTlsMutex); cdecl; +begin + mutex.Lock := TObject.Create; +end; + +procedure _mutex_free(mutex: PTlsMutex); cdecl; +begin + mutex.Lock.DisposeOf; +end; + +function _mutex_lock(mutex: PTlsMutex): Integer; cdecl; +begin + Result := 0; + System.TMonitor.Enter(mutex.Lock); +end; + +function _mutex_unlock(mutex: PTlsMutex): Integer; cdecl; +begin + Result := 0; + System.TMonitor.Exit(mutex.Lock); +end; + +function MbedErrToStr(AErrCode: Integer): string; +var + LBuf: array [0 .. 511] of Byte; +begin + mbedtls_strerror(AErrCode, @LBuf, 512); + Result := TMarshal.ReadStringAsAnsi(TPtrWrapper.Create(@LBuf)); +end; + +{$IFDEF WIN32} +function _calloc(nitems: Size_T; size: Size_T): Pointer; cdecl; +begin + Result := AllocMem(nitems * size); +end; + +procedure __llmul; cdecl; +asm + jmp System.@_llmul +end; + +procedure __lludiv; cdecl; +asm + jmp System.@_lludiv +end; + +procedure __llumod; cdecl; +asm + jmp System.@_llumod +end; + +procedure __lldiv; cdecl; +asm + jmp System.@_lldiv +end; + +procedure __llshl; cdecl; +asm + jmp System.@_llshl +end; +{$ENDIF} + +{$IFDEF WIN64} +function calloc(nitems: Size_T; size: Size_T): Pointer; cdecl; +begin + Result := AllocMem(nitems * size); +end; + +procedure __chkstk; +label + _loop1, _exit; +asm + lea r10, [rsp] + mov r11, r10 + sub r11, rax + and r11w, 0f000h + and r10w, 0f000h + _loop1: + sub r10, 01000h + cmp r10, r11 // more to go? + jl _exit + mov qword [r10], 0 // probe this page + jmp _loop1 + _exit: + ret +end; +{$ENDIF} + +initialization + mbedtls_threading_set_alt(@_mutex_init, @_mutex_free, @_mutex_lock, @_mutex_unlock); + +finalization + mbedtls_threading_free_alt; + +end. diff --git a/ThirdParty/DCS/Net/Net.OpenSSL.pas b/ThirdParty/DCS/Net/Net.OpenSSL.pas new file mode 100644 index 00000000..87abc488 --- /dev/null +++ b/ThirdParty/DCS/Net/Net.OpenSSL.pas @@ -0,0 +1,1377 @@ +{******************************************************************************} +{ } +{ Delphi cross platform socket library } +{ } +{ Copyright (c) 2017 WiNDDRiVER(soulawing@gmail.com) } +{ } +{ Homepage: https://github.com/winddriver/Delphi-Cross-Socket } +{ } +{******************************************************************************} +unit Net.OpenSSL; + +{ + OpenSSL 下载: + https://indy.fulgan.com/SSL/ + https://github.com/leenjewel/openssl_for_ios_and_android + + OpenSSL iOS静态库下载: + https://indy.fulgan.com/SSL/OpenSSLStaticLibs.7z + + LibreSSL 下载: + http://ftp.openbsd.org/pub/OpenBSD/LibreSSL/ + + Linux下需要安装libssl开发包 + sudo apt-get install libssl-dev +} + +// 使用 LibreSSL +// LibreSSL 是 OpenSSL 的一个分支, 由 OpenBSD 维护, 接口与 OpenSSL 兼容 +// 目前(2.4.2) 执行效率比 OpenSSL(1.0.2h) 低 +{.$DEFINE __LIBRE_SSL__} + +// iOS真机必须用openssl的静态库 +{$IF defined(IOS) and defined(CPUARM)} + {$DEFINE __SSL_STATIC__} +{$ENDIF} + +interface + +uses + {$IFDEF MSWINDOWS} + Winapi.Windows, + {$ENDIF} + {$IFDEF POSIX} + Posix.Base, Posix.Pthread, + {$ENDIF} + System.SysUtils, System.SyncObjs; + +const + SSLEAY_DLL = + {$IFDEF MSWINDOWS} + {$IFDEF __LIBRE_SSL__} + 'libssl-39.dll' + {$ELSE} + 'ssleay32.dll' + {$ENDIF} + {$ENDIF} + {$IFDEF POSIX} + {$IFDEF __SSL_STATIC__} + 'libssl.a' + {$ELSEIF defined(MACOS)} + 'libssl.dylib' + {$ELSE} + 'libssl.so' + {$ENDIF} + {$ENDIF}; + + LIBEAY_DLL = + {$IFDEF MSWINDOWS} + {$IFDEF __LIBRE_SSL__} + 'libcrypto-38.dll' + {$ELSE} + 'libeay32.dll' + {$ENDIF} + {$ENDIF} + {$IFDEF POSIX} + {$IFDEF __SSL_STATIC__} + 'libcrypto.a' + {$ELSEIF defined(MACOS)} + 'libcrypto.dylib' + {$ELSE} + 'libcrypto.so' + {$ENDIF} + {$ENDIF}; + + {$IFDEF MSWINDOWS} + _PU = ''; + {$ENDIF} + + SSL_ERROR_NONE = 0; + SSL_ERROR_SSL = 1; + SSL_ERROR_WANT_READ = 2; + SSL_ERROR_WANT_WRITE = 3; + SSL_ERROR_WANT_X509_LOOKUP = 4; + SSL_ERROR_SYSCALL = 5; + SSL_ERROR_ZERO_RETURN = 6; + SSL_ERROR_WANT_CONNECT = 7; + SSL_ERROR_WANT_ACCEPT = 8; + + SSL_ST_CONNECT = $1000; + SSL_ST_ACCEPT = $2000; + SSL_ST_MASK = $0FFF; + SSL_ST_INIT = (SSL_ST_CONNECT or SSL_ST_ACCEPT); + SSL_ST_BEFORE = $4000; + SSL_ST_OK = $03; + SSL_ST_RENEGOTIATE = ($04 or SSL_ST_INIT); + + BIO_CTRL_EOF = 2; + BIO_CTRL_INFO = 3; + BIO_CTRL_PENDING = 10; + SSL_VERIFY_NONE = 0; + SSL_VERIFY_PEER = 1; + SSL_VERIFY_FAIL_IF_NO_PEER_CERT = 2; + SSL_VERIFY_CLIENT_ONCE = 4; + + SSL_CTRL_NEED_TMP_RSA = 1; + SSL_CTRL_SET_TMP_RSA = 2; + SSL_CTRL_SET_TMP_DH = 3; + SSL_CTRL_SET_TMP_ECDH = 4; + SSL_CTRL_SET_TMP_RSA_CB = 5; + SSL_CTRL_SET_TMP_DH_CB = 6; + SSL_CTRL_SET_TMP_ECDH_CB = 7; + SSL_CTRL_GET_SESSION_REUSED = 8; + SSL_CTRL_GET_CLIENT_CERT_REQUEST = 9; + SSL_CTRL_GET_NUM_RENEGOTIATIONS = 10; + SSL_CTRL_CLEAR_NUM_RENEGOTIATIONS = 11; + SSL_CTRL_GET_TOTAL_RENEGOTIATIONS = 12; + SSL_CTRL_GET_FLAGS = 13; + SSL_CTRL_EXTRA_CHAIN_CERT = 14; + SSL_CTRL_SET_MSG_CALLBACK = 15; + SSL_CTRL_SET_MSG_CALLBACK_ARG = 16; + SSL_CTRL_SET_MTU = 17; + + SSL_CTRL_SESS_NUMBER = 20; + SSL_CTRL_SESS_CONNECT = 21; + SSL_CTRL_SESS_CONNECT_GOOD = 22; + SSL_CTRL_SESS_CONNECT_RENEGOTIATE = 23; + SSL_CTRL_SESS_ACCEPT = 24; + SSL_CTRL_SESS_ACCEPT_GOOD = 25; + SSL_CTRL_SESS_ACCEPT_RENEGOTIATE = 26; + SSL_CTRL_SESS_HIT = 27; + SSL_CTRL_SESS_CB_HIT = 28; + SSL_CTRL_SESS_MISSES = 29; + SSL_CTRL_SESS_TIMEOUTS = 30; + SSL_CTRL_SESS_CACHE_FULL = 31; + SSL_CTRL_OPTIONS = 32; + SSL_CTRL_MODE = 33; + SSL_CTRL_GET_READ_AHEAD = 40; + SSL_CTRL_SET_READ_AHEAD = 41; + SSL_CTRL_SET_SESS_CACHE_SIZE = 42; + SSL_CTRL_GET_SESS_CACHE_SIZE = 43; + SSL_CTRL_SET_SESS_CACHE_MODE = 44; + SSL_CTRL_GET_SESS_CACHE_MODE = 45; + SSL_CTRL_GET_MAX_CERT_LIST = 50; + SSL_CTRL_SET_MAX_CERT_LIST = 51; + SSL_CTRL_SET_MAX_SEND_FRAGMENT = 52; + SSL_CTRL_GET_RI_SUPPORT = 76; + SSL_CTRL_CLEAR_OPTIONS = 77; + SSL_CTRL_CLEAR_MODE = 78; + SSL_CTRL_GET_EXTRA_CHAIN_CERTS = 82; + SSL_CTRL_CLEAR_EXTRA_CHAIN_CERTS = 83; + SSL_CTRL_CHAIN = 88; + SSL_CTRL_CHAIN_CERT = 89; + SSL_CTRL_GET_CURVES = 90; + SSL_CTRL_SET_CURVES = 91; + SSL_CTRL_SET_CURVES_LIST = 92; + SSL_CTRL_GET_SHARED_CURVE = 93; + SSL_CTRL_SET_ECDH_AUTO = 94; + SSL_CTRL_SET_SIGALGS = 97; + SSL_CTRL_SET_SIGALGS_LIST = 98; + SSL_CTRL_CERT_FLAGS = 99; + SSL_CTRL_CLEAR_CERT_FLAGS = 100; + SSL_CTRL_SET_CLIENT_SIGALGS = 101; + SSL_CTRL_SET_CLIENT_SIGALGS_LIST = 102; + SSL_CTRL_GET_CLIENT_CERT_TYPES = 103; + SSL_CTRL_SET_CLIENT_CERT_TYPES = 104; + SSL_CTRL_BUILD_CERT_CHAIN = 105; + SSL_CTRL_SET_VERIFY_CERT_STORE = 106; + SSL_CTRL_SET_CHAIN_CERT_STORE = 107; + SSL_CTRL_GET_PEER_SIGNATURE_NID = 108; + SSL_CTRL_GET_SERVER_TMP_KEY = 109; + SSL_CTRL_GET_RAW_CIPHERLIST = 110; + SSL_CTRL_GET_EC_POINT_FORMATS = 111; + SSL_CTRL_GET_CHAIN_CERTS = 115; + SSL_CTRL_SELECT_CURRENT_CERT = 116; + SSL_CTRL_SET_CURRENT_CERT = 117; + SSL_CTRL_SET_DH_AUTO = 118; + SSL_CTRL_CHECK_PROTO_VERSION = 119; + DTLS_CTRL_SET_LINK_MTU = 120; + DTLS_CTRL_GET_LINK_MIN_MTU = 121; + SSL_CTRL_GET_EXTMS_SUPPORT = 122; + SSL_CTRL_SET_MIN_PROTO_VERSION = 123; + SSL_CTRL_SET_MAX_PROTO_VERSION = 124; + SSL_CTRL_SET_SPLIT_SEND_FRAGMENT = 125; + SSL_CTRL_SET_MAX_PIPELINES = 126; + + SSL_MODE_ENABLE_PARTIAL_WRITE = $00000001; + SSL_MODE_ACCEPT_MOVING_WRITE_BUFFER = $00000002; + SSL_MODE_AUTO_RETRY = $00000004; + SSL_MODE_NO_AUTO_CHAIN = $00000008; + + SSL_OP_MICROSOFT_SESS_ID_BUG = $00000001; + SSL_OP_NETSCAPE_CHALLENGE_BUG = $00000002; + SSL_OP_NETSCAPE_REUSE_CIPHER_CHANGE_BUG = $00000008; + SSL_OP_TLSEXT_PADDING = $00000010; + SSL_OP_SSLREF2_REUSE_CERT_TYPE_BUG = $00000000; + SSL_OP_MICROSOFT_BIG_SSLV3_BUFFER = $00000020; + SSL_OP_SAFARI_ECDHE_ECDSA_BUG = $00000040; + SSL_OP_MSIE_SSLV2_RSA_PADDING = $00000000; + SSL_OP_SSLEAY_080_CLIENT_DH_BUG = $00000080; + SSL_OP_TLS_D5_BUG = $00000100; + SSL_OP_TLS_BLOCK_PADDING_BUG = $00000200; + SSL_OP_DONT_INSERT_EMPTY_FRAGMENTS = $00000800; + SSL_OP_ALL = $00000BFF; + SSL_OP_NO_QUERY_MTU = $00001000; + SSL_OP_COOKIE_EXCHANGE = $00002000; + SSL_OP_NO_TICKET = $00004000; + SSL_OP_CISCO_ANYCONNECT = $00008000; + SSL_OP_NO_SESSION_RESUMPTION_ON_RENEGOTIATION = $00010000; + SSL_OP_NO_COMPRESSION = $00020000; + SSL_OP_ALLOW_UNSAFE_LEGACY_RENEGOTIATION = $00040000; + SSL_OP_SINGLE_ECDH_USE = $00080000; + SSL_OP_SINGLE_DH_USE = $00100000; + SSL_OP_EPHEMERAL_RSA = $00200000; + SSL_OP_CIPHER_SERVER_PREFERENCE = $00400000; + SSL_OP_TLS_ROLLBACK_BUG = $00800000; + SSL_OP_NO_SSLv2 = $01000000; + SSL_OP_NO_SSLv3 = $02000000; + SSL_OP_NO_TLSv1 = $04000000; + SSL_OP_NO_TLSv1_2 = $08000000; + SSL_OP_NO_TLSv1_1 = $10000000; + SSL_OP_PKCS1_CHECK_1 = $00000000; + SSL_OP_PKCS1_CHECK_2 = $00000000; + SSL_OP_NETSCAPE_CA_DN_BUG = $20000000; + SSL_OP_NETSCAPE_DEMO_CIPHER_CHANGE_BUG = $40000000; + SSL_OP_CRYPTOPRO_TLSEXT_BUG = $80000000; + + NID_X9_62_prime192v1 = 409; + NID_X9_62_prime192v2 = 410; + NID_X9_62_prime192v3 = 411; + NID_X9_62_prime239v1 = 412; + NID_X9_62_prime239v2 = 413; + NID_X9_62_prime239v3 = 414; + NID_X9_62_prime256v1 = 415; + + CRYPTO_LOCK = 1; + CRYPTO_UNLOCK = 2; + CRYPTO_READ = 4; + CRYPTO_WRITE = 8; + + BIO_FLAGS_READ = 1; + BIO_FLAGS_WRITE = 2; + BIO_FLAGS_IO_SPECIAL = 4; + BIO_FLAGS_RWS = (BIO_FLAGS_READ or + BIO_FLAGS_WRITE or + BIO_FLAGS_IO_SPECIAL); + BIO_FLAGS_SHOULD_RETRY = 8; + + OSSL_VER_0908 = $00908000; + OSSL_VER_1000 = $10000000; + OSSL_VER_1001 = $10001000; + OSSL_VER_1002 = $10002000; + OSSL_VER_1100 = $10100000; + + TLS1_VERSION = $0301; + TLS1_VERSION_MAJOR = $03; + TLS1_VERSION_MINOR = $01; + + TLS1_1_VERSION = $0302; + TLS1_1_VERSION_MAJOR = $03; + TLS1_1_VERSION_MINOR = $02; + + TLS1_2_VERSION = $0303; + TLS1_2_VERSION_MAJOR = $03; + TLS1_2_VERSION_MINOR = $03; + + TLS_MAX_VERSION = TLS1_2_VERSION; + TLS_ANY_VERSION = $10000; + + DTLS1_VERSION = $FEFF; + DTLS1_2_VERSION = $FEFD; + DTLS_MAX_VERSION = DTLS1_2_VERSION; + DTLS1_VERSION_MAJOR = $FE; + + DTLS1_BAD_VER = $0100; + + // Special value for method supporting multiple versions + DTLS_ANY_VERSION = $1FFFF; + +type + size_t = NativeUInt; + + {$REGION 'SSL'} + TSSL_METHOD_st = packed record + Dummy: array [0..0] of Byte; + end; + PSSL_METHOD = ^TSSL_METHOD_st; + + TSSL_CTX_st = packed record + Dummy: array [0..0] of Byte; + end; + PSSL_CTX = ^TSSL_CTX_st; + + TBIO_st = packed record + Dummy: array [0..0] of Byte; + end; + PBIO = ^TBIO_st; + PPBIO = ^PBIO; + + TSSL_st = packed record + Dummy: array [0..0] of Byte; + end; + PSSL = ^TSSL_st; + + TX509_STORE_CTX_st = packed record + Dummy: array [0..0] of Byte; + end; + PX509_STORE_CTX = ^TX509_STORE_CTX_st; + + TEVP_PKEY_st = packed record + Dummy: array [0..0] of Byte; + end; + PEVP_PKEY = ^TEVP_PKEY_st; + PPEVP_PKEY = ^PEVP_PKEY; + + TX509_st = packed record + Dummy: array [0..0] of Byte; + end; + PX509 = ^TX509_st; + PPX509 = ^PX509; + + TX509_STORE_st = packed record + Dummy : array [0..0] of Byte; + end; + PX509_STORE = ^TX509_STORE_st; + + // 0.9.7g, 0.9.8a, 0.9.8e, 1.0.0d + TASN1_STRING_st = record + length : Integer; + type_ : Integer; + data : MarshaledAString; + //* The value of the following field depends on the type being + //* held. It is mostly being used for BIT_STRING so if the + //* input data has a non-zero 'unused bits' value, it will be + //* handled correctly */ + flags : Longword; + end; + PASN1_STRING = ^TASN1_STRING_st; + TASN1_OCTET_STRING = TASN1_STRING_st; + PASN1_OCTET_STRING = ^TASN1_OCTET_STRING; + TASN1_BIT_STRING = TASN1_STRING_st; + PASN1_BIT_STRING = ^TASN1_BIT_STRING; + + TSetVerify_cb = function(Ok: Integer; StoreCtx: PX509_STORE_CTX): Integer; cdecl; + {$ENDREGION} + + {$REGION 'LIBEAY'} + TCRYPTO_THREADID_st = packed record + Dummy: array [0..0] of Byte; + end; + PCRYPTO_THREADID = ^TCRYPTO_THREADID_st; + + TCRYPTO_dynlock_value_st = record + Mutex: TCriticalSection; + end; + PCRYPTO_dynlock_value = ^TCRYPTO_dynlock_value_st; + CRYPTO_dynlock_value = TCRYPTO_dynlock_value_st; + + TBIO_METHOD_st = packed record + Dummy: array [0..0] of Byte; + end; + PBIO_METHOD = ^TBIO_METHOD_st; + + TX509_NAME_st = packed record + Dummy: array [0..0] of Byte; + end; + PX509_NAME = ^TX509_NAME_st; + + TSTACK_st = packed record + Dummy : array [0..0] of Byte; + end; + PSTACK = ^TSTACK_st; + + TASN1_OBJECT_st = packed record + Dummy : array [0..0] of Byte; + end; + PASN1_OBJECT = ^TASN1_OBJECT_st; + + TEC_KEY_st = packed record + Dummy : array [0..0] of Byte; + end; + PEC_KEY = ^TEC_KEY_st; + + TStatLockLockCallback = procedure(Mode: Integer; N: Integer; const _File: MarshaledAString; Line: Integer); cdecl; + TStatLockIDCallback = function: Longword; cdecl; + TCryptoThreadIDCallback = procedure(ID: PCRYPTO_THREADID) cdecl; + + TDynLockCreateCallback = function(const _file: MarshaledAString; Line: Integer): PCRYPTO_dynlock_value; cdecl; + TDynLockLockCallback = procedure(Mode: Integer; L: PCRYPTO_dynlock_value; _File: MarshaledAString; Line: Integer); cdecl; + TDynLockDestroyCallback = procedure(L: PCRYPTO_dynlock_value; _File: MarshaledAString; Line: Integer); cdecl; + pem_password_cb = function(buf: Pointer; size: Integer; rwflag: Integer; userdata: Pointer): Integer; cdecl; + {$ENDREGION} + +{$IFDEF __SSL_STATIC__} + +{$REGION 'SSL-FUNC'} +function SSL_library_init: Integer; cdecl; + external SSLEAY_DLL name _PU + 'SSL_library_init'; +procedure SSL_load_error_strings; cdecl; + external SSLEAY_DLL name _PU + 'SSL_load_error_strings'; + +function SSLv23_method: PSSL_METHOD; cdecl; + external SSLEAY_DLL name _PU + 'SSLv23_method'; +function SSLv23_client_method: PSSL_METHOD; cdecl; + external SSLEAY_DLL name _PU + 'SSLv23_client_method'; +function SSLv23_server_method: PSSL_METHOD; cdecl; + external SSLEAY_DLL name _PU + 'SSLv23_server_method'; + +function TLSv1_method: PSSL_METHOD; cdecl; + external SSLEAY_DLL name _PU + 'TLSv1_method'; +function TLSv1_client_method: PSSL_METHOD; cdecl; + external SSLEAY_DLL name _PU + 'TLSv1_client_method'; +function TLSv1_server_method: PSSL_METHOD; cdecl; + external SSLEAY_DLL name _PU + 'TLSv1_server_method'; + +{$IF not(defined(MACOS) and not defined(IOS))} +function TLSv1_2_method: PSSL_METHOD; cdecl; + external SSLEAY_DLL name _PU + 'TLSv1_2_method'; +{$ENDIF} +function TLSv1_2_client_method: PSSL_METHOD; cdecl; + external SSLEAY_DLL name _PU + 'TLSv1_2_client_method'; +function TLSv1_2_server_method: PSSL_METHOD; cdecl; + external SSLEAY_DLL name _PU + 'TLSv1_2_server_method'; + +function SSL_CTX_new(meth: PSSL_METHOD): PSSL_CTX; cdecl; + external SSLEAY_DLL name _PU + 'SSL_CTX_new'; +procedure SSL_CTX_free(ctx: PSSL_CTX); cdecl; + external SSLEAY_DLL name _PU + 'SSL_CTX_free'; +function SSL_CTX_ctrl(ctx: PSSL_CTX; Cmd: Integer; LArg: Integer; PArg: MarshaledAString): Integer; cdecl; + external SSLEAY_DLL name _PU + 'SSL_CTX_ctrl'; +procedure SSL_CTX_set_verify(ctx: PSSL_CTX; mode: Integer; callback: TSetVerify_cb); cdecl; + external SSLEAY_DLL name _PU + 'SSL_CTX_set_verify'; +function SSL_CTX_set_cipher_list(ctx: PSSL_CTX; CipherString: MarshaledAString): Integer; cdecl; + external SSLEAY_DLL name _PU + 'SSL_CTX_set_cipher_list'; +function SSL_CTX_use_PrivateKey(ctx: PSSL_CTX; pkey: PEVP_PKEY): Integer; cdecl; + external SSLEAY_DLL name _PU + 'SSL_CTX_use_PrivateKey'; +function SSL_CTX_use_certificate(ctx: PSSL_CTX; cert: PX509): Integer; cdecl; + external SSLEAY_DLL name _PU + 'SSL_CTX_use_certificate'; +function SSL_CTX_check_private_key(ctx: PSSL_CTX): Integer; cdecl; + external SSLEAY_DLL name _PU + 'SSL_CTX_check_private_key'; + +function SSL_new(ctx: PSSL_CTX): PSSL; cdecl; + external SSLEAY_DLL name _PU + 'SSL_new'; +procedure SSL_set_bio(s: PSSL; rbio, wbio: PBIO); cdecl; + external SSLEAY_DLL name _PU + 'SSL_set_bio'; +function SSL_get_peer_certificate(s: PSSL): PX509; cdecl; + external SSLEAY_DLL name _PU + 'SSL_get_peer_certificate'; +function SSL_get_error(s: PSSL; ret_code: Integer): Integer; cdecl; + external SSLEAY_DLL name _PU + 'SSL_get_error'; + +function SSL_ctrl(S: PSSL; Cmd: Integer; LArg: Integer; PArg: Pointer): Integer; cdecl; + external SSLEAY_DLL name _PU + 'SSL_ctrl'; + +function SSL_shutdown(s: PSSL): Integer; cdecl; + external SSLEAY_DLL name _PU + 'SSL_shutdown'; +procedure SSL_free(s: PSSL); cdecl; + external SSLEAY_DLL name _PU + 'SSL_free'; + +procedure SSL_set_connect_state(s: PSSL); cdecl; + external SSLEAY_DLL name _PU + 'SSL_set_connect_state'; +procedure SSL_set_accept_state(s: PSSL); cdecl; + external SSLEAY_DLL name _PU + 'SSL_set_accept_state'; +function SSL_accept(S: PSSL): Integer; cdecl; + external SSLEAY_DLL name _PU + 'SSL_accept'; +function SSL_connect(S: PSSL): Integer; cdecl; + external SSLEAY_DLL name _PU + 'SSL_connect'; +function SSL_do_handshake(S: PSSL): Integer; cdecl; + external SSLEAY_DLL name _PU + 'SSL_do_handshake'; +function SSL_read(s: PSSL; buf: Pointer; num: Integer): Integer; cdecl; + external SSLEAY_DLL name _PU + 'SSL_read'; +function SSL_write(s: PSSL; const buf: Pointer; num: Integer): Integer; cdecl; + external SSLEAY_DLL name _PU + 'SSL_write'; +function SSL_state(s: PSSL): Integer; cdecl; + external SSLEAY_DLL name _PU + 'SSL_state'; +function SSL_pending(s: PSSL): Integer; cdecl; + external SSLEAY_DLL name _PU + 'SSL_pending'; + +function SSL_CTX_get_cert_store(const Ctx: PSSL_CTX): PX509_STORE; cdecl; + external SSLEAY_DLL name _PU + 'SSL_CTX_get_cert_store'; +function SSL_CTX_add_client_CA(C: PSSL_CTX; CaCert: PX509): Integer; cdecl; + external SSLEAY_DLL name _PU + 'SSL_CTX_add_client_CA'; +{$ENDREGION} + +{$REGION 'LIBEAY-FUNC'} +function SSLeay: Longword; cdecl; + external LIBEAY_DLL name _PU + 'SSLeay'; + +function CRYPTO_set_id_callback(callback: TStatLockIDCallback): Integer; cdecl; + external LIBEAY_DLL name _PU + 'CRYPTO_set_id_callback'; + +{$IF not(defined(MACOS) and not defined(IOS))} +// MACOS 内置的 OpenSSL 库版本较低(0.9.x) +// CRYPTO_THREADID_set_callback 只有 OpenSSL 1.0 以上版本才有 +function CRYPTO_THREADID_set_callback(callback: TCryptoThreadIDCallback): Integer; cdecl; + external LIBEAY_DLL name _PU + 'CRYPTO_THREADID_set_callback'; +procedure CRYPTO_THREADID_set_numeric(id : PCRYPTO_THREADID; val: LongWord); cdecl; + external LIBEAY_DLL name _PU + 'CRYPTO_THREADID_set_numeric'; +procedure CRYPTO_THREADID_set_pointer(id : PCRYPTO_THREADID; ptr: Pointer); cdecl; + external LIBEAY_DLL name _PU + 'CRYPTO_THREADID_set_pointer'; +{$ENDIF} + +function CRYPTO_num_locks: Integer; cdecl; + external LIBEAY_DLL name _PU + 'CRYPTO_num_locks'; +procedure CRYPTO_set_locking_callback(callback: TStatLockLockCallback); cdecl; + external LIBEAY_DLL name _PU + 'CRYPTO_set_locking_callback'; +procedure CRYPTO_set_dynlock_create_callback(callback: TDynLockCreateCallBack); cdecl; + external LIBEAY_DLL name _PU + 'CRYPTO_set_dynlock_create_callback'; +procedure CRYPTO_set_dynlock_lock_callback(callback: TDynLockLockCallBack); cdecl; + external LIBEAY_DLL name _PU + 'CRYPTO_set_dynlock_lock_callback'; +procedure CRYPTO_set_dynlock_destroy_callback(callback: TDynLockDestroyCallBack); cdecl; + external LIBEAY_DLL name _PU + 'CRYPTO_set_dynlock_destroy_callback'; +procedure CRYPTO_cleanup_all_ex_data; cdecl; + external LIBEAY_DLL name _PU + 'CRYPTO_cleanup_all_ex_data'; + +procedure ERR_remove_state(tid: Cardinal); cdecl; + external LIBEAY_DLL name _PU + 'ERR_remove_state'; + +{$IF not(defined(MACOS) and not defined(IOS))} +procedure ERR_remove_thread_state(tid: PCRYPTO_THREADID); cdecl; + external LIBEAY_DLL name _PU + 'ERR_remove_thread_state'; +{$ENDIF} + +procedure ERR_free_strings; cdecl; + external LIBEAY_DLL name _PU + 'ERR_free_strings'; +procedure ERR_error_string_n(err: Cardinal; buf: MarshaledAString; len: size_t); cdecl; + external LIBEAY_DLL name _PU + 'ERR_error_string_n'; +function ERR_get_error: Cardinal; cdecl; + external LIBEAY_DLL name _PU + 'ERR_get_error'; +procedure ERR_clear_error; cdecl; + external LIBEAY_DLL name _PU + 'ERR_clear_error'; + +procedure EVP_cleanup; cdecl; + external LIBEAY_DLL name _PU + 'EVP_cleanup'; +procedure EVP_PKEY_free(pkey: PEVP_PKEY); cdecl; + external LIBEAY_DLL name _PU + 'EVP_PKEY_free'; + +function BIO_new(BioMethods: PBIO_METHOD): PBIO; cdecl; + external LIBEAY_DLL name _PU + 'BIO_new'; +function BIO_ctrl(bp: PBIO; cmd: Integer; larg: Longint; parg: Pointer): Longint; cdecl; + external LIBEAY_DLL name _PU + 'BIO_ctrl'; +function BIO_new_mem_buf(buf: Pointer; len: Integer): PBIO; cdecl; + external LIBEAY_DLL name _PU + 'BIO_new_mem_buf'; +function BIO_free(b: PBIO): Integer; cdecl; + external LIBEAY_DLL name _PU + 'BIO_free'; +function BIO_s_mem: PBIO_METHOD; cdecl; + external LIBEAY_DLL name _PU + 'BIO_s_mem'; +function BIO_read(b: PBIO; Buf: Pointer; Len: Integer): Integer; cdecl; + external LIBEAY_DLL name _PU + 'BIO_read'; +function BIO_write(b: PBIO; Buf: Pointer; Len: Integer): Integer; cdecl; + external LIBEAY_DLL name _PU + 'BIO_write'; + +function EC_KEY_new_by_curve_name(nid: Integer): PEC_KEY; cdecl; + external LIBEAY_DLL name _PU + 'EC_KEY_new_by_curve_name'; +procedure EC_KEY_free(key: PEC_KEY); cdecl; + external LIBEAY_DLL name _PU + 'EC_KEY_free'; + +function X509_get_issuer_name(cert: PX509): PX509_NAME; cdecl; + external LIBEAY_DLL name _PU + 'X509_get_issuer_name'; +function X509_get_subject_name(cert: PX509): PX509_NAME; cdecl; + external LIBEAY_DLL name _PU + 'X509_get_subject_name'; +procedure X509_free(cert: PX509); cdecl; + external LIBEAY_DLL name _PU + 'X509_free'; +function X509_NAME_print_ex(bout: PBIO; nm: PX509_NAME; indent: Integer; flags: Cardinal): Integer; cdecl; + external LIBEAY_DLL name _PU + 'X509_NAME_print_ex'; +function X509_get_ext_d2i(x: PX509; nid: Integer; var crit, idx: Integer): Pointer; cdecl; + external LIBEAY_DLL name _PU + 'X509_get_ext_d2i'; + +function X509_STORE_add_cert(Store: PX509_STORE; Cert: PX509): Integer; cdecl; + external LIBEAY_DLL name _PU + 'X509_STORE_add_cert'; + +function sk_num(stack: PSTACK): Integer; cdecl; + external LIBEAY_DLL name _PU + 'sk_num'; +function sk_pop(stack: PSTACK): Pointer; cdecl; + external LIBEAY_DLL name _PU + 'sk_pop'; + +function ASN1_BIT_STRING_get_bit(a: PASN1_BIT_STRING; n: Integer): Integer; cdecl; + external LIBEAY_DLL name _PU + 'ASN1_BIT_STRING_get_bit'; +function OBJ_obj2nid(o: PASN1_OBJECT): Integer; cdecl; + external LIBEAY_DLL name _PU + 'OBJ_obj2nid'; +function OBJ_nid2sn(n: Integer): MarshaledAString; cdecl; + external LIBEAY_DLL name _PU + 'OBJ_nid2sn'; +function ASN1_STRING_data(x: PASN1_STRING): Pointer; cdecl; + external LIBEAY_DLL name _PU + 'ASN1_STRING_data'; +function PEM_read_bio_X509(bp: PBIO; x: PPX509; cb: pem_password_cb; u: Pointer): PX509; cdecl; + external LIBEAY_DLL name _PU + 'PEM_read_bio_X509'; +function PEM_read_bio_X509_AUX(bp: PBIO; x: PPX509; cb: pem_password_cb; u: Pointer): PX509; cdecl; + external LIBEAY_DLL name _PU + 'PEM_read_bio_X509_AUX'; +function PEM_read_bio_PrivateKey(bp: PBIO; x: PPEVP_PKEY; cb: pem_password_cb; u: Pointer): PEVP_PKEY; cdecl; + external LIBEAY_DLL name _PU + 'PEM_read_bio_PrivateKey'; + +procedure OPENSSL_add_all_algorithms_noconf; cdecl; + external LIBEAY_DLL name _PU + 'OPENSSL_add_all_algorithms_noconf'; +procedure OPENSSL_add_all_algorithms_conf; cdecl; + external LIBEAY_DLL name _PU + 'OPENSSL_add_all_algorithms_conf'; + +procedure OpenSSL_add_all_ciphers; cdecl; + external LIBEAY_DLL name _PU + 'OpenSSL_add_all_ciphers'; +procedure OpenSSL_add_all_digests; cdecl; + external LIBEAY_DLL name _PU + 'OpenSSL_add_all_digests'; +{$ENDREGION} + +{$ENDIF} + +function SSL_CTX_need_tmp_RSA(ctx: PSSL_CTX): Integer; inline; +function SSL_CTX_set_tmp_rsa(ctx: PSSL_CTX; rsa: MarshaledAString): Integer; inline; +function SSL_CTX_set_tmp_dh(ctx: PSSL_CTX; dh: MarshaledAString): Integer; inline; +function SSL_CTX_set_tmp_ecdh(ctx: PSSL_CTX; ecdh: PEC_KEY): Integer; inline; +function SSL_CTX_add_extra_chain_cert(ctx: PSSL_CTX; cert: PX509): Integer; inline; +function SSL_need_tmp_RSA(ssl: PSSL): Integer; inline; +function SSL_set_tmp_rsa(ssl: PSSL; rsa: MarshaledAString): Integer; inline; +function SSL_set_tmp_dh(ssl: PSSL; dh: MarshaledAString): Integer; inline; +function SSL_set_tmp_ecdh(ssl: PSSL; ecdh: MarshaledAString): Integer; inline; + +function SSL_CTX_set_options(ctx: PSSL_CTX; Op: Integer): Integer; inline; +function SSL_CTX_get_options(ctx: PSSL_CTX): Integer; inline; +function SSL_CTX_set_mode(ctx: PSSL_CTX; op: Integer): Integer; inline; +function SSL_CTX_clear_mode(ctx: PSSL_CTX; op: Integer): Integer; inline; +function SSL_CTX_get_mode(ctx: PSSL_CTX): Integer; inline; + +function SSL_set_options(ssl: PSSL; Op: Integer): Integer; inline; +function SSL_get_options(ssl: PSSL): Integer; inline; +function SSL_clear_options(ssl: PSSL; Op: Integer): Integer; inline; + +function BIO_eof(bp: PBIO): Boolean; inline; +function BIO_pending(bp: PBIO): Integer; inline; +function BIO_get_mem_data(bp: PBIO; parg: Pointer): Integer; inline; +function BIO_get_flags(b: PBIO): Integer; inline; +function BIO_should_retry(b: PBIO): Boolean; inline; + +function SSL_is_init_finished(s: PSSL): Boolean; inline; + +function ssl_is_fatal_error(ssl_error: Integer): Boolean; +function ssl_error_message(ssl_error: Integer): string; + +function sk_ASN1_OBJECT_num(stack: PSTACK): Integer; inline; +function sk_GENERAL_NAME_num(stack: PSTACK): Integer; inline; +function sk_GENERAL_NAME_pop(stack: PSTACK): Pointer; inline; + +type + ESsl = class(Exception); + ESslInvalidLib = class(ESsl); + ESslInvalidProc = class(ESsl); + + {$REGION 'SSLTools'} + TSSLTools = class + private class var + FRef: Integer; + public + class procedure LoadSSL; + class procedure UnloadSSL; + class function SSLVersion: Longword; + class function NewCTX(meth: PSSL_METHOD = nil): PSSL_CTX; + class procedure FreeCTX(var AContext: PSSL_CTX); + + class procedure SetCertificate(AContext: PSSL_CTX; ACertBuf: Pointer; ACertBufSize: Integer); overload; + class procedure SetCertificate(AContext: PSSL_CTX; const ACertStr: string); overload; + class procedure SetCertificateFile(AContext: PSSL_CTX; const ACertFile: string); + + class procedure SetPrivateKey(AContext: PSSL_CTX; APKeyBuf: Pointer; APKeyBufSize: Integer); overload; + class procedure SetPrivateKey(AContext: PSSL_CTX; const APKeyStr: string); overload; + class procedure SetPrivateKeyFile(AContext: PSSL_CTX; const APKeyFile: string); + end; + {$ENDREGION} + +{$IFNDEF __SSL_STATIC__} + +var + _SslLibPath: string; + _SslLibHandle: THandle; + _CryptoLibHandle: THandle; + + {$REGION 'SSL-FUNC'} + SSL_library_init: function: Integer; cdecl; + SSL_load_error_strings: procedure; cdecl; + + SSLv23_method: function: PSSL_METHOD; cdecl; + SSLv23_client_method: function: PSSL_METHOD; cdecl; + SSLv23_server_method: function: PSSL_METHOD; cdecl; + + TLSv1_method: function: PSSL_METHOD; cdecl; + TLSv1_client_method: function: PSSL_METHOD; cdecl; + TLSv1_server_method: function: PSSL_METHOD; cdecl; + + TLSv1_2_method: function: PSSL_METHOD; cdecl; + TLSv1_2_client_method: function: PSSL_METHOD; cdecl; + TLSv1_2_server_method: function : PSSL_METHOD; cdecl; + + SSL_CTX_new: function(meth: PSSL_METHOD): PSSL_CTX; cdecl; + SSL_CTX_free: procedure(ctx: PSSL_CTX); cdecl; + SSL_CTX_ctrl: function(ctx: PSSL_CTX; Cmd: Integer; LArg: Integer; PArg: MarshaledAString): Integer; cdecl; + SSL_CTX_set_verify: procedure(ctx: PSSL_CTX; mode: Integer; callback: TSetVerify_cb); cdecl; + SSL_CTX_set_cipher_list: function(ctx: PSSL_CTX; CipherString: MarshaledAString): Integer; cdecl; + SSL_CTX_use_PrivateKey: function(ctx: PSSL_CTX; pkey: PEVP_PKEY): Integer; cdecl; + SSL_CTX_use_certificate: function(ctx: PSSL_CTX; cert: PX509): Integer; cdecl; + SSL_CTX_check_private_key: function(ctx: PSSL_CTX): Integer; cdecl; + + SSL_new: function(ctx: PSSL_CTX): PSSL; cdecl; + SSL_set_bio: procedure(s: PSSL; rbio, wbio: PBIO); cdecl; + SSL_get_peer_certificate: function(s: PSSL): PX509; cdecl; + SSL_get_error: function(s: PSSL; ret_code: Integer): Integer; cdecl; + + SSL_ctrl: function(S: PSSL; Cmd: Integer; LArg: Integer; PArg: Pointer): Integer; cdecl; + + SSL_shutdown: function(s: PSSL): Integer; cdecl; + SSL_free: procedure(s: PSSL); cdecl; + + SSL_set_connect_state: procedure(s: PSSL); cdecl; + SSL_set_accept_state: procedure(s: PSSL); cdecl; + SSL_accept: function(S: PSSL): Integer; cdecl; + SSL_connect: function(S: PSSL): Integer; cdecl; + SSL_do_handshake: function(S: PSSL): Integer; cdecl; + SSL_read: function(s: PSSL; buf: Pointer; num: Integer): Integer; cdecl; + SSL_write: function(s: PSSL; const buf: Pointer; num: Integer): Integer; cdecl; + SSL_state: function(s: PSSL): Integer; cdecl; + SSL_pending: function(s: PSSL): Integer; cdecl; + + SSL_CTX_get_cert_store: function(const Ctx: PSSL_CTX): PX509_STORE; cdecl; + SSL_CTX_add_client_CA: function(C: PSSL_CTX; CaCert: PX509): Integer; cdecl; + {$ENDREGION} + + {$REGION 'LIBEAY-FUNC'} + SSLeay: function: Longword; cdecl; + + CRYPTO_set_id_callback: function(callback: TStatLockIDCallback): Integer; cdecl; + + {$IF not(defined(MACOS) and not defined(IOS))} + // MACOS 内置的 OpenSSL 库版本较低(0.9.x) + // CRYPTO_THREADID_set_callback 只有 OpenSSL 1.0 以上版本才有 + CRYPTO_THREADID_set_callback: function(callback: TCryptoThreadIDCallback): Integer; cdecl; + CRYPTO_THREADID_set_numeric: procedure(id : PCRYPTO_THREADID; val: LongWord); cdecl; + CRYPTO_THREADID_set_pointer: procedure(id : PCRYPTO_THREADID; ptr: Pointer); cdecl; + {$ENDIF} + + CRYPTO_num_locks: function: Integer; cdecl; + CRYPTO_set_locking_callback: procedure(callback: TStatLockLockCallback); cdecl; + CRYPTO_set_dynlock_create_callback: procedure(callback: TDynLockCreateCallBack); cdecl; + CRYPTO_set_dynlock_lock_callback: procedure(callback: TDynLockLockCallBack); cdecl; + CRYPTO_set_dynlock_destroy_callback: procedure(callback: TDynLockDestroyCallBack); cdecl; + CRYPTO_cleanup_all_ex_data: procedure; cdecl; + + ERR_remove_state: procedure(tid: Cardinal); cdecl; + + {$IF not(defined(MACOS) and not defined(IOS))} + ERR_remove_thread_state: procedure(tid: PCRYPTO_THREADID); cdecl; + {$ENDIF} + + ERR_free_strings: procedure; cdecl; + ERR_error_string_n: procedure(err: Cardinal; buf: MarshaledAString; len: size_t); cdecl; + ERR_get_error: function: Cardinal; cdecl; + ERR_clear_error: procedure; cdecl; + + EVP_cleanup: procedure; cdecl; + EVP_PKEY_free: procedure(pkey: PEVP_PKEY); cdecl; + + BIO_new: function(BioMethods: PBIO_METHOD): PBIO; cdecl; + BIO_ctrl: function(bp: PBIO; cmd: Integer; larg: Longint; parg: Pointer): Longint; cdecl; + BIO_new_mem_buf: function(buf: Pointer; len: Integer): PBIO; cdecl; + BIO_free: function(b: PBIO): Integer; cdecl; + BIO_s_mem: function: PBIO_METHOD; cdecl; + BIO_read: function(b: PBIO; Buf: Pointer; Len: Integer): Integer; cdecl; + BIO_write: function(b: PBIO; Buf: Pointer; Len: Integer): Integer; cdecl; + + EC_KEY_new_by_curve_name: function(nid: Integer): PEC_KEY; cdecl; + EC_KEY_free: procedure(key: PEC_KEY); cdecl; + + X509_get_issuer_name: function(cert: PX509): PX509_NAME; cdecl; + X509_get_subject_name: function(cert: PX509): PX509_NAME; cdecl; + X509_free: procedure(cert: PX509); cdecl; + X509_NAME_print_ex: function(bout: PBIO; nm: PX509_NAME; indent: Integer; flags: Cardinal): Integer; cdecl; + X509_get_ext_d2i: function(x: PX509; nid: Integer; var crit, idx: Integer): Pointer; cdecl; + + X509_STORE_add_cert: function(Store: PX509_STORE; Cert: PX509): Integer; cdecl; + + sk_num: function(stack: PSTACK): Integer; cdecl; + sk_pop: function(stack: PSTACK): Pointer; cdecl; + + ASN1_BIT_STRING_get_bit: function(a: PASN1_BIT_STRING; n: Integer): Integer; cdecl; + OBJ_obj2nid: function(o: PASN1_OBJECT): Integer; cdecl; + OBJ_nid2sn: function(n: Integer): MarshaledAString; cdecl; + ASN1_STRING_data: function(x: PASN1_STRING): Pointer; cdecl; + PEM_read_bio_X509: function(bp: PBIO; x: PPX509; cb: pem_password_cb; u: Pointer): PX509; cdecl; + PEM_read_bio_X509_AUX: function(bp: PBIO; x: PPX509; cb: pem_password_cb; u: Pointer): PX509; cdecl; + PEM_read_bio_PrivateKey: function(bp: PBIO; x: PPEVP_PKEY; cb: pem_password_cb; u: Pointer): PEVP_PKEY; cdecl; + + OPENSSL_add_all_algorithms_noconf: procedure; cdecl; + OPENSSL_add_all_algorithms_conf: procedure; cdecl; + + OpenSSL_add_all_ciphers: procedure; cdecl; + OpenSSL_add_all_digests: procedure; cdecl; + {$ENDREGION} + +{$ENDIF} + +implementation + +uses + System.IOUtils; + +var + _FSslLocks: TArray; + _FSslVersion: Longword; + +function SSL_CTX_need_tmp_RSA(ctx: PSSL_CTX): Integer; +begin + Result := SSL_CTX_ctrl(ctx, SSL_CTRL_NEED_TMP_RSA, 0, nil); +end; + +function SSL_CTX_set_tmp_rsa(ctx: PSSL_CTX; rsa: MarshaledAString): Integer; +begin + Result := SSL_CTX_ctrl(ctx, SSL_CTRL_SET_TMP_RSA, 0, rsa); +end; + +function SSL_CTX_set_tmp_dh(ctx: PSSL_CTX; dh: MarshaledAString): Integer; +begin + Result := SSL_CTX_ctrl(ctx, SSL_CTRL_SET_TMP_DH, 0, dh); +end; + +function SSL_CTX_set_tmp_ecdh(ctx: PSSL_CTX; ecdh: PEC_KEY): Integer; +begin + Result := SSL_CTX_ctrl(ctx, SSL_CTRL_SET_TMP_ECDH, 0, MarshaledAString(ecdh)); +end; + +function SSL_CTX_add_extra_chain_cert(ctx: PSSL_CTX; cert: PX509): Integer; +begin + Result := SSL_CTX_ctrl(ctx, SSL_CTRL_EXTRA_CHAIN_CERT, 0, MarshaledAString(cert)); +end; + +function SSL_need_tmp_RSA(ssl: PSSL): Integer; +begin + Result := SSL_ctrl(ssl, SSL_CTRL_NEED_TMP_RSA, 0, nil); +end; + +function SSL_set_tmp_rsa(ssl: PSSL; rsa: MarshaledAString): Integer; +begin + Result := SSL_ctrl(ssl, SSL_CTRL_SET_TMP_RSA, 0, rsa); +end; + +function SSL_set_tmp_dh(ssl: PSSL; dh: MarshaledAString): Integer; +begin + Result := SSL_ctrl(ssl, SSL_CTRL_SET_TMP_DH, 0, dh); +end; + +function SSL_set_tmp_ecdh(ssl: PSSL; ecdh: MarshaledAString): Integer; +begin + Result := SSL_ctrl(ssl, SSL_CTRL_SET_TMP_ECDH, 0, ecdh); +end; + +function SSL_CTX_set_options(ctx: PSSL_CTX; Op: Integer): Integer; +begin + Result := SSL_CTX_ctrl(ctx, SSL_CTRL_OPTIONS, Op, nil); +end; + +function SSL_CTX_get_options(ctx: PSSL_CTX): Integer; +begin + Result := SSL_CTX_ctrl(ctx, SSL_CTRL_OPTIONS, 0, nil); +end; + +function SSL_CTX_set_mode(ctx: PSSL_CTX; op: Integer): Integer; +begin + Result := SSL_CTX_ctrl(ctx, SSL_CTRL_MODE, op, nil); +end; + +function SSL_CTX_clear_mode(ctx: PSSL_CTX; op: Integer): Integer; +begin + Result := SSL_CTX_ctrl(ctx, SSL_CTRL_CLEAR_MODE, op, nil); +end; + +function SSL_CTX_get_mode(ctx: PSSL_CTX): Integer; +begin + Result := SSL_CTX_ctrl(ctx, SSL_CTRL_MODE, 0, nil); +end; + +function SSL_set_options(ssl: PSSL; Op: Integer): Integer; +begin + Result := SSL_ctrl(ssl, SSL_CTRL_OPTIONS, Op, nil); +end; + +function SSL_get_options(ssl: PSSL): Integer; +begin + Result := SSL_ctrl(ssl, SSL_CTRL_OPTIONS, 0, nil); +end; + +function SSL_clear_options(ssl: PSSL; Op: Integer): Integer; +begin + Result := SSL_ctrl(ssl, SSL_CTRL_CLEAR_OPTIONS, Op, nil); +end; + +function BIO_eof(bp: PBIO): Boolean; +begin + Result := (BIO_ctrl(bp, BIO_CTRL_EOF, 0, nil) <> 0); +end; + +function BIO_pending(bp: PBIO): Integer; +begin + Result := BIO_ctrl(bp, BIO_CTRL_PENDING, 0, nil); +end; + +function BIO_get_mem_data(bp: PBIO; parg: Pointer): Integer; +begin + Result := BIO_ctrl(bp, BIO_CTRL_INFO, 0, parg); +end; + +function sk_ASN1_OBJECT_num(stack: PSTACK): Integer; +begin + Result := sk_num(stack); +end; + +function sk_GENERAL_NAME_num(stack: PSTACK): Integer; +begin + Result := sk_num(stack); +end; + +function sk_GENERAL_NAME_pop(stack: PSTACK): Pointer; +begin + Result := sk_pop(stack); +end; + +function BIO_get_flags(b: PBIO): Integer; +begin + // This is a hack : BIO structure has not been defined. But I know + // flags member is the 6th field in the structure (index is 5) + // This could change when OpenSSL is updated. Check "struct bio_st". + Result := PInteger(MarshaledAString(b) + 3 * SizeOf(Pointer) + 2 * SizeOf(Integer))^; +end; + +function BIO_should_retry(b: PBIO): Boolean; +begin + Result := ((BIO_get_flags(b) and BIO_FLAGS_SHOULD_RETRY) <> 0); +end; + +function SSL_is_init_finished(s: PSSL): Boolean; +begin + Result := (SSL_state(s) = SSL_ST_OK); +end; + +function ssl_is_fatal_error(ssl_error: Integer): Boolean; +begin + case ssl_error of + SSL_ERROR_NONE, + SSL_ERROR_WANT_READ, + SSL_ERROR_WANT_WRITE, + SSL_ERROR_WANT_CONNECT, + SSL_ERROR_WANT_ACCEPT: Result := False; + else + Result := True; + end; +end; + +function ssl_error_message(ssl_error: Integer): string; +var + LPtr: TPtrWrapper; +begin + LPtr := TMarshal.AllocMem(1024); + try + ERR_error_string_n(ssl_error, LPtr.ToPointer, 1024); + Result := TMarshal.ReadStringAsAnsi(LPtr); + finally + TMarshal.FreeMem(LPtr); + end; +end; + +function set_id_callback: Longword; cdecl; +begin + Result := GetCurrentThreadID; +end; + +{$IF not(defined(MACOS) and not defined(IOS))} +procedure ssl_threadid_callback(ID : PCRYPTO_THREADID); cdecl; +begin + CRYPTO_THREADID_set_numeric(ID, GetCurrentThreadId); +end; +{$ENDIF} + +procedure ssl_lock_callback(Mode, N: Integer; + const _File: MarshaledAString; Line: Integer); cdecl; +begin + if(mode and CRYPTO_LOCK <> 0) then + _FSslLocks[N].Enter + else + _FSslLocks[N].Leave; +end; + +procedure ssl_lock_dyn_callback(Mode: Integer; + L: PCRYPTO_dynlock_value; _File: MarshaledAString; Line: Integer); cdecl; +begin + if (Mode and CRYPTO_LOCK <> 0) then + L.Mutex.Enter + else + L.Mutex.Leave; +end; + +function ssl_lock_dyn_create_callback( + const _file: MarshaledAString; Line: Integer): PCRYPTO_dynlock_value; cdecl; +begin + New(Result); + Result.Mutex := TCriticalSection.Create; +end; + +procedure ssl_lock_dyn_destroy_callback( + L: PCRYPTO_dynlock_value; _File: MarshaledAString; Line: Integer); cdecl; +begin + L.Mutex.Free; + Dispose(L); +end; + +{$IFNDEF __SSL_STATIC__} + +function GetSslLibPath: string; +begin + if (_SslLibPath <> '') then + Result := IncludeTrailingPathDelimiter(_SslLibPath) + else + Result := _SslLibPath; +end; + +function LoadSslLib(const ALibName: string): THandle; +begin + Result := SafeLoadLibrary(GetSslLibPath + ALibName); + if (Result = 0) then + raise ESslInvalidLib.CreateFmt('无效的SSL库: %s', [ALibName]); +end; + +function GetSslLibProc(const ALibHandle: THandle; const AProcName: string): Pointer; +begin + Result := GetProcAddress(ALibHandle, PChar(AProcName)); + if (Result = nil) then + raise ESslInvalidProc.CreateFmt('无效的SSL接口函数: %s', [AProcName]); +end; + +procedure LoadSslLibs; +begin + if (_SslLibHandle = 0) then + begin + _SslLibHandle := LoadSslLib(SSLEAY_DLL); + + @SSL_library_init := GetSslLibProc(_SslLibHandle, 'SSL_library_init'); + @SSL_load_error_strings := GetSslLibProc(_SslLibHandle, 'SSL_load_error_strings'); + + @SSLv23_method := GetSslLibProc(_SslLibHandle, 'SSLv23_method'); + @SSLv23_client_method := GetSslLibProc(_SslLibHandle, 'SSLv23_client_method'); + @SSLv23_server_method := GetSslLibProc(_SslLibHandle, 'SSLv23_server_method'); + + @TLSv1_method := GetSslLibProc(_SslLibHandle, 'TLSv1_method'); + @TLSv1_client_method := GetSslLibProc(_SslLibHandle, 'TLSv1_client_method'); + @TLSv1_server_method := GetSslLibProc(_SslLibHandle, 'TLSv1_server_method'); + + {$IF not(defined(MACOS) and not defined(IOS))} + @TLSv1_2_method := GetSslLibProc(_SslLibHandle, 'TLSv1_2_method'); + @TLSv1_2_client_method := GetSslLibProc(_SslLibHandle, 'TLSv1_2_client_method'); + @TLSv1_2_server_method := GetSslLibProc(_SslLibHandle, 'TLSv1_2_server_method'); + {$ENDIF} + + @SSL_CTX_new := GetSslLibProc(_SslLibHandle, 'SSL_CTX_new'); + @SSL_CTX_free := GetSslLibProc(_SslLibHandle, 'SSL_CTX_free'); + @SSL_CTX_ctrl := GetSslLibProc(_SslLibHandle, 'SSL_CTX_ctrl'); + @SSL_CTX_set_verify := GetSslLibProc(_SslLibHandle, 'SSL_CTX_set_verify'); + @SSL_CTX_set_cipher_list := GetSslLibProc(_SslLibHandle, 'SSL_CTX_set_cipher_list'); + @SSL_CTX_use_PrivateKey := GetSslLibProc(_SslLibHandle, 'SSL_CTX_use_PrivateKey'); + @SSL_CTX_use_certificate := GetSslLibProc(_SslLibHandle, 'SSL_CTX_use_certificate'); + @SSL_CTX_check_private_key := GetSslLibProc(_SslLibHandle, 'SSL_CTX_check_private_key'); + + @SSL_new := GetSslLibProc(_SslLibHandle, 'SSL_new'); + @SSL_set_bio := GetSslLibProc(_SslLibHandle, 'SSL_set_bio'); + @SSL_get_peer_certificate := GetSslLibProc(_SslLibHandle, 'SSL_get_peer_certificate'); + @SSL_get_error := GetSslLibProc(_SslLibHandle, 'SSL_get_error'); + + @SSL_ctrl := GetSslLibProc(_SslLibHandle, 'SSL_ctrl'); + + @SSL_shutdown := GetSslLibProc(_SslLibHandle, 'SSL_shutdown'); + @SSL_free := GetSslLibProc(_SslLibHandle, 'SSL_free'); + + @SSL_set_connect_state := GetSslLibProc(_SslLibHandle, 'SSL_set_connect_state'); + @SSL_set_accept_state := GetSslLibProc(_SslLibHandle, 'SSL_set_accept_state'); + @SSL_accept := GetSslLibProc(_SslLibHandle, 'SSL_accept'); + @SSL_connect := GetSslLibProc(_SslLibHandle, 'SSL_connect'); + @SSL_do_handshake := GetSslLibProc(_SslLibHandle, 'SSL_do_handshake'); + @SSL_read := GetSslLibProc(_SslLibHandle, 'SSL_read'); + @SSL_write := GetSslLibProc(_SslLibHandle, 'SSL_write'); + @SSL_state := GetSslLibProc(_SslLibHandle, 'SSL_state'); + @SSL_pending := GetSslLibProc(_SslLibHandle, 'SSL_pending'); + + @SSL_CTX_get_cert_store := GetSslLibProc(_SslLibHandle, 'SSL_CTX_get_cert_store'); + @SSL_CTX_add_client_CA := GetSslLibProc(_SslLibHandle, 'SSL_CTX_add_client_CA'); + end; + + if (_CryptoLibHandle = 0) then + begin + _CryptoLibHandle := LoadSslLib(LIBEAY_DLL); + + @SSLeay := GetSslLibProc(_CryptoLibHandle, 'SSLeay'); + + @CRYPTO_set_id_callback := GetSslLibProc(_CryptoLibHandle, 'CRYPTO_set_id_callback'); + + {$IF not(defined(MACOS) and not defined(IOS))} + // MACOS 内置的 OpenSSL 库版本较低(0.9.x) + // CRYPTO_THREADID_set_callback 只有 OpenSSL 1.0 以上版本才有 + @CRYPTO_THREADID_set_callback := GetSslLibProc(_CryptoLibHandle, 'CRYPTO_THREADID_set_callback'); + @CRYPTO_THREADID_set_numeric := GetSslLibProc(_CryptoLibHandle, 'CRYPTO_THREADID_set_numeric'); + @CRYPTO_THREADID_set_pointer := GetSslLibProc(_CryptoLibHandle, 'CRYPTO_THREADID_set_pointer'); + {$ENDIF} + + @CRYPTO_num_locks := GetSslLibProc(_CryptoLibHandle, 'CRYPTO_num_locks'); + @CRYPTO_set_locking_callback := GetSslLibProc(_CryptoLibHandle, 'CRYPTO_set_locking_callback'); + @CRYPTO_set_dynlock_create_callback := GetSslLibProc(_CryptoLibHandle, 'CRYPTO_set_dynlock_create_callback'); + @CRYPTO_set_dynlock_lock_callback := GetSslLibProc(_CryptoLibHandle, 'CRYPTO_set_dynlock_lock_callback'); + @CRYPTO_set_dynlock_destroy_callback := GetSslLibProc(_CryptoLibHandle, 'CRYPTO_set_dynlock_destroy_callback'); + @CRYPTO_cleanup_all_ex_data := GetSslLibProc(_CryptoLibHandle, 'CRYPTO_cleanup_all_ex_data'); + + @ERR_remove_state := GetSslLibProc(_CryptoLibHandle, 'ERR_remove_state'); + + {$IF not(defined(MACOS) and not defined(IOS))} + @ERR_remove_thread_state := GetSslLibProc(_CryptoLibHandle, 'ERR_remove_thread_state'); + {$ENDIF} + + @ERR_free_strings := GetSslLibProc(_CryptoLibHandle, 'ERR_free_strings'); + @ERR_error_string_n := GetSslLibProc(_CryptoLibHandle, 'ERR_error_string_n'); + @ERR_get_error := GetSslLibProc(_CryptoLibHandle, 'ERR_get_error'); + @ERR_clear_error := GetSslLibProc(_CryptoLibHandle, 'ERR_clear_error'); + + @EVP_cleanup := GetSslLibProc(_CryptoLibHandle, 'EVP_cleanup'); + @EVP_PKEY_free := GetSslLibProc(_CryptoLibHandle, 'EVP_PKEY_free'); + + @BIO_new := GetSslLibProc(_CryptoLibHandle, 'BIO_new'); + @BIO_ctrl := GetSslLibProc(_CryptoLibHandle, 'BIO_ctrl'); + @BIO_new_mem_buf := GetSslLibProc(_CryptoLibHandle, 'BIO_new_mem_buf'); + @BIO_free := GetSslLibProc(_CryptoLibHandle, 'BIO_free'); + @BIO_s_mem := GetSslLibProc(_CryptoLibHandle, 'BIO_s_mem'); + @BIO_read := GetSslLibProc(_CryptoLibHandle, 'BIO_read'); + @BIO_write := GetSslLibProc(_CryptoLibHandle, 'BIO_write'); + + @EC_KEY_new_by_curve_name := GetSslLibProc(_CryptoLibHandle, 'EC_KEY_new_by_curve_name'); + @EC_KEY_free := GetSslLibProc(_CryptoLibHandle, 'EC_KEY_free'); + + @X509_get_issuer_name := GetSslLibProc(_CryptoLibHandle, 'X509_get_issuer_name'); + @X509_get_subject_name := GetSslLibProc(_CryptoLibHandle, 'X509_get_subject_name'); + @X509_free := GetSslLibProc(_CryptoLibHandle, 'X509_free'); + @X509_NAME_print_ex := GetSslLibProc(_CryptoLibHandle, 'X509_NAME_print_ex'); + @X509_get_ext_d2i := GetSslLibProc(_CryptoLibHandle, 'X509_get_ext_d2i'); + + @X509_STORE_add_cert := GetSslLibProc(_CryptoLibHandle, 'X509_STORE_add_cert'); + + @sk_num := GetSslLibProc(_CryptoLibHandle, 'sk_num'); + @sk_pop := GetSslLibProc(_CryptoLibHandle, 'sk_pop'); + + @ASN1_BIT_STRING_get_bit := GetSslLibProc(_CryptoLibHandle, 'ASN1_BIT_STRING_get_bit'); + @OBJ_obj2nid := GetSslLibProc(_CryptoLibHandle, 'OBJ_obj2nid'); + @OBJ_nid2sn := GetSslLibProc(_CryptoLibHandle, 'OBJ_nid2sn'); + @ASN1_STRING_data := GetSslLibProc(_CryptoLibHandle, 'ASN1_STRING_data'); + @PEM_read_bio_X509 := GetSslLibProc(_CryptoLibHandle, 'PEM_read_bio_X509'); + @PEM_read_bio_X509_AUX := GetSslLibProc(_CryptoLibHandle, 'PEM_read_bio_X509_AUX'); + @PEM_read_bio_PrivateKey := GetSslLibProc(_CryptoLibHandle, 'PEM_read_bio_PrivateKey'); + + @OPENSSL_add_all_algorithms_noconf := GetSslLibProc(_CryptoLibHandle, 'OPENSSL_add_all_algorithms_noconf'); + @OPENSSL_add_all_algorithms_conf := GetSslLibProc(_CryptoLibHandle, 'OPENSSL_add_all_algorithms_conf'); + + @OpenSSL_add_all_ciphers := GetSslLibProc(_CryptoLibHandle, 'OpenSSL_add_all_ciphers'); + @OpenSSL_add_all_digests := GetSslLibProc(_CryptoLibHandle, 'OpenSSL_add_all_digests'); + end; +end; + +procedure UnloadSslLibs; +begin + if (_SslLibHandle <> 0) then + begin + FreeLibrary(_SslLibHandle); + _SslLibHandle := 0; + end; + + if (_CryptoLibHandle <> 0) then + begin + FreeLibrary(_CryptoLibHandle); + _CryptoLibHandle := 0; + end; +end; + +{$ENDIF} + +procedure SslInit; +var + LNumberOfLocks, I: Integer; +begin + SSL_library_init(); + OPENSSL_add_all_algorithms_noconf; + SSL_load_error_strings(); + + _FSslVersion := SSLeay(); + + LNumberOfLocks := CRYPTO_num_locks(); + if(LNumberOfLocks > 0) then + begin + SetLength(_FSslLocks, LNumberOfLocks); + for I := Low(_FSslLocks) to High(_FSslLocks) do + _FSslLocks[I] := TCriticalSection.Create; + end; + + CRYPTO_set_locking_callback(ssl_lock_callback); + {$IF defined(MACOS) and not defined(IOS)} + CRYPTO_set_id_callback(set_id_callback); + {$ELSE} + CRYPTO_THREADID_set_callback(ssl_threadid_callback); + {$ENDIF} +end; + +procedure SslUninit; +var + I: Integer; +begin + CRYPTO_set_locking_callback(nil); + + CRYPTO_cleanup_all_ex_data(); + {$IF defined(MACOS) and not defined(IOS)} + ERR_remove_state(0); + {$ELSE} + ERR_remove_thread_state(nil); + {$ENDIF} + ERR_clear_error(); + ERR_free_strings(); + EVP_cleanup(); + + for I := Low(_FSslLocks) to High(_FSslLocks) do + _FSslLocks[I].Free; + _FSslLocks := nil; +end; + +{ TSSLTools } + +class function TSSLTools.NewCTX(meth: PSSL_METHOD): PSSL_CTX; +begin + if (meth = nil) then + meth := SSLv23_method(); + Result := SSL_CTX_new(meth); +end; + +class function TSSLTools.SSLVersion: Longword; +begin + Result := _FSslVersion; +end; + +class procedure TSSLTools.FreeCTX(var AContext: PSSL_CTX); +begin + SSL_CTX_free(AContext); + AContext := nil; +end; + +class procedure TSSLTools.LoadSSL; +begin + if (TInterlocked.Increment(FRef) <> 1) then Exit; + + {$IFNDEF __SSL_STATIC__} + LoadSslLibs; + {$ENDIF} + + SslInit; +end; + +class procedure TSSLTools.UnloadSSL; +begin + if (TInterlocked.Decrement(FRef) <> 0) then Exit; + + SslUninit; + + {$IFNDEF __SSL_STATIC__} + UnloadSslLibs; + {$ENDIF} +end; + +class procedure TSSLTools.SetCertificate(AContext: PSSL_CTX; ACertBuf: Pointer; + ACertBufSize: Integer); +var + bio_cert: PBIO; + ssl_cert: PX509; + store: PX509_STORE; +begin + bio_cert := BIO_new_mem_buf(ACertBuf, ACertBufSize); + if (bio_cert = nil) then + raise ESsl.Create('分配证书缓存失败'); + + ssl_cert := PEM_read_bio_X509_AUX(bio_cert, nil, nil, nil); + if (ssl_cert = nil) then + raise ESsl.Create('读取证书数据失败'); + + if (SSL_CTX_use_certificate(AContext, ssl_cert) <= 0) then + raise ESsl.Create('使用证书失败'); + + X509_free(ssl_cert); + + store := SSL_CTX_get_cert_store(AContext); + if (store = nil) then + raise ESsl.Create('获取证书仓库失败'); + + // 将证书链中剩余的证书添加到仓库中 + // 有完整证书链在 ssllabs.com 评分中才能评为 A + while not BIO_eof(bio_cert) do + begin + ssl_cert := PEM_read_bio_X509(bio_cert, nil, nil, nil); + if (ssl_cert = nil) then + raise ESsl.Create('读取证书数据失败'); + + if (X509_STORE_add_cert(store, ssl_cert) <= 0) then + raise ESsl.Create('添加证书到仓库失败'); + + X509_free(ssl_cert); + end; + + BIO_free(bio_cert); +end; + +class procedure TSSLTools.SetCertificate(AContext: PSSL_CTX; + const ACertStr: string); +var + LCertBytes: TBytes; +begin + LCertBytes := TEncoding.ANSI.GetBytes(ACertStr); + SetCertificate(AContext, Pointer(LCertBytes), Length(LCertBytes)); +end; + +class procedure TSSLTools.SetCertificateFile(AContext: PSSL_CTX; + const ACertFile: string); +var + LCertBytes: TBytes; +begin + LCertBytes := TFile.ReadAllBytes(ACertFile); + SetCertificate(AContext, Pointer(LCertBytes), Length(LCertBytes)); +end; + +class procedure TSSLTools.SetPrivateKey(AContext: PSSL_CTX; APKeyBuf: Pointer; + APKeyBufSize: Integer); +var + bio_pkey: PBIO; + ssl_pkey: PEVP_PKEY; +begin + bio_pkey := BIO_new_mem_buf(APKeyBuf, APKeyBufSize); + if (bio_pkey = nil) then + raise ESsl.Create('分配私钥缓存失败'); + + ssl_pkey := PEM_read_bio_PrivateKey(bio_pkey, nil, nil, nil); + if (ssl_pkey = nil) then + raise ESsl.Create('读取私钥数据失败'); + + if (SSL_CTX_use_PrivateKey(AContext, ssl_pkey) <= 0) then + raise ESsl.Create('使用私钥失败'); + + EVP_PKEY_free(ssl_pkey); + BIO_free(bio_pkey); + + if (SSL_CTX_check_private_key(AContext) <= 0) then + raise ESsl.Create('私钥与证书的公钥不匹配'); +end; + +class procedure TSSLTools.SetPrivateKey(AContext: PSSL_CTX; + const APKeyStr: string); +var + LPKeyBytes: TBytes; +begin + LPKeyBytes := TEncoding.ANSI.GetBytes(APKeyStr); + SetPrivateKey(AContext, Pointer(LPKeyBytes), Length(LPKeyBytes)); +end; + +class procedure TSSLTools.SetPrivateKeyFile(AContext: PSSL_CTX; + const APKeyFile: string); +var + LPKeyBytes: TBytes; +begin + LPKeyBytes := TFile.ReadAllBytes(APKeyFile); + SetPrivateKey(AContext, Pointer(LPKeyBytes), Length(LPKeyBytes)); +end; + +end. diff --git a/ThirdParty/DCS/Net/Net.Posix.inc b/ThirdParty/DCS/Net/Net.Posix.inc new file mode 100644 index 00000000..2542e007 --- /dev/null +++ b/ThirdParty/DCS/Net/Net.Posix.inc @@ -0,0 +1,42 @@ +function PosixSend(ASocket: THandle; ABuf: Pointer; + ALen: Integer): Integer; +var + LBuf: PByte; + LSent, LError: Integer; + LFlags: Integer; +begin + Result := 0; + + // һѾرյ׽ַʱϵͳֱ׳EPIPE쳣³˳ + // LINUX¿sendʱMSG_NOSIGNALܱķ + // OSXпͨ׽ֵSO_NOSIGPIPEﵽͬĿ + {$IF defined(LINUX) or defined(ANDROID)} + LFlags := MSG_NOSIGNAL; + {$ELSE} + LFlags := 0; + {$ENDIF} + + LBuf := ABuf; + while (Result < ALen) do + begin + LSent := TSocketAPI.Send(ASocket, LBuf^, ALen - Result, LFlags); + + if (LSent < 0) then + begin + LError := GetLastError; + + // ϵͳźж, send + if (LError = EINTR) then + Continue + // ͻѱ + else if (LError = EAGAIN) or (LError = EWOULDBLOCK) then + Break + // ͳ + else + Exit(-1); + end; + + Inc(Result, LSent); + Inc(LBuf, LSent); + end; +end; diff --git a/ThirdParty/DCS/Net/Net.RawSocket.pas b/ThirdParty/DCS/Net/Net.RawSocket.pas new file mode 100644 index 00000000..427905e5 --- /dev/null +++ b/ThirdParty/DCS/Net/Net.RawSocket.pas @@ -0,0 +1,189 @@ +unit Net.RawSocket; + +interface + +uses + System.SysUtils, Net.SocketAPI, + {$IFDEF POSIX} + Posix.Base, Posix.SysSocket, Posix.NetinetIn + {$ELSE} + Winapi.Windows, Net.Winsock2, Net.Wship6 + {$ENDIF}; + +type + /// + /// 򵥵׽ֲ + /// + TRawSocket = class + private + FSocket: THandle; + FSockAddr: TRawSockAddrIn; + FPeerAddr: string; + FPeerPort: Word; + public + /// + /// ر Socket + /// + procedure Close; + + /// + /// ӵ, ֧ IPv6 + /// + function Connect(const AHost: string; APort: Word): Integer; + + /// + /// Socket ַָͶ˿, ֧ IPv6 + /// + function Bind(const Addr: string; APort: Word): Integer; + + /// + /// + /// + function Listen(backlog: Integer = SOMAXCONN): Integer; + + /// + /// һ, Socket + /// + function Accept(Addr: PSockAddr; AddrLen: PInteger): THandle; + + /// + /// + /// + function Recv(var Buf; len: Integer; flags: Integer = 0): Integer; + + /// + /// + /// + function Send(const Buf; len: Integer; flags: Integer = 0): Integer; + + /// + /// ݴַָ˿(UDP) + /// + function RecvFrom(const Addr: PSockAddr; var AddrLen: Integer; var Buf; + len: Integer; flags: Integer = 0): Integer; + + /// + /// ݵַָ˿(UDP) + /// + function SendTo(const Addr: PSockAddr; AddrLen: Integer; const Buf; + len: Integer; flags: Integer = 0): Integer; + + /// + /// ж׽ǷЧ + /// + function IsValid: Boolean; + + /// + /// ׽־ + /// + property Socket: THandle read FSocket; + + /// + /// ׽ֵַϢ + /// Connect Զ˵׽, õַԶ˵ַϢ + /// Bind ص׽, õַDZصַϢ + /// + property SockAddr: TRawSockAddrIn read FSockAddr; + property PeerAddr: string read FPeerAddr; + property PeerPort: Word read FPeerPort; + end; + +implementation + +{ TRawSocket } + +procedure TRawSocket.Close; +begin + if (FSocket = INVALID_HANDLE_VALUE) then Exit; + + TSocketAPI.CloseSocket(FSocket); + FSocket := INVALID_HANDLE_VALUE; +end; + +function TRawSocket.Connect(const AHost: string; APort: Word): Integer; +var + LHints: TRawAddrInfo; + LAddrInfo: PRawAddrInfo; +begin + FillChar(LHints, SizeOf(TRawAddrInfo), 0); + LHints.ai_family := AF_UNSPEC; + LHints.ai_socktype := SOCK_STREAM; + LHints.ai_protocol := IPPROTO_TCP; + LAddrInfo := TSocketAPI.GetAddrInfo(AHost, APort.ToString, LHints); + if (LAddrInfo = nil) then Exit(-1); + + try + FSocket := TSocketAPI.NewSocket(LAddrInfo.ai_family, LAddrInfo.ai_socktype, + LAddrInfo.ai_protocol); + if (FSocket = INVALID_HANDLE_VALUE) then Exit(-1); + + FSockAddr.AddrLen := LAddrInfo.ai_addrlen; + Move(LAddrInfo.ai_addr^, FSockAddr.Addr, LAddrInfo.ai_addrlen); + TSocketAPI.ExtractAddrInfo(@FSockAddr.Addr, FSockAddr.AddrLen, FPeerAddr, FPeerPort); + + TSocketAPI.SetKeepAlive(FSocket, 5, 3, 5); + Result := TSocketAPI.Connect(FSocket, @FSockAddr.Addr, FSockAddr.AddrLen); + finally + TSocketAPI.FreeAddrInfo(LAddrInfo); + end; +end; + +function TRawSocket.Bind(const Addr: string; APort: Word): Integer; +var + LHints: TRawAddrInfo; + LAddrInfo: PRawAddrInfo; +begin + FillChar(LHints, SizeOf(TRawAddrInfo), 0); + LHints.ai_family := AF_UNSPEC; + LHints.ai_socktype := SOCK_STREAM; + LHints.ai_protocol := IPPROTO_TCP; + LAddrInfo := TSocketAPI.GetAddrInfo(Addr, APort.ToString, LHints); + if (LAddrInfo = nil) then Exit(-1); + + FSockAddr.AddrLen := LAddrInfo.ai_addrlen; + Move(LAddrInfo.ai_addr^, FSockAddr.Addr, LAddrInfo.ai_addrlen); + TSocketAPI.FreeAddrInfo(LAddrInfo); + + TSocketAPI.ExtractAddrInfo(@FSockAddr.Addr, FSockAddr.AddrLen, FPeerAddr, FPeerPort); + + Result := TSocketAPI.Bind(FSocket, @FSockAddr.Addr, FSockAddr.AddrLen); +end; + +function TRawSocket.Listen(backlog: Integer): Integer; +begin + Result := TSocketAPI.Listen(FSocket, backlog); +end; + +function TRawSocket.Accept(Addr: PSockAddr; AddrLen: PInteger): THandle; +begin + Result := TSocketAPI.Accept(FSocket, Addr, AddrLen); +end; + +function TRawSocket.Recv(var Buf; len, flags: Integer): Integer; +begin + Result := TSocketAPI.Recv(FSocket, Buf, len, flags); +end; + +function TRawSocket.Send(const Buf; len, flags: Integer): Integer; +begin + Result := TSocketAPI.Send(FSocket, Buf, len, flags); +end; + +function TRawSocket.RecvFrom(const Addr: PSockAddr; var AddrLen: Integer; + var Buf; len, flags: Integer): Integer; +begin + Result := TSocketAPI.RecvFrom(FSocket, Addr, AddrLen, Buf, len, flags); +end; + +function TRawSocket.SendTo(const Addr: PSockAddr; AddrLen: Integer; const Buf; + len: Integer; flags: Integer): Integer; +begin + Result := TSocketAPI.SendTo(FSocket, Addr, AddrLen, Buf, len, flags); +end; + +function TRawSocket.IsValid: Boolean; +begin + Result := TSocketAPI.IsValidSocket(FSocket); +end; + +end. diff --git a/ThirdParty/DCS/Net/Net.SocketAPI.pas b/ThirdParty/DCS/Net/Net.SocketAPI.pas new file mode 100644 index 00000000..a88a0877 --- /dev/null +++ b/ThirdParty/DCS/Net/Net.SocketAPI.pas @@ -0,0 +1,776 @@ +{******************************************************************************} +{ } +{ Delphi cross platform socket library } +{ } +{ Copyright (c) 2017 WiNDDRiVER(soulawing@gmail.com) } +{ } +{ Homepage: https://github.com/winddriver/Delphi-Cross-Socket } +{ } +{******************************************************************************} +unit Net.SocketAPI; + +interface + +uses + System.SysUtils, + {$IFDEF POSIX} + Posix.Base, Posix.UniStd, Posix.SysSocket, Posix.ArpaInet, Posix.NetinetIn, + Posix.NetDB, Posix.NetinetTCP, Posix.Fcntl, Posix.SysSelect, Posix.StrOpts, + Posix.SysTime, Posix.Errno + {$IFDEF LINUX} + ,Linuxapi.KernelIoctl + {$ENDIF} + {$ELSE} + Winapi.Windows, Net.Winsock2, Net.Wship6 + {$ENDIF}; + +type + TRawSockAddrIn = packed record + AddrLen: Integer; + case Integer of + 0: (Addr: sockaddr_in); + 1: (Addr6: sockaddr_in6); + end; + + {$IFDEF POSIX} + TRawAddrInfo = Posix.NetDB.addrinfo; + {$ELSE} + TRawAddrInfo = Net.Winsock2.ADDRINFOW; + {$ENDIF} + PRawAddrInfo = ^TRawAddrInfo; + + /// + /// ׽ֻӿڷװ + /// + TSocketAPI = class + public + /// + /// ½׽ + /// + class function NewSocket(const ADomain, AType, AProtocol: Integer): THandle; static; + + /// + /// ½ Tcp ׽ + /// + class function NewTcp: THandle; static; + + /// + /// ½ Udp ׽ + /// + class function NewUdp: THandle; static; + + /// + /// ر׽ + /// + class function CloseSocket(ASocket: THandle): Integer; static; + + /// + /// ֹͣ׽(SD_RECEIVE=0, SD_SEND=1, SD_BOTH=2) + /// + class function Shutdown(ASocket: THandle; AHow: Integer = 2): Integer; static; + + /// + /// һ, Socket + /// + class function Accept(ASocket: THandle; Addr: PSockAddr; AddrLen: PInteger): THandle; static; + + /// + /// ׽ֵַָͶ˿, ֧ IPv6 + /// + class function Bind(ASocket: THandle; Addr: PSockAddr; AddrLen: Integer): Integer; static; + + /// + /// ӵ, ֧ IPv6 + /// + class function Connect(ASocket: THandle; Addr: PSockAddr; AddrLen: Integer): Integer; static; + + /// + /// + /// + class function Listen(ASocket: THandle; backlog: Integer = SOMAXCONN): Integer; overload; static; + + /// + /// + /// + class function Recv(ASocket: THandle; var Buf; len: Integer; flags: Integer = 0): Integer; static; + + /// + /// + /// + class function Send(ASocket: THandle; const Buf; len: Integer; flags: Integer = 0): Integer; static; + + /// + /// ݴַָ˿(UDP) + /// + class function RecvFrom(ASocket: THandle; const Addr: PSockAddr; + var AddrLen: Integer; var Buf; len: Integer; flags: Integer = 0): Integer; static; + + /// + /// ݵַָ˿(UDP) + /// + class function SendTo(ASocket: THandle; const Addr: PSockAddr; + AddrLen: Integer; const Buf; len: Integer; flags: Integer = 0): Integer; static; + + /// + /// ׽ֹԶЭַ + /// + class function GetPeerName(ASocket: THandle; Addr: PSockAddr; + var AddrLen: Integer): Integer; static; + + /// + /// ׽ֹıЭַ + /// + class function GetSockName(ASocket: THandle; Addr: PSockAddr; + var AddrLen: Integer): Integer; static; + + /// + /// ȡ׽ֲ + /// + class function GetSockOpt(ASocket: THandle; ALevel, AOptionName: Integer; + var AOptionValue; var AOptionLen: Integer): Integer; overload; static; + + /// + /// ȡ׽ֲ + /// + class function GetSockOpt(ASocket: THandle; ALevel, AOptionName: Integer; + var AOptionValue: T): Integer; overload; static; + + /// + /// ׽ֲ + /// + class function SetSockOpt(ASocket: THandle; ALevel, AOptionName: Integer; + const AOptionValue; AOptionLen: Integer): Integer; overload; static; + + /// + /// ׽ֲ + /// + class function SetSockOpt(ASocket: THandle; ALevel, AOptionName: Integer; + const AOptionValue: T): Integer; overload; static; + + /// + /// ׽ִ + /// + class function GetError(ASocket: THandle): Integer; static; + + /// + /// ÷ģʽ + /// + class function SetNonBlock(ASocket: THandle; ANonBlock: Boolean = True): Integer; static; + + /// + /// õַģʽ + /// + class function SetReUseAddr(ASocket: THandle; AReUseAddr: Boolean = True): Integer; static; + + /// + /// + /// + class function SetKeepAlive(ASocket: THandle; AIdleSeconds, AInterval, ACount: Integer): Integer; static; + + /// + /// TCP_NODELAY + /// + class function SetTcpNoDelay(ASocket: THandle; ANoDelay: Boolean = True): Integer; static; + + /// + /// ÷ͻС + /// + class function SetSndBuf(ASocket: THandle; ABufSize: Integer): Integer; static; + + /// + /// ýջС + /// + class function SetRcvBuf(ASocket: THandle; ABufSize: Integer): Integer; static; + + /// + /// Linger(closesocket(), ǻûʱ) + /// + class function SetLinger(ASocket: THandle; const AOnOff: Boolean; ALinger: Integer): Integer; static; + + /// + /// ù㲥SO_BROADCAST + /// + class function SetBroadcast(ASocket: THandle; ABroadcast: Boolean = True): Integer; static; + + /// + /// ýճʱ(λΪms) + /// + class function SetRecvTimeout(ASocket: THandle; ATimeout: Cardinal): Integer; static; + + /// + /// ÷ͳʱ(λΪms) + /// + class function SetSendTimeout(ASocket: THandle; ATimeout: Cardinal): Integer; static; + + /// + /// 鿴ն + /// ATimeout < 0 + /// ATimeout = 0 + /// ATimeout > 0 ȴʱʱ + /// + class function Readable(ASocket: THandle; ATimeout: Integer): Integer; static; + + /// + /// 鿴Ͷ + /// ATimeout < 0 + /// ATimeout = 0 + /// ATimeout > 0 ȴʱʱ + /// + class function Writeable(ASocket: THandle; ATimeout: Integer): Integer; static; + + /// + /// ѽյֽ + /// + class function RecvdCount(ASocket: THandle): Integer; static; + + /// + /// ַϢ, ֧ IPv6 + /// + class function GetAddrInfo(const AHostName, AServiceName: string; + const AHints: TRawAddrInfo): PRawAddrInfo; overload; static; + + /// + /// ַϢ, ֧ IPv6 + /// + class function GetAddrInfo(const AHostName: string; APort: Word; + const AHints: TRawAddrInfo): PRawAddrInfo; overload; static; + + /// + /// ͷ GetAddrInfo ص + /// + class procedure FreeAddrInfo(ARawAddrInfo: PRawAddrInfo); static; + + /// + /// SockAddr ṹн IP ˿, ֧ IPv6 + /// + class procedure ExtractAddrInfo(const AAddr: PSockAddr; AAddrLen: Integer; + var AIP: string; var APort: Word); static; + + /// + /// Ϊ IP ַ, ֧ IPv6 + /// + class function GetIpAddrByHost(const AHost: string): string; static; + + /// + /// ׽ǷЧ + /// + class function IsValidSocket(ASocket: THandle): Boolean; static; + end; + +implementation + +{ TSocketAPI } + +class function TSocketAPI.NewSocket(const ADomain, AType, + AProtocol: Integer): THandle; +begin + Result := + {$IFDEF POSIX} + Posix.SysSocket. + {$ELSE} + Net.Winsock2. + {$ENDIF} + socket(ADomain, AType, AProtocol); + + {$IFDEF DEBUG} + if not IsValidSocket(Result) then + RaiseLastOSError; + {$ENDIF} +end; + +class function TSocketAPI.NewTcp: THandle; +begin + Result := TSocketAPI.NewSocket(AF_INET, SOCK_STREAM, IPPROTO_TCP); +end; + +class function TSocketAPI.NewUdp: THandle; +begin + Result := TSocketAPI.NewSocket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); +end; + +class function TSocketAPI.Readable(ASocket: THandle; ATimeout: Integer): Integer; +var + {$IFDEF POSIX} + LFDSet: fd_set; + LTime_val: timeval; + {$ELSE} + LFDSet: TFDSet; + LTime_val: TTimeval; + {$ENDIF} + P: PTimeVal; +begin + if (ATimeout >= 0) then + begin + LTime_val.tv_sec := ATimeout div 1000; + LTime_val.tv_usec := 1000 * (ATimeout mod 1000); + P := @LTime_val; + end else + P := nil; + + {$IFDEF POSIX} + FD_ZERO(LFDSet); + _FD_SET(ASocket, LFDSet); + Result := Posix.SysSelect.select(0, @LFDSet, nil, nil, P); + {$ELSE} + FD_ZERO(LFDSet); + FD_SET(ASocket, LFDSet); + Result := Net.Winsock2.select(0, @LFDSet, nil, nil, P); + {$ENDIF} +end; + +class function TSocketAPI.Recv(ASocket: THandle; var Buf; len, + flags: Integer): Integer; +begin + Result := + {$IFDEF POSIX} + Posix.SysSocket. + {$ELSE} + Net.Winsock2. + {$ENDIF} + recv(ASocket, Buf, len, flags); +end; + +class function TSocketAPI.RecvdCount(ASocket: THandle): Integer; +{$IFNDEF POSIX} +var + LTemp : Cardinal; +{$ENDIF} +begin + {$IFDEF POSIX} + Result := ioctl(ASocket, FIONREAD); + {$ELSE} + if ioctlsocket(ASocket, FIONREAD, LTemp) = SOCKET_ERROR then + Result := -1 + else + Result := LTemp; + {$ENDIF} +end; + +class function TSocketAPI.RecvFrom(ASocket: THandle; const Addr: PSockAddr; + var AddrLen: Integer; var Buf; len, flags: Integer): Integer; +begin + {$IFDEF POSIX} + Result := Posix.SysSocket.recvfrom(ASocket, Buf, len, flags, Addr^, Cardinal(AddrLen)); + {$ELSE} + Result := Net.Winsock2.recvfrom(ASocket, Buf, len, flags, Addr, @AddrLen); + {$ENDIF} +end; + +class function TSocketAPI.Accept(ASocket: THandle; Addr: PSockAddr; + AddrLen: PInteger): THandle; +begin + {$IFDEF POSIX} + Result := Posix.SysSocket.accept(ASocket, Addr^, Cardinal(AddrLen^)); + {$ELSE} + Result := Net.Winsock2.accept(ASocket, Addr, AddrLen); + {$ENDIF} +end; + +class function TSocketAPI.Bind(ASocket: THandle; Addr: PSockAddr; + AddrLen: Integer): Integer; +begin + {$IFDEF POSIX} + Result := Posix.SysSocket.bind(ASocket, Addr^, AddrLen); + {$ELSE} + Result := Net.Winsock2.bind(ASocket, Addr, AddrLen); + {$ENDIF} +end; + +class function TSocketAPI.CloseSocket(ASocket: THandle): Integer; +begin + {$IFDEF POSIX} + Result := Posix.UniStd.__close(ASocket); + {$ELSE} + Result := Net.Winsock2.closesocket(ASocket); + {$ENDIF} +end; + +class function TSocketAPI.Shutdown(ASocket: THandle; AHow: Integer): Integer; +begin + {$IFDEF POSIX} + Result := Posix.SysSocket.shutdown(ASocket, AHow); + {$ELSE} + Result := Net.Winsock2.shutdown(ASocket, AHow); + {$ENDIF} +end; + +class function TSocketAPI.Connect(ASocket: THandle; Addr: PSockAddr; + AddrLen: Integer): Integer; +begin + {$IFDEF POSIX} + Result := Posix.SysSocket.connect(ASocket, Addr^, AddrLen); + {$ELSE} + Result := Net.Winsock2.connect(ASocket, Addr, AddrLen); + {$ENDIF} + + {$IFDEF DEBUG} +// if (Result <> 0) and (GetLastError <> EINPROGRESS) then +// RaiseLastOSError; + {$ENDIF} +end; + +class function TSocketAPI.GetAddrInfo(const AHostName, AServiceName: string; + const AHints: TRawAddrInfo): PRawAddrInfo; +var + M: TMarshaller; + LHost, LService: Pointer; + LRet: Integer; + LAddrInfo: PRawAddrInfo; +begin + Result := nil; + + {$IFDEF POSIX} + if (AHostName <> '') then + LHost := M.AsAnsi(AHostName).ToPointer + else + LHost := nil; + if (AServiceName <> '') then + LService := M.AsAnsi(AServiceName).ToPointer + else + LService := nil; + LRet := Posix.NetDB.getaddrinfo(LHost, LService, AHints, Paddrinfo(LAddrInfo)); + {$ELSE} + if (AHostName <> '') then + LHost := M.OutString(AHostName).ToPointer + else + LHost := nil; + if (AServiceName <> '') then + LService := M.OutString(AServiceName).ToPointer + else + LService := nil; + LRet := Net.Wship6.getaddrinfo(LHost, LService, @AHints, @LAddrInfo); + {$ENDIF} + + if (LRet <> 0) then Exit; + + Result := LAddrInfo; +end; + +class function TSocketAPI.GetAddrInfo(const AHostName: string; APort: Word; + const AHints: TRawAddrInfo): PRawAddrInfo; +begin + Result := GetAddrInfo(AHostName, APort.ToString, AHints); +end; + +class function TSocketAPI.GetError(ASocket: THandle): Integer; +var + LRet, LErrLen: Integer; +begin + LErrLen := SizeOf(Integer); + LRet := TSocketAPI.GetSockOpt(ASocket, SOL_SOCKET, SO_ERROR, Result, LErrLen); + if (LRet <> 0) then + Result := LRet; +end; + +class procedure TSocketAPI.FreeAddrInfo(ARawAddrInfo: PRawAddrInfo); +begin + {$IFDEF POSIX} + Posix.NetDB.freeaddrinfo(ARawAddrInfo^); + {$ELSE} + Net.Wship6.freeaddrinfo(PAddrInfoW(ARawAddrInfo)); + {$ENDIF} +end; + +class procedure TSocketAPI.ExtractAddrInfo(const AAddr: PSockAddr; + AAddrLen: Integer; var AIP: string; var APort: Word); +var + M: TMarshaller; + LIP, LServInfo: TPtrWrapper; +begin + LIP := M.AllocMem(NI_MAXHOST); + LServInfo := M.AllocMem(NI_MAXSERV); + {$IFDEF POSIX} + getnameinfo(AAddr^, AAddrLen, LIP.ToPointer, NI_MAXHOST, LServInfo.ToPointer, NI_MAXSERV, NI_NUMERICHOST or NI_NUMERICSERV); + AIP := TMarshal.ReadStringAsAnsi(LIP); + APort := TMarshal.ReadStringAsAnsi(LServInfo).ToInteger; + {$ELSE} + getnameinfo(AAddr, AAddrLen, LIP.ToPointer, NI_MAXHOST, LServInfo.ToPointer, NI_MAXSERV, NI_NUMERICHOST or NI_NUMERICSERV); + AIP := TMarshal.ReadStringAsUnicode(LIP); + APort := TMarshal.ReadStringAsUnicode(LServInfo).ToInteger; + {$ENDIF} +end; + +class function TSocketAPI.GetIpAddrByHost(const AHost: string): string; +var + LHints: TRawAddrInfo; + LAddrInfo: PRawAddrInfo; + LPort: Word; +begin + FillChar(LHints, SizeOf(TRawAddrInfo), 0); + LAddrInfo := GetAddrInfo(AHost, '', LHints); + if (LAddrInfo = nil) then Exit(''); + ExtractAddrInfo(LAddrInfo.ai_addr, LAddrInfo.ai_addrlen, Result, LPort); + FreeAddrInfo(LAddrInfo); +end; + +class function TSocketAPI.GetPeerName(ASocket: THandle; Addr: PSockAddr; + var AddrLen: Integer): Integer; +begin + {$IFDEF POSIX} + Result := Posix.SysSocket.getpeername(ASocket, Addr^, Cardinal(AddrLen)); + {$ELSE} + Result := Net.Winsock2.getpeername(ASocket, Addr, AddrLen); + {$ENDIF} +end; + +class function TSocketAPI.GetSockName(ASocket: THandle; Addr: PSockAddr; + var AddrLen: Integer): Integer; +begin + {$IFDEF POSIX} + Result := Posix.SysSocket.getsockname(ASocket, Addr^, Cardinal(AddrLen)); + {$ELSE} + Result := Net.Winsock2.getsockname(ASocket, Addr, AddrLen); + {$ENDIF} +end; + +class function TSocketAPI.GetSockOpt(ASocket: THandle; ALevel, AOptionName: Integer; + var AOptionValue; var AOptionLen: Integer): Integer; +begin + {$IFDEF POSIX} + Result := Posix.SysSocket.getsockopt(ASocket, ALevel, AOptionName, AOptionValue, Cardinal(AOptionLen)); + {$ELSE} + Result := Net.Winsock2.getsockopt(ASocket, ALevel, AOptionName, PAnsiChar(@AOptionValue), AOptionLen); + {$ENDIF} +end; + +class function TSocketAPI.GetSockOpt(ASocket: THandle; ALevel, + AOptionName: Integer; var AOptionValue: T): Integer; +var + LOptionLen: Integer; +begin + Result := GetSockOpt(ASocket, ALevel, AOptionName, AOptionValue, LOptionLen); +end; + +class function TSocketAPI.IsValidSocket(ASocket: THandle): Boolean; +begin + Result := (ASocket <> INVALID_HANDLE_VALUE); +end; + +class function TSocketAPI.Listen(ASocket: THandle; backlog: Integer): Integer; +begin + Result := + {$IFDEF POSIX} + Posix.SysSocket. + {$ELSE} + Net.Winsock2. + {$ENDIF} + listen(ASocket, backlog); + + {$IFDEF DEBUG} +// if (Result <> 0) then +// RaiseLastOSError; + {$ENDIF} +end; + +class function TSocketAPI.Send(ASocket: THandle; const Buf; len, + flags: Integer): Integer; +begin + Result := + {$IFDEF POSIX} + Posix.SysSocket. + {$ELSE} + Net.Winsock2. + {$ENDIF} + send(ASocket, Buf, len, flags); +end; + +class function TSocketAPI.SendTo(ASocket: THandle; const Addr: PSockAddr; + AddrLen: Integer; const Buf; len, flags: Integer): Integer; +begin + {$IFDEF POSIX} + Result := Posix.SysSocket.sendto(ASocket, Buf, len, flags, Addr^, AddrLen); + {$ELSE} + Result := Net.Winsock2.sendto(ASocket, Buf, len, flags, Addr, AddrLen); + {$ENDIF} +end; + +class function TSocketAPI.SetBroadcast(ASocket: THandle; + ABroadcast: Boolean): Integer; +var + LOptVal: Integer; +begin + if ABroadcast then + LOptVal := 1 + else + LOptVal := 0; + Result := TSocketAPI.SetSockOpt(ASocket, SOL_SOCKET, SO_BROADCAST, LOptVal, SizeOf(Integer)); +end; + +class function TSocketAPI.SetKeepAlive(ASocket: THandle; AIdleSeconds, + AInterval, ACount: Integer): Integer; +var + LOptVal: Integer; + {$IFDEF MSWINDOWS} + LKeepAlive: tcp_keepalive; + LBytes: Cardinal; + {$ENDIF} +begin + LOptVal := 1; + Result := SetSockOpt(ASocket, SOL_SOCKET, SO_KEEPALIVE, LOptVal, SizeOf(Integer)); + if (Result < 0) then Exit; + + {$IFDEF MSWINDOWS} + // Windows ԴΪ 3 , ޷޸ + LKeepAlive.onoff := 1; + LKeepAlive.keepalivetime := AIdleSeconds * 1000; + LKeepAlive.keepaliveinterval := AInterval * 1000; + LBytes := 0; + Result := WSAIoctl(ASocket, SIO_KEEPALIVE_VALS, @LKeepAlive, SizeOf(tcp_keepalive), + nil, 0, @LBytes, nil, nil); + {$ELSEIF defined(MACOS)} + // MAC TCP_KEEPALIVE ൱ Linux е TCP_KEEPIDLE + // ݲ֧ TCP_KEEPINTVL TCP_KEEPCNT + // OSX 10.9.5Ĭϵ + // sysctl -A | grep net.inet.tcp.*keep + // ************************************** + // net.inet.tcp.keepidle: 7200000 + // net.inet.tcp.keepintvl: 75000 + // net.inet.tcp.keepinit: 75000 + // net.inet.tcp.keepcnt: 8 + // net.inet.tcp.always_keepalive: 0 + // ************************************** + Result := SetSockOpt(ASocket, IPPROTO_TCP, TCP_KEEPALIVE, AIdleSeconds, SizeOf(Integer)); + {$ELSEIF defined(LINUX) or defined(ANDROID)} + Result := SetSockOpt(ASocket, IPPROTO_TCP, TCP_KEEPIDLE, AIdleSeconds, SizeOf(Integer)); + if (Result < 0) then Exit; + + Result := SetSockOpt(ASocket, IPPROTO_TCP, TCP_KEEPINTVL, AInterval, SizeOf(Integer)); + if (Result < 0) then Exit; + + Result := SetSockOpt(ASocket, IPPROTO_TCP, TCP_KEEPCNT, ACount, SizeOf(Integer)); + if (Result < 0) then Exit; + {$ENDIF} +end; + +class function TSocketAPI.SetLinger(ASocket: THandle; + const AOnOff: Boolean; ALinger: Integer): Integer; +var + LLinger: linger; +begin + if AOnOff then + LLinger.l_onoff := 1 + else + LLinger.l_onoff := 0; + LLinger.l_linger := ALinger; + Result := SetSockOpt(ASocket, SOL_SOCKET, SO_LINGER, LLinger, SizeOf(linger)); +end; + +class function TSocketAPI.SetNonBlock(ASocket: THandle; + ANonBlock: Boolean): Integer; +var + LFlag: Cardinal; +begin + {$IFDEF POSIX} + LFlag := fcntl(ASocket, F_GETFL); + if ANonBlock then + LFlag := LFlag and not O_SYNC or O_NONBLOCK + else + LFlag := LFlag and not O_NONBLOCK or O_SYNC; + Result := fcntl(ASocket, F_SETFL, LFlag); + {$ELSE} + if ANonBlock then + LFlag := 1 + else + LFlag := 0; + Result := ioctlsocket(ASocket, FIONBIO, LFlag); + {$ENDIF} +end; + +class function TSocketAPI.SetReUseAddr(ASocket: THandle; + AReUseAddr: Boolean): Integer; +var + LOptVal: Integer; +begin + if AReUseAddr then + LOptVal := 1 + else + LOptVal := 0; + Result := TSocketAPI.SetSockOpt(ASocket, SOL_SOCKET, SO_REUSEADDR, LOptVal, SizeOf(Integer)); +end; + +class function TSocketAPI.SetRcvBuf(ASocket: THandle; + ABufSize: Integer): Integer; +begin + Result := TSocketAPI.SetSockOpt(ASocket, SOL_SOCKET, SO_RCVBUF, ABufSize, SizeOf(Integer)); +end; + +class function TSocketAPI.SetRecvTimeout(ASocket: THandle; + ATimeout: Cardinal): Integer; +begin + Result := SetSockOpt(ASocket, + SOL_SOCKET, SO_RCVTIMEO, ATimeout, SizeOf(Cardinal)); +end; + +class function TSocketAPI.SetSendTimeout(ASocket: THandle; + ATimeout: Cardinal): Integer; +begin + Result := TSocketAPI.SetSockOpt(ASocket, + SOL_SOCKET, SO_SNDTIMEO, ATimeout, SizeOf(Cardinal)); +end; + +class function TSocketAPI.SetSndBuf(ASocket: THandle; + ABufSize: Integer): Integer; +begin + Result := TSocketAPI.SetSockOpt(ASocket, SOL_SOCKET, SO_SNDBUF, ABufSize, SizeOf(Integer)); +end; + +class function TSocketAPI.SetSockOpt(ASocket: THandle; ALevel, AOptionName: Integer; + const AOptionValue; AOptionLen: Integer): Integer; +begin + {$IFDEF POSIX} + Result := Posix.SysSocket.setsockopt(ASocket, ALevel, AOptionName, AOptionValue, Cardinal(AOptionLen)); + {$ELSE} + Result := Net.Winsock2.setsockopt(ASocket, ALevel, AOptionName, PAnsiChar(@AOptionValue), AOptionLen); + {$ENDIF} +end; + +class function TSocketAPI.SetSockOpt(ASocket: THandle; ALevel, + AOptionName: Integer; const AOptionValue: T): Integer; +begin + Result := SetSockOpt(ASocket, ALevel, AOptionName, AOptionValue, SizeOf(T)); +end; + +class function TSocketAPI.SetTcpNoDelay(ASocket: THandle; + ANoDelay: Boolean): Integer; +var + LOptVal: Integer; +begin + if ANoDelay then + LOptVal := 1 + else + LOptVal := 0; + Result := TSocketAPI.SetSockOpt(ASocket, IPPROTO_TCP, TCP_NODELAY, LOptVal, SizeOf(Integer)); +end; + +class function TSocketAPI.Writeable(ASocket: THandle; + ATimeout: Integer): Integer; +var + {$IFDEF POSIX} + LFDSet: fd_set; + LTime_val: timeval; + {$ELSE} + LFDSet: TFDSet; + LTime_val: TTimeval; + {$ENDIF} + P: PTimeVal; +begin + if (ATimeout >= 0) then + begin + LTime_val.tv_sec := ATimeout div 1000; + LTime_val.tv_usec := 1000 * (ATimeout mod 1000); + P := @LTime_val; + end else + P := nil; + + {$IFDEF POSIX} + FD_ZERO(LFDSet); + _FD_SET(ASocket, LFDSet); + Result := Posix.SysSelect.select(0, nil, @LFDSet, nil, P); + {$ELSE} + FD_ZERO(LFDSet); + FD_SET(ASocket, LFDSet); + Result := Net.Winsock2.select(0, nil, @LFDSet, nil, P); + {$ENDIF} +end; + +end. diff --git a/ThirdParty/DCS/Net/Net.Winsock.inc b/ThirdParty/DCS/Net/Net.Winsock.inc new file mode 100644 index 00000000..7141c294 --- /dev/null +++ b/ThirdParty/DCS/Net/Net.Winsock.inc @@ -0,0 +1,1049 @@ +// General + +// Make this $DEFINE to use the 16 color icons required by Borland +// or DEFINE to use the 256 color Indy versions +{.$DEFINE Borland} + +// S.G. 4/9/2002: IPv4/IPv6 general switch (for defaults only) +{$DEFINE IdIPv4} + +{$DEFINE INDY100} +{$DEFINE 10_5_8} //so developers can IFDEF for this specific version + +{$IFDEF BCB} + {$DEFINE CBUILDER} +{$ELSE} + {$DEFINE DELPHI} +{$ENDIF} + +{$UNDEF USE_OPENSSL} +{$UNDEF USE_ZLIB_UNIT} +{$UNDEF USE_SSPI} + +// $DEFINE the following if the global objects in the IdStack and IdThread +// units should be freed on finalization +{.$DEFINE FREE_ON_FINAL} +{$UNDEF FREE_ON_FINAL} + +// Make sure the following is $DEFINE'd only for suitable environments +// as specified further below. This works in conjunction with the +// FREE_ON_FINAL define above. +{$UNDEF REGISTER_EXPECTED_MEMORY_LEAK} + +// FastMM is natively available in BDS 2006 and higher. $DEFINE the +// following if FastMM has been installed manually +{.$DEFINE USE_FASTMM4} +{$UNDEF USE_FASTMM4} + +// Make sure the following is $DEFINE'd only for Delphi/C++Builder 2009 onwards +// as specified further below. The VCL is fully Unicode, where the 'String' +// type maps to System.UnicodeString, not System.AnsiString anymore +{$UNDEF STRING_IS_UNICODE} +{$UNDEF STRING_IS_ANSI} + +// Make sure the following is $DEFINE'd only for suitable environments +// as specified further below. This works in conjunction with the +// STRING_IS_ANSI and STRING_IS_UNICODE defines above. +{$UNDEF UNICODE_BUT_STRING_IS_ANSI} + +// Make sure the following are $DEFINE'd only for suitable environments +// as specified further below. +{$UNDEF HAS_TEncoding} +{$UNDEF HAS_TCharacter} +{$UNDEF HAS_TInterlocked} +{$UNDEF TIdTextEncoding_IS_NATIVE} + +// Make sure that this is defined only for environments where we are using +// the iconv library to charactor conversions. +{.$UNDEF USE_ICONV} + +//Define for Delphi cross-compiler targetting Posix +{$UNDEF USE_VCL_POSIX} + +// detect compiler versions + +// Delphi 4 +{$IFDEF VER120} + {$DEFINE DCC} + {$DEFINE VCL_40} + {$DEFINE DELPHI_4} +{$ENDIF} + +// C++Builder 4 +{$IFDEF VER125} + {$DEFINE DCC} + {$DEFINE VCL_40} + {$DEFINE CBUILDER_4} +{$ENDIF} + +// Delphi & C++Builder 5 +{$IFDEF VER130} + {$DEFINE DCC} + {$DEFINE VCL_50} + {$IFDEF CBUILDER} + {$DEFINE CBUILDER_5} + {$ELSE} + {$DEFINE DELPHI_5} + {$ENDIF} +{$ENDIF} + +//Delphi & C++Builder 6 +{$IFDEF VER140} + {$DEFINE DCC} + {$DEFINE VCL_60} + {$IFDEF CBUILDER} + {$DEFINE CBUILDER_6} + {$ELSE} + {$DEFINE DELPHI_6} + {$ENDIF} +{$ENDIF} + +//Delphi 7 +{$IFDEF VER150} + {$DEFINE DCC} + {$DEFINE VCL_70} + {$DEFINE DELPHI_7} // there was no C++ Builder 7 +{$ENDIF} + +//Delphi 8 +{$IFDEF VER160} + {$DEFINE DCC} + {$DEFINE VCL_80} + {$DEFINE DELPHI_8} // there was no C++ Builder 8 +{$ENDIF} + +//Delphi 2005 +{$IFDEF VER170} + {$DEFINE DCC} + {$DEFINE VCL_2005} + {$DEFINE DELPHI_2005} // there was no C++Builder 2005 +{$ENDIF} + +// NOTE: CodeGear decided to make Highlander be a non-breaking release +// (no interface changes, thus fully backwards compatible without any +// end user code changes), so VER180 applies to both BDS 2006 and +// Highlander prior to the release of RAD Studio 2007. Use VER185 to +// identify Highlanger specifically. + +//Delphi & C++Builder 2006 +//Delphi & C++Builder 2007 (Highlander) +{$IFDEF VER180} + {$DEFINE DCC} + {$DEFINE VCL_2006} + {$IFDEF CBUILDER} + {$DEFINE CBUILDER_2006} + {$ELSE} + {$DEFINE DELPHI_2006} + {$ENDIF} +{$ENDIF} + +//Delphi & C++Builder 2007 (Highlander) +{$IFDEF VER185} + {$DEFINE DCC} + {$UNDEF VCL_2006} + {$DEFINE VCL_2007} + {$IFDEF CBUILDER} + {$UNDEF CBUILDER_2006} + {$DEFINE CBUILDER_2007} + {$ELSE} + {$UNDEF DELPHI_2006} + {$DEFINE DELPHI_2007} + {$ENDIF} +{$ENDIF} + +// BDS 2007 NET personality uses VER190 instead of 185. +//Delphi .NET 2007 +{$IFDEF VER190} + {$DEFINE DCC} + {$IFDEF CIL} + //Delphi 2007 + {$DEFINE VCL_2007} + {$DEFINE DELPHI_2007} + {$ENDIF} +{$ENDIF} + +//Delphi & C++Builder 2009 (Tiburon) +{$IFDEF VER200} + {$DEFINE DCC} + {$DEFINE VCL_2009} + {$IFDEF CBUILDER} + {$DEFINE CBUILDER_2009} + {$ELSE} + {$DEFINE DELPHI_2009} + {$ENDIF} +{$ENDIF} + +//Delphi & C++Builder 2010 (Weaver) +{$IFDEF VER210} + {$DEFINE DCC} + {$DEFINE VCL_2010} + {$IFDEF CBUILDER} + {$DEFINE CBUILDER_2010} + {$ELSE} + {$DEFINE DELPHI_2010} + {$ENDIF} +{$ENDIF} + +//Delphi & C++Builder XE (Fulcrum) +{$IFDEF VER220} +//REMOVE DCC DEFINE after the next Fulcrum beta. +//It will be defined there. + {$DEFINE DCC} + {$DEFINE VCL_XE} + {$IFDEF CBUILDER} + {$DEFINE CBUILDER_XE} + {$ELSE} + {$DEFINE DELPHI_XE} + {$ENDIF} +{$ENDIF} + +//Delphi & CBuilder XE2 (Pulsar) +{$IFDEF VER230} + // DCC is now defined by the compiler + {$DEFINE VCL_XE2} + {$IFDEF CBUILDER} + {$DEFINE CBUILDER_XE2} + {$ELSE} + {$DEFINE DELPHI_XE2} + {$ENDIF} +{$ENDIF} + +//Delphi & CBuilder XE3 & Above +{$if COMPILERVERSION >= 24} + // DCC is now defined by the compiler + {$DEFINE VCL_XE2} + {$IFDEF CBUILDER} + {$DEFINE CBUILDER_XE2} + {$ELSE} + {$DEFINE DELPHI_XE2} + {$ENDIF} +{$ifend} + +// Delphi.NET +// Covers D8+ +{$IFDEF CIL} + // Platform specific conditional. Used for platform specific code. + {$DEFINE DOTNET} + {$DEFINE STRING_IS_UNICODE} +{$ENDIF} + +// Kylix +// +//Important: Don't use CompilerVersion here as IF's are evaluated before +//IFDEF's and Kylix 1 does not have CompilerVersion defined at all. +{$IFNDEF FPC} + {$IFDEF LINUX} + {$DEFINE UNIX} + {$IFDEF CONDITIONALEXPRESSIONS} + {$IF (RTLVersion >= 14.0) and (RTLVersion <= 14.5) } + {$DEFINE KYLIX} + {$IF RTLVersion = 14.5} + {$DEFINE KYLIX_3} + {$ELSEIF RTLVersion >= 14.2} + {$DEFINE KYLIX_2} + {$ELSE} + {$DEFINE KYLIX_1} + {$IFEND} + {$IFEND} + {$ENDIF} + {$ENDIF} +{$ENDIF} + +{$IFDEF KYLIX} + {$DEFINE VCL_60} + {$DEFINE INT_THREAD_PRIORITY} + {$DEFINE CPUI386} + {$UNDEF USE_BASEUNIX} + + {$IFDEF KYLIX_3} + {$DEFINE KYLIX_3_OR_ABOVE} + {$ENDIF} + + {$IFDEF KYLIX_3_OR_ABOVE} + {$DEFINE KYLIX_2_OR_ABOVE} + {$ELSE} + {$IFDEF KYLIX_2} + {$DEFINE KYLIX_2_OR_ABOVE} + {$ENDIF} + {$ENDIF} + + {$IFDEF KYLIX_2_OR_ABOVE} + {$DEFINE KYLIX_1_OR_ABOVE} + {$ELSE} + {$IFDEF KYLIX_1} + {$DEFINE KYLIX_1_OR_ABOVE} + {$ENDIF} + {$ENDIF} + + {$IFNDEF KYLIX_3_OR_ABOVE} + {$DEFINE KYLIXCOMPAT} + {$ENDIF} + + {$IFDEF KYLIX_2_OR_ABOVE} + {$DEFINE USE_ZLIB_UNIT} + {$ENDIF} +{$ENDIF} + +// FPC (2+) + +{$IFDEF FPC} + {$MODE Delphi} + //note that we may need further defines for widget types depending on + //what we do and what platforms we support in FPC. + //I'll let Marco think about that one. + {$IFDEF UNIX} + {$DEFINE USE_BASEUNIX} + {$IFDEF LINUX} + //In Linux for I386, you can choose between a Kylix-libc API or + //the standard RTL Unix API. Just pass -dKYLIXCOMPAT to the FPC compiler. + //I will see what I can do about the Makefile. + {$IFDEF KYLIXCOMPAT} + {$IFDEF CPUI386} + {$UNDEF USE_BASEUNIX} + {$ENDIF} + {$ENDIF} + {$ENDIF} + {$IFDEF USE_BASEUNIX} + {$UNDEF KYLIXCOMPAT} + {$ENDIF} + {$ENDIF} + {$DEFINE VCL_70} + {$DEFINE DELPHI_7} + + // FPC_FULLVERSION was added in FPC 2.2.4 + // Have to use Declared() or else Delphi compiler chokes, since it + // evaluates $IF statements before $IFDEF statements... + + {$MACRO ON} // must be on in order to use versioning macros + {$IF DECLARED(FPC_FULLVERSION) and (FPC_FULLVERSION >= 20402)} + {$DEFINE FPC_2_4_2_OR_ABOVE} + {$IFEND} + + {$IFDEF FPC_2_4_2_OR_ABOVE} + {$DEFINE FPC_2_2_0_OR_ABOVE} + {$ELSE} + {$IFDEF VER2_2} + {$DEFINE FPC_2_2_0_OR_ABOVE} + {$ENDIF} + {$ENDIF} +{$ENDIF} + +// end FPC + +{$IFDEF VCL_XE2} + {$DEFINE VCL_XE2_OR_ABOVE} +{$ENDIF} + +{$IFDEF VCL_XE2_OR_ABOVE} + {$DEFINE VCL_XE_OR_ABOVE} +{$ELSE} + {$IFDEF VCL_XE} + {$DEFINE VCL_XE_OR_ABOVE} + {$ENDIF} +{$ENDIF} + +{$IFDEF VCL_XE_OR_ABOVE} + {$DEFINE VCL_2010_OR_ABOVE} +{$ELSE} + {$IFDEF VCL_2010} + {$DEFINE VCL_2010_OR_ABOVE} + {$ENDIF} +{$ENDIF} + +{$IFDEF VCL_2010_OR_ABOVE} + {$DEFINE VCL_2009_OR_ABOVE} +{$ELSE} + {$IFDEF VCL_2009} + {$DEFINE VCL_2009_OR_ABOVE} + {$ENDIF} +{$ENDIF} + +{$IFDEF VCL_2009_OR_ABOVE} + {$DEFINE VCL_2007_OR_ABOVE} +{$ELSE} + {$IFDEF VCL_2007} + {$DEFINE VCL_2007_OR_ABOVE} + {$ENDIF} +{$ENDIF} + +{$IFDEF VCL_2007_OR_ABOVE} + {$DEFINE VCL_2006_OR_ABOVE} +{$ELSE} + {$IFDEF VCL_2006} + {$DEFINE VCL_2006_OR_ABOVE} + {$ENDIF} +{$ENDIF} + +{$IFDEF VCL_2006_OR_ABOVE} + {$DEFINE VCL_2005_OR_ABOVE} +{$ELSE} + {$IFDEF VCL_2005} + {$DEFINE VCL_2005_OR_ABOVE} + {$ENDIF} +{$ENDIF} + +{$IFDEF VCL_2005_OR_ABOVE} + {$DEFINE VCL_8_OR_ABOVE} +{$ELSE} + {$IFDEF VCL_80} + {$DEFINE VCL_8_OR_ABOVE} + {$ENDIF} +{$ENDIF} + +{$IFDEF VCL_8_OR_ABOVE} + {$DEFINE VCL_7_OR_ABOVE} +{$ELSE} + {$IFDEF VCL_70} + {$DEFINE VCL_7_OR_ABOVE} + {$ENDIF} +{$ENDIF} + +{$IFDEF VCL_7_OR_ABOVE} + {$DEFINE VCL_6_OR_ABOVE} +{$ELSE} + {$IFDEF VCL_60} + {$DEFINE VCL_6_OR_ABOVE} + {$ENDIF} +{$ENDIF} + +{$IFDEF VCL_6_OR_ABOVE} + {$DEFINE VCL_5_OR_ABOVE} +{$ELSE} + {$IFDEF VCL_50} + {$DEFINE VCL_5_OR_ABOVE} + {$ENDIF} +{$ENDIF} + +{$IFDEF VCL_5_OR_ABOVE} + {$DEFINE VCL_4_OR_ABOVE} +{$ELSE} + {$IFDEF VCL_40} + {$DEFINE VCL_4_OR_ABOVE} + {$ENDIF} +{$ENDIF} + +// Normalize Delphi compiler defines to match FPC for consistency: +// +// CPU32 - any 32-bit CPU +// CPU64 - any 64-bit CPU +// WINDOWS - any Windows platform (32-bit, 64-bit, CE) +// WIN32 - Windows 32-bit +// WIN64 - Windows 64-bit +// WINCE - Windows CE +// +// Consult the "Free Pascal Programmer's Guide", Appendix G for the complete +// list of defines that are used. Do not work on this unless you understand +// what the FreePascal developers are doing. Not only do you have to +// descriminate with operating systems, but also with chip architectures +// are well. +// +// DCC Pulsar+ define the following values: +// ASSEMBLER +// DCC +// CONDITIONALEXPRESSIONS +// NATIVECODE +// UNICODE +// MACOS +// MACOS32 +// MACOS64 +// MSWINDOWS +// WIN32 +// WIN64 +// LINUX +// POSIX +// POSIX32 +// CPU386 +// CPUX86 +// CPUX64 +// +// Kylix defines the following values: +// LINUX +// (others??) +// + +{$IFNDEF FPC} + // TODO: We need to use ENDIAN_BIG for big endian chip architectures, + // such as 680x0, PowerPC, Sparc, and MIPS, once DCC supports them, + // provided it does not already define its own ENDIAN values by then... + {$DEFINE ENDIAN_LITTLE} + {$IFNDEF VCL_6_OR_ABOVE} + {$DEFINE MSWINDOWS} + {$ENDIF} + {$IFDEF MSWINDOWS} + {$DEFINE WINDOWS} + {$ENDIF} + // TODO: map Pulsar's non-Windows platform defines... + {$IFDEF VCL_XE2_OR_ABOVE} + {$IFDEF CPU386} + //any 32-bit CPU + {$DEFINE CPU32} + //Intel 386 compatible chip architecture + {$DEFINE CPUI386} + {$ENDIF} + {$IFDEF CPUX86} + {$DEFINE CPU32} + {$ENDIF} + {$IFDEF CPUX64} + //any 64-bit CPU + {$DEFINE CPU64} + //AMD64 compatible chip architecture + {$DEFINE CPUX86_64} //historical name for AMD64 + {$DEFINE CPUAMD64} + {$ENDIF} + {$ELSE} + {$IFNDEF DOTNET} + {$IFNDEF KYLIX} + {$DEFINE I386} + {$ENDIF} + {$ENDIF} + {$DEFINE CPU32} + {$ENDIF} +{$ENDIF} + +{$IFDEF DOTNET} + //differences in DotNET Framework versions. + {$IFDEF VCL_2007_OR_ABOVE} + {$DEFINE DOTNET_2} + {$DEFINE DOTNET_2_OR_ABOVE} + {$ELSE} + {$DEFINE DOTNET_1_1} + {$ENDIF} + {$DEFINE DOTNET_1_1_OR_ABOVE} + // Extra include used in D7 for testing. Remove later when all comps are + // ported. Used to selectively exclude non ported parts. Allowed in places + // IFDEFs are otherwise not permitted. + {$DEFINE DOTNET_EXCLUDE} +{$ENDIF} + +// Check for available features + +{$IFDEF VCL_5_OR_ABOVE} + {$IFNDEF FPC} + {$IFNDEF KYLIX} + {$DEFINE HAS_REMOVEFREENOTIFICATION} + {$ENDIF} + {$ENDIF} +{$ENDIF} + +{$IFDEF VCL_6_OR_ABOVE} + {$DEFINE HAS_TSelectionEditor} + {$DEFINE HAS_PCardinal} + {$DEFINE HAS_PByte} + {$DEFINE HAS_PWord} + {$DEFINE HAS_PPointer} + {$DEFINE HAS_TList_Assign} + {$DEFINE HAS_sLineBreak} + {$DEFINE HAS_RaiseLastOSError} + {$DEFINE HAS_SysUtils_IncludeExcludeTrailingPathDelimiter} + {$DEFINE HAS_SysUtils_DirectoryExists} + {$DEFINE HAS_UNIT_DateUtils} + {$DEFINE HAS_UNIT_Types} + {$DEFINE HAS_TryStrToInt} + {$DEFINE HAS_TryStrToInt64} + {$DEFINE HAS_ENUM_ELEMENT_VALUES} + {$IFNDEF FPC} + {$DEFINE HAS_TStringList_CaseSensitive} + {$IFNDEF KYLIX} + {$DEFINE HAS_DEPRECATED} + {$ENDIF} + {$ENDIF} + {$IFNDEF DOTNET} + //Widget defines are omitted in .NET + {$DEFINE VCL_60_PLUS} + {$ENDIF} +{$ENDIF} + +{$IFDEF VCL_7_OR_ABOVE} + {$IFNDEF FPC} + {$DEFINE HAS_UInt64} + {$DEFINE HAS_NAMED_THREADS} + {$ENDIF} + {$DEFINE HAS_TFormatSettings} + {$IFNDEF VCL_70} + // not implemented in D7 + {$DEFINE HAS_STATIC_TThread_Queue} + {$ENDIF} + {$IFNDEF VCL_80} + // not implemented in D8 + {$DEFINE HAS_STATIC_TThread_Synchronize} + {$ENDIF} +{$ELSE} + {$IFDEF CBUILDER_6} + {$DEFINE HAS_NAMED_THREADS} + {$ENDIF} +{$ENDIF} + +{$IFDEF VCL_2006_OR_ABOVE} + {$DEFINE USE_INLINE} + {$DEFINE HAS_2PARAM_FileAge} + {$DEFINE HAS_System_RegisterExpectedMemoryLeak} + {$IFNDEF FREE_ON_FINAL} + {$IFNDEF DOTNET} + {$DEFINE REGISTER_EXPECTED_MEMORY_LEAK} + {$ENDIF} + {$ENDIF} +{$ELSE} + {$DEFINE HAS_InterlockedCompareExchange_Pointers} +{$ENDIF} + +{$IFDEF VCL_2007_OR_ABOVE} + {$IFNDEF CBUILDER_2007} + // class properties are broken in C++Builder 2007, causing AVs at compile-time + {$DEFINE HAS_CLASSPROPERTIES} + {$ENDIF} + // Native(U)Int existed but were buggy, so do not use them yet + {.$DEFINE HAS_NativeInt} + {.$DEFINE HAS_NativeUInt} + {$DEFINE HAS_StrToInt64Def} + {$DEFINE HAS_ULONG_PTR} + {$DEFINE HAS_PGUID} + {$DEFINE HAS_PPAnsiChar} + {$DEFINE HAS_CurrentYear} + {$IFNDEF DOTNET} + {$DEFINE HAS_TIMEUNITS} + {$ENDIF} +{$ENDIF} + +{$IFDEF VCL_2009_OR_ABOVE} + {$IFNDEF DOTNET} + {$DEFINE STRING_IS_UNICODE} + {$DEFINE HAS_TEncoding} + {$DEFINE HAS_TCharacter} + {$DEFINE HAS_InterlockedCompareExchangePointer} + {$DEFINE HAS_WIDE_TCharArray} + {$IFDEF VCL_2009} + // TODO: need to differentiate between RTM and Update 1 + // FmtStr() is broken in RTM but was fixed in Update 1 + {$DEFINE BROKEN_FmtStr} + {$ENDIF} + {$IFNDEF VCL_XE_OR_ABOVE} + // TEncoding.GetEncoding() and TMBCSEncoding support for codepage 20127 + // have some bugs in 2009 and 2010 that were finally fixed in XE + {$DEFINE BROKEN_TEncoding_GetEncoding} + {$DEFINE BROKEN_TMBCSEncoding_CP20127} + {$ENDIF} + {$ENDIF} + {$DEFINE HAS_CLASSVARS} + {$DEFINE HAS_DEPRECATED_MSG} + {$DEFINE HAS_TBytes} + {$DEFINE HAS_NativeInt} + {$DEFINE HAS_NativeUInt} +{$ENDIF} + +{$IFDEF VCL_2010_OR_ABOVE} + {$DEFINE HAS_CLASSCONSTRUCTOR} + {$DEFINE HAS_CLASSDESTRUCTOR} + {$DEFINE HAS_DELAYLOAD} + {$DEFINE HAS_TStrings_ValueFromIndex} + {$DEFINE HAS_TThread_NameThreadForDebugging} + {$DEFINE DEPRECATED_TThread_SuspendResume} +{$ENDIF} + +{$IFDEF VCL_XE_OR_ABOVE} + {$DEFINE HAS_TFormatSettings_Object} + {$IFNDEF DOTNET} + {$DEFINE HAS_TInterlocked} + {$ENDIF} +{$ENDIF} + +{$IFDEF VCL_XE2_OR_ABOVE} + {$DEFINE HAS_SIZE_T} +{$ENDIF} + +// Delphi XE+ cross-compiling +{$IFNDEF FPC} + {$IFDEF POSIX} + {$IF RTLVersion >= 22.0} + {$DEFINE UNIX} + {$UNDEF USE_BASEUNIX} + {$DEFINE VCL_CROSS_COMPILE} + {$DEFINE USE_VCL_POSIX} + {$IFEND} + {$ENDIF} + {$IFDEF LINUX} + {$IFDEF CONDITIONALEXPRESSIONS} + {$IF RTLVersion >= 22.0} + {$DEFINE VCL_CROSS_COMPILE} + {$DEFINE USE_VCL_POSIX} + {$IFEND} + {$ENDIF} + {$ENDIF} +{$ENDIF} + +{$IFDEF VCL_CROSS_COMPILE} + {$UNDEF KYLIXCOMPAT} +{$ELSE} + {$IFDEF KYLIXCOMPAT} + {$linklib c} + {$ENDIF} +{$ENDIF} + +{$IFDEF FPC} + {$DEFINE USE_INLINE} + {$DEFINE USE_CLASSINLINE} + {$DEFINE USE_TBitBtn} //use Bit Buttons instead of Buttons + {$DEFINE FPC_REINTRODUCE_BUG} + {$DEFINE FPC_CIRCULAR_BUG} + {$DEFINE NO_REDECLARE} + {$DEFINE BYTE_COMPARE_SETS} + // FreePascal 2.2.0 has an overload for InterlockedCompareExchange that takes pointers. + {$IFDEF FPC_2_2_0_OR_ABOVE} + {$DEFINE HAS_InterlockedCompareExchange_Pointers} + {$ENDIF} + {$DEFINE HAS_PtrInt} + {$DEFINE HAS_PtrUInt} + {$DEFINE HAS_PGUID} + {$DEFINE HAS_LPGUID} + {$DEFINE HAS_PPAnsiChar} + {$DEFINE HAS_ENUM_ELEMENT_VALUES} + {$IFDEF WINDOWS} + {$DEFINE HAS_ULONG_PTR} + {$ENDIF} + {$DEFINE HAS_UNIT_ctypes} + // FreePascal 2.4.2 has size_t + {$IFDEF FPC_2_4_2_OR_ABOVE} + {$DEFINE HAS_SIZE_T} + {$ENDIF} +{$ENDIF} + +{$IFDEF DOTNET} + {$DEFINE WIDGET_WINFORMS} +{$ELSE} + {$DEFINE WIDGET_VCL_LIKE} // LCL included. + {$DEFINE WIDGET_VCL_LIKE_OR_KYLIX} + {$IFDEF FPC} + {$DEFINE WIDGET_LCL} + {$ELSE} + {$IFDEF KYLIX} + {$DEFINE WIDGET_KYLIX} + {$ELSE} + {$DEFINE WIDGET_VCL} + {$ENDIF} + {$ENDIF} +{$ENDIF} + +// .NET and Delphi 2009+ support UNICODE strings natively! +// NOTE: Do not define UNICODE here. The compiler defines +// the symbol automatically. +{$IFNDEF STRING_IS_UNICODE} + {$DEFINE STRING_IS_ANSI} + {$IFDEF UNICODE} + {$DEFINE UNICODE_BUT_STRING_IS_ANSI} + {$ENDIF} +{$ENDIF} + +{$IFDEF WIN32} + {$DEFINE WIN32_OR_WIN64} +{$ENDIF} +{$IFDEF WIN64} + {$DEFINE WIN32_OR_WIN64} +{$ENDIF} + +{$IFDEF WIN32_OR_WIN64} + {$DEFINE USE_OPENSSL} + {$DEFINE USE_ZLIB_UNIT} + {$DEFINE USE_SSPI} + {$IFDEF STRING_IS_UNICODE} + {$DEFINE SSPI_UNICODE} + {$ENDIF} +{$ENDIF} + +// High-performance counters are not reliable on multi-core systems, and have +// been known to cause problems with TIdIOHandler.ReadLn() timeouts in Windows +// XP SP3, both 32-bit and 64-bit. Refer to these discussions for more info: +// +// http://www.virtualdub.org/blog/pivot/entry.php?id=106 +// http://blogs.msdn.com/oldnewthing/archive/2008/09/08/8931563.aspx +// +// Do not enable thus unless you know it will work correctly on your systems! +{$IFDEF WINDOWS} + {.$DEFINE USE_HI_PERF_COUNTER_FOR_TICKS} +{$ENDIF} + +{$IFDEF UNIX} + {$DEFINE USE_OPENSSL} + {$DEFINE USE_ZLIB_UNIT} +{$ENDIF} + +{$IFDEF FPC_REQUIRES_PROPER_ALIGNMENT} + {$DEFINE REQUIRES_PROPER_ALIGNMENT} +{$ENDIF} + +// +//iconv defines section. +{$DEFINE USE_ICONV_UNIT} +{$DEFINE USE_ICONV_ENC} +{$IFDEF UNIX} + {$DEFINE USE_ICONV} + {$IFDEF USE_BASEUNIX} + {$IFDEF FPC} + {$UNDEF USE_ICONV_UNIT} + {$ELSE} + {$UNDEF USE_ICONV_ENC} + {$ENDIF} + {$ENDIF} + {$IFDEF KYLIXCOMPAT} + //important!! Iconv functions are defined in the libc.pas Kylix compatible unit. + {$UNDEF USE_ICONV_ENC} + {$UNDEF USE_ICONV_UNIT} + {$ENDIF} +{$ENDIF} +{$IFDEF NETWARELIBC} + {$DEFINE USE_ICONV} + //important!!! iconv functions are defined in the libc.pas Novell Netware header. + //Do not define USE_ICONVUNIT + {$UNDEF USE_ICONV_UNIT} + {$UNDEF USE_ICONV_ENC} +{$ENDIF} +{$IFNDEF USE_ICONV} + {$UNDEF USE_ICONV_UNIT} + {$UNDEF USE_ICONV_ENC} +{$ENDIF} +{$UNDEF USE_SAFELOADLIBRARY} +{$IFDEF WINDOWS} + {$UNDEF USE_ICONV_ENC} + {$DEFINE USE_SAFELOADLIBRARY} +{$ENDIF} + +{$UNDEF USE_INVALIDATE_MOD_CACHE} +{$UNDEF USE_SAFELOADLIBRARY} +//This must come after the iconv defines because this compiler targets a Unix-like +//operating system. One key difference is that it does have a TEncoding class. +//If this comes before the ICONV defines, it creates problems. +//This also must go before the THandle size calculations. +{$IFDEF VCL_CROSS_COMPILE} + {$IFDEF MACOS} + {$DEFINE BSD} + {$DEFINE DARWIN} + {$DEFINE USE_SAFELOADLIBRARY} + {$DEFINE USE_INVALIDATE_MOD_CACHE} + {$ENDIF} + //important!!! iconv functions are defined in the libc.pas Novell Netware header. + //Do not define USE_ICONVUNIT + {$UNDEF USE_ICONV} + {$UNDEF USE_ICONV_UNIT} + {$UNDEF USE_ICONV_ENC} + {$DEFINE INT_THREAD_PRIORITY} +{$ENDIF} + +//IMPORTANT!!!! +// +//Do not remove this!!! This is to work around a conflict. In DCC, MACOS +//will mean OS X. In FreePascal, the DEFINE MACOS means MacIntosh System OS Classic. +{$IFDEF DCC} + {$IFDEF MACOS} + {$DEFINE DARWIN} + {$ENDIF} +{$ENDIF} +{$IFDEF FPC} + {$IFDEF MACOS} + {$DEFINE MACOS_CLASSIC} + {$ENDIF} +{$ENDIF} + +{ +BSD 4.4 introduced a minor API change. sa_family was changed from a 16bit +word to an 8 bit byte and an 8 bit byte field named sa_len was added. +} +//Place this only after DARWIN has been defined for Delphi MACOS +{$IFDEF FREEBSD} + {$DEFINE SOCK_HAS_SINLEN} +{$ENDIF} +{$IFDEF DARWIN} + {$DEFINE SOCK_HAS_SINLEN} +{$ENDIF} +{$IFDEF HAIKU} + {$DEFINE SOCK_HAS_SINLEN} +{$ENDIF} +{$IFDEF MORPHOS} + {$DEFINE SOCK_HAS_SINLEN} +{$ENDIF} + +// Do NOT remove these IFDEF's. They are here because InterlockedExchange +// only handles 32bit values. Some Operating Systems may have 64bit +// THandles. This is not always tied to the platform architecture. + +{$IFDEF AMIGA} + {$DEFINE THANDLE_CPUBITS} +{$ENDIF} +{$IFDEF ATARI} + {$DEFINE THANDLE_32} +{$ENDIF} +{$IFDEF BEOS} + {$DEFINE THANDLE_32} +{$ENDIF} +{$IFDEF BSD} + //I think BSD might handle FreeBSD, NetBSD, OpenBSD, and Darwin + {$DEFINE THANDLE_32} +{$ENDIF} +{$IFDEF EMBEDDED} + {$DEFINE THANDLE_CPUBITS} +{$ENDIF} +{$IFDEF EMX} + {$DEFINE THANDLE_32} +{$ENDIF} +{$IFDEF GBA} + {$DEFINE THANDLE_32} +{$ENDIF} +{$IFDEF GO32} + {$DEFINE THANDLE_32} +{$ENDIF} +{$IFDEF HAIKU} + {$DEFINE THANDLE_32} +{$ENDIF} +{$IFDEF LINUX} + {$DEFINE THANDLE_32} +{$ENDIF} +{$IFDEF MACOS_CLASSIC} + {$DEFINE THANDLE_CPUBITS} +{$ENDIF} +{$IFDEF MORPHOS} + {$DEFINE THANDLE_CPUBITS} +{$ENDIF} +{$IFDEF NATIVENT} //Native NT for kernel level drivers + {$DEFINE THANDLE_CPUBITS} +{$ENDIF} +{$IFDEF NDS} + {$DEFINE THANDLE_32} +{$ENDIF} +{$IFDEF NETWARE} + {$DEFINE THANDLE_32} +{$ENDIF} +{$IFDEF NETWARELIBC} + {$DEFINE THANDLE_32} +{$ENDIF} +{$IFDEF OS2} + {$DEFINE THANDLE_32} +{$ENDIF} +{$IFDEF PALMOS} + {$DEFINE THANDLE_32} +{$ENDIF} +{$IFDEF SOLARIS} + {$DEFINE THANDLE_32} +{$ENDIF} +{$IFDEF SYMBIAN} + {$DEFINE THANDLE_32} +{$ENDIF} +{$IFDEF WII} + {$DEFINE THANDLE_32} +{$ENDIF} +{$IFDEF WATCOM} + {$DEFINE THANDLE_32} +{$ENDIF} +{$IFDEF WINDOWS} + {$DEFINE THANDLE_CPUBITS} +{$ENDIF} + +// end platform specific stuff for THandle size + +{$IFDEF THANDLE_CPUBITS} + {$IFDEF CPU64} + {$DEFINE THANDLE_64} + {$ELSE} + {$DEFINE THANDLE_32} + {$ENDIF} +{$ENDIF} + +{$IFDEF DOTNET} + {$DEFINE DOTNET_OR_ICONV} + {$DEFINE TIdTextEncoding_IS_NATIVE} +{$ELSE} + {$IFDEF HAS_TEncoding} + {$IFNDEF USE_ICONV} + {$DEFINE TIdTextEncoding_IS_NATIVE} + {$ENDIF} + {$ENDIF} +{$ENDIF} +{$IFDEF USE_ICONV} + {$DEFINE DOTNET_OR_ICONV} +{$ENDIF} + +{$UNDEF STREAM_SIZE_64} +{$IFDEF FPC} + {$DEFINE STREAM_SIZE_64} +{$ELSE} + {$IFDEF VCL_6_OR_ABOVE} + {$DEFINE STREAM_SIZE_64} + {$ENDIF} +{$ENDIF} + +{$IFNDEF FREE_ON_FINAL} + {$IFNDEF REGISTER_EXPECTED_MEMORY_LEAK} + {$IFDEF USE_FASTMM4} + {$DEFINE REGISTER_EXPECTED_MEMORY_LEAK} + {$ENDIF} + {$ENDIF} +{$ENDIF} + +{$IFDEF REGISTER_EXPECTED_MEMORY_LEAK} + {$IFDEF DOTNET} + {$UNDEF REGISTER_EXPECTED_MEMORY_LEAK} + {$ENDIF} + {$IFDEF VCL_CROSS_COMPILE} + // RLebeau: should this be enabled for Windows, at least? + {$UNDEF REGISTER_EXPECTED_MEMORY_LEAK} + {$ENDIF} +{$ENDIF} + +{ +We must determine what the SocketType parameter is for the Socket function. +In DotNET, it's SocketType. In Kylix and the libc.pas Kylix-compatibility +library, it's a __socket_type. In BaseUnix, it's a C-type Integer. In Windows, +it's a LongInt. + +} +{$UNDEF SOCKETTYPE_IS_SOCKETTYPE} +{$UNDEF SOCKETTYPE_IS_CINT} +{$UNDEF SOCKETTYPE_IS___SOCKETTYPE} +{$UNDEF SOCKETTYPE_IS_LONGINT} +{$UNDEF SOCKETTYPE_IS_NUMERIC} +{$UNDEF SOCKET_LEN_IS_socklen_t} +{$IFDEF DOTNET} + {$DEFINE SOCKETTYPE_IS_SOCKETTYPE} +{$ENDIF} +{$IFDEF USE_BASEUNIX} + {$DEFINE SOCKETTYPE_IS_CINT} + {$DEFINE SOCKETTYPE_IS_NUMERIC} +{$ENDIF} +{$IFDEF KYLIXCOMPAT} + {$DEFINE SOCKETTYPE_IS___SOCKETTYPE} +{$ENDIF} +{$IFDEF USE_VCL_POSIX} + {$DEFINE SOCKETTYPE_IS_NUMERIC} + {$DEFINE SOCKETTYPE_IS_LONGINT} + {$DEFINE SOCKET_LEN_IS_socklen_t} +{$ENDIF} +{$IFDEF WINDOWS} + {$DEFINE SOCKETTYPE_IS_LONGINT} + {$DEFINE SOCKETTYPE_IS_NUMERIC} +{$ENDIF} +{$IFDEF OS2} + {$DEFINE SOCKETTYPE_IS_LONGINT} + {$DEFINE SOCKETTYPE_IS_NUMERIC} +{$ENDIF} +{$IFDEF NETWARE} + {$DEFINE SOCKETTYPE_IS_LONGINT} + {$DEFINE SOCKETTYPE_IS_NUMERIC} +{$ENDIF} + +{Take advantage of some TCP features specific to some stacks. +They work somewhat similarly but there's a key difference. +In Linux, TCP_CORK is turned on to send fixed packet sizes and +when turned-off (uncorked), any remaining data is sent. With +TCP_NOPUSH, this might not happen and remaining data is only sent +before disconnect.} +{$UNDEF HAS_TCP_NOPUSH} +{$UNDEF HAS_TCP_CORK} +{$IFDEF BSD} + {$DEFINE HAS_TCP_NOPUSH} +{$ENDIF} +{$IFDEF LINUX} + {$DEFINE HAS_TCP_CORK} +{$ENDIF} +{$IFDEF SOLARIS} + {$DEFINE HAS_TCP_CORK} +{$ENDIF} + +{$IFDEF DEBUG} + {$UNDEF USE_INLINE} +{$ENDIF} diff --git a/ThirdParty/DCS/Net/Net.Winsock2.pas b/ThirdParty/DCS/Net/Net.Winsock2.pas new file mode 100644 index 00000000..1261e0ef --- /dev/null +++ b/ThirdParty/DCS/Net/Net.Winsock2.pas @@ -0,0 +1,6755 @@ +{ + $Project$ + $Workfile$ + $Revision$ + $DateUTC$ + $Id$ + + This file is part of the Indy (Internet Direct) project, and is offered + under the dual-licensing agreement described on the Indy website. + (http://www.indyproject.org/) + + Copyright: + (c) 1993-2005, Chad Z. Hower and the Indy Pit Crew. All rights reserved. +} +{ + $Log$ +} +{ + Log: 56400: IdWinsock2.pas + + Rev 1.0 2004.02.03 3:14:50 PM czhower + Move and updates + + + Rev 1.15 1/3/2004 12:41:48 AM BGooijen + Fixed WSAEnumProtocols + + + Rev 1.14 10/15/2003 1:20:48 PM DSiders + Added localization comments. + + + Rev 1.13 2003.10.01 11:16:38 AM czhower + .Net + + + Rev 1.12 9/24/2003 09:18:24 AM JPMugaas + Fixed an AV that happened when a stack call was made. + + + Rev 1.11 24/9/2003 3:11:34 PM SGrobety + First wave of fixes for compiling in dotnet. Still not functional, needed to + unlock to fix critical failure in Delphi code + + + Rev 1.10 9/22/2003 11:20:14 PM EHill + Removed assembly code and replaced with defined API stubs. + + + Rev 1.9 7/7/2003 12:55:10 PM BGooijen + Fixed ServiceQueryTransmitFile, and made it public + + + Rev 1.8 2003.05.09 10:59:30 PM czhower + + + Rev 1.7 4/19/2003 10:28:24 PM BGooijen + some functions were linked to the wrong dll + + + Rev 1.6 4/19/2003 11:14:40 AM JPMugaas + Made some tentitive wrapper functions for some things that should be called + from the Service Provider. Fixed WSARecvMsg. + + + Rev 1.5 4/19/2003 02:29:26 AM JPMugaas + Added TransmitPackets API function call. Note that this is only supported in + Winapi.Windows XP or later. + + + Rev 1.4 4/19/2003 12:22:58 AM BGooijen + fixed: ConnectEx DisconnectEx WSARecvMsg + + + Rev 1.3 4/18/2003 12:00:58 AM JPMugaas + added + ConnectEx + DisconnectEx + WSARecvMsg + + Changed header procedure type names to be consistant with the old + IdWinsock.pas in Indy 8.0 and with the rest of the unit. + + + Rev 1.2 3/22/2003 10:01:26 PM JPMugaas + WSACreateEvent couldn't load because of a space. + + + Rev 1.1 3/22/2003 09:46:54 PM JPMugaas + It turns out that we really do not need the TGUID defination in the header at + all. It's defined in D4, D5, D6, and D7. + + + Rev 1.0 11/13/2002 09:02:54 AM JPMugaas +} +//------------------------------------------------------------- +// +// Borland Delphi Runtime Library +// interface unit +// +// Portions created by Microsoft are +// Copyright (C) 1995-1999 Microsoft Corporation. +// All Rights Reserved. +// +// The original file is: Winsock2.h from CBuilder5 distribution. +// The original Pascal code is: winsock2.pas, released 03 Mar 2001. +// The initial developer of the Pascal code is Alex Konshin +// (alexk@mtgroup.ru). +//------------------------------------------------------------- + + +{ Winsock2.h -- definitions to be used with the WinSock 2 DLL and WinSock 2 applications. + This header file corresponds to version 2.2.x of the WinSock API specification. + This file includes parts which are Copyright (c) 1982-1986 Regents + of the University of California. All rights reserved. + The Berkeley Software License Agreement specifies the terms and + conditions for redistribution. } + +// Note that the original unit is copyrighted by the original author and I did obtain his +// permission to port and use this as part of Indy - J. Peter Mugaas + +// 2002-01-28 - Hadi Hariri. Fixes for C++ Builder. Thanks to Chuck Smith. +// 2001 - Oct -25 J. Peter Mugaas +// Made adjustments for Indy usage by +// 1) including removing Trace logging +// 2) renaming and consolidating some .INC files as appropriate +// 3) modifying the unit to follow Indy conventions +// 4) Adding TransmitFile support for the HTTP Server +// 5) Removing all static loading code that was IFDEF'ed. {Do not Localize} +// 2001 - Mar - 1 Alex Konshin +// Revision 3 +// converted by Alex Konshin, mailto:alexk@mtgroup.ru +// revision 3, March,1 2001 + + +unit Net.Winsock2; + +interface + +{$I Net.Winsock.inc} + +{ +Important!!! + +With the ARM architecture, you may get an EBusError exception sating that +data is misaligned. Sometimes, that architecture does not have the ability to +read misaligned data. On an i386 and x86_64 architecure, you can do this but it +is inefficient. For the ARM chip architecture, we have to make sure our records +are aligned on a 4 byte boundery. See: + +http://wiki.lazarus.freepascal.org/Windows_CE_Development_Notes + +This is not necessary and can cause problems +when using the standard Win32 API (win32 and win64) where records are packed +instead of aligned. + +To deal with this, I use the FPC predefined FPC_REQUIRES_PROPER_ALIGNMENT. + +} + +{$RANGECHECKS OFF} +{$IFDEF FPC} + {$IFDEF WIN32} + {$ALIGN OFF} + {$ELSE} + //It turns out that Win64 and WinCE require record alignment + {$PACKRECORDS C} + {$ENDIF} +{$ELSE} + {$IFDEF WIN64} + {$ALIGN ON} + {$MINENUMSIZE 4} + {$ELSE} + {$MINENUMSIZE 4} + {$IFDEF REQUIRES_PROPER_ALIGNMENT} + {$ALIGN ON} + {$ELSE} + {$ALIGN OFF} + {$WRITEABLECONST OFF} + {$ENDIF} + {$ENDIF} +{$ENDIF} + +uses + System.SysUtils, + System.AnsiStrings, + Winapi.Windows; + +type + EIdWinsockStubError = class(Exception) + protected + FWin32Error : DWORD; + FWin32ErrorMessage : String; + FTitle : String; + public + constructor Build(AWin32Error: DWORD; const ATitle: String; AArgs: array of const); + property Win32Error : DWORD read FWin32Error; + property Win32ErrorMessage : String read FWin32ErrorMessage; + property Title : String read FTitle; + end; + +const + {$IFDEF WINCE} + WINSOCK2_DLL = 'ws2.dll'; {Do not Localize} + {$ELSE} + WINSOCK2_DLL = 'WS2_32.DLL'; {Do not Localize} + MSWSOCK_DLL = 'MSWSOCK.DLL'; {Do not Localize} + {$ENDIF} + {$EXTERNALSYM WINSOCK_VERSION} + WINSOCK_VERSION = $0202; + +{$DEFINE WS2_DLL_FUNC_VARS} +{$DEFINE INCL_WINSOCK_API_PROTOTYPES} +{$DEFINE INCL_WINSOCK_API_TYPEDEFS} + +type + {$EXTERNALSYM u_char} + u_char = Byte; + {$EXTERNALSYM u_short} + u_short = Word; + {$EXTERNALSYM u_int} + u_int = DWord; //Integer; + {$EXTERNALSYM u_long} + u_long = DWORD; +// The new type to be used in all instances which refer to sockets. + {$EXTERNALSYM TSocket} + TSocket = UINT_PTR; + {$EXTERNALSYM WSAEVENT} + WSAEVENT = THandle; + {$NODEFINE PWSAEVENT} + PWSAEVENT = ^WSAEVENT; + {$EXTERNALSYM LPWSAEVENT} + LPWSAEVENT = PWSAEVENT; + + {$IFNDEF HAS_ULONG_PTR} + {$EXTERNALSYM ULONG_PTR} + ULONG_PTR = UINT_PTR; + {$ENDIF} + +const + {$EXTERNALSYM FD_SETSIZE} + FD_SETSIZE = 64; + +// the following emits are a workaround to the name conflicts +// with the winsock2 header files +(*$HPPEMIT '#include '*) +(*$HPPEMIT '#include '*) +(*$HPPEMIT '#include '*) +(*$HPPEMIT '// workaround for a bug in wsnwlink.h where a couple of commented lines are not terminated property'*) +(*$HPPEMIT '#pragma option push -C-'*) +(*$HPPEMIT '#include '*) +(*$HPPEMIT '#pragma option pop'*) +(*$HPPEMIT '#include '*) +(*$HPPEMIT '#include '*) +(*$HPPEMIT '#include '*) +(*$HPPEMIT ''*) +(*$HPPEMIT 'namespace Idwinsock2'*) +(*$HPPEMIT '{'*) +(*$HPPEMIT ' typedef fd_set *PFDSet;'*) // due to name conflict with procedure FD_SET +(*$HPPEMIT ' typedef fd_set TFDSet;'*) // due to name conflict with procedure FD_SET +(*$HPPEMIT '}'*) +(*$HPPEMIT ''*) + +type + {$NODEFINE PFDSet} + PFDSet = ^TFDSet; + {$NODEFINE TFDSet} + TFDSet = record + fd_count: u_int; + fd_array: array[0..FD_SETSIZE-1] of TSocket; + end; + + {$EXTERNALSYM timeval} + timeval = record + tv_sec: Longint; + tv_usec: Longint; + end; + {$NODEFINE TTimeVal} + TTimeVal = timeval; + {$NODEFINE PTimeVal} + PTimeVal = ^TTimeVal; + +const + {$EXTERNALSYM IOCPARM_MASK} + IOCPARM_MASK = $7F; + {$EXTERNALSYM IOC_VOID} + IOC_VOID = $20000000; + {$EXTERNALSYM IOC_OUT} + IOC_OUT = $40000000; + {$EXTERNALSYM IOC_IN} + IOC_IN = $80000000; + {$EXTERNALSYM IOC_INOUT} + IOC_INOUT = (IOC_IN or IOC_OUT); + +// get # bytes to read + {$EXTERNALSYM FIONREAD} + FIONREAD = IOC_OUT or ((SizeOf(u_long) and IOCPARM_MASK) shl 16) or (Ord('f') shl 8) or 127; {Do not Localize} +// set/clear non-blocking i/o + {$EXTERNALSYM FIONBIO} + FIONBIO = IOC_IN or ((SizeOf(u_long) and IOCPARM_MASK) shl 16) or (Ord('f') shl 8) or 126; {Do not Localize} +// set/clear async i/o + {$EXTERNALSYM FIOASYNC} + FIOASYNC = IOC_IN or ((SizeOf(u_long) and IOCPARM_MASK) shl 16) or (Ord('f') shl 8) or 125; {Do not Localize} + +// Socket I/O Controls + +// set high watermark + {$EXTERNALSYM SIOCSHIWAT} + SIOCSHIWAT = IOC_IN or ((SizeOf(u_long) and IOCPARM_MASK) shl 16) or (Ord('s') shl 8) or 0; {Do not Localize} +// get high watermark + {$EXTERNALSYM SIOCGHIWAT} + SIOCGHIWAT = IOC_OUT or ((SizeOf(u_long) and IOCPARM_MASK) shl 16) or (Ord('s') shl 8) or 1; {Do not Localize} +// set low watermark + {$EXTERNALSYM SIOCSLOWAT} + SIOCSLOWAT = IOC_IN or ((SizeOf(u_long) and IOCPARM_MASK) shl 16) or (Ord('s') shl 8) or 2; {Do not Localize} +// get low watermark + {$EXTERNALSYM SIOCGLOWAT} + SIOCGLOWAT = IOC_OUT or ((SizeOf(u_long) and IOCPARM_MASK) shl 16) or (Ord('s') shl 8) or 3; {Do not Localize} +// at oob mark? + {$EXTERNALSYM SIOCATMARK} + SIOCATMARK = IOC_OUT or ((SizeOf(u_long) and IOCPARM_MASK) shl 16) or (Ord('s') shl 8) or 7; {Do not Localize} + + +// Structures returned by network data base library, taken from the +// BSD file netdb.h. All addresses are supplied in host order, and +// returned in network order (suitable for use in system calls). +type + {$EXTERNALSYM hostent} + hostent = record + h_name: PAnsiChar; // official name of host + h_aliases: ^PAnsiChar; // alias list + h_addrtype: Smallint; // host address type + h_length: Smallint; // length of address + case Byte of + 0: (h_address_list: ^PAnsiChar); + 1: (h_addr: PAnsiChar); // address, for backward compat + end; + {$NODEFINE THostEnt} + THostEnt = hostent; + {$NODEFINE PHostEnt} + PHostEnt = ^THostEnt; + +// It is assumed here that a network number +// fits in 32 bits. + {$EXTERNALSYM netent} + netent = record + n_name: PAnsiChar; // official name of net + n_aliases: ^PAnsiChar; // alias list + n_addrtype: Smallint; // net address type + n_net: u_long; // network # + end; + {$NODEFINE TNetEnt} + TNetEnt = netent; + {$NODEFINE PNetEnt} + PNetEnt = ^TNetEnt; + + {$EXTERNALSYM servent} + servent = record + s_name: PAnsiChar; // official service name + s_aliases: ^PAnsiChar; // alias list + {$IFDEF _WIN64} + s_proto: PAnsiChar; // protocol to use + s_port: Smallint; // port # + {$ELSE} + s_port: Smallint; // port # + s_proto: PAnsiChar; // protocol to use + {$ENDIF} + end; + {$NODEFINE TServEnt} + TServEnt = servent; + {$NODEFINE PServEnt} + PServEnt = ^TServEnt; + + {$EXTERNALSYM protoent} + protoent = record + p_name: PAnsiChar; // official protocol name + p_aliases: ^PAnsiChar; // alias list + p_proto: Smallint; // protocol # + end; + {$NODEFINE TProtoEnt} + TProtoEnt = protoent; + {$NODEFINE PProtoEnt} + PProtoEnt = ^TProtoEnt; + +// Constants and structures defined by the internet system, +// Per RFC 790, September 1981, taken from the BSD file netinet/in.h. +const + +// Protocols + {$EXTERNALSYM IPPROTO_IP} + IPPROTO_IP = 0; // dummy for IP + {$EXTERNALSYM IPPROTO_ICMP} + IPPROTO_ICMP = 1; // control message protocol + {$EXTERNALSYM IPPROTO_IGMP} + IPPROTO_IGMP = 2; // group management protocol + {$EXTERNALSYM IPPROTO_GGP} + IPPROTO_GGP = 3; // gateway^2 (deprecated) + {$EXTERNALSYM IPPROTO_TCP} + IPPROTO_TCP = 6; // TCP + {$EXTERNALSYM IPPROTO_PUP} + IPPROTO_PUP = 12; // pup + {$EXTERNALSYM IPPROTO_UDP} + IPPROTO_UDP = 17; // UDP - user datagram protocol + {$EXTERNALSYM IPPROTO_IDP} + IPPROTO_IDP = 22; // xns idp + {$EXTERNALSYM IPPROTO_ND} + IPPROTO_ND = 77; // UNOFFICIAL net disk proto + + {$EXTERNALSYM IPPROTO_IPV6} + IPPROTO_IPV6 = 41; // IPv6 + {$EXTERNALSYM IPPROTO_ICLFXBM} + IPPROTO_ICLFXBM = 78; + + {$EXTERNALSYM IPPROTO_ICMPV6} + IPPROTO_ICMPV6 = 58; // control message protocol + + {$EXTERNALSYM IPPROTO_RAW} + IPPROTO_RAW = 255; // raw IP packet + {$EXTERNALSYM IPPROTO_MAX} + IPPROTO_MAX = 256; + +// Port/socket numbers: network standard functions + {$EXTERNALSYM IPPORT_ECHO} + IPPORT_ECHO = 7; + {$EXTERNALSYM IPPORT_DISCARD} + IPPORT_DISCARD = 9; + {$EXTERNALSYM IPPORT_SYSTAT} + IPPORT_SYSTAT = 11; + {$EXTERNALSYM IPPORT_DAYTIME} + IPPORT_DAYTIME = 13; + {$EXTERNALSYM IPPORT_NETSTAT} + IPPORT_NETSTAT = 15; + {$EXTERNALSYM IPPORT_FTP} + IPPORT_FTP = 21; + {$EXTERNALSYM IPPORT_TELNET} + IPPORT_TELNET = 23; + {$EXTERNALSYM IPPORT_SMTP} + IPPORT_SMTP = 25; + {$EXTERNALSYM IPPORT_TIMESERVER} + IPPORT_TIMESERVER = 37; + {$EXTERNALSYM IPPORT_NAMESERVER} + IPPORT_NAMESERVER = 42; + {$EXTERNALSYM IPPORT_WHOIS} + IPPORT_WHOIS = 43; + {$EXTERNALSYM IPPORT_MTP} + IPPORT_MTP = 57; + +// Port/socket numbers: host specific functions + {$EXTERNALSYM IPPORT_TFTP} + IPPORT_TFTP = 69; + {$EXTERNALSYM IPPORT_RJE} + IPPORT_RJE = 77; + {$EXTERNALSYM IPPORT_FINGER} + IPPORT_FINGER = 79; + {$EXTERNALSYM ipport_ttylink} + IPPORT_TTYLINK = 87; + {$EXTERNALSYM IPPORT_SUPDUP} + IPPORT_SUPDUP = 95; + +// UNIX TCP sockets + {$EXTERNALSYM IPPORT_EXECSERVER} + IPPORT_EXECSERVER = 512; + {$EXTERNALSYM IPPORT_LOGINSERVER} + IPPORT_LOGINSERVER = 513; + {$EXTERNALSYM IPPORT_CMDSERVER} + IPPORT_CMDSERVER = 514; + {$EXTERNALSYM IPPORT_EFSSERVER} + IPPORT_EFSSERVER = 520; + +// UNIX UDP sockets + {$EXTERNALSYM IPPORT_BIFFUDP} + IPPORT_BIFFUDP = 512; + {$EXTERNALSYM IPPORT_WHOSERVER} + IPPORT_WHOSERVER = 513; + {$EXTERNALSYM IPPORT_ROUTESERVER} + IPPORT_ROUTESERVER = 520; + +// Ports < IPPORT_RESERVED are reserved for privileged processes (e.g. root). + {$EXTERNALSYM IPPORT_RESERVED} + IPPORT_RESERVED = 1024; + + {$EXTERNALSYM IPPORT_REGISTERED_MIN} + IPPORT_REGISTERED_MIN = IPPORT_RESERVED; + + {$IFNDEF WINCE} + {$EXTERNALSYM IPPORT_REGISTERED_MAX} + IPPORT_REGISTERED_MAX = $bfff; + {$EXTERNALSYM IPPORT_DYNAMIC_MIN} + IPPORT_DYNAMIC_MIN = $c000; + {$EXTERNALSYM IPPORT_DYNAMIC_MAX} + IPPORT_DYNAMIC_MAX = $ffff; + {$ENDIF} + +// Link numbers + {$EXTERNALSYM IMPLINK_IP} + IMPLINK_IP = 155; + {$EXTERNALSYM IMPLINK_LOWEXPER} + IMPLINK_LOWEXPER = 156; + {$EXTERNALSYM IMPLINK_HIGHEXPER} + IMPLINK_HIGHEXPER = 158; + + {$EXTERNALSYM TF_DISCONNECT} + TF_DISCONNECT = $01; + {$EXTERNALSYM TF_REUSE_SOCKET} + TF_REUSE_SOCKET = $02; + {$EXTERNALSYM TF_WRITE_BEHIND} + TF_WRITE_BEHIND = $04; + {$EXTERNALSYM TF_USE_DEFAULT_WORKER} + TF_USE_DEFAULT_WORKER = $00; + {$EXTERNALSYM TF_USE_SYSTEM_THREAD} + TF_USE_SYSTEM_THREAD = $10; + {$EXTERNALSYM TF_USE_KERNEL_APC} + TF_USE_KERNEL_APC = $20; + +// This is used instead of -1, since the TSocket type is unsigned. + {$EXTERNALSYM INVALID_SOCKET} + INVALID_SOCKET = TSocket(not(0)); + {$EXTERNALSYM SOCKET_ERROR} + SOCKET_ERROR = -1; + +// The following may be used in place of the address family, socket type, or +// protocol in a call to WSASocket to indicate that the corresponding value +// should be taken from the supplied WSAPROTOCOL_INFO structure instead of the +// parameter itself. + {$EXTERNALSYM FROM_PROTOCOL_INFO} + FROM_PROTOCOL_INFO = -1; + + +// Types + {$EXTERNALSYM SOCK_STREAM} + SOCK_STREAM = 1; { stream socket } + {$EXTERNALSYM SOCK_DGRAM} + SOCK_DGRAM = 2; { datagram socket } + {$EXTERNALSYM SOCK_RAW} + SOCK_RAW = 3; { raw-protocol interface } + {$EXTERNALSYM SOCK_RDM} + SOCK_RDM = 4; { reliably-delivered message } + {$EXTERNALSYM SOCK_SEQPACKET} + SOCK_SEQPACKET = 5; { sequenced packet stream } + +// option flags per-socket. + {$EXTERNALSYM SO_DEBUG} + SO_DEBUG = $0001; // turn on debugging info recording + {$EXTERNALSYM SO_ACCEPTCONN} + SO_ACCEPTCONN = $0002; // socket has had listen() + {$EXTERNALSYM SO_REUSEADDR} + SO_REUSEADDR = $0004; // allow local address reuse + {$EXTERNALSYM SO_KEEPALIVE} + SO_KEEPALIVE = $0008; // keep connections alive + {$EXTERNALSYM SO_DONTROUTE} + SO_DONTROUTE = $0010; // just use interface addresses + {$EXTERNALSYM SO_BROADCAST} + SO_BROADCAST = $0020; // permit sending of broadcast msgs + {$EXTERNALSYM SO_USELOOPBACK} + SO_USELOOPBACK = $0040; // bypass hardware when possible + {$EXTERNALSYM SO_LINGER} + SO_LINGER = $0080; // linger on close if data present + {$EXTERNALSYM SO_OOBINLINE} + SO_OOBINLINE = $0100; // leave received OOB data in line + + {$EXTERNALSYM SO_DONTLINGER} + SO_DONTLINGER = not SO_LINGER; + {$EXTERNALSYM SO_EXCLUSIVEADDRUSE} + SO_EXCLUSIVEADDRUSE = not SO_REUSEADDR; // disallow local address reuse + +// additional options. + + {$EXTERNALSYM SO_SNDBUF} + SO_SNDBUF = $1001; // send buffer size + {$EXTERNALSYM SO_RCVBUF} + SO_RCVBUF = $1002; // receive buffer size + {$EXTERNALSYM SO_SNDLOWAT} + SO_SNDLOWAT = $1003; // send low-water mark + {$EXTERNALSYM SO_RCVLOWAT} + SO_RCVLOWAT = $1004; // receive low-water mark + {$EXTERNALSYM SO_SNDTIMEO} + SO_SNDTIMEO = $1005; // send timeout + {$EXTERNALSYM SO_RCVTIMEO} + SO_RCVTIMEO = $1006; // receive timeout + {$EXTERNALSYM SO_ERROR} + SO_ERROR = $1007; // get error status and clear + {$EXTERNALSYM SO_TYPE} + SO_TYPE = $1008; // get socket type + +// options for connect and disconnect data and options. +// used only by non-tcp/ip transports such as DECNet, OSI TP4, etc. + {$EXTERNALSYM SO_CONNDATA} + SO_CONNDATA = $7000; + {$EXTERNALSYM SO_CONNOPT} + SO_CONNOPT = $7001; + {$EXTERNALSYM SO_DISCDATA} + SO_DISCDATA = $7002; + {$EXTERNALSYM SO_DISCOPT} + SO_DISCOPT = $7003; + {$EXTERNALSYM SO_CONNDATALEN} + SO_CONNDATALEN = $7004; + {$EXTERNALSYM SO_CONNOPTLEN} + SO_CONNOPTLEN = $7005; + {$EXTERNALSYM SO_DISCDATALEN} + SO_DISCDATALEN = $7006; + {$EXTERNALSYM SO_DISCOPTLEN} + SO_DISCOPTLEN = $7007; + +// option for opening sockets for synchronous access. + {$EXTERNALSYM SO_OPENTYPE} + SO_OPENTYPE = $7008; + {$EXTERNALSYM SO_SYNCHRONOUS_ALERT} + SO_SYNCHRONOUS_ALERT = $10; + {$EXTERNALSYM SO_SYNCHRONOUS_NONALERT} + SO_SYNCHRONOUS_NONALERT = $20; + +// other nt-specific options. + {$EXTERNALSYM SO_MAXDG} + SO_MAXDG = $7009; + {$EXTERNALSYM SO_MAXPATHDG} + SO_MAXPATHDG = $700A; + {$EXTERNALSYM SO_UPDATE_ACCEPT_CONTEXT} + SO_UPDATE_ACCEPT_CONTEXT = $700B; + {$EXTERNALSYM SO_CONNECT_TIME} + SO_CONNECT_TIME = $700C; + +// tcp options. + {$EXTERNALSYM TCP_NODELAY} + TCP_NODELAY = $0001; + {$EXTERNALSYM TCP_BSDURGENT} + TCP_BSDURGENT = $7000; + +// winsock 2 extension -- new options + {$EXTERNALSYM SO_GROUP_ID} + SO_GROUP_ID = $2001; // ID of a socket group + {$EXTERNALSYM SO_GROUP_PRIORITY} + SO_GROUP_PRIORITY = $2002; // the relative priority within a group + {$EXTERNALSYM SO_MAX_MSG_SIZE} + SO_MAX_MSG_SIZE = $2003; // maximum message size + {$EXTERNALSYM SO_PROTOCOL_INFOA} + SO_PROTOCOL_INFOA = $2004; // WSAPROTOCOL_INFOA structure + {$EXTERNALSYM SO_PROTOCOL_INFOW} + SO_PROTOCOL_INFOW = $2005; // WSAPROTOCOL_INFOW structure + {$EXTERNALSYM SO_PROTOCOL_INFO} + {$IFDEF UNICODE} + SO_PROTOCOL_INFO = SO_PROTOCOL_INFOW; + {$ELSE} + SO_PROTOCOL_INFO = SO_PROTOCOL_INFOA; + {$ENDIF} + {$EXTERNALSYM PVD_CONFIG} + PVD_CONFIG = $3001; // configuration info for service provider + {$EXTERNALSYM SO_CONDITIONAL_ACCEPT} + SO_CONDITIONAL_ACCEPT = $3002; // enable true conditional accept: + // connection is not ack-ed to the + // other side until conditional + // function returns CF_ACCEPT + {$IFNDEF WINCE} + {$EXTERNALSYM SO_RANDOMIZE_PORT} + SO_RANDOMIZE_PORT = $3005; // randomize assignment of wildcard ports + {$EXTERNALSYM SO_PORT_SCALABILITY} + SO_PORT_SCALABILITY = $3006; // enable port scalability + {$ENDIF} +// Address families. + {$EXTERNALSYM AF_UNSPEC} + AF_UNSPEC = 0; // unspecified + {$EXTERNALSYM AF_UNIX} + AF_UNIX = 1; // local to host (pipes, portals) + {$EXTERNALSYM AF_INET} + AF_INET = 2; // internetwork: UDP, TCP, etc. + {$EXTERNALSYM AF_IMPLINK} + AF_IMPLINK = 3; // arpanet imp addresses + {$EXTERNALSYM AF_PUP} + AF_PUP = 4; // pup protocols: e.g. BSP + {$EXTERNALSYM AF_CHAOS} + AF_CHAOS = 5; // mit CHAOS protocols + {$EXTERNALSYM AF_IPX} + AF_IPX = 6; // ipx and SPX + {$EXTERNALSYM AF_NS} + AF_NS = AF_IPX; // xerOX NS protocols + {$EXTERNALSYM AF_ISO} + AF_ISO = 7; // iso protocols + {$EXTERNALSYM AF_OSI} + AF_OSI = AF_ISO; // osi is ISO + {$EXTERNALSYM AF_ECMA} + AF_ECMA = 8; // european computer manufacturers + {$EXTERNALSYM AF_DATAKIT} + AF_DATAKIT = 9; // datakit protocols + {$EXTERNALSYM AF_CCITT} + AF_CCITT = 10; // cciTT protocols, X.25 etc + {$EXTERNALSYM AF_SNA} + AF_SNA = 11; // ibm SNA + {$EXTERNALSYM AF_DECNET} + AF_DECNET = 12; // decnet + {$EXTERNALSYM AF_DLI} + AF_DLI = 13; // direct data link interface + {$EXTERNALSYM AF_LAT} + AF_LAT = 14; // lat + {$EXTERNALSYM AF_HYLINK} + AF_HYLINK = 15; // nsc Hyperchannel + {$EXTERNALSYM AF_APPLETALK} + AF_APPLETALK = 16; // appleTalk + {$EXTERNALSYM AF_NETBIOS} + AF_NETBIOS = 17; // netBios-style addresses + {$EXTERNALSYM AF_VOICEVIEW} + AF_VOICEVIEW = 18; // voiceView + {$EXTERNALSYM AF_FIREFOX} + AF_FIREFOX = 19; // fireFox + {$EXTERNALSYM AF_UNKNOWN1} + AF_UNKNOWN1 = 20; // somebody is using this! + {$EXTERNALSYM AF_BAN} + AF_BAN = 21; // banyan + {$IFDEF WINCE} + {$EXTERNALSYM AF_IRDA} + AF_IRDA = 22; //* IrDA */ + {$ELSE} + {$EXTERNALSYM AF_ATM} + AF_ATM = 22; // native ATM Services + {$ENDIF} + {$EXTERNALSYM AF_INET6} + AF_INET6 = 23; // internetwork Version 6 + {$EXTERNALSYM AF_CLUSTER} + AF_CLUSTER = 24; // microsoft Wolfpack + {$EXTERNALSYM AF_12844} + AF_12844 = 25; // ieeE 1284.4 WG AF + {$IFDEF WINCE} + {$EXTERNALSYM AF_ATM} + AF_ATM = 26; //* Native ATM Services */ + {$ELSE} + {$EXTERNALSYM AF_IRDA} + AF_IRDA = 26; // irdA + {$ENDIF} + {$EXTERNALSYM AF_NETDES} + AF_NETDES = 28; // network Designers OSI & gateway enabled protocols + {$EXTERNALSYM AF_TCNPROCESS} + AF_TCNPROCESS = 29; + {$EXTERNALSYM AF_TCNMESSAGE} + AF_TCNMESSAGE = 30; + {$EXTERNALSYM AF_ICLFXBM} + AF_ICLFXBM = 31; + + {$EXTERNALSYM AF_MAX} + AF_MAX = 32; + +// protocol families, same as address families for now. + + {$EXTERNALSYM PF_UNSPEC} + PF_UNSPEC = AF_UNSPEC; + {$EXTERNALSYM PF_UNIX} + PF_UNIX = AF_UNIX; + {$EXTERNALSYM PF_INET} + PF_INET = AF_INET; + {$EXTERNALSYM PF_IMPLINK} + PF_IMPLINK = AF_IMPLINK; + {$EXTERNALSYM PF_PUP} + PF_PUP = AF_PUP; + {$EXTERNALSYM PF_CHAOS} + PF_CHAOS = AF_CHAOS; + {$EXTERNALSYM PF_NS} + PF_NS = AF_NS; + {$EXTERNALSYM PF_IPX} + PF_IPX = AF_IPX; + {$EXTERNALSYM PF_ISO} + PF_ISO = AF_ISO; + {$EXTERNALSYM PF_OSI} + PF_OSI = AF_OSI; + {$EXTERNALSYM PF_ECMA} + PF_ECMA = AF_ECMA; + {$EXTERNALSYM PF_DATAKIT} + PF_DATAKIT = AF_DATAKIT; + {$EXTERNALSYM PF_CCITT} + PF_CCITT = AF_CCITT; + {$EXTERNALSYM PF_SNA} + PF_SNA = AF_SNA; + {$EXTERNALSYM PF_DECNET} + PF_DECNET = AF_DECNET; + {$EXTERNALSYM PF_DLI} + PF_DLI = AF_DLI; + {$EXTERNALSYM PF_LAT} + PF_LAT = AF_LAT; + {$EXTERNALSYM PF_HYLINK} + PF_HYLINK = AF_HYLINK; + {$EXTERNALSYM PF_APPLETALK} + PF_APPLETALK = AF_APPLETALK; + {$EXTERNALSYM PF_VOICEVIEW} + PF_VOICEVIEW = AF_VOICEVIEW; + {$EXTERNALSYM PF_FIREFOX} + PF_FIREFOX = AF_FIREFOX; + {$EXTERNALSYM PF_UNKNOWN1} + PF_UNKNOWN1 = AF_UNKNOWN1; + {$EXTERNALSYM pf_ban} + PF_BAN = AF_BAN; + {$EXTERNALSYM PF_ATM} + PF_ATM = AF_ATM; + {$EXTERNALSYM PF_INET6} + PF_INET6 = AF_INET6; + + {$EXTERNALSYM PF_MAX} + PF_MAX = AF_MAX; + + {$EXTERNALSYM _SS_MAXSIZE} + _SS_MAXSIZE = 128; + {$EXTERNALSYM _SS_ALIGNSIZE} + _SS_ALIGNSIZE = SizeOf(Int64); + {$EXTERNALSYM _SS_PAD1SIZE} + _SS_PAD1SIZE = _SS_ALIGNSIZE - SizeOf(short); + {$EXTERNALSYM _SS_PAD2SIZE} + _SS_PAD2SIZE = _SS_MAXSIZE - (SizeOf(short) + _SS_PAD1SIZE + _SS_ALIGNSIZE); + +type + {$NODEFINE SunB} + SunB = record + s_b1, s_b2, s_b3, s_b4: u_char; + end; + + {$NODEFINE SunW} + SunW = record + s_w1, s_w2: u_short; + end; + + {$EXTERNALSYM in_addr} + in_addr = record + case integer of + 0: (S_un_b: SunB); + 1: (S_un_w: SunW); + 2: (S_addr: u_long); + end; + {$NODEFINE TInAddr} + TInAddr = in_addr; + {$NODEFINE PInAddr} + PInAddr = ^TInAddr; + + // Structure used by kernel to store most addresses. + + {$EXTERNALSYM sockaddr_in} + sockaddr_in = record + case Integer of + 0: (sin_family : u_short; + sin_port : u_short; + sin_addr : TInAddr; + sin_zero : array[0..7] of AnsiChar); + 1: (sa_family : u_short; + sa_data : array[0..13] of AnsiChar) + end; + {$NODEFINE TSockAddrIn} + TSockAddrIn = sockaddr_in; + {$NODEFINE PSockAddrIn} + PSockAddrIn = ^TSockAddrIn; + + {$NODEFINE TSockAddr} + TSockAddr = TSockAddrIn; + {$EXTERNALSYM SOCKADDR} + SOCKADDR = TSockAddr; + {$EXTERNALSYM PSOCKADDR} + PSOCKADDR = ^TSockAddr; + {$EXTERNALSYM LPSOCKADDR} + LPSOCKADDR = PSOCKADDR; + + {$EXTERNALSYM SOCKADDR_STORAGE} + SOCKADDR_STORAGE = record + ss_family: short; // Address family. + __ss_pad1: array[0.._SS_PAD1SIZE-1] of AnsiChar; // 6 byte pad, this is to make + // implementation specific pad up to + // alignment field that follows explicit + // in the data structure. + __ss_align: Int64; // Field to force desired structure. + __ss_pad2: array[0.._SS_PAD2SIZE-1] of AnsiChar; // 112 byte pad to achieve desired size; + // _SS_MAXSIZE value minus size of + // ss_family, __ss_pad1, and + // __ss_align fields is 112. + end; + {$NODEFINE TSockAddrStorage} + TSockAddrStorage = SOCKADDR_STORAGE; + {$NODEFINE PSockAddrStorage} + PSockAddrStorage = ^TSockAddrStorage; + {$EXTERNALSYM PSOCKADDR_STORAGE} + PSOCKADDR_STORAGE = PSockAddrStorage; + {$EXTERNALSYM LPSOCKADDR_STORAGE} + LPSOCKADDR_STORAGE = PSOCKADDR_STORAGE; + + // Structure used by kernel to pass protocol information in raw sockets. + {$EXTERNALSYM sockproto} + sockproto = record + sp_family : u_short; + sp_protocol : u_short; + end; + {$NODEFINE TSockProto} + TSockProto = sockproto; + {$NODEFINE PSockProto} + PSockProto = ^TSockProto; + +// Structure used for manipulating linger option. + {$EXTERNALSYM linger} + linger = record + l_onoff: u_short; + l_linger: u_short; + end; + {$NODEFINE TLinger} + TLinger = linger; + {$EXTERNALSYM PLINGER} + PLINGER = ^TLinger; + {$EXTERNALSYM LPLINGER} + LPLINGER = PLINGER; + +const + {$EXTERNALSYM INADDR_ANY} + INADDR_ANY = $00000000; + {$EXTERNALSYM INADDR_LOOPBACK} + INADDR_LOOPBACK = $7F000001; + {$EXTERNALSYM INADDR_BROADCAST} + INADDR_BROADCAST = $FFFFFFFF; + {$EXTERNALSYM INADDR_NONE} + INADDR_NONE = $FFFFFFFF; + + {$EXTERNALSYM ADDR_ANY} + ADDR_ANY = INADDR_ANY; + + {$EXTERNALSYM SOL_SOCKET} + SOL_SOCKET = $FFFF; // options for socket level + + {$EXTERNALSYM MSG_OOB} + MSG_OOB = $1; // process out-of-band data + {$EXTERNALSYM MSG_PEEK} + MSG_PEEK = $2; // peek at incoming message + {$EXTERNALSYM MSG_DONTROUTE} + MSG_DONTROUTE = $4; // send without using routing tables + + {$EXTERNALSYM MSG_PARTIAL} + MSG_PARTIAL = $8000; // partial send or recv for message xport + +// WinSock 2 extension -- new flags for WSASend(), WSASendTo(), WSARecv() and WSARecvFrom() + {$EXTERNALSYM MSG_INTERRUPT} + MSG_INTERRUPT = $10; // send/recv in the interrupt context + {$EXTERNALSYM MSG_MAXIOVLEN} + MSG_MAXIOVLEN = 16; + +// Define constant based on rfc883, used by gethostbyxxxx() calls. + + {$EXTERNALSYM MAXGETHOSTSTRUCT} + MAXGETHOSTSTRUCT = 1024; + +// Maximum queue length specifiable by listen. + {$EXTERNALSYM SOMAXCONN} + SOMAXCONN = $7FFFFFFF; + +// WinSock 2 extension -- bit values and indices for FD_XXX network events + {$EXTERNALSYM FD_READ_BIT} + FD_READ_BIT = 0; + {$EXTERNALSYM FD_WRITE_BIT} + FD_WRITE_BIT = 1; + {$EXTERNALSYM FD_OOB_BIT} + FD_OOB_BIT = 2; + {$EXTERNALSYM FD_ACCEPT_BIT} + FD_ACCEPT_BIT = 3; + {$EXTERNALSYM FD_CONNECT_BIT} + FD_CONNECT_BIT = 4; + {$EXTERNALSYM FD_CLOSE_BIT} + FD_CLOSE_BIT = 5; + {$EXTERNALSYM fd_qos_bit} + FD_QOS_BIT = 6; + {$EXTERNALSYM FD_GROUP_QOS_BIT} + FD_GROUP_QOS_BIT = 7; + {$EXTERNALSYM FD_ROUTING_INTERFACE_CHANGE_BIT} + FD_ROUTING_INTERFACE_CHANGE_BIT = 8; + {$EXTERNALSYM FD_ADDRESS_LIST_CHANGE_BIT} + FD_ADDRESS_LIST_CHANGE_BIT = 9; + + {$EXTERNALSYM FD_MAX_EVENTS} + FD_MAX_EVENTS = 10; + + {$EXTERNALSYM FD_READ} + FD_READ = (1 shl FD_READ_BIT); + {$EXTERNALSYM FD_WRITE} + FD_WRITE = (1 shl FD_WRITE_BIT); + {$EXTERNALSYM FD_OOB} + FD_OOB = (1 shl FD_OOB_BIT); + {$EXTERNALSYM FD_ACCEPT} + FD_ACCEPT = (1 shl FD_ACCEPT_BIT); + {$EXTERNALSYM FD_CONNECT} + FD_CONNECT = (1 shl FD_CONNECT_BIT); + {$EXTERNALSYM FD_CLOSE} + FD_CLOSE = (1 shl FD_CLOSE_BIT); + {$EXTERNALSYM FD_QOS} + FD_QOS = (1 shl FD_QOS_BIT); + {$EXTERNALSYM FD_GROUP_QOS} + FD_GROUP_QOS = (1 shl FD_GROUP_QOS_BIT); + {$EXTERNALSYM FD_ROUTING_INTERFACE_CHANGE} + FD_ROUTING_INTERFACE_CHANGE = (1 shl FD_ROUTING_INTERFACE_CHANGE_BIT); + {$EXTERNALSYM FD_ADDRESS_LIST_CHANGE} + FD_ADDRESS_LIST_CHANGE = (1 shl FD_ADDRESS_LIST_CHANGE_BIT); + + {$EXTERNALSYM FD_ALL_EVENTS} + FD_ALL_EVENTS = (1 shl FD_MAX_EVENTS) - 1; + +// All Winapi.Windows Sockets error constants are biased by WSABASEERR from the "normal" + + {$EXTERNALSYM WSABASEERR} + WSABASEERR = 10000; + +// Winapi.Windows Sockets definitions of regular Microsoft C error constants + + {$EXTERNALSYM WSAEINTR} + WSAEINTR = WSABASEERR+ 4; + {$EXTERNALSYM WSAEBADF} + WSAEBADF = WSABASEERR+ 9; + {$EXTERNALSYM WSAEACCES} + WSAEACCES = WSABASEERR+ 13; + {$EXTERNALSYM WSAEFAULT} + WSAEFAULT = WSABASEERR+ 14; + {$EXTERNALSYM WSAEINVAL} + WSAEINVAL = WSABASEERR+ 22; + {$EXTERNALSYM WSAEMFILE} + WSAEMFILE = WSABASEERR+ 24; + +// Winapi.Windows Sockets definitions of regular Berkeley error constants + + {$EXTERNALSYM WSAEWOULDBLOCK} + WSAEWOULDBLOCK = WSABASEERR+ 35; + {$EXTERNALSYM WSAEINPROGRESS} + WSAEINPROGRESS = WSABASEERR+ 36; + {$EXTERNALSYM WSAEALREADY} + WSAEALREADY = WSABASEERR+ 37; + {$EXTERNALSYM WSAENOTSOCK} + WSAENOTSOCK = WSABASEERR+ 38; + {$EXTERNALSYM WSAEDESTADDRREQ} + WSAEDESTADDRREQ = WSABASEERR+ 39; + {$EXTERNALSYM WSAEMSGSIZE} + WSAEMSGSIZE = WSABASEERR+ 40; + {$EXTERNALSYM WSAEPROTOTYPE} + WSAEPROTOTYPE = WSABASEERR+ 41; + {$EXTERNALSYM WSAENOPROTOOPT} + WSAENOPROTOOPT = WSABASEERR+ 42; + {$EXTERNALSYM WSAEPROTONOSUPPORT} + WSAEPROTONOSUPPORT = WSABASEERR+ 43; + {$EXTERNALSYM WSAESOCKTNOSUPPORT} + WSAESOCKTNOSUPPORT = WSABASEERR+ 44; + {$EXTERNALSYM WSAEOPNOTSUPP} + WSAEOPNOTSUPP = WSABASEERR+ 45; + {$EXTERNALSYM WSAEPFNOSUPPORT} + WSAEPFNOSUPPORT = WSABASEERR+ 46; + {$EXTERNALSYM WSAEAFNOSUPPORT} + WSAEAFNOSUPPORT = WSABASEERR+ 47; + {$EXTERNALSYM WSAEADDRINUSE} + WSAEADDRINUSE = WSABASEERR+ 48; + {$EXTERNALSYM WSAEADDRNOTAVAIL} + WSAEADDRNOTAVAIL = WSABASEERR+ 49; + {$EXTERNALSYM WSAENETDOWN} + WSAENETDOWN = WSABASEERR+ 50; + {$EXTERNALSYM WSAENETUNREACH} + WSAENETUNREACH = WSABASEERR+ 51; + {$EXTERNALSYM WSAENETRESET} + WSAENETRESET = WSABASEERR+ 52; + {$EXTERNALSYM WSAECONNABORTED} + WSAECONNABORTED = WSABASEERR+ 53; + {$EXTERNALSYM WSAECONNRESET} + WSAECONNRESET = WSABASEERR+ 54; + {$EXTERNALSYM WSAENOBUFS} + WSAENOBUFS = WSABASEERR+ 55; + {$EXTERNALSYM WSAEISCONN} + WSAEISCONN = WSABASEERR+ 56; + {$EXTERNALSYM WSAENOTCONN} + WSAENOTCONN = WSABASEERR+ 57; + {$EXTERNALSYM WSAESHUTDOWN} + WSAESHUTDOWN = WSABASEERR+ 58; + {$EXTERNALSYM WSAETOOMANYREFS} + WSAETOOMANYREFS = WSABASEERR+ 59; + {$EXTERNALSYM WSAETIMEDOUT} + WSAETIMEDOUT = WSABASEERR+ 60; + {$EXTERNALSYM WSAECONNREFUSED} + WSAECONNREFUSED = WSABASEERR+ 61; + {$EXTERNALSYM WSAELOOP} + WSAELOOP = WSABASEERR+ 62; + {$EXTERNALSYM WSAENAMETOOLONG} + WSAENAMETOOLONG = WSABASEERR+ 63; + {$EXTERNALSYM WSAEHOSTDOWN} + WSAEHOSTDOWN = WSABASEERR+ 64; + {$EXTERNALSYM WSAEHOSTUNREACH} + WSAEHOSTUNREACH = WSABASEERR+ 65; + {$EXTERNALSYM wsaenotempty} + WSAENOTEMPTY = WSABASEERR+ 66; + {$EXTERNALSYM WSAEPROCLIM} + WSAEPROCLIM = WSABASEERR+ 67; + {$EXTERNALSYM WSAEUSERS} + WSAEUSERS = WSABASEERR+ 68; + {$EXTERNALSYM WSAEDQUOT} + WSAEDQUOT = WSABASEERR+ 69; + {$EXTERNALSYM WSAESTALE} + WSAESTALE = WSABASEERR+ 70; + {$EXTERNALSYM WSAEREMOTE} + WSAEREMOTE = WSABASEERR+ 71; + +// Extended Winapi.Windows Sockets error constant definitions + + {$EXTERNALSYM WSASYSNOTREADY} + WSASYSNOTREADY = WSABASEERR+ 91; + {$EXTERNALSYM WSAVERNOTSUPPORTED} + WSAVERNOTSUPPORTED = WSABASEERR+ 92; + {$EXTERNALSYM WSANOTINITIALISED} + WSANOTINITIALISED = WSABASEERR+ 93; + {$EXTERNALSYM WSAEDISCON} + WSAEDISCON = WSABASEERR+101; + {$EXTERNALSYM WSAENOMORE} + WSAENOMORE = WSABASEERR+102; + {$EXTERNALSYM WSAECANCELLED} + WSAECANCELLED = WSABASEERR+103; + {$EXTERNALSYM WSAEINVALIDPROCTABLE} + WSAEINVALIDPROCTABLE = WSABASEERR+104; + {$EXTERNALSYM WSAEINVALIDPROVIDER} + WSAEINVALIDPROVIDER = WSABASEERR+105; + {$EXTERNALSYM WSAEPROVIDERFAILEDINIT} + WSAEPROVIDERFAILEDINIT = WSABASEERR+106; + {$EXTERNALSYM WSASYSCALLFAILURE} + WSASYSCALLFAILURE = WSABASEERR+107; + {$EXTERNALSYM WSASERVICE_NOT_FOUND} + WSASERVICE_NOT_FOUND = WSABASEERR+108; + {$EXTERNALSYM WSATYPE_NOT_FOUND} + WSATYPE_NOT_FOUND = WSABASEERR+109; + {$EXTERNALSYM WSA_E_NO_MORE} + WSA_E_NO_MORE = WSABASEERR+110; + {$EXTERNALSYM WSA_E_CANCELLED} + WSA_E_CANCELLED = WSABASEERR+111; + {$EXTERNALSYM WSAEREFUSED} + WSAEREFUSED = WSABASEERR+112; + + {$IFDEF WINCE} + WSAEDUPLICATE_NAME = WSABASEERR+900; + {$ENDIF} + +{ Error return codes from gethostbyname() and gethostbyaddr() + (when using the resolver). Note that these errors are + retrieved via WSAGetLastError() and must therefore follow + the rules for avoiding clashes with error numbers from + specific implementations or language run-time systems. + For this reason the codes are based at WSABASEERR+1001. + Note also that [WSA]NO_ADDRESS is defined only for + compatibility purposes. } + +// Authoritative Answer: Host not found + {$EXTERNALSYM WSAHOST_NOT_FOUND} + WSAHOST_NOT_FOUND = WSABASEERR+1001; + {$EXTERNALSYM HOST_NOT_FOUND} + HOST_NOT_FOUND = WSAHOST_NOT_FOUND; + +// Non-Authoritative: Host not found, or SERVERFAIL + {$EXTERNALSYM WSATRY_AGAIN} + WSATRY_AGAIN = WSABASEERR+1002; + {$EXTERNALSYM TRY_AGAIN} + TRY_AGAIN = WSATRY_AGAIN; + +// Non recoverable errors, FORMERR, REFUSED, NOTIMP + {$EXTERNALSYM WSANO_RECOVERY} + WSANO_RECOVERY = WSABASEERR+1003; + {$EXTERNALSYM NO_RECOVERY} + NO_RECOVERY = WSANO_RECOVERY; + +// Valid name, no data record of requested type + {$EXTERNALSYM WSANO_DATA} + WSANO_DATA = WSABASEERR+1004; + {$EXTERNALSYM NO_DATA} + NO_DATA = WSANO_DATA; + +// no address, look for MX record + {$EXTERNALSYM WSANO_ADDRESS} + WSANO_ADDRESS = WSANO_DATA; + {$EXTERNALSYM NO_ADDRESS} + NO_ADDRESS = WSANO_ADDRESS; + +// Define QOS related error return codes + + {$EXTERNALSYM WSA_QOS_RECEIVERS} + WSA_QOS_RECEIVERS = WSABASEERR+1005; // at least one reserve has arrived + {$EXTERNALSYM WSA_QOS_SENDERS} + WSA_QOS_SENDERS = WSABASEERR+1006; // at least one path has arrived + {$EXTERNALSYM WSA_QOS_NO_SENDERS} + WSA_QOS_NO_SENDERS = WSABASEERR+1007; // there are no senders + {$EXTERNALSYM WSA_QOS_NO_RECEIVERS} + WSA_QOS_NO_RECEIVERS = WSABASEERR+1008; // there are no receivers + {$EXTERNALSYM WSA_QOS_REQUEST_CONFIRMED} + WSA_QOS_REQUEST_CONFIRMED = WSABASEERR+1009; // reserve has been confirmed + {$EXTERNALSYM WSA_QOS_ADMISSION_FAILURE} + WSA_QOS_ADMISSION_FAILURE = WSABASEERR+1010; // error due to lack of resources + {$EXTERNALSYM WSA_QOS_POLICY_FAILURE} + WSA_QOS_POLICY_FAILURE = WSABASEERR+1011; // rejected for administrative reasons - bad credentials + {$EXTERNALSYM WSA_QOS_BAD_STYLE} + WSA_QOS_BAD_STYLE = WSABASEERR+1012; // unknown or conflicting style + {$EXTERNALSYM WSA_QOS_BAD_OBJECT} + WSA_QOS_BAD_OBJECT = WSABASEERR+1013; // problem with some part of the filterspec or providerspecific buffer in general + {$EXTERNALSYM WSA_QOS_TRAFFIC_CTRL_ERROR} + WSA_QOS_TRAFFIC_CTRL_ERROR = WSABASEERR+1014; // problem with some part of the flowspec + {$EXTERNALSYM WSA_QOS_GENERIC_ERROR} + WSA_QOS_GENERIC_ERROR = WSABASEERR+1015; // general error + {$EXTERNALSYM WSA_QOS_ESERVICETYPE} + WSA_QOS_ESERVICETYPE = WSABASEERR+1016; // invalid service type in flowspec + {$EXTERNALSYM WSA_QOS_EFLOWSPEC} + WSA_QOS_EFLOWSPEC = WSABASEERR+1017; // invalid flowspec + {$EXTERNALSYM WSA_QOS_EPROVSPECBUF} + WSA_QOS_EPROVSPECBUF = WSABASEERR+1018; // invalid provider specific buffer + {$EXTERNALSYM WSA_QOS_EFILTERSTYLE} + WSA_QOS_EFILTERSTYLE = WSABASEERR+1019; // invalid filter style + {$EXTERNALSYM WSA_QOS_EFILTERTYPE} + WSA_QOS_EFILTERTYPE = WSABASEERR+1020; // invalid filter type + {$EXTERNALSYM WSA_QOS_EFILTERCOUNT} + WSA_QOS_EFILTERCOUNT = WSABASEERR+1021; // incorrect number of filters + {$EXTERNALSYM WSA_QOS_EOBJLENGTH} + WSA_QOS_EOBJLENGTH = WSABASEERR+1022; // invalid object length + {$EXTERNALSYM WSA_QOS_EFLOWCOUNT} + WSA_QOS_EFLOWCOUNT = WSABASEERR+1023; // incorrect number of flows + {$EXTERNALSYM WSA_QOS_EUNKOWNPSOBJ} + WSA_QOS_EUNKOWNPSOBJ = WSABASEERR+1024; // unknown object in provider specific buffer + {$EXTERNALSYM WSA_QOS_EPOLICYOBJ} + WSA_QOS_EPOLICYOBJ = WSABASEERR+1025; // invalid policy object in provider specific buffer + {$EXTERNALSYM WSA_QOS_EFLOWDESC} + WSA_QOS_EFLOWDESC = WSABASEERR+1026; // invalid flow descriptor in the list + {$EXTERNALSYM WSA_QOS_EPSFLOWSPEC} + WSA_QOS_EPSFLOWSPEC = WSABASEERR+1027; // inconsistent flow spec in provider specific buffer + {$EXTERNALSYM WSA_QOS_EPSFILTERSPEC} + WSA_QOS_EPSFILTERSPEC = WSABASEERR+1028; // invalid filter spec in provider specific buffer + {$EXTERNALSYM WSA_QOS_ESDMODEOBJ} + WSA_QOS_ESDMODEOBJ = WSABASEERR+1029; // invalid shape discard mode object in provider specific buffer + {$EXTERNALSYM WSA_QOS_ESHAPERATEOBJ} + WSA_QOS_ESHAPERATEOBJ = WSABASEERR+1030; // invalid shaping rate object in provider specific buffer + {$EXTERNALSYM WSA_QOS_RESERVED_PETYPE} + WSA_QOS_RESERVED_PETYPE = WSABASEERR+1031; // reserved policy element in provider specific buffer + + +{ WinSock 2 extension -- new error codes and type definition } + {$EXTERNALSYM WSA_IO_PENDING} + WSA_IO_PENDING = ERROR_IO_PENDING; + {$EXTERNALSYM WSA_IO_INCOMPLETE} + WSA_IO_INCOMPLETE = ERROR_IO_INCOMPLETE; + {$EXTERNALSYM WSA_INVALID_HANDLE} + WSA_INVALID_HANDLE = ERROR_INVALID_HANDLE; + {$EXTERNALSYM WSA_INVALID_PARAMETER} + WSA_INVALID_PARAMETER = ERROR_INVALID_PARAMETER; + {$EXTERNALSYM WSA_NOT_ENOUGH_MEMORY} + WSA_NOT_ENOUGH_MEMORY = ERROR_NOT_ENOUGH_MEMORY; + {$EXTERNALSYM WSA_OPERATION_ABORTED} + WSA_OPERATION_ABORTED = ERROR_OPERATION_ABORTED; + {$EXTERNALSYM WSA_INVALID_EVENT} + WSA_INVALID_EVENT = WSAEVENT(nil); + {$EXTERNALSYM WSA_MAXIMUM_WAIT_EVENTS} + WSA_MAXIMUM_WAIT_EVENTS = MAXIMUM_WAIT_OBJECTS; + {$EXTERNALSYM WSA_WAIT_FAILED} + WSA_WAIT_FAILED = $FFFFFFFF; + {$EXTERNALSYM WSA_WAIT_EVENT_0} + WSA_WAIT_EVENT_0 = WAIT_OBJECT_0; + {$EXTERNALSYM WSA_WAIT_IO_COMPLETION} + WSA_WAIT_IO_COMPLETION = WAIT_IO_COMPLETION; + {$EXTERNALSYM WSA_WAIT_TIMEOUT} + WSA_WAIT_TIMEOUT = WAIT_TIMEOUT; + {$EXTERNALSYM WSA_INFINITE} + WSA_INFINITE = INFINITE; + +{ Winapi.Windows Sockets errors redefined as regular Berkeley error constants. + These are commented out in Winapi.Windows NT to avoid conflicts with errno.h. + Use the WSA constants instead. } + + {$EXTERNALSYM EWOULDBLOCK} + EWOULDBLOCK = WSAEWOULDBLOCK; + {$EXTERNALSYM EINPROGRESS} + EINPROGRESS = WSAEINPROGRESS; + {$EXTERNALSYM EALREADY} + EALREADY = WSAEALREADY; + {$EXTERNALSYM ENOTSOCK} + ENOTSOCK = WSAENOTSOCK; + {$EXTERNALSYM EDESTADDRREQ} + EDESTADDRREQ = WSAEDESTADDRREQ; + {$EXTERNALSYM EMSGSIZE} + EMSGSIZE = WSAEMSGSIZE; + {$EXTERNALSYM EPROTOTYPE} + EPROTOTYPE = WSAEPROTOTYPE; + {$EXTERNALSYM ENOPROTOOPT} + ENOPROTOOPT = WSAENOPROTOOPT; + {$EXTERNALSYM EPROTONOSUPPORT} + EPROTONOSUPPORT = WSAEPROTONOSUPPORT; + {$EXTERNALSYM ESOCKTNOSUPPORT} + ESOCKTNOSUPPORT = WSAESOCKTNOSUPPORT; + {$EXTERNALSYM EOPNOTSUPP} + EOPNOTSUPP = WSAEOPNOTSUPP; + {$EXTERNALSYM EPFNOSUPPORT} + EPFNOSUPPORT = WSAEPFNOSUPPORT; + {$EXTERNALSYM EAFNOSUPPORT} + EAFNOSUPPORT = WSAEAFNOSUPPORT; + {$EXTERNALSYM EADDRINUSE} + EADDRINUSE = WSAEADDRINUSE; + {$EXTERNALSYM EADDRNOTAVAIL} + EADDRNOTAVAIL = WSAEADDRNOTAVAIL; + {$EXTERNALSYM ENETDOWN} + ENETDOWN = WSAENETDOWN; + {$EXTERNALSYM ENETUNREACH} + ENETUNREACH = WSAENETUNREACH; + {$EXTERNALSYM ENETRESET} + ENETRESET = WSAENETRESET; + {$EXTERNALSYM ECONNABORTED} + ECONNABORTED = WSAECONNABORTED; + {$EXTERNALSYM ECONNRESET} + ECONNRESET = WSAECONNRESET; + {$EXTERNALSYM ENOBUFS} + ENOBUFS = WSAENOBUFS; + {$EXTERNALSYM EISCONN} + EISCONN = WSAEISCONN; + {$EXTERNALSYM ENOTCONN} + ENOTCONN = WSAENOTCONN; + {$EXTERNALSYM ESHUTDOWN} + ESHUTDOWN = WSAESHUTDOWN; + {$EXTERNALSYM ETOOMANYREFS} + ETOOMANYREFS = WSAETOOMANYREFS; + {$EXTERNALSYM ETIMEDOUT} + ETIMEDOUT = WSAETIMEDOUT; + {$EXTERNALSYM ECONNREFUSED} + ECONNREFUSED = WSAECONNREFUSED; + {$EXTERNALSYM ELOOP} + ELOOP = WSAELOOP; + {$EXTERNALSYM ENAMETOOLONG} + ENAMETOOLONG = WSAENAMETOOLONG; + {$EXTERNALSYM EHOSTDOWN} + EHOSTDOWN = WSAEHOSTDOWN; + {$EXTERNALSYM EHOSTUNREACH} + EHOSTUNREACH = WSAEHOSTUNREACH; + {$EXTERNALSYM ENOTEMPTY} + ENOTEMPTY = WSAENOTEMPTY; + {$EXTERNALSYM EPROCLIM} + EPROCLIM = WSAEPROCLIM; + {$EXTERNALSYM EUSERS} + EUSERS = WSAEUSERS; + {$EXTERNALSYM EDQUOT} + EDQUOT = WSAEDQUOT; + {$EXTERNALSYM ESTALE} + ESTALE = WSAESTALE; + {$EXTERNALSYM EREMOTE} + EREMOTE = WSAEREMOTE; + + {$EXTERNALSYM WSADESCRIPTION_LEN} + WSADESCRIPTION_LEN = 256; + {$EXTERNALSYM WSASYS_STATUS_LEN} + WSASYS_STATUS_LEN = 128; + +type + {$EXTERNALSYM WSADATA} + WSADATA = record + wVersion : Word; + wHighVersion : Word; + {$IFDEF _WIN64} + iMaxSockets : Word; + iMaxUdpDg : Word; + lpVendorInfo : PAnsiChar; + szDescription : array[0..WSADESCRIPTION_LEN] of AnsiChar; + szSystemStatus : array[0..WSASYS_STATUS_LEN] of AnsiChar; + {$ELSE} + szDescription : array[0..WSADESCRIPTION_LEN] of AnsiChar; + szSystemStatus : array[0..WSASYS_STATUS_LEN] of AnsiChar; + iMaxSockets : Word; + iMaxUdpDg : Word; + lpVendorInfo : PAnsiChar; + {$ENDIF} + end; + {$NODEFINE TWSAData} + TWSAData = WSADATA; + {$NODEFINE PWSAData} + PWSAData = ^TWSAData; + {$EXTERNALSYM LPWSADATA} + LPWSADATA = PWSAData; + + {$EXTERNALSYM WSAOVERLAPPED} + WSAOVERLAPPED = TOverlapped; + {$NODEFINE TWSAOverlapped} + TWSAOverlapped = WSAOVERLAPPED; + {$NODEFINE PWSAOverlapped} + PWSAOverlapped = ^TWSAOverlapped; + {$EXTERNALSYM LPWSAOVERLAPPED} + LPWSAOVERLAPPED = PWSAOverlapped; + {$IFNDEF WINCE} + {$EXTERNALSYM WSC_PROVIDER_INFO_TYPE} + {$EXTERNALSYM PROVIDERINFOLSPCATEGORIES} + {$EXTERNALSYM PROVIDERINFOAUDIT} + WSC_PROVIDER_INFO_TYPE = (ProviderInfoLspCategories, ProviderInfoAudit); + {$ENDIF} + +{ WinSock 2 extension -- WSABUF and QOS struct, include qos.h } +{ to pull in FLOWSPEC and related definitions } + + {$EXTERNALSYM WSABUF} + WSABUF = record + len: u_long; { the length of the buffer } + buf: PAnsiChar; { the pointer to the buffer } + end; + {$NODEFINE TWSABuf} + TWSABuf = WSABUF; + {$NODEFINE PWSABuf} + PWSABuf = ^TWSABuf; + {$EXTERNALSYM LPWSABUF} + LPWSABUF = PWSABUF; + + {$EXTERNALSYM SERVICETYPE} + SERVICETYPE = LongInt; + {$NODEFINE TServiceType} + TServiceType = SERVICETYPE; + + {$EXTERNALSYM FLOWSPEC} + FLOWSPEC = record + TokenRate, // In Bytes/sec + TokenBucketSize, // In Bytes + PeakBandwidth, // In Bytes/sec + Latency, // In microseconds + DelayVariation : LongInt;// In microseconds + ServiceType : TServiceType; + MaxSduSize, MinimumPolicedSize : LongInt;// In Bytes + end; + {$NODEFINE TFlowSpec} + TFlowSpec = FLOWSPEC; + {$EXTERNALSYM PFLOWSPEC} + PFLOWSPEC = ^TFlowSpec; + {$EXTERNALSYM LPFLOWSPEC} + LPFLOWSPEC = PFLOWSPEC; + + {$EXTERNALSYM QOS} + QOS = record + SendingFlowspec: TFlowSpec; { the flow spec for data sending } + ReceivingFlowspec: TFlowSpec; { the flow spec for data receiving } + ProviderSpecific: TWSABuf; { additional provider specific stuff } + end; + {$NODEFINE TQualityOfService} + TQualityOfService = QOS; + {$NODEFINE PQOS} + PQOS = ^QOS; + {$EXTERNALSYM LPQOS} + LPQOS = PQOS; + +const + {$EXTERNALSYM SERVICETYPE_NOTRAFFIC} + SERVICETYPE_NOTRAFFIC = $00000000; // No data in this direction + {$EXTERNALSYM SERVICETYPE_BESTEFFORT} + SERVICETYPE_BESTEFFORT = $00000001; // Best Effort + {$EXTERNALSYM SERVICETYPE_CONTROLLEDLOAD} + SERVICETYPE_CONTROLLEDLOAD = $00000002; // Controlled Load + {$EXTERNALSYM SERVICETYPE_GUARANTEED} + SERVICETYPE_GUARANTEED = $00000003; // Guaranteed + {$EXTERNALSYM SERVICETYPE_NETWORK_UNAVAILABLE} + SERVICETYPE_NETWORK_UNAVAILABLE = $00000004; // Used to notify change to user + {$EXTERNALSYM SERVICETYPE_GENERAL_INFORMATION} + SERVICETYPE_GENERAL_INFORMATION = $00000005; // corresponds to "General Parameters" defined by IntServ + {$EXTERNALSYM SERVICETYPE_NOCHANGE} + SERVICETYPE_NOCHANGE = $00000006; // used to indicate that the flow spec contains no change from any previous one +// to turn on immediate traffic control, OR this flag with the ServiceType field in the FLOWSPEC + {$EXTERNALSYM SERVICE_IMMEDIATE_TRAFFIC_CONTROL} + SERVICE_IMMEDIATE_TRAFFIC_CONTROL = $80000000; + +// WinSock 2 extension -- manifest constants for return values of the condition function + {$EXTERNALSYM CF_ACCEPT} + CF_ACCEPT = $0000; + {$EXTERNALSYM CF_REJECT} + CF_REJECT = $0001; + {$EXTERNALSYM CF_DEFER} + CF_DEFER = $0002; + +// WinSock 2 extension -- manifest constants for shutdown() + {$EXTERNALSYM SD_RECEIVE} + SD_RECEIVE = $00; + {$EXTERNALSYM SD_SEND} + SD_SEND = $01; + {$EXTERNALSYM SD_BOTH} + SD_BOTH = $02; + +// WinSock 2 extension -- data type and manifest constants for socket groups + {$EXTERNALSYM SG_UNCONSTRAINED_GROUP} + SG_UNCONSTRAINED_GROUP = $01; + {$EXTERNALSYM SG_CONSTRAINED_GROUP} + SG_CONSTRAINED_GROUP = $02; + +type + {$EXTERNALSYM GROUP} + GROUP = DWORD; + +// WinSock 2 extension -- data type for WSAEnumNetworkEvents() + {$EXTERNALSYM WSANETWORKEVENTS} + WSANETWORKEVENTS = record + lNetworkEvents: LongInt; + iErrorCode: Array[0..FD_MAX_EVENTS-1] of Integer; + end; + {$NODEFINE TWSANetworkEvents} + TWSANetworkEvents = WSANETWORKEVENTS; + {$NODEFINE PWSANetworkEvents} + PWSANetworkEvents = ^TWSANetworkEvents; + {$EXTERNALSYM LPWSANETWORKEVENTS} + LPWSANETWORKEVENTS = PWSANetworkEvents; + +//TransmitFile types used for the TransmitFile API function in WinNT/2000/XP +//not sure why its defined in WinCE when TransmitFile is not available. + {$IFNDEF NO_REDECLARE} + {$EXTERNALSYM TRANSMIT_FILE_BUFFERS} + TRANSMIT_FILE_BUFFERS = record + Head: Pointer; + HeadLength: DWORD; + Tail: Pointer; + TailLength: DWORD; + end; + {$NODEFINE TTransmitFileBuffers} + TTransmitFileBuffers = TRANSMIT_FILE_BUFFERS; + {$NODEFINE PTransmitFileBuffers} + PTransmitFileBuffers = ^TTransmitFileBuffers; + {$ENDIF} + {$EXTERNALSYM LPTRANSMIT_FILE_BUFFERS} + LPTRANSMIT_FILE_BUFFERS = PTransmitFileBuffers; + +const + {$EXTERNALSYM TP_ELEMENT_MEMORY} + TP_ELEMENT_MEMORY = 1; + {$EXTERNALSYM TP_ELEMENT_FILE} + TP_ELEMENT_FILE = 2; + {$EXTERNALSYM TP_ELEMENT_EOP} + TP_ELEMENT_EOP = 4; + + {$EXTERNALSYM TP_DISCONNECT} + TP_DISCONNECT = TF_DISCONNECT; + {$EXTERNALSYM TP_REUSE_SOCKET} + TP_REUSE_SOCKET = TF_REUSE_SOCKET; + {$EXTERNALSYM TP_USE_DEFAULT_WORKER} + TP_USE_DEFAULT_WORKER = TF_USE_DEFAULT_WORKER; + {$EXTERNALSYM TP_USE_SYSTEM_THREAD} + TP_USE_SYSTEM_THREAD = TF_USE_SYSTEM_THREAD; + {$EXTERNALSYM TP_USE_KERNEL_APC} + TP_USE_KERNEL_APC = TF_USE_KERNEL_APC; + +type + {$EXTERNALSYM TRANSMIT_PACKETS_ELEMENT} + TRANSMIT_PACKETS_ELEMENT = record + dwElFlags: ULONG; + cLength: ULONG; + case Integer of + 1: (nFileOffset: TLargeInteger; + hFile: THandle); + 2: (pBuffer: Pointer); + end; + {$NODEFINE TTransmitPacketsElement} + TTransmitPacketsElement = TRANSMIT_PACKETS_ELEMENT; + {$NODEFINE PTransmitPacketsElement} + PTransmitPacketsElement = ^TTransmitPacketsElement; + {$NODEFINE LPTransmitPacketsElement} + LPTransmitPacketsElement = PTransmitPacketsElement; + + {$EXTERNALSYM PTRANSMIT_PACKETS_ELEMENT} + PTRANSMIT_PACKETS_ELEMENT = ^TTransmitPacketsElement; + {$EXTERNALSYM LPTRANSMIT_PACKETS_ELEMENT} + LPTRANSMIT_PACKETS_ELEMENT = PTRANSMIT_PACKETS_ELEMENT; + +// WinSock 2 extension -- WSAPROTOCOL_INFO structure + +{$IFNDEF HAS_LPGUID} +type + {$IFNDEF HAS_PGUID} + {$NODEFINE PGUID} + PGUID = ^TGUID; + {$ENDIF} + {$EXTERNALSYM LPGUID} + LPGUID = PGUID; +{$ENDIF} + +// WinSock 2 extension -- WSAPROTOCOL_INFO manifest constants + +const + {$EXTERNALSYM MAX_PROTOCOL_CHAIN} + MAX_PROTOCOL_CHAIN = 7; + {$EXTERNALSYM BASE_PROTOCOL} + BASE_PROTOCOL = 1; + {$EXTERNALSYM LAYERED_PROTOCOL} + LAYERED_PROTOCOL = 0; + {$EXTERNALSYM WSAPROTOCOL_LEN} + WSAPROTOCOL_LEN = 255; + +type + {$EXTERNALSYM WSAPROTOCOLCHAIN} + WSAPROTOCOLCHAIN = record + ChainLen: Integer; // the length of the chain, + // length = 0 means layered protocol, + // length = 1 means base protocol, + // length > 1 means protocol chain + ChainEntries: Array[0..MAX_PROTOCOL_CHAIN-1] of LongInt; // a list of dwCatalogEntryIds + end; + {$NODEFINE TWSAProtocolChain} + TWSAProtocolChain = WSAPROTOCOLCHAIN; + {$EXTERNALSYM LPWSAPROTOCOLCHAIN} + LPWSAPROTOCOLCHAIN = ^TWSAProtocolChain; + +type + {$EXTERNALSYM WSAPROTOCOL_INFOA} + WSAPROTOCOL_INFOA = record + dwServiceFlags1: DWORD; + dwServiceFlags2: DWORD; + dwServiceFlags3: DWORD; + dwServiceFlags4: DWORD; + dwProviderFlags: DWORD; + ProviderId: TGUID; + dwCatalogEntryId: DWORD; + ProtocolChain: TWSAProtocolChain; + iVersion: Integer; + iAddressFamily: Integer; + iMaxSockAddr: Integer; + iMinSockAddr: Integer; + iSocketType: Integer; + iProtocol: Integer; + iProtocolMaxOffset: Integer; + iNetworkByteOrder: Integer; + iSecurityScheme: Integer; + dwMessageSize: DWORD; + dwProviderReserved: DWORD; + szProtocol: Array[0..WSAPROTOCOL_LEN+1-1] of AnsiChar; + end; + {$NODEFINE TWSAProtocol_InfoA} + TWSAProtocol_InfoA = WSAPROTOCOL_INFOA; + {$NODEFINE PWSAProtocol_InfoA} + PWSAProtocol_InfoA = ^WSAPROTOCOL_INFOA; + {$EXTERNALSYM LPWSAPROTOCOL_INFOA} + LPWSAPROTOCOL_INFOA = PWSAProtocol_InfoA; + + {$EXTERNALSYM WSAPROTOCOL_INFOW} + WSAPROTOCOL_INFOW = record + dwServiceFlags1: DWORD; + dwServiceFlags2: DWORD; + dwServiceFlags3: DWORD; + dwServiceFlags4: DWORD; + dwProviderFlags: DWORD; + ProviderId: TGUID; + dwCatalogEntryId: DWORD; + ProtocolChain: TWSAProtocolChain; + iVersion: Integer; + iAddressFamily: Integer; + iMaxSockAddr: Integer; + iMinSockAddr: Integer; + iSocketType: Integer; + iProtocol: Integer; + iProtocolMaxOffset: Integer; + iNetworkByteOrder: Integer; + iSecurityScheme: Integer; + dwMessageSize: DWORD; + dwProviderReserved: DWORD; + szProtocol: Array[0..WSAPROTOCOL_LEN+1-1] of WideChar; + end; + {$NODEFINE TWSAProtocol_InfoW} + TWSAProtocol_InfoW = WSAPROTOCOL_INFOW; + {$NODEFINE PWSAProtocol_InfoW} + PWSAProtocol_InfoW = ^TWSAProtocol_InfoW; + {$EXTERNALSYM LPWSAPROTOCOL_INFOW} + LPWSAPROTOCOL_INFOW = PWSAProtocol_InfoW; + + {$EXTERNALSYM WSAPROTOCOL_INFO} + {$EXTERNALSYM LPWSAPROTOCOL_INFO} + {$NODEFINE TWSAProtocol_Info} + {$NODEFINE PWSAProtocol_Info} + {$IFDEF UNICODE} + WSAPROTOCOL_INFO = TWSAProtocol_InfoW; + TWSAProtocol_Info = TWSAProtocol_InfoW; + PWSAProtocol_Info = PWSAProtocol_InfoW; + LPWSAPROTOCOL_INFO = PWSAProtocol_InfoW; + {$ELSE} + WSAPROTOCOL_INFO = TWSAProtocol_InfoA; + TWSAProtocol_Info = TWSAProtocol_InfoA; + PWSAProtocol_Info = PWSAProtocol_InfoA; + LPWSAPROTOCOL_INFO = PWSAProtocol_InfoA; + {$ENDIF} + +const +// flag bit definitions for dwProviderFlags + {$EXTERNALSYM PFL_MULTIPLE_PROTO_ENTRIES} + PFL_MULTIPLE_PROTO_ENTRIES = $00000001; + {$EXTERNALSYM PFL_RECOMMENTED_PROTO_ENTRY} + PFL_RECOMMENTED_PROTO_ENTRY = $00000002; + {$EXTERNALSYM PFL_HIDDEN} + PFL_HIDDEN = $00000004; + {$EXTERNALSYM PFL_MATCHES_PROTOCOL_ZERO} + PFL_MATCHES_PROTOCOL_ZERO = $00000008; + +// flag bit definitions for dwServiceFlags1 + {$EXTERNALSYM XP1_CONNECTIONLESS} + XP1_CONNECTIONLESS = $00000001; + {$EXTERNALSYM XP1_GUARANTEED_DELIVERY} + XP1_GUARANTEED_DELIVERY = $00000002; + {$EXTERNALSYM XP1_GUARANTEED_ORDER} + XP1_GUARANTEED_ORDER = $00000004; + {$EXTERNALSYM XP1_MESSAGE_ORIENTED} + XP1_MESSAGE_ORIENTED = $00000008; + {$EXTERNALSYM XP1_PSEUDO_STREAM} + XP1_PSEUDO_STREAM = $00000010; + {$EXTERNALSYM XP1_GRACEFUL_CLOSE} + XP1_GRACEFUL_CLOSE = $00000020; + {$EXTERNALSYM XP1_EXPEDITED_DATA} + XP1_EXPEDITED_DATA = $00000040; + {$EXTERNALSYM XP1_CONNECT_DATA} + XP1_CONNECT_DATA = $00000080; + {$EXTERNALSYM XP1_DISCONNECT_DATA} + XP1_DISCONNECT_DATA = $00000100; + {$EXTERNALSYM XP1_SUPPORT_BROADCAST} + XP1_SUPPORT_BROADCAST = $00000200; + {$EXTERNALSYM XP1_SUPPORT_MULTIPOINT} + XP1_SUPPORT_MULTIPOINT = $00000400; + {$EXTERNALSYM XP1_MULTIPOINT_CONTROL_PLANE} + XP1_MULTIPOINT_CONTROL_PLANE = $00000800; + {$EXTERNALSYM XP1_MULTIPOINT_DATA_PLANE} + XP1_MULTIPOINT_DATA_PLANE = $00001000; + {$EXTERNALSYM XP1_QOS_SUPPORTED} + XP1_QOS_SUPPORTED = $00002000; + {$EXTERNALSYM XP1_INTERRUPT} + XP1_INTERRUPT = $00004000; + {$EXTERNALSYM XP1_UNI_SEND} + XP1_UNI_SEND = $00008000; + {$EXTERNALSYM XP1_UNI_RECV} + XP1_UNI_RECV = $00010000; + {$EXTERNALSYM XP1_IFS_HANDLES} + XP1_IFS_HANDLES = $00020000; + {$EXTERNALSYM XP1_PARTIAL_MESSAGE} + XP1_PARTIAL_MESSAGE = $00040000; + + {$EXTERNALSYM BIGENDIAN} + BIGENDIAN = $0000; + {$EXTERNALSYM LITTLEENDIAN} + LITTLEENDIAN = $0001; + + {$EXTERNALSYM SECURITY_PROTOCOL_NONE} + SECURITY_PROTOCOL_NONE = $0000; + +// WinSock 2 extension -- manifest constants for WSAJoinLeaf() + {$EXTERNALSYM JL_SENDER_ONLY} + JL_SENDER_ONLY = $01; + {$EXTERNALSYM JL_RECEIVER_ONLY} + JL_RECEIVER_ONLY = $02; + {$EXTERNALSYM JL_BOTH} + JL_BOTH = $04; + +// WinSock 2 extension -- manifest constants for WSASocket() + {$EXTERNALSYM WSA_FLAG_OVERLAPPED} + WSA_FLAG_OVERLAPPED = $01; + {$EXTERNALSYM WSA_FLAG_MULTIPOINT_C_ROOT} + WSA_FLAG_MULTIPOINT_C_ROOT = $02; + {$EXTERNALSYM WSA_FLAG_MULTIPOINT_C_LEAF} + WSA_FLAG_MULTIPOINT_C_LEAF = $04; + {$EXTERNALSYM WSA_FLAG_MULTIPOINT_D_ROOT} + WSA_FLAG_MULTIPOINT_D_ROOT = $08; + {$EXTERNALSYM WSA_FLAG_MULTIPOINT_D_LEAF} + WSA_FLAG_MULTIPOINT_D_LEAF = $10; + +// WinSock 2 extension -- manifest constants for WSAIoctl() + {$EXTERNALSYM IOC_UNIX} + IOC_UNIX = $00000000; + {$EXTERNALSYM IOC_WS2} + IOC_WS2 = $08000000; + {$EXTERNALSYM IOC_PROTOCOL} + IOC_PROTOCOL = $10000000; + {$EXTERNALSYM IOC_VENDOR} + IOC_VENDOR = $18000000; + + {$IFNDEF WINCE} +///* +// * WSK-specific IO control codes are Winsock2 codes with the highest-order +// * 3 bits of the Vendor/AddressFamily-specific field set to 1. +// */ + {$EXTERNALSYM IOC_WSK} + IOC_WSK = IOC_WS2 or $07000000; + {$ENDIF} + {$EXTERNALSYM SIO_ASSOCIATE_HANDLE} + SIO_ASSOCIATE_HANDLE = DWORD(IOC_IN or IOC_WS2 or 1); + {$EXTERNALSYM SIO_ENABLE_CIRCULAR_QUEUEING} + SIO_ENABLE_CIRCULAR_QUEUEING = DWORD(IOC_VOID or IOC_WS2 or 2); + {$EXTERNALSYM SIO_FIND_ROUTE} + SIO_FIND_ROUTE = DWORD(IOC_OUT or IOC_WS2 or 3); + {$EXTERNALSYM SIO_FLUSH} + SIO_FLUSH = DWORD(IOC_VOID or IOC_WS2 or 4); + {$EXTERNALSYM SIO_GET_BROADCAST_ADDRESS} + SIO_GET_BROADCAST_ADDRESS = DWORD(IOC_OUT or IOC_WS2 or 5); + {$EXTERNALSYM SIO_GET_EXTENSION_FUNCTION_POINTER} + SIO_GET_EXTENSION_FUNCTION_POINTER = DWORD(IOC_INOUT or IOC_WS2 or 6); + {$EXTERNALSYM SIO_GET_QOS} + SIO_GET_QOS = DWORD(IOC_INOUT or IOC_WS2 or 7); + {$EXTERNALSYM SIO_GET_GROUP_QOS} + SIO_GET_GROUP_QOS = DWORD(IOC_INOUT or IOC_WS2 or 8); + {$EXTERNALSYM SIO_MULTIPOINT_LOOPBACK} + SIO_MULTIPOINT_LOOPBACK = DWORD(IOC_IN or IOC_WS2 or 9); + {$EXTERNALSYM SIO_MULTICAST_SCOPE} + SIO_MULTICAST_SCOPE = DWORD(IOC_IN or IOC_WS2 or 10); + {$EXTERNALSYM SIO_SET_QOS} + SIO_SET_QOS = DWORD(IOC_IN or IOC_WS2 or 11); + {$EXTERNALSYM SIO_SET_GROUP_QOS} + SIO_SET_GROUP_QOS = DWORD(IOC_IN or IOC_WS2 or 12); + {$EXTERNALSYM SIO_TRANSLATE_HANDLE} + SIO_TRANSLATE_HANDLE = DWORD(IOC_INOUT or IOC_WS2 or 13); + {$EXTERNALSYM SIO_ROUTING_INTERFACE_QUERY} + SIO_ROUTING_INTERFACE_QUERY = DWORD(IOC_INOUT or IOC_WS2 or 20); + {$EXTERNALSYM SIO_ROUTING_INTERFACE_CHANGE} + SIO_ROUTING_INTERFACE_CHANGE = DWORD(IOC_IN or IOC_WS2 or 21); + {$EXTERNALSYM SIO_ADDRESS_LIST_QUERY} + SIO_ADDRESS_LIST_QUERY = DWORD(IOC_OUT or IOC_WS2 or 22); // see below SOCKET_ADDRESS_LIST + {$EXTERNALSYM SIO_ADDRESS_LIST_CHANGE} + SIO_ADDRESS_LIST_CHANGE = DWORD(IOC_VOID or IOC_WS2 or 23); + {$EXTERNALSYM SIO_QUERY_TARGET_PNP_HANDLE} + SIO_QUERY_TARGET_PNP_HANDLE = DWORD(IOC_OUT or IOC_WS2 or 24); + {$EXTERNALSYM SIO_NSP_NOTIFY_CHANGE} + SIO_NSP_NOTIFY_CHANGE = DWORD(IOC_IN or IOC_WS2 or 25); + {$EXTERNALSYM SIO_ADDRESS_LIST_SORT} + SIO_ADDRESS_LIST_SORT = DWORD(IOC_INOUT or IOC_WS2 or 25); + {$IFNDEF WINCE} + {$EXTERNALSYM SIO_RESERVED_1} + SIO_RESERVED_1 = DWORD(IOC_IN or IOC_WS2 or 26); + {$EXTERNALSYM SIO_RESERVED_2} + SIO_RESERVED_2 = DWORD(IOC_IN or IOC_WS2 or 33); + {$ENDIF} + +// WinSock 2 extension -- manifest constants for SIO_TRANSLATE_HANDLE ioctl + {$EXTERNALSYM TH_NETDEV} + TH_NETDEV = $00000001; + {$EXTERNALSYM TH_TAPI} + TH_TAPI = $00000002; + +type +// Manifest constants and type definitions related to name resolution and +// registration (RNR) API + {$IFNDEF NO_REDECLARE} + {$EXTERNALSYM BLOB} + BLOB = record + cbSize : U_LONG; + pBlobData : PBYTE; + end; + {$NODEFINE TBLOB} + TBLOB = BLOB; + {$NODEFINE PBLOB} + PBLOB = ^TBLOB; + {$ENDIF} + {$EXTERNALSYM LPBLOB} + LPBLOB = PBLOB; + +// Service Install Flags + +const + {$EXTERNALSYM SERVICE_MULTIPLE} + SERVICE_MULTIPLE = $00000001; + +// & name spaces + {$EXTERNALSYM NS_ALL} + NS_ALL = 0; + + {$EXTERNALSYM NS_SAP} + NS_SAP = 1; + {$EXTERNALSYM NS_NDS} + NS_NDS = 2; + {$EXTERNALSYM NS_PEER_BROWSE} + NS_PEER_BROWSE = 3; + {$EXTERNALSYM NS_SLP} + NS_SLP = 5; + {$EXTERNALSYM NS_DHCP} + NS_DHCP = 6; + + {$EXTERNALSYM NS_TCPIP_LOCAL} + NS_TCPIP_LOCAL = 10; + {$EXTERNALSYM NS_TCPIP_HOSTS} + NS_TCPIP_HOSTS = 11; + {$EXTERNALSYM NS_DNS} + NS_DNS = 12; + {$EXTERNALSYM NS_NETBT} + NS_NETBT = 13; + {$EXTERNALSYM NS_WINS} + NS_WINS = 14; + {$EXTERNALSYM NS_NLA} + NS_NLA = 15; //* Network Location Awareness*/ - WindowsXP + {$EXTERNALSYM NS_BTH} + NS_BTH = 16; //* Bluetooth SDP Namespace */ - Winapi.Windows Vista + + {$EXTERNALSYM NS_NBP} + NS_NBP = 20; + + {$EXTERNALSYM NS_MS} + NS_MS = 30; + {$EXTERNALSYM NS_STDA} + NS_STDA = 31; + {$EXTERNALSYM NS_NTDS} + NS_NTDS = 32; + + //Winapi.Windows Vista namespaces + {$EXTERNALSYM NS_EMAIL} + NS_EMAIL = 37; + {$EXTERNALSYM NS_PNRPNAME} + NS_PNRPNAME = 38; + {$EXTERNALSYM NS_PNRPCLOUD} + NS_PNRPCLOUD = 39; + // + + {$EXTERNALSYM NS_X500} + NS_X500 = 40; + {$EXTERNALSYM NS_NIS} + NS_NIS = 41; + {$EXTERNALSYM NS_NISPLUS} + NS_NISPLUS = 42; + + {$EXTERNALSYM NS_WRQ} + NS_WRQ = 50; + + {$EXTERNALSYM NS_NETDES} + NS_NETDES = 60; // Network Designers Limited + +{ Resolution flags for WSAGetAddressByName(). + Note these are also used by the 1.1 API GetAddressByName, so leave them around. } + {$EXTERNALSYM RES_UNUSED_1} + RES_UNUSED_1 = $00000001; + {$EXTERNALSYM RES_FLUSH_CACHE} + RES_FLUSH_CACHE = $00000002; + {$EXTERNALSYM RES_SERVICE} + RES_SERVICE = $00000004; + +{ Well known value names for Service Types } + {$EXTERNALSYM SERVICE_TYPE_VALUE_IPXPORTA} + SERVICE_TYPE_VALUE_IPXPORTA : PAnsiChar = 'IpxSocket'; {Do not Localize} + {$EXTERNALSYM SERVICE_TYPE_VALUE_SAPIDA} + SERVICE_TYPE_VALUE_SAPIDA : PAnsiChar = 'SapId'; {Do not Localize} + {$EXTERNALSYM SERVICE_TYPE_VALUE_TCPPORTA} + SERVICE_TYPE_VALUE_TCPPORTA : PAnsiChar = 'TcpPort'; {Do not Localize} + {$EXTERNALSYM SERVICE_TYPE_VALUE_UDPPORTA} + SERVICE_TYPE_VALUE_UDPPORTA : PAnsiChar = 'UdpPort'; {Do not Localize} + {$EXTERNALSYM SERVICE_TYPE_VALUE_OBJECTIDA} + SERVICE_TYPE_VALUE_OBJECTIDA : PAnsiChar = 'ObjectId'; {Do not Localize} + + {$EXTERNALSYM SERVICE_TYPE_VALUE_IPXPORTW} + SERVICE_TYPE_VALUE_IPXPORTW : PWideChar = 'IpxSocket'; {Do not Localize} + {$EXTERNALSYM SERVICE_TYPE_VALUE_SAPIDW} + SERVICE_TYPE_VALUE_SAPIDW : PWideChar = 'SapId'; {Do not Localize} + {$EXTERNALSYM SERVICE_TYPE_VALUE_TCPPORTW} + SERVICE_TYPE_VALUE_TCPPORTW : PWideChar = 'TcpPort'; {Do not Localize} + {$EXTERNALSYM SERVICE_TYPE_VALUE_UDPPORTW} + SERVICE_TYPE_VALUE_UDPPORTW : PWideChar = 'UdpPort'; {Do not Localize} + {$EXTERNALSYM SERVICE_TYPE_VALUE_OBJECTIDW} + SERVICE_TYPE_VALUE_OBJECTIDW : PWideChar = 'ObjectId'; {Do not Localize} + + {$EXTERNALSYM SERVICE_TYPE_VALUE_SAPID} + {$EXTERNALSYM SERVICE_TYPE_VALUE_TCPPORT} + {$EXTERNALSYM SERVICE_TYPE_VALUE_UDPPORT} + {$EXTERNALSYM SERVICE_TYPE_VALUE_OBJECTID} + {$IFDEF UNICODE} + SERVICE_TYPE_VALUE_SAPID : PWideChar = 'SapId'; {Do not Localize} + SERVICE_TYPE_VALUE_TCPPORT : PWideChar = 'TcpPort'; {Do not Localize} + SERVICE_TYPE_VALUE_UDPPORT : PWideChar = 'UdpPort'; {Do not Localize} + SERVICE_TYPE_VALUE_OBJECTID : PWideChar = 'ObjectId'; {Do not Localize} + {$ELSE} + SERVICE_TYPE_VALUE_SAPID : PAnsiChar = 'SapId'; {Do not Localize} + SERVICE_TYPE_VALUE_TCPPORT : PAnsiChar = 'TcpPort'; {Do not Localize} + SERVICE_TYPE_VALUE_UDPPORT : PAnsiChar = 'UdpPort'; {Do not Localize} + SERVICE_TYPE_VALUE_OBJECTID : PAnsiChar = 'ObjectId'; {Do not Localize} + {$ENDIF} + +// SockAddr Information +type + {$EXTERNALSYM SOCKET_ADDRESS} + SOCKET_ADDRESS = record + lpSockaddr : PSOCKADDR; + iSockaddrLength : Integer; + end; + {$NODEFINE TSocket_Address} + TSocket_Address = SOCKET_ADDRESS; + {$EXTERNALSYM PSOCKET_ADDRESS} + PSOCKET_ADDRESS = ^TSocket_Address; + + {$EXTERNALSYM SOCKET_ADDRESS_LIST} + SOCKET_ADDRESS_LIST = record + iAddressCount : Integer; + Address : SOCKET_ADDRESS; + end; + {$NODEFINE TSocket_Address_List} + TSocket_Address_List = SOCKET_ADDRESS_LIST; + {$EXTERNALSYM PSOCKET_ADDRESS_LIST} + PSOCKET_ADDRESS_LIST = ^TSocket_Address_List; + {$EXTERNALSYM LPSOCKET_ADDRESS_LIST} + LPSOCKET_ADDRESS_LIST = PSOCKET_ADDRESS_LIST; + +// CSAddr Information + {$EXTERNALSYM CSADDR_INFO} + CSADDR_INFO = record + LocalAddr, + RemoteAddr : TSocket_Address; + iSocketType, + iProtocol : Integer; + end; + {$NODEFINE TCSAddr_Info} + TCSAddr_Info = CSADDR_INFO; + {$EXTERNALSYM PCSADDR_INFO} + PCSADDR_INFO = ^TCSAddr_Info; + {$EXTERNALSYM LPCSADDR_INFO} + LPCSADDR_INFO = PCSADDR_INFO; + +// Address Family/Protocol Tuples + {$EXTERNALSYM AFPROTOCOLS} + AFPROTOCOLS = record + iAddressFamily : Integer; + iProtocol : Integer; + end; + {$NODEFINE TAFProtocols} + TAFProtocols = AFPROTOCOLS; + {$EXTERNALSYM PAFPROTOCOLS} + PAFPROTOCOLS = ^TAFProtocols; + {$EXTERNALSYM LPAFPROTOCOLS} + LPAFPROTOCOLS = PAFPROTOCOLS; + +// Client Query API Typedefs + +// The comparators + {$EXTERNALSYM WSAECOMPARATOR} + WSAECOMPARATOR = (COMP_EQUAL {= 0}, COMP_NOTLESS); + {$NODEFINE TWSAEComparator} + TWSAEComparator = WSAECOMPARATOR; + {$EXTERNALSYM PWSAECOMPARATOR} + PWSAECOMPARATOR = ^WSAECOMPARATOR; + + {$EXTERNALSYM WSAVERSION} + WSAVERSION = record + dwVersion : DWORD; + ecHow : TWSAEComparator; + end; + {$NODEFINE TWSAVersion} + TWSAVersion = WSAVERSION; + {$EXTERNALSYM PWSAVERSION} + PWSAVERSION = ^TWSAVersion; + {$EXTERNALSYM LPWSAVERSION} + LPWSAVERSION = PWSAVERSION; + + {$EXTERNALSYM WSAQUERYSETA} + WSAQUERYSETA = record + dwSize : DWORD; + lpszServiceInstanceName : PAnsiChar; + lpServiceClassId : PGUID; + lpVersion : LPWSAVERSION; + lpszComment : PAnsiChar; + dwNameSpace : DWORD; + lpNSProviderId : PGUID; + lpszContext : PAnsiChar; + dwNumberOfProtocols : DWORD; + lpafpProtocols : LPAFPROTOCOLS; + lpszQueryString : PAnsiChar; + dwNumberOfCsAddrs : DWORD; + lpcsaBuffer : LPCSADDR_INFO; + dwOutputFlags : DWORD; + lpBlob : LPBLOB; + end; + {$NODEFINE TWSAQuerySetA} + TWSAQuerySetA = WSAQUERYSETA; + {$EXTERNALSYM PWSAQUERYSETA} + PWSAQUERYSETA = ^TWSAQuerySetA; + {$EXTERNALSYM LPWSAQUERYSETA} + LPWSAQUERYSETA = PWSAQUERYSETA; + + {$EXTERNALSYM WSAQUERYSETW} + WSAQUERYSETW = record + dwSize : DWORD; + lpszServiceInstanceName : PWideChar; + lpServiceClassId : PGUID; + lpVersion : LPWSAVERSION; + lpszComment : PWideChar; + dwNameSpace : DWORD; + lpNSProviderId : PGUID; + lpszContext : PWideChar; + dwNumberOfProtocols : DWORD; + lpafpProtocols : LPAFPROTOCOLS; + lpszQueryString : PWideChar; + dwNumberOfCsAddrs : DWORD; + lpcsaBuffer : LPCSADDR_INFO; + dwOutputFlags : DWORD; + lpBlob : LPBLOB; + end; + {$NODEFINE TWSAQuerySetW} + TWSAQuerySetW = WSAQUERYSETW; + {$EXTERNALSYM PWSAQUERYSETW} + PWSAQUERYSETW = ^TWSAQuerySetW; + {$EXTERNALSYM LPWSAQUERYSETW} + LPWSAQUERYSETW = PWSAQUERYSETW; + + {$NODEFINE TWSAQuerySet} + {$EXTERNALSYM PWSAQUERYSET} + {$EXTERNALSYM LPWSAQUERYSET} + {$IFDEF UNICODE} + TWSAQuerySet = TWSAQuerySetW; + PWSAQUERYSET = PWSAQUERYSETW; + LPWSAQUERYSET = LPWSAQUERYSETW; + {$ELSE} + TWSAQuerySet = TWSAQuerySetA; + PWSAQUERYSET = PWSAQUERYSETA; + LPWSAQUERYSET = LPWSAQUERYSETA; + {$ENDIF} + +const + {$EXTERNALSYM LUP_DEEP} + LUP_DEEP = $0001; + {$EXTERNALSYM LUP_CONTAINERS} + LUP_CONTAINERS = $0002; + {$EXTERNALSYM LUP_NOCONTAINERS} + LUP_NOCONTAINERS = $0004; + {$EXTERNALSYM LUP_NEAREST} + LUP_NEAREST = $0008; + {$EXTERNALSYM LUP_RETURN_NAME} + LUP_RETURN_NAME = $0010; + {$EXTERNALSYM LUP_RETURN_TYPE} + LUP_RETURN_TYPE = $0020; + {$EXTERNALSYM LUP_RETURN_VERSION} + LUP_RETURN_VERSION = $0040; + {$EXTERNALSYM LUP_RETURN_COMMENT} + LUP_RETURN_COMMENT = $0080; + {$EXTERNALSYM LUP_RETURN_ADDR} + LUP_RETURN_ADDR = $0100; + {$EXTERNALSYM LUP_RETURN_BLOB} + LUP_RETURN_BLOB = $0200; + {$EXTERNALSYM LUP_RETURN_ALIASES} + LUP_RETURN_ALIASES = $0400; + {$EXTERNALSYM LUP_RETURN_QUERY_STRING} + LUP_RETURN_QUERY_STRING = $0800; + {$EXTERNALSYM LUP_RETURN_ALL} + LUP_RETURN_ALL = $0FF0; + {$EXTERNALSYM LUP_RES_SERVICE} + LUP_RES_SERVICE = $8000; + + {$EXTERNALSYM LUP_FLUSHCACHE} + LUP_FLUSHCACHE = $1000; + {$EXTERNALSYM LUP_FLUSHPREVIOUS} + LUP_FLUSHPREVIOUS = $2000; + +// Return flags + {$EXTERNALSYM RESULT_IS_ALIAS} + RESULT_IS_ALIAS = $0001; + //These are not supported in WinCE 4.2 but are available in later versions. + {$EXTERNALSYM RESULT_IS_ADDED} + RESULT_IS_ADDED = $0010; + {$EXTERNALSYM RESULT_IS_CHANGED} + RESULT_IS_CHANGED = $0020; + {$EXTERNALSYM RESULT_IS_DELETED} + RESULT_IS_DELETED = $0040; + + {$EXTERNALSYM MAX_NATURAL_ALIGNMENT} + {$IFDEF _WIN64} + MAX_NATURAL_ALIGNMENT = SizeOf(Int64); + {$ELSE} + MAX_NATURAL_ALIGNMENT = SizeOf(DWORD); + {$ENDIF} + +// WSARecvMsg flags + {$EXTERNALSYM MSG_TRUNC} + MSG_TRUNC = $0100; + {$EXTERNALSYM MSG_CTRUNC} + MSG_CTRUNC = $0200; + {$EXTERNALSYM MSG_BCAST} + MSG_BCAST = $0400; + {$EXTERNALSYM MSG_MCAST} + MSG_MCAST = $0800; + +{$IFNDEF WINCE} + //Winapi.Windows Vista WSAPoll +//* Event flag definitions for WSAPoll(). */ + {$EXTERNALSYM POLLRDNORM} + POLLRDNORM = $0100; + {$EXTERNALSYM POLLRDBAND} + POLLRDBAND = $0200; + {$EXTERNALSYM POLLIN} + POLLIN = (POLLRDNORM or POLLRDBAND); + {$EXTERNALSYM POLLPRI} + POLLPRI = $0400; + {$EXTERNALSYM POLLWRNORM} + POLLWRNORM = $0010; + {$EXTERNALSYM POLLOUT} + POLLOUT = (POLLWRNORM); + {$EXTERNALSYM POLLWRBAND} + POLLWRBAND = $0020; + {$EXTERNALSYM POLLERR} + POLLERR = $0001; + {$EXTERNALSYM POLLHUP} + POLLHUP = $0002; + {$EXTERNALSYM POLLNVAL} + POLLNVAL = $0004; +{$ENDIF} + +type +// Service Address Registration and Deregistration Data Types. + {$EXTERNALSYM WSAESETSERVICEOP} + WSAESETSERVICEOP = (RNRSERVICE_REGISTER{=0}, RNRSERVICE_DEREGISTER, RNRSERVICE_DELETE); + {$NODEFINE TWSAESetServiceOp} + TWSAESetServiceOp = WSAESETSERVICEOP; + +{ Service Installation/Removal Data Types. } + {$EXTERNALSYM WSANSCLASSINFOA} + WSANSCLASSINFOA = record + lpszName : PAnsiChar; + dwNameSpace : DWORD; + dwValueType : DWORD; + dwValueSize : DWORD; + lpValue : Pointer; + end; + {$NODEFINE TWSANSClassInfoA} + TWSANSClassInfoA = WSANSCLASSINFOA; + {$EXTERNALSYM PWSANSClassInfoA} + PWSANSCLASSINFOA = ^TWSANSClassInfoA; + {$EXTERNALSYM LPWSANSCLASSINFOA} + LPWSANSCLASSINFOA = PWSANSCLASSINFOA; + + {$EXTERNALSYM WSANSCLASSINFOW} + WSANSCLASSINFOW = record + lpszName : PWideChar; + dwNameSpace : DWORD; + dwValueType : DWORD; + dwValueSize : DWORD; + lpValue : Pointer; + end; + {$NODEFINE TWSANSClassInfoW} + TWSANSClassInfoW = WSANSCLASSINFOW; + {$EXTERNALSYM PWSANSClassInfoW} + PWSANSCLASSINFOW = ^TWSANSClassInfoW; + {$EXTERNALSYM LPWSANSCLASSINFOW} + LPWSANSCLASSINFOW = PWSANSCLASSINFOW; + + {$NODEFINE TWSANSClassInfo} + {$EXTERNALSYM WSANSCLASSINFO} + {$EXTERNALSYM PWSANSCLASSINFO} + {$EXTERNALSYM LPWSANSCLASSINFO} + {$IFDEF UNICODE} + TWSANSClassInfo = TWSANSClassInfoW; + WSANSCLASSINFO = TWSANSClassInfoW; + PWSANSCLASSINFO = PWSANSCLASSINFOW; + LPWSANSCLASSINFO = LPWSANSCLASSINFOW; + {$ELSE} + TWSANSClassInfo = TWSANSClassInfoA; + WSANSCLASSINFO = TWSANSClassInfoA; + PWSANSCLASSINFO = PWSANSCLASSINFOA; + LPWSANSCLASSINFO = LPWSANSCLASSINFOA; + {$ENDIF // UNICODE} + + {$EXTERNALSYM WSASERVICECLASSINFOA} + WSASERVICECLASSINFOA = record + lpServiceClassId : PGUID; + lpszServiceClassName : PAnsiChar; + dwCount : DWORD; + lpClassInfos : LPWSANSCLASSINFOA; + end; + {$NODEFINE TWSAServiceClassInfoA} + TWSAServiceClassInfoA = WSASERVICECLASSINFOA; + {$EXTERNALSYM PWSASERVICECLASSINFOA} + PWSASERVICECLASSINFOA = ^TWSAServiceClassInfoA; + {$EXTERNALSYM LPWSASERVICECLASSINFOA} + LPWSASERVICECLASSINFOA = PWSASERVICECLASSINFOA; + + {$EXTERNALSYM WSASERVICECLASSINFOW} + WSASERVICECLASSINFOW = record + lpServiceClassId : PGUID; + lpszServiceClassName : PWideChar; + dwCount : DWORD; + lpClassInfos : LPWSANSCLASSINFOW; + end; + {$NODEFINE TWSAServiceClassInfoW} + TWSAServiceClassInfoW = WSASERVICECLASSINFOW; + {$EXTERNALSYM PWSASERVICECLASSINFOW} + PWSASERVICECLASSINFOW = ^TWSAServiceClassInfoW; + {$EXTERNALSYM LPWSASERVICECLASSINFOW} + LPWSASERVICECLASSINFOW = PWSASERVICECLASSINFOW; + + {$NODEFINE TWSAServiceClassInfo} + {$EXTERNALSYM WSASERVICECLASSINFO} + {$EXTERNALSYM PWSASERVICECLASSINFO} + {$EXTERNALSYM LPWSASERVICECLASSINFO} + {$IFDEF UNICODE} + TWSAServiceClassInfo = TWSAServiceClassInfoW; + WSASERVICECLASSINFO = TWSAServiceClassInfoW; + PWSASERVICECLASSINFO = PWSASERVICECLASSINFOW; + LPWSASERVICECLASSINFO = LPWSASERVICECLASSINFOW; + {$ELSE} + TWSAServiceClassInfo = TWSAServiceClassInfoA; + WSASERVICECLASSINFO = TWSAServiceClassInfoA; + PWSASERVICECLASSINFO = PWSASERVICECLASSINFOA; + LPWSASERVICECLASSINFO = LPWSASERVICECLASSINFOA; + {$ENDIF} + + {$EXTERNALSYM WSANAMESPACE_INFOA} + WSANAMESPACE_INFOA = record + NSProviderId : TGUID; + dwNameSpace : DWORD; + fActive : DWORD{Bool}; + dwVersion : DWORD; + lpszIdentifier : PAnsiChar; + end; + {$NODEFINE TWSANameSpace_InfoA} + TWSANameSpace_InfoA = WSANAMESPACE_INFOA; + {$EXTERNALSYM PWSANAMESPACE_INFOA} + PWSANAMESPACE_INFOA = ^TWSANameSpace_InfoA; + {$EXTERNALSYM LPWSANAMESPACE_INFOA} + LPWSANAMESPACE_INFOA = PWSANAMESPACE_INFOA; + + {$EXTERNALSYM WSANAMESPACE_INFOW} + WSANAMESPACE_INFOW = record + NSProviderId : TGUID; + dwNameSpace : DWORD; + fActive : DWORD{Bool}; + dwVersion : DWORD; + lpszIdentifier : PWideChar; + end; + {$NODEFINE TWSANameSpace_InfoW} + TWSANameSpace_InfoW = WSANAMESPACE_INFOW; + {$EXTERNALSYM PWSANAMESPACE_INFOW} + PWSANAMESPACE_INFOW = ^TWSANameSpace_InfoW; + {$EXTERNALSYM LPWSANAMESPACE_INFOW} + LPWSANAMESPACE_INFOW = PWSANAMESPACE_INFOW; + +{$IFNDEF WINCE} + {$EXTERNALSYM WSANAMESPACE_INFOEXW} + WSANAMESPACE_INFOEXW = record + NSProviderId : TGUID; + dwNameSpace : DWord; + fActive : LongBool; + lpszIdentifier : LPWSTR; + ProviderSpecific : BLOB; + end; + {$NODEFINE TWSANameSpace_InfoExW} + TWSANameSpace_InfoExW = WSANAMESPACE_INFOEXW; + {$EXTERNALSYM PWSANAMESPACE_INFOEXW} + PWSANAMESPACE_INFOEXW = ^TWSANameSpace_InfoExW; + {$EXTERNALSYM LPWSANAMESPACE_INFOEXW} + LPWSANAMESPACE_INFOEXW = PWSANAMESPACE_INFOEXW; + + {$EXTERNALSYM WSANAMESPACE_INFOEXA} + WSANAMESPACE_INFOEXA = record + NSProviderId : TGUID; + dwNameSpace : DWord; + fActive : LongBool; + lpszIdentifier : LPSTR; + ProviderSpecific : BLOB; + end; + {$NODEFINE TWSANameSpace_InfoExA} + TWSANameSpace_InfoExA = WSANAMESPACE_INFOEXA; + {$EXTERNALSYM PWSANAMESPACE_INFOEXA} + PWSANAMESPACE_INFOEXA = ^TWSANameSpace_InfoExA; + {$EXTERNALSYM LPWSANAMESPACE_INFOEXA} + LPWSANAMESPACE_INFOEXA = PWSANAMESPACE_INFOEXA; + + {$EXTERNALSYM LPFN_WSAENUMNAMESPACEPROVIDERSEXW} + LPFN_WSAENUMNAMESPACEPROVIDERSEXW = function (var lpdwBufferLength : DWord; + lpnspBuffer : PWSANAMESPACE_INFOEXW): Integer; stdcall; + {$EXTERNALSYM LPFN_WSAENUMNAMESPACEPROVIDERSEXA} + LPFN_WSAENUMNAMESPACEPROVIDERSEXA = function (var lpdwBufferLength : DWord; + lpnspBuffer : PWSANAMESPACE_INFOEXA): Integer; stdcall; + + {$NODEFINE TWSANameSpace_InfoEx} + {$EXTERNALSYM WSANAMESPACE_INFOEX} + {$EXTERNALSYM PWSANAMESPACE_INFOEX} + {$EXTERNALSYM LPWSANAMESPACE_INFOEX} + {$EXTERNALSYM LPFN_WSAENUMNAMESPACEPROVIDERSEX} + {$IFDEF UNICODE} + WSANAMESPACE_INFOEX = WSANAMESPACE_INFOEXW; + TWSANameSpace_InfoEx = TWSANameSpace_InfoExW; + PWSANAMESPACE_INFOEX = PWSANAMESPACE_INFOEXW; + LPWSANAMESPACE_INFOEX = PWSANAMESPACE_INFOEX; + LPFN_WSAENUMNAMESPACEPROVIDERSEX = LPFN_WSAENUMNAMESPACEPROVIDERSEXW; + {$ELSE} + WSANAMESPACE_INFOEX = WSANAMESPACE_INFOEXA; + TWSANameSpace_InfoEx = TWSANameSpace_InfoExA; + PWSANAMESPACE_INFOEX = PWSANAMESPACE_INFOEXA; + LPWSANAMESPACE_INFOEX = PWSANAMESPACE_INFOEX; + LPFN_WSAENUMNAMESPACEPROVIDERSEX = LPFN_WSAENUMNAMESPACEPROVIDERSEXA; + {$ENDIF} +{$ENDIF} // WINCE + + {$NODEFINE TWSANameSpace_Info} + {$EXTERNALSYM WSANAMESPACE_INFO} + {$EXTERNALSYM PWSANAMESPACE_INFO} + {$EXTERNALSYM LPWSANAMESPACE_INFO} + {$IFDEF UNICODE} + TWSANameSpace_Info = TWSANameSpace_InfoW; + WSANAMESPACE_INFO = TWSANameSpace_InfoW; + PWSANAMESPACE_INFO = PWSANAMESPACE_INFOW; + LPWSANAMESPACE_INFO = LPWSANAMESPACE_INFOW; + {$ELSE} + TWSANameSpace_Info = TWSANameSpace_InfoA; + WSANAMESPACE_INFO = TWSANameSpace_InfoA; + PWSANAMESPACE_INFO = PWSANAMESPACE_INFOA; + LPWSANAMESPACE_INFO = LPWSANAMESPACE_INFOA; + {$ENDIF} + + {$IFDEF WINCE} + {$EXTERNALSYM DSCP_TRAFFIC_TYPE} + {$EXTERNALSYM DSCPTYPENOTSET} + {$EXTERNALSYM DSCPBESTEFFORT} + {$EXTERNALSYM DSCPBACKGROUND} + {$EXTERNALSYM DSCPEXCELLENTEFFORT} + {$EXTERNALSYM DSCPVIDEO} + {$EXTERNALSYM DSCPAUDIO} + {$EXTERNALSYM DSCPCONTROL} + {$EXTERNALSYM NUMDSCPTRAFFICTYPES} + DSCP_TRAFFIC_TYPE = ( + DSCPTypeNotSet = 0, + DSCPBestEffort = 1, + DSCPBackground = 2, + DSCPExcellentEffort = 3, + DSCPVideo = 4, + DSCPAudio = 5, + DSCPControl = 6); +// Define NumDSCPTrafficTypes as DSCPControl +//because FPC warns that enumerations must be descending. +//The original definition for DSCP_TRAFFIC_TYPE is: +// +///* differential service traffic types */ +//typedef enum _DSCP_TRAFFIC_TYPE +//{ +// DSCPTypeNotSet = 0, +// DSCPBestEffort = 1, +// DSCPBackground = 2, +// DSCPExcellentEffort = 3, +// DSCPVideo = 4, +// DSCPAudio = 5, +// DSCPControl = 6, +// NumDSCPTrafficTypes = 6 +//} //DSCP_TRAFFIC_TYPE; +const + NumDSCPTrafficTypes : DSCP_TRAFFIC_TYPE = DSCPControl; +type + {$ENDIF} + + {$EXTERNALSYM WSAMSG} + WSAMSG = record + name : PSOCKADDR; ///* Remote address */ + namelen : Integer; ///* Remote address length * + lpBuffers : LPWSABUF; // /* Data buffer array */ + dwBufferCount : DWord; // /* Number of elements in the array */ + Control : WSABUF; // /* Control buffer */ + dwFlags : DWord; // /* Flags */ + end; + {$NODEFINE TWSAMSG} + TWSAMSG = WSAMSG; + {$EXTERNALSYM PWSAMSG} + PWSAMSG = ^TWSAMSG; + {$EXTERNALSYM LPWSAMSG} + LPWSAMSG = PWSAMSG; + + {$EXTERNALSYM _WSACMSGHDR} + _WSACMSGHDR = record + cmsg_len: SIZE_T; + cmsg_level: Integer; + cmsg_type: Integer; + { followed by UCHAR cmsg_data[] } + end; + {$EXTERNALSYM WSACMSGHDR} + WSACMSGHDR = _WSACMSGHDR; + {$EXTERNALSYM cmsghdr} + cmsghdr = _WSACMSGHDR; + {$NODEFINE TWSACMsgHdr} + TWSACMsgHdr = WSACMSGHDR; + {$EXTERNALSYM PWSACMSGHDR} + PWSACMSGHDR = ^TWSACMsgHdr; + {$EXTERNALSYM LPWSACMSGHDR} + LPWSACMSGHDR = PWSACMSGHDR; + {$EXTERNALSYM CMSGHDR} + PCMSGHDR = ^CMSGHDR; +{$IFNDEF WINCE} + {$EXTERNALSYM WSAPOLLFD} + WSAPOLLFD = record + fd : TSocket; + events : SHORT; + revents : SHORT; + end; + {$NODEFINE TWSAPOLLFD} + TWSAPOLLFD = WSAPOLLFD; + {$EXTERNALSYM PWSAPOLLFD} + PWSAPOLLFD = ^TWSAPOLLFD; + {$EXTERNALSYM LPWSAPOLLFD} + LPWSAPOLLFD = PWSAPOLLFD; +{$ENDIF} + +{ WinSock 2 extensions -- data types for the condition function in } +{ WSAAccept() and overlapped I/O completion routine. } +type + {$EXTERNALSYM LPCONDITIONPROC} + LPCONDITIONPROC = function(lpCallerId: LPWSABUF; lpCallerData: LPWSABUF; lpSQOS, pGQOS: LPQOS; + lpCalleeId,lpCalleeData: LPWSABUF; g: GROUP; dwCallbackData: DWORD): Integer; stdcall; + {$EXTERNALSYM LPWSAOVERLAPPED_COMPLETION_ROUTINE} + LPWSAOVERLAPPED_COMPLETION_ROUTINE = procedure(const dwError, cbTransferred: DWORD; + const lpOverlapped : LPWSAOVERLAPPED; const dwFlags: DWORD); stdcall; + + {$EXTERNALSYM WSACOMPLETIONTYPE} + {$EXTERNALSYM NSP_NOTIFY_IMMEDIATELY} + {$EXTERNALSYM NSP_NOTIFY_HWND} + {$EXTERNALSYM NSP_NOTIFY_EVENT} + {$EXTERNALSYM NSP_NOTIFY_PORT} + {$EXTERNALSYM NSP_NOTIFY_APC} + WSACOMPLETIONTYPE = (NSP_NOTIFY_IMMEDIATELY, + NSP_NOTIFY_HWND, + NSP_NOTIFY_EVENT, + NSP_NOTIFY_PORT, + NSP_NOTIFY_APC); + {$EXTERNALSYM WSACOMPLETION_WINDOWMESSAGE} + WSACOMPLETION_WINDOWMESSAGE = record + hWnd : HWND; + uMsg : UINT; + context : WPARAM; + end; + {$EXTERNALSYM WSACOMPLETION_EVENT} + WSACOMPLETION_EVENT = record + lpOverlapped : LPWSAOVERLAPPED; + end; + {$EXTERNALSYM WSACOMPLETION_APC} + WSACOMPLETION_APC = record + lpOverlapped : LPWSAOVERLAPPED; + lpfnCompletionProc : LPWSAOVERLAPPED_COMPLETION_ROUTINE; + end; + {$EXTERNALSYM WSACOMPLETION_PORT} + WSACOMPLETION_PORT = record + lpOverlapped : LPWSAOVERLAPPED; + hPort : THANDLE; + Key : ULONG_PTR; + end; + {$EXTERNALSYM WSACOMPLETION_UNION} + WSACOMPLETION_union = record + case Integer of + 0: (WindowMessage : WSACOMPLETION_WINDOWMESSAGE); + 1: (Event : WSACOMPLETION_EVENT); + 2: (Apc : WSACOMPLETION_APC); + 3: (Port : WSACOMPLETION_PORT); + end; + {$EXTERNALSYM WSACOMPLETION} + WSACOMPLETION = record + _Type : WSACOMPLETIONTYPE; + Parameters : WSACOMPLETION_union; + end; + {$EXTERNALSYM PWSACOMPLETION} + PWSACOMPLETION = ^WSACOMPLETION; + {$EXTERNALSYM LPWSACOMPLETION} + LPWSACOMPLETION = PWSACOMPLETION; + +type +{$IFDEF INCL_WINSOCK_API_TYPEDEFS} + {$EXTERNALSYM LPFN_WSASTARTUP} + LPFN_WSASTARTUP = function(const wVersionRequired: WORD; out WSData: TWSAData): Integer; stdcall; + {$EXTERNALSYM LPFN_WSACLEANUP} + LPFN_WSACLEANUP = function: Integer; stdcall; + {$EXTERNALSYM LPFN_ACCEPT} + LPFN_ACCEPT = function(const s: TSocket; AAddr: PSOCKADDR; addrlen: PInteger): TSocket; stdcall; + {$EXTERNALSYM LPFN_BIND} + LPFN_BIND = function(const s: TSocket; const name: PSOCKADDR; const namelen: Integer): Integer; stdcall; + {$EXTERNALSYM LPFN_CLOSESOCKET} + LPFN_CLOSESOCKET = function(const s: TSocket): Integer; stdcall; + {$EXTERNALSYM LPFN_CONNECT} + LPFN_CONNECT = function(const s: TSocket; const name: PSOCKADDR; const namelen: Integer): Integer; stdcall; + {$EXTERNALSYM lpfn_IOCTLSOCKET} + LPFN_IOCTLSOCKET = function(const s: TSocket; const cmd: DWORD; var arg: u_long): Integer; stdcall; + {$EXTERNALSYM LPFN_GETPEERNAME} + LPFN_GETPEERNAME = function(const s: TSocket; const name: PSOCKADDR; var namelen: Integer): Integer; stdcall; + {$EXTERNALSYM LPFN_GETSOCKNAME} + LPFN_GETSOCKNAME = function(const s: TSocket; const name: PSOCKADDR; var namelen: Integer): Integer; stdcall; + {$EXTERNALSYM LPFN_GETSOCKOPT} + LPFN_GETSOCKOPT = function(const s: TSocket; const level, optname: Integer; optval: PAnsiChar; var optlen: Integer): Integer; stdcall; + {$EXTERNALSYM LPFN_HTONL} + LPFN_HTONL = function(hostlong: u_long): u_long; stdcall; + {$EXTERNALSYM LPFN_HTONS} + LPFN_HTONS = function(hostshort: u_short): u_short; stdcall; + {$EXTERNALSYM LPFN_INET_ADDR} + LPFN_INET_ADDR = function(cp: PAnsiChar): u_long; stdcall; + {$EXTERNALSYM LPFN_INET_NTOA} + LPFN_INET_NTOA = function(inaddr: TInAddr): PAnsiChar; stdcall; + {$EXTERNALSYM LPFN_LISTEN} + LPFN_LISTEN = function(const s: TSocket; backlog: Integer): Integer; stdcall; + {$EXTERNALSYM LPFN_NTOHL} + LPFN_NTOHL = function(netlong: u_long): u_long; stdcall; + {$EXTERNALSYM LPFN_NTOHS} + LPFN_NTOHS = function(netshort: u_short): u_short; stdcall; + {$EXTERNALSYM LPFN_RECV} + LPFN_RECV = function(const s: TSocket; var Buf; len, flags: Integer): Integer; stdcall; + {$EXTERNALSYM LPFN_RECVFROM} + LPFN_RECVFROM = function(const s: TSocket; var Buf; len, flags: Integer; from: PSOCKADDR; fromlen: PInteger): Integer; stdcall; + {$EXTERNALSYM LPFN_SELECT} + LPFN_SELECT = function(nfds: Integer; readfds, writefds, exceptfds: PFDSet; timeout: PTimeVal): Integer; stdcall; + {$EXTERNALSYM LPFN_SEND} + LPFN_SEND = function(const s: TSocket; const Buf; len, flags: Integer): Integer; stdcall; + {$EXTERNALSYM LPFN_SENDTO} + LPFN_SENDTO = function(const s: TSocket; const Buf; const len, flags: Integer; const addrto: PSOCKADDR; const tolen: Integer): Integer; stdcall; + {$EXTERNALSYM LPFN_SETSOCKOPT} + LPFN_SETSOCKOPT = function(const s: TSocket; const level, optname: Integer; optval: PAnsiChar; const optlen: Integer): Integer; stdcall; + {$EXTERNALSYM LPFN_SHUTDOWN} + LPFN_SHUTDOWN = function(const s: TSocket; const how: Integer): Integer; stdcall; + {$EXTERNALSYM LPFN_SOCKET} + LPFN_SOCKET = function(const af, istruct, protocol: Integer): TSocket; stdcall; + {$EXTERNALSYM LPFN_GETHOSTBYADDR} + LPFN_GETHOSTBYADDR = function(AAddr: Pointer; const len, addrtype: Integer): PHostEnt; stdcall; + {$EXTERNALSYM LPFN_GETHOSTBYNAME} + LPFN_GETHOSTBYNAME = function(name: PAnsiChar): PHostEnt; stdcall; + {$EXTERNALSYM LPFN_GETHOSTNAME} + LPFN_GETHOSTNAME = function(name: PAnsiChar; len: Integer): Integer; stdcall; +{$IFDEF WINCE} + // WinCE specific for setting the host name + {$EXTERNALSYM LPFN_SETHOSTNAME} + LPFN_SETHOSTNAME = function(pName : PAnsiChar; len : Integer) : Integer; stdcall; +{$ENDIF} + {$EXTERNALSYM LPFN_GETSERVBYPORT} + LPFN_GETSERVBYPORT = function(const port: Integer; const proto: PAnsiChar): PServEnt; stdcall; + {$EXTERNALSYM LPFN_GETSERVBYNAME} + LPFN_GETSERVBYNAME = function(const name, proto: PAnsiChar): PServEnt; stdcall; + {$EXTERNALSYM LPFN_GETPROTOBYNUMBER} + LPFN_GETPROTOBYNUMBER = function(const proto: Integer): PProtoEnt; stdcall; + {$EXTERNALSYM LPFN_GETPROTOBYNAME} + LPFN_GETPROTOBYNAME = function(const name: PAnsiChar): PProtoEnt; stdcall; + {$EXTERNALSYM LPFN_WSASETLASTERROR} + LPFN_WSASETLASTERROR = procedure(const iError: Integer); stdcall; + {$EXTERNALSYM LPFN_WSAGETLASTERROR} + LPFN_WSAGETLASTERROR = function: Integer; stdcall; +{$IFNDEF WINCE} + {$EXTERNALSYM LPFN_WSACANCELASYNCREQUEST} + LPFN_WSACANCELASYNCREQUEST = function(hAsyncTaskHandle: THandle): Integer; stdcall; + {$EXTERNALSYM LPFN_WSAISBLOCKING} + LPFN_WSAISBLOCKING = function: BOOL; stdcall; + {$EXTERNALSYM LPFN_WSAUNHOOKBLOCKINGHOOK} + LPFN_WSAUNHOOKBLOCKINGHOOK = function: Integer; stdcall; + {$EXTERNALSYM LPFN_WSASETBLOCKINGHOOK} + LPFN_WSASETBLOCKINGHOOK = function(lpBlockFunc: TFarProc): TFarProc; stdcall; + {$EXTERNALSYM LPFN_WSACANCELBLOCKINGCALL} + LPFN_WSACANCELBLOCKINGCALL = function: Integer; stdcall; + {$EXTERNALSYM LPFN_WSAASYNCGETSERVBYNAME} + LPFN_WSAASYNCGETSERVBYNAME = function(HWindow: HWND; wMsg: u_int; name, proto, buf: PAnsiChar; buflen: Integer): THandle; stdcall; + {$EXTERNALSYM LPFN_WSAASYNCGETSERVBYPORT} + LPFN_WSAASYNCGETSERVBYPORT = function(HWindow: HWND; wMsg, port: u_int; proto, buf: PAnsiChar; buflen: Integer): THandle; stdcall; + {$EXTERNALSYM LPFN_WSAASYNCGETPROTOBYNAME} + LPFN_WSAASYNCGETPROTOBYNAME = function(HWindow: HWND; wMsg: u_int; name, buf: PAnsiChar; buflen: Integer): THandle; stdcall; + {$EXTERNALSYM LPFN_WSAASYNCGETPROTOBYNUMBER} + LPFN_WSAASYNCGETPROTOBYNUMBER = function(HWindow: HWND; wMsg: u_int; number: Integer; buf: PAnsiChar; buflen: Integer): THandle; stdcall; + {$EXTERNALSYM LPFN_WSAASYNCGETHOSTBYNAME} + LPFN_WSAASYNCGETHOSTBYNAME = function(HWindow: HWND; wMsg: u_int; name, buf: PAnsiChar; buflen: Integer): THandle; stdcall; + {$EXTERNALSYM LPFN_WSAASYNCGETHOSTBYADDR} + LPFN_WSAASYNCGETHOSTBYADDR = function(HWindow: HWND; wMsg: u_int; AAddr: PAnsiChar; len, istruct: Integer; buf: PAnsiChar; buflen: Integer): THandle; stdcall; + {$EXTERNALSYM LPFN_WSAASYNCSELECT} + LPFN_WSAASYNCSELECT = function(const s: TSocket; HWindow: HWND; wMsg: u_int; lEvent: Longint): Integer; stdcall; +{$ENDIF} + {$EXTERNALSYM LPFN___WSAFDISSET} + LPFN___WSAFDISSET = function(const s: TSocket; var FDSet: TFDSet): Bool; stdcall; + +// WinSock 2 API new function prototypes + {$EXTERNALSYM LPFN_WSAACCEPT} + LPFN_WSAACCEPT = function(const s : TSocket; AAddr : PSOCKADDR; addrlen : PInteger; lpfnCondition : LPCONDITIONPROC; const dwCallbackData : DWORD): TSocket; stdcall; + {$EXTERNALSYM LPFN_WSAENUMPROTOCOLSA} + LPFN_WSAENUMPROTOCOLSA = function(lpiProtocols : PInteger; lpProtocolBuffer : LPWSAPROTOCOL_INFOA; var lpdwBufferLength : DWORD) : Integer; stdcall; + {$EXTERNALSYM LPFN_WSAENUMPROTOCOLSW} + LPFN_WSAENUMPROTOCOLSW = function(lpiProtocols : PInteger; lpProtocolBuffer : LPWSAPROTOCOL_INFOW; var lpdwBufferLength : DWORD) : Integer; stdcall; + {$EXTERNALSYM LPFN_WSAGETOVERLAPPEDRESULT} + LPFN_WSAGETOVERLAPPEDRESULT = function(const s : TSocket; AOverlapped: Pointer; lpcbTransfer : LPDWORD; fWait : BOOL; var lpdwFlags : DWORD) : WordBool; stdcall; + {$EXTERNALSYM LPFN_WSAIOCTL} + LPFN_WSAIOCTL = function(const s : TSocket; dwIoControlCode : DWORD; lpvInBuffer : Pointer; cbInBuffer : DWORD; lpvOutBuffer : Pointer; cbOutBuffer : DWORD; + lpcbBytesReturned : LPDWORD; AOverlapped: Pointer; lpCompletionRoutine : LPWSAOVERLAPPED_COMPLETION_ROUTINE) : LongInt; stdcall; + {$EXTERNALSYM LPFN_WSARECVFROM} + LPFN_WSARECVFROM = function(const s : TSocket; lpBuffers : LPWSABUF; dwBufferCount : DWORD; var lpNumberOfBytesRecvd : DWORD; var lpFlags : DWORD; + lpFrom : PSOCKADDR; lpFromlen : PInteger; AOverlapped: Pointer; lpCompletionRoutine : LPWSAOVERLAPPED_COMPLETION_ROUTINE): Integer; stdcall; + + {$EXTERNALSYM LPFN_TRANSMITFILE} + LPFN_TRANSMITFILE = function(hSocket: TSocket; hFile: THandle; nNumberOfBytesToWrite, nNumberOfBytesPerSend: DWORD; + lpOverlapped: POverlapped; lpTransmitBuffers: LPTRANSMIT_FILE_BUFFERS; dwReserved: DWORD): BOOL; stdcall; + {$EXTERNALSYM LPFN_ACCEPTEX} + LPFN_ACCEPTEX = function(sListenSocket, sAcceptSocket: TSocket; + lpOutputBuffer: Pointer; dwReceiveDataLength, dwLocalAddressLength, + dwRemoteAddressLength: DWORD; var lpdwBytesReceived: DWORD; + lpOverlapped: POverlapped): BOOL; stdcall; + {$IFNDEF WINCE} + {$EXTERNALSYM LPFN_WSACONNECTBYLIST} + LPFN_WSACONNECTBYLIST = function(const s : TSocket; SocketAddressList : PSOCKET_ADDRESS_LIST; + var LocalAddressLength : DWORD; LocalAddress : LPSOCKADDR; + var RemoteAddressLength : DWORD; RemoteAddress : LPSOCKADDR; + timeout : Ptimeval; Reserved : LPWSAOVERLAPPED):LongBool; stdcall; + {$EXTERNALSYM LPFN_WSACONNECTBYNAMEA} + LPFN_WSACONNECTBYNAMEA = function(const s : TSOCKET; + nodename : PAnsiChar; servicename : PAnsiChar; + var LocalAddressLength : DWORD; LocalAddress : LPSOCKADDR; + var RemoteAddressLength : DWORD; RemoteAddress : LPSOCKADDR; + timeout : Ptimeval; Reserved : LPWSAOVERLAPPED) : Integer; stdcall; + {$EXTERNALSYM LPFN_WSACONNECTBYNAMEW} + LPFN_WSACONNECTBYNAMEW = function(const s : TSOCKET; + nodename : PWChar; servicename : PWChar; + var LocalAddressLength : DWORD; LocalAddress : LPSOCKADDR; + var RemoteAddressLength : DWORD; RemoteAddress : LPSOCKADDR; + timeout : Ptimeval; Reserved : LPWSAOVERLAPPED) : Integer; stdcall; + {$EXTERNALSYM LPFN_WSACONNECTBYNAME} + {$IFDEF UNICODE} + LPFN_WSACONNECTBYNAME = LPFN_WSACONNECTBYNAMEW; + {$ELSE} + LPFN_WSACONNECTBYNAME = LPFN_WSACONNECTBYNAMEA; + {$ENDIF} +{$ENDIF} + + {$EXTERNALSYM LPFN_WSAENUMPROTOCOLS} + {wince} + {$IFDEF UNICODE} + LPFN_WSAENUMPROTOCOLS = LPFN_WSAENUMPROTOCOLSW; + {$ELSE} + LPFN_WSAENUMPROTOCOLS = LPFN_WSAENUMPROTOCOLSA; + {$ENDIF} + + {$EXTERNALSYM LPFN_WSACLOSEEVENT} + LPFN_WSACLOSEEVENT = function(const hEvent : WSAEVENT) : WordBool; stdcall; + {$EXTERNALSYM LPFN_WSACONNECT} + LPFN_WSACONNECT = function(const s : TSocket; const name : PSOCKADDR; const namelen : Integer; lpCallerData, lpCalleeData : LPWSABUF; lpSQOS, lpGQOS : LPQOS) : Integer; stdcall; + + {$EXTERNALSYM LPFN_WSACREATEEVENT} + LPFN_WSACREATEEVENT = function: WSAEVENT; stdcall; + + {$IFNDEF WINCE} + {$EXTERNALSYM LPFN_WSADUPLICATESOCKETA} + LPFN_WSADUPLICATESOCKETA = function(const s : TSocket; const dwProcessId : DWORD; lpProtocolInfo : LPWSAPROTOCOL_INFOA) : Integer; stdcall; + {$EXTERNALSYM LPFN_WSADUPLICATESOCKETW} + LPFN_WSADUPLICATESOCKETW = function(const s : TSocket; const dwProcessId : DWORD; lpProtocolInfo : LPWSAPROTOCOL_INFOW) : Integer; stdcall; + {$EXTERNALSYM LPFN_WSADUPLICATESOCKET} + {$IFDEF UNICODE} + LPFN_WSADUPLICATESOCKET = LPFN_WSADUPLICATESOCKETW; + {$ELSE} + LPFN_WSADUPLICATESOCKET = LPFN_WSADUPLICATESOCKETA; + {$ENDIF} + {$ENDIF} + + {$EXTERNALSYM LPFN_WSAENUMNETWORKEVENTS} + LPFN_WSAENUMNETWORKEVENTS = function(const s : TSocket; const hEventObject : WSAEVENT; lpNetworkEvents : LPWSANETWORKEVENTS) :Integer; stdcall; + + {$EXTERNALSYM LPFN_WSAEVENTSELECT} + LPFN_WSAEVENTSELECT = function(const s : TSocket; const hEventObject : WSAEVENT; lNetworkEvents : LongInt): Integer; stdcall; + {$EXTERNALSYM LPFN_WSAGETQOSBYNAME} + LPFN_WSAGETQOSBYNAME = function(const s : TSocket; lpQOSName : LPWSABUF; lpQOS : LPQOS): WordBool; stdcall; + {$EXTERNALSYM LPFN_WSAHTONL} + LPFN_WSAHTONL = function(const s : TSocket; hostlong : u_long; var lpnetlong : DWORD): Integer; stdcall; + {$EXTERNALSYM LPFN_WSAHTONS} + LPFN_WSAHTONS = function(const s : TSocket; hostshort : u_short; var lpnetshort : WORD): Integer; stdcall; + + {$EXTERNALSYM LPFN_WSAJOINLEAF} + LPFN_WSAJOINLEAF = function(const s : TSocket; name : PSOCKADDR; namelen : Integer; lpCallerData, lpCalleeData : LPWSABUF; + lpSQOS,lpGQOS : LPQOS; dwFlags : DWORD) : TSocket; stdcall; + + {$EXTERNALSYM LPFN_WSANTOHL} + LPFN_WSANTOHL = function(const s : TSocket; netlong : u_long; var lphostlong : DWORD): Integer; stdcall; + {$EXTERNALSYM LPFN_WSANTOHS} + LPFN_WSANTOHS = function(const s : TSocket; netshort : u_short; var lphostshort : WORD): Integer; stdcall; + + {$EXTERNALSYM LPFN_WSARECV} + LPFN_WSARECV = function(const s : TSocket; lpBuffers : LPWSABUF; dwBufferCount : DWORD; var lpNumberOfBytesRecvd : DWORD; var lpFlags : DWORD; + lpOverlapped : LPWSAOVERLAPPED; lpCompletionRoutine : LPWSAOVERLAPPED_COMPLETION_ROUTINE): Integer; stdcall; + {$EXTERNALSYM LPFN_WSARECVDISCONNECT} + LPFN_WSARECVDISCONNECT = function(const s : TSocket; lpInboundDisconnectData : LPWSABUF): Integer; stdcall; + + {$EXTERNALSYM LPFN_WSARESETEVENT} + LPFN_WSARESETEVENT = function(hEvent : WSAEVENT): WordBool; stdcall; + + {$EXTERNALSYM LPFN_WSASEND} + LPFN_WSASEND = function(const s : TSocket; lpBuffers : LPWSABUF; dwBufferCount : DWORD; var lpNumberOfBytesSent : DWORD; dwFlags : DWORD; + lpOverlapped : LPWSAOVERLAPPED; lpCompletionRoutine : LPWSAOVERLAPPED_COMPLETION_ROUTINE): Integer; stdcall; + {$EXTERNALSYM LPFN_WSASENDDISCONNECT} + LPFN_WSASENDDISCONNECT = function(const s : TSocket; lpOutboundDisconnectData : LPWSABUF): Integer; stdcall; + {$EXTERNALSYM LPFN_WSASENDTO} + LPFN_WSASENDTO = function(const s : TSocket; lpBuffers : LPWSABUF; dwBufferCount : DWORD; var lpNumberOfBytesSent : DWORD; dwFlags : DWORD; + lpTo : LPSOCKADDR; iTolen : Integer; lpOverlapped : LPWSAOVERLAPPED; lpCompletionRoutine : LPWSAOVERLAPPED_COMPLETION_ROUTINE): Integer; stdcall; + + {$EXTERNALSYM LPFN_WSASETEVENT} + LPFN_WSASETEVENT = function(hEvent : WSAEVENT): WordBool; stdcall; + + {$EXTERNALSYM LPFN_WSASOCKETA} + LPFN_WSASOCKETA = function(af, iType, protocol : Integer; lpProtocolInfo : LPWSAPROTOCOL_INFOA; g : GROUP; dwFlags : DWORD): TSocket; stdcall; + {$EXTERNALSYM LPFN_WSASOCKETW} + LPFN_WSASOCKETW = function(af, iType, protocol : Integer; lpProtocolInfo : LPWSAPROTOCOL_INFOW; g : GROUP; dwFlags : DWORD): TSocket; stdcall; + {$EXTERNALSYM LPFN_WSASOCKET} + {$IFDEF UNICODE} + LPFN_WSASOCKET = LPFN_WSASOCKETW; + {$ELSE} + LPFN_WSASOCKET = LPFN_WSASOCKETA; + {$ENDIF} + + {$EXTERNALSYM LPFN_WSAWAITFORMULTIPLEEVENTS} + LPFN_WSAWAITFORMULTIPLEEVENTS = function(cEvents : DWORD; lphEvents : PWSAEVENT; fWaitAll : LongBool; + dwTimeout : DWORD; fAlertable : LongBool): DWORD; stdcall; + + {$EXTERNALSYM LPFN_WSAADDRESSTOSTRINGA} + LPFN_WSAADDRESSTOSTRINGA = function(lpsaAddress : PSOCKADDR; const dwAddressLength : DWORD; const lpProtocolInfo : LPWSAPROTOCOL_INFOA; + const lpszAddressString : PAnsiChar; var lpdwAddressStringLength : DWORD): Integer; stdcall; + {$EXTERNALSYM LPFN_WSAADDRESSTOSTRINGW} + LPFN_WSAADDRESSTOSTRINGW = function(lpsaAddress : PSOCKADDR; const dwAddressLength : DWORD; const lpProtocolInfo : LPWSAPROTOCOL_INFOW; + const lpszAddressString : PWideChar; var lpdwAddressStringLength : DWORD): Integer; stdcall; + {$EXTERNALSYM LPFN_WSAADDRESSTOSTRING} + {$IFDEF UNICODE} + LPFN_WSAADDRESSTOSTRING = LPFN_WSAADDRESSTOSTRINGW; + {$ELSE} + LPFN_WSAADDRESSTOSTRING = LPFN_WSAADDRESSTOSTRINGA; + {$ENDIF} + + {$EXTERNALSYM LPFN_WSASTRINGTOADDRESSA} + LPFN_WSASTRINGTOADDRESSA = function(const AddressString : PAnsiChar; const AddressFamily: Integer; const lpProtocolInfo : LPWSAPROTOCOL_INFOA; + var lpAddress : TSockAddr; var lpAddressLength : Integer): Integer; stdcall; + {$EXTERNALSYM LPFN_WSASTRINGTOADDRESSW} + LPFN_WSASTRINGTOADDRESSW = function(const AddressString : PWideChar; const AddressFamily: Integer; const lpProtocolInfo : LPWSAPROTOCOL_INFOW; + var lpAddress : TSockAddr; var lpAddressLength : Integer): Integer; stdcall; + {$EXTERNALSYM LPFN_WSASTRINGTOADDRESS} + {$IFDEF UNICODE} + LPFN_WSASTRINGTOADDRESS = LPFN_WSASTRINGTOADDRESSW; + {$ELSE} + LPFN_WSASTRINGTOADDRESS = LPFN_WSASTRINGTOADDRESSA; + {$ENDIF} + +// Registration and Name Resolution API functions + {$EXTERNALSYM LPFN_WSALOOKUPSERVICEBEGINA} + LPFN_WSALOOKUPSERVICEBEGINA = function(var qsRestrictions : TWSAQuerySetA; const dwControlFlags : DWORD; var hLookup : THandle): Integer; stdcall; + {$EXTERNALSYM LPFN_WSALOOKUPSERVICEBEGINw} + LPFN_WSALOOKUPSERVICEBEGINW = function(var qsRestrictions : TWSAQuerySetW; const dwControlFlags : DWORD; var hLookup : THandle): Integer; stdcall; + {$EXTERNALSYM LPFN_WSALOOKUPSERVICEBEGIN} + {$IFDEF UNICODE} + LPFN_WSALOOKUPSERVICEBEGIN = LPFN_WSALOOKUPSERVICEBEGINW; + {$ELSE} + LPFN_WSALOOKUPSERVICEBEGIN = LPFN_WSALOOKUPSERVICEBEGINA; + {$ENDIF} + + {$EXTERNALSYM LPFN_WSALOOKUPSERVICENEXTA} + LPFN_WSALOOKUPSERVICENEXTA = function(const hLookup : THandle; const dwControlFlags : DWORD; var dwBufferLength : DWORD; lpqsResults : PWSAQUERYSETA): Integer; stdcall; + {$EXTERNALSYM LPFN_WSALOOKUPSERVICENEXTW} + LPFN_WSALOOKUPSERVICENEXTW = function(const hLookup : THandle; const dwControlFlags : DWORD; var dwBufferLength : DWORD; lpqsResults : PWSAQUERYSETW): Integer; stdcall; + {$EXTERNALSYM LPFN_WSALOOKUPSERVICENEXT} + {$IFDEF UNICODE} + LPFN_WSALOOKUPSERVICENEXT = LPFN_WSALOOKUPSERVICENEXTW; + {$ELSE} + LPFN_WSALOOKUPSERVICENEXT = LPFN_WSALOOKUPSERVICENEXTA; + {$ENDIF} + + //WinCE 4.20 doesn't support WSANSPIoctl but later versions do. + {$EXTERNALSYM LPFN_WSANSPIOCTL} + LPFN_WSANSPIOCTL = function(const hLookup : THANDLE; const dwControlCode : DWORD; lpvInBuffer : Pointer; var cbInBuffer : DWORD; lpvOutBuffer : Pointer; var cbOutBuffer : DWORD; var lpcbBytesReturned : DWORD; lpCompletion : LPWSACOMPLETION) : Integer; stdcall; + + {$EXTERNALSYM LPFN_WSALOOKUPSERVICEEND} + LPFN_WSALOOKUPSERVICEEND = function(const hLookup : THandle): Integer; stdcall; + + + {$EXTERNALSYM LPFN_WSAINSTALLSERVICECLASSA} + LPFN_WSAINSTALLSERVICECLASSA = function(const lpServiceClassInfo : LPWSASERVICECLASSINFOA) : Integer; stdcall; + {$EXTERNALSYM LPFN_WSAINSTALLSERVICECLASSW} + LPFN_WSAINSTALLSERVICECLASSW = function(const lpServiceClassInfo : LPWSASERVICECLASSINFOW) : Integer; stdcall; + {$EXTERNALSYM LPFN_WSAINSTALLSERVICECLASS} + {$IFDEF UNICODE} + LPFN_WSAINSTALLSERVICECLASS = LPFN_WSAINSTALLSERVICECLASSW; + {$ELSE} + LPFN_WSAINSTALLSERVICECLASS = LPFN_WSAINSTALLSERVICECLASSA; + {$ENDIF} + + {$EXTERNALSYM LPFN_WSAREMOVESERVICECLASS} + LPFN_WSAREMOVESERVICECLASS = function(const lpServiceClassId : LPGUID) : Integer; stdcall; + + {$EXTERNALSYM LPFN_WSAGETSERVICECLASSINFOA} + LPFN_WSAGETSERVICECLASSINFOA = function(const lpProviderId : LPGUID; const lpServiceClassId : LPGUID; var lpdwBufSize : DWORD; + lpServiceClassInfo : LPWSASERVICECLASSINFOA): Integer; stdcall; + {$EXTERNALSYM LPFN_WSAGETSERVICECLASSINFOW} + LPFN_WSAGETSERVICECLASSINFOW = function(const lpProviderId : LPGUID; const lpServiceClassId : LPGUID; var lpdwBufSize : DWORD; + lpServiceClassInfo : LPWSASERVICECLASSINFOW): Integer; stdcall; + {$EXTERNALSYM LPFN_WSAGETSERVICECLASSINFO} + {$IFDEF UNICODE} + LPFN_WSAGETSERVICECLASSINFO = LPFN_WSAGETSERVICECLASSINFOW; + {$ELSE} + LPFN_WSAGETSERVICECLASSINFO = LPFN_WSAGETSERVICECLASSINFOA; + {$ENDIF} + {$ENDIF} + + {$EXTERNALSYM LPFN_WSAENUMNAMESPACEPROVIDERSA} + LPFN_WSAENUMNAMESPACEPROVIDERSA = function(var lpdwBufferLength: DWORD; const lpnspBuffer: LPWSANAMESPACE_INFOA): Integer; stdcall; + {$EXTERNALSYM LPFN_WSAENUMNAMESPACEPROVIDERSW} + LPFN_WSAENUMNAMESPACEPROVIDERSW = function(var lpdwBufferLength: DWORD; const lpnspBuffer: LPWSANAMESPACE_INFOW): Integer; stdcall; + {$EXTERNALSYM LPFN_WSAENUMNAMESPACEPROVIDERS} + {$IFDEF UNICODE} + LPFN_WSAENUMNAMESPACEPROVIDERS = LPFN_WSAENUMNAMESPACEPROVIDERSW; + {$ELSE} + LPFN_WSAENUMNAMESPACEPROVIDERS = LPFN_WSAENUMNAMESPACEPROVIDERSA; + {$ENDIF} + + {$EXTERNALSYM LPFN_WSAGETSERVICECLASSNAMEBYCLASSIDA} + LPFN_WSAGETSERVICECLASSNAMEBYCLASSIDA = function(const lpServiceClassId: LPGUID; lpszServiceClassName: PAnsiChar; var lpdwBufferLength: DWORD): Integer; stdcall; + {$EXTERNALSYM LPFN_WSAGETSERVICECLASSNAMEBYCLASSIDW} + LPFN_WSAGETSERVICECLASSNAMEBYCLASSIDW = function(const lpServiceClassId: LPGUID; lpszServiceClassName: PWideChar; var lpdwBufferLength: DWORD): Integer; stdcall; + {$EXTERNALSYM LPFN_WSAGETSERVICECLASSNAMEBYCLASSID} + {$IFDEF UNICODE} + LPFN_WSAGETSERVICECLASSNAMEBYCLASSID = LPFN_WSAGETSERVICECLASSNAMEBYCLASSIDW; + {$ELSE} + LPFN_WSAGETSERVICECLASSNAMEBYCLASSID = LPFN_WSAGETSERVICECLASSNAMEBYCLASSIDA; + {$ENDIF} + + {$EXTERNALSYM LPFN_WSASETSERVICEA} + LPFN_WSASETSERVICEA = function(const lpqsRegInfo: LPWSAQUERYSETA; const essoperation: WSAESETSERVICEOP; const dwControlFlags: DWORD): Integer; stdcall; + {$EXTERNALSYM LPFN_WSASETSERVICEW} + LPFN_WSASETSERVICEW = function(const lpqsRegInfo: LPWSAQUERYSETW; const essoperation: WSAESETSERVICEOP; const dwControlFlags: DWORD): Integer; stdcall; + {$EXTERNALSYM LPFN_WSASETSERVICE} + {$IFDEF UNICODE} + LPFN_WSASETSERVICE = LPFN_WSASETSERVICEW; + {$ELSE} + LPFN_WSASETSERVICE = LPFN_WSASETSERVICEA; + {$ENDIF} + + {$EXTERNALSYM LPFN_WSAPROVIDERCONFIGCHANGE} + LPFN_WSAPROVIDERCONFIGCHANGE = function(var lpNotificationHandle : THandle; lpOverlapped : LPWSAOVERLAPPED; lpCompletionRoutine : LPWSAOVERLAPPED_COMPLETION_ROUTINE) : Integer; stdcall; + + //microsoft specific extension + {$EXTERNALSYM LPFN_GETACCEPTEXSOCKADDRS} + LPFN_GETACCEPTEXSOCKADDRS = procedure(lpOutputBuffer: Pointer; + dwReceiveDataLength, dwLocalAddressLength, dwRemoteAddressLength: DWORD; + var LocalSockaddr: PSockAddr; var LocalSockaddrLength: Integer; + var RemoteSockaddr: PSockAddr; var RemoteSockaddrLength: Integer); stdcall; + + {$IFNDEF WINCE} + //This is defined in the Winapi.Windows Mobile 6 Standard SDK Refresh + //but I'm not sure what .DLL the function is in. I also couldn't find a WSAID + //constant for it. + {$EXTERNALSYM LPFN_WSARECVEX} + LPFN_WSARECVEX = function(s: TSocket; var buf; len: Integer; var flags: Integer): Integer; stdcall; + {$ENDIF} + + //Winapi.Windows Server 2003, Winapi.Windows Vista + {$EXTERNALSYM LPFN_CONNECTEX} + LPFN_CONNECTEX = function(const s : TSocket; const name: PSOCKADDR; const namelen: Integer; lpSendBuffer : Pointer; dwSendDataLength : DWORD; var lpdwBytesSent : DWORD; lpOverlapped : LPWSAOVERLAPPED) : BOOL; stdcall; + {$EXTERNALSYM LPFN_DISCONNECTEX} + LPFN_DISCONNECTEX = function(const hSocket : TSocket; AOverlapped: Pointer; const dwFlags : DWORD; const dwReserved : DWORD) : BOOL; stdcall; + {$EXTERNALSYM LPFN_WSARECVMSG} //XP and Server 2003 only + LPFN_WSARECVMSG = function(const s : TSocket; lpMsg : LPWSAMSG; var lpNumberOfBytesRecvd : DWORD; AOverlapped: Pointer; lpCompletionRoutine : LPWSAOVERLAPPED_COMPLETION_ROUTINE): Integer; stdcall; + {$EXTERNALSYM LPFN_TRANSMITPACKETS} + LPFN_TRANSMITPACKETS = function(s: TSocket; lpPacketArray: LPTRANSMIT_PACKETS_ELEMENT; nElementCount: DWORD; nSendSize: DWORD; lpOverlapped: LPWSAOVERLAPPED; dwFlags: DWORD): BOOL; stdcall; + //Winapi.Windows Vista, Winapi.Windows Server 2008 +{$IFNDEF WINCE} + {$EXTERNALSYM LPFN_WSASENDMSG} + LPFN_WSASENDMSG = function(const s : TSocket; lpMsg : LPWSAMSG; const dwFlags : DWORD; var lpNumberOfBytesSent : DWORD; lpOverlapped : LPWSAOVERLAPPED; lpCompletionRoutine : LPWSAOVERLAPPED_COMPLETION_ROUTINE) : Integer; stdcall; + {$EXTERNALSYM LPFN_WSAPOLL} + LPFN_WSAPOLL = function(fdarray : LPWSAPOLLFD; const nfds : u_long; const timeout : Integer) : Integer; stdcall; +{$ENDIF} // $IFDEF INCL_WINSOCK_API_TYPEDEFS + + +const + //GUID's for Microsoft extensions + {$EXTERNALSYM WSAID_ACCEPTEX} + WSAID_ACCEPTEX: TGuid = (D1:$b5367df1;D2:$cbac;D3:$11cf;D4:($95,$ca,$00,$80,$5f,$48,$a1,$92)); + {$EXTERNALSYM WSAID_CONNECTEX} + WSAID_CONNECTEX: TGuid = (D1:$25a207b9;D2:$ddf3;D3:$4660;D4:($8e,$e9,$76,$e5,$8c,$74,$06,$3e)); + {$EXTERNALSYM WSAID_DISCONNECTEX} + WSAID_DISCONNECTEX: TGuid = (D1:$7fda2e11;D2:$8630;D3:$436f;D4:($a0,$31,$f5,$36,$a6,$ee,$c1,$57)); + {$EXTERNALSYM WSAID_GETACCEPTEXSOCKADDRS} + WSAID_GETACCEPTEXSOCKADDRS: TGuid = (D1:$b5367df2;D2:$cbac;D3:$11cf;D4:($95,$ca,$00,$80,$5f,$48,$a1,$92)); + {$EXTERNALSYM WSAID_TRANSMITFILE} + WSAID_TRANSMITFILE: TGuid = (D1:$b5367df0; D2:$cbac; D3:$11cf; D4:($95, $ca, $00, $80, $5f, $48, $a1, $92)); + {$EXTERNALSYM WSAID_TRANSMITPACKETS} + WSAID_TRANSMITPACKETS: TGuid = (D1:$d9689da0;D2:$1f90;D3:$11d3;D4:($99,$71,$00,$c0,$4f,$68,$c8,$76)); +{$IFNDEF WINCE} + {$EXTERNALSYM WSAID_WSAPOLL} + WSAID_WSAPOLL: TGuid = (D1:$18C76F85;D2:$DC66;D3:$4964;D4:($97,$2E,$23,$C2,$72,$38,$31,$2B)); +{$ENDIF} + {$EXTERNALSYM WSAID_WSARECVMSG} + WSAID_WSARECVMSG: TGuid = (D1:$f689d7c8;D2:$6f1f;D3:$436b;D4:($8a,$53,$e5,$4f,$e3,$51,$c3,$22)); +{$IFNDEF WINCE} + {$EXTERNALSYM WSAID_WSASENDMSG} + WSAID_WSASENDMSG : TGuid = (D1:$a441e712;D2:$754f;D3:$43ca;D4:($84,$a7,$0d,$ee,$44,$cf,$60,$6d)); +{$ENDIF} + +{$IFDEF WS2_DLL_FUNC_VARS} +var + {$EXTERNALSYM WSAStartup} + WSAStartup : LPFN_WSASTARTUP = nil; + {$EXTERNALSYM WSACleanup} + WSACleanup : LPFN_WSACLEANUP = nil; + {$EXTERNALSYM accept} + accept : LPFN_ACCEPT = nil; + {$EXTERNALSYM bind} + bind : LPFN_BIND = nil; + {$EXTERNALSYM closesocket} + closesocket : LPFN_CLOSESOCKET = nil; + {$EXTERNALSYM connect} + connect : LPFN_CONNECT = nil; + {$EXTERNALSYM ioctlsocket} + ioctlsocket : LPFN_IOCTLSOCKET = nil; + {$EXTERNALSYM getpeername} + getpeername : LPFN_GETPEERNAME = nil; + {$EXTERNALSYM getsockname} + getsockname : LPFN_GETSOCKNAME = nil; + {$EXTERNALSYM getsockopt} + getsockopt : LPFN_GETSOCKOPT = nil; + {$EXTERNALSYM htonl} + htonl : LPFN_HTONL = nil; + {$EXTERNALSYM htons} + htons : LPFN_HTONS = nil; + {$EXTERNALSYM inet_addr} + inet_addr : LPFN_INET_ADDR = nil; + {$EXTERNALSYM inet_ntoa} + inet_ntoa : LPFN_INET_NTOA = nil; + {$EXTERNALSYM listen} + listen : LPFN_LISTEN = nil; + {$EXTERNALSYM ntohl} + ntohl : LPFN_NTOHL = nil; + {$EXTERNALSYM ntohs} + ntohs : LPFN_NTOHS = nil; + {$EXTERNALSYM recv} + recv : LPFN_RECV = nil; + {$EXTERNALSYM recvfrom} + recvfrom : LPFN_RECVFROM = nil; + {$EXTERNALSYM select} + select : LPFN_SELECT = nil; + {$EXTERNALSYM send} + send : LPFN_SEND = nil; + {$EXTERNALSYM sendto} + sendto : LPFN_SENDTO = nil; + {$EXTERNALSYM setsockopt} + setsockopt : LPFN_SETSOCKOPT = nil; + {$EXTERNALSYM shutdown} + shutdown : LPFN_SHUTDOWN = nil; + {$EXTERNALSYM socket} + socket : LPFN_SOCKET = nil; + {$EXTERNALSYM gethostbyaddr} + gethostbyaddr : LPFN_GETHOSTBYADDR = nil; + {$EXTERNALSYM gethostbyname} + gethostbyname : LPFN_GETHOSTBYNAME = nil; + {$EXTERNALSYM gethostname} + gethostname : LPFN_GETHOSTNAME = nil; + {$IFDEF WINCE} + {$EXTERNALSYM sethostname} + sethostname : LPFN_SETHOSTNAME = nil; + {$ENDIF} + {$EXTERNALSYM getservbyport} + getservbyport : LPFN_GETSERVBYPORT = nil; + {$EXTERNALSYM getservbyname} + getservbyname : LPFN_GETSERVBYNAME = nil; + {$EXTERNALSYM getprotobynumber} + getprotobynumber : LPFN_GETPROTOBYNUMBER = nil; + {$EXTERNALSYM getprotobyname} + getprotobyname : LPFN_GETPROTOBYNAME = nil; + {$EXTERNALSYM WSASetLastError} + WSASetLastError : LPFN_WSASETLASTERROR = nil; + {$EXTERNALSYM WSAGetLastError} + WSAGetLastError : LPFN_WSAGETLASTERROR = nil; +{$IFNDEF WINCE} + {$EXTERNALSYM WSAIsblocking} + WSAIsBlocking : LPFN_WSAISBLOCKING = nil; + {$EXTERNALSYM WSAUnhookBlockingHook} + WSAUnhookBlockingHook : LPFN_WSAUNHOOKBLOCKINGHOOK = nil; + {$EXTERNALSYM WSASetBlockingHook} + WSASetBlockingHook : LPFN_WSASETBLOCKINGHOOK = nil; + {$EXTERNALSYM WSACancelBlockingCall} + WSACancelBlockingCall : LPFN_WSACANCELBLOCKINGCALL = nil; + {$EXTERNALSYM WSAAsyncGetServByName} + WSAAsyncGetServByName : LPFN_WSAASYNCGETSERVBYNAME = nil; + {$EXTERNALSYM WSAAsyncGetServByPort} + WSAAsyncGetServByPort : LPFN_WSAASYNCGETSERVBYPORT = nil; + {$EXTERNALSYM WSAAsyncGetProtoByName} + WSAAsyncGetProtoByName : LPFN_WSAASYNCGETPROTOBYNAME = nil; + {$EXTERNALSYM WSAAsyncGetProtoByNumber} + WSAAsyncGetProtoByNumber : LPFN_WSAASYNCGETPROTOBYNUMBER = nil; + {$EXTERNALSYM WSAAsyncGetHostByName} + WSAAsyncGetHostByName : LPFN_WSAASYNCGETHOSTBYNAME = nil; + {$EXTERNALSYM WSAAsyncGetHostByAddr} + WSAAsyncGetHostByAddr : LPFN_WSAASYNCGETHOSTBYADDR = nil; + {$EXTERNALSYM WSACancelAsyncRequest} + WSACancelAsyncRequest : LPFN_WSACANCELASYNCREQUEST = nil; + {$EXTERNALSYM WSAAsyncSelect} + WSAAsyncSelect : LPFN_WSAASYNCSELECT = nil; +{$ENDIF} + {$EXTERNALSYM __WSAFDIsSet} + __WSAFDIsSet : LPFN___WSAFDISSET = nil; + {$EXTERNALSYM WSAAccept} + WSAAccept : LPFN_WSAACCEPT = nil; + {$EXTERNALSYM WSAAddressToStringA} + WSAAddressToStringA : LPFN_WSAADDRESSTOSTRINGA = nil; + {$EXTERNALSYM WSAAddressToStringW} + WSAAddressToStringW : LPFN_WSAADDRESSTOSTRINGW = nil; + {$EXTERNALSYM WSAAddressToString} + WSAAddressToString : LPFN_WSAADDRESSTOSTRING = nil; + {$EXTERNALSYM WSACloseEvent} + WSACloseEvent : LPFN_WSACLOSEEVENT = nil; + {$EXTERNALSYM WSAConnect} + WSAConnect : LPFN_WSACONNECT = nil; + {$EXTERNALSYM WSACreateEvent} + WSACreateEvent : LPFN_WSACREATEEVENT = nil; + {$IFNDEF WINCE} + {$EXTERNALSYM WSADuplicateSocketA} + WSADuplicateSocketA : LPFN_WSADUPLICATESOCKETA = nil; + {$EXTERNALSYM WSADuplicateSocketW} + WSADuplicateSocketW : LPFN_WSADUPLICATESOCKETW = nil; + {$EXTERNALSYM WSADuplicateSocket} + WSADuplicateSocket : LPFN_WSADUPLICATESOCKET = nil; + {$ENDIF} + {$EXTERNALSYM WSAEnumNetworkEvents} + WSAEnumNetworkEvents : LPFN_WSAENUMNETWORKEVENTS = nil; + {$EXTERNALSYM WSAEnumProtocolsA} + WSAEnumProtocolsA : LPFN_WSAENUMPROTOCOLSA = nil; + {$EXTERNALSYM WSAEnumProtocolsW} + WSAEnumProtocolsW : LPFN_WSAENUMPROTOCOLSW = nil; + {$EXTERNALSYM WSAEnumProtocols} + WSAEnumProtocols : LPFN_WSAENUMPROTOCOLS = nil; + {$EXTERNALSYM WSAEnumNameSpaceProvidersA} + WSAEnumNameSpaceProvidersA : LPFN_WSAENUMNAMESPACEPROVIDERSA = nil; + {$EXTERNALSYM WSAEnumNameSpaceProvidersW} + WSAEnumNameSpaceProvidersW : LPFN_WSAENUMNAMESPACEPROVIDERSW = nil; + {$EXTERNALSYM WSAEnumNameSpaceProviders} + WSAEnumNameSpaceProviders : LPFN_WSAENUMNAMESPACEPROVIDERS = nil; + {$EXTERNALSYM WSAEventSelect} + WSAEventSelect : LPFN_WSAEVENTSELECT = nil; + {$EXTERNALSYM WSAGetOverlappedResult} + WSAGetOverlappedResult : LPFN_WSAGETOVERLAPPEDRESULT = nil; + + {$EXTERNALSYM WSAGetQosByName} + WSAGetQosByName : LPFN_WSAGETQOSBYNAME = nil; + {$EXTERNALSYM WSAGetServiceClassInfoA} + WSAGetServiceClassInfoA : LPFN_WSAGETSERVICECLASSINFOA = nil; + {$EXTERNALSYM WSAGetServiceClassInfoW} + WSAGetServiceClassInfoW : LPFN_WSAGETSERVICECLASSINFOW = nil; + {$EXTERNALSYM WSAGetServiceClassInfo} + WSAGetServiceClassInfo : LPFN_WSAGETSERVICECLASSINFO = nil; + {$EXTERNALSYM WSAGetServiceClassNameByClassIdA} + WSAGetServiceClassNameByClassIdA : LPFN_WSAGETSERVICECLASSNAMEBYCLASSIDA = nil; + {$EXTERNALSYM WSAGetServiceClassNameByClassIdW} + WSAGetServiceClassNameByClassIdW : LPFN_WSAGETSERVICECLASSNAMEBYCLASSIDW = nil; + {$EXTERNALSYM WSAGetServiceClassNameByClassId} + WSAGetServiceClassNameByClassId : LPFN_WSAGETSERVICECLASSNAMEBYCLASSID = nil; + + {$EXTERNALSYM WSAHtonl} + WSAHtonl : LPFN_WSAHTONL = nil; + {$EXTERNALSYM WSAHtons} + WSAHtons : LPFN_WSAHTONS = nil; + {$EXTERNALSYM WSAIoctl} + WSAIoctl : LPFN_WSAIOCTL = nil; + {$EXTERNALSYM WSAInstallServiceClassA} + WSAInstallServiceClassA : LPFN_WSAINSTALLSERVICECLASSA = nil; + {$EXTERNALSYM WSAInstallServiceClassW} + WSAInstallServiceClassW : LPFN_WSAINSTALLSERVICECLASSW = nil; + {$EXTERNALSYM WSAInstallServiceClass} + WSAInstallServiceClass : LPFN_WSAINSTALLSERVICECLASS = nil; + {$EXTERNALSYM WSAJoinLeaf} + WSAJoinLeaf : LPFN_WSAJOINLEAF = nil; + {$EXTERNALSYM WSALookupServiceBeginA} + WSALookupServiceBeginA : LPFN_WSALOOKUPSERVICEBEGINA = nil; + {$EXTERNALSYM WSALookupServiceBeginW} + WSALookupServiceBeginW : LPFN_WSALOOKUPSERVICEBEGINW = nil; + {$EXTERNALSYM WSALookupServiceBegin} + WSALookupServiceBegin : LPFN_WSALOOKUPSERVICEBEGIN = nil; + {$EXTERNALSYM WSALookupServiceEnd} + WSALookupServiceEnd : LPFN_WSALOOKUPSERVICEEND = nil; + {$EXTERNALSYM WSALookupServiceNextA} + WSALookupServiceNextA : LPFN_WSALOOKUPSERVICENEXTA = nil; + {$EXTERNALSYM WSALookupServiceNextW} + WSALookupServiceNextW : LPFN_WSALOOKUPSERVICENEXTW = nil; + {$EXTERNALSYM WSALookupServiceNext} + WSALookupServiceNext : LPFN_WSALOOKUPSERVICENEXT = nil; + {$EXTERNALSYM WSANtohl} + WSANtohl : LPFN_WSANTOHL = nil; + {$EXTERNALSYM WSANtohs} + WSANtohs : LPFN_WSANTOHS = nil; + {$EXTERNALSYM WSARecv} + WSARecv : LPFN_WSARECV = nil; + {$EXTERNALSYM WSARecvDisconnect} + WSARecvDisconnect : LPFN_WSARECVDISCONNECT = nil; + {$EXTERNALSYM WSARecvFrom} + WSARecvFrom : LPFN_WSARECVFROM = nil; + {$EXTERNALSYM WSARemoveServiceClass} + WSARemoveServiceClass : LPFN_WSAREMOVESERVICECLASS = nil; + {$EXTERNALSYM WSAResetEvent} + WSAResetEvent : LPFN_WSARESETEVENT = nil; + {$EXTERNALSYM WSASend} + WSASend : LPFN_WSASEND = nil; + {$EXTERNALSYM WSASendDisconnect} + WSASendDisconnect : LPFN_WSASENDDISCONNECT = nil; + {$EXTERNALSYM WSASendTo} + WSASendTo : LPFN_WSASENDTO = nil; + {$EXTERNALSYM WSASetEvent} + WSASetEvent : LPFN_WSASETEVENT = nil; + {$EXTERNALSYM WSASetServiceA} + WSASetServiceA : LPFN_WSASETSERVICEA = nil; + {$EXTERNALSYM WSASetServiceW} + WSASetServiceW : LPFN_WSASETSERVICEW = nil; + {$EXTERNALSYM WSASetService} + WSASetService : LPFN_WSASETSERVICE = nil; + {$EXTERNALSYM WSASocketA} + WSASocketA : LPFN_WSASOCKETA = nil; + {$EXTERNALSYM WSASocketW} + WSASocketW : LPFN_WSASOCKETW = nil; + {$EXTERNALSYM WSASocket} + WSASocket : LPFN_WSASOCKET = nil; + {$EXTERNALSYM WSAStringToAddressA} + WSAStringToAddressA : LPFN_WSASTRINGTOADDRESSA = nil; + {$EXTERNALSYM WSAStringToAddressW} + WSAStringToAddressW : LPFN_WSASTRINGTOADDRESSW = nil; + {$EXTERNALSYM WSAStringToAddress} + WSAStringToAddress : LPFN_WSASTRINGTOADDRESS = nil; + + {$EXTERNALSYM WSAWaitForMultipleEvents} + WSAWaitForMultipleEvents : LPFN_WSAWAITFORMULTIPLEEVENTS = nil; + {$EXTERNALSYM WSAProviderConfigChange} + WSAProviderConfigChange : LPFN_WSAPROVIDERCONFIGCHANGE = nil; + {$EXTERNALSYM TransmitFile} + TransmitFile : LPFN_TRANSMITFILE = nil; + {$EXTERNALSYM AcceptEx} + AcceptEx : LPFN_ACCEPTEX = nil; + {$EXTERNALSYM GetAcceptExSockaddrs} + GetAcceptExSockaddrs : LPFN_GETACCEPTEXSOCKADDRS = nil; + {$IFNDEF WINCE} + //This is defined in the Winapi.Windows Mobile 6 Standard SDK Refresh + //but I'm not sure what .DLL the function is in. I also couldn't find a WSAID + //constant for it. + {$EXTERNALSYM WSARecvEx} + WSARecvEx : LPFN_WSARECVEX = nil; + {$ENDIF} + {$EXTERNALSYM ConnectEx} + ConnectEx : LPFN_CONNECTEX = nil; + {$EXTERNALSYM DisconnectEx} + DisconnectEx : LPFN_DISCONNECTEX = nil; + {$EXTERNALSYM WSARecvMsg} + WSARecvMsg : LPFN_WSARECVMSG = nil; + {$EXTERNALSYM TransmitPackets} + TransmitPackets : LPFN_TRANSMITPACKETS = nil; + {$IFNDEF WINCE} + //Winapi.Windows Vista, Winapi.Windows Server 2008 + {$EXTERNALSYM WSASendMsg} + WSASendMsg: LPFN_WSASENDMSG = nil; + {$EXTERNALSYM WSAPoll} + WSAPoll: LPFN_WSAPOLL = nil; + {$ENDIF} + //WSANSPIoctl is not supported in WinCE 4.20 but is supported in later versions. + {$EXTERNALSYM WSANSPIoctl} + WSANSPIoctl : LPFN_WSANSPIOCTL = nil; +{$ENDIF} // $IFDEF WS2_DLL_FUNC_VARS + + { Macros } + {$EXTERNALSYM WSAMakeSyncReply} + function WSAMakeSyncReply(Buflen, AError: Word): Longint; + {$EXTERNALSYM WSAMakeSelectReply} + function WSAMakeSelectReply(Event, AError: Word): Longint; + {$EXTERNALSYM WSAGetAsyncBuflen} + function WSAGetAsyncBuflen(Param: Longint): Word; + {$EXTERNALSYM WSAGetAsyncError} + function WSAGetAsyncError(Param: Longint): Word; + {$EXTERNALSYM WSAGetSelectEvent} + function WSAGetSelectEvent(Param: Longint): Word; + {$EXTERNALSYM WSAGetSelectError} + function WSAGetSelectError(Param: Longint): Word; + + {$EXTERNALSYM FD_CLR} + procedure FD_CLR(ASocket: TSocket; var FDSet: TFDSet); + {$EXTERNALSYM FD_ISSET} + function FD_ISSET(ASocket: TSocket; var FDSet: TFDSet): Boolean; + {$EXTERNALSYM FD_SET} + procedure FD_SET(ASocket: TSocket; var FDSet: TFDSet); + {$EXTERNALSYM FD_ZERO} + procedure FD_ZERO(var FDSet: TFDSet); + + {$IFNDEF WINCE} +//Posix aliases for helper macros +// #define CMSGHDR_ALIGN WSA_CMSGHDR_ALIGN + {$EXTERNALSYM CMSGHDR_ALIGN} + function CMSGHDR_ALIGN(const Alength: SIZE_T): SIZE_T; +// #define CMSGDATA_ALIGN WSA_CMSGDATA_ALIGN + {$EXTERNALSYM CMSGDATA_ALIGN} + function CMSGDATA_ALIGN(const Alength: UINT_PTR): UINT_PTR; +//#define CMSG_FIRSTHDR WSA_CMSG_FIRSTHDR + {$EXTERNALSYM CMSG_FIRSTHDR} + function CMSG_FIRSTHDR(const msg: LPWSAMSG): LPWSACMSGHDR; +// #define CMSG_NXTHDR WSA_CMSG_NXTHDR + {$EXTERNALSYM CMSG_NXTHDR} + function CMSG_NXTHDR(const msg: LPWSAMSG; const cmsg: LPWSACMSGHDR): LPWSACMSGHDR; +// #define CMSG_SPACE WSA_CMSG_SPACE + {$EXTERNALSYM CMSG_SPACE} + function CMSG_SPACE(const Alength: UINT_PTR): UINT_PTR; +// #define CMSG_LEN WSA_CMSG_LEN + {$EXTERNALSYM CMSG_LEN} + function CMSG_LEN(const Alength: SIZE_T): SIZE_T; +// + {$EXTERNALSYM WSA_CMSGHDR_ALIGN} + function WSA_CMSGHDR_ALIGN(const Alength: UINT_PTR): UINT_PTR; + {$EXTERNALSYM WSA_CMSGDATA_ALIGN} + function WSA_CMSGDATA_ALIGN(const Alength: UINT_PTR): UINT_PTR; + {$EXTERNALSYM WSA_CMSG_FIRSTHDR} + function WSA_CMSG_FIRSTHDR(const msg: LPWSAMSG): LPWSACMSGHDR; +// #define CMSG_FIRSTHDR WSA_CMSG_FIRSTHDR + {$EXTERNALSYM WSA_CMSG_NXTHDR} + function WSA_CMSG_NXTHDR(const msg: LPWSAMSG; const cmsg: LPWSACMSGHDR): LPWSACMSGHDR; + + {$EXTERNALSYM WSA_CMSG_DATA} + function WSA_CMSG_DATA(const cmsg: LPWSACMSGHDR): PByte; + {$EXTERNALSYM WSA_CMSG_SPACE} + function WSA_CMSG_SPACE(const Alength: SIZE_T): SIZE_T; + {$EXTERNALSYM WSA_CMSG_LEN} + function WSA_CMSG_LEN(const Alength: SIZE_T): SIZE_T; + {$ENDIF} + +//============================================================= + +{ + WS2TCPIP.H - WinSock2 Extension for TCP/IP protocols + + This file contains TCP/IP specific information for use + by WinSock2 compatible applications. + + Copyright (c) 1995-1999 Microsoft Corporation + + To provide the backward compatibility, all the TCP/IP + specific definitions that were included in the WINSOCK.H + file are now included in WINSOCK2.H file. WS2TCPIP.H + file includes only the definitions introduced in the + "WinSock 2 Protocol-Specific Annex" document. + + Rev 0.3 Nov 13, 1995 + Rev 0.4 Dec 15, 1996 +} + +type +// Argument structure for IP_ADD_MEMBERSHIP and IP_DROP_MEMBERSHIP + {$EXTERNALSYM ip_mreq} + ip_mreq = record + imr_multiaddr : TInAddr; // IP multicast address of group + imr_interface : TInAddr; // local IP address of interface + end; + +// Argument structure for IP_ADD_SOURCE_MEMBERSHIP, IP_DROP_SOURCE_MEMBERSHIP, +// IP_BLOCK_SOURCE, and IP_UNBLOCK_SOURCE + {$EXTERNALSYM ip_mreq_source} + ip_mreq_source = record + imr_multiaddr: TInAddr; // IP multicast address of group + imr_sourceaddr: TInAddr; // IP address of source + imr_interface: TInAddr; // local IP address of interface + end; + +// Argument structure for SIO_{GET,SET}_MULTICAST_FILTER + {$EXTERNALSYM ip_msfilter} + ip_msfilter = record + imsf_multiaddr: TInAddr; // IP multicast address of group + imsf_interface: TInAddr; // local IP address of interface + imsf_fmode: u_long; // filter mode - INCLUDE or EXCLUDE + imsf_numsrc: u_long; // number of sources in src_list + imsf_slist: Array[0..0] of TInAddr; + end; + + {$EXTERNALSYM IP_MSFILTER_SIZE} + function IP_MSFILTER_SIZE(const numsrc: DWORD): UINT_PTR; + +// TCP/IP specific Ioctl codes +const + {$EXTERNALSYM SIO_GET_INTERFACE_LIST} + SIO_GET_INTERFACE_LIST = IOC_OUT or ((SizeOf(u_long) and IOCPARM_MASK) shl 16) or (Ord('t') shl 8) or 127; {Do not Localize} +// New IOCTL with address size independent address array + {$EXTERNALSYM SIO_GET_INTERFACE_LIST_EX} + SIO_GET_INTERFACE_LIST_EX = IOC_OUT or ((SizeOf(u_long) and IOCPARM_MASK) shl 16) or (Ord('t') shl 8) or 126; {Do not Localize} + {$EXTERNALSYM SIO_SET_MULTICAST_FILTER} + SIO_SET_MULTICAST_FILTER = IOC_IN or ((SizeOf(u_long) and IOCPARM_MASK) shl 16) or (Ord('t') shl 8) or 125; {Do not Localize} + {$EXTERNALSYM SIO_GET_MULTICAST_FILTER} + SIO_GET_MULTICAST_FILTER = IOC_IN or ((SizeOf(u_long) and IOCPARM_MASK) shl 16) or (Ord('t') shl 8) or (124 or IOC_IN); {Do not Localize} + {$EXTERNALSYM SIOCSIPMSFILTER} + SIOCSIPMSFILTER = SIO_SET_MULTICAST_FILTER; + {$EXTERNALSYM SIOCGIPMSFILTER} + SIOCGIPMSFILTER = SIO_GET_MULTICAST_FILTER; +// +// Protocol independent ioctls for setting and retrieving multicast filters. +// + {$EXTERNALSYM SIOCSMSFILTER} + SIOCSMSFILTER = IOC_IN or ((SizeOf(u_long) and IOCPARM_MASK) shl 16) or (Ord('t') shl 8) or 126; {Do not Localize} + {$EXTERNALSYM SIOCGMSFILTER} + SIOCGMSFILTER = IOC_IN or ((SizeOf(u_long) and IOCPARM_MASK) shl 16) or (Ord('t') shl 8) or (127 or IOC_IN); {Do not Localize} + + {$IFNDEF WINCE} + //Winapi.Windows 2008 and Winapi.Windows Vista SP1 additions + {$EXTERNALSYM SIO_IDEAL_SEND_BACKLOG_QUERY} + SIO_IDEAL_SEND_BACKLOG_QUERY = IOC_OUT or ((SizeOf(u_long) and IOCPARM_MASK) shl 16) or (Ord('t') shl 8) or 123; + {$EXTERNALSYM SIO_IDEAL_SEND_BACKLOG_CHANGE} + SIO_IDEAL_SEND_BACKLOG_CHANGE = IOC_VOID or (Ord('t') shl 8) or 122; + {$ENDIF} + + {$IFNDEF WINCE} +// Options for use with [gs]etsockopt at the IP level. + {$EXTERNALSYM IP_OPTIONS} + IP_OPTIONS = 1; // set/get IP options + {$EXTERNALSYM IP_HDRINCL} + IP_HDRINCL = 2; // header is included with data + {$EXTERNALSYM IP_TOS} + IP_TOS = 3; // IP type of service and preced + {$EXTERNALSYM IP_TTL} + IP_TTL = 4; // IP time to live + {$EXTERNALSYM IP_MULTICAST_IF} + IP_MULTICAST_IF = 9; // set/get IP multicast i/f + {$EXTERNALSYM IP_MULTICAST_TTL} + IP_MULTICAST_TTL = 10; // set/get IP multicast ttl + {$EXTERNALSYM IP_MULTICAST_LOOP} + IP_MULTICAST_LOOP = 11; // set/get IP multicast loopback + {$EXTERNALSYM IP_ADD_MEMBERSHIP} + IP_ADD_MEMBERSHIP = 12; // add an IP group membership + {$EXTERNALSYM IP_DROP_MEMBERSHIP} + IP_DROP_MEMBERSHIP = 13; // drop an IP group membership + {$ELSE} + {$EXTERNALSYM IP_TOS} + IP_TOS = 8; //* IP type of service and preced*/ + {$EXTERNALSYM IP_TTL} + IP_TTL = 7; //* IP time to live */ + {$EXTERNALSYM IP_MULTICAST_IF} + IP_MULTICAST_IF = 2; //* set/get IP multicast i/f */ + {$EXTERNALSYM IP_MULTICAST_TTL} + IP_MULTICAST_TTL = 3; //* set/get IP multicast ttl */ + {$EXTERNALSYM IP_MULTICAST_LOOP} + IP_MULTICAST_LOOP = 4; //*set/get IP multicast loopback */ + {$EXTERNALSYM IP_ADD_MEMBERSHIP} + IP_ADD_MEMBERSHIP = 5; //* add an IP group membership */ + {$EXTERNALSYM IP_DROP_MEMBERSHIP} + IP_DROP_MEMBERSHIP = 6; //* drop an IP group membership */ + //JPM Notes. IP_HDRINCL is not supported in WinCE 4.0. + {$EXTERNALSYM IP_HDRINCL} + IP_HDRINCL = 9; //* header is included with data */ + {$ENDIF} + + {$EXTERNALSYM IP_DONTFRAGMENT} + IP_DONTFRAGMENT = 14; // don't fragment IP datagrams {Do not Localize} + {$EXTERNALSYM IP_ADD_SOURCE_MEMBERSHIP} + IP_ADD_SOURCE_MEMBERSHIP = 15; // join IP group/source + {$EXTERNALSYM IP_DROP_SOURCE_MEMBERSHIP} + IP_DROP_SOURCE_MEMBERSHIP = 16; // leave IP group/source + {$EXTERNALSYM IP_BLOCK_SOURCE} + IP_BLOCK_SOURCE = 17; // block IP group/source + {$EXTERNALSYM IP_UNBLOCK_SOURCE} + IP_UNBLOCK_SOURCE = 18; // unblock IP group/source + {$EXTERNALSYM IP_PKTINFO} + IP_PKTINFO = 19; // receive packet information for ipv4 + {$EXTERNALSYM IP_RECEIVE_BROADCAST} + IP_RECEIVE_BROADCAST = 22; // Allow/block broadcast reception. + {$EXTERNALSYM IP_RECVIF} + IP_RECVIF = 24; // Receive arrival interface. + {$EXTERNALSYM IP_RECVDSTADDR} + IP_RECVDSTADDR = 25; // Receive destination address. + {$EXTERNALSYM IP_IFLIST} + IP_IFLIST = 28; // Enable/Disable an interface list. + {$EXTERNALSYM IP_ADD_IFLIST} + IP_ADD_IFLIST = 29; // Add an interface list entry. + {$EXTERNALSYM IP_DEL_IFLIST} + IP_DEL_IFLIST = 30; // Delete an interface list entry. + {$EXTERNALSYM IP_UNICAST_IF} + IP_UNICAST_IF = 31; // IP unicast interface. + {$EXTERNALSYM IP_RTHDR} + IP_RTHDR = 32; // Set/get IPv6 routing header. + {$EXTERNALSYM IP_RECVRTHDR} + IP_RECVRTHDR = 38; // Receive the routing header. + {$EXTERNALSYM IP_TCLASS} + IP_TCLASS = 39; // Packet traffic class. + {$EXTERNALSYM IP_RECVTCLASS} + IP_RECVTCLASS = 40; // Receive packet traffic class. + {$EXTERNALSYM IP_ORIGINAL_ARRIVAL_IF} + IP_ORIGINAL_ARRIVAL_IF = 47; // Original Arrival Interface Index. (Winapi.Windows 7) + + + {$IFDEF WINCE} + {$EXTERNALSYM IP_DSCP_TRAFFIC_TYPE} + IP_DSCP_TRAFFIC_TYPE = 100; //* differential services */ + {$EXTERNALSYM IP_RELOAD_DSCP_MAPPINGS} + IP_RELOAD_DSCP_MAPPINGS = 101; //* reload DSCP registry mappings */ + {$ENDIF} + + {$EXTERNALSYM IP_DEFAULT_MULTICAST_TTL} + IP_DEFAULT_MULTICAST_TTL = 1; // normally limit m'casts to 1 hop {Do not Localize} + {$EXTERNALSYM IP_DEFAULT_MULTICAST_LOOP} + IP_DEFAULT_MULTICAST_LOOP = 1; // normally hear sends if a member + {$EXTERNALSYM IP_MAX_MEMBERSHIPS} + IP_MAX_MEMBERSHIPS = 20; // per socket; must fit in one mbuf + + + // Option to use with [gs]etsockopt at the IPPROTO_IPV6 level + {$EXTERNALSYM IPV6_HDRINCL} + IPV6_HDRINCL = 2; // Header is included with data + {$EXTERNALSYM IPV6_UNICAST_HOPS} + IPV6_UNICAST_HOPS = 4; // Set/get IP unicast hop limit + {$EXTERNALSYM IPV6_MULTICAST_IF} + IPV6_MULTICAST_IF = 9; // Set/get IP multicast interface + {$EXTERNALSYM IPV6_MULTICAST_HOPS} + IPV6_MULTICAST_HOPS = 10; // Set/get IP multicast ttl + {$EXTERNALSYM IPV6_MULTICAST_LOOP} + IPV6_MULTICAST_LOOP = 11; // Set/get IP multicast loopback + {$EXTERNALSYM IPV6_ADD_MEMBERSHIP} + IPV6_ADD_MEMBERSHIP = 12; // Add an IP group membership + {$EXTERNALSYM IPV6_DROP_MEMBERSHIP} + IPV6_DROP_MEMBERSHIP = 13; // Drop an IP group membership + {$EXTERNALSYM IPV6_JOIN_GROUP} + IPV6_JOIN_GROUP = IPV6_ADD_MEMBERSHIP; + {$EXTERNALSYM IPV6_LEAVE_GROUP} + IPV6_LEAVE_GROUP = IPV6_DROP_MEMBERSHIP; + {$EXTERNALSYM IPV6_PKTINFO} + IPV6_PKTINFO = 19; // Receive packet information for ipv6 + {$EXTERNALSYM IPV6_HOPLIMIT} + IPV6_HOPLIMIT = 21; // Receive packet hop limit + //Note that IPV6_PROTECTION_LEVEL is not supported for WinCE 4.2 + {$EXTERNALSYM IPV6_PROTECTION_LEVEL} + IPV6_PROTECTION_LEVEL = 23; // Set/get IPv6 protection level + + // Option to use with [gs]etsockopt at the IPPROTO_UDP level + {$EXTERNALSYM UDP_NOCHECKSUM} + UDP_NOCHECKSUM = 1; + {$EXTERNALSYM UDP_CHECKSUM_COVERAGE} + UDP_CHECKSUM_COVERAGE = 20; // Set/get UDP-Lite checksum coverage + +// Option to use with [gs]etsockopt at the IPPROTO_TCP level + {$EXTERNALSYM TCP_EXPEDITED_1122} + TCP_EXPEDITED_1122 = $0002; + +// IPv6 definitions +type + {$EXTERNALSYM IN6_ADDR} + IN6_ADDR = record + case Integer of + 0: (s6_addr: array[0..15] of u_char); + 1: (word: array[0..7] of u_short); + end; + {$NODEFINE TIn6Addr} + TIn6Addr = IN6_ADDR; + {$NODEFINE PIn6Addr} + PIn6Addr = ^TIn6Addr; + {$EXTERNALSYM PIN6_ADDR} + PIN6_ADDR = ^PIn6Addr; + {$EXTERNALSYM LPIN6_ADDR} + LPIN6_ADDR = PIN6_ADDR; + +{$IFNDEF WINCE} + {$IFNDEF NO_REDECLARE} + // Argument structure for IPV6_JOIN_GROUP and IPV6_LEAVE_GROUP + {$EXTERNALSYM ipv6_mreq} + ipv6_mreq = record + ipv6mr_multiaddr: TIn6Addr; // IPv6 multicast address + ipv6mr_interface: u_int; // Interface index + end; + {$NODEFINE TIPv6_MReq} + TIPv6_MReq = IPV6_MREQ; + {$NODEFINE PIPv6_MReq} + PIPv6_MReq = ^TIPv6_MReq; + {$ENDIF} +{$ENDIF} + + // Old IPv6 socket address structure (retained for sockaddr_gen definition below) + {$EXTERNALSYM sockaddr_in6_old} + sockaddr_in6_old = record + sin6_family : Smallint; // AF_INET6 + sin6_port : u_short; // Transport level port number + sin6_flowinfo : u_long; // IPv6 flow information + sin6_addr : TIn6Addr; // IPv6 address + end; + +// IPv6 socket address structure, RFC 2553 + {$EXTERNALSYM SOCKADDR_IN6} + SOCKADDR_IN6 = record + sin6_family : Smallint; // AF_INET6 + sin6_port : u_short; // Transport level port number + sin6_flowinfo : u_long; // IPv6 flow information + sin6_addr : TIn6Addr; // IPv6 address + sin6_scope_id : u_long; // set of interfaces for a scope + end; + {$NODEFINE TSockAddrIn6} + TSockAddrIn6 = SOCKADDR_IN6; + {$NODEFINE PSockAddrIn6} + PSockAddrIn6 = ^TSockAddrIn6; + {$EXTERNALSYM PSOCKADDR_IN6} + PSOCKADDR_IN6 = PSockAddrIn6; + {$EXTERNALSYM LPSOCKADDR_IN6} + LPSOCKADDR_IN6 = PSOCKADDR_IN6; + + {$EXTERNALSYM sockaddr_gen} + sockaddr_gen = record + case Integer of + 1 : ( Address : TSockAddr; ); + 2 : ( AddressIn : TSockAddrIn; ); + 3 : ( AddressIn6 : sockaddr_in6_old; ); + end; + {$NODEFINE TSockAddrGen} + TSockAddrGen = sockaddr_gen; + +// Structure to keep interface specific information + {$EXTERNALSYM INTERFACE_INFO} + INTERFACE_INFO = record + iiFlags : u_long; // Interface flags + iiAddress : TSockAddrGen; // Interface address + iiBroadcastAddress : TSockAddrGen; // Broadcast address + iiNetmask : TSockAddrGen; // Network mask + end; + {$NODEFINE TInterface_Info} + TInterface_Info = INTERFACE_INFO; + {$EXTERNALSYM PINTERFACE_INFO} + PINTERFACE_INFO = ^TInterface_Info; + {$EXTERNALSYM LPINTERFACE_INFO} + LPINTERFACE_INFO = PINTERFACE_INFO; + +// New structure that does not have dependency on the address size + {$EXTERNALSYM INTERFACE_INFO_EX} + INTERFACE_INFO_EX = record + iiFlags : u_long; // Interface flags + iiAddress : TSocket_Address; // Interface address + iiBroadcastAddress : TSocket_Address; // Broadcast address + iiNetmask : TSocket_Address; // Network mask + end; + {$NODEFINE TInterface_Info_Ex} + TInterface_Info_Ex = INTERFACE_INFO_EX; + {$EXTERNALSYM PINTERFACE_INFO_EX} + PINTERFACE_INFO_EX = ^TInterface_Info_Ex; + {$EXTERNALSYM LPINTERFACE_INFO_EX} + LPINTERFACE_INFO_EX = PINTERFACE_INFO_EX; + +// Macro that works for both IPv4 and IPv6 + {$EXTERNALSYM SS_PORT} + function SS_PORT(ssp: PSockAddrIn): u_short; + + {$EXTERNALSYM IN6ADDR_ANY_INIT} + function IN6ADDR_ANY_INIT: TIn6Addr; + {$EXTERNALSYM IN6ADDR_LOOPBACK_INIT} + function IN6ADDR_LOOPBACK_INIT: TIn6Addr; + + {$EXTERNALSYM IN6ADDR_SETANY} + procedure IN6ADDR_SETANY(sa: PSockAddrIn6); + {$EXTERNALSYM IN6ADDR_SETLOOPBACK} + procedure IN6ADDR_SETLOOPBACK(sa: PSockAddrIn6); + {$EXTERNALSYM IN6ADDR_ISANY} + function IN6ADDR_ISANY(sa: PSockAddrIn6): Boolean; + {$EXTERNALSYM IN6ADDR_ISLOOPBACK} + function IN6ADDR_ISLOOPBACK(sa: PSockAddrIn6): Boolean; + {$EXTERNALSYM IN6_IS_ADDR_SUBNET_ROUTER_ANYCAST} + function IN6_IS_ADDR_SUBNET_ROUTER_ANYCAST(const a : PIn6Addr) : Boolean; + {$EXTERNALSYM IN6_IS_ADDR_SUBNET_RESERVED_ANYCAST} + function IN6_IS_ADDR_SUBNET_RESERVED_ANYCAST(const a: PIn6Addr) : Boolean; + {$EXTERNALSYM IN6_IS_ADDR_ANYCAST} + function IN6_IS_ADDR_ANYCAST(const a: PIn6Addr) : Boolean; + {$EXTERNALSYM IN6_ADDR_EQUAL} + function IN6_ADDR_EQUAL(const a: PIn6Addr; const b: PIn6Addr): Boolean; + {$EXTERNALSYM IN6_IS_ADDR_UNSPECIFIED} + function IN6_IS_ADDR_UNSPECIFIED(const a: PIn6Addr): Boolean; + {$EXTERNALSYM IN6_IS_ADDR_LOOPBACK} + function IN6_IS_ADDR_LOOPBACK(const a: PIn6Addr): Boolean; + {$EXTERNALSYM IN6_IS_ADDR_MULTICAST} + function IN6_IS_ADDR_MULTICAST(const a: PIn6Addr): Boolean; + {$EXTERNALSYM IN6_IS_ADDR_EUI64} + function IN6_IS_ADDR_EUI64(const a : PIn6Addr) : Boolean; + + {$EXTERNALSYM IN6_IS_ADDR_LINKLOCAL} + function IN6_IS_ADDR_LINKLOCAL(const a: PIn6Addr): Boolean; + {$EXTERNALSYM IN6_IS_ADDR_SITELOCAL} + function IN6_IS_ADDR_SITELOCAL(const a: PIn6Addr): Boolean; + {$EXTERNALSYM IN6_IS_ADDR_V4MAPPED} + function IN6_IS_ADDR_V4MAPPED(const a: PIn6Addr): Boolean; + {$EXTERNALSYM IN6_IS_ADDR_V4COMPAT} + function IN6_IS_ADDR_V4COMPAT(const a: PIn6Addr): Boolean; + {$EXTERNALSYM IN6_IS_ADDR_MC_NODELOCAL} + function IN6_IS_ADDR_MC_NODELOCAL(const a: PIn6Addr): Boolean; + {$EXTERNALSYM IN6_IS_ADDR_MC_LINKLOCAL} + function IN6_IS_ADDR_MC_LINKLOCAL(const a: PIn6Addr): Boolean; + {$EXTERNALSYM IN6_IS_ADDR_MC_SITELOCAL} + function IN6_IS_ADDR_MC_SITELOCAL(const a: PIn6Addr): Boolean; + {$EXTERNALSYM IN6_IS_ADDR_MC_ORGLOCAL} + function IN6_IS_ADDR_MC_ORGLOCAL(const a: PIn6Addr): Boolean; + {$EXTERNALSYM IN6_IS_ADDR_MC_GLOBAL} + function IN6_IS_ADDR_MC_GLOBAL(const a: PIn6Addr): Boolean; + + {$EXTERNALSYM IN6_SET_ADDR_UNSPECIFIED} + procedure IN6_SET_ADDR_UNSPECIFIED(a : PIN6_ADDR); + +// Possible flags for the iiFlags - bitmask +const + {$EXTERNALSYM IFF_UP} + IFF_UP = $00000001; // Interface is up + {$EXTERNALSYM IFF_BROADCAST} + IFF_BROADCAST = $00000002; // Broadcast is supported + {$EXTERNALSYM IFF_LOOPBACK} + IFF_LOOPBACK = $00000004; // this is loopback interface + {$EXTERNALSYM IFF_POINTTOPOINT} + IFF_POINTTOPOINT = $00000008; // this is point-to-point interface + {$EXTERNALSYM IFF_MULTICAST} + IFF_MULTICAST = $00000010; // multicast is supported + +type + {$EXTERNALSYM MULTICAST_MODE_TYPE} + {$EXTERNALSYM MCAST_INCLUDE} + {$EXTERNALSYM MCAST_EXCLUDE} + MULTICAST_MODE_TYPE = (MCAST_INCLUDE, MCAST_EXCLUDE); + {$EXTERNALSYM GROUP_FILTER} + GROUP_FILTER = record + gf_interface : PULONG; // Interface index. + gf_group : SOCKADDR_STORAGE; // Multicast address. + gf_fmode : MULTICAST_MODE_TYPE; // Filter mode. + gf_numsrc : ULONG; // Number of sources. + gf_slist : SOCKADDR_STORAGE; //gf_slist[1] : SOCKADDR_STORAGE; // Source address. + end; + {$EXTERNALSYM PGROUP_FILTER} + PGROUP_FILTER = ^GROUP_FILTER; + + {$EXTERNALSYM GROUP_REQ} + GROUP_REQ = record + gr_interface : ULONG; // Interface index. + gr_group : SOCKADDR_STORAGE; // Multicast address. + end; + {$EXTERNALSYM PGROUP_REQ} + PGROUP_REQ = ^GROUP_REQ; + + {$EXTERNALSYM GROUP_SOURCE_REQ} + GROUP_SOURCE_REQ = record + gsr_interface : ULONG; // Interface index. + gsr_group : SOCKADDR_STORAGE; // Group address. + gsr_source : SOCKADDR_STORAGE; // Source address. + end; + {$EXTERNALSYM PGROUP_SOURCE_REQ} + PGROUP_SOURCE_REQ = ^GROUP_SOURCE_REQ; + +{$EXTERNALSYM GROUP_FILTER_SIZE} +function GROUP_FILTER_SIZE(const numsrc : DWord) : UINT_PTR; + +type + {$EXTERNALSYM WSAQUERYSET2} + WSAQUERYSET2 = record + dwSize : DWORD; + lpszServiceInstanceName : LPTSTR; + lpVersion : LPWSAVERSION; + lpszComment : LPTSTR; + dwNameSpace : DWORD; + lpNSProviderId : LPGUID; + lpszContext : LPTSTR; + dwNumberOfProtocols : DWORD; + lpafpProtocols : LPAFPROTOCOLS; + lpszQueryString : LPTSTR; + dwNumberOfCsAddrs : DWORD; + lpcsaBuffer : LPCSADDR_INFO; + dwOutputFlags : DWORD; + lpBlob : LPBLOB; + end; + {$EXTERNALSYM PWSAQUERYSET2} + PWSAQUERYSET2 = ^WSAQUERYSET2; + {$EXTERNALSYM LPWSAQUERYSET2} + LPWSAQUERYSET2 = PWSAQUERYSET2; + + {$EXTERNALSYM NAPI_PROVIDER_TYPE} + {$EXTERNALSYM ProviderType_Application} + {$EXTERNALSYM ProviderType_Service} + // The Pascal compiler in Delphi/BCB prior to v6 does not + // support specifying values for individual enum items + {$IFDEF HAS_ENUM_ELEMENT_VALUES} + NAPI_PROVIDER_TYPE = (ProviderType_Application = 1, ProviderType_Service); + {$ELSE} + NAPI_PROVIDER_TYPE = (nptUnused, ProviderType_Application, ProviderType_Service); + {$ENDIF} + {$EXTERNALSYM NAPI_DOMAIN_DESCRIPTION_BLOB} + NAPI_DOMAIN_DESCRIPTION_BLOB = record + AuthLevel : DWORD; + cchDomainName : DWORD; + OffsetNextDomainDescription : DWORD; + OffsetThisDomainName : DWORD; + end; + {$EXTERNALSYM PNAPI_DOMAIN_DESCRIPTION_BLOB} + PNAPI_DOMAIN_DESCRIPTION_BLOB = ^NAPI_DOMAIN_DESCRIPTION_BLOB; + + {$EXTERNALSYM NAPI_PROVIDER_LEVEL} + {$EXTERNALSYM PROVIDERLEVEL_NONE} + {$EXTERNALSYM PROVIDERLEVEL_SECONDARY} + {$EXTERNALSYM PROVIDERLEVEL_PRIMARY} + NAPI_PROVIDER_LEVEL = (PROVIDERLEVEL_NONE, PROVIDERLEVEL_SECONDARY,PROVIDERLEVEL_PRIMARY); + + {$EXTERNALSYM NAPI_PROVIDER_INSTALLATION_BLOB} + NAPI_PROVIDER_INSTALLATION_BLOB = record + dwVersion : DWORD; + dwProviderType : DWORD; + fSupportsWildCard : DWORD; + cDomains : DWORD; + OffsetFirstDomain : DWORD; + end; + {$EXTERNALSYM PNAPI_PROVIDER_INSTALLATION_BLOB} + PNAPI_PROVIDER_INSTALLATION_BLOB = ^NAPI_PROVIDER_INSTALLATION_BLOB; + + {$IFNDEF NOREDECLARE} + {$EXTERNALSYM SERVICE_ADDRESS} + SERVICE_ADDRESS = record + dwAddressType : DWORD; + dwAddressFlags : DWORD; + dwAddressLength : DWORD; + dwPrincipalLength : DWORD; + lpAddress : PByte; + lpPrincipal : PByte; + end; + {$ENDIF} + {$EXTERNALSYM PSERVICE_ADDRESS} + PSERVICE_ADDRESS = ^SERVICE_ADDRESS; + {$EXTERNALSYM LPSERVICE_ADDRESS} + LPSERVICE_ADDRESS = PSERVICE_ADDRESS; + + {$IFNDEF NOREDECLARE} + {$EXTERNALSYM SERVICE_ADDRESSES} + SERVICE_ADDRESSES = record + dwAddressCount : DWORD; +//#ifdef MIDL_PASS +// [size_is(dwAddressCount)] SERVICE_ADDRESS Addressses[*]; +//#else // MIDL_PASS +// SERVICE_ADDRESS Addresses[1] ; +//#endif // MIDL_PASS + Addresses : SERVICE_ADDRESS; + end; + {$EXTERNALSYM PSERVICE_ADDRESSES} + PSERVICE_ADDRESSES = ^SERVICE_ADDRESSES; + {$EXTERNALSYM LPSERVICE_ADDRESSES} + LPSERVICE_ADDRESSES = PSERVICE_ADDRESSES; + {$ENDIF} + +{$IFNDEF VCL_2007_OR_ABOVE} +const + {$EXTERNALSYM RESOURCEDISPLAYTYPE_GENERIC} + RESOURCEDISPLAYTYPE_GENERIC = $00000000; + {$EXTERNALSYM RESOURCEDISPLAYTYPE_DOMAIN} + RESOURCEDISPLAYTYPE_DOMAIN = $00000001; + {$EXTERNALSYM RESOURCEDISPLAYTYPE_SERVER} + RESOURCEDISPLAYTYPE_SERVER = $00000002; + {$EXTERNALSYM RESOURCEDISPLAYTYPE_SHARE} + RESOURCEDISPLAYTYPE_SHARE = $00000003; + {$EXTERNALSYM RESOURCEDISPLAYTYPE_FILE} + RESOURCEDISPLAYTYPE_FILE = $00000004; + {$EXTERNALSYM RESOURCEDISPLAYTYPE_GROUP} + RESOURCEDISPLAYTYPE_GROUP = $00000005; + {$EXTERNALSYM RESOURCEDISPLAYTYPE_NETWORK} + RESOURCEDISPLAYTYPE_NETWORK = $00000006; + {$EXTERNALSYM RESOURCEDISPLAYTYPE_ROOT} + RESOURCEDISPLAYTYPE_ROOT = $00000007; + {$EXTERNALSYM RESOURCEDISPLAYTYPE_SHAREADMIN} + RESOURCEDISPLAYTYPE_SHAREADMIN = $00000008; + {$EXTERNALSYM RESOURCEDISPLAYTYPE_DIRECTORY} + RESOURCEDISPLAYTYPE_DIRECTORY = $00000009; + {$EXTERNALSYM RESOURCEDISPLAYTYPE_TREE} + RESOURCEDISPLAYTYPE_TREE = $0000000A; + {$EXTERNALSYM RESOURCEDISPLAYTYPE_NDSCONTAINER} + RESOURCEDISPLAYTYPE_NDSCONTAINER = $0000000B; +{$ENDIF} + +type + {$EXTERNALSYM SERVICE_TYPE_VALUE_ABSA} + SERVICE_TYPE_VALUE_ABSA = record + dwNameSpace : DWORD; + dwValueType : DWORD; + dwValueSize : DWORD; + lpValueName : LPSTR; + lpValue : Pointer; + end; + {$EXTERNALSYM PSERVICE_TYPE_VALUE_ABSA} + PSERVICE_TYPE_VALUE_ABSA = ^SERVICE_TYPE_VALUE_ABSA; + {$EXTERNALSYM LPSERVICE_TYPE_VALUE_ABSA} + LPSERVICE_TYPE_VALUE_ABSA = PSERVICE_TYPE_VALUE_ABSA; + + {$EXTERNALSYM SERVICE_INFOA} + SERVICE_INFOA = record + lpServiceType : LPGUID; + lpServiceName : PAnsiChar; + lpComment : PAnsiChar; + lpLocale : PAnsiChar; + dwDisplayHint : DWORD; + dwVersion : DWORD; + dwTime : DWORD; + lpMachineName : PAnsiChar; + lpServiceAddress : LPSERVICE_ADDRESSES; + ServiceSpecificInfo : BLOB; + end; + {$EXTERNALSYM SERVICE_INFOW} + SERVICE_INFOW = record + lpServiceType : LPGUID; + lpServiceName : PWideChar; + lpComment : PWideChar; + lpLocale : PWideChar; + dwDisplayHint : DWORD; + dwVersion : DWORD; + dwTime : DWORD; + lpMachineName : PWideChar; + lpServiceAddress : LPSERVICE_ADDRESSES; + ServiceSpecificInfo : BLOB; + end; + + {$EXTERNALSYM SOCKET_USAGE_TYPE} + {$EXTERNALSYM SYSTEM_CRITICAL_SOCKET} + // The Pascal compiler in Delphi/BCB prior to v6 does not + // support specifying values for individual enum items + {$IFDEF HAS_ENUM_ELEMENT_VALUES} + SOCKET_USAGE_TYPE = (SYSTEM_CRITICAL_SOCKET = 1); + {$ELSE} + SOCKET_USAGE_TYPE = (sutUnused, SYSTEM_CRITICAL_SOCKET); + {$ENDIF} + + {$IFNDEF NO_REDECLARE} + {$EXTERNALSYM SERVICE_INFO} + {$IFDEF UNICODE} + SERVICE_INFO = SERVICE_INFOW; + {$ELSE} + SERVICE_INFO = SERVICE_INFOA; + {$ENDIF} + {$ENDIF} + {$EXTERNALSYM NS_SERVICE_INFOA} + NS_SERVICE_INFOA = record + dwNameSpace : DWORD; + ServiceInfo : SERVICE_INFOA; + end; + {$EXTERNALSYM PNS_SERVICE_INFOA} + PNS_SERVICE_INFOA = ^NS_SERVICE_INFOA; + {$EXTERNALSYM LPNS_SERVICE_INFOA} + LPNS_SERVICE_INFOA = NS_SERVICE_INFOA; + {$EXTERNALSYM NS_SERVICE_INFOW} + NS_SERVICE_INFOW = record + dwNameSpace : DWORD; + ServiceInfo : SERVICE_INFOW; + end; + {$EXTERNALSYM PNS_SERVICE_INFOW} + PNS_SERVICE_INFOW = ^NS_SERVICE_INFOW; + {$EXTERNALSYM LPNS_SERVICE_INFOW} + LPNS_SERVICE_INFOW = NS_SERVICE_INFOW; + {$IFNDEF NO_REDECLARE} + {$EXTERNALSYM NS_SERVICE_INFO} + {$EXTERNALSYM PNS_SERVICE_INFO} + {$EXTERNALSYM LPNS_SERVICE_INFO} + {$IFDEF UNICODE} + NS_SERVICE_INFO = NS_SERVICE_INFOW; + PNS_SERVICE_INFO = PNS_SERVICE_INFOW; + LPNS_SERVICE_INFO = LPNS_SERVICE_INFOW; + {$ELSE} + NS_SERVICE_INFO = NS_SERVICE_INFOA; + PNS_SERVICE_INFO = PNS_SERVICE_INFOA; + LPNS_SERVICE_INFO = LPNS_SERVICE_INFOA; + {$ENDIF} + {$ENDIF} + +{$IFNDEF WINCE} +type +// structure for IP_PKTINFO option + {$EXTERNALSYM IN_PKTINFO} + IN_PKTINFO = record + ipi_addr : TInAddr; // destination IPv4 address + ipi_ifindex : UINT; // received interface index + end; + {$NODEFINE TInPktInfo} + TInPktInfo = IN_PKTINFO; + {$NODEFINE PInPktInfo} + PInPktInfo = ^IN_PKTINFO; + +// structure for IPV6_PKTINFO option + {$EXTERNALSYM IN6_PKTINFO} + IN6_PKTINFO = record + ipi6_addr : TIn6Addr; // destination IPv6 address + ipi6_ifindex : UINT; // received interface index + end; + {$NODEFINE TIn6PktInfo} + TIn6PktInfo = IN6_PKTINFO; + {$NODEFINE PIn6PktInfo} + PIn6PktInfo = ^TIn6PktInfo; +{$ENDIF} + +// Error codes from getaddrinfo() +const + {$EXTERNALSYM EAI_AGAIN} + EAI_AGAIN = WSATRY_AGAIN; + {$EXTERNALSYM EAI_BADFLAGS} + EAI_BADFLAGS = WSAEINVAL; + {$EXTERNALSYM EAI_FAIL} + EAI_FAIL = WSANO_RECOVERY; + {$EXTERNALSYM EAI_FAMILY} + EAI_FAMILY = WSAEAFNOSUPPORT; + {$EXTERNALSYM EAI_MEMORY} + EAI_MEMORY = WSA_NOT_ENOUGH_MEMORY; +// {$EXTERNALSYM EAI_NODATA} +// EAI_NODATA = WSANO_DATA; + {$EXTERNALSYM EAI_NONAME} + EAI_NONAME = WSAHOST_NOT_FOUND; + {$EXTERNALSYM EAI_SERVICE} + EAI_SERVICE = WSATYPE_NOT_FOUND; + {$EXTERNALSYM EAI_SOCKTYPE} + EAI_SOCKTYPE = WSAESOCKTNOSUPPORT; + +// DCR_FIX: EAI_NODATA remove or fix +// +// EAI_NODATA was removed from rfc2553bis +// need to find out from the authors why and +// determine the error for "no records of this type" +// temporarily, we'll keep #define to avoid changing +// code that could change back; use NONAME + {$EXTERNALSYM EAI_NODATA} + EAI_NODATA = EAI_NONAME; + +// Structure used in getaddrinfo() call +type + {$NODEFINE PAddrInfo} + PAddrInfo = ^ADDRINFO; + {$NODEFINE PPaddrinfo} + PPaddrinfo = ^PAddrInfo; + {$NODEFINE PPaddrinfoW} + PPaddrinfoW = ^PAddrInfoW; + {$EXTERNALSYM ADDRINFO} + ADDRINFO = record + ai_flags : Integer; // AI_PASSIVE, AI_CANONNAME, AI_NUMERICHOST + ai_family : Integer; // PF_xxx + ai_socktype : Integer; // SOCK_xxx + ai_protocol : Integer; // 0 or IPPROTO_xxx for IPv4 and IPv6 + ai_addrlen : size_t; // Length of ai_addr + ai_canonname : PAnsiChar; // Canonical name for nodename + ai_addr : PSOCKADDR; // Binary address + ai_next : PAddrInfo; // Next structure in linked list + end; + {$NODEFINE TAddrInfo} + TAddrInfo = ADDRINFO; + {$EXTERNALSYM LPADDRINFO} + LPADDRINFO = PAddrInfo; + + {$NODEFINE PAddrInfoW} + PAddrInfoW = ^ADDRINFOW; + {$EXTERNALSYM ADDRINFOW} + ADDRINFOW = record + ai_flags : Integer; // AI_PASSIVE, AI_CANONNAME, AI_NUMERICHOST + ai_family : Integer; // PF_xxx + ai_socktype : Integer; // SOCK_xxx + ai_protocol : Integer; // 0 or IPPROTO_xxx for IPv4 and IPv6 + ai_addrlen : size_t; // Length of ai_addr + ai_canonname : PWideChar; // Canonical name for nodename + ai_addr : PSOCKADDR; // Binary address + ai_next : PAddrInfoW; // Next structure in linked list + end; + {$NODEFINE TAddrInfoW} + TAddrInfoW = ADDRINFOW; + {$EXTERNALSYM LPADDRINFOW} + LPADDRINFOW = PAddrInfoW; + +// Flags used in "hints" argument to getaddrinfo() +const + {$EXTERNALSYM AI_PASSIVE} + AI_PASSIVE = $00000001; // Socket address will be used in bind() call + {$EXTERNALSYM AI_CANONNAME} + AI_CANONNAME = $00000002; // Return canonical name in first ai_canonname + {$EXTERNALSYM AI_NUMERICHOST} + AI_NUMERICHOST = $00000004; // Nodename must be a numeric address string + {$EXTERNALSYM AI_NUMERICSERV} + AI_NUMERICSERV = $00000008; // Servicename must be a numeric port number + {$EXTERNALSYM AI_ALL} + AI_ALL = $00000100; // Query both IP6 and IP4 with AI_V4MAPPED + {$EXTERNALSYM AI_ADDRCONFIG} + AI_ADDRCONFIG = $00000400; // Resolution only if global address configured + {$EXTERNALSYM AI_V4MAPPED} + AI_V4MAPPED = $00000800; // On v6 failure, query v4 and convert to V4MAPPED format (Vista or later) + {$EXTERNALSYM AI_NON_AUTHORITATIVE} + AI_NON_AUTHORITATIVE = $00004000; // LUP_NON_AUTHORITATIVE (Vista or later) + {$EXTERNALSYM AI_SECURE} + AI_SECURE = $00008000; // LUP_SECURE (Vista or later and applies only to NS_EMAIL namespace.) + {$EXTERNALSYM AI_RETURN_PREFERRED_NAMES} + AI_RETURN_PREFERRED_NAMES = $00010000; // LUP_RETURN_PREFERRED_NAMES (Vista or later and applies only to NS_EMAIL namespace.) + {$EXTERNALSYM AI_FQDN} + AI_FQDN = $00020000; // Return the FQDN in ai_canonname (Winapi.Windows 7 or later) + {$EXTERNALSYM AI_FILESERVER} + AI_FILESERVER = $00040000; // Resolving fileserver name resolution (Winapi.Windows 7 or later) + + +type + {$EXTERNALSYM PADDRINFOEXA} + PADDRINFOEXA = ^TAddrInfoEXA; + {$EXTERNALSYM ADDRINFOEXA} + ADDRINFOEXA = record + ai_flags : Integer; // AI_PASSIVE, AI_CANONNAME, AI_NUMERICHOST + ai_family : Integer; // PF_xxx + ai_socktype : Integer; // SOCK_xxx + ai_protocol : Integer; // 0 or IPPROTO_xxx for IPv4 and IPv6 + ai_addrlen : size_t; // Length of ai_addr + ai_canonname : PAnsiChar; // Canonical name for nodename + ai_addr : Psockaddr; // Binary address + ai_blob : Pointer; + ai_bloblen : size_t; + ai_provider : LPGUID; + ai_next : PADDRINFOEXA; // Next structure in linked list + end; + {$NODEFINE TAddrInfoEXA} + TAddrInfoExA = ADDRINFOEXA; + {$EXTERNALSYM LPADDRINFOEXA} + LPADDRINFOEXA = PADDRINFOEXA; + + {$EXTERNALSYM PADDRINFOEXW} + PADDRINFOEXW = ^TAddrInfoEXW; + {$EXTERNALSYM ADDRINFOEXW} + ADDRINFOEXW = record + ai_flags : Integer; // AI_PASSIVE, AI_CANONNAME, AI_NUMERICHOST + ai_family : Integer; // PF_xxx + ai_socktype : Integer; // SOCK_xxx + ai_protocol : Integer; // 0 or IPPROTO_xxx for IPv4 and IPv6 + ai_addrlen : size_t; // Length of ai_addr + ai_canonname : PWideChar; // Canonical name for nodename + ai_addr : Psockaddr; // Binary address + ai_blob : Pointer; + ai_bloblen : size_t; + ai_provider : LPGUID; + ai_next : PADDRINFOEXW; // Next structure in linked list + end; + {$NODEFINE TAddrInfoExW} + TAddrInfoExW = ADDRINFOEXA; + {$EXTERNALSYM LPADDRINFOEXW} + LPADDRINFOEXW = PADDRINFOEXW; + +var + {$EXTERNALSYM in6addr_any} + in6addr_any: TIn6Addr; + {$EXTERNALSYM in6addr_loopback} + in6addr_loopback: TIn6Addr; + +//============================================================= + +{ + wsipx.h + + Microsoft Winapi.Windows + Copyright (C) Microsoft Corporation, 1992-1999. + + Winapi.Windows Sockets include file for IPX/SPX. This file contains all + standardized IPX/SPX information. Include this header file after + winsock.h. + + To open an IPX socket, call socket() with an address family of + AF_IPX, a socket type of SOCK_DGRAM, and protocol NSPROTO_IPX. + Note that the protocol value must be specified, it cannot be 0. + All IPX packets are sent with the packet type field of the IPX + header set to 0. + + To open an SPX or SPXII socket, call socket() with an address + family of AF_IPX, socket type of SOCK_SEQPACKET or SOCK_STREAM, + and protocol of NSPROTO_SPX or NSPROTO_SPXII. If SOCK_SEQPACKET + is specified, then the end of message bit is respected, and + recv() calls are not completed until a packet is received with + the end of message bit set. If SOCK_STREAM is specified, then + the end of message bit is not respected, and recv() completes + as soon as any data is received, regardless of the setting of the + end of message bit. Send coalescing is never performed, and sends + smaller than a single packet are always sent with the end of + message bit set. Sends larger than a single packet are packetized + with the end of message bit set on only the last packet of the + send. +} + +// This is the structure of the SOCKADDR structure for IPX and SPX. +type + {$EXTERNALSYM SOCKADDR_IPX} + SOCKADDR_IPX = record + sa_family : u_short; + sa_netnum : Array [0..3] of AnsiChar; + sa_nodenum : Array [0..5] of AnsiChar; + sa_socket : u_short; + end; + {$NODEFINE TSockAddrIPX} + TSockAddrIPX = SOCKADDR_IPX; + {$NODEFINE PSockAddrIPX} + PSockAddrIPX = ^TSockAddrIPX; + {$EXTERNALSYM PSOCKADDR_IPX} + PSOCKADDR_IPX = PSockAddrIPX; + {$EXTERNALSYM LPSOCKADDR_IPX} + LPSOCKADDR_IPX = PSOCKADDR_IPX; + +// Protocol families used in the "protocol" parameter of the socket() API. +const + {$EXTERNALSYM NSPROTO_IPX} + NSPROTO_IPX = 1000; + {$EXTERNALSYM NSPROTO_SPX} + NSPROTO_SPX = 1256; + {$EXTERNALSYM NSPROTO_SPXII} + NSPROTO_SPXII = 1257; + + +//============================================================= + +{ + wsnwlink.h + + Microsoft Winapi.Windows + Copyright (C) Microsoft Corporation, 1992-1999. + Microsoft-specific extensions to the Winapi.Windows NT IPX/SPX Winapi.Windows + Sockets interface. These extensions are provided for use as + necessary for compatibility with existing applications. They are + otherwise not recommended for use, as they are only guaranteed to + work over the Microsoft IPX/SPX stack. An application which + uses these extensions may not work over other IPX/SPX + implementations. Include this header file after winsock.h and + wsipx.h. + + To open an IPX socket where a particular packet type is sent in + the IPX header, specify NSPROTO_IPX + n as the protocol parameter + of the socket() API. For example, to open an IPX socket that + sets the packet type to 34, use the following socket() call: + + s = socket(AF_IPX, SOCK_DGRAM, NSPROTO_IPX + 34); +} + +// Below are socket option that may be set or retrieved by specifying +// the appropriate manifest in the "optname" parameter of getsockopt() +// or setsockopt(). Use NSPROTO_IPX as the "level" argument for the +// call. +const + +// Set/get the IPX packet type. The value specified in the +// optval argument will be set as the packet type on every IPX +// packet sent from this socket. The optval parameter of +// getsockopt()/setsockopt() points to an int. + {$EXTERNALSYM IPX_PTYPE} + IPX_PTYPE = $4000; + +// Set/get the receive filter packet type. Only IPX packets with +// a packet type equal to the value specified in the optval +// argument will be returned; packets with a packet type that +// does not match are discarded. optval points to an int. + {$EXTERNALSYM IPX_FILTERPTYPE} + IPX_FILTERPTYPE = $4001; + +// Stop filtering on packet type set with IPX_FILTERPTYPE. + {$EXTERNALSYM IPX_STOPFILTERPTYPE} + IPX_STOPFILTERPTYPE = $4003; + +// Set/get the value of the datastream field in the SPX header on +// every packet sent. optval points to an int. + {$EXTERNALSYM IPX_DSTYPE} + IPX_DSTYPE = $4002; + +// Enable extended addressing. On sends, adds the element +// "unsigned char sa_ptype" to the SOCKADDR_IPX structure, +// making the total length 15 bytes. On receives, add both +// the sa_ptype and "unsigned char sa_flags" to the SOCKADDR_IPX +// structure, making the total length 16 bytes. The current +// bits defined in sa_flags are: +// 0x01 - the received frame was sent as a broadcast +// 0x02 - the received frame was sent from this machine +// optval points to a BOOL. + {$EXTERNALSYM IPX_EXTENDED_ADDRESS} + IPX_EXTENDED_ADDRESS = $4004; + +// Send protocol header up on all receive packets. optval points +// to a BOOL. + {$EXTERNALSYM IPX_RECVHDR} + IPX_RECVHDR = $4005; + +// Get the maximum data size that can be sent. Not valid with +// setsockopt(). optval points to an int where the value is +// returned. + {$EXTERNALSYM IPX_MAXSIZE} + IPX_MAXSIZE = $4006; + +// Query information about a specific adapter that IPX is bound +// to. In a system with n adapters they are numbered 0 through n-1. +// Callers can issue the IPX_MAX_ADAPTER_NUM getsockopt() to find +// out the number of adapters present, or call IPX_ADDRESS with +// increasing values of adapternum until it fails. Not valid +// with setsockopt(). optval points to an instance of the +// IPX_ADDRESS_DATA structure with the adapternum filled in. + {$EXTERNALSYM IPX_ADDRESS} + IPX_ADDRESS = $4007; + +type + {$EXTERNALSYM IPX_ADDRESS_DATA} + IPX_ADDRESS_DATA = record + adapternum : Integer; // input: 0-based adapter number + netnum : Array [0..3] of Byte; // output: IPX network number + nodenum : Array [0..5] of Byte; // output: IPX node address + wan : Boolean; // output: TRUE = adapter is on a wan link + status : Boolean; // output: TRUE = wan link is up (or adapter is not wan) + maxpkt : Integer; // output: max packet size, not including IPX header + linkspeed : ULONG; // output: link speed in 100 bytes/sec (i.e. 96 == 9600 bps) + end; + {$NODEFINE TIPXAddressData} + TIPXAddressData = IPX_ADDRESS_DATA; + {$NODEFINE PIPXAddressData} + PIPXAddressData = ^TIPXAddressData; + {$EXTERNALSYM PIPX_ADDRESS_DATA} + PIPX_ADDRESS_DATA = PIPXAddressData; + +const +// Query information about a specific IPX network number. If the +// network is in IPX's cache it will return the information directly, {Do not Localize} +// otherwise it will issue RIP requests to find it. Not valid with +// setsockopt(). optval points to an instance of the IPX_NETNUM_DATA +// structure with the netnum filled in. + {$EXTERNALSYM IPX_GETNETINFO} + IPX_GETNETINFO = $4008; + +type + {$EXTERNALSYM IPX_NETNUM_DATA} + IPX_NETNUM_DATA = record + netnum : Array [0..3] of Byte; // input: IPX network number + hopcount : Word; // output: hop count to this network, in machine order + netdelay : Word; // output: tick count to this network, in machine order + cardnum : Integer; // output: 0-based adapter number used to route to this net; + // can be used as adapternum input to IPX_ADDRESS + router : Array [0..5] of Byte; // output: MAC address of the next hop router, zeroed if + // the network is directly attached + end; + {$NODEFINE TIPXNetNumData} + TIPXNetNumData = IPX_NETNUM_DATA; + {$NODEFINE PIPXNetNumData} + PIPXNetNumData = ^TIPXNetNumData; + {$EXTERNALSYM PIPX_NETNUM_DATA} + PIPX_NETNUM_DATA = PIPXNetNumData; + +const +// Like IPX_GETNETINFO except it does not issue RIP requests. If the +// network is in IPX's cache it will return the information, otherwise {Do not Localize} +// it will fail (see also IPX_RERIPNETNUMBER which always forces a +// re-RIP). Not valid with setsockopt(). optval points to an instance of +// the IPX_NETNUM_DATA structure with the netnum filled in. + {$EXTERNALSYM IPX_GETNETINFO_NORIP} + IPX_GETNETINFO_NORIP = $4009; + +// Get information on a connected SPX socket. optval points +// to an instance of the IPX_SPXCONNSTATUS_DATA structure. +// *** All numbers are in Novell (high-low) order. *** + {$EXTERNALSYM IPX_SPXGETCONNECTIONSTATUS} + IPX_SPXGETCONNECTIONSTATUS = $400B; + +type + {$EXTERNALSYM IPX_SPXCONNSTATUS_DATA} + IPX_SPXCONNSTATUS_DATA = record + ConnectionState : Byte; + WatchDogActive : Byte; + LocalConnectionId : Word; + RemoteConnectionId : Word; + LocalSequenceNumber : Word; + LocalAckNumber : Word; + LocalAllocNumber : Word; + RemoteAckNumber : Word; + RemoteAllocNumber : Word; + LocalSocket : Word; + ImmediateAddress : Array [0..5] of Byte; + RemoteNetwork : Array [0..3] of Byte; + RemoteNode : Array [0..5] of Byte; + RemoteSocket : Word; + RetransmissionCount : Word; + EstimatedRoundTripDelay : Word; // In milliseconds + RetransmittedPackets : Word; + SuppressedPacket : Word; + end; + {$NODEFINE TIPXSPXConnStatusData} + TIPXSPXConnStatusData = IPX_SPXCONNSTATUS_DATA; + {$NODEFINE PIPXSPXConnStatusData} + PIPXSPXConnStatusData = ^TIPXSPXConnStatusData; + {$EXTERNALSYM PIPX_SPXCONNSTATUS_DATA} + PIPX_SPXCONNSTATUS_DATA = PIPXSPXConnStatusData; + +const +// Get notification when the status of an adapter that IPX is +// bound to changes. Typically this will happen when a wan line +// goes up or down. Not valid with setsockopt(). optval points +// to a buffer which contains an IPX_ADDRESS_DATA structure +// followed immediately by a HANDLE to an unsignaled event. +// +// When the getsockopt() query is submitted, it will complete +// successfully. However, the IPX_ADDRESS_DATA pointed to by +// optval will not be updated at that point. Instead the +// request is queued internally inside the transport. +// +// When the status of an adapter changes, IPX will locate a +// queued getsockopt() query and fill in all the fields in the +// IPX_ADDRESS_DATA structure. It will then signal the event +// pointed to by the HANDLE in the optval buffer. This handle +// should be obtained before calling getsockopt() by calling +// CreateEvent(). If multiple getsockopts() are submitted at +// once, different events must be used. +// +// The event is used because the call needs to be asynchronous +// but currently getsockopt() does not support this. +// +// WARNING: In the current implementation, the transport will +// only signal one queued query for each status change. Therefore +// only one service which uses this query should be running at +// once. + {$EXTERNALSYM IPX_ADDRESS_NOTIFY} + IPX_ADDRESS_NOTIFY = $400C; + +// Get the maximum number of adapters present. If this call returns +// n then the adapters are numbered 0 through n-1. Not valid +// with setsockopt(). optval points to an int where the value +// is returned. + {$EXTERNALSYM IPX_MAX_ADAPTER_NUM} + IPX_MAX_ADAPTER_NUM = $400D; + +// Like IPX_GETNETINFO except it forces IPX to re-RIP even if the +// network is in its cache (but not if it is directly attached to). +// Not valid with setsockopt(). optval points to an instance of +// the IPX_NETNUM_DATA structure with the netnum filled in. + {$EXTERNALSYM IPX_RERIPNETNUMBER} + IPX_RERIPNETNUMBER = $400E; + +// A hint that broadcast packets may be received. The default is +// TRUE. Applications that do not need to receive broadcast packets +// should set this sockopt to FALSE which may cause better system +// performance (note that it does not necessarily cause broadcasts +// to be filtered for the application). Not valid with getsockopt(). +// optval points to a BOOL. + {$EXTERNALSYM IPX_RECEIVE_BROADCAST} + IPX_RECEIVE_BROADCAST = $400F; + +// On SPX connections, don't delay before sending ack. Applications {Do not Localize} +// that do not tend to have back-and-forth traffic over SPX should +// set this; it will increase the number of acks sent but will remove +// delays in sending acks. optval points to a BOOL. + {$EXTERNALSYM IPX_IMMEDIATESPXACK} + IPX_IMMEDIATESPXACK = $4010; + + +//============================================================= + +// wsnetbs.h +// Copyright (c) 1994-1999, Microsoft Corp. All rights reserved. +// +// Winapi.Windows Sockets include file for NETBIOS. This file contains all +// standardized NETBIOS information. Include this header file after +// winsock.h. + +// To open a NetBIOS socket, call the socket() function as follows: +// +// s = socket( AF_NETBIOS, {SOCK_SEQPACKET|SOCK_DGRAM}, -Lana ); +// +// where Lana is the NetBIOS Lana number of interest. For example, to +// open a socket for Lana 2, specify -2 as the "protocol" parameter +// to the socket() function. + + +// This is the structure of the SOCKADDR structure for NETBIOS. + +const + {$EXTERNALSYM NETBIOS_NAME_LENGTH} + NETBIOS_NAME_LENGTH = 16; + +type + {$EXTERNALSYM SOCKADDR_NB} + SOCKADDR_NB = record + snb_family : Smallint; + snb_type : u_short; + snb_name : array[0..NETBIOS_NAME_LENGTH-1] of AnsiChar; + end; + {$NODEFINE TSockAddrNB} + TSockAddrNB = SOCKADDR_NB; + {$NODEFINE PSockAddrNB} + PSockAddrNB = ^TSockAddrNB; + {$EXTERNALSYM PSOCKADDR_NB} + PSOCKADDR_NB = PSockAddrNB; + {$EXTERNALSYM LPSOCKADDR_NB} + LPSOCKADDR_NB = PSOCKADDR_NB; + +// Bit values for the snb_type field of SOCKADDR_NB. +const + {$EXTERNALSYM NETBIOS_UNIQUE_NAME} + NETBIOS_UNIQUE_NAME = $0000; + {$EXTERNALSYM NETBIOS_GROUP_NAME} + NETBIOS_GROUP_NAME = $0001; + {$EXTERNALSYM NETBIOS_TYPE_QUICK_UNIQUE} + NETBIOS_TYPE_QUICK_UNIQUE = $0002; + {$EXTERNALSYM NETBIOS_TYPE_QUICK_GROUP} + NETBIOS_TYPE_QUICK_GROUP = $0003; + +// A macro convenient for setting up NETBIOS SOCKADDRs. +{$EXTERNALSYM SET_NETBIOS_SOCKADDR} +procedure SET_NETBIOS_SOCKADDR(snb : PSockAddrNB; const SnbType : Word; const Name : PAnsiChar; const Port : AnsiChar); + + +//============================================================= + +// Copyright 1997 - 1998 Microsoft Corporation +// +// Module Name: +// +// ws2atm.h +// +// Abstract: +// +// Winsock 2 ATM Annex definitions. + +const + {$EXTERNALSYM ATMPROTO_AALUSER} + ATMPROTO_AALUSER = $00; // User-defined AAL + {$EXTERNALSYM ATMPROTO_AAL1} + ATMPROTO_AAL1 = $01; // AAL 1 + {$EXTERNALSYM ATMPROTO_AAL2} + ATMPROTO_AAL2 = $02; // AAL 2 + {$EXTERNALSYM ATMPROTO_AAL34} + ATMPROTO_AAL34 = $03; // AAL 3/4 + {$EXTERNALSYM ATMPROTO_AAL5} + ATMPROTO_AAL5 = $05; // AAL 5 + + {$EXTERNALSYM SAP_FIELD_ABSENT} + SAP_FIELD_ABSENT = $FFFFFFFE; + {$EXTERNALSYM SAP_FIELD_ANY} + SAP_FIELD_ANY = $FFFFFFFF; + {$EXTERNALSYM SAP_FIELD_ANY_AESA_SEL} + SAP_FIELD_ANY_AESA_SEL = $FFFFFFFA; + {$EXTERNALSYM SAP_FIELD_ANY_AESA_REST} + SAP_FIELD_ANY_AESA_REST = $FFFFFFFB; + + // values used for AddressType in struct ATM_ADDRESS + {$EXTERNALSYM ATM_E164} + ATM_E164 = $01; // E.164 addressing scheme + {$EXTERNALSYM ATM_NSAP} + ATM_NSAP = $02; // NSAP-style ATM Endsystem Address scheme + {$EXTERNALSYM ATM_AESA} + ATM_AESA = $02; // NSAP-style ATM Endsystem Address scheme + + {$EXTERNALSYM ATM_ADDR_SIZE} + ATM_ADDR_SIZE = 20; + +type + {$EXTERNALSYM ATM_ADDRESS} + ATM_ADDRESS = record + AddressType : DWORD; // E.164 or NSAP-style ATM Endsystem Address + NumofDigits : DWORD; // number of digits; + Addr : Array[0..ATM_ADDR_SIZE-1] of Byte; // IA5 digits for E164, BCD encoding for NSAP + // format as defined in the ATM Forum UNI 3.1 + end; + +// values used for Layer2Protocol in B-LLI +const + {$EXTERNALSYM BLLI_L2_ISO_1745} + BLLI_L2_ISO_1745 = $01; // Basic mode ISO 1745 + {$EXTERNALSYM BLLI_L2_Q921} + BLLI_L2_Q921 = $02; // CCITT Rec. Q.921 + {$EXTERNALSYM BLLI_L2_X25L} + BLLI_L2_X25L = $06; // CCITT Rec. X.25, link layer + {$EXTERNALSYM BLLI_L2_X25M} + BLLI_L2_X25M = $07; // CCITT Rec. X.25, multilink + {$EXTERNALSYM BLLI_L2_ELAPB} + BLLI_L2_ELAPB = $08; // Extended LAPB; for half duplex operation + {$EXTERNALSYM BLLI_L2_HDLC_NRM} + BLLI_L2_HDLC_NRM = $09; // HDLC NRM (ISO 4335) + {$EXTERNALSYM BLLI_L2_HDLC_ABM} + BLLI_L2_HDLC_ABM = $0A; // HDLC ABM (ISO 4335) + {$EXTERNALSYM BLLI_L2_HDLC_ARM} + BLLI_L2_HDLC_ARM = $0B; // HDLC ARM (ISO 4335) + {$EXTERNALSYM BLLI_L2_LLC} + BLLI_L2_LLC = $0C; // LAN logical link control (ISO 8802/2) + {$EXTERNALSYM BLLI_L2_X75} + BLLI_L2_X75 = $0D; // CCITT Rec. X.75, single link procedure + {$EXTERNALSYM BLLI_L2_Q922} + BLLI_L2_Q922 = $0E; // CCITT Rec. Q.922 + {$EXTERNALSYM BLLI_L2_USER_SPECIFIED} + BLLI_L2_USER_SPECIFIED = $10; // User Specified + {$EXTERNALSYM BLLI_L2_ISO_7776} + BLLI_L2_ISO_7776 = $11; // ISO 7776 DTE-DTE operation + +// values used for Layer3Protocol in B-LLI + {$EXTERNALSYM BLLI_L3_X25} + BLLI_L3_X25 = $06; // CCITT Rec. X.25, packet layer + {$EXTERNALSYM BLLI_L3_ISO_8208} + BLLI_L3_ISO_8208 = $07; // ISO/IEC 8208 (X.25 packet layer for DTE + {$EXTERNALSYM BLLI_L3_X223} + BLLI_L3_X223 = $08; // X.223/ISO 8878 + {$EXTERNALSYM BLLI_L3_SIO_8473} + BLLI_L3_SIO_8473 = $09; // ISO/IEC 8473 (OSI connectionless) + {$EXTERNALSYM BLLI_L3_T70} + BLLI_L3_T70 = $0A; // CCITT Rec. T.70 min. network layer + {$EXTERNALSYM BLLI_L3_ISO_TR9577} + BLLI_L3_ISO_TR9577 = $0B; // ISO/IEC TR 9577 Network Layer Protocol ID + {$EXTERNALSYM BLLI_L3_USER_SPECIFIED} + BLLI_L3_USER_SPECIFIED = $10; // User Specified + +// values used for Layer3IPI in B-LLI + {$EXTERNALSYM BLLI_L3_IPI_SNAP} + BLLI_L3_IPI_SNAP = $80; // IEEE 802.1 SNAP identifier + {$EXTERNALSYM BLLI_L3_IPI_IP} + BLLI_L3_IPI_IP = $CC; // Internet Protocol (IP) identifier + +type + {$EXTERNALSYM ATM_BLLI} + ATM_BLLI = record + // Identifies the layer-two protocol. + // Corresponds to the User information layer 2 protocol field in the B-LLI information element. + // A value of SAP_FIELD_ABSENT indicates that this field is not used, and a value of SAP_FIELD_ANY means wildcard. + Layer2Protocol : DWORD; // User information layer 2 protocol + // Identifies the user-specified layer-two protocol. + // Only used if the Layer2Protocol parameter is set to BLLI_L2_USER_SPECIFIED. + // The valid values range from zero-127. + // Corresponds to the User specified layer 2 protocol information field in the B-LLI information element. + Layer2UserSpecifiedProtocol : DWORD; // User specified layer 2 protocol information + // Identifies the layer-three protocol. + // Corresponds to the User information layer 3 protocol field in the B-LLI information element. + // A value of SAP_FIELD_ABSENT indicates that this field is not used, and a value of SAP_FIELD_ANY means wildcard. + Layer3Protocol : DWORD; // User information layer 3 protocol + // Identifies the user-specified layer-three protocol. + // Only used if the Layer3Protocol parameter is set to BLLI_L3_USER_SPECIFIED. + // The valid values range from zero-127. + // Corresponds to the User specified layer 3 protocol information field in the B-LLI information element. + Layer3UserSpecifiedProtocol : DWORD; // User specified layer 3 protocol information + // Identifies the layer-three Initial Protocol Identifier. + // Only used if the Layer3Protocol parameter is set to BLLI_L3_ISO_TR9577. + // Corresponds to the ISO/IEC TR 9577 Initial Protocol Identifier field in the B-LLI information element. + Layer3IPI : DWORD; // ISO/IEC TR 9577 Initial Protocol Identifier + // Identifies the 802.1 SNAP identifier. + // Only used if the Layer3Protocol parameter is set to BLLI_L3_ISO_TR9577 and Layer3IPI is set to BLLI_L3_IPI_SNAP, + // indicating an IEEE 802.1 SNAP identifier. Corresponds to the OUI and PID fields in the B-LLI information element. + SnapID : Array[0..4] of Byte; // SNAP ID consisting of OUI and PID + end; + +// values used for the HighLayerInfoType field in ATM_BHLI +const + {$EXTERNALSYM BHLI_ISO} + BHLI_ISO = $00; // ISO + {$EXTERNALSYM BHLI_UserSpecific} + BHLI_UserSpecific = $01; // User Specific + {$EXTERNALSYM BHLI_HighLayerProfile} + BHLI_HighLayerProfile = $02; // High layer profile (only in UNI3.0) + {$EXTERNALSYM BHLI_VendorSpecificAppId} + BHLI_VendorSpecificAppId = $03; // Vendor-Specific Application ID + +type + {$EXTERNALSYM ATM_BHLI} + ATM_BHLI = record + // Identifies the high layer information type field in the B-LLI information element. + // Note that the type BHLI_HighLayerProfile has been eliminated in UNI 3.1. + // A value of SAP_FIELD_ABSENT indicates that B-HLI is not present, and a value of SAP_FIELD_ANY means wildcard. + HighLayerInfoType : DWORD; // High Layer Information Type + // Identifies the number of bytes from one to eight in the HighLayerInfo array. + // Valid values include eight for the cases of BHLI_ISO and BHLI_UserSpecific, + // four for BHLI_HighLayerProfile, and seven for BHLI_VendorSpecificAppId. + HighLayerInfoLength : DWORD; // number of bytes in HighLayerInfo + // Identifies the high layer information field in the B-LLI information element. + // In the case of HighLayerInfoType being BHLI_VendorSpecificAppId, + // the first 3 bytes consist of a globally-administered Organizationally Unique Identifier (OUI) + // (as per IEEE standard 802-1990), followed by a 4-byte application identifier, + // which is administered by the vendor identified by the OUI. + // Value for the case of BHLI_UserSpecific is user defined and requires bilateral agreement between two end users. + HighLayerInfo : Array[0..7] of Byte; // the value dependent on the HighLayerInfoType field + end; + +// A new address family, AF_ATM, is introduced for native ATM services, +// and the corresponding SOCKADDR structure, sockaddr_atm, is defined in the following. +// To open a socket for native ATM services, parameters in socket should contain +// AF_ATM, SOCK_RAW, and ATMPROTO_AAL5 or ATMPROTO_AALUSER, respectively. + {$EXTERNALSYM SOCKADDR_ATM} + SOCKADDR_ATM = record + // Identifies the address family, which is AF_ATM in this case. + satm_family : u_short; + // Identifies the ATM address that could be either in E.164 or NSAP-style ATM End Systems Address format. + // This field will be mapped to the Called Party Number IE (Information Element) + // if it is specified in bind and WSPBind for a listening socket, or in connect, WSAConnect, WSPConnect, + // WSAJoinLeaf, or WSPJoinLeaf for a connecting socket. + // It will be mapped to the Calling Party Number IE if specified in bind and WSPBind for a connecting socket. + satm_number : ATM_ADDRESS; + // Identifies the fields in the B-LLI Information Element that are used along with satm_bhli to identify an application. + // Note that the B-LLI layer two information is treated as not present + // if its Layer2Protocol field contains SAP_FIELD_ABSENT, or as a wildcard if it contains SAP_FIELD_ANY. + // Similarly, the B-LLI layer three information is treated as not present + // if its Layer3Protocol field contains SAP_FIELD_ABSENT, or as a wildcard if it contains SAP_FIELD_ANY. + satm_blli : ATM_BLLI; // B-LLI + // Identifies the fields in the B-HLI Information Element that are used along with satm_blli to identify an application. + satm_bhli : ATM_BHLI; // B-HLI + end; + {$NODEFINE TSockAddrATM} + TSockAddrATM = SOCKADDR_ATM; + {$NODEFINE PSockAddrATM} + PSockAddrATM = ^TSockAddrATM; + {$EXTERNALSYM PSOCKADDR_ATM} + PSOCKADDR_ATM = PSockAddrATM; + {$EXTERNALSYM LPSOCKADDR_ATM} + LPSOCKADDR_ATM = PSOCKADDR_ATM; + + {$EXTERNALSYM Q2931_IE_TYPE} + Q2931_IE_TYPE = (IE_AALParameters, IE_TrafficDescriptor, + IE_BroadbandBearerCapability, IE_BHLI, IE_BLLI,IE_CalledPartyNumber, + IE_CalledPartySubaddress, IE_CallingPartyNumber, IE_CallingPartySubaddress, + IE_Cause, IE_QOSClass, IE_TransitNetworkSelection + ); + + {$EXTERNALSYM Q2931_IE} + Q2931_IE = record + IEType : Q2931_IE_TYPE; + IELength : ULONG; + IE : Array[0..0] of Byte; + end; + +// manifest constants for the AALType field in struct AAL_PARAMETERS_IE + {$EXTERNALSYM AAL_TYPE} + AAL_TYPE = LongInt; + +const + {$EXTERNALSYM AALTYPE_5} + AALTYPE_5 = 5; // AAL 5 + {$EXTERNALSYM AALTYPE_USER} + AALTYPE_USER = 16; // user-defined AAL + + // values used for the Mode field in struct AAL5_PARAMETERS + {$EXTERNALSYM AAL5_MODE_MESSAGE} + AAL5_MODE_MESSAGE = $01; + {$EXTERNALSYM AAL5_MODE_STREAMING} + AAL5_MODE_STREAMING = $02; + +// values used for the SSCSType field in struct AAL5_PARAMETERS + {$EXTERNALSYM AAL5_SSCS_NULL} + AAL5_SSCS_NULL = $00; + {$EXTERNALSYM AAL5_SSCS_SSCOP_ASSURED} + AAL5_SSCS_SSCOP_ASSURED = $01; + {$EXTERNALSYM AAL5_SSCS_SSCOP_NON_ASSURED} + AAL5_SSCS_SSCOP_NON_ASSURED = $02; + {$EXTERNALSYM AAL5_SSCS_FRAME_RELAY} + AAL5_SSCS_FRAME_RELAY = $04; + +type + {$EXTERNALSYM AAL5_PARAMETERS} + AAL5_PARAMETERS = record + ForwardMaxCPCSSDUSize : ULONG; + BackwardMaxCPCSSDUSize : ULONG; + Mode : Byte; // only available in UNI 3.0 + SSCSType : Byte; + end; + + {$EXTERNALSYM AALUSER_PARAMETERS} + AALUSER_PARAMETERS = record + UserDefined : ULONG; + end; + + {$EXTERNALSYM AAL_PARAMETERS_IE} + AAL_PARAMETERS_IE = record + AALType : AAL_TYPE; + case Byte of + 0: ( AAL5Parameters : AAL5_PARAMETERS ); + 1: ( AALUserParameters : AALUSER_PARAMETERS ); + end; + + {$EXTERNALSYM ATM_TD} + ATM_TD = record + PeakCellRate_CLP0 : ULONG; + PeakCellRate_CLP01 : ULONG; + SustainableCellRate_CLP0 : ULONG; + SustainableCellRate_CLP01 : ULONG; + MaxBurstSize_CLP0 : ULONG; + MaxBurstSize_CLP01 : ULONG; + Tagging : LongBool; + end; + + {$EXTERNALSYM ATM_TRAFFIC_DESCRIPTOR_IE} + ATM_TRAFFIC_DESCRIPTOR_IE = record + _Forward : ATM_TD; + Backward : ATM_TD; + BestEffort : LongBool; + end; + +// values used for the BearerClass field in struct ATM_BROADBAND_BEARER_CAPABILITY_IE +const + {$EXTERNALSYM BCOB_A} + BCOB_A = $01; // Bearer class A + {$EXTERNALSYM BCOB_C} + BCOB_C = $03; // Bearer class C + {$EXTERNALSYM BCOB_X} + BCOB_X = $10; // Bearer class X + +// values used for the TrafficType field in struct ATM_BROADBAND_BEARER_CAPABILITY_IE + {$EXTERNALSYM TT_NOIND} + TT_NOIND = $00; // No indication of traffic type + {$EXTERNALSYM TT_CBR} + TT_CBR = $04; // Constant bit rate + {$EXTERNALSYM TT_VBR} + TT_VBR = $06; // Variable bit rate + +// values used for the TimingRequirements field in struct ATM_BROADBAND_BEARER_CAPABILITY_IE + {$EXTERNALSYM TR_NOIND} + TR_NOIND = $00; // No timing requirement indication + {$EXTERNALSYM TR_END_TO_END} + TR_END_TO_END = $01; // End-to-end timing required + {$EXTERNALSYM TR_NO_END_TO_END} + TR_NO_END_TO_END = $02; // End-to-end timing not required + +// values used for the ClippingSusceptability field in struct ATM_BROADBAND_BEARER_CAPABILITY_IE + {$EXTERNALSYM CLIP_NOT} + CLIP_NOT = $00; // Not susceptible to clipping + {$EXTERNALSYM CLIP_SUS} + CLIP_SUS = $20; // Susceptible to clipping + +// values used for the UserPlaneConnectionConfig field in struct ATM_BROADBAND_BEARER_CAPABILITY_IE + {$EXTERNALSYM UP_P2P} + UP_P2P = $00; // Point-to-point connection + {$EXTERNALSYM UP_P2MP} + UP_P2MP = $01; // Point-to-multipoint connection + +type + {$EXTERNALSYM ATM_BROADBAND_BEARER_CAPABILITY_IE} + ATM_BROADBAND_BEARER_CAPABILITY_IE = record + BearerClass : Byte; + TrafficType : Byte; + TimingRequirements : Byte; + ClippingSusceptability : Byte; + UserPlaneConnectionConfig : Byte; + end; + {$EXTERNALSYM ATM_BHLI_IE} + ATM_BHLI_IE = ATM_BHLI; + +// values used for the Layer2Mode field in struct ATM_BLLI_IE +const + {$EXTERNALSYM BLLI_L2_MODE_NORMAL} + BLLI_L2_MODE_NORMAL = $40; + {$EXTERNALSYM BLLI_L2_MODE_EXT} + BLLI_L2_MODE_EXT = $80; + +// values used for the Layer3Mode field in struct ATM_BLLI_IE + {$EXTERNALSYM BLLI_L3_MODE_NORMAL} + BLLI_L3_MODE_NORMAL = $40; + {$EXTERNALSYM BLLI_L3_MODE_EXT} + BLLI_L3_MODE_EXT = $80; + +// values used for the Layer3DefaultPacketSize field in struct ATM_BLLI_IE + {$EXTERNALSYM BLLI_L3_PACKET_16} + BLLI_L3_PACKET_16 = $04; + {$EXTERNALSYM BLLI_L3_PACKET_32} + BLLI_L3_PACKET_32 = $05; + {$EXTERNALSYM BLLI_L3_PACKET_64} + BLLI_L3_PACKET_64 = $06; + {$EXTERNALSYM BLLI_L3_PACKET_128} + BLLI_L3_PACKET_128 = $07; + {$EXTERNALSYM BLLI_L3_PACKET_256} + BLLI_L3_PACKET_256 = $08; + {$EXTERNALSYM BLLI_L3_PACKET_512} + BLLI_L3_PACKET_512 = $09; + {$EXTERNALSYM BLLI_L3_PACKET_1024} + BLLI_L3_PACKET_1024 = $0A; + {$EXTERNALSYM BLLI_L3_PACKET_2048} + BLLI_L3_PACKET_2048 = $0B; + {$EXTERNALSYM BLLI_L3_PACKET_4096} + BLLI_L3_PACKET_4096 = $0C; + +type + {$EXTERNALSYM ATM_BLLI_IE} + ATM_BLLI_IE = record + Layer2Protocol : DWORD; // User information layer 2 protocol + Layer2Mode : Byte; + Layer2WindowSize : Byte; + Layer2UserSpecifiedProtocol : DWORD; // User specified layer 2 protocol information + Layer3Protocol : DWORD; // User information layer 3 protocol + Layer3Mode : Byte; + Layer3DefaultPacketSize : Byte; + Layer3PacketWindowSize : Byte; + Layer3UserSpecifiedProtocol : DWORD; // User specified layer 3 protocol information + Layer3IPI : DWORD; // ISO/IEC TR 9577 Initial Protocol Identifier + SnapID : Array[0..4] of Byte; // SNAP ID consisting of OUI and PID + end; + {$EXTERNALSYM ATM_CALLED_PARTY_NUMBER_IE} + ATM_CALLED_PARTY_NUMBER_IE = ATM_ADDRESS; + {$EXTERNALSYM ATM_CALLED_PARTY_SUBADDRESS_IE} + ATM_CALLED_PARTY_SUBADDRESS_IE = ATM_ADDRESS; + +// values used for the Presentation_Indication field in struct ATM_CALLING_PARTY_NUMBER_IE +const + {$EXTERNALSYM PI_ALLOWED} + PI_ALLOWED = $00; + {$EXTERNALSYM PI_RESTRICTED} + PI_RESTRICTED = $40; + {$EXTERNALSYM PI_NUMBER_NOT_AVAILABLE} + PI_NUMBER_NOT_AVAILABLE = $80; + +// values used for the Screening_Indicator field in struct ATM_CALLING_PARTY_NUMBER_IE + {$EXTERNALSYM SI_USER_NOT_SCREENED} + SI_USER_NOT_SCREENED = $00; + {$EXTERNALSYM SI_USER_PASSED} + SI_USER_PASSED = $01; + {$EXTERNALSYM SI_USER_FAILED} + SI_USER_FAILED = $02; + {$EXTERNALSYM SI_NETWORK} + SI_NETWORK = $03; + +type + {$EXTERNALSYM ATM_CALLING_PARTY_NUMBER_IE} + ATM_CALLING_PARTY_NUMBER_IE = record + ATM_Number : ATM_ADDRESS; + Presentation_Indication : Byte; + Screening_Indicator : Byte; + end; + {$EXTERNALSYM ATM_CALLING_PARTY_SUBADDRESS_IE} + ATM_CALLING_PARTY_SUBADDRESS_IE = ATM_ADDRESS; + +// values used for the Location field in struct ATM_CAUSE_IE +const + {$EXTERNALSYM CAUSE_LOC_USER} + CAUSE_LOC_USER = $00; + {$EXTERNALSYM CAUSE_LOC_PRIVATE_LOCAL} + CAUSE_LOC_PRIVATE_LOCAL = $01; + {$EXTERNALSYM CAUSE_LOC_PUBLIC_LOCAL} + CAUSE_LOC_PUBLIC_LOCAL = $02; + {$EXTERNALSYM CAUSE_LOC_TRANSIT_NETWORK} + CAUSE_LOC_TRANSIT_NETWORK = $03; + {$EXTERNALSYM CAUSE_LOC_PUBLIC_REMOTE} + CAUSE_LOC_PUBLIC_REMOTE = $04; + {$EXTERNALSYM CAUSE_LOC_PRIVATE_REMOTE} + CAUSE_LOC_PRIVATE_REMOTE = $05; + {$EXTERNALSYM CAUSE_LOC_INTERNATIONAL_NETWORK} + CAUSE_LOC_INTERNATIONAL_NETWORK = $06; + {$EXTERNALSYM CAUSE_LOC_BEYOND_INTERWORKING} + CAUSE_LOC_BEYOND_INTERWORKING = $0A; + +// values used for the Cause field in struct ATM_CAUSE_IE + {$EXTERNALSYM CAUSE_UNALLOCATED_NUMBER} + CAUSE_UNALLOCATED_NUMBER = $01; + {$EXTERNALSYM CAUSE_NO_ROUTE_TO_TRANSIT_NETWORK} + CAUSE_NO_ROUTE_TO_TRANSIT_NETWORK = $02; + {$EXTERNALSYM CAUSE_NO_ROUTE_TO_DESTINATION} + CAUSE_NO_ROUTE_TO_DESTINATION = $03; + {$EXTERNALSYM CAUSE_VPI_VCI_UNACCEPTABLE} + CAUSE_VPI_VCI_UNACCEPTABLE = $0A; + {$EXTERNALSYM CAUSE_NORMAL_CALL_CLEARING} + CAUSE_NORMAL_CALL_CLEARING = $10; + {$EXTERNALSYM CAUSE_USER_BUSY} + CAUSE_USER_BUSY = $11; + {$EXTERNALSYM CAUSE_NO_USER_RESPONDING} + CAUSE_NO_USER_RESPONDING = $12; + {$EXTERNALSYM CAUSE_CALL_REJECTED} + CAUSE_CALL_REJECTED = $15; + {$EXTERNALSYM CAUSE_NUMBER_CHANGED} + CAUSE_NUMBER_CHANGED = $16; + {$EXTERNALSYM CAUSE_USER_REJECTS_CLIR} + CAUSE_USER_REJECTS_CLIR = $17; + {$EXTERNALSYM CAUSE_DESTINATION_OUT_OF_ORDER} + CAUSE_DESTINATION_OUT_OF_ORDER = $1B; + {$EXTERNALSYM CAUSE_INVALID_NUMBER_FORMAT} + CAUSE_INVALID_NUMBER_FORMAT = $1C; + {$EXTERNALSYM CAUSE_STATUS_ENQUIRY_RESPONSE} + CAUSE_STATUS_ENQUIRY_RESPONSE = $1E; + {$EXTERNALSYM CAUSE_NORMAL_UNSPECIFIED} + CAUSE_NORMAL_UNSPECIFIED = $1F; + {$EXTERNALSYM CAUSE_VPI_VCI_UNAVAILABLE} + CAUSE_VPI_VCI_UNAVAILABLE = $23; + {$EXTERNALSYM CAUSE_NETWORK_OUT_OF_ORDER} + CAUSE_NETWORK_OUT_OF_ORDER = $26; + {$EXTERNALSYM CAUSE_TEMPORARY_FAILURE} + CAUSE_TEMPORARY_FAILURE = $29; + {$EXTERNALSYM CAUSE_ACCESS_INFORMAION_DISCARDED} + CAUSE_ACCESS_INFORMAION_DISCARDED = $2B; + {$EXTERNALSYM CAUSE_NO_VPI_VCI_AVAILABLE} + CAUSE_NO_VPI_VCI_AVAILABLE = $2D; + {$EXTERNALSYM CAUSE_RESOURCE_UNAVAILABLE} + CAUSE_RESOURCE_UNAVAILABLE = $2F; + {$EXTERNALSYM CAUSE_QOS_UNAVAILABLE} + CAUSE_QOS_UNAVAILABLE = $31; + {$EXTERNALSYM CAUSE_USER_CELL_RATE_UNAVAILABLE} + CAUSE_USER_CELL_RATE_UNAVAILABLE = $33; + {$EXTERNALSYM CAUSE_BEARER_CAPABILITY_UNAUTHORIZED} + CAUSE_BEARER_CAPABILITY_UNAUTHORIZED = $39; + {$EXTERNALSYM CAUSE_BEARER_CAPABILITY_UNAVAILABLE} + CAUSE_BEARER_CAPABILITY_UNAVAILABLE = $3A; + {$EXTERNALSYM CAUSE_OPTION_UNAVAILABLE} + CAUSE_OPTION_UNAVAILABLE = $3F; + {$EXTERNALSYM CAUSE_BEARER_CAPABILITY_UNIMPLEMENTED} + CAUSE_BEARER_CAPABILITY_UNIMPLEMENTED = $41; + {$EXTERNALSYM CAUSE_UNSUPPORTED_TRAFFIC_PARAMETERS} + CAUSE_UNSUPPORTED_TRAFFIC_PARAMETERS = $49; + {$EXTERNALSYM CAUSE_INVALID_CALL_REFERENCE} + CAUSE_INVALID_CALL_REFERENCE = $51; + {$EXTERNALSYM CAUSE_CHANNEL_NONEXISTENT} + CAUSE_CHANNEL_NONEXISTENT = $52; + {$EXTERNALSYM CAUSE_INCOMPATIBLE_DESTINATION} + CAUSE_INCOMPATIBLE_DESTINATION = $58; + {$EXTERNALSYM CAUSE_INVALID_ENDPOINT_REFERENCE} + CAUSE_INVALID_ENDPOINT_REFERENCE = $59; + {$EXTERNALSYM CAUSE_INVALID_TRANSIT_NETWORK_SELECTION} + CAUSE_INVALID_TRANSIT_NETWORK_SELECTION = $5B; + {$EXTERNALSYM CAUSE_TOO_MANY_PENDING_ADD_PARTY} + CAUSE_TOO_MANY_PENDING_ADD_PARTY = $5C; + {$EXTERNALSYM CAUSE_AAL_PARAMETERS_UNSUPPORTED} + CAUSE_AAL_PARAMETERS_UNSUPPORTED = $5D; + {$EXTERNALSYM CAUSE_MANDATORY_IE_MISSING} + CAUSE_MANDATORY_IE_MISSING = $60; + {$EXTERNALSYM CAUSE_UNIMPLEMENTED_MESSAGE_TYPE} + CAUSE_UNIMPLEMENTED_MESSAGE_TYPE = $61; + {$EXTERNALSYM CAUSE_UNIMPLEMENTED_IE} + CAUSE_UNIMPLEMENTED_IE = $63; + {$EXTERNALSYM CAUSE_INVALID_IE_CONTENTS} + CAUSE_INVALID_IE_CONTENTS = $64; + {$EXTERNALSYM CAUSE_INVALID_STATE_FOR_MESSAGE} + CAUSE_INVALID_STATE_FOR_MESSAGE = $65; + {$EXTERNALSYM CAUSE_RECOVERY_ON_TIMEOUT} + CAUSE_RECOVERY_ON_TIMEOUT = $66; + {$EXTERNALSYM CAUSE_INCORRECT_MESSAGE_LENGTH} + CAUSE_INCORRECT_MESSAGE_LENGTH = $68; + {$EXTERNALSYM CAUSE_PROTOCOL_ERROR} + CAUSE_PROTOCOL_ERROR = $6F; + +// values used for the Condition portion of the Diagnostics field +// in struct ATM_CAUSE_IE, for certain Cause values + {$EXTERNALSYM CAUSE_COND_UNKNOWN} + CAUSE_COND_UNKNOWN = $00; + {$EXTERNALSYM CAUSE_COND_PERMANENT} + CAUSE_COND_PERMANENT = $01; + {$EXTERNALSYM CAUSE_COND_TRANSIENT} + CAUSE_COND_TRANSIENT = $02; + +// values used for the Rejection Reason portion of the Diagnostics field +// in struct ATM_CAUSE_IE, for certain Cause values + {$EXTERNALSYM CAUSE_REASON_USER} + CAUSE_REASON_USER = $00; + {$EXTERNALSYM CAUSE_REASON_IE_MISSING} + CAUSE_REASON_IE_MISSING = $04; + {$EXTERNALSYM CAUSE_REASON_IE_INSUFFICIENT} + CAUSE_REASON_IE_INSUFFICIENT = $08; + +// values used for the P-U flag of the Diagnostics field +// in struct ATM_CAUSE_IE, for certain Cause values + {$EXTERNALSYM CAUSE_PU_PROVIDER} + CAUSE_PU_PROVIDER = $00; + {$EXTERNALSYM CAUSE_PU_USER} + CAUSE_PU_USER = $08; + +// values used for the N-A flag of the Diagnostics field +// in struct ATM_CAUSE_IE, for certain Cause values + {$EXTERNALSYM CAUSE_NA_NORMAL} + CAUSE_NA_NORMAL = $00; + {$EXTERNALSYM CAUSE_NA_ABNORMAL} + CAUSE_NA_ABNORMAL = $04; + +type + {$EXTERNALSYM ATM_CAUSE_IE} + ATM_CAUSE_IE = record + Location : Byte; + Cause : Byte; + DiagnosticsLength : Byte; + Diagnostics : Array[0..3] of Byte; + end; + +// values used for the QOSClassForward and QOSClassBackward +// field in struct ATM_QOS_CLASS_IE +const + {$EXTERNALSYM QOS_CLASS0} + QOS_CLASS0 = $00; + {$EXTERNALSYM QOS_CLASS1} + QOS_CLASS1 = $01; + {$EXTERNALSYM QOS_CLASS2} + QOS_CLASS2 = $02; + {$EXTERNALSYM QOS_CLASS3} + QOS_CLASS3 = $03; + {$EXTERNALSYM QOS_CLASS4} + QOS_CLASS4 = $04; + +type + {$EXTERNALSYM ATM_QOS_CLASS_IE} + ATM_QOS_CLASS_IE = record + QOSClassForward : Byte; + QOSClassBackward : Byte; + end; + +// values used for the TypeOfNetworkId field in struct ATM_TRANSIT_NETWORK_SELECTION_IE +const + {$EXTERNALSYM TNS_TYPE_NATIONAL} + TNS_TYPE_NATIONAL = $40; + +// values used for the NetworkIdPlan field in struct ATM_TRANSIT_NETWORK_SELECTION_IE + {$EXTERNALSYM TNS_PLAN_CARRIER_ID_CODE} + TNS_PLAN_CARRIER_ID_CODE = $01; + +type + {$EXTERNALSYM ATM_TRANSIT_NETWORK_SELECTION_IE} + ATM_TRANSIT_NETWORK_SELECTION_IE = record + TypeOfNetworkId : Byte; + NetworkIdPlan : Byte; + NetworkIdLength : Byte; + NetworkId : Array[0..0] of Byte; + end; + +// ATM specific Ioctl codes +const + {$EXTERNALSYM SIO_GET_NUMBER_OF_ATM_DEVICES} + SIO_GET_NUMBER_OF_ATM_DEVICES = $50160001; + {$EXTERNALSYM SIO_GET_ATM_ADDRESS} + SIO_GET_ATM_ADDRESS = $d0160002; + {$EXTERNALSYM SIO_ASSOCIATE_PVC} + SIO_ASSOCIATE_PVC = $90160003; + {$EXTERNALSYM SIO_GET_ATM_CONNECTION_ID} + SIO_GET_ATM_CONNECTION_ID = $50160004; + +// ATM Connection Identifier +type + {$EXTERNALSYM ATM_CONNECTION_ID} + ATM_CONNECTION_ID = record + DeviceNumber : DWORD; + VPI : DWORD; + VCI : DWORD; + end; + +// Input buffer format for SIO_ASSOCIATE_PVC + {$EXTERNALSYM ATM_PVC_PARAMS} + ATM_PVC_PARAMS = record + PvcConnectionId : ATM_CONNECTION_ID; + PvcQos : QOS; + end; + + {$NODEFINE InitializeWinSock} + procedure InitializeWinSock; + {$NODEFINE UninitializeWinSock} + procedure UninitializeWinSock; + procedure InitializeStubsEx; + function Winsock2Loaded: Boolean; + function WinsockHandle : THandle; + +//JPM +{ +I made these symbols up so to prevent range check warnings in FreePascal. +SizeOf is a smallInt when an expression is evaluated at run-time. This +run-time evaluation makes no sense because the compiler knows these when compiling +so it should give us the numbers. + +} +const + {$EXTERNALSYM SIZE_WSACMSGHDR} + SIZE_WSACMSGHDR = DWORD(SizeOf(WSACMSGHDR)); + {$EXTERNALSYM SIZE_FARPROC} + SIZE_FARPROC = DWORD(SizeOf(FARPROC)); + {$EXTERNALSYM MAX_NATURAL_ALIGNMENT_SUB_1} + MAX_NATURAL_ALIGNMENT_SUB_1 = DWORD(MAX_NATURAL_ALIGNMENT - 1); + {$EXTERNALSYM SIZE_IP_MSFILTER} + SIZE_IP_MSFILTER = DWORD(SizeOf(ip_msfilter)); + {$EXTERNALSYM SIZE_TINADDR} + SIZE_TINADDR = DWORD(SizeOf(TInAddr)); + {$EXTERNALSYM SIZE_TIN6ADDR} + SIZE_TIN6ADDR = DWORD(SizeOf(TIn6Addr)); + {$EXTERNALSYM SIZE_TSOCKADDRIN} + SIZE_TSOCKADDRIN = DWORD(SizeOf(TSockAddrIn)); + {$EXTERNALSYM SIZE_TSOCKADDRIN6} + SIZE_TSOCKADDRIN6 = DWORD(SizeOf(TSockAddrIn6)); + {$EXTERNALSYM SIZE_GROUP_FILTER} + SIZE_GROUP_FILTER = DWORD(SizeOf(GROUP_FILTER)); + {$EXTERNALSYM SIZE_TADDRINFO} + SIZE_TADDRINFO = DWORD(SizeOf(TAddrInfo)); + {$EXTERNALSYM SIZE_SOCKADDR_STORAGE} + SIZE_SOCKADDR_STORAGE = DWORD(sizeof(SOCKADDR_STORAGE)); + {$IFNDEF WINCE} + {$EXTERNALSYM SIZE_TWSAMSG} + SIZE_TWSAMSG = DWORD(SizeOf(TWSAMSG)); + {$ENDIF} + {$EXTERNALSYM SIZE_GUID} + SIZE_GUID = DWORD(SizeOf(TGuid)); + {$EXTERNALSYM SIZE_INTEGER} + SIZE_INTEGER = DWORD(SizeOf(Integer)); + +//============================================================= + +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// Module Name: +// +// mstcpip.h +// +// Abstract: +// +// This module contains Microsoft-specific extensions to the core +// Winsock definitions. +// +// Environment: +// +// user mode or kernel mode + +type + {$EXTERNALSYM tcp_keepalive} + tcp_keepalive = record + onoff: u_long; + keepalivetime: u_long; + keepaliveinterval: u_long; + end; + +const + {$EXTERNALSYM SIO_KEEPALIVE_VALS} + SIO_KEEPALIVE_VALS = DWORD(IOC_IN or IOC_VENDOR or 4); + + RSWinsockCallError = 'Error on call to Winsock2 library function %s'; + RSWinsockLoadError = 'Error on loading Winsock2 library (%s)'; + +//============================================================= +implementation +//============================================================= + +var + hWinSockDll : THandle = 0; // WS2_32.DLL handle + {$IFNDEF WINCE} + hMSWSockDll : THandle = 0; // MSWSOCK.DLL handle + {$ENDIF} + +function WinsockHandle : THandle; +begin + Result := hWinSockDll; +end; + +function Winsock2Loaded : Boolean; +begin + Result := hWinSockDll <> 0; +end; + +procedure InitializeWinSock; +var + LData: TWSAData; + LError: DWORD; +begin + if hWinSockDll = 0 then begin + hWinSockDll := SafeLoadLibrary(WINSOCK2_DLL); + if hWinSockDll <> 0 then begin + LError := WSAStartup($202, LData); + if LError = 0 then begin + Exit; + end; + Winapi.Windows.FreeLibrary(hWinSockDll); + hWinSockDll := 0; + end else begin + LError := Winapi.Windows.GetLastError; + end; + raise EIdWinsockStubError.Build(LError, RSWinsockLoadError, [WINSOCK2_DLL]); + end; +end; + +{$IFNDEF WINCE} +procedure LoadMSWSock; +{$IFDEF USE_INLINE}inline;{$ENDIF} +begin + if hMSWSockDll = 0 then begin + hMSWSockDll := SafeLoadLibrary(MSWSOCK_DLL); + if hMSWSockDll = 0 then begin + raise EIdWinsockStubError.Build(Winapi.Windows.GetLastError, RSWinsockLoadError, [MSWSOCK_DLL]); + end; + end; +end; +{$ENDIF} + +procedure UninitializeWinSock; +begin +{$IFNDEF WINCE} + if hMSWSockDll <> 0 then + begin + FreeLibrary(hMSWSockDll); + hMSWSockDll := 0; + end; +{$ENDIF} + if hWinSockDll <> 0 then + begin + WSACleanup; + FreeLibrary(hWinSockDll); + hWinSockDll := 0; + end; +end; + +constructor EIdWinsockStubError.Build(AWin32Error: DWORD; const ATitle: String; AArgs: array of const); +begin + FTitle := Format(ATitle, AArgs, TFormatSettings.Create); + FWin32Error := AWin32Error; + if AWin32Error = 0 then begin + inherited Create(FTitle); + end else + begin + FWin32ErrorMessage := System.SysUtils.SysErrorMessage(AWin32Error); + inherited Create(FTitle + ': ' + FWin32ErrorMessage); {Do not Localize} + end; +end; + +{ IMPORTANT!!! + +WindowsCE only has a Unicode (WideChar) version of GetProcAddress. We could use +a version of GetProcAddress in the FreePascal dynlibs unit but that does a +conversion from ASCII to Unicode which might not be necessary since most calls +pass a constant anyway. +} +function FixupStub(hDll: THandle; const AName:{$IFDEF WINCE}TIdUnicodeString{$ELSE}string{$ENDIF}): Pointer; +{$IFDEF USE_INLINE}inline;{$ENDIF} +begin + if hDll = 0 then begin + raise EIdWinsockStubError.Build(WSANOTINITIALISED, RSWinsockCallError, [AName]); + end; + Result := Winapi.Windows.GetProcAddress(hDll, {$IFDEF WINCE}PWideChar{$ELSE}PChar{$ENDIF}(AName)); + if Result = nil then begin + raise EIdWinsockStubError.Build(WSAEINVAL, RSWinsockCallError, [AName]); + end; +end; + +function FixupStubEx(hSocket: TSocket; const AName: string; const AGuid: TGUID): Pointer; +var + LStatus: LongInt; + LBytesSend: DWORD; +begin + LStatus := WSAIoctl(hSocket, SIO_GET_EXTENSION_FUNCTION_POINTER, @AGuid, LongWord(SIZE_GUID), + @Result, SIZE_FARPROC, @LBytesSend, nil, nil); + if LStatus <> 0 then begin + raise EIdWinsockStubError.Build(WSAGetLastError, RSWinsockCallError, [AName]); + end; +end; + +function Stub_WSAStartup(const wVersionRequired: word; out WSData: TWSAData): Integer; stdcall; +begin + @WSAStartup := FixupStub(hWinSockDll, 'WSAStartup'); {Do not Localize} + Result := WSAStartup(wVersionRequired, WSData); +end; + +function Stub_WSACleanup: Integer; stdcall; +begin + @WSACleanup := FixupStub(hWinSockDll, 'WSACleanup'); {Do not Localize} + Result := WSACleanup; +end; + +function Stub_accept(const s: TSocket; AAddr: PSockAddr; addrlen: PInteger): TSocket; stdcall; +begin + @accept := FixupStub(hWinSockDll, 'accept'); {Do not Localize} + Result := accept(s, AAddr, addrlen); +end; + +function Stub_bind(const s: TSocket; const name: PSockAddr; const namelen: Integer): Integer; stdcall; +begin + @bind := FixupStub(hWinSockDll, 'bind'); {Do not Localize} + Result := bind(s, name, namelen); +end; + +function Stub_closesocket(const s: TSocket): Integer; stdcall; +begin + @closesocket := FixupStub(hWinSockDll, 'closesocket'); {Do not Localize} + Result := closesocket(s); +end; + +function Stub_connect(const s: TSocket; const name: PSockAddr; const namelen: Integer): Integer; stdcall; +begin + @connect := FixupStub(hWinSockDll, 'connect'); {Do not Localize} + Result := connect(s, name, namelen); +end; + +function Stub_ioctlsocket(const s: TSocket; const cmd: DWORD; var arg: u_long): Integer; stdcall; +begin + @ioctlsocket := FixupStub(hWinSockDll, 'ioctlsocket'); {Do not Localize} + Result := ioctlsocket(s, cmd, arg); +end; + +function Stub_getpeername(const s: TSocket; const name: PSockAddr; var namelen: Integer): Integer; stdcall; +begin + @getpeername := FixupStub(hWinSockDll, 'getpeername'); {Do not Localize} + Result := getpeername(s, name, namelen); +end; + +function Stub_getsockname(const s: TSocket; const name: PSockAddr; var namelen: Integer): Integer; stdcall; +begin + @getsockname := FixupStub(hWinSockDll, 'getsockname'); {Do not Localize} + Result := getsockname(s, name, namelen); +end; + +function Stub_getsockopt(const s: TSocket; const level, optname: Integer; optval: PAnsiChar; var optlen: Integer): Integer; stdcall; +begin + @getsockopt := FixupStub(hWinSockDll, 'getsockopt'); {Do not Localize} + Result := getsockopt(s, level, optname, optval, optlen); +end; + +function Stub_htonl(hostlong: u_long): u_long; stdcall; +begin + @htonl := FixupStub(hWinSockDll, 'htonl'); {Do not Localize} + Result := htonl(hostlong); +end; + +function Stub_htons(hostshort: u_short): u_short; stdcall; +begin + @htons := FixupStub(hWinSockDll, 'htons'); {Do not Localize} + Result := htons(hostshort); +end; + +function Stub_inet_addr(cp: PAnsiChar): u_long; stdcall; +begin + @inet_addr := FixupStub(hWinSockDll, 'inet_addr'); {Do not Localize} + Result := inet_addr(cp); +end; + +function Stub_inet_ntoa(inaddr: TInAddr): PAnsiChar; stdcall; +begin + @inet_ntoa := FixupStub(hWinSockDll, 'inet_ntoa'); {Do not Localize} + Result := inet_ntoa(inaddr); +end; + +function Stub_listen(const s: TSocket; backlog: Integer): Integer; stdcall; +begin + @listen := FixupStub(hWinSockDll, 'listen'); {Do not Localize} + Result := listen(s, backlog); +end; + +function Stub_ntohl(netlong: u_long): u_long; stdcall; +begin + @ntohl := FixupStub(hWinSockDll, 'ntohl'); {Do not Localize} + Result := ntohl(netlong); +end; + +function Stub_ntohs(netshort: u_short): u_short; stdcall; +begin + @ntohs := FixupStub(hWinSockDll, 'ntohs'); {Do not Localize} + Result := ntohs(netshort); +end; + +function Stub_recv(const s: TSocket; var Buf; len, flags: Integer): Integer; stdcall; +begin + @recv := FixupStub(hWinSockDll, 'recv'); {Do not Localize} + Result := recv(s, Buf, len, flags); +end; + +function Stub_recvfrom(const s: TSocket; var Buf; len, flags: Integer; from: PSockAddr; fromlen: PInteger): Integer; stdcall; +begin + @recvfrom := FixupStub(hWinSockDll, 'recvfrom'); {Do not Localize} + Result := recvfrom(s, Buf, len, flags, from, fromlen); +end; + +function Stub_select(nfds: Integer; readfds, writefds, exceptfds: PFDSet; timeout: PTimeVal): Integer; stdcall; +begin + @select := FixupStub(hWinSockDll, 'select'); {Do not Localize} + Result := select(nfds, readfds, writefds, exceptfds, timeout); +end; + +function Stub_send(const s: TSocket; const Buf; len, flags: Integer): Integer; stdcall; +begin + @send := FixupStub(hWinSockDll, 'send'); {Do not Localize} + Result := send(s, Buf, len, flags); +end; + +function Stub_sendto(const s: TSocket; const Buf; const len, flags: Integer; const addrto: PSockAddr; const tolen: Integer): Integer; stdcall; +begin + @sendto := FixupStub(hWinSockDll, 'sendto'); {Do not Localize} + Result := sendto(s, Buf, len, flags, addrto, tolen); +end; + +function Stub_setsockopt(const s: TSocket; const level, optname: Integer; optval: PAnsiChar; const optlen: Integer): Integer; stdcall; +begin + @setsockopt := FixupStub(hWinSockDll, 'setsockopt'); {Do not Localize} + Result := setsockopt(s, level, optname, optval, optlen); +end; + +function Stub_shutdown(const s: TSocket; const how: Integer): Integer; stdcall; +begin + @shutdown := FixupStub(hWinSockDll, 'shutdown'); {Do not Localize} + Result := shutdown(s, how); +end; + +function Stub_socket(const af, istruct, protocol: Integer): TSocket; stdcall; +begin + @socket := FixupStub(hWinSockDll, 'socket'); {Do not Localize} + Result := socket(af, istruct, protocol); +end; + +function Stub_gethostbyaddr(AAddr: Pointer; const len, addrtype: Integer): PHostEnt; stdcall; +begin + @gethostbyaddr := FixupStub(hWinSockDll, 'gethostbyaddr'); {Do not Localize} + Result := gethostbyaddr(AAddr, len, addrtype); +end; + +function Stub_gethostbyname(name: PAnsiChar): PHostEnt; stdcall; +begin + @gethostbyname := FixupStub(hWinSockDll, 'gethostbyname'); {Do not Localize} + Result := gethostbyname(name); +end; + +{$IFDEF WINCE} +function Stub_sethostname(pName : PAnsiChar; cName : Integer) : Integer; stdcall; +begin + @sethostname := FixupStub(hWinSockDll, 'sethostname'); {Do not Localize} + Result := sethostname(pName, cName); +end; +{$ENDIF} + +function Stub_gethostname(name: PAnsiChar; len: Integer): Integer; stdcall; +begin + @gethostname := FixupStub(hWinSockDll, 'gethostname'); {Do not Localize} + Result := gethostname(name, len); +end; + +function Stub_getservbyport(const port: Integer; const proto: PAnsiChar): PServEnt; stdcall; +begin + @getservbyport := FixupStub(hWinSockDll, 'getservbyport'); {Do not Localize} + Result := getservbyport(port, proto); +end; + +function Stub_getservbyname(const name, proto: PAnsiChar): PServEnt; stdcall; +begin + @getservbyname := FixupStub(hWinSockDll, 'getservbyname'); {Do not Localize} + Result := getservbyname(name, proto); +end; + +function Stub_getprotobynumber(const proto: Integer): PProtoEnt; stdcall; +begin + @getprotobynumber := FixupStub(hWinSockDll, 'getprotobynumber'); {Do not Localize} + Result := getprotobynumber(proto); +end; + +function Stub_getprotobyname(const name: PAnsiChar): PProtoEnt; stdcall; +begin + @getprotobyname := FixupStub(hWinSockDll, 'getprotobyname'); {Do not Localize} + Result := getprotobyname(name); +end; + +procedure Stub_WSASetLastError(const iError: Integer); stdcall; +begin + @WSASetLastError := FixupStub(hWinSockDll, 'WSASetLastError'); {Do not Localize} + WSASetLastError(iError); +end; + +function Stub_WSAGetLastError: Integer; stdcall; +begin + @WSAGetLastError := FixupStub(hWinSockDll, 'WSAGetLastError'); {Do not Localize} + Result := WSAGetLastError; +end; + +{$IFNDEF WINCE} +function Stub_WSAIsBlocking: BOOL; stdcall; +begin + @WSAIsBlocking := FixupStub(hWinSockDll, 'WSAIsBlocking'); {Do not Localize} + Result := WSAIsBlocking; +end; + +function Stub_WSAUnhookBlockingHook: Integer; stdcall; +begin + @WSAUnhookBlockingHook := FixupStub(hWinSockDll, 'WSAUnhookBlockingHook'); {Do not Localize} + Result := WSAUnhookBlockingHook; +end; + +function Stub_WSASetBlockingHook(lpBlockFunc: TFarProc): TFarProc; stdcall; +begin + @WSASetBlockingHook := FixupStub(hWinSockDll, 'WSASetBlockingHook'); {Do not Localize} + Result := WSASetBlockingHook(lpBlockFunc); +end; + +function Stub_WSACancelBlockingCall: Integer; stdcall; +begin + @WSACancelBlockingCall := FixupStub(hWinSockDll, 'WSACancelBlockingCall'); {Do not Localize} + Result := WSACancelBlockingCall; +end; + +function Stub_WSAAsyncGetServByName(HWindow: HWND; wMsg: u_int; name, proto, buf: PAnsiChar; buflen: Integer): THandle; stdcall; +begin + @WSAAsyncGetServByName := FixupStub(hWinSockDll, 'WSAAsyncGetServByName'); {Do not Localize} + Result := WSAAsyncGetServByName(HWindow, wMsg, name, proto, buf, buflen); +end; + +function Stub_WSAAsyncGetServByPort(HWindow: HWND; wMsg, port: u_int; proto, buf: PAnsiChar; buflen: Integer): THandle; stdcall; +begin + @WSAAsyncGetServByPort := FixupStub(hWinSockDll, 'WSAAsyncGetServByPort'); {Do not Localize} + Result := WSAAsyncGetServByPort(HWindow, wMsg, port, proto, buf, buflen); +end; + +function Stub_WSAAsyncGetProtoByName(HWindow: HWND; wMsg: u_int; name, buf: PAnsiChar; buflen: Integer): THandle; stdcall; +begin + @WSAAsyncGetProtoByName := FixupStub(hWinSockDll, 'WSAAsyncGetProtoByName'); {Do not Localize} + Result := WSAAsyncGetProtoByName(HWindow, wMsg, name, buf, buflen); +end; + +function Stub_WSAAsyncGetProtoByNumber(HWindow: HWND; wMsg: u_int; number: Integer; buf: PAnsiChar; buflen: Integer): THandle; stdcall; +begin + @WSAAsyncGetProtoByNumber := FixupStub(hWinSockDll, 'WSAAsyncGetProtoByNumber'); {Do not Localize} + Result := WSAAsyncGetProtoByNumber(HWindow, wMsg, number, buf, buflen); +end; + +function Stub_WSAAsyncGetHostByName(HWindow: HWND; wMsg: u_int; name, buf: PAnsiChar; buflen: Integer): THandle; stdcall; +begin + @WSAAsyncGetHostByName := FixupStub(hWinSockDll, 'WSAAsyncGetHostByName'); {Do not Localize} + Result := WSAAsyncGetHostByName(HWindow, wMsg, name, buf, buflen); +end; + +function Stub_WSAAsyncGetHostByAddr(HWindow: HWND; wMsg: u_int; AAddr: PAnsiChar; len, istruct: Integer; buf: PAnsiChar; buflen: Integer): THandle; stdcall; +begin + @WSAAsyncGetHostByAddr := FixupStub(hWinSockDll, 'WSAAsyncGetHostByAddr'); {Do not Localize} + Result := WSAAsyncGetHostByAddr(HWindow, wMsg, AAddr, len, istruct, buf, buflen); +end; + +function Stub_WSACancelAsyncRequest(hAsyncTaskHandle: THandle): Integer; stdcall; +begin + @WSACancelAsyncRequest := FixupStub(hWinSockDll, 'WSACancelAsyncRequest'); {Do not Localize} + Result := WSACancelAsyncRequest(hAsyncTaskHandle); +end; + +function Stub_WSAAsyncSelect(const s: TSocket; HWindow: HWND; wMsg: u_int; lEvent: Longint): Integer; stdcall; +begin + @WSAAsyncSelect := FixupStub(hWinSockDll, 'WSAAsyncSelect'); {Do not Localize} + Result := WSAAsyncSelect(s, HWindow, wMsg, lEvent); +end; +{$ENDIF} + +function Stub___WSAFDIsSet(const s: TSocket; var FDSet: TFDSet): Bool; stdcall; +begin + @__WSAFDIsSet := FixupStub(hWinSockDll, '__WSAFDIsSet'); {Do not Localize} + Result := __WSAFDIsSet(s, FDSet); +end; + +function Stub_WSAAccept(const s: TSocket; AAddr: PSockAddr; addrlen: PInteger; lpfnCondition: LPCONDITIONPROC; const dwCallbackData: DWORD): TSocket; stdcall; +begin + @WSAAccept := FixupStub(hWinSockDll, 'WSAAccept'); {Do not Localize} + Result := WSAAccept(s, AAddr, addrlen, lpfnCondition, dwCallbackData); +end; + +function Stub_WSACloseEvent(const hEvent: wsaevent): WordBool; stdcall; +begin + @WSACloseEvent := FixupStub(hWinSockDll, 'WSACloseEvent'); {Do not Localize} + Result := WSACloseEvent(hEvent); +end; + +function Stub_WSAConnect(const s: TSocket; const name: PSockAddr; const namelen: Integer; lpCallerData, lpCalleeData: LPWSABUF; lpSQOS, lpGQOS: LPQOS): Integer; stdcall; +begin + @WSAConnect := FixupStub(hWinSockDll, 'WSAConnect'); {Do not Localize} + Result := WSAConnect(s, name, namelen, lpCallerData, lpCalleeData, lpSQOS, lpGQOS); +end; + +function Stub_WSACreateEvent: wsaevent; stdcall; +begin + @WSACreateEvent := FixupStub(hWinSockDll, 'WSACreateEvent'); {Do not Localize} + Result := WSACreateEvent; +end; + +{$IFNDEF WINCE} +function Stub_WSADuplicateSocketA(const s: TSocket; const dwProcessId: DWORD; lpProtocolInfo: LPWSAPROTOCOL_INFOA): Integer; stdcall; +begin + @WSADuplicateSocketA := FixupStub(hWinSockDll, 'WSADuplicateSocketA'); {Do not Localize} + Result := WSADuplicateSocketA(s, dwProcessId, lpProtocolInfo); +end; + +function Stub_WSADuplicateSocketW(const s: TSocket; const dwProcessId: DWORD; lpProtocolInfo: LPWSAPROTOCOL_INFOW): Integer; stdcall; +begin + @WSADuplicateSocketW := FixupStub(hWinSockDll, 'WSADuplicateSocketW'); {Do not Localize} + Result := WSADuplicateSocketW(s, dwProcessId, lpProtocolInfo); +end; + +function Stub_WSADuplicateSocket(const s: TSocket; const dwProcessId: DWORD; lpProtocolInfo: LPWSAPROTOCOL_INFO): Integer; stdcall; +begin + {$IFDEF UNICODE} + @WSADuplicateSocket := FixupStub(hWinSockDll, 'WSADuplicateSocketW'); {Do not Localize} + {$ELSE} + @WSADuplicateSocket := FixupStub(hWinSockDll, 'WSADuplicateSocketA'); {Do not Localize} + {$ENDIF} + Result := WSADuplicateSocket(s, dwProcessId, lpProtocolInfo); +end; +{$ENDIF} + +function Stub_WSAEnumNetworkEvents(const s: TSocket; const hEventObject: WSAEVENT; lpNetworkEvents: LPWSANETWORKEVENTS): Integer; stdcall; +begin + @WSAEnumNetworkEvents := FixupStub(hWinSockDll, 'WSAEnumNetworkEvents'); {Do not Localize} + Result := WSAEnumNetworkEvents(s, hEventObject, lpNetworkEvents); +end; + +function Stub_WSAEnumProtocolsA(lpiProtocols: PInteger; lpProtocolBuffer: LPWSAPROTOCOL_INFOA; var lpdwBufferLength: DWORD): Integer; stdcall; +begin + @WSAEnumProtocolsA := FixupStub(hWinSockDll, 'WSAEnumProtocolsA'); {Do not Localize} + Result := WSAEnumProtocolsA(lpiProtocols, lpProtocolBuffer, lpdwBufferLength); +end; + +function Stub_WSAEnumProtocolsW(lpiProtocols: PInteger; lpProtocolBuffer: LPWSAPROTOCOL_INFOW; var lpdwBufferLength: DWORD): Integer; stdcall; +begin + @WSAEnumProtocolsW := FixupStub(hWinSockDll, 'WSAEnumProtocolsW'); {Do not Localize} + Result := WSAEnumProtocolsW(lpiProtocols, lpProtocolBuffer, lpdwBufferLength); +end; + +function Stub_WSAEnumProtocols(lpiProtocols: PInteger; lpProtocolBuffer: LPWSAPROTOCOL_INFO; var lpdwBufferLength: DWORD): Integer; stdcall; +begin + {$IFDEF UNICODE} + @WSAEnumProtocols := FixupStub(hWinSockDll, 'WSAEnumProtocolsW'); {Do not Localize} + {$ELSE} + @WSAEnumProtocols := FixupStub(hWinSockDll, 'WSAEnumProtocolsA'); {Do not Localize} + {$ENDIF} + Result := WSAEnumProtocols(lpiProtocols, lpProtocolBuffer, lpdwBufferLength); +end; + +function Stub_WSAEventSelect(const s: TSocket; const hEventObject: WSAEVENT; lNetworkEvents: LongInt): Integer; stdcall; +begin + @WSAEventSelect := FixupStub(hWinSockDll, 'WSAEventSelect'); {Do not Localize} + Result := WSAEventSelect(s, hEventObject, lNetworkEvents); +end; + +function Stub_WSAGetOverlappedResult(const s: TSocket; AOverlapped: Pointer; lpcbTransfer: LPDWORD; fWait: BOOL; var lpdwFlags: DWORD): WordBool; stdcall; +begin + @WSAGetOverlappedResult := FixupStub(hWinSockDll, 'WSAGetOverlappedResult'); {Do not Localize} + Result := WSAGetOverlappedResult(s, AOverlapped, lpcbTransfer, fWait, lpdwFlags); +end; + +{$IFNDEF WINCE} +function Stub_WSAGetQOSByName(const s: TSocket; lpQOSName: LPWSABUF; lpQOS: LPQOS): WordBool; stdcall; +begin + @WSAGetQOSByName := FixupStub(hWinSockDll, 'WSAGetQOSByName'); {Do not Localize} + Result := WSAGetQOSByName(s, lpQOSName, lpQOS); +end; +{$ENDIF} + +function Stub_WSAHtonl(const s: TSocket; hostlong: u_long; var lpnetlong: DWORD): Integer; stdcall; +begin + @WSAHtonl := FixupStub(hWinSockDll, 'WSAHtonl'); {Do not Localize} + Result := WSAHtonl(s, hostlong, lpnetlong); +end; + +function Stub_WSAHtons(const s: TSocket; hostshort: u_short; var lpnetshort: WORD): Integer; stdcall; +begin + @WSAHtons := FixupStub(hWinSockDll, 'WSAHtons'); {Do not Localize} + Result := WSAHtons(s, hostshort, lpnetshort); +end; + +function Stub_WSAIoctl(const s: TSocket; dwIoControlCode: DWORD; lpvInBuffer: Pointer; cbInBuffer: DWORD; lpvOutBuffer: Pointer; cbOutBuffer: DWORD; lpcbBytesReturned: LPDWORD; AOverlapped: Pointer; lpCompletionRoutine: LPWSAOVERLAPPED_COMPLETION_ROUTINE): Integer; stdcall; +begin + @WSAIoctl := FixupStub(hWinSockDll, 'WSAIoctl'); {Do not Localize} + Result := WSAIoctl(s, dwIoControlCode, lpvInBuffer, cbInBuffer, lpvOutBuffer, cbOutBuffer, lpcbBytesReturned, AOverlapped, lpCompletionRoutine); +end; + +function Stub_WSAJoinLeaf(const s: TSocket; name: PSockAddr; namelen: Integer; lpCallerData, lpCalleeData: LPWSABUF; lpSQOS, lpGQOS: LPQOS; dwFlags: DWORD): TSocket; stdcall; +begin + @WSAJoinLeaf := FixupStub(hWinSockDll, 'WSAJoinLeaf'); {Do not Localize} + Result := WSAJoinLeaf(s, name, namelen, lpCallerData, lpCalleeData, lpSQOS, lpGQOS, dwFlags); +end; + +function Stub_WSANtohl(const s: TSocket; netlong: u_long; var lphostlong: DWORD): Integer; stdcall; +begin + @WSANtohl := FixupStub(hWinSockDll, 'WSANtohl'); {Do not Localize} + Result := WSANtohl(s, netlong, lphostlong); +end; + +function Stub_WSANtohs(const s: TSocket; netshort: u_short; var lphostshort: WORD): Integer; stdcall; +begin + @WSANtohs := FixupStub(hWinSockDll, 'WSANtohs'); {Do not Localize} + Result := WSANtohs(s, netshort, lphostshort); +end; + +function Stub_WSARecv(const s: TSocket; lpBuffers: LPWSABUF; dwBufferCount: DWORD; var lpNumberOfBytesRecvd: DWORD; var lpFlags: DWORD; AOverlapped: LPWSAOVERLAPPED; lpCompletionRoutine: LPWSAOVERLAPPED_COMPLETION_ROUTINE): Integer; stdcall; +begin + @WSARecv := FixupStub(hWinSockDll, 'WSARecv'); {Do not Localize} + Result := WSARecv(s, lpBuffers, dwBufferCount, lpNumberOfBytesRecvd, lpFlags, AOverlapped, lpCompletionRoutine); +end; + +function Stub_WSARecvDisconnect(const s: TSocket; lpInboundDisconnectData: LPWSABUF): Integer; stdcall; +begin + @WSARecvDisconnect := FixupStub(hWinSockDll, 'WSARecvDisconnect'); {Do not Localize} + Result := WSARecvDisconnect(s, lpInboundDisconnectData); +end; + +function Stub_WSARecvFrom(const s: TSocket; lpBuffers: LPWSABUF; dwBufferCount: DWORD; var lpNumberOfBytesRecvd: DWORD; var lpFlags: DWORD; lpFrom: PSockAddr; lpFromlen: PInteger; AOverlapped: Pointer; lpCompletionRoutine: LPWSAOVERLAPPED_COMPLETION_ROUTINE): Integer; stdcall; +begin + @WSARecvFrom := FixupStub(hWinSockDll, 'WSARecvFrom'); {Do not Localize} + Result := WSARecvFrom(s, lpBuffers, dwBufferCount, lpNumberOfBytesRecvd, lpFlags, lpFrom, lpFromlen, AOverlapped, lpCompletionRoutine); +end; + +function Stub_WSAResetEvent(hEvent: wsaevent): WordBool; stdcall; +begin + @WSAResetEvent := FixupStub(hWinSockDll, 'WSAResetEvent'); {Do not Localize} + Result := WSAResetEvent(hEvent); +end; + +function Stub_WSASend(const s: TSocket; lpBuffers: LPWSABUF; dwBufferCount: DWORD; var lpNumberOfBytesSent: DWORD; dwFlags: DWORD; AOverlapped: LPWSAOVERLAPPED; lpCompletionRoutine: LPWSAOVERLAPPED_COMPLETION_ROUTINE): Integer; stdcall; +begin + @WSASend := FixupStub(hWinSockDll, 'WSASend'); {Do not Localize} + Result := WSASend(s, lpBuffers, dwBufferCount, lpNumberOfBytesSent, dwFlags, AOverlapped, lpCompletionRoutine); +end; + +{$IFNDEF WINCE} +function Stub_WSASendDisconnect(const s: TSocket; lpOutboundDisconnectData: LPWSABUF): Integer; stdcall; +begin + @WSASendDisconnect := FixupStub(hWinSockDll, 'WSASendDisconnect'); {Do not Localize} + Result := WSASendDisconnect(s, lpOutboundDisconnectData); +end; +{$ENDIF} + +function Stub_WSASendTo(const s: TSocket; lpBuffers: LPWSABUF; dwBufferCount: DWORD; var lpNumberOfBytesSent: DWORD; dwFlags: DWORD; lpTo: PSOCKADDR; iTolen: Integer; AOverlapped: LPWSAOVERLAPPED; lpCompletionRoutine: LPWSAOVERLAPPED_COMPLETION_ROUTINE): Integer; stdcall; +begin + @WSASendTo := FixupStub(hWinSockDll, 'WSASendTo'); {Do not Localize} + Result := WSASendTo(s, lpBuffers, dwBufferCount, lpNumberOfBytesSent, dwFlags, lpTo, iTolen, AOverlapped, lpCompletionRoutine); +end; + +function Stub_WSASetEvent(hEvent: WSAEVENT): WordBool; stdcall; +begin + @WSASetEvent := FixupStub(hWinSockDll, 'WSASetEvent'); {Do not Localize} + Result := WSASetEvent(hEvent); +end; + +function Stub_WSASocketA(af, iType, protocol: Integer; lpProtocolInfo: LPWSAPROTOCOL_INFOA; g: GROUP; dwFlags: DWORD): TSocket; stdcall; +begin + @WSASocketA := FixupStub(hWinSockDll, 'WSASocketA'); {Do not Localize} + Result := WSASocketA(af, iType, protocol, lpProtocolInfo, g, dwFlags); +end; + +function Stub_WSASocketW(af, iType, protocol: Integer; lpProtocolInfo: LPWSAPROTOCOL_INFOW; g: GROUP; dwFlags: DWORD): TSocket; stdcall; +begin + @WSASocketW := FixupStub(hWinSockDll, 'WSASocketW'); {Do not Localize} + Result := WSASocketW(af, iType, protocol, lpProtocolInfo, g, dwFlags); +end; + +function Stub_WSASocket(af, iType, protocol: Integer; lpProtocolInfo: LPWSAPROTOCOL_INFO; g: GROUP; dwFlags: DWORD): TSocket; stdcall; +begin + {$IFDEF UNICODE} + @WSASocket := FixupStub(hWinSockDll, 'WSASocketW'); {Do not Localize} + {$ELSE} + @WSASocket := FixupStub(hWinSockDll, 'WSASocketA'); {Do not Localize} + {$ENDIF} + Result := WSASocket(af, iType, protocol, lpProtocolInfo, g, dwFlags); +end; + +function Stub_WSAWaitForMultipleEvents(cEvents: DWORD; lphEvents: Pwsaevent; fWaitAll: LongBool; dwTimeout: DWORD; fAlertable: LongBool): DWORD; stdcall; +begin + @WSAWaitForMultipleEvents := FixupStub(hWinSockDll, 'WSAWaitForMultipleEvents'); {Do not Localize} + Result := WSAWaitForMultipleEvents(cEvents, lphEvents, fWaitAll, dwTimeout, fAlertable); +end; + +function Stub_WSAAddressToStringA(lpsaAddress: PSockAddr; const dwAddressLength: DWORD; const lpProtocolInfo: LPWSAPROTOCOL_INFOA; const lpszAddressString: PAnsiChar; var lpdwAddressStringLength: DWORD): Integer; stdcall; +begin + @WSAAddressToStringA := FixupStub(hWinSockDll, 'WSAAddressToStringA'); {Do not Localize} + Result := WSAAddressToStringA(lpsaAddress, dwAddressLength, lpProtocolInfo, lpszAddressString, lpdwAddressStringLength); +end; + +function Stub_WSAAddressToStringW(lpsaAddress: PSockAddr; const dwAddressLength: DWORD; const lpProtocolInfo: LPWSAPROTOCOL_INFOW; const lpszAddressString: PWideChar; var lpdwAddressStringLength: DWORD): Integer; stdcall; +begin + @WSAAddressToStringW := FixupStub(hWinSockDll, 'WSAAddressToStringW'); {Do not Localize} + Result := WSAAddressToStringW(lpsaAddress, dwAddressLength, lpProtocolInfo, lpszAddressString, lpdwAddressStringLength); +end; + +function Stub_WSAAddressToString(lpsaAddress: PSockAddr; const dwAddressLength: DWORD; const lpProtocolInfo: LPWSAPROTOCOL_INFO; + const lpszAddressString: {$IFDEF UNICODE}PWideChar{$ELSE}PAnsiChar{$ENDIF}; var lpdwAddressStringLength: DWORD): Integer; stdcall; +begin + {$IFDEF UNICODE} + @WSAAddressToString := FixupStub(hWinSockDll, 'WSAAddressToStringW'); {Do not Localize} + {$ELSE} + @WSAAddressToString := FixupStub(hWinSockDll, 'WSAAddressToStringA'); {Do not Localize} + {$ENDIF} + Result := WSAAddressToString(lpsaAddress, dwAddressLength, lpProtocolInfo, lpszAddressString, lpdwAddressStringLength); +end; + +function Stub_WSAStringToAddressA(const AddressString: PAnsiChar; const AddressFamily: Integer; const lpProtocolInfo: LPWSAPROTOCOL_INFOA; var lpAddress: TSockAddr; var lpAddressLength: Integer): Integer; stdcall; +begin + @WSAStringToAddressA := FixupStub(hWinSockDll, 'WSAStringToAddressA'); {Do not Localize} + Result := WSAStringToAddressA(AddressString, AddressFamily, lpProtocolInfo, lpAddress, lpAddressLength); +end; + +function Stub_WSAStringToAddressW(const AddressString: PWideChar; const AddressFamily: Integer; const lpProtocolInfo: LPWSAPROTOCOL_INFOW; var lpAddress: TSockAddr; var lpAddressLength: Integer): Integer; stdcall; +begin + @WSAStringToAddressW := FixupStub(hWinSockDll, 'WSAStringToAddressW'); {Do not Localize} + Result := WSAStringToAddressW(AddressString, AddressFamily, lpProtocolInfo, lpAddress, lpAddressLength); +end; + +function Stub_WSAStringToAddress (const AddressString: {$IFDEF UNICODE}PWideChar{$ELSE}PAnsiChar{$ENDIF}; + const AddressFamily: Integer; const lpProtocolInfo: LPWSAProtocol_Info; + var lpAddress: TSockAddr; var lpAddressLength: Integer): Integer; stdcall; +begin + {$IFDEF UNICODE} + @WSAStringToAddress := FixupStub(hWinSockDll, 'WSAStringToAddressW'); {Do not Localize} + {$ELSE} + @WSAStringToAddress := FixupStub(hWinSockDll, 'WSAStringToAddressA'); {Do not Localize} + {$ENDIF} + Result := WSAStringToAddress(AddressString, AddressFamily, lpProtocolInfo, lpAddress, lpAddressLength); +end; + +function Stub_WSALookupServiceBeginA(var qsRestrictions: TWSAQuerySetA; const dwControlFlags: DWORD; var hLookup: THandle): Integer; stdcall; +begin + @WSALookupServiceBeginA := FixupStub(hWinSockDll, 'WSALookupServiceBeginA'); {Do not Localize} + Result := WSALookupServiceBeginA(qsRestrictions, dwControlFlags, hLookup); +end; + +function Stub_WSALookupServiceBeginW(var qsRestrictions: TWSAQuerySetW; const dwControlFlags: DWORD; var hLookup: THandle): Integer; stdcall; +begin + @WSALookupServiceBeginW := FixupStub(hWinSockDll, 'WSALookupServiceBeginW'); {Do not Localize} + Result := WSALookupServiceBeginW(qsRestrictions, dwControlFlags, hLookup); +end; + +function Stub_WSALookupServiceBegin(var qsRestrictions: TWSAQuerySet; const dwControlFlags: DWORD; var hLookup: THandle): Integer; stdcall; +begin + {$IFDEF UNICODE} + @WSALookupServiceBegin := FixupStub(hWinSockDll, 'WSALookupServiceBeginW'); {Do not Localize} + {$ELSE} + @WSALookupServiceBegin := FixupStub(hWinSockDll, 'WSALookupServiceBeginA'); {Do not Localize} + {$ENDIF} + Result := WSALookupServiceBegin(qsRestrictions, dwControlFlags, hLookup); +end; + +function Stub_WSALookupServiceNextA(const hLookup: THandle; const dwControlFlags: DWORD; var dwBufferLength: DWORD; lpqsResults: LPWSAQUERYSETA): Integer; stdcall; +begin + @WSALookupServiceNextA := FixupStub(hWinSockDll, 'WSALookupServiceNextA'); {Do not Localize} + Result := WSALookupServiceNextA(hLookup, dwControlFlags, dwBufferLength, lpqsResults); +end; + +function Stub_WSALookupServiceNextW(const hLookup: THandle; const dwControlFlags: DWORD; var dwBufferLength: DWORD; lpqsResults: LPWSAQUERYSETW): Integer; stdcall; +begin + @WSALookupServiceNextW := FixupStub(hWinSockDll, 'WSALookupServiceNextW'); {Do not Localize} + Result := WSALookupServiceNextW(hLookup, dwControlFlags, dwBufferLength, lpqsResults); +end; + +function Stub_WSALookupServiceNext(const hLookup: THandle; const dwControlFlags: DWORD; var dwBufferLength: DWORD; lpqsResults: LPWSAQUERYSET): Integer; stdcall; +begin + {$IFDEF UNICODE} + @WSALookupServiceNext := FixupStub(hWinSockDll, 'WSALookupServiceNextW'); {Do not Localize} + {$ELSE} + @WSALookupServiceNext := FixupStub(hWinSockDll, 'WSALookupServiceNextA'); {Do not Localize} + {$ENDIF} + Result := WSALookupServiceNext(hLookup, dwControlFlags, dwBufferLength, lpqsResults); +end; + +function Stub_WSALookupServiceEnd(const hLookup: THandle): Integer; stdcall; +begin + @WSALookupServiceEnd := FixupStub(hWinSockDll, 'WSALookupServiceEnd'); {Do not Localize} + Result := WSALookupServiceEnd(hLookup); +end; + + +function Stub_WSANSPIoctl(const hLookup : THANDLE; const dwControlCode : DWORD; + lpvInBuffer : Pointer; var cbInBuffer : DWORD; lpvOutBuffer : Pointer; + var cbOutBuffer : DWORD; var lpcbBytesReturned : DWORD; + lpCompletion : LPWSACOMPLETION) : Integer; stdcall; +begin + @WSANSPIoctl := FixupStub(hWinSockDLL, 'WSANSPIoctl'); {Do not Localize} + Result := WSANSPIoctl(hLookup,dwControlCode,lpvInBuffer,cbInBuffer,lpvOutBuffer, + cbOutBuffer, lpcbBytesReturned,lpCompletion); +end; + +function Stub_WSAInstallServiceClassA(const lpServiceClassInfo: LPWSASERVICECLASSINFOA): Integer; stdcall; +begin + @WSAInstallServiceClassA := FixupStub(hWinSockDll, 'WSAInstallServiceClassA'); {Do not Localize} + Result := WSAInstallServiceClassA(lpServiceClassInfo); +end; + +function Stub_WSAInstallServiceClassW(const lpServiceClassInfo: LPWSASERVICECLASSINFOW): Integer; stdcall; +begin + @WSAInstallServiceClassW := FixupStub(hWinSockDll, 'WSAInstallServiceClassW'); {Do not Localize} + Result := WSAInstallServiceClassW(lpServiceClassInfo); +end; + +function Stub_WSAInstallServiceClass(const lpServiceClassInfo: LPWSASERVICECLASSINFO): Integer; stdcall; +begin + {$IFDEF UNICODE} + @WSAInstallServiceClass := FixupStub(hWinSockDll, 'WSAInstallServiceClassW'); {Do not Localize} + {$ELSE} + @WSAInstallServiceClass := FixupStub(hWinSockDll, 'WSAInstallServiceClassA'); {Do not Localize} + {$ENDIF} + Result := WSAInstallServiceClass(lpServiceClassInfo); +end; + +function Stub_WSARemoveServiceClass(const lpServiceClassId: PGUID): Integer; stdcall; +begin + @WSARemoveServiceClass := FixupStub(hWinSockDll, 'WSARemoveServiceClass'); {Do not Localize} + Result := WSARemoveServiceClass(lpServiceClassId); +end; + +function Stub_WSAGetServiceClassInfoA(const lpProviderId: PGUID; const lpServiceClassId: PGUID; var lpdwBufSize: DWORD; lpServiceClassInfo: LPWSASERVICECLASSINFOA): Integer; stdcall; +begin + @WSAGetServiceClassInfoA := FixupStub(hWinSockDll, 'WSAGetServiceClassInfoA'); {Do not Localize} + Result := WSAGetServiceClassInfoA(lpProviderId, lpServiceClassId, lpdwBufSize, lpServiceClassInfo); +end; + +function Stub_WSAGetServiceClassInfoW(const lpProviderId: PGUID; const lpServiceClassId: PGUID; var lpdwBufSize: DWORD; lpServiceClassInfo: LPWSASERVICECLASSINFOW): Integer; stdcall; +begin + @WSAGetServiceClassInfoW := FixupStub(hWinSockDll, 'WSAGetServiceClassInfoW'); {Do not Localize} + Result := WSAGetServiceClassInfoW(lpProviderId, lpServiceClassId, lpdwBufSize, lpServiceClassInfo); +end; + +function Stub_WSAGetServiceClassInfo(const lpProviderId: PGUID; const lpServiceClassId: PGUID; var lpdwBufSize: DWORD; lpServiceClassInfo: LPWSASERVICECLASSINFO): Integer; stdcall; +begin + {$IFDEF UNICODE} + @WSAGetServiceClassInfo := FixupStub(hWinSockDll, 'WSAGetServiceClassInfoW'); {Do not Localize} + {$ELSE} + @WSAGetServiceClassInfo := FixupStub(hWinSockDll, 'WSAGetServiceClassInfoA'); {Do not Localize} + {$ENDIF} + Result := WSAGetServiceClassInfo(lpProviderId, lpServiceClassId, lpdwBufSize, lpServiceClassInfo); +end; + +function Stub_WSAEnumNameSpaceProvidersA(var lpdwBufferLength: DWORD; const lpnspBuffer: LPWSANAMESPACE_INFOA): Integer; stdcall; +begin + @WSAEnumNameSpaceProvidersA := FixupStub(hWinSockDll, 'WSAEnumNameSpaceProvidersA'); {Do not Localize} + Result := WSAEnumNameSpaceProvidersA(lpdwBufferLength, lpnspBuffer); +end; + +function Stub_WSAEnumNameSpaceProvidersW(var lpdwBufferLength: DWORD; const lpnspBuffer: LPWSANAMESPACE_INFOW): Integer; stdcall; +begin + @WSAEnumNameSpaceProvidersW := FixupStub(hWinSockDll, 'WSAEnumNameSpaceProvidersW'); {Do not Localize} + Result := WSAEnumNameSpaceProvidersW(lpdwBufferLength, lpnspBuffer); +end; + +function Stub_WSAEnumNameSpaceProviders(var lpdwBufferLength: DWORD; const lpnspBuffer: LPWSANAMESPACE_INFO): Integer; stdcall; +begin + {$IFDEF UNICODE} + @WSAEnumNameSpaceProviders := FixupStub(hWinSockDll, 'WSAEnumNameSpaceProvidersW'); {Do not Localize} + {$ELSE} + @WSAEnumNameSpaceProviders := FixupStub(hWinSockDll, 'WSAEnumNameSpaceProvidersA'); {Do not Localize} + {$ENDIF} + Result := WSAEnumNameSpaceProviders(lpdwBufferLength, lpnspBuffer); +end; + +function Stub_WSAGetServiceClassNameByClassIdA(const lpServiceClassId: PGUID; lpszServiceClassName: PAnsiChar; var lpdwBufferLength: DWORD): Integer; stdcall; +begin + @WSAGetServiceClassNameByClassIdA := FixupStub(hWinSockDll, 'WSAGetServiceClassNameByClassIdA'); {Do not Localize} + Result := WSAGetServiceClassNameByClassIdA(lpServiceClassId, lpszServiceClassName, lpdwBufferLength); +end; + +function Stub_WSAGetServiceClassNameByClassIdW(const lpServiceClassId: PGUID; lpszServiceClassName: PWideChar; var lpdwBufferLength: DWORD): Integer; stdcall; +begin + @WSAGetServiceClassNameByClassIdW := FixupStub(hWinSockDll, 'WSAGetServiceClassNameByClassIdW'); {Do not Localize} + Result := WSAGetServiceClassNameByClassIdW(lpServiceClassId, lpszServiceClassName, lpdwBufferLength); +end; + +function Stub_WSAGetServiceClassNameByClassId(const lpServiceClassId: PGUID; + lpszServiceClassName: {$IFDEF UNICODE}PWideChar{$ELSE}PAnsiChar{$ENDIF}; + var lpdwBufferLength: DWORD): Integer; stdcall; +begin + {$IFDEF UNICODE} + @WSAGetServiceClassNameByClassId := FixupStub(hWinSockDll, 'WSAGetServiceClassNameByClassIdW'); {Do not Localize} + {$ELSE} + @WSAGetServiceClassNameByClassId := FixupStub(hWinSockDll, 'WSAGetServiceClassNameByClassIdA'); {Do not Localize} + {$ENDIF} + Result := WSAGetServiceClassNameByClassId(lpServiceClassId, lpszServiceClassName, lpdwBufferLength); +end; + +function Stub_WSASetServiceA(const lpqsRegInfo: LPWSAQUERYSETA; const essoperation: WSAESETSERVICEOP; const dwControlFlags: DWORD): Integer; stdcall; +begin + @WSASetServiceA := FixupStub(hWinSockDll, 'WSASetServiceA'); {Do not Localize} + Result := WSASetServiceA(lpqsRegInfo, essoperation, dwControlFlags); +end; + +function Stub_WSASetServiceW(const lpqsRegInfo: LPWSAQUERYSETW; const essoperation: WSAESETSERVICEOP; const dwControlFlags: DWORD): Integer; stdcall; +begin + @WSASetServiceW := FixupStub(hWinSockDll, 'WSASetServiceW'); {Do not Localize} + Result := WSASetServiceW(lpqsRegInfo, essoperation, dwControlFlags); +end; + +function Stub_WSASetService(const lpqsRegInfo: LPWSAQUERYSET; const essoperation: WSAESETSERVICEOP; const dwControlFlags: DWORD): Integer; stdcall; +begin + {$IFDEF UNICODE} + @WSASetService := FixupStub(hWinSockDll, 'WSASetServiceW'); {Do not Localize} + {$ELSE} + @WSASetService := FixupStub(hWinSockDll, 'WSASetServiceA'); {Do not Localize} + {$ENDIF} + Result := WSASetService(lpqsRegInfo, essoperation, dwControlFlags); +end; + +function Stub_WSAProviderConfigChange(var lpNotificationHandle: THandle; AOverlapped: LPWSAOVERLAPPED; lpCompletionRoutine: LPWSAOVERLAPPED_COMPLETION_ROUTINE): Integer; stdcall; +begin + @WSAProviderConfigChange := FixupStub(hWinSockDll, 'WSAProviderConfigChange'); {Do not Localize} + Result := WSAProviderConfigChange(lpNotificationHandle, AOverlapped, lpCompletionRoutine); +end; + +function Stub_TransmitFile(hSocket: TSocket; hFile: THandle; nNumberOfBytesToWrite: DWORD; + nNumberOfBytesPerSend: DWORD; lpOverlapped: POverlapped; + lpTransmitBuffers: LPTRANSMIT_FILE_BUFFERS; dwReserved: DWORD): BOOL; stdcall; +begin + @TransmitFile := FixupStubEx(hSocket, 'TransmitFile', WSAID_TRANSMITFILE); {Do not localize} + Result := TransmitFile(hSocket, hFile, nNumberOfBytesToWrite, nNumberOfBytesPerSend, lpOverlapped, lpTransmitBuffers, dwReserved); +end; + +{RLebeau 1/26/2006 - loading GetAcceptExSockaddrs() at the same time as AcceptEx(). +This is because GetAcceptExSockaddrs() is not passed a SOCKET that can be passed to +WSAIoCtrl() to get the function pointer. Also, GetAcceptExSockaddrs() is needed to +parse AcceptEx()'s return data, so there is no point in calling AcceptEx() unless +its data can be parsed afterwards.} +function Stub_AcceptEx(sListenSocket, sAcceptSocket: TSocket; + lpOutputBuffer: Pointer; dwReceiveDataLength, dwLocalAddressLength, + dwRemoteAddressLength: DWORD; var lpdwBytesReceived: DWORD; + lpOverlapped: POverlapped): BOOL; stdcall; +begin + {RLebeau - loading GetAcceptExSockaddrs() first in case it fails} + @GetAcceptExSockaddrs := FixupStubEx(sListenSocket, 'GetAcceptExSockaddrs', WSAID_GETACCEPTEXSOCKADDRS); {Do not localize} + @AcceptEx := FixupStubEx(sListenSocket, 'AcceptEx', WSAID_ACCEPTEX); {Do not localize} + Result := AcceptEx(sListenSocket, sAcceptSocket, lpOutputBuffer, dwReceiveDataLength, + dwLocalAddressLength, dwRemoteAddressLength, lpdwBytesReceived, lpOverlapped); +end; + +{$IFNDEF WINCE} +function Stub_WSARecvEx(s: TSocket; var buf; len: Integer; var flags: Integer): Integer; stdcall; +begin + LoadMSWSock; + @WSARecvEx := FixupStub(hMSWSockDll, 'WSARecvEx'); {Do not localize} + Result := WSARecvEx(s, buf, len, flags); +end; +{$ENDIF} + +function Stub_ConnectEx(const s : TSocket; const name: PSockAddr; const namelen: Integer; lpSendBuffer : Pointer; + dwSendDataLength : DWORD; var lpdwBytesSent : DWORD; lpOverlapped : LPWSAOVERLAPPED) : BOOL; stdcall; +begin + @ConnectEx := FixupStubEx(s, 'ConnectEx', WSAID_CONNECTEX); {Do not localize} + Result := ConnectEx(s, name, namelen, lpSendBuffer, dwSendDataLength, lpdwBytesSent, lpOverlapped); +end; + +function Stub_DisconnectEx(const s : TSocket; AOverlapped: Pointer; const dwFlags : DWord; const dwReserved : DWORD) : BOOL; stdcall; +begin + @DisconnectEx := FixupStubEx(s, 'DisconnectEx', WSAID_DISCONNECTEX); {Do not localize} + Result := DisconnectEx(s, AOverlapped, dwFlags, dwReserved); +end; + +function Stub_WSARecvMsg(const s : TSocket; lpMsg : LPWSAMSG; var lpNumberOfBytesRecvd : DWORD; AOverlapped: Pointer; lpCompletionRoutine : LPWSAOVERLAPPED_COMPLETION_ROUTINE): Integer; stdcall; +begin + @WSARecvMsg := FixupStubEx(s, 'WSARecvMsg', WSAID_WSARECVMSG); {Do not localize} + Result := WSARecvMsg(s, lpMsg, lpNumberOfBytesRecvd, AOverlapped, lpCompletionRoutine); +end; + +function Stub_TransmitPackets(s: TSocket; lpPacketArray: LPTRANSMIT_PACKETS_ELEMENT; + nElementCount: DWORD; nSendSize: DWORD; lpOverlapped: LPWSAOVERLAPPED; dwFlags: DWORD): BOOL; stdcall; +begin + @TransmitPackets := FixupStubEx(s, 'TransmitPackets', WSAID_TRANSMITPACKETS); {Do not localize} + Result := TransmitPackets(s, lpPacketArray, nElementCount, nSendSize, lpOverlapped, dwFlags); +end; + +{$IFNDEF WINCE} +function Stub_WSASendMsg(const s : TSocket; lpMsg : LPWSAMSG; const dwFlags : DWORD; var lpNumberOfBytesSent : DWORD; lpOverlapped : LPWSAOVERLAPPED; lpCompletionRoutine : LPWSAOVERLAPPED_COMPLETION_ROUTINE) : Integer; stdcall; +begin + @WSASendMsg := FixupStubEx(s, 'WSASendMsg', WSAID_WSASENDMSG); {Do not localize} + Result := WSASendMsg(s, lpMsg, dwFlags, lpNumberOfBytesSent, lpOverlapped, lpCompletionRoutine); +end; + +function Stub_WSAPoll(fdarray : LPWSAPOLLFD; const nfds : u_long; const timeout : Integer) : Integer; stdcall; +begin + @WSAPoll := FixupStubEx(fdarray.fd, 'WSAPoll', WSAID_WSAPOLL); {Do not localize} + Result := WSAPoll(fdarray, nfds, timeout); +end; +{$ENDIF} + +procedure InitializeStubsEx; +var + LSocket: TSocket; +begin + LSocket := WSASocket(AF_INET, SOCK_STREAM, IPPROTO_TCP, nil, 0, WSA_FLAG_OVERLAPPED); + try + @AcceptEx := FixupStubEx(LSocket, 'AcceptEx', WSAID_ACCEPTEX); + @GetAcceptExSockaddrs := FixupStubEx(LSocket, 'GetAcceptExSockaddrs', WSAID_GETACCEPTEXSOCKADDRS); {Do not localize} + @ConnectEx := FixupStubEx(LSocket, 'ConnectEx', WSAID_CONNECTEX); {Do not localize} + @DisconnectEx := FixupStubEx(LSocket, 'DisconnectEx', WSAID_DISCONNECTEX); {Do not localize} + @WSARecvMsg := FixupStubEx(LSocket, 'WSARecvMsg', WSAID_WSARECVMSG); {Do not localize} + @WSARecvMsg := FixupStubEx(LSocket, 'WSARecvMsg', WSAID_WSARECVMSG); {Do not localize} + @TransmitFile := FixupStubEx(LSocket, 'TransmitFile', WSAID_TRANSMITFILE); {Do not localize} + @TransmitPackets := FixupStubEx(LSocket, 'TransmitPackets', WSAID_TRANSMITPACKETS); {Do not localize} + + {$IFNDEF WINCE} +// @WSASendMsg := FixupStubEx(LSocket, 'WSASendMsg', WSAID_WSASENDMSG); {Do not localize} +// @WSAPoll := FixupStubEx(LSocket, 'WSAPoll', WSAID_WSAPOLL); {Do not localize} + {$ENDIF} + finally + closesocket(LSocket); + end; +end; + +procedure InitializeStubs; +{Alphabetize these so we can more easily determine what's available on a platform. +by section in Winsock SDK reference} +begin + accept := Stub_accept; + bind := Stub_bind; + closesocket := Stub_closesocket; + connect := Stub_connect; + ioctlsocket := Stub_ioctlsocket; + getpeername := Stub_getpeername; + getsockname := Stub_getsockname; + getsockopt := Stub_getsockopt; + htonl := Stub_htonl; + htons := Stub_htons; + inet_addr := Stub_inet_addr; + inet_ntoa := Stub_inet_ntoa; + listen := Stub_listen; + ntohl := Stub_ntohl; + ntohs := Stub_ntohs; + recv := Stub_recv; + recvfrom := Stub_recvfrom; + select := Stub_select; + send := Stub_send; + sendto := Stub_sendto; + {$IFDEF WINCE} + sethostname := Stub_sethostname; + {$ENDIF} + setsockopt := Stub_setsockopt; + shutdown := Stub_shutdown; + socket := Stub_socket; + gethostbyaddr := Stub_gethostbyaddr; + gethostbyname := Stub_gethostbyname; + gethostname := Stub_gethostname; + getservbyport := Stub_getservbyport; + getservbyname := Stub_getservbyname; + getprotobynumber := Stub_getprotobynumber; + getprotobyname := Stub_getprotobyname; + //extensions + __WSAFDIsSet := Stub___WSAFDIsSet; + {$IFNDEF WINCE} + AcceptEx := Stub_AcceptEx; + //GetAcceptExSockaddrs is loaded by Stub_AcceptEx + ConnectEx := Stub_ConnectEx; + DisconnectEx := Stub_DisconnectEx; + TransmitFile := Stub_TransmitFile; + TransmitPackets := Stub_TransmitPackets; + {$ENDIF} + WSAAccept := Stub_WSAAccept; + {$IFNDEF WINCE} + WSACancelAsyncRequest := Stub_WSACancelAsyncRequest; + WSAAsyncGetHostByAddr := Stub_WSAAsyncGetHostByAddr; + WSAAsyncGetHostByName := Stub_WSAAsyncGetHostByName; + WSAAsyncGetProtoByName := Stub_WSAAsyncGetProtoByName; + WSAAsyncGetProtoByNumber := Stub_WSAAsyncGetProtoByNumber; + WSAAsyncGetServByName := Stub_WSAAsyncGetServByName; + WSAAsyncGetServByPort := Stub_WSAAsyncGetServByPort; + WSAAsyncSelect := Stub_WSAAsyncSelect; + {$ENDIF} + WSAAddressToStringA := Stub_WSAAddressToStringA; + WSAAddressToStringW := Stub_WSAAddressToStringW; + WSAAddressToString := Stub_WSAAddressToString; + {$IFNDEF WINCE} + WSACancelBlockingCall := Stub_WSACancelBlockingCall; + {$ENDIF} + WSACleanup := Stub_WSACleanup; + WSACloseEvent := Stub_WSACloseEvent; + WSAConnect := Stub_WSAConnect; + WSACreateEvent := Stub_WSACreateEvent; + {$IFNDEF WINCE} + WSADuplicateSocketA := Stub_WSADuplicateSocketA; + WSADuplicateSocketW := Stub_WSADuplicateSocketW; + WSADuplicateSocket := Stub_WSADuplicateSocket; + {$ENDIF} + WSAEnumNameSpaceProvidersA := Stub_WSAEnumNameSpaceProvidersA; + WSAEnumNameSpaceProvidersW := Stub_WSAEnumNameSpaceProvidersW; + WSAEnumNameSpaceProviders := Stub_WSAEnumNameSpaceProviders; + WSAEnumNetworkEvents := Stub_WSAEnumNetworkEvents; + WSAEnumProtocolsA := Stub_WSAEnumProtocolsA; + WSAEnumProtocolsW := Stub_WSAEnumProtocolsW; + WSAEnumProtocols := Stub_WSAEnumProtocols; + WSAEventSelect := Stub_WSAEventSelect; + WSAGetLastError := Stub_WSAGetLastError; + WSAGetOverlappedResult := Stub_WSAGetOverlappedResult; + {$IFNDEF WINCE} + WSAGetQOSByName := Stub_WSAGetQOSByName; + WSAGetServiceClassInfoA := Stub_WSAGetServiceClassInfoA; + WSAGetServiceClassInfoW := Stub_WSAGetServiceClassInfoW; + WSAGetServiceClassInfo := Stub_WSAGetServiceClassInfo; + WSAGetServiceClassNameByClassIdA := Stub_WSAGetServiceClassNameByClassIdA; + WSAGetServiceClassNameByClassIdW := Stub_WSAGetServiceClassNameByClassIdW; + WSAGetServiceClassNameByClassId := Stub_WSAGetServiceClassNameByClassId; + {$ENDIF} + WSAHtonl := Stub_WSAHtonl; + WSAHtons := Stub_WSAHtons; + {$IFNDEF WINCE} + WSAInstallServiceClassA := Stub_WSAInstallServiceClassA; + WSAInstallServiceClassW := Stub_WSAInstallServiceClassW; + WSAInstallServiceClass := Stub_WSAInstallServiceClass; + {$ENDIF} + WSAIoctl := Stub_WSAIoctl; + {$IFNDEF WINCE} + WSAIsBlocking := Stub_WSAIsBlocking; + {$ENDIF} + WSAJoinLeaf := Stub_WSAJoinLeaf; + WSALookupServiceBeginA := Stub_WSALookupServiceBeginA; + WSALookupServiceBeginW := Stub_WSALookupServiceBeginW; + WSALookupServiceBegin := Stub_WSALookupServiceBegin; + WSALookupServiceEnd := Stub_WSALookupServiceEnd; + WSALookupServiceNextA := Stub_WSALookupServiceNextA; + WSALookupServiceNextW := Stub_WSALookupServiceNextW; + WSALookupServiceNext := Stub_WSALookupServiceNext; + + // WSANSPIoctl is not supported in WinCE 4.20 but is in later versions. + WSANSPIoctl := Stub_WSANSPIoctl; + + WSANtohl := Stub_WSANtohl; + WSANtohs := Stub_WSANtohs; + {$IFNDEF WINCE} + WSAPoll := Stub_WSAPoll; + WSAProviderConfigChange := Stub_WSAProviderConfigChange; + {$ENDIF} + WSARecv := Stub_WSARecv; + {$IFNDEF WINCE} + WSARecvDisconnect := Stub_WSARecvDisconnect; + WSARecvEx := Stub_WSARecvEx; + {$ENDIF} + WSARecvFrom := Stub_WSARecvFrom; + WSARecvMsg := Stub_WSARecvMsg; + WSARemoveServiceClass := Stub_WSARemoveServiceClass; + WSAResetEvent := Stub_WSAResetEvent; + WSASend := Stub_WSASend; + {$IFNDEF WINCE} + WSASendDisconnect := Stub_WSASendDisconnect; + WSASendMsg := Stub_WSASendMsg; + {$ENDIF} + WSASendTo := Stub_WSASendTo; + {$IFNDEF WINCE} + WSASetBlockingHook := Stub_WSASetBlockingHook; + {$ENDIF} + WSASetEvent := Stub_WSASetEvent; + WSASetLastError := Stub_WSASetLastError; + WSASetServiceA := Stub_WSASetServiceA; + WSASetServiceW := Stub_WSASetServiceW; + WSASetService := Stub_WSASetService; + WSASocketA := Stub_WSASocketA; + WSASocketW := Stub_WSASocketW; + WSASocket := Stub_WSASocket; + WSAStartup := Stub_WSAStartup; + WSAStringToAddressA := Stub_WSAStringToAddressA; + WSAStringToAddressW := Stub_WSAStringToAddressW; + WSAStringToAddress := Stub_WSAStringToAddress; + {$IFNDEF WINCE} + WSAUnhookBlockingHook := Stub_WSAUnhookBlockingHook; + {$ENDIF} + WSAWaitForMultipleEvents := Stub_WSAWaitForMultipleEvents; +end; + +function WSAMakeSyncReply(Buflen, AError: Word): Longint; +{$IFDEF USE_INLINE}inline;{$ENDIF} +begin + Result := MakeLong(Buflen, AError); +end; + +function WSAMakeSelectReply(Event, AError: Word): Longint; +{$IFDEF USE_INLINE}inline;{$ENDIF} +begin + Result := MakeLong(Event, AError); +end; + +function WSAGetAsyncBuflen(Param: Longint): Word; +{$IFDEF USE_INLINE}inline;{$ENDIF} +begin + Result := LOWORD(Param); +end; + +function WSAGetAsyncError(Param: Longint): Word; +{$IFDEF USE_INLINE}inline;{$ENDIF} +begin + Result := HIWORD(Param); +end; + +function WSAGetSelectEvent(Param: Longint): Word; +{$IFDEF USE_INLINE}inline;{$ENDIF} +begin + Result := LOWORD(Param); +end; + +function WSAGetSelectError(Param: Longint): Word; +{$IFDEF USE_INLINE}inline;{$ENDIF} +begin + WSAGetSelectError := HIWORD(Param); +end; + +procedure FD_CLR(ASocket: TSocket; var FDSet: TFDSet); +var + i: u_int; +begin + i := 0; + while i < FDSet.fd_count do + begin + if FDSet.fd_array[i] = ASocket then + begin + while i < FDSet.fd_count - 1 do + begin + FDSet.fd_array[i] := FDSet.fd_array[i+1]; + Inc(i); + end; + Dec(FDSet.fd_count); + Break; + end; + Inc(i); + end; +end; + +function FD_ISSET(ASocket: TSocket; var FDSet: TFDSet): Boolean; +{$IFDEF USE_INLINE}inline;{$ENDIF} +begin + Result := __WSAFDIsSet(ASocket, FDSet); +end; + +procedure FD_SET(ASocket: TSocket; var FDSet: TFDSet); +{$IFDEF USE_INLINE}inline;{$ENDIF} +begin + if FDSet.fd_count < fd_setsize then + begin + FDSet.fd_array[FDSet.fd_count] := ASocket; + Inc(FDSet.fd_count); + end; +end; + +procedure FD_ZERO(var FDSet: TFDSet); +{$IFDEF USE_INLINE}inline;{$ENDIF} +begin + FDSet.fd_count := 0; +end; + +//Posix aliases +// #define CMSGHDR_ALIGN WSA_CMSGHDR_ALIGN +function CMSGHDR_ALIGN(const Alength: SIZE_T): SIZE_T; +{$IFDEF USE_INLINE}inline;{$ENDIF} +begin + Result := WSA_CMSGHDR_ALIGN(Alength); +end; + +// #define CMSGDATA_ALIGN WSA_CMSGDATA_ALIGN +function CMSGDATA_ALIGN(const Alength: UINT_PTR): UINT_PTR; + {$IFDEF USE_INLINE}inline;{$ENDIF} +begin + Result := WSA_CMSGDATA_ALIGN(Alength); +end; + +//#define CMSG_FIRSTHDR WSA_CMSG_FIRSTHDR +function CMSG_FIRSTHDR(const msg: LPWSAMSG): LPWSACMSGHDR; +{$IFDEF USE_INLINE}inline;{$ENDIF} +begin + Result := WSA_CMSG_FIRSTHDR(msg); +end; + +// #define CMSG_NXTHDR WSA_CMSG_NXTHDR +function CMSG_NXTHDR(const msg: LPWSAMSG; const cmsg: LPWSACMSGHDR): LPWSACMSGHDR; +{$IFDEF USE_INLINE}inline;{$ENDIF} +begin + Result := WSA_CMSG_NXTHDR(msg, cmsg); +end; + +// #define CMSG_SPACE WSA_CMSG_SPACE +function CMSG_SPACE(const Alength: UINT_PTR): UINT_PTR; +{$IFDEF USE_INLINE}inline;{$ENDIF} +begin + Result := WSA_CMSG_SPACE(ALength); +end; + +// #define CMSG_LEN WSA_CMSG_LEN +function CMSG_LEN(const Alength: SIZE_T): SIZE_T; +{$IFDEF USE_INLINE}inline;{$ENDIF} +begin + Result := WSA_CMSG_LEN(ALength); +end; + +// +function WSA_CMSGHDR_ALIGN(const Alength: SIZE_T): SIZE_T; +type + {$IFDEF WIN32} + {$ALIGN ON} + TempRec = record + x: AnsiChar; + test: WSACMSGHDR; + end; + {$ALIGN OFF} + {$ELSE} + //Win64 and WinCE seem to require alignment for API records + TempRec = record + x: AnsiChar; + test: WSACMSGHDR; + end; + {$ENDIF} +var + Alignment: SIZE_T; + Tmp: ^TempRec; +begin + Tmp := nil; + Alignment := UINT_PTR(@(Tmp^.test)); + Result := (Alength + (Alignment-1)) and not (Alignment-1); +end; + +function WSA_CMSGDATA_ALIGN(const Alength: UINT_PTR): UINT_PTR; +{$IFDEF USE_INLINE}inline;{$ENDIF} +begin + Result := (Alength + MAX_NATURAL_ALIGNMENT_SUB_1) and not (MAX_NATURAL_ALIGNMENT_SUB_1); +end; + +function WSA_CMSG_FIRSTHDR(const msg: LPWSAMSG): LPWSACMSGHDR; +{$IFDEF USE_INLINE}inline;{$ENDIF} +begin + if (msg <> nil) and (msg^.Control.len >= SIZE_WSACMSGHDR) then begin + Result := LPWSACMSGHDR(msg^.Control.buf); + end else begin + Result := nil; + end; +end; + +function WSA_CMSG_NXTHDR(const msg: LPWSAMSG; const cmsg: LPWSACMSGHDR): LPWSACMSGHDR; +{$IFDEF USE_INLINE}inline;{$ENDIF} +begin + if cmsg = nil then begin + Result := WSA_CMSG_FIRSTHDR(msg); + end else begin + if (UINT_PTR(cmsg) + WSA_CMSGHDR_ALIGN(cmsg^.cmsg_len) + SIZE_WSACMSGHDR) > (UINT_PTR(msg^.Control.buf) + msg^.Control.len) then begin + Result := nil; + end else begin + Result := LPWSACMSGHDR(UINT_PTR(cmsg) + WSA_CMSGHDR_ALIGN(cmsg^.cmsg_len)); + end; + end; +end; + +function WSA_CMSG_DATA(const cmsg: LPWSACMSGHDR): PByte; +{$IFDEF USE_INLINE}inline;{$ENDIF} +begin + Result := PByte(UINT_PTR(cmsg) + WSA_CMSGDATA_ALIGN(SIZE_WSACMSGHDR)); +end; + +function WSA_CMSG_SPACE(const Alength: UINT_PTR): UINT_PTR; +{$IFDEF USE_INLINE}inline;{$ENDIF} +begin + Result := WSA_CMSGDATA_ALIGN(UINT_PTR(SIZE_WSACMSGHDR + WSA_CMSGHDR_ALIGN(Alength))); +end; + +function WSA_CMSG_LEN(const Alength: SIZE_T): SIZE_T; +{$IFDEF USE_INLINE}inline;{$ENDIF} +begin + Result := (WSA_CMSGDATA_ALIGN(SizeOf(WSACMSGHDR)) + Alength); +end; + +function IP_MSFILTER_SIZE(const numsrc: DWORD): UINT_PTR; +{$IFDEF USE_INLINE}inline;{$ENDIF} +begin + Result := SIZE_IP_MSFILTER - SIZE_TINADDR + (numsrc*SIZE_TINADDR); +end; + +function SS_PORT(ssp: PSockAddrIn): u_short; +{$IFDEF USE_INLINE}inline;{$ENDIF} +begin + if ssp <> nil then begin + Result := ssp^.sin_port; + end else begin + Result := 0; + end; +end; + +function IN6ADDR_ANY_INIT: TIn6Addr; +{$IFDEF USE_INLINE}inline;{$ENDIF} +begin + with Result do begin + System.FillChar(s6_addr, SIZE_TIN6ADDR, 0); {Do not Localize} + end; +end; + +function IN6ADDR_LOOPBACK_INIT: TIn6Addr; +{$IFDEF USE_INLINE}inline;{$ENDIF} +begin + with Result do begin + System.FillChar(s6_addr, SIZE_TIN6ADDR, 0); {Do not Localize} + s6_addr[15] := 1; + end; +end; + +procedure IN6ADDR_SETANY(sa: PSockAddrIn6); +{$IFDEF USE_INLINE}inline;{$ENDIF} +begin + if sa <> nil then begin + with sa^ do begin + sin6_family := AF_INET6; + sin6_port := 0; + sin6_flowinfo := 0; + PULONG(@sin6_addr.s6_addr[0])^ := 0; + PULONG(@sin6_addr.s6_addr[4])^ := 0; + PULONG(@sin6_addr.s6_addr[8])^ := 0; + PULONG(@sin6_addr.s6_addr[12])^ := 0; + end; + end; +end; + +procedure IN6ADDR_SETLOOPBACK(sa: PSockAddrIn6); +{$IFDEF USE_INLINE}inline;{$ENDIF} +begin + if sa <> nil then begin + with sa^ do begin + sin6_family := AF_INET6; + sin6_port := 0; + sin6_flowinfo := 0; + PULONG(@sin6_addr.s6_addr[0])^ := 0; + PULONG(@sin6_addr.s6_addr[4])^ := 0; + PULONG(@sin6_addr.s6_addr[8])^ := 0; + PULONG(@sin6_addr.s6_addr[12])^ := 1; + end; + end; +end; + +function IN6ADDR_ISANY(sa: PSockAddrIn6): Boolean; +{$IFDEF USE_INLINE}inline;{$ENDIF} +begin + if sa <> nil then begin + with sa^ do begin + Result := (sin6_family = AF_INET6) and + (PULONG(@sin6_addr.s6_addr[0])^ = 0) and + (PULONG(@sin6_addr.s6_addr[4])^ = 0) and + (PULONG(@sin6_addr.s6_addr[8])^ = 0) and + (PULONG(@sin6_addr.s6_addr[12])^ = 0); + end; + end else begin + Result := False; + end; +end; + +function IN6ADDR_ISLOOPBACK(sa: PSockAddrIn6): Boolean; +{$IFDEF USE_INLINE}inline;{$ENDIF} +begin + if sa <> nil then begin + with sa^ do begin + Result := (sin6_family = AF_INET6) and + (PULONG(@sin6_addr.s6_addr[0])^ = 0) and + (PULONG(@sin6_addr.s6_addr[4])^ = 0) and + (PULONG(@sin6_addr.s6_addr[8])^ = 0) and + (PULONG(@sin6_addr.s6_addr[12])^ = 1); + end; + end else begin + Result := False; + end; +end; + +function IN6_ADDR_EQUAL(const a: PIn6Addr; const b: PIn6Addr): Boolean; +{$IFDEF USE_INLINE}inline;{$ENDIF} +begin + Result := System.SysUtils.CompareMem(a, b, SIZE_TIN6ADDR); +end; + +function IN6_IS_ADDR_UNSPECIFIED(const a: PIn6Addr): Boolean; +{$IFDEF USE_INLINE}inline;{$ENDIF} +begin + Result := IN6_ADDR_EQUAL(a, @in6addr_any); +end; + +function IN6_IS_ADDR_LOOPBACK(const a: PIn6Addr): Boolean; +{$IFDEF USE_INLINE}inline;{$ENDIF} +begin + Result := IN6_ADDR_EQUAL(a, @in6addr_loopback); +end; + +function IN6_IS_ADDR_MULTICAST(const a: PIn6Addr): Boolean; +{$IFDEF USE_INLINE}inline;{$ENDIF} +begin + if a <> nil then begin + Result := (a^.s6_addr[0] = $FF); + end else begin + Result := False; + end; +end; + +function IN6_IS_ADDR_EUI64(const a : PIn6Addr) : Boolean; +{$IFDEF USE_INLINE}inline;{$ENDIF} + // + // Format prefixes 001 through 111, except for multicast. + // +begin + if a <> nil then begin + Result := ((a^.s6_addr[0] and $e0) <> 0 ) and (not IN6_IS_ADDR_MULTICAST(a)); + end else begin + Result := False; + end; +end; + +function IN6_IS_ADDR_SUBNET_ROUTER_ANYCAST(const a : PIn6Addr) : Boolean; +{$IFDEF USE_INLINE}inline;{$ENDIF} +// +// Is this the subnet router anycast address? +// See RFC 2373. +// +begin + if a <> nil then begin + Result := IN6_IS_ADDR_EUI64(a) and (a^.word[4] = 0) and + (a^.word[5] = 0) and (a^.word[6]=0) and (a^.word[7]=0); + end else begin + Result := False; + end; +end; + +function IN6_IS_ADDR_SUBNET_RESERVED_ANYCAST(const a: PIn6Addr) : Boolean; +{$IFDEF USE_INLINE}inline;{$ENDIF} +begin + if a <> nil then begin + Result := IN6_IS_ADDR_EUI64(a) and (a^.word[4] = $fffd) and + (a^.word[5] = $ffff) and (a^.word[6] = $ffff) and + ((a^.word[7] and $80ff) = $80ff); + end else begin + Result := False; + end; +end; + +function IN6_IS_ADDR_ANYCAST(const a: PIn6Addr) : Boolean; +{$IFDEF USE_INLINE}inline;{$ENDIF} +begin + if a <> nil then begin + Result := IN6_IS_ADDR_SUBNET_RESERVED_ANYCAST(a) or IN6_IS_ADDR_SUBNET_ROUTER_ANYCAST(a); + end else begin + Result := False; + end; +end; + +function IN6_IS_ADDR_LINKLOCAL(const a: PIn6Addr): Boolean; +{$IFDEF USE_INLINE}inline;{$ENDIF} +begin + if a <> nil then begin + Result := (a^.s6_addr[0] = $FE) and ((a^.s6_addr[1] and $C0) = $80); + end else begin + Result := False; + end; +end; + +function IN6_IS_ADDR_SITELOCAL(const a: PIn6Addr): Boolean; +{$IFDEF USE_INLINE}inline;{$ENDIF} +begin + if a <> nil then begin + Result := (a^.s6_addr[0] = $FE) and ((a^.s6_addr[1] and $C0) = $C0); + end else begin + Result := False; + end; +end; + +function IN6_IS_ADDR_V4MAPPED(const a: PIn6Addr): Boolean; +{$IFDEF USE_INLINE}inline;{$ENDIF} +begin + if a <> nil then begin + with a^ do begin + Result := (word[0] = 0) and + (word[1] = 0) and + (word[2] = 0) and + (word[3] = 0) and + (word[4] = 0) and + (word[5] = $FFFF); + end; + end else begin + Result := False; + end; +end; + +function IN6_IS_ADDR_V4COMPAT(const a: PIn6Addr): Boolean; +{$IFDEF USE_INLINE}inline;{$ENDIF} +begin + if a <> nil then begin + with a^ do begin + Result := (word[0] = 0) and + (word[1] = 0) and + (word[2] = 0) and + (word[3] = 0) and + (word[4] = 0) and + (word[5] = 0) and + not ((word[6] = 0) and (s6_addr[14] = 0) and + ((s6_addr[15] = 0) or (s6_addr[15] = 1))); + end; + end else begin + Result := False; + end; +end; + +function IN6_IS_ADDR_MC_NODELOCAL(const a: PIn6Addr): Boolean; +{$IFDEF USE_INLINE}inline;{$ENDIF} +begin + if a <> nil then begin + Result := IN6_IS_ADDR_MULTICAST(a) and ((a^.s6_addr[1] and $F) = 1); + end else begin + Result := False; + end; +end; + +function IN6_IS_ADDR_MC_LINKLOCAL(const a: PIn6Addr): Boolean; +{$IFDEF USE_INLINE}inline;{$ENDIF} +begin + if a <> nil then begin + Result := IN6_IS_ADDR_MULTICAST(a) and ((a^.s6_addr[1] and $F) = 2); + end else begin + Result := False; + end; +end; + +function IN6_IS_ADDR_MC_SITELOCAL(const a: PIn6Addr): Boolean; +{$IFDEF USE_INLINE}inline;{$ENDIF} +begin + if a <> nil then begin + Result := IN6_IS_ADDR_MULTICAST(a) and ((a^.s6_addr[1] and $F) = 5); + end else begin + Result := False; + end; +end; + +function IN6_IS_ADDR_MC_ORGLOCAL(const a: PIn6Addr): Boolean; +{$IFDEF USE_INLINE}inline;{$ENDIF} +begin + if a <> nil then begin + Result := IN6_IS_ADDR_MULTICAST(a) and ((a^.s6_addr[1] and $F) = 8); + end else begin + Result := False; + end; +end; + +function IN6_IS_ADDR_MC_GLOBAL(const a: PIn6Addr): Boolean; +{$IFDEF USE_INLINE}inline;{$ENDIF} +begin + if a <> nil then begin + Result := IN6_IS_ADDR_MULTICAST(a) and ((a^.s6_addr[1] and $F) = $E); + end else begin + Result := False; + end; +end; + +procedure IN6_SET_ADDR_UNSPECIFIED(a : PIN6_ADDR); +{$IFDEF USE_INLINE}inline;{$ENDIF} +begin + system.FillChar(a^.s6_addr, SizeOf(IN6_ADDR), 0 ); +end; + +procedure IN6_SET_ADDR_LOOPBACK(a : PIN6_ADDR); +{$IFDEF USE_INLINE}inline;{$ENDIF} +begin + system.FillChar(a^.s6_addr, SizeOf(IN6_ADDR), 0 ); + a^.s6_addr[15] := 1; +end; + +// A macro convenient for setting up NETBIOS SOCKADDRs. +procedure SET_NETBIOS_SOCKADDR(snb : PSockAddrNB; const SnbType : Word; const Name : PAnsiChar; const Port : AnsiChar); +var + {$IFDEF FPC} + len : sizeint; + {$ELSE} + len : DWord; + {$ENDIF} +begin + if snb <> nil then begin + with snb^ do begin + snb_family := AF_NETBIOS; + snb_type := SnbType; + len := System.AnsiStrings.StrLen(Name); + if len >= NETBIOS_NAME_LENGTH-1 then begin + System.Move(Name^, snb_name, NETBIOS_NAME_LENGTH-1); + end else begin + if len > 0 then begin + System.Move(Name^, snb_name, LongInt(len)); + end; + System.FillChar((PAnsiChar(@snb_name)+len)^, NETBIOS_NAME_LENGTH-1-len, ' '); {Do not Localize} + end; + snb_name[NETBIOS_NAME_LENGTH-1] := Port; + end; + end; +end; + +function GROUP_FILTER_SIZE(const numsrc : DWord) : UINT_PTR; +{$IFDEF USE_INLINE}inline;{$ENDIF} +begin + Result := (SIZE_GROUP_FILTER - SIZE_SOCKADDR_STORAGE) + + (numsrc * SIZE_SOCKADDR_STORAGE); +end; + +initialization + in6addr_any := IN6ADDR_ANY_INIT; + in6addr_loopback := IN6ADDR_LOOPBACK_INIT; + InitializeStubs; + InitializeWinSock; + InitializeStubsEx; + +finalization + UninitializeWinSock; + +end. + diff --git a/ThirdParty/DCS/Net/Net.Wship6.pas b/ThirdParty/DCS/Net/Net.Wship6.pas new file mode 100644 index 00000000..0f93507b --- /dev/null +++ b/ThirdParty/DCS/Net/Net.Wship6.pas @@ -0,0 +1,590 @@ +{ + $Project$ + $Workfile$ + $Revision$ + $DateUTC$ + $Id$ + + This file is part of the Indy (Internet Direct) project, and is offered + under the dual-licensing agreement described on the Indy website. + (http://www.indyproject.org/) + + Copyright: + (c) 1993-2005, Chad Z. Hower and the Indy Pit Crew. All rights reserved. +} +{ + $Log$ +} +{ + Rev 1.0 2004.02.03 3:14:52 PM czhower + Move and updates + + Rev 1.2 10/15/2003 9:43:20 PM DSiders + Added localization comments. + + Rev 1.1 1-10-2003 19:44:28 BGooijen + fixed leak in CloseLibrary() + + Rev 1.0 11/13/2002 09:03:24 AM JPMugaas +} + +unit Net.Wship6; + +interface + +{$I Net.Winsock.inc} + +{$IFDEF FPC} + {$IFDEF WIN32} + {$ALIGN OFF} + {$ELSE} + //It turns out that Win64 and WinCE require record alignment + {$PACKRECORDS C} + {$ENDIF} +{$ELSE} + {$IFDEF WIN64} + {$ALIGN ON} + {$MINENUMSIZE 4} + {$ELSE} + {$MINENUMSIZE 4} + {$IFDEF REQUIRES_PROPER_ALIGNMENT} + {$ALIGN ON} + {$ELSE} + {$ALIGN OFF} + {$WRITEABLECONST OFF} + {$ENDIF} + {$ENDIF} +{$ENDIF} + +uses + {$IFDEF HAS_TInterlocked} + syncobjs, //here to facilitate inlining with Delphi + {$ENDIF} + Windows, + Net.Winsock2; + +const + Wship6_dll = 'Wship6.dll'; {do not localize} + iphlpapi_dll = 'iphlpapi.dll'; {do not localize} + fwpuclnt_dll = 'Fwpuclnt.dll'; {Do not localize} + + // Error codes from getaddrinfo(). + + //JPM + //Note that I am adding a GIA_ prefix on my own because + //some names here share some names defined in Iocp.Winsock2 causing + //an unpredictible problem. The values are not defined the same in Iocp.Winsock2 + {$EXTERNALSYM GIA_EAI_ADDRFAMILY} + GIA_EAI_ADDRFAMILY = 1 ; // Address family for nodename not supported. + {$EXTERNALSYM GIA_EAI_AGAIN} + GIA_EAI_AGAIN = 2 ; // Temporary failure in name resolution. + {$EXTERNALSYM GIA_EAI_BADFLAGS} + GIA_EAI_BADFLAGS = 3 ; // Invalid value for ai_flags. + {$EXTERNALSYM GIA_EAI_FAIL} + GIA_EAI_FAIL = 4 ; // Non-recoverable failure in name resolution. + {$EXTERNALSYM GIA_EAI_FAMILY} + GIA_EAI_FAMILY = 5 ; // Address family ai_family not supported. + {$EXTERNALSYM GIA_EAI_MEMORY} + GIA_EAI_MEMORY = 6 ; // Memory allocation failure. + {$EXTERNALSYM GIA_EAI_NODATA} + GIA_EAI_NODATA = 7 ; // No address associated with nodename. + {$EXTERNALSYM GIA_EAI_NONAME} + GIA_EAI_NONAME = 8 ; // Nodename nor servname provided, or not known. + {$EXTERNALSYM GIA_EAI_SERVICE} + GIA_EAI_SERVICE = 9 ; // Servname not supported for ai_socktype. + {$EXTERNALSYM GIA_EAI_SOCKTYPE} + GIA_EAI_SOCKTYPE = 10 ; // Socket type ai_socktype not supported. + {$EXTERNALSYM GIA_EAI_SYSTEM} + GIA_EAI_SYSTEM = 11 ; // System error returned in errno. + + {$EXTERNALSYM NI_MAXHOST} + NI_MAXHOST = 1025; // Max size of a fully-qualified domain name. + {$EXTERNALSYM NI_MAXSERV} + NI_MAXSERV = 32; // Max size of a service name. + + // Flags for getnameinfo(). + + {$EXTERNALSYM NI_NOFQDN} + NI_NOFQDN = $1 ; // Only return nodename portion for local hosts. + {$EXTERNALSYM NI_NUMERICHOST} + NI_NUMERICHOST = $2 ; // Return numeric form of the host's address. + {$EXTERNALSYM NI_NAMEREQD} + NI_NAMEREQD = $4 ; // Error if the host's name not in DNS. + {$EXTERNALSYM NI_NUMERICSERV} + NI_NUMERICSERV = $8 ; // Return numeric form of the service (port #). + {$EXTERNALSYM NI_DGRAM} + NI_DGRAM = $10 ; // Service is a datagram service. + + //JPM - These may not be supported in WinCE 4.2 + {$EXTERNALSYM PROTECTION_LEVEL_RESTRICTED} + PROTECTION_LEVEL_RESTRICTED = 30; //* for Intranet apps /* + {$EXTERNALSYM PROTECTION_LEVEL_DEFAULT} + PROTECTION_LEVEL_DEFAULT = 20; //* default level /* + {$EXTERNALSYM PROTECTION_LEVEL_UNRESTRICTED} + PROTECTION_LEVEL_UNRESTRICTED = 10; //* for peer-to-peer apps /* + + {$EXTERNALSYM SOCKET_SETTINGS_GUARANTEE_ENCRYPTION} + SOCKET_SETTINGS_GUARANTEE_ENCRYPTION = $00000001; + {$EXTERNALSYM SOCKET_SETTINGS_ALLOW_INSECURE} + SOCKET_SETTINGS_ALLOW_INSECURE = $00000002; + + {$EXTERNALSYM SOCKET_INFO_CONNECTION_SECURED} + SOCKET_INFO_CONNECTION_SECURED = $00000001; + {$EXTERNALSYM SOCKET_INFO_CONNECTION_ENCRYPTED} + SOCKET_INFO_CONNECTION_ENCRYPTED = $00000002; + +type + // RLebeau: find a better place for this + {$IFNDEF HAS_UInt64} + {$EXTERNALSYM UINT64} + UINT64 = Int64; + {$ENDIF} + + {$NODEFINE PPaddrinfo} + PPaddrinfo = ^PAddrInfo; + {$NODEFINE PPaddrinfoW} + PPaddrinfoW = ^PAddrInfoW; + + {$IFNDEF WINCE} + {$EXTERNALSYM SOCKET_SECURITY_PROTOCOL} + {$EXTERNALSYM SOCKET_SECURITY_PROTOCOL_DEFAULT} + {$EXTERNALSYM SOCKET_SECURITY_PROTOCOL_IPSEC} + {$EXTERNALSYM SOCKET_SECURITY_PROTOCOL_INVALID} + SOCKET_SECURITY_PROTOCOL = ( + SOCKET_SECURITY_PROTOCOL_DEFAULT, SOCKET_SECURITY_PROTOCOL_IPSEC, SOCKET_SECURITY_PROTOCOL_INVALID + ); + + {$EXTERNALSYM SOCKET_SECURITY_SETTINGS_IPSEC} + SOCKET_SECURITY_SETTINGS_IPSEC = record + SecurityProtocol : SOCKET_SECURITY_PROTOCOL; + SecurityFlags : ULONG; + IpsecFlags : ULONG; + AuthipMMPolicyKey : TGUID; + AuthipQMPolicyKey : TGUID; + Reserved : TGUID; + Reserved2 : UINT64; + UserNameStringLen : ULONG; + DomainNameStringLen : ULONG; + PasswordStringLen : ULONG; + // wchar_t AllStrings[0]; + end; + {$EXTERNALSYM PSOCKET_SECURITY_SETTINGS_IPSEC} + PSOCKET_SECURITY_SETTINGS_IPSEC = ^SOCKET_SECURITY_SETTINGS_IPSEC; + + {$EXTERNALSYM SOCKET_PEER_TARGET_NAME} + SOCKET_PEER_TARGET_NAME = record + SecurityProtocol : SOCKET_SECURITY_PROTOCOL; + PeerAddress : SOCKADDR_STORAGE; + PeerTargetNameStringLen : ULONG; + //wchar_t AllStrings[0]; + end; + {$EXTERNALSYM PSOCKET_PEER_TARGET_NAME} + PSOCKET_PEER_TARGET_NAME = ^SOCKET_PEER_TARGET_NAME; + + {$EXTERNALSYM SOCKET_SECURITY_QUERY_INFO} + SOCKET_SECURITY_QUERY_INFO = record + SecurityProtocol : SOCKET_SECURITY_PROTOCOL; + Flags : ULONG; + PeerApplicationAccessTokenHandle : UINT64; + PeerMachineAccessTokenHandle : UINT64; + end; + {$EXTERNALSYM PSOCKET_SECURITY_QUERY_INFO} + PSOCKET_SECURITY_QUERY_INFO = ^SOCKET_SECURITY_QUERY_INFO; + {$EXTERNALSYM SOCKET_SECURITY_QUERY_TEMPLATE} + SOCKET_SECURITY_QUERY_TEMPLATE = record + SecurityProtocol : SOCKET_SECURITY_PROTOCOL; + PeerAddress : SOCKADDR_STORAGE; + PeerTokenAccessMask : ULONG; + end; + {$EXTERNALSYM PSOCKET_SECURITY_QUERY_TEMPLATE} + PSOCKET_SECURITY_QUERY_TEMPLATE = ^SOCKET_SECURITY_QUERY_TEMPLATE; + +//callback defs +type + {$EXTERNALSYM LPLOOKUPSERVICE_COMPLETION_ROUTINE} + LPLOOKUPSERVICE_COMPLETION_ROUTINE = procedure (const dwError, dwBytes : DWORD; lpOverlapped : LPWSAOVERLAPPED); stdcall; +{$ENDIF} + +type + {$EXTERNALSYM LPFN_GETADDRINFO} + LPFN_GETADDRINFO = function(NodeName: PAnsiChar; ServiceName: PAnsiChar; Hints: Paddrinfo; ppResult: PPaddrinfo): Integer; stdcall; + {$EXTERNALSYM LPFN_GETADDRINFOW} + LPFN_GETADDRINFOW = function(NodeName: PWideChar; ServiceName: PWideChar; Hints: PaddrinfoW; ppResult: PPaddrinfoW): Integer; stdcall; + {$EXTERNALSYM LPFN_GETNAMEINFO} + //The IPv6 preview for Win2K defines hostlen and servelen as size_t but do not use them + //for these definitions as the newer SDK's define those as DWORD. + LPFN_GETNAMEINFO = function(sa: psockaddr; salen: u_int; host: PAnsiChar; hostlen: u_int; serv: PAnsiChar; servlen: u_int; flags: Integer): Integer; stdcall; + {$EXTERNALSYM LPFN_GETNAMEINFOW} + LPFN_GETNAMEINFOW = function(sa: psockaddr; salen: u_int; host: PWideChar; hostlen: u_int; serv: PWideChar; servlen: u_int; flags: Integer): Integer; stdcall; + {$EXTERNALSYM LPFN_FREEADDRINFO} + LPFN_FREEADDRINFO = procedure(ai: Paddrinfo); stdcall; + {$EXTERNALSYM LPFN_FREEADDRINFOW} + LPFN_FREEADDRINFOW = procedure(ai: PaddrinfoW); stdcall; + +//function GetAdaptersAddresses( Family:cardinal; Flags:cardinal; Reserved:pointer; pAdapterAddresses: PIP_ADAPTER_ADDRESSES; pOutBufLen:pcardinal):cardinal;stdcall; external iphlpapi_dll; + +{ the following are not used, nor tested} +{function getipnodebyaddr(const src:pointer; len:integer; af:integer;var error_num:integer) :phostent;stdcall; external Wship6_dll; +procedure freehostent(ptr:phostent);stdcall; external Wship6_dll; +function inet_pton(af:integer; const src:pchar; dst:pointer):integer;stdcall; external Wship6_dll; +function inet_ntop(af:integer; const src:pointer; dst:pchar;size:integer):pchar;stdcall; external Wship6_dll; +} + {$IFNDEF WINCE} + {$EXTERNALSYM LPFN_INET_PTON} + LPFN_INET_PTON = function (af: Integer; const src: PAnsiChar; dst: Pointer): Integer; stdcall; + {$EXTERNALSYM LPFN_INET_PTONW} + LPFN_INET_PTONW = function (af: Integer; const src: PWideChar; dst: Pointer): Integer; stdcall; + {$EXTERNALSYM LPFN_INET_NTOP} + LPFN_INET_NTOP = function (af: Integer; const src: Pointer; dst: PAnsiChar; size: size_t): PAnsiChar; stdcall; + {$EXTERNALSYM LPFN_INET_NTOPW} + LPFN_INET_NTOPW = function (af: Integer; const src: Pointer; dst: PWideChar; size: size_t): PAnsiChar; stdcall; + +{ end the following are not used, nor tested} +//These are provided in case we need them later +//Windows Vista + {$EXTERNALSYM LPFN_GETADDRINFOEXA} + LPFN_GETADDRINFOEXA = function(pName : PAnsiChar; pServiceName : PAnsiChar; + const dwNameSpace: DWord; lpNspId : LPGUID; hints : PADDRINFOEXA; + ppResult : PADDRINFOEXA; timeout : Ptimeval; lpOverlapped : LPWSAOVERLAPPED; + lpCompletionRoutine : LPLOOKUPSERVICE_COMPLETION_ROUTINE; + var lpNameHandle : THandle) : Integer; stdcall; + {$EXTERNALSYM LPFN_GETADDRINFOEXW} + LPFN_GETADDRINFOEXW = function(pName : PWideChar; pServiceName : PWideChar; + const dwNameSpace: DWord; lpNspId : LPGUID;hints : PADDRINFOEXW; + ppResult : PADDRINFOEXW; timeout : Ptimeval; lpOverlapped : LPWSAOVERLAPPED; + lpCompletionRoutine : LPLOOKUPSERVICE_COMPLETION_ROUTINE; + var lpNameHandle : THandle) : Integer; stdcall; + {$EXTERNALSYM LPFN_SETADDRINFOEXA} + LPFN_SETADDRINFOEXA= function(pName : PAnsiChar; pServiceName : PAnsiChar; + pAddresses : PSOCKET_ADDRESS; const dwAddressCount : DWord; lpBlob : LPBLOB; + const dwFlags : DWord; const dwNameSpace : DWord; lpNspId : LPGUID; + timeout : Ptimeval; + lpOverlapped : LPWSAOVERLAPPED; + lpCompletionRoutine : LPLOOKUPSERVICE_COMPLETION_ROUTINE; var lpNameHandle : THandle) : Integer; stdcall; + {$EXTERNALSYM LPFN_SETADDRINFOEXW} + LPFN_SETADDRINFOEXW= function(pName : PWideChar; pServiceName : PWideChar; + pAddresses : PSOCKET_ADDRESS; const dwAddressCount : DWord; lpBlob : LPBLOB; + const dwFlags : DWord; const dwNameSpace : DWord; lpNspId : LPGUID; + timeout : Ptimeval; + lpOverlapped : LPWSAOVERLAPPED; + lpCompletionRoutine : LPLOOKUPSERVICE_COMPLETION_ROUTINE; var lpNameHandle : THandle) : Integer; stdcall; + + {$EXTERNALSYM LPFN_FREEADDRINFOEX} + LPFN_FREEADDRINFOEX = procedure(pAddrInfoEx : PADDRINFOEXA) ; stdcall; + {$EXTERNALSYM LPFN_FREEADDRINFOEXW} + LPFN_FREEADDRINFOEXW = procedure(pAddrInfoEx : PADDRINFOEXW) ; stdcall; + + {$EXTERNALSYM LPFN_GETADDRINFOEX} + {$EXTERNALSYM LPFN_SETADDRINFOEX} + {$IFDEF UNICODE} + LPFN_GETADDRINFOEX = LPFN_GETADDRINFOEXW; + LPFN_SETADDRINFOEX = LPFN_SETADDRINFOEXW; + {$ELSE} + LPFN_GETADDRINFOEX = LPFN_GETADDRINFOEXA; + LPFN_SETADDRINFOEX = LPFN_SETADDRINFOEXA; + {$ENDIF} + + // Fwpuclnt.dll - API + {$EXTERNALSYM LPFN_WSADELETESOCKETPEERTARGETNAME} + LPFN_WSADELETESOCKETPEERTARGETNAME = function (Socket : TSocket; + PeerAddr : Psockaddr; PeerAddrLen : ULONG; + Overlapped : LPWSAOVERLAPPED; CompletionRoutine : LPWSAOVERLAPPED_COMPLETION_ROUTINE): Integer; stdcall; + {$EXTERNALSYM LPFN_WSASETSOCKETPEERTARGETNAME} + LPFN_WSASETSOCKETPEERTARGETNAME = function (Socket : TSocket; + PeerTargetName : PSOCKET_PEER_TARGET_NAME; PeerTargetNameLen : ULONG; + Overlapped : LPWSAOVERLAPPED; CompletionRoutine : LPWSAOVERLAPPED_COMPLETION_ROUTINE) : Integer; stdcall; + {$EXTERNALSYM LPFN_WSAIMPERSONATESOCKETPEER} + LPFN_WSAIMPERSONATESOCKETPEER = function (Socket : TSocket; + PeerAddress : Psockaddr; peerAddressLen : ULONG) : Integer; stdcall; + {$EXTERNALSYM LPFN_WSAQUERYSOCKETSECURITY} + LPFN_WSAQUERYSOCKETSECURITY = function (Socket : TSocket; + SecurityQueryTemplate : PSOCKET_SECURITY_QUERY_TEMPLATE; const SecurityQueryTemplateLen : ULONG; + var SecurityQueryInfo : PSOCKET_SECURITY_QUERY_INFO; var SecurityQueryInfoLen : ULONG; + Overlapped : LPWSAOVERLAPPED; CompletionRoutine : LPWSAOVERLAPPED_COMPLETION_ROUTINE) : Integer; stdcall; + {$EXTERNALSYM LPFN_WSAREVERTIMPERSONATION} + LPFN_WSAREVERTIMPERSONATION = function : Integer; stdcall; +{$ENDIF} + +const + {$NODEFINE fn_GetAddrInfoEx} + {$NODEFINE fn_SetAddrInfoEx} + {$NODEFINE fn_FreeAddrInfoEx} + {$NODEFINE fn_GetAddrInfo} + {$NODEFINE fn_getnameinfo} + {$NODEFINE fn_freeaddrinfo} + {$NODEFINE fn_inet_pton} + {$NODEFINE fn_inet_ntop} + {$IFDEF UNICODE} + {$IFNDEF WINCE} + fn_GetAddrInfoEx = 'GetAddrInfoExW'; + fn_SetAddrInfoEx = 'SetAddrInfoExW'; + fn_FreeAddrInfoEx = 'FreeAddrInfoExW'; + {$ENDIF} + fn_GetAddrInfo = 'GetAddrInfoW'; + fn_getnameinfo = 'GetNameInfoW'; + fn_freeaddrinfo = 'FreeAddrInfoW'; + {$IFNDEF WINCE} + fn_inet_pton = 'InetPtonW'; + fn_inet_ntop = 'InetNtopW'; + {$ENDIF} + {$ELSE} + {$IFNDEF WINCE} + fn_GetAddrInfoEx = 'GetAddrInfoExA'; + fn_SetAddrInfoEx = 'SetAddrInfoExA'; + fn_FreeAddrInfoEx = 'FreeAddrInfoEx'; + {$ENDIF} + fn_GetAddrInfo = 'getaddrinfo'; + fn_getnameinfo = 'getnameinfo'; + fn_freeaddrinfo = 'freeaddrinfo'; + {$IFNDEF WINCE} + fn_inet_pton = 'inet_pton'; + fn_inet_ntop = 'inet_ntop'; + {$ENDIF} + {$ENDIF} + +var + {$EXTERNALSYM getaddrinfo} + {$EXTERNALSYM getnameinfo} + {$EXTERNALSYM freeaddrinfo} + {$EXTERNALSYM inet_pton} + {$EXTERNALSYM inet_ntop} + {$IFDEF UNICODE} + getaddrinfo: LPFN_GETADDRINFOW = nil; + getnameinfo: LPFN_GETNAMEINFOW = nil; + freeaddrinfo: LPFN_FREEADDRINFOW = nil; + {$IFNDEF WINCE} + //These are here for completeness + inet_pton : LPFN_inet_ptonW = nil; + inet_ntop : LPFN_inet_ntopW = nil; + {$ENDIF} + {$ELSE} + getaddrinfo: LPFN_GETADDRINFO = nil; + getnameinfo: LPFN_GETNAMEINFO = nil; + freeaddrinfo: LPFN_FREEADDRINFO = nil; + {$IFNDEF WINCE} + //These are here for completeness + inet_pton : LPFN_inet_pton = nil; + inet_ntop : LPFN_inet_ntop = nil; + {$ENDIF} + {$ENDIF} + {$IFNDEF WINCE} + { + IMPORTANT!!! + + These are Windows Vista functions and there's no guarantee that you will have + them so ALWAYS check the function pointer before calling them. + } + {$EXTERNALSYM GetAddrInfoEx} + GetAddrInfoEx : LPFN_GETADDRINFOEX = nil; + {$EXTERNALSYM SetAddrInfoEx} + SetAddrInfoEx : LPFN_SETADDRINFOEX = nil; + {$EXTERNALSYM FreeAddrInfoEx} + //You can't alias the LPFN for this because the ASCII version of this + //does not end with an "a" + {$IFDEF UNICODE} + FreeAddrInfoEx : LPFN_FREEADDRINFOEX = nil; + {$ELSE} + FreeAddrInfoEx : LPFN_FREEADDRINFOEXW = nil; + {$ENDIF} + + //Fwpuclnt.dll available for Windows Vista and later + {$EXTERNALSYM WSASETSOCKETPEERTARGETNAME} + WSASetSocketPeerTargetName : LPFN_WSASETSOCKETPEERTARGETNAME = nil; + {$EXTERNALSYM WSADELETESOCKETPEERTARGETNAME} + WSADeleteSocketPeerTargetName : LPFN_WSADELETESOCKETPEERTARGETNAME = nil; + {$EXTERNALSYM WSAImpersonateSocketPeer} + WSAImpersonateSocketPeer : LPFN_WSAIMPERSONATESOCKETPEER = nil; + {$EXTERNALSYM WSAQUERYSOCKETSECURITY} + WSAQUERYSOCKETSECURITY : LPFN_WSAQUERYSOCKETSECURITY = nil; + {$EXTERNALSYM WSAREVERTIMPERSONATION} + WSARevertImpersonation : LPFN_WSAREVERTIMPERSONATION = nil; + {$ENDIF} + +var + GIdIPv6FuncsAvailable: Boolean = False; + +function gaiErrorToWsaError(const gaiError: Integer): Integer; + +//We want to load this library only after loading Winsock and unload immediately +//before unloading Winsock. +procedure InitLibrary; +procedure CloseLibrary; + +implementation + +uses + SysUtils; + +var + hWship6Dll : THandle = 0; // Wship6.dll handle + //Use this instead of hWship6Dll because this will point to the correct lib. + hProcHandle : THandle = 0; + {$IFNDEF WINCE} + hfwpuclntDll : THandle = 0; + {$ENDIF} + +function gaiErrorToWsaError(const gaiError: Integer): Integer; +begin + case gaiError of + GIA_EAI_ADDRFAMILY: Result := 0; + GIA_EAI_AGAIN: Result := WSATRY_AGAIN; + GIA_EAI_BADFLAGS: Result := WSAEINVAL; + GIA_EAI_FAIL: Result := WSANO_RECOVERY; + GIA_EAI_FAMILY: Result := WSAEAFNOSUPPORT; + GIA_EAI_MEMORY: Result := WSA_NOT_ENOUGH_MEMORY; + GIA_EAI_NODATA: Result := WSANO_DATA; + GIA_EAI_NONAME: Result := WSAHOST_NOT_FOUND; + GIA_EAI_SERVICE: Result := WSATYPE_NOT_FOUND; + GIA_EAI_SOCKTYPE: Result := WSAESOCKTNOSUPPORT; + GIA_EAI_SYSTEM: + begin + Result := 0; // avoid warning + RaiseLastOSError; + end; + else + Result := gaiError; + end; +end; + +function InterlockedExchangeTHandle(var VTarget: THandle; const AValue: THandle): THandle; +{$IFDEF USE_INLINE}inline;{$ENDIF} +begin + {$IFDEF HAS_TInterlocked} + {$IFDEF THANDLE_32} + Result := THandle(TInterlocked.Exchange(LongInt(VTarget), LongInt(AValue))); + {$ENDIF} + //Temporary workaround. TInterlocked for Emb really should accept 64 bit unsigned values as set of parameters + //for TInterlocked.Exchange since 64-bit wide integers are common on 64 bit platforms. + {$IFDEF THANDLE_64} + Result := THandle(TInterlocked.Exchange(Int64(VTarget), Int64(AValue))); + {$ENDIF} + {$ELSE} + {$IFDEF THANDLE_32} + Result := THandle(InterlockedExchange(LongInt(VTarget), LongInt(AValue))); + {$ENDIF} + {$IFDEF THANDLE_64} + Result := THandle(InterlockedExchange64(Int64(VTarget), Int64(AValue))); + {$ENDIF} + {$ENDIF} +end; + +procedure CloseLibrary; +var + h : THandle; +begin + {$IFNDEF WINCE} + {$IFNDEF WIN64} + //Only unload the IPv6 functions for Windows NT (2000 or greater). + //Note that Win64 was introduced after Windows XP. That was based on Windows + //Server code so we'll skip this in Win64. + //I'm just doing this as a minor shortcut. + if (Win32Platform <> VER_PLATFORM_WIN32_NT) or (Win32MajorVersion < 5) then begin + Exit; + end; + {$ENDIF} + {$ENDIF} + h := InterlockedExchangeTHandle(hWship6Dll, 0); + if h <> 0 then begin + FreeLibrary(h); + end; + {$IFNDEF WINCE} + h := InterlockedExchangeTHandle(hfwpuclntDll, 0); + if h <> 0 then begin + FreeLibrary(h); + end; + {$ENDIF} + GIdIPv6FuncsAvailable := False; + + getaddrinfo := nil; + getnameinfo := nil; + freeaddrinfo := nil; + {$IFNDEF WINCE} + WSASetSocketPeerTargetName := nil; + WSADeleteSocketPeerTargetName := nil; + WSAImpersonateSocketPeer := nil; + WSAQuerySocketSecurity := nil; + WSARevertImpersonation := nil; + {$ENDIF} +end; + +procedure InitLibrary; +begin + GIdIPv6FuncsAvailable := False; + {$IFNDEF WINCE} + {$IFNDEF WIN64} + //Only attempt to load the IPv6 functions for Windows NT (2000 or greater). + //Note that Win64 was introduced after Windows XP. That was based on Windows + //Server code so we'll skip this in Win64. + if (Win32Platform <> VER_PLATFORM_WIN32_NT) or (Win32MajorVersion < 5) then begin + Exit; + end; + {$ENDIF} + {$ENDIF} +{ +IMPORTANT!!! + +I am doing things this way because the functions we want are probably in +the Winsock2 dll. If they are not there, only then do you actually want +to try the Wship6.dll. I know it's a mess but I found that the functions +may not load if they aren't in Wship6.dll (and they aren't there in some +versions of Windows). + +hProcHandle provides a transparant way of managing the two possible library +locations. hWship6Dll is kept so we can unload the Wship6.dll if necessary. +} + //Winsock2 has to be loaded by IdWinsock first. + if not Net.Winsock2.Winsock2Loaded then + begin + Net.Winsock2.InitializeWinSock; + end; + hProcHandle := Net.Winsock2.WinsockHandle; + getaddrinfo := GetProcAddress(hProcHandle, fn_getaddrinfo); + if not Assigned(getaddrinfo) then + begin + hWship6Dll := SafeLoadLibrary(Wship6_dll); + hProcHandle := hWship6Dll; + getaddrinfo := GetProcAddress(hProcHandle, fn_getaddrinfo); {do not localize} + end; + + if Assigned(getaddrinfo) then + begin + getnameinfo := GetProcAddress(hProcHandle, fn_getnameinfo); {do not localize} + if Assigned(getnameinfo) then + begin + freeaddrinfo := GetProcAddress(hProcHandle, fn_freeaddrinfo); {do not localize} + if Assigned(freeaddrinfo) then + begin + GIdIPv6FuncsAvailable := True; + + //Additional functions should be initialized here. + {$IFNDEF WINCE} + inet_pton := GetProcAddress(hProcHandle, fn_inet_pton); {do not localize} + inet_ntop := GetProcAddress(hProcHandle, fn_inet_ntop); {do not localize} + GetAddrInfoEx := GetProcAddress(hProcHandle, fn_GetAddrInfoEx); {Do not localize} + SetAddrInfoEx := GetProcAddress(hProcHandle, fn_SetAddrInfoEx); {Do not localize} + FreeAddrInfoEx := GetProcAddress(hProcHandle, fn_FreeAddrInfoEx); {Do not localize} + hfwpuclntDll := SafeLoadLibrary(fwpuclnt_dll); + if hfwpuclntDll <> 0 then + begin + WSASetSocketPeerTargetName := GetProcAddress(hfwpuclntDll, 'WSASetSocketPeerTargetName'); {Do not localize} + WSADeleteSocketPeerTargetName := GetProcAddress(hfwpuclntDll, 'WSADeleteSocketPeerTargetName'); {Do not localize} + WSAImpersonateSocketPeer := GetProcAddress(hfwpuclntDll, 'WSAImpersonateSocketPeer'); {Do not localize} + WSAQuerySocketSecurity := GetProcAddress(hfwpuclntDll, 'WSAQuerySocketSecurity'); {Do not localize} + WSARevertImpersonation := GetProcAddress(hfwpuclntDll, 'WSARevertImpersonation'); {Do not localize} + end; + {$ENDIF} + Exit; + end; + end; + end; + + CloseLibrary; +end; + +initialization + InitLibrary; + +finalization + CloseLibrary; + +end. diff --git a/ThirdParty/DCS/README.en.md b/ThirdParty/DCS/README.en.md new file mode 100644 index 00000000..04b6e8bc --- /dev/null +++ b/ThirdParty/DCS/README.en.md @@ -0,0 +1,70 @@ +# Delphi Cross Platform Socket Communication Library + +Author: WiNDDRiVER(soulawing@gmail.com) + +### [中文](README.md) + +## Update list + +#### 2019.02.17 +- Fix the problem of memory leakage caused by TIoEventThread + > thank viniciusfbb for finding and fixing the problem +- Fix memory leak caused by [weak] + > when used with a third-party memory management library, there will be a memory leak. robertodellapasqua found the problem and pony5551 finally found the cause of the problem. Thank you very much! This should be a defect in Delphi's [weak] internal implementation. The problem was solved after replacing [weak] with [unsafe] + +#### 2019.01.15 +- increase mbedtls support + - mbedtls enabling method: turn on \_\_CROSS\_SSL\_\_ and \_\_MBED\_TLS\_\_ in the engineering compilation option, and add the directory under MbedObj to the Library path of the corresponding platform + - mbedtls support is not stable at present, please do not use it in production environment + + #### 2017.08.22 + - code refactoring, with many modifications, see source code for details + - Several new interface have been added. See demos for usage + - ICrossSocket + - ICrossSslSocket + - ICrossServer + - ICrossSslServer + +## Features +- Use different IO models for different platforms: + - IOCP + > Windows + + - KQUEUE + > FreeBSD(MacOSX, iOS...) + + - EPOLL + > Linux(Linux, Android...) + + - Supports extremely high concurrency + + - Windows + > can run more than 100000 concurrent number, need to modify the registry to adjust the default maximum port number + + - Mac + > preliminary tests were conducted. the test environment was OSX 10.9.5 in the virtual machine. even if the limit on the number of handles in the system was modified, + > can only open more than 32000 concurrent connections at most, perhaps OSX Server version can support higher concurrency + + - IPv4 and IPv6 are supported at the same time. + - Zero Memory Copy + +## Passed the test + - Windows + - OSX + - iOS + - Android + - Linux + +## Suggested Development Environment + - To give full play to cross-platform functions, please use Delphi 10.2 Tokyo and above + - The minimum requirement is to support the Delphi version of generic and anonymous functions. I am not sure from which version generic and anonymous functions are supported. + +## Known Issues + - SSL under non - Windows platform is unstable, please do not use it in production environment + +## Some Test Screenshots +- **HTTP**(ubuntu 16.04 desktop for server) +![20170607110011](https://user-images.githubusercontent.com/3221597/26860614-61b750b4-4b71-11e7-8afc-74c3ebf16f7e.png) + +- **HTTPS**(ubuntu 16.04 desktop for server) +![20170607142650](https://user-images.githubusercontent.com/3221597/26868229-d8d79f40-4b9a-11e7-927c-bfb3d7e6e55d.png) diff --git a/ThirdParty/DCS/README.md b/ThirdParty/DCS/README.md new file mode 100644 index 00000000..f523cbe0 --- /dev/null +++ b/ThirdParty/DCS/README.md @@ -0,0 +1,74 @@ +# Delphi 跨平台 Socket 通讯库 + +作者: WiNDDRiVER(soulawing@gmail.com) + +### [English](README.en.md) + +## 更新记录 + +#### 2019.02.17 +- 修复 TIoEventThread 可能引起的内存泄漏的问题 + > 感谢 viniciusfbb 发现并修复了该问题 +- 修复 [weak] 引起的内存泄漏问题 + > 与第三方内存管理库搭配使用时会出现内存泄漏,robertodellapasqua 发现了该问题,最终由 pony5551 找到了该问题产生的原因,特此感谢!这应该是 Delphi 的 [weak] 内部实现有缺陷,将 [weak] 替换成 [unsafe] 后该问题得以解决。 + +#### 2019.01.15 +- 增加 mbedtls 支持 + - mbedtls启用方法:在工程编译选项中开启 \_\_CROSS\_SSL\_\_ 和 \_\_MBED\_TLS\_\_ 这两个编译开关, 并且将 MbedObj 下的目录添加到对应平台的 Library path 中 + - 目前 mbedtls 支持还不够稳定, 请勿用于生产环境 + +#### 2017.08.22 +- 代码重构, 做了大量修改, 详见源码 +- 增加了几个新的 interface, 用法详见 demos + - ICrossSocket + - ICrossSslSocket + - ICrossServer + - ICrossSslServer + + +## 特性 + +- 针对不同平台使用不同的IO模型: + - IOCP + > Windows + + - KQUEUE + > FreeBSD(MacOSX, iOS...) + + - EPOLL + > Linux(Linux, Android...) + +- 支持极高的并发 + + - Windows + > 能跑10万以上的并发数, 需要修改注册表调整默认的最大端口数 + + - Mac + > 做了初步测试, 测试环境为虚拟机中的 OSX 10.9.5, 即便修改了系统的句柄数限制, + > 最多也只能打开32000多个并发连接, 或许 OSX Server 版能支持更高的并发吧 + +- 同时支持IPv4、IPv6 + +- 零内存拷贝 + +## 已通过测试 +- Windows +- OSX +- iOS +- Android +- Linux + +## 建议开发环境 +- 要发挥跨平台的完整功能请使用Delphi 10.2 Tokyo及以上的版本 +- 最低要求支持泛型和匿名函数的Delphi版本, 具体是从哪个版本开始支持泛型和匿名函数的我也不是太清楚 + +## 已知问题 +- 非Windows平台下的SSL不稳定, 请勿用于生产环境 + +## 部分测试截图 + +- **HTTP**(服务端为ubuntu 16.04 desktop) +![20170607110011](https://user-images.githubusercontent.com/3221597/26860614-61b750b4-4b71-11e7-8afc-74c3ebf16f7e.png) + +- **HTTPS**(服务端为ubuntu 16.04 desktop) +![20170607142650](https://user-images.githubusercontent.com/3221597/26868229-d8d79f40-4b9a-11e7-927c-bfb3d7e6e55d.png) diff --git a/ThirdParty/DCS/Utils/Utils.DateTime.pas b/ThirdParty/DCS/Utils/Utils.DateTime.pas new file mode 100644 index 00000000..ff3e11ce --- /dev/null +++ b/ThirdParty/DCS/Utils/Utils.DateTime.pas @@ -0,0 +1,565 @@ +{******************************************************************************} +{ } +{ Delphi cross platform socket library } +{ } +{ Copyright (c) 2017 WiNDDRiVER(soulawing@gmail.com) } +{ } +{ Homepage: https://github.com/winddriver/Delphi-Cross-Socket } +{ } +{******************************************************************************} +unit Utils.DateTime; + +interface + +uses + System.SysUtils, + System.DateUtils, + System.Types, + System.Math; + +type + TDateTimeHelper = record helper for TDateTime + private const + CMillisPerDay = Int64(MSecsPerSec * SecsPerMin * MinsPerHour * HoursPerDay); + private + function GetDay: Word; inline; + function GetDate: TDateTime; inline; + function GetDayOfWeek: Word; inline; + function GetDayOfYear: Word; inline; + function GetHour: Word; inline; + function GetMillisecond: Word; inline; + function GetMinute: Word; inline; + function GetMonth: Word; inline; + function GetSecond: Word; inline; + function GetTime: TDateTime; inline; + function GetYear: Integer; inline; + class function GetNow: TDateTime; static; inline; + class function GetToday: TDateTime; static; inline; + class function GetTomorrow: TDateTime; static; inline; + class function GetYesterDay: TDateTime; static; inline; + procedure SetYear(const Value: Integer); inline; + procedure SetDay(const Value: Word); inline; + procedure SetHour(const Value: Word); inline; + procedure SetMillisecond(const Value: Word); inline; + procedure SetMinute(const Value: Word); inline; + procedure SetMonth(const Value: Word); inline; + procedure SetSecond(const Value: Word); inline; + procedure SetDate(const Value: TDateTime); inline; + procedure SetTime(const Value: TDateTime); inline; + public + class function Create(const AYear, AMonth, ADay: Word): TDateTime; overload; static; inline; + class function Create(const AYear, AMonth, ADay, AHour, AMinute, ASecond, + AMillisecond: Word): TDateTime; overload; static; inline; + + class property Now: TDateTime read GetNow; + class property Today: TDateTime read GetToday; + class property Yesterday: TDateTime read GetYesterDay; + class property Tomorrow: TDateTime read GetTomorrow; + + property Date: TDateTime read GetDate write SetDate; + property Time: TDateTime read GetTime write SetTime; + + property DayOfWeek: Word read GetDayOfWeek; + property DayOfYear: Word read GetDayOfYear; + + property Year: Integer read GetYear write SetYear; + property Month: Word read GetMonth write SetMonth; + property Day: Word read GetDay write SetDay; + property Hour: Word read GetHour write SetHour; + property Minute: Word read GetMinute write SetMinute; + property Second: Word read GetSecond write SetSecond; + property Millisecond: Word read GetMillisecond write SetMillisecond; + + function ToString(const AFormatStr: string = ''): string; inline; + function ToMilliseconds: Int64; inline; + + function StartOfYear: TDateTime; inline; + function EndOfYear: TDateTime; inline; + function StartOfMonth: TDateTime; inline; + function EndOfMonth: TDateTime; inline; + function StartOfWeek: TDateTime; inline; + function EndOfWeek: TDateTime; inline; + function StartOfDay: TDateTime; inline; + function EndOfDay: TDateTime; inline; + + function AddYears(const ANumberOfYears: Integer = 1): TDateTime; inline; + function AddMonths(const ANumberOfMonths: Integer = 1): TDateTime; inline; + function AddDays(const ANumberOfDays: Integer = 1): TDateTime; inline; + function AddHours(const ANumberOfHours: Int64 = 1): TDateTime; inline; + function AddMinutes(const ANumberOfMinutes: Int64 = 1): TDateTime; inline; + function AddSeconds(const ANumberOfSeconds: Int64 = 1): TDateTime; inline; + function AddMilliseconds(const ANumberOfMilliseconds: Int64 = 1): TDateTime; inline; + + function CompareTo(const ADateTime: TDateTime): TValueRelationship; inline; + function Equals(const ADateTime: TDateTime): Boolean; inline; + function IsSameDay(const ADateTime: TDateTime): Boolean; inline; + function IsSameMonth(const ADateTime: TDateTime): Boolean; + function IsSameYear(const ADateTime: TDateTime): Boolean; + function InRange(const AStartDateTime, AEndDateTime: TDateTime; const AInclusive: Boolean = True): Boolean; inline; + function IsInLeapYear: Boolean; inline; + function IsToday: Boolean; inline; + function IsAM: Boolean; inline; + function IsPM: Boolean; inline; + + function YearsBetween(const ADateTime: TDateTime): Integer; inline; + function MonthsBetween(const ADateTime: TDateTime): Integer; inline; + function WeeksBetween(const ADateTime: TDateTime): Integer; inline; + function DaysBetween(const ADateTime: TDateTime): Integer; inline; + function HoursBetween(const ADateTime: TDateTime): Int64; inline; + function MinutesBetween(const ADateTime: TDateTime): Int64; inline; + function SecondsBetween(const ADateTime: TDateTime): Int64; inline; + function MilliSecondsBetween(const ADateTime: TDateTime): Int64; inline; + + function YearsDiffer(const ADateTime: TDateTime): Integer; inline; + function MonthsDiffer(const ADateTime: TDateTime): Integer; inline; + function WeeksDiffer(const ADateTime: TDateTime): Integer; inline; + function DaysDiffer(const ADateTime: TDateTime): Integer; inline; + function HoursDiffer(const ADateTime: TDateTime): Int64; inline; + function MinutesDiffer(const ADateTime: TDateTime): Int64; inline; + function SecondsDiffer(const ADateTime: TDateTime): Int64; inline; + function MilliSecondsDiffer(const ADateTime: TDateTime): Int64; inline; + + function WithinYears(const ADateTime: TDateTime; const AYears: Integer): Boolean; inline; + function WithinMonths(const ADateTime: TDateTime; const AMonths: Integer): Boolean; inline; + function WithinWeeks(const ADateTime: TDateTime; const AWeeks: Integer): Boolean; inline; + function WithinDays(const ADateTime: TDateTime; const ADays: Integer): Boolean; inline; + function WithinHours(const ADateTime: TDateTime; const AHours: Int64): Boolean; inline; + function WithinMinutes(const ADateTime: TDateTime; const AMinutes: Int64): Boolean; inline; + function WithinSeconds(const ADateTime: TDateTime; const ASeconds: Int64): Boolean; inline; + function WithinMilliseconds(const ADateTime: TDateTime; const AMilliseconds: Int64): Boolean; inline; + + function ToUniversalTime(const AForceDaylight: Boolean = False): TDateTime; inline; + function ToLocalTime: TDateTime; inline; + end; + +implementation + +{ TDateTimeHelper } + +function TDateTimeHelper.AddDays(const ANumberOfDays: Integer): TDateTime; +begin + Result := IncDay(Self, ANumberOfDays); +end; + +function TDateTimeHelper.AddHours(const ANumberOfHours: Int64): TDateTime; +begin + Result := IncHour(Self, ANumberOfHours); +end; + +function TDateTimeHelper.AddMilliseconds(const ANumberOfMilliseconds: Int64): TDateTime; +begin + Result := IncMilliSecond(Self, ANumberOfMilliseconds); +end; + +function TDateTimeHelper.AddMinutes(const ANumberOfMinutes: Int64): TDateTime; +begin + Result := IncMinute(Self, ANumberOfMinutes); +end; + +function TDateTimeHelper.AddMonths(const ANumberOfMonths: Integer): TDateTime; +begin + Result := IncMonth(Self, ANumberOfMonths); +end; + +function TDateTimeHelper.AddSeconds(const ANumberOfSeconds: Int64): TDateTime; +begin + Result := IncSecond(Self, ANumberOfSeconds); +end; + +function TDateTimeHelper.AddYears(const ANumberOfYears: Integer): TDateTime; +begin + Result := IncYear(Self, ANumberOfYears); +end; + +function TDateTimeHelper.CompareTo(const ADateTime: TDateTime): TValueRelationship; +begin + Result := CompareDateTime(Self, ADateTime); +end; + +class function TDateTimeHelper.Create(const AYear, AMonth, + ADay: Word): TDateTime; +begin + Result := EncodeDate(AYear, AMonth, ADay); +end; + +class function TDateTimeHelper.Create(const AYear, AMonth, ADay, AHour, AMinute, + ASecond, AMillisecond: Word): TDateTime; +begin + Result := EncodeDateTime(AYear, AMonth, ADay, AHour, AMinute, ASecond, AMillisecond); +end; + +function TDateTimeHelper.DaysBetween(const ADateTime: TDateTime): Integer; +begin + Result := System.DateUtils.DaysBetween(Self, ADateTime); +end; + +function TDateTimeHelper.DaysDiffer(const ADateTime: TDateTime): Integer; +begin + Result := (Self.ToMilliseconds - ADateTime.ToMilliseconds) div CMillisPerDay; +end; + +function TDateTimeHelper.EndOfDay: TDateTime; +begin + Result := EndOfTheDay(Self); +end; + +function TDateTimeHelper.EndOfMonth: TDateTime; +begin + Result := EndOfTheMonth(Self); +end; + +function TDateTimeHelper.EndOfWeek: TDateTime; +begin + Result := EndOfTheWeek(Self); +end; + +function TDateTimeHelper.EndOfYear: TDateTime; +begin + Result := EndOfTheYear(Self); +end; + +function TDateTimeHelper.Equals(const ADateTime: TDateTime): Boolean; +begin + Result := SameDateTime(Self, ADateTime); +end; + +function TDateTimeHelper.GetDate: TDateTime; +begin + Result := DateOf(Self); +end; + +function TDateTimeHelper.GetDay: Word; +begin + Result := DayOf(Self); +end; + +function TDateTimeHelper.GetDayOfWeek: Word; +begin + Result := DayOfTheWeek(Self); +end; + +function TDateTimeHelper.GetDayOfYear: Word; +begin + Result := DayOfTheYear(Self); +end; + +function TDateTimeHelper.GetHour: Word; +begin + Result := HourOf(Self); +end; + +function TDateTimeHelper.GetMillisecond: Word; +begin + Result := MilliSecondOf(Self); +end; + +function TDateTimeHelper.GetMinute: Word; +begin + Result := MinuteOf(Self); +end; + +function TDateTimeHelper.GetMonth: Word; +begin + Result := MonthOf(Self); +end; + +class function TDateTimeHelper.GetNow: TDateTime; +begin + Result := System.SysUtils.Now; +end; + +function TDateTimeHelper.GetSecond: Word; +begin + Result := SecondOf(Self); +end; + +function TDateTimeHelper.GetTime: TDateTime; +begin + Result := TimeOf(Self); +end; + +class function TDateTimeHelper.GetToday: TDateTime; +begin + Result := System.SysUtils.Date; +end; + +class function TDateTimeHelper.GetTomorrow: TDateTime; +begin + Result := System.SysUtils.Date + 1; +end; + +function TDateTimeHelper.GetYear: Integer; +begin + Result := YearOf(Self); +end; + +class function TDateTimeHelper.GetYesterDay: TDateTime; +begin + Result := System.SysUtils.Date - 1; +end; + +function TDateTimeHelper.HoursBetween(const ADateTime: TDateTime): Int64; +begin + Result := System.DateUtils.HoursBetween(Self, ADateTime); +end; + +function TDateTimeHelper.HoursDiffer(const ADateTime: TDateTime): Int64; +begin + Result := (Self.ToMilliseconds - ADateTime.ToMilliseconds) + div (MSecsPerSec * SecsPerMin * MinsPerHour); +end; + +function TDateTimeHelper.InRange(const AStartDateTime, AEndDateTime: TDateTime; const AInclusive: Boolean): Boolean; +begin + Result := DateTimeInRange(Self, AStartDateTime, AEndDateTime, AInclusive); +end; + +function TDateTimeHelper.IsAM: Boolean; +begin + Result := System.DateUtils.IsAM(Self); +end; + +function TDateTimeHelper.IsInLeapYear: Boolean; +begin + Result := System.DateUtils.IsInLeapYear(Self); +end; + +function TDateTimeHelper.IsPM: Boolean; +begin + Result := System.DateUtils.IsPM(Self); +end; + +function TDateTimeHelper.IsSameDay(const ADateTime: TDateTime): Boolean; +begin + Result := (Trunc(Self) = Trunc(ADateTime)); +end; + +function TDateTimeHelper.IsSameMonth(const ADateTime: TDateTime): Boolean; +var + Y, M1, M2, D: Word; +begin + DecodeDate(Self, Y, M1, D); + DecodeDate(ADateTime, Y, M2, D); + Result := (M1 = M2); +end; + +function TDateTimeHelper.IsSameYear(const ADateTime: TDateTime): Boolean; +var + Y1, Y2, M, D: Word; +begin + DecodeDate(Self, Y1, M, D); + DecodeDate(ADateTime, Y2, M, D); + Result := (Y1 = Y2); +end; + +function TDateTimeHelper.IsToday: Boolean; +begin + Result := System.DateUtils.IsToday(Self); +end; + +function TDateTimeHelper.MilliSecondsBetween(const ADateTime: TDateTime): Int64; +begin + Result := System.DateUtils.MilliSecondsBetween(Self, ADateTime); +end; + +function TDateTimeHelper.MilliSecondsDiffer(const ADateTime: TDateTime): Int64; +begin + Result := (Self.ToMilliseconds - ADateTime.ToMilliseconds); +end; + +function TDateTimeHelper.MinutesBetween(const ADateTime: TDateTime): Int64; +begin + Result := System.DateUtils.MinutesBetween(Self, ADateTime); +end; + +function TDateTimeHelper.MinutesDiffer(const ADateTime: TDateTime): Int64; +begin + Result := (Self.ToMilliseconds - ADateTime.ToMilliseconds) + div (MSecsPerSec * SecsPerMin); +end; + +function TDateTimeHelper.MonthsBetween(const ADateTime: TDateTime): Integer; +begin + Result := System.DateUtils.MonthsBetween(Self, ADateTime); +end; + +function TDateTimeHelper.MonthsDiffer(const ADateTime: TDateTime): Integer; +begin + Result := (Self.ToMilliseconds - ADateTime.ToMilliseconds) + div Round(CMillisPerDay * ApproxDaysPerMonth); +end; + +function TDateTimeHelper.SecondsBetween(const ADateTime: TDateTime): Int64; +begin + Result := System.DateUtils.SecondsBetween(Self, ADateTime); +end; + +function TDateTimeHelper.SecondsDiffer(const ADateTime: TDateTime): Int64; +begin + Result := (Self.ToMilliseconds - ADateTime.ToMilliseconds) + div (MSecsPerSec); +end; + +procedure TDateTimeHelper.SetDate(const Value: TDateTime); +begin + Self := Trunc(Value) + Frac(Self); +end; + +procedure TDateTimeHelper.SetDay(const Value: Word); +begin + Self := RecodeDay(Self, Value); +end; + +procedure TDateTimeHelper.SetHour(const Value: Word); +begin + Self := RecodeHour(Self, Value); +end; + +procedure TDateTimeHelper.SetMillisecond(const Value: Word); +begin + Self := RecodeMilliSecond(Self, Value); +end; + +procedure TDateTimeHelper.SetMinute(const Value: Word); +begin + Self := RecodeMinute(Self, Value); +end; + +procedure TDateTimeHelper.SetMonth(const Value: Word); +begin + Self := RecodeMonth(Self, Value); +end; + +procedure TDateTimeHelper.SetSecond(const Value: Word); +begin + Self := RecodeSecond(Self, Value); +end; + +procedure TDateTimeHelper.SetTime(const Value: TDateTime); +begin + Self := Trunc(Self) + Frac(Value); +end; + +procedure TDateTimeHelper.SetYear(const Value: Integer); +begin + Self := RecodeYear(Self, Value); +end; + +function TDateTimeHelper.StartOfDay: TDateTime; +begin + Result := StartOfTheDay(Self); +end; + +function TDateTimeHelper.StartOfMonth: TDateTime; +begin + Result := StartOfTheMonth(Self); +end; + +function TDateTimeHelper.StartOfWeek: TDateTime; +begin + Result := StartOfTheWeek(Self); +end; + +function TDateTimeHelper.StartOfYear: TDateTime; +begin + Result := StartOfTheYear(Self); +end; + +function TDateTimeHelper.ToLocalTime: TDateTime; +begin + Result := TTimeZone.Local.ToLocalTime(Self); +end; + +function TDateTimeHelper.ToMilliseconds: Int64; +var + LTimeStamp: TTimeStamp; +begin + LTimeStamp := DateTimeToTimeStamp(Self); + Result := (Int64(LTimeStamp.Date) * MSecsPerDay) + LTimeStamp.Time; +end; + +function TDateTimeHelper.ToString(const AFormatStr: string): string; +begin + if AFormatStr = '' then + Result := DateToStr(Self) + else + Result := FormatDateTime(AFormatStr, Self); +end; + +function TDateTimeHelper.ToUniversalTime( + const AForceDaylight: Boolean): TDateTime; +begin + Result := TTimeZone.Local.ToUniversalTime(Self, AForceDaylight); +end; + +function TDateTimeHelper.WeeksBetween(const ADateTime: TDateTime): Integer; +begin + Result := System.DateUtils.WeeksBetween(Self, ADateTime); +end; + +function TDateTimeHelper.WeeksDiffer(const ADateTime: TDateTime): Integer; +begin + Result := (Self.ToMilliseconds - ADateTime.ToMilliseconds) + div (CMillisPerDay * DaysPerWeek); +end; + +function TDateTimeHelper.WithinDays(const ADateTime: TDateTime; + const ADays: Integer): Boolean; +begin + Result := System.DateUtils.WithinPastDays(Self, ADateTime, ADays); +end; + +function TDateTimeHelper.WithinHours(const ADateTime: TDateTime; + const AHours: Int64): Boolean; +begin + Result := System.DateUtils.WithinPastHours(Self, ADateTime, AHours); +end; + +function TDateTimeHelper.WithinMilliseconds(const ADateTime: TDateTime; + const AMilliseconds: Int64): Boolean; +begin + Result := System.DateUtils.WithinPastMilliSeconds(Self, ADateTime, AMilliseconds); +end; + +function TDateTimeHelper.WithinMinutes(const ADateTime: TDateTime; + const AMinutes: Int64): Boolean; +begin + Result := System.DateUtils.WithinPastMinutes(Self, ADateTime, AMinutes); +end; + +function TDateTimeHelper.WithinMonths(const ADateTime: TDateTime; + const AMonths: Integer): Boolean; +begin + Result := System.DateUtils.WithinPastMonths(Self, ADateTime, AMonths); +end; + +function TDateTimeHelper.WithinSeconds(const ADateTime: TDateTime; + const ASeconds: Int64): Boolean; +begin + Result := System.DateUtils.WithinPastSeconds(Self, ADateTime, ASeconds); +end; + +function TDateTimeHelper.WithinWeeks(const ADateTime: TDateTime; + const AWeeks: Integer): Boolean; +begin + Result := System.DateUtils.WithinPastWeeks(Self, ADateTime, AWeeks); +end; + +function TDateTimeHelper.WithinYears(const ADateTime: TDateTime; + const AYears: Integer): Boolean; +begin + Result := System.DateUtils.WithinPastYears(Self, ADateTime, AYears); +end; + +function TDateTimeHelper.YearsBetween(const ADateTime: TDateTime): Integer; +begin + Result := System.DateUtils.YearsBetween(Self, ADateTime); +end; + +function TDateTimeHelper.YearsDiffer(const ADateTime: TDateTime): Integer; +begin + Result := (Self.ToMilliseconds - ADateTime.ToMilliseconds) + div Round(CMillisPerDay * ApproxDaysPerYear); +end; + +end. diff --git a/ThirdParty/DCS/Utils/Utils.Logger.pas b/ThirdParty/DCS/Utils/Utils.Logger.pas new file mode 100644 index 00000000..2d97b419 --- /dev/null +++ b/ThirdParty/DCS/Utils/Utils.Logger.pas @@ -0,0 +1,377 @@ +{******************************************************************************} +{ } +{ Delphi cross platform socket library } +{ } +{ Copyright (c) 2017 WiNDDRiVER(soulawing@gmail.com) } +{ } +{ Homepage: https://github.com/winddriver/Delphi-Cross-Socket } +{ } +{******************************************************************************} +unit Utils.Logger; + +interface + +uses + System.Classes, System.SysUtils, System.IOUtils, System.Diagnostics, + System.Generics.Collections, Utils.Utils, Utils.DateTime; + +type + TLogType = (ltNormal, ltWarning, ltError, ltException); + TLogTypeSets = set of TLogType; + +const + LogTypeStr: array [TLogType] of string = ('', 'WAR', 'ERR', 'EXP'); + +type + ILogger = interface + ['{D9AE7F7B-95DE-4840-98FA-A49A4368D658}'] + function GetFilters: TLogTypeSets; + procedure SetFilters(const Value: TLogTypeSets); + + function GetLogDir: string; + function GetLogFileName(ALogType: TLogType; ADate: TDateTime): string; + + procedure AppendLog(const ALog: string; const ATimeFormat: string; ALogType: TLogType = ltNormal; const CRLF: string = ''); overload; + procedure AppendLog(const ALog: string; ALogType: TLogType = ltNormal; const CRLF: string = ''); overload; + procedure AppendLog(const Fmt: string; const Args: array of const; const ATimeFormat: string; ALogType: TLogType = ltNormal; const CRLF: string = ''); overload; + procedure AppendLog(const Fmt: string; const Args: array of const; ALogType: TLogType = ltNormal; const CRLF: string = ''); overload; + + procedure Flush; + + property Filters: TLogTypeSets read GetFilters write SetFilters; + end; + + TLogItem = record + Time: TDateTime; + Text: string; + end; + + TLogBuffer = TList; + + TLogger = class(TInterfacedObject, ILogger) + private const + FLUSH_INTERVAL = 200; + private + FFilters: TLogTypeSets; + + class var FLogger: ILogger; + class constructor Create; + class destructor Destroy; + + function GetFilters: TLogTypeSets; + procedure SetFilters(const Value: TLogTypeSets); + private + FBuffer: array [TLogType] of TLogBuffer; + FBufferLock: array [TLogType] of TObject; + FShutdown, FQuit: Boolean; + + procedure _Lock(const ALogType: TLogType); inline; + procedure _Unlock(const ALogType: TLogType); inline; + procedure _WriteLogFile(const ALogType: TLogType); + procedure _WriteAllLogFiles; inline; + procedure _CreateWriteThread; + procedure _Shutdown; inline; + protected + procedure _AppendLogToBuffer(const S: string; ALogType: TLogType); + public + constructor Create; virtual; + destructor Destroy; override; + + function GetLogDir: string; + function GetLogFileName(ALogType: TLogType; ADate: TDateTime): string; + + procedure AppendLog(const ALog: string; const ATimeFormat: string; ALogType: TLogType = ltNormal; const CRLF: string = ''); overload; + procedure AppendLog(const ALog: string; ALogType: TLogType = ltNormal; const CRLF: string = ''); overload; + procedure AppendLog(const Fmt: string; const Args: array of const; const ATimeFormat: string; ALogType: TLogType = ltNormal; const CRLF: string = ''); overload; + procedure AppendLog(const Fmt: string; const Args: array of const; ALogType: TLogType = ltNormal; const CRLF: string = ''); overload; + + procedure Flush; + + property Filters: TLogTypeSets read GetFilters write SetFilters; + + class property Logger: ILogger read FLogger; + end; + +procedure AppendLog(const ALog: string; const ATimeFormat: string; ALogType: TLogType = ltNormal; const CRLF: string = ';'); overload; +procedure AppendLog(const ALog: string; ALogType: TLogType = ltNormal; const CRLF: string = ';'); overload; +procedure AppendLog(const Fmt: string; const Args: array of const; const ATimeFormat: string; ALogType: TLogType = ltNormal; const CRLF: string = ';'); overload; +procedure AppendLog(const Fmt: string; const Args: array of const; ALogType: TLogType = ltNormal; const CRLF: string = ';'); overload; + +function Logger: ILogger; + +var + // Ĭ־Ŀ¼ + // ɳԶ趨 + DefaultLogDir: string = ''; + +implementation + +class constructor TLogger.Create; +begin + FLogger := TLogger.Create; +end; + +class destructor TLogger.Destroy; +begin +end; + +constructor TLogger.Create; +var + I: TLogType; +begin + FFilters := [ltNormal, ltWarning, ltError, ltException]; + + for I := Low(TLogType) to High(TLogType) do + begin + FBuffer[I] := TLogBuffer.Create; + FBufferLock[I] := TObject.Create; + end; + + _CreateWriteThread; +end; + +destructor TLogger.Destroy; +var + I: TLogType; +begin + Flush; + + _Shutdown; + + for I := Low(TLogType) to High(TLogType) do + begin + FreeAndNil(FBuffer[I]); + FreeAndNil(FBufferLock[I]); + end; + + inherited Destroy; +end; + +procedure TLogger.Flush; +begin + _WriteAllLogFiles; +end; + +function TLogger.GetFilters: TLogTypeSets; +begin + Result := FFilters; +end; + +function TLogger.GetLogDir: string; +begin + if (DefaultLogDir <> '') then + Result := DefaultLogDir + else + Result := + {$IFDEF MSWINDOWS} + TUtils.AppPath + + {$ELSE} + TUtils.AppDocuments + + {$ENDIF} + TUtils.AppName + '.log' + PathDelim; +end; + +function TLogger.GetLogFileName(ALogType: TLogType; ADate: TDateTime): string; +begin + Result := LogTypeStr[ALogType]; + if (Result <> '') then + Result := Result + '-'; + Result := Result + TUtils.DateTimeToStr(ADate, 'YYYY-MM-DD') + '.log'; +end; + +procedure TLogger.SetFilters(const Value: TLogTypeSets); +begin + FFilters := Value; +end; + +procedure TLogger._CreateWriteThread; +begin + TThread.CreateAnonymousThread( + procedure + var + LWatch: TStopwatch; + begin + LWatch := TStopwatch.StartNew; + while not FShutdown do + begin + if (LWatch.ElapsedTicks > FLUSH_INTERVAL) then + begin + Flush; + + LWatch.Reset; + LWatch.Start; + end; + Sleep(10); + end; + + Flush; + + FQuit := True; + end).Start; +end; + +procedure TLogger._Lock(const ALogType: TLogType); +begin + System.TMonitor.Enter(FBufferLock[ALogType]); +end; + +procedure TLogger._Shutdown; +begin + FShutdown := True; + while not FQuit do + Sleep(1); +end; + +procedure TLogger._Unlock(const ALogType: TLogType); +begin + System.TMonitor.Exit(FBufferLock[ALogType]); +end; + +procedure TLogger._WriteLogFile(const ALogType: TLogType); +var + LLogDir, LLogFile: string; + LLastTime: TDateTime; + I: Integer; + LLogItem: TLogItem; + LBuffer: TBytesStream; + + procedure _WriteLogToBuffer(const ALogItem: TLogItem); + var + LBytes: TBytes; + begin + LBytes := TEncoding.UTF8.GetBytes(ALogItem.Text); + LBuffer.Seek(0, TSeekOrigin.soEnd); + LBuffer.Write(LBytes, Length(LBytes)); + end; + + procedure _WriteBufferToFile(const ALogFile: string); + var + LStream: TFileStream; + LBytes: TBytes; + begin + try + LStream := TFile.Open(ALogFile, TFileMode.fmOpenOrCreate, TFileAccess.faReadWrite, TFileShare.fsRead); + try + LStream.Seek(0, TSeekOrigin.soEnd); + LBytes := LBuffer.Bytes; + SetLength(LBytes, LBuffer.Size); + LStream.Write(LBytes, Length(LBytes)); + finally + FreeAndNil(LStream); + end; + except + end; + end; +begin + _Lock(ALogType); + try + if (FBuffer[ALogType].Count <= 0) then Exit; + + LLastTime := 0; + LLogDir := GetLogDir; + ForceDirectories(LLogDir); + + LBuffer := TBytesStream.Create(nil); + try + for I := 0 to FBuffer[ALogType].Count - 1 do + begin + LLogItem := FBuffer[ALogType].Items[I]; + _WriteLogToBuffer(LLogItem); + + if not LLogItem.Time.IsSameDay(LLastTime) + or (I >= FBuffer[ALogType].Count - 1) then + begin + LLastTime := LLogItem.Time; + LLogFile := LLogDir + GetLogFileName(ALogType, LLogItem.Time); + _WriteBufferToFile(LLogFile); + LBuffer.Clear; + end; + end; + FBuffer[ALogType].Clear; + finally + FreeAndNil(LBuffer); + end; + finally + _Unlock(ALogType); + end; +end; + +procedure TLogger._WriteAllLogFiles; +var + I: TLogType; +begin + for I := Low(TLogType) to High(TLogType) do + _WriteLogFile(I); +end; + +procedure TLogger.AppendLog(const ALog: string; const ATimeFormat: string; ALogType: TLogType; const CRLF: string); +var + LText: string; +begin + if not (ALogType in FFilters) then Exit; + + if (CRLF <> '') then + LText := StringReplace(ALog, sLineBreak, CRLF, [rfReplaceAll]) + else + LText := ALog; + LText := TUtils.DateTimeToStr(Now, ATimeFormat) + ' ' + LText + sLineBreak; + + _AppendLogToBuffer(LText, ALogType); +end; + +procedure TLogger.AppendLog(const ALog: string; ALogType: TLogType; const CRLF: string); +begin + AppendLog(ALog, 'HH:NN:SS:ZZZ', ALogType, CRLF); +end; + +procedure TLogger.AppendLog(const Fmt: string; const Args: array of const; const ATimeFormat: string; ALogType: TLogType; const CRLF: string); +begin + AppendLog(TUtils.ThreadFormat(Fmt, Args), ATimeFormat, ALogType, CRLF); +end; + +procedure TLogger.AppendLog(const Fmt: string; const Args: array of const; ALogType: TLogType; const CRLF: string); +begin + AppendLog(TUtils.ThreadFormat(Fmt, Args), ALogType, CRLF); +end; + +procedure TLogger._AppendLogToBuffer(const S: string; ALogType: TLogType); +var + LLogItem: TLogItem; +begin + _Lock(ALogType); + try + LLogItem.Time := Now; + LLogItem.Text := S; + FBuffer[ALogType].Add(LLogItem); + finally + _Unlock(ALogType); + end; +end; + +procedure AppendLog(const ALog: string; const ATimeFormat: string; ALogType: TLogType = ltNormal; const CRLF: string = ';'); +begin + Logger.AppendLog(ALog, ATimeFormat, ALogType, CRLF); +end; + +procedure AppendLog(const ALog: string; ALogType: TLogType = ltNormal; const CRLF: string = ';'); +begin + Logger.AppendLog(ALog, ALogType, CRLF); +end; + +procedure AppendLog(const Fmt: string; const Args: array of const; const ATimeFormat: string; ALogType: TLogType = ltNormal; const CRLF: string = ';'); +begin + Logger.AppendLog(Fmt, Args, ATimeFormat, ALogType, CRLF); +end; + +procedure AppendLog(const Fmt: string; const Args: array of const; ALogType: TLogType = ltNormal; const CRLF: string = ';'); +begin + Logger.AppendLog(Fmt, Args, ALogType, CRLF); +end; + +function Logger: ILogger; +begin + Result := TLogger.FLogger; +end; + +end. + diff --git a/ThirdParty/DCS/Utils/Utils.RegEx.pas b/ThirdParty/DCS/Utils/Utils.RegEx.pas new file mode 100644 index 00000000..918a2301 --- /dev/null +++ b/ThirdParty/DCS/Utils/Utils.RegEx.pas @@ -0,0 +1,101 @@ +{******************************************************************************} +{ } +{ Delphi cross platform socket library } +{ } +{ Copyright (c) 2017 WiNDDRiVER(soulawing@gmail.com) } +{ } +{ Homepage: https://github.com/winddriver/Delphi-Cross-Socket } +{ } +{******************************************************************************} +unit Utils.RegEx; + +interface + +uses + System.RegularExpressions, System.RegularExpressionsCore; + +type + TMatchEvaluatorProc = reference to function(const AMatch: TMatch): string; + + IScopeEvaluator = interface + function GetMatchEvaluator: TMatchEvaluator; + + property MatchEvaluator: TMatchEvaluator read GetMatchEvaluator; + end; + + TScopeEvaluator = class(TInterfacedObject, IScopeEvaluator) + private + FMatchEvaluatorProc: TMatchEvaluatorProc; + + function MatchEvaluator(const AMatch: TMatch): string; + function GetMatchEvaluator: TMatchEvaluator; + public + constructor Create(AMatchEvaluatorProc: TMatchEvaluatorProc); + end; + + TRegExHelper = record helper for TRegEx + function Replace(const Input: string; Evaluator: TMatchEvaluatorProc): string; overload; + function Replace(const Input: string; Evaluator: TMatchEvaluatorProc; Count: Integer): string; overload; + class function Replace(const Input, Pattern: string; Evaluator: TMatchEvaluatorProc): string; overload; static; + class function Replace(const Input, Pattern: string; Evaluator: TMatchEvaluatorProc; Options: TRegExOptions): string; overload; static; + end; + +implementation + +{ TScopeEvaluator } + +constructor TScopeEvaluator.Create(AMatchEvaluatorProc: TMatchEvaluatorProc); +begin + FMatchEvaluatorProc := AMatchEvaluatorProc; +end; + +function TScopeEvaluator.GetMatchEvaluator: TMatchEvaluator; +begin + Result := Self.MatchEvaluator; +end; + +function TScopeEvaluator.MatchEvaluator(const AMatch: TMatch): string; +begin + if Assigned(FMatchEvaluatorProc) then + Result := FMatchEvaluatorProc(AMatch); +end; + +{ TRegExHelper } + +function TRegExHelper.Replace(const Input: string; + Evaluator: TMatchEvaluatorProc; Count: Integer): string; +var + LScopeEvaluator: IScopeEvaluator; +begin + LScopeEvaluator := TScopeEvaluator.Create(Evaluator); + Result := Self.Replace(Input, LScopeEvaluator.MatchEvaluator, Count); +end; + +function TRegExHelper.Replace(const Input: string; + Evaluator: TMatchEvaluatorProc): string; +var + LScopeEvaluator: IScopeEvaluator; +begin + LScopeEvaluator := TScopeEvaluator.Create(Evaluator); + Result := Self.Replace(Input, LScopeEvaluator.MatchEvaluator); +end; + +class function TRegExHelper.Replace(const Input, Pattern: string; + Evaluator: TMatchEvaluatorProc; Options: TRegExOptions): string; +var + LScopeEvaluator: IScopeEvaluator; +begin + LScopeEvaluator := TScopeEvaluator.Create(Evaluator); + Result := Replace(Input, Pattern, LScopeEvaluator.MatchEvaluator, Options); +end; + +class function TRegExHelper.Replace(const Input, Pattern: string; + Evaluator: TMatchEvaluatorProc): string; +var + LScopeEvaluator: IScopeEvaluator; +begin + LScopeEvaluator := TScopeEvaluator.Create(Evaluator); + Result := Replace(Input, Pattern, LScopeEvaluator.MatchEvaluator); +end; + +end. diff --git a/ThirdParty/DCS/Utils/Utils.Utils.pas b/ThirdParty/DCS/Utils/Utils.Utils.pas new file mode 100644 index 00000000..1cd395ba --- /dev/null +++ b/ThirdParty/DCS/Utils/Utils.Utils.pas @@ -0,0 +1,492 @@ +{******************************************************************************} +{ } +{ Delphi cross platform socket library } +{ } +{ Copyright (c) 2017 WiNDDRiVER(soulawing@gmail.com) } +{ } +{ Homepage: https://github.com/winddriver/Delphi-Cross-Socket } +{ } +{******************************************************************************} +unit Utils.Utils; + +interface + +uses + System.SysUtils, + System.Classes, + System.Types, + System.IOUtils, + System.Math, + System.Diagnostics, + System.TimeSpan, + System.Character, + System.SysConst; + +type + TUtils = class + private class var + FAppFile, FAppPath, FAppHome, FAppDocuments, FAppName: string; + private + class constructor Create; + public + class function CalcTickDiff(AStartTick, AEndTick: Cardinal): Cardinal; + class function TestTime(AProc: TProc): TTimeSpan; + class function StrToDateTime(const S, Fmt: string): TDateTime; overload; + class function StrToDateTime(const S: string): TDateTime; overload; + class function DateTimeToStr(const D: TDateTime; const Fmt: string): string; overload; + class function DateTimeToStr(const D: TDateTime): string; overload; + class function ThreadFormat(const Fmt: string; const Args: array of const): string; + class function BytesToStr(const BytesCount: Int64): string; static; + class function CompareVersion(const V1, V2: string): Integer; static; + class procedure DelayCall(ATick: Cardinal; AProc: TProc); static; + class function GetGUID: string; static; + class function RandomStr(const ABaseChars: string; ASize: Integer): string; static; + class function EditDistance(const ASourceStr, ATargetStr: string): Integer; static; + class function SimilarText(const AStr1, AStr2: string): Single; static; + + class function IsSpaceChar(const C: Char): Boolean; static; + class function UnicodeTrim(const S: string): string; static; + class function UnicodeTrimLeft(const S: string): string; static; + class function UnicodeTrimRight(const S: string): string; static; + class function StrIPos(const ASubStr, AStr: string; AOffset: Integer): Integer; static; + + class procedure BinToHex(ABuffer: Pointer; ABufSize: Integer; AText: PChar); overload; static; + class function BinToHex(ABuffer: Pointer; ABufSize: Integer): string; overload; static; inline; + class function BytesToHex(const ABytes: TBytes; AOffset, ACount: Integer): string; overload; static; inline; + class function BytesToHex(const ABytes: TBytes): string; overload; static; inline; + + class function GetFullFileName(const AFileName: string): string; static; + class function GetFileSize(const AFileName: string): Int64; static; + + // 判断两段日期是否有交集 + class function IsCrossDate(const AStartDate1, AEndDate1, AStartDate2, AEndDate2: TDateTime): Boolean; + + class property AppFile: string read FAppFile; + class property AppPath: string read FAppPath; + class property AppHome: string read FAppHome; + class property AppDocuments: string read FAppDocuments; // ios, android 可写 + class property AppName: string read FAppName; + end; + + TEncodingHelper = class helper for TEncoding + /// + /// 从内存块中直接解码出字符串, 省去先将内存块转换为TBytes, 从而提高效率 + /// + function GetString(ABytes: PByte; AByteCount: Integer): string; overload; + end; + +implementation + +{ TUtils } + +class constructor TUtils.Create; +begin + FAppFile := ParamStr(0); + FAppName := ChangeFileExt(ExtractFileName(FAppFile), ''); + FAppPath := IncludeTrailingPathDelimiter(ExtractFilePath(FAppFile)); + + {$IF defined(IOS) or defined(ANDROID)} + FAppHome := IncludeTrailingPathDelimiter(TPath.GetHomePath); + {$ELSE} + FAppHome := IncludeTrailingPathDelimiter(TPath.Combine(TPath.GetHomePath, FAppName)); + {$ENDIF} + + {$IF defined(IOS) or defined(ANDROID)} + FAppDocuments := IncludeTrailingPathDelimiter(TPath.GetDocumentsPath); + {$ELSE} + FAppDocuments := IncludeTrailingPathDelimiter(TPath.Combine(TPath.GetDocumentsPath, FAppName)); + {$ENDIF} +end; + +class function TUtils.DateTimeToStr(const D: TDateTime; + const Fmt: string): string; +begin + Result := FormatDateTime(Fmt, D, TFormatSettings.Create); +end; + +class function TUtils.DateTimeToStr(const D: TDateTime): string; +begin + Result := DateTimeToStr(D, 'yyyy-mm-dd hh:nn:ss'); +end; + +class procedure TUtils.DelayCall(ATick: Cardinal; AProc: TProc); +begin + TThread.CreateAnonymousThread( + procedure + begin + Sleep(ATick); + AProc(); + end).Start; +end; + +class function TUtils.GetFileSize(const AFileName: string): Int64; +var + LFileStream: TStream; +begin + LFileStream := TFile.Open(AFileName, TFileMode.fmOpen, TFileAccess.faRead, TFileShare.fsReadWrite); + try + Result := LFileStream.Size; + finally + FreeAndNil(LFileStream); + end; +end; + +class function TUtils.GetFullFileName(const AFileName: string): string; +begin + if + {$IFDEF MSWINDOWS} + // Windows 下不以驱动器号开头的文件名都视为相对路径 + not TPath.DriveExists(AFileName) + {$ELSE} + // Posix 下直接调用相对路径的现成函数判断 + TPath.IsRelativePath(AFileName) + {$ENDIF} + then + // 相对路径的文件名用程序所在路径补全 + Result := TPath.Combine(TUtils.AppPath, AFileName) + else + Result := AFileName; +end; + +class function TUtils.GetGUID: string; +var + LGuid: TGUID; +begin + CreateGUID(LGuid); + Result := Format('%.8x%.4x%.4x%.2x%.2x%.2x%.2x%.2x%.2x%.2x%.2x', + [LGuid.D1, LGuid.D2, LGuid.D3, LGuid.D4[0], LGuid.D4[1], LGuid.D4[2], LGuid.D4[3], + LGuid.D4[4], LGuid.D4[5], LGuid.D4[6], LGuid.D4[7]]); + +// SetLength(Result, 32); +// StrLFmt(PChar(Result), 32, '%.8x%.4x%.4x%.2x%.2x%.2x%.2x%.2x%.2x%.2x%.2x', +// [LGuid.D1, LGuid.D2, LGuid.D3, LGuid.D4[0], LGuid.D4[1], LGuid.D4[2], LGuid.D4[3], +// LGuid.D4[4], LGuid.D4[5], LGuid.D4[6], LGuid.D4[7]]); +end; + +class function TUtils.IsCrossDate(const AStartDate1, AEndDate1, AStartDate2, + AEndDate2: TDateTime): Boolean; +begin + Result := (AEndDate1 >= AStartDate2) and (AStartDate1 <= AEndDate2); +end; + +class function TUtils.IsSpaceChar(const C: Char): Boolean; +begin + Result := (C.GetUnicodeCategory in [ + TUnicodeCategory.ucControl, + TUnicodeCategory.ucUnassigned, + TUnicodeCategory.ucSpaceSeparator + ]); +end; + +class function TUtils.RandomStr(const ABaseChars: string; + ASize: Integer): string; +var + LBaseLow, LBaseHigh, I: Integer; +begin + Randomize; + LBaseLow := Low(ABaseChars); + LBaseHigh := High(ABaseChars); + SetLength(Result, ASize); + for I := Low(Result) to High(Result) do + Result[I] := ABaseChars[RandomRange(LBaseLow, LBaseHigh + 1)]; +end; + +class function TUtils.CalcTickDiff(AStartTick, AEndTick: Cardinal): Cardinal; +begin + if (AEndTick >= AStartTick) then + Result := AEndTick - AStartTick + else + Result := High(Cardinal) - AStartTick + AEndTick; +end; + +class function TUtils.CompareVersion(const V1, V2: string): Integer; +var + LArr1, LArr2: TArray; + I, I1, I2, LSize1, LSize2: Integer; +begin + LArr1 := V1.Split(['.']); + LArr2 := V2.Split(['.']); + LSize1 := Length(LArr1); + LSize2 := Length(LArr2); + + I := 0; + while (I < LSize1) and (I < LSize2) do + begin + I1 := StrToIntDef(LArr1[I], 0); + I2 := StrToIntDef(LArr2[I], 0); + if (I1 > I2) then + Exit(1) + else if (I1 < I2) then + Exit(-1); + + Inc(I); + end; + + Result := (LSize1 - LSize2); +end; + +class function TUtils.SimilarText(const AStr1, AStr2: string): Single; +begin + Result := 1 - (EditDistance(AStr1, AStr2) / Max(AStr1.Length, AStr2.Length)); +end; + +class function TUtils.StrIPos(const ASubStr, AStr: string; + AOffset: Integer): Integer; +var + I, LIterCnt, L, J: Integer; + PSubStr, PS: PChar; + LCh: Char; +begin + PSubStr := Pointer(ASubStr); + PS := Pointer(AStr); + if (PSubStr = nil) or (PS = nil) or (AOffset < 1) then + Exit(0); + L := Length(ASubStr); + { Calculate the number of possible iterations. } + LIterCnt := Length(AStr) - AOffset - L + 2; + if (L > 0) and (LIterCnt > 0) then + begin + Inc(PS, AOffset - 1); + I := 0; + LCh := UpCase(PSubStr[0]); + if L = 1 then // Special case when Substring length is 1 + repeat + if UpCase(PS[I]) = LCh then + Exit(I + AOffset); + Inc(I); + until I = LIterCnt + else + repeat + if UpCase(PS[I]) = LCh then + begin + J := 1; + repeat + if UpCase(PS[I + J]) = UpCase(PSubStr[J]) then + begin + Inc(J); + if J = L then + Exit(I + AOffset); + end + else + Break; + until False; + end; + Inc(I); + until I = LIterCnt; + end; + + Result := 0; +end; + +class function TUtils.StrToDateTime(const S: string): TDateTime; +begin + Result := StrToDateTime(S, 'yyyy-mm-dd hh:nn:ss'); +end; + +class function TUtils.StrToDateTime(const S, Fmt: string): TDateTime; +// Fmt格式字符串:空格前是日期格式,空格后是时间格式 +// 必须是这样:YYYY-MM-DD HH:NN:SS或者MM-DD-YYYY HH:NN:SS +// 不能用空格做时间单位中间的间隔符 + function GetSeparator(const S: string): Char; + begin + for Result in S do + if not CharInSet(Result, ['a'..'z', 'A'..'Z']) then Exit; + Result := #0; + end; +var + Fms: TFormatSettings; + DateFmt, TimeFmt: string; + p: Integer; +begin + p := Fmt.IndexOf(' '); + DateFmt := Fmt.Substring(0, p); + TimeFmt := Fmt.Substring(p + 1); + {$if COMPILERVERSION >= 20} + Fms := TFormatSettings.Create; + {$else} + GetLocaleFormatSettings(GetThreadLocale, Fms); + {$ifend} + Fms.DateSeparator := GetSeparator(DateFmt); + Fms.TimeSeparator := GetSeparator(TimeFmt); + Fms.ShortDateFormat := DateFmt; + Fms.LongDateFormat := DateFmt; + Fms.ShortTimeFormat := TimeFmt; + Fms.LongTimeFormat := TimeFmt; + Result := System.SysUtils.StrToDateTime(S, Fms); +end; + +class procedure TUtils.BinToHex(ABuffer: Pointer; ABufSize: Integer; + AText: PChar); +const + XD: array[0..15] of char = ('0', '1', '2', '3', '4', '5', '6', '7', + '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'); +var + I: Integer; + PBuffer: PByte; + PText: PChar; +begin + PBuffer := ABuffer; + PText := AText; + for I := 0 to ABufSize - 1 do + begin + PText[0] := XD[(PBuffer[I] shr 4) and $0f]; + PText[1] := XD[PBuffer[I] and $0f]; + Inc(PText, 2); + end; +end; + +class function TUtils.BinToHex(ABuffer: Pointer; ABufSize: Integer): string; +begin + SetLength(Result, ABufSize * 2); + BinToHex(ABuffer, ABufSize, PChar(Result)); +end; + +class function TUtils.BytesToHex(const ABytes: TBytes; AOffset, + ACount: Integer): string; +begin + Result := BinToHex(@ABytes[AOffset], ACount); +end; + +class function TUtils.BytesToHex(const ABytes: TBytes): string; +begin + Result := BytesToHex(ABytes, 0, Length(ABytes)); +end; + +class function TUtils.BytesToStr(const BytesCount: Int64): string; +const + KBYTES = Int64(1024); + MBYTES = KBYTES * 1024; + GBYTES = MBYTES * 1024; + TBYTES = GBYTES * 1024; +begin + if (BytesCount = 0) then + Result := '' + else if (BytesCount < KBYTES) then + Result := Format('%dB', [BytesCount]) + else if (BytesCount < MBYTES) then + Result := FormatFloat('0.##KB', BytesCount / KBYTES) + else if (BytesCount < GBYTES) then + Result := FormatFloat('0.##MB', BytesCount / MBYTES) + else if (BytesCount < TBYTES) then + Result := FormatFloat('0.##GB', BytesCount / GBYTES) + else + Result := FormatFloat('0.##TB', BytesCount / TBYTES); +end; + +class function TUtils.TestTime(AProc: TProc): TTimeSpan; +var + LWatch: TStopwatch; +begin + LWatch := TStopwatch.StartNew; + AProc(); + LWatch.Stop; + Result := LWatch.Elapsed; +end; + +class function TUtils.ThreadFormat(const Fmt: string; + const Args: array of const): string; +begin + Result := Format(Fmt, Args, TFormatSettings.Create); +end; + +class function TUtils.UnicodeTrim(const S: string): string; +var + I, L: Integer; +begin + L := S.Length - 1; + I := 0; + if (L > -1) and not IsSpaceChar(S.Chars[I]) and not IsSpaceChar(S.Chars[L]) then Exit(S); + + while (I <= L) and IsSpaceChar(S.Chars[I]) do + Inc(I); + + if (I > L) then Exit(''); + + while IsSpaceChar(S.Chars[L]) do + Dec(L); + + Result := S.SubString(I, L - I + 1); +end; + +class function TUtils.UnicodeTrimLeft(const S: string): string; +var + I, L: Integer; +begin + L := S.Length - 1; + I := 0; + while (I <= L) and IsSpaceChar(S.Chars[I]) do + Inc(I); + if (I > 0) then + Result := S.SubString(I) + else + Result := S; +end; + +class function TUtils.UnicodeTrimRight(const S: string): string; +var + I: Integer; +begin + I := S.Length - 1; + if (I >= 0) and not IsSpaceChar(S.Chars[I]) then + Result := S + else + begin + while (I >= 0) and IsSpaceChar(S.Chars[I]) do + Dec(I); + Result := S.SubString(0, I + 1); + end; +end; + +class function TUtils.EditDistance(const ASourceStr, ATargetStr: string): Integer; +var + i, j, edIns, edDel, edRep: Integer; + d: TArray>; +begin + SetLength(d, Length(ASourceStr) + 1, Length(ATargetStr) + 1); + + for i := 0 to ASourceStr.Length do + d[i][0] := i; + + for j := 0 to ATargetStr.Length do + d[0][j] := j; + + for i := 1 to ASourceStr.Length do + begin + for j := 1 to ATargetStr.Length do + begin + if((ASourceStr[i - 1] = ATargetStr[j - 1])) then + begin + d[i][j] := d[i - 1][j - 1]; //不需要编辑操作 + end else + begin + edIns := d[i][j - 1] + 1; //ASourceStr 插入字符 + edDel := d[i - 1][j] + 1; //ASourceStr 删除字符 + edRep := d[i - 1][j - 1] + 1; //ASourceStr 替换字符 + + d[i][j] := Min(Min(edIns, edDel), edRep); + end; + end; + end; + + Result := d[ASourceStr.length][ATargetStr.length]; +end; + +{ TEncodingHelper } + +function TEncodingHelper.GetString(ABytes: PByte; AByteCount: Integer): string; +var + LSize: Integer; +begin + if (ABytes = nil) then + raise EEncodingError.CreateRes(@SInvalidSourceArray); + if (AByteCount < 0) then + raise EEncodingError.CreateResFmt(@SInvalidCharCount, [AByteCount]); + + LSize := GetCharCount(ABytes, AByteCount); + if (AByteCount > 0) and (LSize = 0) then + raise EEncodingError.CreateRes(@SNoMappingForUnicodeCharacter); + SetLength(Result, LSize); + GetChars(ABytes, AByteCount, PChar(Result), LSize); +end; + +end. diff --git a/ThirdParty/delphi-jose-jwt/Source/JOSE.Cryptography.RSA.pas b/ThirdParty/delphi-jose-jwt/Source/JOSE.Builder.pas similarity index 81% rename from ThirdParty/delphi-jose-jwt/Source/JOSE.Cryptography.RSA.pas rename to ThirdParty/delphi-jose-jwt/Source/JOSE.Builder.pas index 9544455d..d026d6ba 100644 --- a/ThirdParty/delphi-jose-jwt/Source/JOSE.Cryptography.RSA.pas +++ b/ThirdParty/delphi-jose-jwt/Source/JOSE.Builder.pas @@ -1,7 +1,7 @@ {******************************************************************************} { } { Delphi JOSE Library } -{ Copyright (c) 2015 Paolo Rossi } +{ Copyright (c) 2015-2017 Paolo Rossi } { https://github.com/paolo-rossi/delphi-jose-jwt } { } {******************************************************************************} @@ -20,10 +20,28 @@ { } {******************************************************************************} -unit JOSE.Cryptography.RSA; +/// +/// Utility class to encode and decode a JWT +/// +unit JOSE.Builder; interface +uses + System.SysUtils, + JOSE.Types.Bytes, + JOSE.Core.Base, + JOSE.Core.Parts, + JOSE.Core.JWA, + JOSE.Core.JWK, + JOSE.Core.JWT, + JOSE.Core.JWS, + JOSE.Core.JWE; + implementation +uses + System.Types, + System.StrUtils; + end. diff --git a/ThirdParty/delphi-jose-jwt/Source/JOSE.Consumer.Validators.pas b/ThirdParty/delphi-jose-jwt/Source/JOSE.Consumer.Validators.pas new file mode 100644 index 00000000..79c8dda2 --- /dev/null +++ b/ThirdParty/delphi-jose-jwt/Source/JOSE.Consumer.Validators.pas @@ -0,0 +1,347 @@ +{******************************************************************************} +{ } +{ Delphi JOSE Library } +{ Copyright (c) 2015-2017 Paolo Rossi } +{ https://github.com/paolo-rossi/delphi-jose-jwt } +{ } +{******************************************************************************} +{ } +{ Licensed under the Apache License, Version 2.0 (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.apache.org/licenses/LICENSE-2.0 } +{ } +{ Unless required by applicable law or agreed to in writing, software } +{ distributed under the License is distributed on an "AS IS" BASIS, } +{ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. } +{ See the License for the specific language governing permissions and } +{ limitations under the License. } +{ } +{******************************************************************************} + +/// +/// Utility class to encode and decode a JWT +/// +unit JOSE.Consumer.Validators; + +interface + +uses + System.SysUtils, System.Generics.Collections, + JOSE.Types.Bytes, + JOSE.Core.Base, + JOSE.Core.Parts, + JOSE.Core.JWT, + JOSE.Context; + +type + IJOSEValidator = interface + function Validate(AJOSEContext: TJOSEContext): string; + end; + + TJOSEDateClaimsParams = record + private + function GetEvaluationTime: TJOSENumericDate; + public + RequireExp: Boolean; + RequireIat: Boolean; + RequireNbf: Boolean; + StaticEvaluationTime: TDateTime; + AllowedClockSkewSeconds: Integer; + MaxFutureValidityInMinutes: Integer; + + class function New: TJOSEDateClaimsParams; static; + function SkewMessage: string; + property EvaluationTime: TJOSENumericDate read GetEvaluationTime; + end; + + TJOSEValidator = reference to function (AJOSEContext: TJOSEContext): string; + + TJOSEValidatorArray = TArray; + TJOSEValidatorArrayHelper = record helper for TJOSEValidatorArray + public + function Add(AValue: TJOSEValidator): Integer; + end; + + TJOSEClaimsValidators = class + class function DateClaimsValidator( + ADateParams: TJOSEDateClaimsParams): TJOSEValidator; + + class function audValidator(AAudience: TJOSEStringArray; + ARequired: Boolean = True): TJOSEValidator; + + class function issValidator(AIssuer: string; + ARequired: Boolean = True): TJOSEValidator; overload; + + class function issValidator(AIssuers: TJOSEStringArray; + ARequired: Boolean = True): TJOSEValidator; overload; + + class function subValidator(const ASubject: string; + ARequired: Boolean): TJOSEValidator; overload; + + class function subValidator( + ARequired: Boolean): TJOSEValidator; overload; + + class function jtiValidator(const AJwtId: string; + ARequired: Boolean): TJOSEValidator; overload; + + class function jtiValidator( + ARequired: Boolean): TJOSEValidator; overload; + end; + + + +implementation + +uses + System.DateUtils, + System.Types, + System.StrUtils; + +{ TJOSEDateClaimsParams } + +function TJOSEDateClaimsParams.GetEvaluationTime: TJOSENumericDate; +begin + if StaticEvaluationTime = 0 then + StaticEvaluationTime := Now; + + Result := TJOSENumericDate.Create(StaticEvaluationTime); +end; + +class function TJOSEDateClaimsParams.New: TJOSEDateClaimsParams; +begin + Result.RequireExp := False; + Result.RequireIat := False; + Result.RequireNbf := False; + Result.StaticEvaluationTime := 0; + Result.AllowedClockSkewSeconds := 0; + Result.MaxFutureValidityInMinutes := 0; +end; + +function TJOSEDateClaimsParams.SkewMessage: string; +begin + if AllowedClockSkewSeconds > 0 then + Result := Format( + '(even when providing [%d] seconds of leeway to account for clock skew)', + [AllowedClockSkewSeconds] + ) + else + Result := '.'; +end; + +{ TJOSEClaimsValidators } + +class function TJOSEClaimsValidators.audValidator(AAudience: TJOSEStringArray; + ARequired: Boolean = True): TJOSEValidator; +begin + Result := + function (AJOSEContext: TJOSEContext): string + var + LOk: Boolean; + LSingleAudience: string; + LClaims: TJWTClaims; + begin + Result := ''; + LClaims := AJOSEContext.GetClaims; + + if not LClaims.HasAudience then + if ARequired then + Exit('No Audience [aud] claim present') + else + Exit(''); + + LOk := False; + for LSingleAudience in LClaims.AudienceArray do + if AAudience.Contains(LSingleAudience) then + LOk := True; + + if not LOk then + begin + Result := 'Audience [aud] claim '; + + if AAudience.IsEmpty then + Result := Result + ' present in the JWT but no expected audience value(s) were provided to the JWT Consumer.' + else + Result := Result + ' doesn''t contain an acceptable identifier.'; + + Result := Result + ' Expected '; + + if Length(LClaims.AudienceArray) = 1 then + Result := Result + '[' + LClaims.Audience + ']' + else + Result := Result + 'one of [' + LClaims.Audience + ']'; + + Result := Result + ' as aud value.'; + end; + end +end; + +class function TJOSEClaimsValidators.DateClaimsValidator( + ADateParams: TJOSEDateClaimsParams): TJOSEValidator; +var + LDeltaInSeconds: Int64; +begin + Result := + function (AJOSEContext: TJOSEContext): string + var + LClaims: TJWTClaims; + LExpiration, LIssuedAt, LNotBefore: TJOSENumericDate; + begin + Result := ''; + LClaims := AJOSEContext.GetClaims; + + LExpiration := TJOSENumericDate.Create(LClaims.Expiration); + LIssuedAt := TJOSENumericDate.Create(LClaims.IssuedAt); + LNotBefore := TJOSENumericDate.Create(LClaims.NotBefore); + + if ADateParams.RequireExp and not LClaims.HasExpiration then + Exit('No Expiration Time [exp] claim present'); + + if ADateParams.RequireIat and not LClaims.HasIssuedAt then + Exit('No IssuedAt [iat] claim present'); + + if ADateParams.RequireNbf and not LClaims.HasNotBefore then + Exit('No NotBefore [nbf] claim present'); + + if LClaims.HasExpiration then + begin + if ADateParams.EvaluationTime.IsAfter(LExpiration, ADateParams.AllowedClockSkewSeconds) then + Exit(Format( + 'The JWT is no longer valid - the evaluation time [%d] is on or after the Expiration Time [exp=%d] claim value %s', + [ADateParams.EvaluationTime.AsSeconds, JSONDate(LClaims.Expiration), ADateParams.SkewMessage]) + ); + + if LClaims.HasIssuedAt and LExpiration.IsBefore(LIssuedAt, ADateParams.AllowedClockSkewSeconds) then + Exit(Format('The Expiration Time (exp=%d) claim value cannot be before the IssuedAt (iat=%d) claim value', + [LExpiration.AsSeconds, LIssuedAt.AsSeconds]) + ); + + if LClaims.HasNotBefore and LExpiration.IsBefore(LNotBefore, ADateParams.AllowedClockSkewSeconds) then + Exit(Format('The Expiration Time (exp=%d) claim value cannot be before the NotBefore (nbf=%d) claim value', + [LExpiration.AsSeconds, LNotBefore.AsSeconds]) + ); + + if ADateParams.MaxFutureValidityInMinutes > 0 then + begin + LDeltaInSeconds := + LExpiration.AsSeconds - + ADateParams.AllowedClockSkewSeconds - + ADateParams.EvaluationTime.AsSeconds; + + if LDeltaInSeconds > (ADateParams.MaxFutureValidityInMinutes * 60) then + Exit(Format('The Expiration Time [exp=%d] claim value cannot be more than [%d] minutes in the future relative to the evaluation time %[d] %s', + [LExpiration.AsSeconds, ADateParams.MaxFutureValidityInMinutes, ADateParams.EvaluationTime.AsSeconds, ADateParams.SkewMessage]) + ); + end; + end; + + if LClaims.HasNotBefore then + if (ADateParams.EvaluationTime.AsSeconds + ADateParams.AllowedClockSkewSeconds) < LNotBefore.AsSeconds then + Exit(Format('The JWT is not yet valid as the evaluation time [%d] is before the NotBefore [nbf=%d] claim time %s', + [ADateParams.EvaluationTime.AsSeconds, LNotBefore.AsSeconds, ADateParams.SkewMessage]) + ); + end; + ; +end; + +class function TJOSEClaimsValidators.issValidator(AIssuer: string; + ARequired: Boolean): TJOSEValidator; +var + LIssuers: TJOSEStringArray; +begin + LIssuers := TJOSEStringArray.Create; + if not AIssuer.IsEmpty then + begin + LIssuers.Push(AIssuer); + end; + Result := issValidator(LIssuers, ARequired); +end; + +class function TJOSEClaimsValidators.issValidator(AIssuers: TJOSEStringArray; + ARequired: Boolean): TJOSEValidator; +begin + Result := + function (AJOSEContext: TJOSEContext): string + var + LIssuer: string; + begin + Result := ''; + LIssuer := AJOSEContext.GetClaims.Issuer; + + if not AJOSEContext.GetClaims.HasIssuer and ARequired then + Exit(Format('No Issuer [iss] claim present but was expecting %s', + [AIssuers.ToStringPluralForm('one of ')])); + + if (AIssuers.Size > 0) and not AIssuers.Contains(LIssuer) then + Exit(Format('Issuer [iss] claim value [%s] doesn''t match expected value of [%s]', + [LIssuer, AIssuers.ToStringPluralForm('one of ')])); + end + ; +end; + +class function TJOSEClaimsValidators.jtiValidator(const AJwtId: string; + ARequired: Boolean): TJOSEValidator; +begin + Result := + function (AJOSEContext: TJOSEContext): string + var + LJWTId: string; + begin + Result := ''; + LJWTId := AJOSEContext.GetClaims.JWTId; + + if not AJOSEContext.GetClaims.HasJWTId and ARequired then + Exit('No JWT ID [jti] claim present.') + else + if not AJwtId.IsEmpty and not AJwtId.Equals(LJwtId) then + Exit(Format( + 'JWT Id [jti] claim value [%s] doesn''t match expected value of [%s]', + [LJwtId, AJwtId])); + end + ; +end; + +class function TJOSEClaimsValidators.jtiValidator(ARequired: Boolean): TJOSEValidator; +begin + Result := TJOSEClaimsValidators.jtiValidator('', ARequired); +end; + +class function TJOSEClaimsValidators.subValidator(const ASubject: string; + ARequired: Boolean): TJOSEValidator; +begin + Result := + function (AJOSEContext: TJOSEContext): string + var + LSubject: string; + begin + Result := ''; + LSubject := AJOSEContext.GetClaims.Subject; + + if not AJOSEContext.GetClaims.HasSubject and ARequired then + Exit('No Subject [sub] claim present') + else + if not ASubject.IsEmpty and not ASubject.Equals(LSubject) then + Exit(Format( + 'Subject [sub] claim value [%s] doesn''t match expected value of [%s]', + [LSubject, ASubject])); + end + ; +end; + +class function TJOSEClaimsValidators.subValidator(ARequired: Boolean): TJOSEValidator; +begin + Result := TJOSEClaimsValidators.subValidator('', ARequired); +end; + +{ TJOSEValidatorArrayHelper } + +function TJOSEValidatorArrayHelper.Add(AValue: TJOSEValidator): Integer; +begin + Result := Length(Self); + + SetLength(Self, Result + 1); + Self[Result] := AValue; +end; + +end. diff --git a/ThirdParty/delphi-jose-jwt/Source/JOSE.Consumer.pas b/ThirdParty/delphi-jose-jwt/Source/JOSE.Consumer.pas new file mode 100644 index 00000000..b28c37c3 --- /dev/null +++ b/ThirdParty/delphi-jose-jwt/Source/JOSE.Consumer.pas @@ -0,0 +1,554 @@ +{******************************************************************************} +{ } +{ Delphi JOSE Library } +{ Copyright (c) 2015-2017 Paolo Rossi } +{ https://github.com/paolo-rossi/delphi-jose-jwt } +{ } +{******************************************************************************} +{ } +{ Licensed under the Apache License, Version 2.0 (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.apache.org/licenses/LICENSE-2.0 } +{ } +{ Unless required by applicable law or agreed to in writing, software } +{ distributed under the License is distributed on an "AS IS" BASIS, } +{ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. } +{ See the License for the specific language governing permissions and } +{ limitations under the License. } +{ } +{******************************************************************************} + +/// +/// Utility class to encode and decode a JWT +/// +unit JOSE.Consumer; + +interface + +uses + System.SysUtils, System.Generics.Collections, System.Classes, + JOSE.Types.Bytes, + JOSE.Core.Base, + JOSE.Core.Parts, + JOSE.Core.JWA, + JOSE.Core.JWK, + JOSE.Core.JWT, + JOSE.Core.JWS, + JOSE.Core.JWE, + JOSE.Context, + JOSE.Consumer.Validators; + +type + EInvalidJWTException = class(Exception) + public + procedure SetDetails(ADetails: TStrings); + end; + + IJOSEConsumer = interface + ['{A270E909-6D79-4FC1-B4F6-B9911EAB8D36}'] + procedure Process(const ACompactToken: TJOSEBytes); + procedure ProcessContext(AContext: TJOSEContext); + procedure Validate(AContext: TJOSEContext); + end; + + TJOSEConsumer = class(TInterfacedObject, IJOSEConsumer) + private + FKey: TJOSEBytes; + FValidators: TJOSEValidatorArray; + FClaimsClass: TJWTClaimsClass; + + FRequireSignature: Boolean; + FRequireEncryption: Boolean; + FSkipSignatureVerification: Boolean; + FSkipVerificationKeyValidation: Boolean; + private + function SetKey(const AKey: TJOSEBytes): TJOSEConsumer; + function SetValidators(const AValidators: TJOSEValidatorArray): TJOSEConsumer; + function SetRequireSignature(ARequireSignature: Boolean): TJOSEConsumer; + function SetRequireEncryption(ARequireEncryption: Boolean): TJOSEConsumer; + function SetSkipSignatureVerification(ASkipSignatureVerification: Boolean): TJOSEConsumer; + function SetSkipVerificationKeyValidation(ASkipVerificationKeyValidation: Boolean): TJOSEConsumer; + public + constructor Create(AClaimsClass: TJWTClaimsClass); + + procedure Process(const ACompactToken: TJOSEBytes); + procedure ProcessContext(AContext: TJOSEContext); + procedure Validate(AContext: TJOSEContext); + end; + + + IJOSEConsumerBuilder = interface + ['{8EC9CB4B-3233-493B-9366-F06CFFA81D99}'] + // Custom Claim Class + function SetClaimsClass(AClaimsClass: TJWTClaimsClass): IJOSEConsumerBuilder; + // Key, Signature & Encryption verification + function SetEnableRequireEncryption: IJOSEConsumerBuilder; + function SetDisableRequireSignature: IJOSEConsumerBuilder; + function SetSkipSignatureVerification: IJOSEConsumerBuilder; + function SetSkipVerificationKeyValidation: IJOSEConsumerBuilder; + function SetVerificationKey(const AKey: TJOSEBytes): IJOSEConsumerBuilder; + function SetDecryptionKey(const AKey: TJOSEBytes): IJOSEConsumerBuilder; + // String-based claims + function SetSkipDefaultAudienceValidation(): IJOSEConsumerBuilder; + function SetExpectedAudience(ARequireAudience: Boolean; const AExpectedAudience: TArray): IJOSEConsumerBuilder; + function SetExpectedIssuer(ARequireIssuer: Boolean; const AExpectedIssuer: string): IJOSEConsumerBuilder; + function SetExpectedIssuers(ARequireIssuer: Boolean; const AExpectedIssuers: TArray): IJOSEConsumerBuilder; + function SetRequireSubject: IJOSEConsumerBuilder; + function SetExpectedSubject(const ASubject: string): IJOSEConsumerBuilder; + function SetRequireJwtId: IJOSEConsumerBuilder; + function SetExpectedJwtId(const AJwtId: string): IJOSEConsumerBuilder; + // Date-based Claims + function SetRequireExpirationTime: IJOSEConsumerBuilder; + function SetRequireIssuedAt: IJOSEConsumerBuilder; + function SetRequireNotBefore: IJOSEConsumerBuilder; + function SetEvaluationTime(AEvaluationTime: TDateTime): IJOSEConsumerBuilder; + function SetAllowedClockSkew(AClockSkew: Integer; ATimeUnit: TJOSETimeUnit): IJOSEConsumerBuilder; + function SetMaxFutureValidity(AMaxFutureValidity: Integer; ATimeUnit: TJOSETimeUnit): IJOSEConsumerBuilder; + // External validators + function RegisterValidator(AValidator: TJOSEValidator): IJOSEConsumerBuilder; + // Validators skipping + function SetSkipAllValidators: IJOSEConsumerBuilder; + function SetSkipAllDefaultValidators: IJOSEConsumerBuilder; + + function Build: TJOSEConsumer; + end; + + /// + /// Used to create the appropriate TJOSEConsumer for the JWT processing needs + /// + TJOSEConsumerBuilder = class(TInterfacedObject, IJOSEConsumerBuilder) + private + FValidators: TJOSEValidatorArray; + FDateValidatorParams: TJOSEDateClaimsParams; + FDateValidator: TJOSEValidator; + FAudValidator: TJOSEValidator; + FIssValidator: TJOSEValidator; + + FKey: TJOSEBytes; + FClaimsClass: TJWTClaimsClass; + FDisableRequireSignature: Boolean; + FEnableRequireEncryption: Boolean; + FSkipVerificationKeyValidation: Boolean; + FRequireJwtId: Boolean; + FRequireSubject: Boolean; + FSkipAllDefaultValidators: Boolean; + FSkipAllValidators: Boolean; + FSkipDefaultAudienceValidation: Boolean; + FSkipSignatureVerification: Boolean; + FSubject: string; + FJwtId: string; + constructor Create; + procedure BuildValidators(const ADateParams: TJOSEDateClaimsParams); + public + class function NewConsumer: IJOSEConsumerBuilder; + destructor Destroy; override; + + // Custom Claim Class + function SetClaimsClass(AClaimsClass: TJWTClaimsClass): IJOSEConsumerBuilder; + // Key, Signature & Encryption verification + function SetEnableRequireEncryption: IJOSEConsumerBuilder; + function SetDisableRequireSignature: IJOSEConsumerBuilder; + function SetSkipSignatureVerification: IJOSEConsumerBuilder; + function SetSkipVerificationKeyValidation: IJOSEConsumerBuilder; + function SetVerificationKey(const AKey: TJOSEBytes): IJOSEConsumerBuilder; + function SetDecryptionKey(const AKey: TJOSEBytes): IJOSEConsumerBuilder; + // String-based claims + function SetSkipDefaultAudienceValidation(): IJOSEConsumerBuilder; + function SetExpectedAudience(ARequireAudience: Boolean; const AExpectedAudience: TArray): IJOSEConsumerBuilder; + function SetExpectedIssuer(ARequireIssuer: Boolean; const AExpectedIssuer: string): IJOSEConsumerBuilder; + function SetExpectedIssuers(ARequireIssuer: Boolean; const AExpectedIssuers: TArray): IJOSEConsumerBuilder; + function SetRequireSubject: IJOSEConsumerBuilder; + function SetExpectedSubject(const ASubject: string): IJOSEConsumerBuilder; + function SetRequireJwtId: IJOSEConsumerBuilder; + function SetExpectedJwtId(const AJwtId: string): IJOSEConsumerBuilder; + // Date-based Claims + function SetRequireExpirationTime: IJOSEConsumerBuilder; + function SetRequireIssuedAt: IJOSEConsumerBuilder; + function SetRequireNotBefore: IJOSEConsumerBuilder; + function SetEvaluationTime(AEvaluationTime: TDateTime): IJOSEConsumerBuilder; + function SetAllowedClockSkew(AClockSkew: Integer; ATimeUnit: TJOSETimeUnit): IJOSEConsumerBuilder; + function SetMaxFutureValidity(AMaxFutureValidity: Integer; ATimeUnit: TJOSETimeUnit): IJOSEConsumerBuilder; + // External validators + function RegisterValidator(AValidator: TJOSEValidator): IJOSEConsumerBuilder; + // Validators skipping + function SetSkipAllValidators: IJOSEConsumerBuilder; + function SetSkipAllDefaultValidators: IJOSEConsumerBuilder; + + function Build: TJOSEConsumer; + end; + +implementation + +uses + System.Types, + System.StrUtils, + JOSE.Types.JSON; + +function TJOSEConsumerBuilder.Build: TJOSEConsumer; +begin + if not Assigned(FClaimsClass) then + FClaimsClass := TJWTClaims; + + Result := TJOSEConsumer.Create(FClaimsClass) + .SetKey(FKey) + .SetRequireSignature(not FDisableRequireSignature) + .SetRequireEncryption(FEnableRequireEncryption) + .SetSkipSignatureVerification(FSkipSignatureVerification) + .SetSkipVerificationKeyValidation(FSkipVerificationKeyValidation); + + BuildValidators(FDateValidatorParams); + Result.SetValidators(FValidators); +end; + +procedure TJOSEConsumerBuilder.BuildValidators(const ADateParams: TJOSEDateClaimsParams); +begin + if not FSkipAllValidators then + begin + if not FSkipAllDefaultValidators then + begin + if not FSkipDefaultAudienceValidation then + begin + if not Assigned(FAudValidator) then + FAudValidator := TJOSEClaimsValidators.audValidator(TJOSEStringArray.Create, False); + FValidators.add(FAudValidator); + end; + + if not Assigned(FIssValidator) then + FIssValidator := TJOSEClaimsValidators.issValidator('', False); + FValidators.Add(FIssValidator); + + if not Assigned(FDateValidator) then + FDateValidator := TJOSEClaimsValidators.DateClaimsValidator(ADateParams); + FValidators.Add(FDateValidator); + + if FSubject.IsEmpty then + FValidators.Add(TJOSEClaimsValidators.subValidator(FRequireSubject)) + else + FValidators.Add(TJOSEClaimsValidators.subValidator(FSubject, FRequireSubject)); + + if FJwtId.IsEmpty then + FValidators.Add(TJOSEClaimsValidators.jtiValidator(FRequireJwtId)) + else + FValidators.Add(TJOSEClaimsValidators.jtiValidator(FJwtId, FRequireJwtId)); + end; + end; +end; + +constructor TJOSEConsumerBuilder.Create; +begin + inherited; + FDateValidatorParams := TJOSEDateClaimsParams.New; +end; + +destructor TJOSEConsumerBuilder.Destroy; +begin + inherited; +end; + +class function TJOSEConsumerBuilder.NewConsumer: IJOSEConsumerBuilder; +begin + Result := Self.Create; +end; + +function TJOSEConsumerBuilder.RegisterValidator(AValidator: TJOSEValidator): IJOSEConsumerBuilder; +var + LLen: Integer; +begin + LLen := Length(FValidators); + SetLength(FValidators, LLen + 1); + FValidators[LLen] := AValidator; + Result := Self as IJOSEConsumerBuilder; +end; + +function TJOSEConsumerBuilder.SetAllowedClockSkew(AClockSkew: Integer; + ATimeUnit: TJOSETimeUnit): IJOSEConsumerBuilder; +begin + FDateValidatorParams.AllowedClockSkewSeconds := ATimeUnit.ToSeconds(AClockSkew); + Result := Self as IJOSEConsumerBuilder; +end; + +function TJOSEConsumerBuilder.SetClaimsClass(AClaimsClass: TJWTClaimsClass): IJOSEConsumerBuilder; +begin + FClaimsClass := AClaimsClass; + Result := Self as IJOSEConsumerBuilder; +end; + +function TJOSEConsumerBuilder.SetDecryptionKey(const AKey: TJOSEBytes): IJOSEConsumerBuilder; +begin + FKey := AKey; + Result := Self as IJOSEConsumerBuilder; +end; + +function TJOSEConsumerBuilder.SetDisableRequireSignature: IJOSEConsumerBuilder; +begin + FDisableRequireSignature := True; + Result := Self as IJOSEConsumerBuilder; +end; + +function TJOSEConsumerBuilder.SetEnableRequireEncryption: IJOSEConsumerBuilder; +begin + FEnableRequireEncryption := True; + Result := Self as IJOSEConsumerBuilder; +end; + +function TJOSEConsumerBuilder.SetEvaluationTime(AEvaluationTime: TDateTime): IJOSEConsumerBuilder; +begin + FDateValidatorParams.StaticEvaluationTime := AEvaluationTime; + Result := Self as IJOSEConsumerBuilder; +end; + +function TJOSEConsumerBuilder.SetExpectedAudience(ARequireAudience: Boolean; + const AExpectedAudience: TArray): IJOSEConsumerBuilder; +begin + FAudValidator := TJOSEClaimsValidators.audValidator(AExpectedAudience, ARequireAudience); + Result := Self as IJOSEConsumerBuilder; +end; + +function TJOSEConsumerBuilder.SetExpectedIssuer(ARequireIssuer: Boolean; + const AExpectedIssuer: string): IJOSEConsumerBuilder; +begin + FIssValidator := TJOSEClaimsValidators.issValidator(AExpectedIssuer, ARequireIssuer); + Result := Self as IJOSEConsumerBuilder; +end; + +function TJOSEConsumerBuilder.SetExpectedIssuers(ARequireIssuer: Boolean; + const AExpectedIssuers: TArray): IJOSEConsumerBuilder; +begin + FIssValidator := TJOSEClaimsValidators.issValidator(AExpectedIssuers, ARequireIssuer); + Result := Self as IJOSEConsumerBuilder; +end; + +function TJOSEConsumerBuilder.SetExpectedJwtId(const AJwtId: string): IJOSEConsumerBuilder; +begin + FJwtId := AJwtId; + SetRequireJwtId; + Result := Self as IJOSEConsumerBuilder; +end; + +function TJOSEConsumerBuilder.SetExpectedSubject(const ASubject: string): IJOSEConsumerBuilder; +begin + FSubject := ASubject; + SetRequireSubject; + Result := Self as IJOSEConsumerBuilder; +end; + +function TJOSEConsumerBuilder.SetMaxFutureValidity(AMaxFutureValidity: Integer; + ATimeUnit: TJOSETimeUnit): IJOSEConsumerBuilder; +begin + FDateValidatorParams.MaxFutureValidityInMinutes := ATimeUnit.ToMinutes(AMaxFutureValidity); + Result := Self as IJOSEConsumerBuilder; +end; + +function TJOSEConsumerBuilder.SetSkipVerificationKeyValidation: IJOSEConsumerBuilder; +begin + FSkipVerificationKeyValidation := True; + Result := Self as IJOSEConsumerBuilder; +end; + +function TJOSEConsumerBuilder.SetRequireExpirationTime: IJOSEConsumerBuilder; +begin + FDateValidatorParams.RequireExp := True; + Result := Self as IJOSEConsumerBuilder; +end; + +function TJOSEConsumerBuilder.SetRequireIssuedAt: IJOSEConsumerBuilder; +begin + FDateValidatorParams.RequireIat := True; + Result := Self as IJOSEConsumerBuilder; +end; + +function TJOSEConsumerBuilder.SetRequireJwtId: IJOSEConsumerBuilder; +begin + FRequireJwtId := True; + Result := Self as IJOSEConsumerBuilder; +end; + +function TJOSEConsumerBuilder.SetRequireNotBefore: IJOSEConsumerBuilder; +begin + FDateValidatorParams.RequireNbf := True; + Result := Self as IJOSEConsumerBuilder; +end; + +function TJOSEConsumerBuilder.SetRequireSubject: IJOSEConsumerBuilder; +begin + FRequireSubject := True; + Result := Self as IJOSEConsumerBuilder; +end; + +function TJOSEConsumerBuilder.SetSkipAllDefaultValidators: IJOSEConsumerBuilder; +begin + FSkipAllDefaultValidators := True; + Result := Self as IJOSEConsumerBuilder; +end; + +function TJOSEConsumerBuilder.SetSkipAllValidators: IJOSEConsumerBuilder; +begin + FSkipAllValidators := True; + Result := Self as IJOSEConsumerBuilder; +end; + +function TJOSEConsumerBuilder.SetSkipDefaultAudienceValidation: IJOSEConsumerBuilder; +begin + FSkipDefaultAudienceValidation := True; + Result := Self as IJOSEConsumerBuilder; +end; + +function TJOSEConsumerBuilder.SetSkipSignatureVerification: IJOSEConsumerBuilder; +begin + FSkipSignatureVerification := True; + Result := Self as IJOSEConsumerBuilder; +end; + +function TJOSEConsumerBuilder.SetVerificationKey(const AKey: TJOSEBytes): IJOSEConsumerBuilder; +begin + FKey := AKey; + Result := Self as IJOSEConsumerBuilder; +end; + +{ TJOSEConsumer } + +constructor TJOSEConsumer.Create(AClaimsClass: TJWTClaimsClass); +begin + FClaimsClass := AClaimsClass; + FRequireSignature := True; +end; + +procedure TJOSEConsumer.Process(const ACompactToken: TJOSEBytes); +var + LContext: TJOSEContext; +begin + LContext := TJOSEContext.Create(ACompactToken, FClaimsClass); + try + ProcessContext(LContext); + finally + LContext.Free; + end; +end; + +procedure TJOSEConsumer.ProcessContext(AContext: TJOSEContext); +var + LJWS: TJWS; + LHasSignature: Boolean; + //LJWE: TJWE; +begin + LHasSignature := False; + if AContext.GetJOSEObject is TJWS then + begin + LJWS := AContext.GetJOSEObject; + + // JWS Signature Verification + if not FSkipSignatureVerification then + begin + if FSkipVerificationKeyValidation then + LJWS.SkipKeyValidation := True; + + LJWS.SetKey(Self.FKey); + if not LJWS.VerifySignature then + raise EJOSEException.Create('JWS signature is invalid: ' + LJWS.Signature); + end; + + if LJWS.HeaderAlgorithm <> TJOSEAlgorithmId.None.AsString then + LHasSignature := True; + + if FRequireSignature and not LHasSignature then + raise EJOSEException.Create('The JWT has no signature but the JWT Consumer is configured to require one'); + + end + else if AContext.GetJOSEObject is TJWE then + begin + + end; + + Validate(AContext); +end; + +function TJOSEConsumer.SetKey(const AKey: TJOSEBytes): TJOSEConsumer; +begin + FKey := AKey; + Result := Self; +end; + +function TJOSEConsumer.SetSkipVerificationKeyValidation(ASkipVerificationKeyValidation: Boolean): TJOSEConsumer; +begin + FSkipVerificationKeyValidation := ASkipVerificationKeyValidation; + Result := Self; +end; + +function TJOSEConsumer.SetRequireEncryption(ARequireEncryption: Boolean): TJOSEConsumer; +begin + FRequireEncryption := ARequireEncryption; + Result := Self; +end; + +function TJOSEConsumer.SetRequireSignature(ARequireSignature: Boolean): TJOSEConsumer; +begin + FRequireSignature := ARequireSignature; + Result := Self; +end; + +function TJOSEConsumer.SetSkipSignatureVerification(ASkipSignatureVerification: Boolean): TJOSEConsumer; +begin + FSkipSignatureVerification := ASkipSignatureVerification; + Result := Self; +end; + +function TJOSEConsumer.SetValidators(const AValidators: TJOSEValidatorArray): TJOSEConsumer; +begin + FValidators := AValidators; + Result := Self; +end; + +procedure TJOSEConsumer.Validate(AContext: TJOSEContext); +var + LValidator: TJOSEValidator; + LResult: string; + LIssues: TStringList; + LException: EInvalidJWTException; +begin + LIssues := TStringList.Create; + try + for LValidator in FValidators do + begin + LResult := LValidator(AContext); + if LResult.IsEmpty then + Continue; + LIssues.Add(LResult); + end; + if LIssues.Count > 0 then + begin + LException := EInvalidJWTException.CreateFmt( + 'JWT (claims: %s) rejected due to invalid claims.', + [TJSONUtils.ToJSON(AContext.GetClaims.JSON)]); + LException.SetDetails(LIssues); + + raise LException; + end; + finally + LIssues.Free; + end; +end; + +{ EInvalidJWTException } + +procedure EInvalidJWTException.SetDetails(ADetails: TStrings); +var + LDetails: TSTringList; +begin + if ADetails.Count > 0 then + begin + LDetails := TStringList.Create; + try + LDetails.Add(Message); + LDetails.Add('Validation errors:'); + LDetails.AddStrings(ADetails); + Message := LDetails.Text; + finally + LDetails.Free; + end; + end; +end; + +end. + diff --git a/ThirdParty/delphi-jose-jwt/Source/JOSE.Context.pas b/ThirdParty/delphi-jose-jwt/Source/JOSE.Context.pas new file mode 100644 index 00000000..bc9ecf85 --- /dev/null +++ b/ThirdParty/delphi-jose-jwt/Source/JOSE.Context.pas @@ -0,0 +1,139 @@ +{******************************************************************************} +{ } +{ Delphi JOSE Library } +{ Copyright (c) 2015-2017 Paolo Rossi } +{ https://github.com/paolo-rossi/delphi-jose-jwt } +{ } +{******************************************************************************} +{ } +{ Licensed under the Apache License, Version 2.0 (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.apache.org/licenses/LICENSE-2.0 } +{ } +{ Unless required by applicable law or agreed to in writing, software } +{ distributed under the License is distributed on an "AS IS" BASIS, } +{ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. } +{ See the License for the specific language governing permissions and } +{ limitations under the License. } +{ } +{******************************************************************************} + +/// +/// Utility class to encode and decode a JWT +/// +unit JOSE.Context; + +interface + +uses + System.SysUtils, System.Generics.Collections, + JOSE.Types.Bytes, + JOSE.Core.Base, + JOSE.Core.Parts, + JOSE.Core.JWA, + JOSE.Core.JWK, + JOSE.Core.JWT, + JOSE.Core.JWS, + JOSE.Core.JWE; + +type + TJOSEContext = class + private + FCompactToken: TJOSEBytes; + FClaimsClass: TJWTClaimsClass; + FJOSEObject: TJOSEParts; + FJWT: TJWT; + procedure FromCompactToken; + public + constructor Create(const ACompactToken: TJOSEBytes; AClaimsClass: TJWTClaimsClass); + destructor Destroy; override; + + function GetJOSEObject: TJOSEParts; overload; + function GetJOSEObject: T; overload; + + function GetHeader: TJWTHeader; + + function GetClaims: TJWTClaims; overload; + function GetClaims: T; overload; + end; + +implementation + +uses + System.Types, + System.StrUtils; + +{ TJOSEContext } + +constructor TJOSEContext.Create(const ACompactToken: TJOSEBytes; AClaimsClass: TJWTClaimsClass); +begin + FCompactToken := ACompactToken; + FClaimsClass := AClaimsClass; + FJWT := TJWT.Create(FClaimsClass); + try + FromCompactToken; + except + FreeAndNil(FJWT); + end; +end; + +destructor TJOSEContext.Destroy; +begin + FJWT.Free; + FJOSEObject.Free; + inherited; +end; + +procedure TJOSEContext.FromCompactToken; +var + LRes: TStringDynArray; +begin + LRes := SplitString(FCompactToken, PART_SEPARATOR); + + case Length(LRes) of + 3: + begin + FJOSEObject := TJWS.Create(FJWT); + try + FJOSEObject.CompactToken := FCompactToken; + except + FreeAndNil(FJOSEObject); + end; + end; + 5: + begin + raise EJOSEException.Create('Compact Serialization appears to be a JWE Token wich is not (yet) supported'); + end; + else + raise EJOSEException.Create('Malformed Compact Serialization'); + end; +end; + +function TJOSEContext.GetClaims: TJWTClaims; +begin + Result := FJWT.Claims; +end; + +function TJOSEContext.GetClaims: T; +begin + Result := FJWT.Claims as T; +end; + +function TJOSEContext.GetHeader: TJWTHeader; +begin + Result := FJWT.Header; +end; + +function TJOSEContext.GetJOSEObject: TJOSEParts; +begin + Result := FJOSEObject; +end; + +function TJOSEContext.GetJOSEObject: T; +begin + Result := FJOSEObject as T; +end; + +end. diff --git a/ThirdParty/delphi-jose-jwt/Source/JOSE.Core.Base.pas b/ThirdParty/delphi-jose-jwt/Source/JOSE.Core.Base.pas index 6dcca7bf..d370a03d 100644 --- a/ThirdParty/delphi-jose-jwt/Source/JOSE.Core.Base.pas +++ b/ThirdParty/delphi-jose-jwt/Source/JOSE.Core.Base.pas @@ -1,7 +1,7 @@ {******************************************************************************} { } { Delphi JOSE Library } -{ Copyright (c) 2015 Paolo Rossi } +{ Copyright (c) 2015-2017 Paolo Rossi } { https://github.com/paolo-rossi/delphi-jose-jwt } { } {******************************************************************************} @@ -25,17 +25,16 @@ /// unit JOSE.Core.Base; -{$I MARS.inc} - interface +{$SCOPEDENUMS ON} + uses - SysUtils, - Generics.Collections, - JOSE.Types.Bytes - // JOSE.Types.JSON; - , MARS.Core.JSON - ; + System.SysUtils, + System.Generics.Collections, + JOSE.Types.Arrays, + JOSE.Types.Bytes, + JOSE.Types.JSON; const PART_SEPARATOR: Char = '.'; @@ -43,35 +42,110 @@ interface type EJOSEException = class(Exception); + TJOSEStringArray = TJOSEArray; + + TJOSETimeUnit = (Days, Hours, Minutes, Seconds, Milliseconds); + TJOSETimeUnitHelper = record helper for TJOSETimeUnit + private + function Convert(ADuration: Cardinal; ADestUnit: TJOSETimeUnit): Cardinal; + public + function ToDays(ADuration: Cardinal): Cardinal; + function ToHours(ADuration: Cardinal): Cardinal; + function ToMinutes(ADuration: Cardinal): Cardinal; + function ToSeconds(ADuration: Cardinal): Cardinal; + function ToMilliseconds(ADuration: Cardinal): Cardinal; + end; + + TJOSENumericDate = record + private + const CONVERSION: Int64 = 1000; + private + FValue: TDateTime; + function GetAsMilliSeconds: Int64; + function GetAsSeconds: Int64; + procedure SetAsSeconds(const AValue: Int64); + public + constructor Create(AValue: TDateTime); + class function FromSeconds(ASecondsFromEpoch: Int64): TJOSENumericDate; static; + class function FromMilliseconds(AMillisecondsFromEpoch: Int64): TJOSENumericDate; static; + + procedure AddSeconds(ASeconds: Int64); + function IsBefore(const AWhen: TJOSENumericDate; ASkewSeconds: Integer): Boolean; + function IsOnOrAfter(const AWhen: TJOSENumericDate; ASkewSeconds: Integer): Boolean; + function IsAfter(const AWhen: TJOSENumericDate; ASkewSeconds: Integer): Boolean; + + property AsSeconds: Int64 read GetAsSeconds write SetAsSeconds; + property AsMilliSeconds: Int64 read GetAsMilliSeconds; + property AsDateTime: TDateTime read FValue write FValue; + end; + + THeaderNames = class + public const + HEADER_TYPE = 'typ'; + ALGORITHM = 'alg'; + KEY_ID = 'kid'; + end; + TJOSEBase = class private - function GetEncoded: TSuperBytes; - function GetURLEncoded: TSuperBytes; - procedure SetEncoded(const Value: TSuperBytes); - procedure SetURLEncoded(const Value: TSuperBytes); + function GetEncoded: TJOSEBytes; + function GetURLEncoded: TJOSEBytes; + procedure SetEncoded(const Value: TJOSEBytes); + procedure SetURLEncoded(const Value: TJOSEBytes); protected FJSON: TJSONObject; -// procedure AddPairOfType(const AName: string; const AValue: T); + procedure AddPairOfType(const AName: string; const AValue: T); public constructor Create; destructor Destroy; override; + function Clone: TJSONObject; + property JSON: TJSONObject read FJSON write FJSON; - property Encoded: TSuperBytes read GetEncoded write SetEncoded; - property URLEncoded: TSuperBytes read GetURLEncoded write SetURLEncoded; + property Encoded: TJOSEBytes read GetEncoded write SetEncoded; + property URLEncoded: TJOSEBytes read GetURLEncoded write SetURLEncoded; end; +function ToJSON(Value: TJSONAncestor): string; +function JSONDate(ADate: TDateTime): Int64; implementation uses - JOSE.Encoding.Base64 - {$ifdef DelphiXE7_UP}, System.JSON {$endif} -; + System.DateUtils, + System.JSON, + JOSE.Encoding.Base64; + +{$IF CompilerVersion >= 28} +function ToJSON(Value: TJSONAncestor): string; +begin + Result := Value.ToJson; +end; +{$ELSE} +function ToJSON(Value: TJSONAncestor): string; +var + LBytes: TBytes; + LLen: Integer; +begin + SetLength(LBytes, Value.EstimatedByteSize); + LLen := Value.ToBytes(LBytes, 0); + Result := TEncoding.UTF8.GetString(LBytes, 0, LLen); +end; +{$IFEND} + +function JSONDate(ADate: TDateTime): Int64; +begin + Result := DateTimeToUnix(ADate, False); +end; { TJOSEBase } +function TJOSEBase.Clone: TJSONObject; +begin + Result := FJSON.Clone as TJSONObject; +end; + constructor TJOSEBase.Create; begin FJSON := TJSONObject.Create; @@ -83,28 +157,27 @@ destructor TJOSEBase.Destroy; inherited; end; -function TJOSEBase.GetEncoded: TSuperBytes; +function TJOSEBase.GetEncoded: TJOSEBytes; begin - Result := TBase64.Encode(FJSON.ToJSON); + Result := TBase64.Encode(ToJSON(FJSON)); end; -function TJOSEBase.GetURLEncoded: TSuperBytes; +function TJOSEBase.GetURLEncoded: TJOSEBytes; begin - Result := TBase64.URLEncode(FJSON.ToJSON); + Result := TBase64.URLEncode(ToJSON(FJSON)); end; -procedure TJOSEBase.SetEncoded(const Value: TSuperBytes); +procedure TJOSEBase.SetEncoded(const Value: TJOSEBytes); var - LJSONStr: TSuperBytes; + LJSONStr: TJOSEBytes; begin LJSONStr := TBase64.Decode(Value); FJSON.Parse(LJSONStr, 0) - end; -procedure TJOSEBase.SetURLEncoded(const Value: TSuperBytes); +procedure TJOSEBase.SetURLEncoded(const Value: TJOSEBytes); var - LJSONStr: TSuperBytes; + LJSONStr: TJOSEBytes; LValue: TJSONValue; begin LJSONStr := TBase64.URLDecode(Value); @@ -115,12 +188,147 @@ procedure TJOSEBase.SetURLEncoded(const Value: TSuperBytes); FJSON.Free; FJSON := LValue as TJSONObject; end; +end; + +procedure TJOSEBase.AddPairOfType(const AName: string; const AValue: T); +begin + TJSONUtils.SetJSONValueFrom(AName, AValue, FJSON); +end; + +{ TJOSENumericDate } + +procedure TJOSENumericDate.AddSeconds(ASeconds: Int64); +begin + FValue := System.DateUtils.IncSecond(FValue, ASeconds); +end; + +constructor TJOSENumericDate.Create(AValue: TDateTime); +begin + FValue := AValue; +end; + +class function TJOSENumericDate.FromMilliseconds(AMillisecondsFromEpoch: Int64): TJOSENumericDate; +begin + Result := TJOSENumericDate.Create(UnixToDateTime(AMillisecondsFromEpoch div CONVERSION, False)); +end; + +class function TJOSENumericDate.FromSeconds(ASecondsFromEpoch: Int64): TJOSENumericDate; +begin + Result := TJOSENumericDate.Create(UnixToDateTime(ASecondsFromEpoch, False)); +end; + +function TJOSENumericDate.GetAsMilliSeconds: Int64; +begin + Result := DateTimeToUnix(FValue, False) * CONVERSION; +end; + +function TJOSENumericDate.GetAsSeconds: Int64; +begin + Result := DateTimeToUnix(FValue, False); +end; + +function TJOSENumericDate.IsAfter(const AWhen: TJOSENumericDate; ASkewSeconds: Integer): Boolean; +begin + Result := ((Self.AsSeconds - ASkewSeconds) > AWhen.AsSeconds); +end; + +function TJOSENumericDate.IsBefore(const AWhen: TJOSENumericDate; ASkewSeconds: Integer): Boolean; +begin + Result := ((Self.AsSeconds + ASkewSeconds) < AWhen.AsSeconds); +end; + +function TJOSENumericDate.IsOnOrAfter(const AWhen: TJOSENumericDate; ASkewSeconds: Integer): Boolean; +begin + Result := ((Self.AsSeconds - ASkewSeconds) >= AWhen.AsSeconds); +end; + +procedure TJOSENumericDate.SetAsSeconds(const AValue: Int64); +begin + FValue := UnixToDateTime(AValue); +end; + +{ TJOSETimeUnitHelper } + +function TJOSETimeUnitHelper.Convert(ADuration: Cardinal; ADestUnit: TJOSETimeUnit): Cardinal; +begin + Result := 0; + case Self of + TJOSETimeUnit.Days: + begin + case ADestUnit of + TJOSETimeUnit.Days: Result := ADuration; + TJOSETimeUnit.Hours: Result := ADuration * 24; + TJOSETimeUnit.Minutes: Result := ADuration * 24 * 60; + TJOSETimeUnit.Seconds: Result := ADuration * 24 * 60 * 60; + TJOSETimeUnit.Milliseconds: Result := ADuration * 24 * 60 * 60 * 1000; + end; + end; + TJOSETimeUnit.Hours: + begin + case ADestUnit of + TJOSETimeUnit.Days: Result := ADuration div 24; + TJOSETimeUnit.Hours: Result := ADuration; + TJOSETimeUnit.Minutes: Result := ADuration * 60; + TJOSETimeUnit.Seconds: Result := ADuration * 60 * 60; + TJOSETimeUnit.Milliseconds: Result := ADuration * 60 * 60 * 1000; + end; + end; + TJOSETimeUnit.Minutes: + begin + case ADestUnit of + TJOSETimeUnit.Days: Result := (ADuration div 24) div 60; + TJOSETimeUnit.Hours: Result := ADuration div 60; + TJOSETimeUnit.Minutes: Result := ADuration; + TJOSETimeUnit.Seconds: Result := ADuration * 60; + TJOSETimeUnit.Milliseconds: Result := ADuration * 60 * 1000; + end; + end; + TJOSETimeUnit.Seconds: + begin + case ADestUnit of + TJOSETimeUnit.Days: Result := ((ADuration div 24) div 60) div 60; + TJOSETimeUnit.Hours: Result := (ADuration div 24) div 60; + TJOSETimeUnit.Minutes: Result := ADuration div 24; + TJOSETimeUnit.Seconds: Result := ADuration; + TJOSETimeUnit.Milliseconds: Result := ADuration * 1000; + end; + end; + TJOSETimeUnit.Milliseconds: + begin + case ADestUnit of + TJOSETimeUnit.Days: Result := (((ADuration div 24) div 60) div 60) div 1000; + TJOSETimeUnit.Hours: Result := ((ADuration div 24) div 60) div 60; + TJOSETimeUnit.Minutes: Result := (ADuration div 24) div 60; + TJOSETimeUnit.Seconds: Result := ADuration div 24; + TJOSETimeUnit.Milliseconds: Result := ADuration; + end; + end; + end; +end; + +function TJOSETimeUnitHelper.ToDays(ADuration: Cardinal): Cardinal; +begin + Result := Convert(ADuration, TJOSETimeUnit.Days); +end; + +function TJOSETimeUnitHelper.ToHours(ADuration: Cardinal): Cardinal; +begin + Result := Convert(ADuration, TJOSETimeUnit.Hours); +end; + +function TJOSETimeUnitHelper.ToMilliseconds(ADuration: Cardinal): Cardinal; +begin + Result := Convert(ADuration, TJOSETimeUnit.Milliseconds); +end; +function TJOSETimeUnitHelper.ToMinutes(ADuration: Cardinal): Cardinal; +begin + Result := Convert(ADuration, TJOSETimeUnit.Minutes); end; -//procedure TJOSEBase.AddPairOfType(const AName: string; const AValue: T); -//begin -// TJSONUtils.SetJSONValueFrom(AName, AValue, FJSON); -//end; +function TJOSETimeUnitHelper.ToSeconds(ADuration: Cardinal): Cardinal; +begin + Result := Convert(ADuration, TJOSETimeUnit.Seconds); +end; end. diff --git a/ThirdParty/delphi-jose-jwt/Source/JOSE.Core.Builder.pas b/ThirdParty/delphi-jose-jwt/Source/JOSE.Core.Builder.pas index 4e32822b..fb01d0a8 100644 --- a/ThirdParty/delphi-jose-jwt/Source/JOSE.Core.Builder.pas +++ b/ThirdParty/delphi-jose-jwt/Source/JOSE.Core.Builder.pas @@ -1,7 +1,7 @@ {******************************************************************************} { } { Delphi JOSE Library } -{ Copyright (c) 2015 Paolo Rossi } +{ Copyright (c) 2015-2017 Paolo Rossi } { https://github.com/paolo-rossi/delphi-jose-jwt } { } {******************************************************************************} @@ -19,16 +19,12 @@ { limitations under the License. } { } {******************************************************************************} - -/// -/// Utility class to encode and decode a JWT -/// unit JOSE.Core.Builder; interface uses - SysUtils, + System.SysUtils, JOSE.Types.Bytes, JOSE.Core.Base, JOSE.Core.Parts, @@ -39,34 +35,57 @@ interface JOSE.Core.JWE; type + /// + /// Utility class to encode and decode a JWT + /// TJOSE = class private - class function DeserialzeVerify(AKey: TJWK; ACompactToken: TSuperBytes; AVerify: Boolean): TJWT; + class function DeserializeVerify(AKey: TJWK; const ACompactToken: TJOSEBytes; AVerify: Boolean; AClaimsClass: TJWTClaimsClass): TJWT; public - class function Sign(AKey: TJWK; AAlg: TJWAEnum; AToken: TJWT): TSuperBytes; - class function Verify(AKey: TJWK; ACompactToken: TSuperBytes): TJWT; + class function Sign(AKey: TJWK; AAlg: TJOSEAlgorithmId; AToken: TJWT): TJOSEBytes; + class function Verify(AKey: TJWK; const ACompactToken: TJOSEBytes; AClaimsClass: TJWTClaimsClass = nil): TJWT; overload; + class function Verify(AKey: TJOSEBytes; const ACompactToken: TJOSEBytes; AClaimsClass: TJWTClaimsClass = nil): TJWT; overload; + + class function SerializeCompact(AKey: TJWK; AAlg: TJOSEAlgorithmId; AToken: TJWT; ASkipValidation: Boolean): TJOSEBytes; overload; + class function SerializeCompact(AKey: TJWK; AAlg: TJOSEAlgorithmId; AToken: TJWT): TJOSEBytes; overload; + class function SerializeCompact(AKey: TJOSEBytes; AAlg: TJOSEAlgorithmId; AToken: TJWT): TJOSEBytes; overload; - class function SerializeCompact(AKey: TJWK; AAlg: TJWAEnum; AToken: TJWT): TSuperBytes; - class function DeserializeCompact(const ACompactToken: TSuperBytes): TJWT; + class function DeserializeCompact(AKey: TJWK; const ACompactToken: TJOSEBytes): TJWT; overload; + class function DeserializeCompact(AKey: TJOSEBytes; const ACompactToken: TJOSEBytes): TJWT; overload; - class function SHA256CompactToken(AKey: TSuperBytes; AToken: TJWT): TSuperBytes; + class function SHA256CompactToken(AKey: TJOSEBytes; AToken: TJWT): TJOSEBytes; + class function SHA284CompactToken(AKey: TJOSEBytes; AToken: TJWT): TJOSEBytes; + class function SHA512CompactToken(AKey: TJOSEBytes; AToken: TJWT): TJOSEBytes; end; implementation uses - Types, - StrUtils; + System.Types, + System.StrUtils; { TJOSE } -class function TJOSE.DeserializeCompact(const ACompactToken: TSuperBytes): TJWT; +class function TJOSE.DeserializeCompact(AKey: TJOSEBytes; const ACompactToken: TJOSEBytes): TJWT; +var + LKey: TJWK; begin - Result := DeserialzeVerify(nil, ACompactToken, True); + LKey := TJWK.Create(AKey); + try + Result := DeserializeVerify(LKey, ACompactToken, True, TJWTClaims); + finally + LKey.Free; + end; +end; + +class function TJOSE.DeserializeCompact(AKey: TJWK; const ACompactToken: TJOSEBytes): TJWT; +begin + Result := DeserializeVerify(AKey, ACompactToken, True, TJWTClaims); end; -class function TJOSE.DeserialzeVerify(AKey: TJWK; ACompactToken: TSuperBytes; AVerify: Boolean): TJWT; +class function TJOSE.DeserializeVerify(AKey: TJWK; const ACompactToken: TJOSEBytes; + AVerify: Boolean; AClaimsClass: TJWTClaimsClass): TJWT; var LRes: TStringDynArray; LSigner: TJWS; @@ -77,14 +96,15 @@ class function TJOSE.DeserialzeVerify(AKey: TJWK; ACompactToken: TSuperBytes; AV case Length(LRes) of 3: begin - Result := TJWT.Create(TJWTClaims); + Result := TJWT.Create(AClaimsClass); try LSigner := TJWS.Create(Result); + LSigner.SkipKeyValidation := True; try + LSigner.SetKey(AKey); + LSigner.CompactToken := ACompactToken; if AVerify then - LSigner.Verify(AKey, ACompactToken) - else - LSigner.CompactToken := ACompactToken; + LSigner.VerifySignature; finally LSigner.Free; end; @@ -101,50 +121,84 @@ class function TJOSE.DeserialzeVerify(AKey: TJWK; ACompactToken: TSuperBytes; AV end end; -class function TJOSE.SerializeCompact(AKey: TJWK; AAlg: TJWAEnum; AToken: TJWT): TSuperBytes; +class function TJOSE.SerializeCompact(AKey: TJWK; AAlg: TJOSEAlgorithmId; AToken: TJWT): TJOSEBytes; +begin + Result := SerializeCompact(AKey, AAlg, AToken, True); +end; + +class function TJOSE.Sign(AKey: TJWK; AAlg: TJOSEAlgorithmId; AToken: TJWT): TJOSEBytes; var LSigner: TJWS; begin LSigner := TJWS.Create(AToken); try - LSigner.Sign(AKey, AAlg); - Result := LSigner.CompactToken; + Result := LSigner.Sign(AKey, AAlg); finally LSigner.Free; end; end; -class function TJOSE.Sign(AKey: TJWK; AAlg: TJWAEnum; AToken: TJWT): TSuperBytes; +class function TJOSE.SerializeCompact(AKey: TJOSEBytes; AAlg: TJOSEAlgorithmId; AToken: TJWT): TJOSEBytes; var - LSigner: TJWS; + LKey: TJWK; begin - LSigner := TJWS.Create(AToken); + LKey := TJWK.Create(AKey); try - Result := LSigner.Sign(AKey, AAlg); + Result := SerializeCompact(LKey, AAlg, AToken, True); finally - LSigner.Free; + LKey.Free; end; end; -class function TJOSE.SHA256CompactToken(AKey: TSuperBytes; AToken: TJWT): TSuperBytes; +class function TJOSE.SerializeCompact(AKey: TJWK; AAlg: TJOSEAlgorithmId; AToken: TJWT; + ASkipValidation: Boolean): TJOSEBytes; var LSigner: TJWS; - LKey: TJWK; begin LSigner := TJWS.Create(AToken); - LKey := TJWK.Create(AKey); try - LSigner.Sign(LKey, HS256); + LSigner.SkipKeyValidation := ASkipValidation; + LSigner.Sign(AKey, AAlg); Result := LSigner.CompactToken; finally - LKey.Free; LSigner.Free; end; end; -class function TJOSE.Verify(AKey: TJWK; ACompactToken: TSuperBytes): TJWT; +class function TJOSE.SHA256CompactToken(AKey: TJOSEBytes; AToken: TJWT): TJOSEBytes; +begin + Result := SerializeCompact(AKey, TJOSEAlgorithmId.HS256, AToken); +end; + +class function TJOSE.SHA284CompactToken(AKey: TJOSEBytes; AToken: TJWT): TJOSEBytes; +begin + Result := SerializeCompact(AKey, TJOSEAlgorithmId.HS384, AToken); +end; + +class function TJOSE.SHA512CompactToken(AKey: TJOSEBytes; AToken: TJWT): TJOSEBytes; +begin + Result := SerializeCompact(AKey, TJOSEAlgorithmId.HS512, AToken); +end; + +class function TJOSE.Verify(AKey: TJWK; const ACompactToken: TJOSEBytes; AClaimsClass: TJWTClaimsClass): TJWT; +begin + if not Assigned(AClaimsClass) then + AClaimsClass := TJWTClaims; + + Result := DeserializeVerify(AKey, ACompactToken, True, AClaimsClass); +end; + +class function TJOSE.Verify(AKey: TJOSEBytes; const ACompactToken: TJOSEBytes; + AClaimsClass: TJWTClaimsClass): TJWT; +var + LKey: TJWK; begin - Result := DeserialzeVerify(AKey, ACompactToken, True); + LKey := TJWK.Create(AKey); + try + Result := Verify(LKey, ACompactToken, AClaimsClass); + finally + LKey.Free; + end; end; end. diff --git a/ThirdParty/delphi-jose-jwt/Source/JOSE.Core.JWA.Compression.pas b/ThirdParty/delphi-jose-jwt/Source/JOSE.Core.JWA.Compression.pas new file mode 100644 index 00000000..6e64aab7 --- /dev/null +++ b/ThirdParty/delphi-jose-jwt/Source/JOSE.Core.JWA.Compression.pas @@ -0,0 +1,47 @@ +{******************************************************************************} +{ } +{ Delphi JOSE Library } +{ Copyright (c) 2015-2017 Paolo Rossi } +{ https://github.com/paolo-rossi/delphi-jose-jwt } +{ } +{******************************************************************************} +{ } +{ Licensed under the Apache License, Version 2.0 (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.apache.org/licenses/LICENSE-2.0 } +{ } +{ Unless required by applicable law or agreed to in writing, software } +{ distributed under the License is distributed on an "AS IS" BASIS, } +{ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. } +{ See the License for the specific language governing permissions and } +{ limitations under the License. } +{ } +{******************************************************************************} + +/// +/// JSON Web Algorithms (JWA) RFC implementation (partial)
+///
+/// +/// JWA RFC Document +/// +unit JOSE.Core.JWA.Compression; + +interface + +uses + System.SysUtils, + JOSE.Types.Bytes, + JOSE.Core.JWA; + +type + IJOSECompressionAlgorithm = interface(IJOSEAlgorithm) + ['{B2782386-F5A2-43BF-B86C-B103A0221FC4}'] + function Compress(const Data: TBytes): TBytes; + function Decompress(const CompressedData: TBytes): TBytes; + end; + +implementation + +end. diff --git a/ThirdParty/delphi-jose-jwt/Source/JOSE.Core.JWA.Encryption.pas b/ThirdParty/delphi-jose-jwt/Source/JOSE.Core.JWA.Encryption.pas new file mode 100644 index 00000000..79b4dc9e --- /dev/null +++ b/ThirdParty/delphi-jose-jwt/Source/JOSE.Core.JWA.Encryption.pas @@ -0,0 +1,70 @@ +{******************************************************************************} +{ } +{ Delphi JOSE Library } +{ Copyright (c) 2015-2017 Paolo Rossi } +{ https://github.com/paolo-rossi/delphi-jose-jwt } +{ } +{******************************************************************************} +{ } +{ Licensed under the Apache License, Version 2.0 (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.apache.org/licenses/LICENSE-2.0 } +{ } +{ Unless required by applicable law or agreed to in writing, software } +{ distributed under the License is distributed on an "AS IS" BASIS, } +{ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. } +{ See the License for the specific language governing permissions and } +{ limitations under the License. } +{ } +{******************************************************************************} + +/// +/// JSON Web Encryption (JWE) RFC implementation (initial) +/// +/// +/// JWE RFC Document +/// +unit JOSE.Core.JWA.Encryption; + +interface + +uses + System.SysUtils, + JOSE.Types.Bytes, + JOSE.Core.JWA; + +type + TEncryptionParts = class + private + FAuthenticationTag: TBytes; + FCiphertext: TBytes; + FIv: TBytes; + public + constructor Create(const AIv,ACiphertext, AAuthenticationTag: TBytes); + + property Iv: TBytes read FIv; + property Ciphertext: TBytes read FCiphertext; + property AuthenticationTag: TBytes read FAuthenticationTag; + end; + + IJOSEEncryptionAlgorithm = interface(IJOSEAlgorithm) + ['{D802ABF3-82E2-494B-B96A-D13C5A782574}'] + //function GetContentEncryptionKeyDescriptor: TContentEncryptionKeyDescriptor; + function Encrypt(const APlaintext, AAdditionalData, AContentEncryptionKey, IvOverride: TBytes{; AHeaders headers}): TEncryptionParts; + function Decrypt(AEncryptionParts: TEncryptionParts; const AAdditionalData, AContentEncryptionKey: TBytes{; Headers headers}): TBytes; + end; + +implementation + +{ TEncryptionParts } + +constructor TEncryptionParts.Create(const AIv, ACiphertext, AAuthenticationTag: TBytes); +begin + FIv := AIv; + FCiphertext := ACiphertext; + FAuthenticationTag := AAuthenticationTag; +end; + +end. diff --git a/ThirdParty/delphi-jose-jwt/Source/JOSE.Core.JWA.Factory.pas b/ThirdParty/delphi-jose-jwt/Source/JOSE.Core.JWA.Factory.pas new file mode 100644 index 00000000..fa305f0c --- /dev/null +++ b/ThirdParty/delphi-jose-jwt/Source/JOSE.Core.JWA.Factory.pas @@ -0,0 +1,163 @@ +{******************************************************************************} +{ } +{ Delphi JOSE Library } +{ Copyright (c) 2015-2017 Paolo Rossi } +{ https://github.com/paolo-rossi/delphi-jose-jwt } +{ } +{******************************************************************************} +{ } +{ Licensed under the Apache License, Version 2.0 (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.apache.org/licenses/LICENSE-2.0 } +{ } +{ Unless required by applicable law or agreed to in writing, software } +{ distributed under the License is distributed on an "AS IS" BASIS, } +{ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. } +{ See the License for the specific language governing permissions and } +{ limitations under the License. } +{ } +{******************************************************************************} + +/// +/// JSON Web Algorithms (JWA) RFC implementation (partial)
+///
+/// +/// JWA RFC Document +/// +unit JOSE.Core.JWA.Factory; + +interface + +uses + System.SysUtils, + System.Generics.Defaults, + System.Generics.Collections, + JOSE.Types.Bytes, + JOSE.Core.JWA, + JOSE.Core.JWA.Signing, + JOSE.Core.JWA.Compression, + JOSE.Core.JWA.Encryption; + +type + TJOSEAlgorithmRegistry = class + private + FName: string; + FAlgorithms: TDictionary; + public + constructor Create(const AName: string); + destructor Destroy; override; + + function GetAlgorithm(AAlgorithmIdentifier: string): T; + + function RegisterAlgorithm(AAlgorithm: T): TJOSEAlgorithmRegistry; + procedure UnregisterAlgorithm(AAlgId: string); overload; + procedure UnregisterAlgorithm(AAlgId: TJOSEAlgorithmId); overload; + end; + + TJOSEAlgorithmRegistryFactory = class + private class var + FInstance: TJOSEAlgorithmRegistryFactory; + private + FSigningAlgorithmRegistry: TJOSEAlgorithmRegistry; + FEncryptionAlgorithmRegistry: TJOSEAlgorithmRegistry; + FCompressionAlgorithmRegistry: TJOSEAlgorithmRegistry; + + constructor Create; + + class function GetInstance: TJOSEAlgorithmRegistryFactory; static; + public + destructor Destroy; override; + + property SigningAlgorithmRegistry: TJOSEAlgorithmRegistry + read FSigningAlgorithmRegistry; + property EncryptionAlgorithmRegistry: TJOSEAlgorithmRegistry + read FEncryptionAlgorithmRegistry; + property CompressionAlgorithmRegistry: TJOSEAlgorithmRegistry + read FCompressionAlgorithmRegistry; + + class property Instance: TJOSEAlgorithmRegistryFactory read GetInstance; + end; + + + +implementation + +{ TJOSEAlgorithmRegistry } + +constructor TJOSEAlgorithmRegistry.Create(const AName: string); +begin + FAlgorithms := TDictionary.Create; + FName := AName; +end; + +destructor TJOSEAlgorithmRegistry.Destroy; +begin + FAlgorithms.Free; + inherited; +end; + +function TJOSEAlgorithmRegistry.GetAlgorithm(AAlgorithmIdentifier: string): T; +begin + FAlgorithms.TryGetValue(AAlgorithmIdentifier, Result); +end; + +function TJOSEAlgorithmRegistry.RegisterAlgorithm(AAlgorithm: T): TJOSEAlgorithmRegistry; +begin + FAlgorithms.Add(AAlgorithm.GetAlgorithmIdentifier.AsString, AAlgorithm); + Result := Self; +end; + +procedure TJOSEAlgorithmRegistry.UnregisterAlgorithm(AAlgId: string); +begin + FAlgorithms.Remove(AAlgId); +end; + +procedure TJOSEAlgorithmRegistry.UnregisterAlgorithm(AAlgId: TJOSEAlgorithmId); +begin + UnregisterAlgorithm(AAlgId.AsString); +end; + +{ TJOSEAlgorithmRegistryFactory } + +constructor TJOSEAlgorithmRegistryFactory.Create; +begin + FSigningAlgorithmRegistry := TJOSEAlgorithmRegistry.Create('alg'); + + FSigningAlgorithmRegistry + .RegisterAlgorithm(THmacUsingShaAlgorithm.HmacSha256) + .RegisterAlgorithm(THmacUsingShaAlgorithm.HmacSha384) + .RegisterAlgorithm(THmacUsingShaAlgorithm.HmacSha512) + .RegisterAlgorithm(TRSAUsingShaAlgorithm.RSA256) + .RegisterAlgorithm(TRSAUsingShaAlgorithm.RSA384) + .RegisterAlgorithm(TRSAUsingShaAlgorithm.RSA512) + ; + + FEncryptionAlgorithmRegistry := TJOSEAlgorithmRegistry.Create('alg'); + + FCompressionAlgorithmRegistry := TJOSEAlgorithmRegistry.Create('alg'); +end; + +destructor TJOSEAlgorithmRegistryFactory.Destroy; +begin + FSigningAlgorithmRegistry.Free; + FEncryptionAlgorithmRegistry.Free; + FCompressionAlgorithmRegistry.Free; + + inherited; +end; + +class function TJOSEAlgorithmRegistryFactory.GetInstance: TJOSEAlgorithmRegistryFactory; +begin + if not Assigned(FInstance) then + FInstance := TJOSEAlgorithmRegistryFactory.Create; + Result := FInstance; +end; + +initialization + +finalization + TJOSEAlgorithmRegistryFactory.FInstance.Free; + +end. diff --git a/ThirdParty/delphi-jose-jwt/Source/JOSE.Core.JWA.Signing.pas b/ThirdParty/delphi-jose-jwt/Source/JOSE.Core.JWA.Signing.pas new file mode 100644 index 00000000..357e6120 --- /dev/null +++ b/ThirdParty/delphi-jose-jwt/Source/JOSE.Core.JWA.Signing.pas @@ -0,0 +1,306 @@ +{******************************************************************************} +{ } +{ Delphi JOSE Library } +{ Copyright (c) 2015-2017 Paolo Rossi } +{ https://github.com/paolo-rossi/delphi-jose-jwt } +{ } +{******************************************************************************} +{ } +{ Licensed under the Apache License, Version 2.0 (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.apache.org/licenses/LICENSE-2.0 } +{ } +{ Unless required by applicable law or agreed to in writing, software } +{ distributed under the License is distributed on an "AS IS" BASIS, } +{ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. } +{ See the License for the specific language governing permissions and } +{ limitations under the License. } +{ } +{******************************************************************************} + +/// +/// JSON Web Algorithms (JWA) RFC implementation (partial)
+///
+/// +/// JWA RFC Document +/// +unit JOSE.Core.JWA.Signing; + +interface + +uses + System.SysUtils, + JOSE.Types.Bytes, + JOSE.Hashing.HMAC, + JOSE.Signing.RSA, + JOSE.Core.Base, + JOSE.Core.Parts, + JOSE.Core.JWA, + JOSE.Core.JWK; + +type + IJOSESigningAlgorithm = interface(IJOSEAlgorithm) + ['{F999E708-40F5-40E3-81F9-C4D20EB2FA79}'] + function VerifySignature(const AKey, AInput, ASignature: TJOSEBytes): Boolean; + function Sign(const AKey, AInput: TJOSEBytes): TJOSEBytes; + procedure ValidateSigningKey(const AKey: TJOSEBytes); + procedure ValidateVerificationKey(const AKey: TJOSEBytes); + end; + + TBaseSignatureAlgorithm = class(TJOSEAlgorithm, IJOSESigningAlgorithm) + public + function VerifySignature(const AKey, AInput, ASignature: TJOSEBytes): Boolean; + function Sign(const AKey, AInput: TJOSEBytes): TJOSEBytes; + procedure ValidateSigningKey(const AKey: TJOSEBytes); + procedure ValidateVerificationKey(const AKey: TJOSEBytes); + end; + + TUnsecureNoneAlgorithm = class(TJOSEAlgorithm, IJOSESigningAlgorithm) + private + const CANNOT_HAVE_KEY = 'Unsecured JWS (%s=%s) must not use a key'; + procedure ValidateKey(const AKey: TJOSEBytes); + public + constructor Create; + function VerifySignature(const AKey, AInput, ASignature: TJOSEBytes): Boolean; + function Sign(const AKey, AInput: TJOSEBytes): TJOSEBytes; + procedure ValidateSigningKey(const AKey: TJOSEBytes); + procedure ValidateVerificationKey(const AKey: TJOSEBytes); + end; + + THmacUsingShaAlgorithm = class(TJOSEAlgorithm, IJOSESigningAlgorithm) + private + FKeyMinLength: Integer; + protected + FHMACAlgorithm: THMACAlgorithm; + constructor Create(const AAlgorithmId: TJOSEAlgorithmId; AKeyMinLength: Integer); + procedure ValidateKey(const AKey: TJOSEBytes); + public + class function HmacSha256: IJOSESigningAlgorithm; + class function HmacSha384: IJOSESigningAlgorithm; + class function HmacSha512: IJOSESigningAlgorithm; + + function VerifySignature(const AKey, AInput, ASignature: TJOSEBytes): Boolean; + function Sign(const AKey, AInput: TJOSEBytes): TJOSEBytes; + procedure ValidateSigningKey(const AKey: TJOSEBytes); + procedure ValidateVerificationKey(const AKey: TJOSEBytes); + end; + + TRSAUsingSHAAlgorithm = class(TJOSEAlgorithm, IJOSESigningAlgorithm) + private + FKeyMinLength: Integer; + protected + FRSAAlgorithm: TRSAAlgorithm; + constructor Create(const AAlgorithmId: TJOSEAlgorithmId; AKeyMinLength: Integer); + public + class function RSA256: IJOSESigningAlgorithm; + class function RSA384: IJOSESigningAlgorithm; + class function RSA512: IJOSESigningAlgorithm; + + function VerifySignature(const AKey, AInput, ASignature: TJOSEBytes): Boolean; + function Sign(const AKey, AInput: TJOSEBytes): TJOSEBytes; + procedure ValidateSigningKey(const AKey: TJOSEBytes); + procedure ValidateVerificationKey(const AKey: TJOSEBytes); + end; + +implementation + +uses + System.Types, + System.StrUtils, + JOSE.Encoding.Base64; + +constructor THmacUsingShaAlgorithm.Create(const AAlgorithmId: TJOSEAlgorithmId; AKeyMinLength: Integer); +begin + FAlgorithmIdentifier := AAlgorithmId; + + case AAlgorithmId of + TJOSEAlgorithmId.HS256: FHMACAlgorithm := THMACAlgorithm.SHA256; + TJOSEAlgorithmId.HS384: FHMACAlgorithm := THMACAlgorithm.SHA384; + TJOSEAlgorithmId.HS512: FHMACAlgorithm := THMACAlgorithm.SHA512; + end; + FKeyCategory := TJOSEKeyCategory.Symmetric; + FKeyType := 'oct'; + FKeyMinLength := AKeyMinLength; +end; + +class function THmacUsingShaAlgorithm.HmacSha256: IJOSESigningAlgorithm; +begin + Result := THmacUsingShaAlgorithm.Create(TJOSEAlgorithmId.HS256, 256); +end; + +class function THmacUsingShaAlgorithm.HmacSha384: IJOSESigningAlgorithm; +begin + Result := THmacUsingShaAlgorithm.Create(TJOSEAlgorithmId.HS384, 384); +end; + +class function THmacUsingShaAlgorithm.HmacSha512: IJOSESigningAlgorithm; +begin + Result := THmacUsingShaAlgorithm.Create(TJOSEAlgorithmId.HS512, 512); +end; + +function THmacUsingShaAlgorithm.Sign(const AKey, AInput: TJOSEBytes): TJOSEBytes; +var + LSign: TJOSEBytes; +begin + LSign := THMAC.Sign(AInput, AKey, FHMACAlgorithm); + Result := TBase64.URLEncode(LSign.AsBytes); +end; + +procedure THmacUsingShaAlgorithm.ValidateKey(const AKey: TJOSEBytes); +begin + if AKey.IsEmpty then + raise EJOSEException.Create('Key is null'); + + if AKey.Size * 8 < FKeyMinLength then + raise EJOSEException.CreateFmt('Key is too short (%dbit), expected (%dbit)', + [AKey.Size * 8, FKeyMinLength]); +end; + +procedure THmacUsingShaAlgorithm.ValidateSigningKey(const AKey: TJOSEBytes); +begin + ValidateKey(AKey); +end; + +procedure THmacUsingShaAlgorithm.ValidateVerificationKey(const AKey: TJOSEBytes); +begin + ValidateKey(AKey); +end; + +function THmacUsingShaAlgorithm.VerifySignature(const AKey, AInput, ASignature: TJOSEBytes): Boolean; +var + LComputedSignature: TJOSEBytes; +begin + LComputedSignature := THMAC.Sign(AInput, AKey, FHMACAlgorithm); + LComputedSignature := TBase64.URLEncode(LComputedSignature.AsBytes); + + Result := LComputedSignature = ASignature; +end; + +{ TBaseSignatureAlgorithm } + +function TBaseSignatureAlgorithm.Sign(const AKey, AInput: TJOSEBytes): TJOSEBytes; +begin + Result := ''; +end; + +procedure TBaseSignatureAlgorithm.ValidateSigningKey(const AKey: TJOSEBytes); +begin + raise EJOSEException.Create('Not implemented'); +end; + +procedure TBaseSignatureAlgorithm.ValidateVerificationKey(const AKey: TJOSEBytes); +begin + raise EJOSEException.Create('Not implemented'); +end; + +function TBaseSignatureAlgorithm.VerifySignature(const AKey, AInput, ASignature: TJOSEBytes): Boolean; +begin + Result := False; +end; + +{ TUnsecureNoneAlgorithm } + +constructor TUnsecureNoneAlgorithm.Create; +begin + FAlgorithmIdentifier := TJOSEAlgorithmId.None; + FKeyCategory := TJOSEKeyCategory.None; +end; + +function TUnsecureNoneAlgorithm.Sign(const AKey, AInput: TJOSEBytes): TJOSEBytes; +begin + ValidateKey(AKey); + Result := TJOSEBytes.Empty; +end; + +procedure TUnsecureNoneAlgorithm.ValidateKey(const AKey: TJOSEBytes); +begin + if not AKey.IsEmpty then + raise EJOSEException.Create(Format(CANNOT_HAVE_KEY, + [THeaderNames.ALGORITHM, TJOSEAlgorithmId.None.AsString])); +end; + +procedure TUnsecureNoneAlgorithm.ValidateSigningKey(const AKey: TJOSEBytes); +begin + ValidateKey(AKey); +end; + +procedure TUnsecureNoneAlgorithm.ValidateVerificationKey(const AKey: TJOSEBytes); +begin + ValidateKey(AKey); +end; + +function TUnsecureNoneAlgorithm.VerifySignature(const AKey, AInput, ASignature: TJOSEBytes): Boolean; +begin + ValidateKey(AKey); + Result := ASignature.IsEmpty; +end; + +{ TRSAAlgorithm } + +constructor TRSAUsingSHAAlgorithm.Create(const AAlgorithmId: TJOSEAlgorithmId; AKeyMinLength: Integer); +begin + FAlgorithmIdentifier := AAlgorithmId; + + case AAlgorithmId of + TJOSEAlgorithmId.RS256: FRSAAlgorithm := TRSAAlgorithm.RS256; + TJOSEAlgorithmId.RS384: FRSAAlgorithm := TRSAAlgorithm.RS384; + TJOSEAlgorithmId.RS512: FRSAAlgorithm := TRSAAlgorithm.RS512; + end; + FKeyCategory := TJOSEKeyCategory.Asymmetric; + FKeyType := 'pem'; + FKeyMinLength := AKeyMinLength; +end; + +class function TRSAUsingSHAAlgorithm.RSA256: IJOSESigningAlgorithm; +begin + Result := TRSAUsingSHAAlgorithm.Create(TJOSEAlgorithmId.RS256, 256); +end; + +class function TRSAUsingSHAAlgorithm.RSA384: IJOSESigningAlgorithm; +begin + Result := TRSAUsingSHAAlgorithm.Create(TJOSEAlgorithmId.RS384, 384); +end; + +class function TRSAUsingSHAAlgorithm.RSA512: IJOSESigningAlgorithm; +begin + Result := TRSAUsingSHAAlgorithm.Create(TJOSEAlgorithmId.RS512, 512); +end; + +function TRSAUsingSHAAlgorithm.Sign(const AKey, AInput: TJOSEBytes): TJOSEBytes; +var + LSign: TJOSEBytes; +begin + LSign := TRSA.Sign(AInput, AKey, FRSAAlgorithm); + Result := TBase64.URLEncode(LSign.AsBytes); +end; + +procedure TRSAUsingSHAAlgorithm.ValidateSigningKey(const AKey: TJOSEBytes); +begin + if AKey.IsEmpty then + raise EJOSEException.Create('Key is null'); + + if not TRSA.VerifyPrivateKey(AKey) then + raise EJOSEException.Create('Key is not RSA key in PEM format'); +end; + +procedure TRSAUsingSHAAlgorithm.ValidateVerificationKey(const AKey: TJOSEBytes); +begin + if AKey.IsEmpty then + raise EJOSEException.Create('Key is null'); + + if not TRSA.VerifyPublicKey(AKey) then + raise EJOSEException.Create('Key is not RSA key in PEM format'); +end; + +function TRSAUsingSHAAlgorithm.VerifySignature(const AKey, AInput, ASignature: TJOSEBytes): Boolean; +var + LDecodedSignature: TJOSEBytes; +begin + ValidateVerificationKey(AKey); + LDecodedSignature := TBase64.URLDecode(ASignature); + Result := TRSA.Verify(AInput, LDecodedSignature, AKey, FRSAAlgorithm); +end; + +end. diff --git a/ThirdParty/delphi-jose-jwt/Source/JOSE.Core.JWA.pas b/ThirdParty/delphi-jose-jwt/Source/JOSE.Core.JWA.pas index ef4b1c74..6760c4d0 100644 --- a/ThirdParty/delphi-jose-jwt/Source/JOSE.Core.JWA.pas +++ b/ThirdParty/delphi-jose-jwt/Source/JOSE.Core.JWA.pas @@ -1,7 +1,7 @@ {******************************************************************************} { } { Delphi JOSE Library } -{ Copyright (c) 2015 Paolo Rossi } +{ Copyright (c) 2015-2017 Paolo Rossi } { https://github.com/paolo-rossi/delphi-jose-jwt } { } {******************************************************************************} @@ -28,98 +28,133 @@ /// unit JOSE.Core.JWA; -{$I ..\..\..\Source\Mars.inc} - interface +uses + System.SysUtils, + System.Generics.Defaults, + System.Generics.Collections, + JOSE.Types.Bytes; + type - TJWAEnum = (None, HS256, HS384, HS512, RS256, RS348, RS512); + {$SCOPEDENUMS ON} + TJOSEKeyCategory = (None, Symmetric, Asymmetric); - {$ifdef DelphiXE8_UP} - TJWAEnumHelper = record helper for TJWAEnum + TJOSEAlgorithmId = ( + Unknown, None, + HS256, HS384, HS512, + RS256, RS384, RS512, + ES256, ES384, ES512, + PS256, PS384, PS512 + ); + TJOSEAlgorithmIdHelper = record helper for TJOSEAlgorithmId private function GetAsString: string; procedure SetAsString(const Value: string); public property AsString: string read GetAsString write SetAsString; end; - {$else} - TJWAEnumHelper = class + + IJOSEAlgorithm = interface + ['{1BA290C7-D139-4CD8-86FE-7F80B9826007}'] + function GetAlgorithmIdentifier: TJOSEAlgorithmId; + function GetKeyCategory: TJOSEKeyCategory; + function GetKeyType: string; + end; + + IJOSEKeyManagementAlgorithm = interface(IJOSEAlgorithm) + ['{2A2FBF37-8267-4DA3-AA11-7E1C3ED235DD}'] + end; + + TJOSEAlgorithm = class(TInterfacedObject, IJOSEAlgorithm) + FAlgorithmIdentifier: TJOSEAlgorithmId; + FKeyCategory: TJOSEKeyCategory; + FKeyType: string; public - class function AsString(AEnum: TJWAEnum): string; static; - class function FromString(AString: string): TJWAEnum; static; + function GetAlgorithmIdentifier: TJOSEAlgorithmId; + function GetKeyCategory: TJOSEKeyCategory; + function GetKeyType: string; end; - {$endif} implementation -{$ifdef DelphiXE8_UP} -function TJWAEnumHelper.GetAsString: string; +{ TJOSEAlgorithm } + +function TJOSEAlgorithm.GetAlgorithmIdentifier: TJOSEAlgorithmId; +begin + Result := FAlgorithmIdentifier; +end; + +function TJOSEAlgorithm.GetKeyCategory: TJOSEKeyCategory; +begin + Result := FKeyCategory; +end; + +function TJOSEAlgorithm.GetKeyType: string; +begin + Result := FKeyType; +end; + +{ TJOSEAlgorithmIdHelper } + +function TJOSEAlgorithmIdHelper.GetAsString: string; begin case Self of - None: Result := 'none'; - HS256: Result := 'HS256'; - HS384: Result := 'HS384'; - HS512: Result := 'HS512'; - RS256: Result := 'RS256'; - RS348: Result := 'RS348'; - RS512: Result := 'RS512'; + TJOSEAlgorithmId.None: Result := 'none'; + + TJOSEAlgorithmId.HS256: Result := 'HS256'; + TJOSEAlgorithmId.HS384: Result := 'HS384'; + TJOSEAlgorithmId.HS512: Result := 'HS512'; + + TJOSEAlgorithmId.RS256: Result := 'RS256'; + TJOSEAlgorithmId.RS384: Result := 'RS384'; + TJOSEAlgorithmId.RS512: Result := 'RS512'; + + TJOSEAlgorithmId.ES256: Result := 'ES256'; + TJOSEAlgorithmId.ES384: Result := 'ES384'; + TJOSEAlgorithmId.ES512: Result := 'ES512'; + + TJOSEAlgorithmId.PS256: Result := 'PS256'; + TJOSEAlgorithmId.PS384: Result := 'PS384'; + TJOSEAlgorithmId.PS512: Result := 'PS512'; end; end; -procedure TJWAEnumHelper.SetAsString(const Value: string); +procedure TJOSEAlgorithmIdHelper.SetAsString(const Value: string); begin if Value = 'none' then - Self := None + Self := TJOSEAlgorithmId.None + else if Value = 'HS256' then - Self := HS256 + Self := TJOSEAlgorithmId.HS256 else if Value = 'HS384' then - Self := HS384 + Self := TJOSEAlgorithmId.HS384 else if Value = 'HS512' then - Self := HS512 + Self := TJOSEAlgorithmId.HS512 + else if Value = 'RS256' then - Self := RS256 - else if Value = 'RS348' then - Self := RS348 + Self := TJOSEAlgorithmId.RS256 + else if Value = 'RS384' then + Self := TJOSEAlgorithmId.RS384 else if Value = 'RS512' then - Self := RS512; -end; -{$else} + Self := TJOSEAlgorithmId.RS512 + else if Value = 'ES256' then + Self := TJOSEAlgorithmId.ES256 + else if Value = 'ES384' then + Self := TJOSEAlgorithmId.ES384 + else if Value = 'ES512' then + Self := TJOSEAlgorithmId.ES512 -{ TJWAEnumHelper } + else if Value = 'PS256' then + Self := TJOSEAlgorithmId.PS256 + else if Value = 'PS384' then + Self := TJOSEAlgorithmId.PS384 + else if Value = 'PS512' then + Self := TJOSEAlgorithmId.PS512 -class function TJWAEnumHelper.FromString(AString: string): TJWAEnum; -begin - if AString = 'none' then - Result := None - else if AString = 'HS256' then - Result := HS256 - else if AString = 'HS384' then - Result := HS384 - else if AString = 'HS512' then - Result := HS512 - else if AString = 'RS256' then - Result := RS256 - else if AString = 'RS348' then - Result := RS348 - else if AString = 'RS512' then - Result := RS512; + else + Self := TJOSEAlgorithmId.Unknown; end; -class function TJWAEnumHelper.AsString(AEnum: TJWAEnum): string; -begin - case AEnum of - None: Result := 'none'; - HS256: Result := 'HS256'; - HS384: Result := 'HS384'; - HS512: Result := 'HS512'; - RS256: Result := 'RS256'; - RS348: Result := 'RS348'; - RS512: Result := 'RS512'; - end; -end; - -{$endif} - end. diff --git a/ThirdParty/delphi-jose-jwt/Source/JOSE.Core.JWE.pas b/ThirdParty/delphi-jose-jwt/Source/JOSE.Core.JWE.pas index 42c57269..f9d5edee 100644 --- a/ThirdParty/delphi-jose-jwt/Source/JOSE.Core.JWE.pas +++ b/ThirdParty/delphi-jose-jwt/Source/JOSE.Core.JWE.pas @@ -1,7 +1,7 @@ {******************************************************************************} { } { Delphi JOSE Library } -{ Copyright (c) 2015 Paolo Rossi } +{ Copyright (c) 2015-2017 Paolo Rossi } { https://github.com/paolo-rossi/delphi-jose-jwt } { } {******************************************************************************} @@ -58,7 +58,7 @@ constructor TJWE.Create(AToken: TJWT); inherited Create(AToken); for LIndex := 0 to COMPACT_PARTS - 1 do - FParts.Add(TSuperBytes.Empty); + FParts.Add(TJOSEBytes.Empty); end; end. diff --git a/ThirdParty/delphi-jose-jwt/Source/JOSE.Core.JWK.pas b/ThirdParty/delphi-jose-jwt/Source/JOSE.Core.JWK.pas index 05d0954a..1b5475fa 100644 --- a/ThirdParty/delphi-jose-jwt/Source/JOSE.Core.JWK.pas +++ b/ThirdParty/delphi-jose-jwt/Source/JOSE.Core.JWK.pas @@ -1,7 +1,7 @@ {******************************************************************************} { } { Delphi JOSE Library } -{ Copyright (c) 2015 Paolo Rossi } +{ Copyright (c) 2015-2017 Paolo Rossi } { https://github.com/paolo-rossi/delphi-jose-jwt } { } {******************************************************************************} @@ -33,22 +33,23 @@ interface uses SysUtils, JOSE.Types.Bytes, - JOSE.Core.Base; + JOSE.Core.Base, + JOSE.Encoding.Base64; type TJWK = class(TJOSEBase) private - FKey: TSuperBytes; + FKey: TJOSEBytes; public - constructor Create(AKey: TSuperBytes); - property Key: TSuperBytes read FKey write FKey; + constructor Create(AKey: TJOSEBytes); + property Key: TJOSEBytes read FKey write FKey; end; implementation { TJWK } -constructor TJWK.Create(AKey: TSuperBytes); +constructor TJWK.Create(AKey: TJOSEBytes); begin inherited Create; FKey := AKey; diff --git a/ThirdParty/delphi-jose-jwt/Source/JOSE.Core.JWS.pas b/ThirdParty/delphi-jose-jwt/Source/JOSE.Core.JWS.pas index 1220afd2..afe5a504 100644 --- a/ThirdParty/delphi-jose-jwt/Source/JOSE.Core.JWS.pas +++ b/ThirdParty/delphi-jose-jwt/Source/JOSE.Core.JWS.pas @@ -1,7 +1,7 @@ {******************************************************************************} { } { Delphi JOSE Library } -{ Copyright (c) 2015 Paolo Rossi } +{ Copyright (c) 2015-2017 Paolo Rossi } { https://github.com/paolo-rossi/delphi-jose-jwt } { } {******************************************************************************} @@ -28,53 +28,64 @@ /// unit JOSE.Core.JWS; -{$I ..\..\..\Source\Mars.inc} - interface uses - SysUtils, + System.SysUtils, JOSE.Types.Bytes, JOSE.Core.Base, JOSE.Core.Parts, JOSE.Core.JWA, JOSE.Core.JWK, - JOSE.Core.JWT; + JOSE.Core.JWT, + JOSE.Core.JWA.Signing; type TJWS = class(TJOSEParts) private const COMPACT_PARTS = 3; - - function GetSigningInput: TSuperBytes; - function GetHeader: TSuperBytes; - function GetPayload: TSuperBytes; - function GetSignature: TSuperBytes; - procedure SetHeader(const Value: TSuperBytes); - procedure SetPayload(const Value: TSuperBytes); - procedure SetSignature(const Value: TSuperBytes); + private + FKey: TJOSEBytes; + private + function GetSigningInput: TJOSEBytes; + function GetHeader: TJOSEBytes; + function GetPayload: TJOSEBytes; + function GetSignature: TJOSEBytes; + procedure SetHeader(const Value: TJOSEBytes); + procedure SetPayload(const Value: TJOSEBytes); + procedure SetSignature(const Value: TJOSEBytes); protected - function GetCompactToken: TSuperBytes; override; - procedure SetCompactToken(const Value: TSuperBytes); override; + function GetAlgorithm(AAlgId: TJOSEAlgorithmId): IJOSESigningAlgorithm; + function GetCompactToken: TJOSEBytes; override; + procedure SetCompactToken(const Value: TJOSEBytes); override; public constructor Create(AToken: TJWT); override; - function Sign(AKey: TJWK; AAlg: TJWAEnum): TSuperBytes; - procedure Verify(AKey: TJWK; const ACompactToken: TSuperBytes); + procedure SetKey(const AKey: TBytes); overload; + procedure SetKey(const AKey: TJOSEBytes); overload; + procedure SetKey(const AKey: TJWK); overload; + + function Sign: TJOSEBytes; overload; + function Sign(AKey: TJWK; AAlgId: TJOSEAlgorithmId): TJOSEBytes; overload; - property Header: TSuperBytes read GetHeader write SetHeader; - property Payload: TSuperBytes read GetPayload write SetPayload; - property Signature: TSuperBytes read GetSignature write SetSignature; - property SigningInput: TSuperBytes read GetSigningInput; + function VerifySignature: Boolean; overload; + function VerifySignature(AKey: TJWK; const ACompactToken: TJOSEBytes): Boolean; overload; + + property Key: TJOSEBytes read FKey; + property Header: TJOSEBytes read GetHeader write SetHeader; + property Payload: TJOSEBytes read GetPayload write SetPayload; + property Signature: TJOSEBytes read GetSignature write SetSignature; + property SigningInput: TJOSEBytes read GetSigningInput; end; implementation uses - Types, - StrUtils, + System.Types, + System.StrUtils, JOSE.Encoding.Base64, - JOSE.Hashing.HMAC; + JOSE.Hashing.HMAC, + JOSE.Core.JWA.Factory; constructor TJWS.Create(AToken: TJWT); var @@ -83,40 +94,57 @@ constructor TJWS.Create(AToken: TJWT); inherited Create(AToken); for LIndex := 0 to COMPACT_PARTS - 1 do - FParts.Add(TSuperBytes.Empty); + FParts.Add(TJOSEBytes.Empty); end; -function TJWS.GetCompactToken: TSuperBytes; +function TJWS.GetAlgorithm(AAlgId: TJOSEAlgorithmId): IJOSESigningAlgorithm; +var + LAlgId: string; +begin + LAlgId := FToken.Header.Algorithm; + + if LAlgId.IsEmpty then + raise EJOSEException.CreateFmt('Signature algorithm header (%s) not set.', + [THeaderNames.ALGORITHM]); + + Result := TJOSEAlgorithmRegistryFactory.Instance + .SigningAlgorithmRegistry.GetAlgorithm(LAlgId); + if Result = nil then + raise EJOSEException.CreateFmt('Signing algorithm (%s) is not supported.', + [LAlgId]); +end; + +function TJWS.GetCompactToken: TJOSEBytes; begin Result := Header + PART_SEPARATOR + Payload + PART_SEPARATOR + Signature; end; -function TJWS.GetHeader: TSuperBytes; +function TJWS.GetHeader: TJOSEBytes; begin Result := FParts[0] end; -function TJWS.GetPayload: TSuperBytes; +function TJWS.GetPayload: TJOSEBytes; begin Result := FParts[1]; end; -function TJWS.GetSignature: TSuperBytes; +function TJWS.GetSignature: TJOSEBytes; begin Result := FParts[2]; end; -function TJWS.GetSigningInput: TSuperBytes; +function TJWS.GetSigningInput: TJOSEBytes; begin Result := Header + PART_SEPARATOR + Payload; end; -procedure TJWS.SetCompactToken(const Value: TSuperBytes); +procedure TJWS.SetCompactToken(const Value: TJOSEBytes); var LRes: TStringDynArray; begin LRes := SplitString(Value, PART_SEPARATOR); - if Length(LRes) = 3 then + if Length(LRes) = COMPACT_PARTS then begin FParts[0] := LRes[0]; FParts[1] := LRes[1]; @@ -129,76 +157,83 @@ procedure TJWS.SetCompactToken(const Value: TSuperBytes); raise EJOSEException.CreateFmt('A JWS Compact Serialization must have %d parts', [COMPACT_PARTS]); end; -procedure TJWS.SetHeader(const Value: TSuperBytes); +procedure TJWS.SetHeader(const Value: TJOSEBytes); begin FParts[0] := Value; end; -procedure TJWS.SetPayload(const Value: TSuperBytes); +procedure TJWS.SetKey(const AKey: TBytes); +begin + FKey := AKey; +end; + +procedure TJWS.SetKey(const AKey: TJOSEBytes); +begin + FKey := AKey; +end; + +procedure TJWS.SetKey(const AKey: TJWK); +begin + FKey := AKey.Key; +end; + +procedure TJWS.SetPayload(const Value: TJOSEBytes); begin FParts[1] := Value; end; -procedure TJWS.SetSignature(const Value: TSuperBytes); +procedure TJWS.SetSignature(const Value: TJOSEBytes); begin FParts[2] := Value; end; -function TJWS.Sign(AKey: TJWK; AAlg: TJWAEnum): TSuperBytes; -var - LSign: TSuperBytes; +function TJWS.Sign(AKey: TJWK; AAlgId: TJOSEAlgorithmId): TJOSEBytes; begin - Empty; + SetKey(AKey); + SetHeaderAlgorithm(AAlgId); + + Result := Sign(); +end; - {$ifdef DelphiXE8_UP} - FToken.Header.Algorithm := AAlg.AsString; - {$else} - FToken.Header.Algorithm := TJWAEnumHelper.AsString(AAlg); - {$endif} +function TJWS.Sign: TJOSEBytes; +var + LAlgId: TJOSEAlgorithmId; + LAlg: IJOSESigningAlgorithm; +begin + LAlgId.AsString := FToken.Header.Algorithm; + LAlg := GetAlgorithm(LAlgId); - Header := TBase64.URLEncode(FToken.Header.JSON.ToString); - Payload := TBase64.URLEncode(FToken.Claims.JSON.ToString); + if not FSkipKeyValidation then + LAlg.ValidateSigningKey(FKey); - case AAlg of - None: LSign.Clear; - HS256: LSign := THMAC.Sign(SigningInput, AKey.Key, SHA256); - HS384: LSign := THMAC.Sign(SigningInput, AKey.Key, SHA384); - HS512: LSign := THMAC.Sign(SigningInput, AKey.Key, SHA512); - else - raise EJOSEException.Create('Signing algorithm not supported'); - end; - Signature := TBase64.URLEncode(LSign.AsBytes); + Header := TBase64.URLEncode(ToJSON(FToken.Header.JSON)); + Payload := TBase64.URLEncode(ToJSON(FToken.Claims.JSON)); + Signature := LAlg.Sign(FKey, SigningInput); Result := Signature; end; -procedure TJWS.Verify(AKey: TJWK; const ACompactToken: TSuperBytes); +function TJWS.VerifySignature(AKey: TJWK; const ACompactToken: TJOSEBytes): Boolean; +begin + SetKey(AKey); + SetCompactToken(ACompactToken); + + Result := VerifySignature; +end; + +function TJWS.VerifySignature: Boolean; var - LExpectedSign: TSuperBytes; - LAlg: TJWAEnum; -begin - CompactToken := ACompactToken; - - {$ifdef DelphiXE8_UP} - LAlg.AsString := FToken.Header.Algorithm; - {$else} - LAlg := TJWAEnumHelper.FromString(FToken.Header.Algorithm); - {$endif} - case LAlg of - None : FToken.Verified := AKey.Key.IsEmpty; - HS256: LExpectedSign := THMAC.Sign(SigningInput, AKey.Key, SHA256); - HS384: LExpectedSign := THMAC.Sign(SigningInput, AKey.Key, SHA384); - HS512: LExpectedSign := THMAC.Sign(SigningInput, AKey.Key, SHA512); - else - raise EJOSEException.Create('Signing algorithm not supported'); - end; + LAlgId: TJOSEAlgorithmId; + LAlg: IJOSESigningAlgorithm; +begin + LAlgId.AsString := FToken.Header.Algorithm; + LAlg := GetAlgorithm(LAlgId); - if LAlg <> None then - begin - LExpectedSign := TBase64.URLEncode(LExpectedSign); - if LExpectedSign = Signature then - FToken.Verified := True; - end; + if not FSkipKeyValidation then + LAlg.ValidateVerificationKey(FKey); + + Result := LAlg.VerifySignature(FKey, SigningInput, Signature); + FToken.Verified := Result; end; end. diff --git a/ThirdParty/delphi-jose-jwt/Source/JOSE.Core.JWT.pas b/ThirdParty/delphi-jose-jwt/Source/JOSE.Core.JWT.pas index c31b20eb..fbecdbd0 100644 --- a/ThirdParty/delphi-jose-jwt/Source/JOSE.Core.JWT.pas +++ b/ThirdParty/delphi-jose-jwt/Source/JOSE.Core.JWT.pas @@ -1,7 +1,7 @@ {******************************************************************************} { } { Delphi JOSE Library } -{ Copyright (c) 2015 Paolo Rossi } +{ Copyright (c) 2015-2017 Paolo Rossi } { https://github.com/paolo-rossi/delphi-jose-jwt } { } {******************************************************************************} @@ -31,34 +31,31 @@ interface uses - SysUtils, - StrUtils, - DateUtils, - Rtti, - Generics.Collections, - //JOSE.Types.JSON, - MARS.Core.JSON, + System.SysUtils, + System.StrUtils, + System.DateUtils, + System.Rtti, + System.JSON, + System.Generics.Collections, + JOSE.Types.JSON, JOSE.Types.Bytes, JOSE.Core.Base, JOSE.Core.JWA, JOSE.Core.JWK; type - THeaderNames = class - public const - HEADER_TYPE = 'typ'; - ALGORITHM = 'alg'; - end; - TJWTHeader = class sealed(TJOSEBase) private function GetAlgorithm: string; function GetHeaderType: string; procedure SetAlgorithm(const Value: string); procedure SetHeaderType(Value: string); + function GetKeyID: string; + procedure SetKeyID(const Value: string); public property Algorithm: string read GetAlgorithm write SetAlgorithm; property HeaderType: string read GetHeaderType write SetHeaderType; + property KeyID: string read GetKeyID write SetKeyID; end; TReservedClaimNames = class @@ -76,6 +73,8 @@ TReservedClaimNames = class TClaimVerifications = set of TClaimVerification; TJWTClaims = class(TJOSEBase) + private + const AUDIENCE_SEPARATOR = ','; private function GetAudience: string; function GetExpiration: TDateTime; @@ -91,18 +90,38 @@ TJWTClaims = class(TJOSEBase) procedure SetJWTId(Value: string); procedure SetNotBefore(Value: TDateTime); procedure SetSubject(Value: string); + + function GetHasAudience: Boolean; + function GetHasExpiration: Boolean; + function GetHasIssuedAt: Boolean; + function GetHasIssuer: Boolean; + function GetHasJWTId: Boolean; + function GetHasNotBefore: Boolean; + function GetHasSubject: Boolean; + + function ClaimExists(const AClaimName: string): Boolean; + function GetAudienceArray: TArray; + procedure SetAudienceArray(const Value: TArray); public -// procedure SetClaimOfType(const AName: string; const AValue: T); + constructor Create; virtual; + procedure SetClaimOfType(const AName: string; const AValue: T); function GenerateJWTId(ANumberOfBytes: Integer = 16): string; - procedure CheckRegisteredClaims(AOptions: TClaimVerifications = []); virtual; property Audience: string read GetAudience write SetAudience; + property AudienceArray: TArray read GetAudienceArray write SetAudienceArray; + property HasAudience: Boolean read GetHasAudience; property Expiration: TDateTime read GetExpiration write SetExpiration; + property HasExpiration: Boolean read GetHasExpiration; property IssuedAt: TDateTime read GetIssuedAt write SetIssuedAt; + property HasIssuedAt: Boolean read GetHasIssuedAt; property Issuer: string read GetIssuer write SetIssuer; + property HasIssuer: Boolean read GetHasIssuer; property JWTId: string read GetJWTId write SetJWTId; + property HasJWTId: Boolean read GetHasJWTId; property NotBefore: TDateTime read GetNotBefore write SetNotBefore; + property HasNotBefore: Boolean read GetHasNotBefore; property Subject: string read GetSubject write SetSubject; + property HasSubject: Boolean read GetHasSubject; end; TJWTClaimsClass = class of TJWTClaims; @@ -114,9 +133,13 @@ TJWT = class FClaims: TJWTClaims; FHeader: TJWTHeader; public - constructor Create(AClaimsClass: TJWTClaimsClass); + constructor Create; overload; + constructor Create(AClaimsClass: TJWTClaimsClass); overload; destructor Destroy; override; + function GetClaimsAs: T; deprecated; + function ClaimsAs: T; + property Header: TJWTHeader read FHeader; property Claims: TJWTClaims read FClaims; property Verified: Boolean read FVerified write FVerified; @@ -127,6 +150,16 @@ implementation uses JOSE.Encoding.Base64; +function TJWT.GetClaimsAs: T; +begin + Result := FClaims as T; +end; + +function TJWT.ClaimsAs: T; +begin + Result := FClaims as T; +end; + constructor TJWT.Create(AClaimsClass: TJWTClaimsClass); begin FHeader := TJWTHeader.Create; @@ -134,6 +167,11 @@ constructor TJWT.Create(AClaimsClass: TJWTClaimsClass); FClaims := AClaimsClass.Create; end; +constructor TJWT.Create; +begin + Create(TJWTClaims); +end; + destructor TJWT.Destroy; begin FHeader.Free; @@ -143,130 +181,230 @@ destructor TJWT.Destroy; { TJWTClaims } -{ procedure TJWTClaims.SetClaimOfType(const AName: string; const AValue: T); begin AddPairOfType(AName, AValue); end; -} -procedure TJWTClaims.CheckRegisteredClaims(AOptions: TClaimVerifications = []); -//var -// LOption: TClaimVerification; + +function TJWTClaims.ClaimExists(const AClaimName: string): Boolean; begin -{ - for LOption in AOptions do - begin - case LOption of - Audience: ; - Expiration: ; - IssuedAt: ; - Issuer: ; - TokenId: ; - NotBefore: ; - Subject: ; - end; - end; -} + Result := TJSONUtils.CheckPair(AClaimName, FJSON); +end; + +constructor TJWTClaims.Create; +begin + inherited Create; end; function TJWTClaims.GenerateJWTId(ANumberOfBytes: Integer): string; var - LID: TSuperBytes; + LID: TJOSEBytes; begin - LID := TSuperBytes.RandomBytes(ANumberOfBytes); + LID := TJOSEBytes.RandomBytes(ANumberOfBytes); Result := TBase64.URLEncode(LID); end; function TJWTClaims.GetAudience: string; +var + LValue, LAudValue: TJSONValue; + LValueArray: TJSONArray; begin - Result := FJSON.ReadStringValue(TReservedClaimNames.AUDIENCE); + Result := ''; + LAudValue := FJSON.GetValue(TReservedClaimNames.AUDIENCE); + if Assigned(LAudValue) and (LAudValue is TJSONArray) then + begin + LValueArray := LAudValue as TJSONArray; + + for LValue in LValueArray do + Result := Result + LValue.Value + AUDIENCE_SEPARATOR; + + Result := Result.TrimRight([AUDIENCE_SEPARATOR]); + end + else + Result := TJSONUtils.GetJSONValue(TReservedClaimNames.AUDIENCE, FJSON).AsString; +end; + +function TJWTClaims.GetAudienceArray: TArray; +begin + Result := Audience.Split([AUDIENCE_SEPARATOR]); end; function TJWTClaims.GetExpiration: TDateTime; begin - Result := FJSON.ReadUnixTimeValue(TReservedClaimNames.EXPIRATION); + Result := TJSONUtils.GetJSONValueAsEpoch(TReservedClaimNames.EXPIRATION, FJSON); +end; + +function TJWTClaims.GetHasAudience: Boolean; +begin + Result := ClaimExists(TReservedClaimNames.AUDIENCE); +end; + +function TJWTClaims.GetHasExpiration: Boolean; +begin + Result := ClaimExists(TReservedClaimNames.EXPIRATION); +end; + +function TJWTClaims.GetHasIssuedAt: Boolean; +begin + Result := ClaimExists(TReservedClaimNames.ISSUED_AT); +end; + +function TJWTClaims.GetHasIssuer: Boolean; +begin + Result := ClaimExists(TReservedClaimNames.ISSUER); +end; + +function TJWTClaims.GetHasJWTId: Boolean; +begin + Result := ClaimExists(TReservedClaimNames.JWT_ID); +end; + +function TJWTClaims.GetHasNotBefore: Boolean; +begin + Result := ClaimExists(TReservedClaimNames.NOT_BEFORE); +end; + +function TJWTClaims.GetHasSubject: Boolean; +begin + Result := ClaimExists(TReservedClaimNames.SUBJECT); end; function TJWTClaims.GetIssuedAt: TDateTime; begin - Result := FJSON.ReadUnixTimeValue(TReservedClaimNames.ISSUED_AT); + Result := TJSONUtils.GetJSONValueAsEpoch(TReservedClaimNames.ISSUED_AT, FJSON); end; function TJWTClaims.GetIssuer: string; begin - Result := FJSON.ReadStringValue(TReservedClaimNames.ISSUER); + Result := TJSONUtils.GetJSONValue(TReservedClaimNames.ISSUER, FJSON).AsString; end; function TJWTClaims.GetJWTId: string; begin - Result := FJSON.ReadStringValue(TReservedClaimNames.JWT_ID); + Result := TJSONUtils.GetJSONValue(TReservedClaimNames.JWT_ID, FJSON).AsString; end; function TJWTClaims.GetNotBefore: TDateTime; begin - Result := FJSON.ReadUnixTimeValue(TReservedClaimNames.NOT_BEFORE); + Result := TJSONUtils.GetJSONValueAsEpoch(TReservedClaimNames.NOT_BEFORE, FJSON); end; function TJWTClaims.GetSubject: string; begin - Result := FJSON.ReadStringValue(TReservedClaimNames.SUBJECT); + Result := TJSONUtils.GetJSONValue(TReservedClaimNames.SUBJECT, FJSON).AsString; end; procedure TJWTClaims.SetAudience(Value: string); +var + LAudienceArray: TArray; + LAudience: string; + LArray: TJSONArray; begin - FJSON.WriteStringValue(TReservedClaimNames.AUDIENCE, Value); + if Value.IsEmpty then + begin + TJSONUtils.RemoveJSONNode(TReservedClaimNames.AUDIENCE, FJSON); + Exit; + end; + + LAudienceArray := Value.Split([AUDIENCE_SEPARATOR]); + + if Length(LAudienceArray) > 1 then + begin + LArray := TJSONArray.Create; + for LAudience in LAudienceArray do + begin + LArray.Add(LAudience); + end; + FJSON.AddPair(TJSONPair.Create(TReservedClaimNames.AUDIENCE, LArray)); + end; + + if (Length(LAudienceArray) = 1) then + TJSONUtils.SetJSONValueFrom(TReservedClaimNames.AUDIENCE, Value, FJSON); +end; + +procedure TJWTClaims.SetAudienceArray(const Value: TArray); +begin + Audience := string.Join(AUDIENCE_SEPARATOR, Value); end; procedure TJWTClaims.SetExpiration(Value: TDateTime); begin - FJSON.WriteUnixTimeValue(TReservedClaimNames.EXPIRATION, Value); + if Value = 0 then + TJSONUtils.RemoveJSONNode(TReservedClaimNames.EXPIRATION, FJSON) + else + TJSONUtils.SetJSONValueFrom(TReservedClaimNames.EXPIRATION, Value, FJSON); end; procedure TJWTClaims.SetIssuedAt(Value: TDateTime); begin - FJSON.WriteUnixTimeValue(TReservedClaimNames.ISSUED_AT, Value); + if Value = 0 then + TJSONUtils.RemoveJSONNode(TReservedClaimNames.ISSUED_AT, FJSON) + else + TJSONUtils.SetJSONValueFrom(TReservedClaimNames.ISSUED_AT, Value, FJSON); end; procedure TJWTClaims.SetIssuer(Value: string); begin - FJSON.WriteStringValue(TReservedClaimNames.ISSUER, Value); + if Value = '' then + TJSONUtils.RemoveJSONNode(TReservedClaimNames.ISSUER, FJSON) + else + TJSONUtils.SetJSONValueFrom(TReservedClaimNames.ISSUER, Value, FJSON); end; procedure TJWTClaims.SetJWTId(Value: string); begin - FJSON.WriteStringValue(TReservedClaimNames.JWT_ID, Value); + if Value = '' then + TJSONUtils.RemoveJSONNode(TReservedClaimNames.JWT_ID, FJSON) + else + TJSONUtils.SetJSONValueFrom(TReservedClaimNames.JWT_ID, Value, FJSON); end; procedure TJWTClaims.SetNotBefore(Value: TDateTime); begin - FJSON.WriteUnixTimeValue(TReservedClaimNames.NOT_BEFORE, Value); + if Value = 0 then + TJSONUtils.RemoveJSONNode(TReservedClaimNames.NOT_BEFORE, FJSON) + else + TJSONUtils.SetJSONValueFrom(TReservedClaimNames.NOT_BEFORE, Value, FJSON); end; procedure TJWTClaims.SetSubject(Value: string); begin - FJSON.WriteStringValue(TReservedClaimNames.SUBJECT, Value); + if Value = '' then + TJSONUtils.RemoveJSONNode(TReservedClaimNames.SUBJECT, FJSON) + else + TJSONUtils.SetJSONValueFrom(TReservedClaimNames.SUBJECT, Value, FJSON); end; { TJWTHeader } function TJWTHeader.GetAlgorithm: string; begin - Result := FJSON.ReadStringValue(THeaderNames.ALGORITHM); + Result := TJSONUtils.GetJSONValue(THeaderNames.ALGORITHM, FJSON).AsString; end; function TJWTHeader.GetHeaderType: string; begin - Result := FJSON.ReadStringValue(THeaderNames.HEADER_TYPE); + Result := TJSONUtils.GetJSONValue(THeaderNames.HEADER_TYPE, FJSON).AsString; +end; + +function TJWTHeader.GetKeyID: string; +begin + Result := TJSONUtils.GetJSONValue(THeaderNames.KEY_ID, FJSON).AsString; end; procedure TJWTHeader.SetAlgorithm(const Value: string); begin - FJSON.WriteStringValue(THeaderNames.ALGORITHM, Value); + TJSONUtils.SetJSONValueFrom(THeaderNames.ALGORITHM, Value, FJSON); end; procedure TJWTHeader.SetHeaderType(Value: string); begin - FJSON.WriteStringValue(THeaderNames.HEADER_TYPE, Value); + TJSONUtils.SetJSONValueFrom(THeaderNames.HEADER_TYPE, Value, FJSON); +end; + +procedure TJWTHeader.SetKeyID(const Value: string); +begin + TJSONUtils.SetJSONValueFrom(THeaderNames.KEY_ID, Value, FJSON); end; end. diff --git a/ThirdParty/delphi-jose-jwt/Source/JOSE.Core.Parts.pas b/ThirdParty/delphi-jose-jwt/Source/JOSE.Core.Parts.pas index 805cbc7c..259fa6b2 100644 --- a/ThirdParty/delphi-jose-jwt/Source/JOSE.Core.Parts.pas +++ b/ThirdParty/delphi-jose-jwt/Source/JOSE.Core.Parts.pas @@ -1,7 +1,7 @@ {******************************************************************************} { } { Delphi JOSE Library } -{ Copyright (c) 2015 Paolo Rossi } +{ Copyright (c) 2015-2017 Paolo Rossi } { https://github.com/paolo-rossi/delphi-jose-jwt } { } {******************************************************************************} @@ -31,22 +31,31 @@ interface SysUtils, Generics.Collections, JOSE.Types.Bytes, + JOSE.Core.JWA, JOSE.Core.JWT; type TJOSEParts = class protected - FParts: TList; + FParts: TList; FToken: TJWT; - function GetCompactToken: TSuperBytes; virtual; abstract; - procedure SetCompactToken(const Value: TSuperBytes); virtual; abstract; + FSkipKeyValidation: Boolean; + function GetCompactToken: TJOSEBytes; virtual; abstract; + procedure SetCompactToken(const Value: TJOSEBytes); virtual; abstract; + + function GetHeaderAlgorithm: string; public constructor Create(AToken: TJWT); virtual; destructor Destroy; override; + procedure SetHeaderAlgorithm(const AAlg: string); overload; + procedure SetHeaderAlgorithm(AAlg: TJOSEAlgorithmId); overload; + procedure Clear; procedure Empty; - property CompactToken: TSuperBytes read GetCompactToken write SetCompactToken; + property CompactToken: TJOSEBytes read GetCompactToken write SetCompactToken; + property HeaderAlgorithm: string read GetHeaderAlgorithm; + property SkipKeyValidation: Boolean read FSkipKeyValidation write FSkipKeyValidation; end; implementation @@ -61,7 +70,7 @@ procedure TJOSEParts.Clear; constructor TJOSEParts.Create(AToken: TJWT); begin FToken := AToken; - FParts := TList.Create; + FParts := TList.Create; end; destructor TJOSEParts.Destroy; @@ -75,7 +84,22 @@ procedure TJOSEParts.Empty; LIndex: Integer; begin for LIndex := 0 to FParts.Count - 1 do - FParts[LIndex] := TSuperBytes.Empty; + FParts[LIndex] := TJOSEBytes.Empty; +end; + +function TJOSEParts.GetHeaderAlgorithm: string; +begin + Result := FToken.Header.Algorithm; +end; + +procedure TJOSEParts.SetHeaderAlgorithm(AAlg: TJOSEAlgorithmId); +begin + FToken.Header.Algorithm := AAlg.AsString; +end; + +procedure TJOSEParts.SetHeaderAlgorithm(const AAlg: string); +begin + FToken.Header.Algorithm := AAlg; end; end. diff --git a/ThirdParty/delphi-jose-jwt/Source/JOSE.Encoding.Base64.pas b/ThirdParty/delphi-jose-jwt/Source/JOSE.Encoding.Base64.pas index d536da73..26d165e9 100644 --- a/ThirdParty/delphi-jose-jwt/Source/JOSE.Encoding.Base64.pas +++ b/ThirdParty/delphi-jose-jwt/Source/JOSE.Encoding.Base64.pas @@ -1,7 +1,7 @@ {******************************************************************************} { } { Delphi JOSE Library } -{ Copyright (c) 2015 Paolo Rossi } +{ Copyright (c) 2015-2017 Paolo Rossi } { https://github.com/paolo-rossi/delphi-jose-jwt } { } {******************************************************************************} @@ -25,49 +25,161 @@ /// unit JOSE.Encoding.Base64; -{$I ..\..\..\Source\Mars.inc} interface uses - SysUtils, -{$ifdef DelphiXE7_UP} + System.SysUtils, + {$IF CompilerVersion >= 28} System.NetEncoding, -{$else} - IdCoderMIME, IdGlobal, -{$endif} + {$IFEND} JOSE.Types.Bytes; type TBase64 = class - class function Encode(const ASource: TSuperBytes): TSuperBytes; overload; - class function Decode(const ASource: TSuperBytes): TSuperBytes; overload; - class function URLEncode(const ASource: TSuperBytes): TSuperBytes; overload; - class function URLDecode(const ASource: TSuperBytes): TSuperBytes; overload; + class function Encode(const ASource: TJOSEBytes): TJOSEBytes; overload; + class function Decode(const ASource: TJOSEBytes): TJOSEBytes; overload; + class function URLEncode(const ASource: TJOSEBytes): TJOSEBytes; overload; + class function URLDecode(const ASource: TJOSEBytes): TJOSEBytes; overload; end; implementation +{$IF CompilerVersion <= 27} +type + TPacket = packed record + case Integer of + 0: (b0, b1, b2, b3: Byte); + 1: (i: Integer); + 2: (a: array[0..3] of Byte); + end; + +function DecodeBase64(const AInput: string): TBytes; +const + DECODE_TABLE: array[#0..#127] of Integer = ( + Byte('='), 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, + 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, + 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 62, 64, 64, 64, 63, + 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 64, 64, 64, 64, 64, 64, + 64, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, + 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 64, 64, 64, 64, 64, + 64, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, + 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 64, 64, 64, 64, 64 + ); + + function DecodePacket(AInputBuffer: PChar; var ANumChars: Integer): TPacket; + begin + Result.a[0] := + (DECODE_TABLE[AInputBuffer[0]] shl 2) or (DECODE_TABLE[AInputBuffer[1]] shr 4); + ANumChars := 1; + if AInputBuffer[2] <> '=' then + begin + Inc(ANumChars); + Result.a[1] := (DECODE_TABLE[AInputBuffer[1]] shl 4) or (DECODE_TABLE[AInputBuffer[2]] shr 2); + end; + if AInputBuffer[3] <> '=' then + begin + Inc(ANumChars); + Result.a[2] := (DECODE_TABLE[AInputBuffer[2]] shl 6) or DECODE_TABLE[AInputBuffer[3]]; + end; + end; + +var + I, J, K: Integer; + LPacket: TPacket; + LLen: Integer; +begin + SetLength(Result, Length(AInput) div 4 * 3); + LLen := 0; + for I := 1 to Length(AInput) div 4 do + begin + LPacket := DecodePacket(PChar(@AInput[(I - 1) * 4 + 1]), J); + K := 0; + while J > 0 do + begin + Result[LLen] := LPacket.a[K]; + Inc(LLen); + Inc(K); + Dec(J); + end; + end; + SetLength(Result, LLen); +end; + +function EncodeBase64(const AInput: TBytes): string; +const + ENCODE_TABLE: array[0..63] of Char = + 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' + + 'abcdefghijklmnopqrstuvwxyz' + + '0123456789+/'; + + procedure EncodePacket(const APacket: TPacket; ANumChars: Integer; AOutBuffer: PChar); + begin + AOutBuffer[0] := ENCODE_TABLE[APacket.a[0] shr 2]; + AOutBuffer[1] := ENCODE_TABLE[((APacket.a[0] shl 4) or (APacket.a[1] shr 4)) and $0000003f]; + + if ANumChars < 2 then + AOutBuffer[2] := '=' + else + AOutBuffer[2] := ENCODE_TABLE[((APacket.a[1] shl 2) or (APacket.a[2] shr 6)) and $0000003f]; + + if ANumChars < 3 then + AOutBuffer[3] := '=' + else + AOutBuffer[3] := ENCODE_TABLE[APacket.a[2] and $0000003f]; + end; + +var + I, K, J: Integer; + LPacket: TPacket; +begin + Result := ''; + I := (Length(AInput) div 3) * 4; + if Length(AInput) mod 3 > 0 then + Inc(I, 4); + SetLength(Result, I); + J := 1; + for I := 1 to Length(AInput) div 3 do + begin + LPacket.i := 0; + LPacket.a[0] := AInput[(I - 1) * 3]; + LPacket.a[1] := AInput[(I - 1) * 3 + 1]; + LPacket.a[2] := AInput[(I - 1) * 3 + 2]; + EncodePacket(LPacket, 3, PChar(@Result[J])); + Inc(J, 4); + end; + K := 0; + LPacket.i := 0; + for I := Length(AInput) - (Length(AInput) mod 3) + 1 to Length(AInput) do + begin + LPacket.a[K] := Byte(AInput[I - 1]); + Inc(K); + if I = Length(AInput) then + EncodePacket(LPacket, Length(AInput) mod 3, PChar(@Result[J])); + end; +end; +{$IFEND} + { TBase64 } -class function TBase64.Decode(const ASource: TSuperBytes): TSuperBytes; +class function TBase64.Decode(const ASource: TJOSEBytes): TJOSEBytes; begin -{$ifdef DelphiXE7_UP} + {$IF CompilerVersion >= 28} Result := TNetEncoding.Base64.Decode(ASource.AsBytes); -{$else} - Result := TBytes(TIdDecoderMIME.DecodeBytes(ASource.AsString)); -{$endif} + {$ELSE} + Result := DecodeBase64(ASource.AsString); + {$IFEND} end; -class function TBase64.Encode(const ASource: TSuperBytes): TSuperBytes; +class function TBase64.Encode(const ASource: TJOSEBytes): TJOSEBytes; begin -{$ifdef DelphiXE7_UP} + {$IF CompilerVersion >= 28} Result := TNetEncoding.Base64.Encode(ASource.AsBytes); -{$else} - Result := TIdEncoderMIME.EncodeBytes(TIdBytes(ASource.AsBytes)); -{$endif} + {$ELSE} + Result := EncodeBase64(ASource.AsBytes); + {$IFEND} end; -class function TBase64.URLDecode(const ASource: TSuperBytes): TSuperBytes; +class function TBase64.URLDecode(const ASource: TJOSEBytes): TJOSEBytes; var LBase64Str: string; begin @@ -79,7 +191,7 @@ class function TBase64.URLDecode(const ASource: TSuperBytes): TSuperBytes; Result := TBase64.Decode(LBase64Str); end; -class function TBase64.URLEncode(const ASource: TSuperBytes): TSuperBytes; +class function TBase64.URLEncode(const ASource: TJOSEBytes): TJOSEBytes; var LBase64Str: string; begin @@ -97,3 +209,4 @@ class function TBase64.URLEncode(const ASource: TSuperBytes): TSuperBytes; end; end. + diff --git a/ThirdParty/delphi-jose-jwt/Source/JOSE.Hashing.HMAC.pas b/ThirdParty/delphi-jose-jwt/Source/JOSE.Hashing.HMAC.pas index a0f182e2..f7efc0d5 100644 --- a/ThirdParty/delphi-jose-jwt/Source/JOSE.Hashing.HMAC.pas +++ b/ThirdParty/delphi-jose-jwt/Source/JOSE.Hashing.HMAC.pas @@ -1,7 +1,7 @@ {******************************************************************************} { } { Delphi JOSE Library } -{ Copyright (c) 2015 Paolo Rossi } +{ Copyright (c) 2015-2017 Paolo Rossi } { https://github.com/paolo-rossi/delphi-jose-jwt } { } {******************************************************************************} @@ -28,39 +28,60 @@ interface uses - SysUtils, + System.SysUtils, + {$IF CompilerVersion >= 30 } // Delphi 10 Seattle or greater + System.Hash, + {$ELSE} IdGlobal, IdHMAC, IdHMACSHA1, IdSSLOpenSSL, IdHash, + {$IFEND} JOSE.Encoding.Base64; type THMACAlgorithm = (SHA256, SHA384, SHA512); - TIdHMACClass = class of TIdHMAC; + THMACAlgorithmHelper = record helper for THMACAlgorithm + procedure FromString(const AValue: string); + function ToString: string; + end; THMAC = class public class function Sign(const AInput, AKey: TBytes; AAlg: THMACAlgorithm): TBytes; - end; implementation +{$IF CompilerVersion >= 30 } // Delphi 10 Seattle or greater +class function THMAC.Sign(const AInput, AKey: TBytes; AAlg: THMACAlgorithm): TBytes; +var + LHashAlg: THashSHA2.TSHA2Version; +begin + LHashAlg := THashSHA2.TSHA2Version.SHA256; + case AAlg of + SHA256: LHashAlg := THashSHA2.TSHA2Version.SHA256; + SHA384: LHashAlg := THashSHA2.TSHA2Version.SHA384; + SHA512: LHashAlg := THashSHA2.TSHA2Version.SHA512; + end; + Result := THashSHA2.GetHMACAsBytes(AInput, AKey, LHashAlg); +end; + +{$ELSE} class function THMAC.Sign(const AInput, AKey: TBytes; AAlg: THMACAlgorithm): TBytes; var LSigner: TIdHMAC; begin + LSigner := nil; + if not IdSSLOpenSSL.LoadOpenSSLLibrary then - raise Exception.Create('Cannot load OpenSSL library'); + raise Exception.Create('Error Message'); case AAlg of SHA256: LSigner := TIdHMACSHA256.Create; SHA384: LSigner := TIdHMACSHA384.Create; SHA512: LSigner := TIdHMACSHA512.Create; - else - raise Exception.Create('Unknown algorithm'); end; try @@ -70,5 +91,30 @@ class function THMAC.Sign(const AInput, AKey: TBytes; AAlg: THMACAlgorithm): TBy LSigner.Free; end; end; +{$IFEND} + + +{ THMACAlgorithmHelper } + +procedure THMACAlgorithmHelper.FromString(const AValue: string); +begin + if AValue = 'SHA256' then + Self := SHA256 + else if AValue = 'SHA384' then + Self := SHA384 + else if AValue = 'SHA512' then + Self := SHA512 + else + raise Exception.Create('Invalid HMAC algorithm type'); +end; + +function THMACAlgorithmHelper.ToString: string; +begin + case Self of + SHA256: Result := 'SHA256'; + SHA384: Result := 'SHA384'; + SHA512: Result := 'SHA512'; + end; +end; end. diff --git a/ThirdParty/delphi-jose-jwt/Source/JOSE.Signing.RSA.pas b/ThirdParty/delphi-jose-jwt/Source/JOSE.Signing.RSA.pas new file mode 100644 index 00000000..651d116f --- /dev/null +++ b/ThirdParty/delphi-jose-jwt/Source/JOSE.Signing.RSA.pas @@ -0,0 +1,391 @@ +{******************************************************************************} +{ } +{ Delphi JOSE Library } +{ Copyright (c) 2015-2017 Paolo Rossi } +{ https://github.com/paolo-rossi/delphi-jose-jwt } +{ } +{******************************************************************************} +{ } +{ Licensed under the Apache License, Version 2.0 (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.apache.org/licenses/LICENSE-2.0 } +{ } +{ Unless required by applicable law or agreed to in writing, software } +{ distributed under the License is distributed on an "AS IS" BASIS, } +{ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. } +{ See the License for the specific language governing permissions and } +{ limitations under the License. } +{ } +{******************************************************************************} + +unit JOSE.Signing.RSA; + +interface + +uses + System.SysUtils, + IdGlobal, IdCTypes, IdSSLOpenSSLHeaders, + JOSE.Encoding.Base64; + +type + TRSAAlgorithm = (RS256, RS384, RS512); + TRSAAlgorithmHelper = record helper for TRSAAlgorithm + procedure FromString(const AValue: string); + function ToString: string; + end; + + TRSA = class + private + const PKCS1_SIGNATURE_PUBKEY: RawByteString = '-----BEGIN RSA PUBLIC KEY-----'; + const PKCS1_X509_CERTIFICATE: RawByteString = '-----BEGIN CERTIFICATE-----'; + private + class var _PEM_read_bio_RSA_PUBKEY: function(bp: PBIO; x: PPRSA; cb: ppem_password_cb; u: Pointer): PRSA cdecl; + class var _EVP_MD_CTX_create: function: PEVP_MD_CTX cdecl; + class var _EVP_MD_CTX_destroy: procedure(ctx: PEVP_MD_CTX); cdecl; + private + class procedure LoadOpenSSL; + class function VerifyWithCertificate(const AInput, ASignature, ACertificate: TBytes; AAlg: TRSAAlgorithm): Boolean; + class function VerifyCertificate(const ACertificate: TBytes): Boolean; + public + class function Sign(const AInput, AKey: TBytes; AAlg: TRSAAlgorithm): TBytes; + class function Verify(const AInput, ASignature, AKeyOrCertificate: TBytes; AAlg: TRSAAlgorithm): Boolean; + class function VerifyPublicKey(const AKeyOrCertificate: TBytes): Boolean; + class function VerifyPrivateKey(const AKey: TBytes): Boolean; + end; + +implementation + +{$IFDEF MSWINDOWS} +uses + WinApi.Windows; +{$ENDIF} + +function ArrayToString(const ASource: array of Char): string; +var + LSourcePointer: PChar; + LTarget: string; +begin + LSourcePointer := Addr(ASource); + SetString(LTarget, LSourcePointer, Length(ASource)); + Result := LTarget; +end; + +// Get OpenSSL error and message text +function ERR_GetErrorMessage_OpenSSL: string; +var + LErrMsg: array[0..160] of Char; +begin + ERR_error_string(ERR_get_error, @LErrMsg); + Result := ArrayToString(LErrMsg); +end; + +{ TRSAAlgorithmHelper } + +procedure TRSAAlgorithmHelper.FromString(const AValue: string); +begin + if AValue = 'RS256' then + Self := RS256 + else if AValue = 'RS384' then + Self := RS384 + else if AValue = 'RS512' then + Self := RS512 + else + raise Exception.Create('Invalid RSA algorithm type'); +end; + +function TRSAAlgorithmHelper.ToString: string; +begin + case Self of + RS256: Result := 'RS256'; + RS384: Result := 'RS384'; + RS512: Result := 'RS512'; + end; +end; + +{ TRSA } + +class function TRSA.Sign(const AInput, AKey: TBytes; AAlg: TRSAAlgorithm): TBytes; +var + LPrivKeyBIO: pBIO; + LPrivKey: pEVP_PKEY; + LRsa: pRSA; + + LCtx: PEVP_MD_CTX; + LSha: PEVP_MD; + + LLen: Integer; + LSig: Pointer; +begin + LoadOpenSSL; + + // Load Private RSA Key into RSA object + LPrivKeyBIO := BIO_new(BIO_s_mem); + try + BIO_write(LPrivKeyBIO, @AKey[0], Length(AKey)); + LRsa := PEM_read_bio_RSAPrivateKey(LPrivKeyBIO, nil, nil, nil); + if LRsa = nil then + raise Exception.Create('[RSA] Unable to load private key: ' + ERR_GetErrorMessage_OpenSSL); + finally + BIO_free(LPrivKeyBIO); + end; + + try + // Extract Private key from RSA object + LPrivKey := EVP_PKEY_new(); + if EVP_PKEY_set1_RSA(LPrivKey, LRsa) <> 1 then + raise Exception.Create('[RSA] Unable to extract private key: ' + ERR_GetErrorMessage_OpenSSL); + try + case AAlg of + RS256: LSha := EVP_sha256(); + RS384: LSha := EVP_sha384(); + RS512: LSha := EVP_sha512(); + else + raise Exception.Create('[RSA] Unsupported signing algorithm!'); + end; + + LCtx := _EVP_MD_CTX_create; + try + if EVP_DigestSignInit(LCtx, NIL, LSha, NIL, LPrivKey ) <> 1 then + raise Exception.Create('[RSA] Unable to init context: ' + ERR_GetErrorMessage_OpenSSL); + if EVP_DigestSignUpdate(LCtx, @AInput[0], Length(AInput) ) <> 1 then + raise Exception.Create('[RSA] Unable to update context with payload: ' + ERR_GetErrorMessage_OpenSSL); + + // Get signature, first read signature len + EVP_DigestSignFinal(LCtx, nil, @LLen); + LSig := OPENSSL_malloc(LLen); + try + EVP_DigestSignFinal(LCtx, LSig, @LLen); + SetLength(Result, LLen); + Move(LSig^, Result[0], LLen); + finally + CRYPTO_free(LSig); + end; + finally + _EVP_MD_CTX_destroy(LCtx); + end; + finally + EVP_PKEY_free(LPrivKey); + end; + finally + RSA_Free(LRsa); + end; +end; + +class function TRSA.Verify(const AInput, ASignature, AKeyOrCertificate: TBytes; AAlg: TRSAAlgorithm): Boolean; +var + LPubKeyBIO: pBIO; + LPubKey: pEVP_PKEY; + LRsa: pRSA; + LCtx: PEVP_MD_CTX; + LSha: PEVP_MD; +begin + if CompareMem(@PKCS1_X509_CERTIFICATE[1], @AKeyOrCertificate[0], Length(PKCS1_X509_CERTIFICATE)) then + Result := VerifyWithCertificate(AInput, ASignature, AKeyOrCertificate, AAlg) + else + begin + LoadOpenSSL; + + // Load Public RSA Key into RSA object + LPubKeyBIO := BIO_new(BIO_s_mem); + try + BIO_write(LPubKeyBIO, @AKeyOrCertificate[0], Length(AKeyOrCertificate)); + if CompareMem(@PKCS1_SIGNATURE_PUBKEY[1], @AKeyOrCertificate[0], Length(PKCS1_SIGNATURE_PUBKEY)) then + LRsa := PEM_read_bio_RSAPublicKey(LPubKeyBIO, nil, nil, nil) + else + LRsa := _PEM_read_bio_RSA_PUBKEY(LPubKeyBIO, nil, nil, nil); + if LRsa = nil then + raise Exception.Create('[RSA] Unable to load public key: ' + ERR_GetErrorMessage_OpenSSL); + finally + BIO_free(LPubKeyBIO); + end; + + try + // Extract Public key from RSA object + LPubKey := EVP_PKEY_new(); + try + if EVP_PKEY_set1_RSA(LPubKey, LRsa) <> 1 then + raise Exception.Create('[RSA] Unable to extract public key: ' + ERR_GetErrorMessage_OpenSSL); + + case AAlg of + RS256: LSha := EVP_sha256(); + RS384: LSha := EVP_sha384(); + RS512: LSha := EVP_sha512(); + else + raise Exception.Create('[RSA] Unsupported signing algorithm!'); + end; + + LCtx := _EVP_MD_CTX_create; + try + if EVP_DigestVerifyInit(LCtx, NIL, LSha, NIL, LPubKey) <> 1 then + raise Exception.Create('[RSA] Unable to init context: ' + ERR_GetErrorMessage_OpenSSL); + if EVP_DigestVerifyUpdate(LCtx, @AInput[0], Length(AInput)) <> 1 then + raise Exception.Create('[RSA] Unable to update context with payload: ' + ERR_GetErrorMessage_OpenSSL); + + Result := EVP_DigestVerifyFinal(LCtx, @ASignature[0], Length(ASignature)) = 1; + finally + _EVP_MD_CTX_destroy(LCtx); + end; + finally + EVP_PKEY_free(LPubKey); + end; + finally + RSA_Free(LRsa); + end; + end; +end; + +class function TRSA.VerifyPrivateKey(const AKey: TBytes): Boolean; +var + LPubKeyBIO: pBIO; + LRsa: pRSA; +begin + LoadOpenSSL; + + // Load Public RSA Key + LPubKeyBIO := BIO_new(BIO_s_mem); + try + BIO_write(LPubKeyBIO, @AKey[0], Length(AKey)); + LRsa := PEM_read_bio_RSAPrivateKey(LPubKeyBIO, nil, nil, nil); + Result := (LRsa <> nil); + if Result then + RSA_Free(LRsa); + finally + BIO_free(LPubKeyBIO); + end; +end; + +class function TRSA.VerifyPublicKey(const AKeyOrCertificate: TBytes): Boolean; +var + LPubKeyBIO: pBIO; + LRsa: pRSA; +begin + if CompareMem(@PKCS1_X509_CERTIFICATE[1], @AKeyOrCertificate[0], Length(PKCS1_X509_CERTIFICATE)) then + Result := VerifyCertificate(AKeyOrCertificate) + else + begin + LoadOpenSSL; + + // Load Public RSA Key + LPubKeyBIO := BIO_new(BIO_s_mem); + try + BIO_write(LPubKeyBIO, @AKeyOrCertificate[0], Length(AKeyOrCertificate)); + if CompareMem(@PKCS1_SIGNATURE_PUBKEY[1], @AKeyOrCertificate[0], Length(PKCS1_SIGNATURE_PUBKEY)) then + LRsa := PEM_read_bio_RSAPublicKey(LPubKeyBIO, nil, nil, nil) + else + LRsa := _PEM_read_bio_RSA_PUBKEY(LPubKeyBIO, nil, nil, nil); + Result := (LRsa <> nil); + if Result then + RSA_Free(LRsa); + finally + BIO_free(LPubKeyBIO); + end; + end; +end; + +class function TRSA.VerifyWithCertificate(const AInput, ASignature, ACertificate: TBytes; AAlg: TRSAAlgorithm): Boolean; +var + LCertificateBIO: pBIO; + LX509: pX509; + LPubKey: PEVP_PKEY; + AlgID: integer; + LCtx: PEVP_MD_CTX; + LSha: PEVP_MD; +begin + LoadOpenSSL; + LCertificateBIO := BIO_new(BIO_s_mem); + try + BIO_write(LCertificateBIO, @ACertificate[0], Length(ACertificate)); + if not CompareMem(@PKCS1_X509_CERTIFICATE[1], @ACertificate[0], Length(PKCS1_X509_CERTIFICATE)) then + raise Exception.Create('[CERT] No X509 certificate received'); + + LX509 := PEM_read_bio_X509(LCertificateBIO, nil, nil, nil); + if not Assigned(LX509) then + raise Exception.Create('[CERT] Failure by X509 certificate loading'); + + LPubKey := X509_PUBKEY_get(LX509.cert_info.key); + if not Assigned(LPubKey) then + raise Exception.Create('[CERT] Failure by public key extracting from X509 certificate'); + + AlgID := OBJ_obj2nid(LX509.cert_info.key.algor.algorithm); + if AlgID <> NID_rsaEncryption then + raise Exception.Create('[CERT] Unsupported algorithm type in X509 public key (RSA expected)'); + + case AAlg of + RS256: LSha := EVP_sha256(); + RS384: LSha := EVP_sha384(); + RS512: LSha := EVP_sha512(); + else + raise Exception.Create('[RSA] Unsupported signing algorithm!'); + end; + + LCtx := _EVP_MD_CTX_create; + try + if EVP_DigestVerifyInit(LCtx, nil, LSha, nil, LPubKey) <> 1 then + raise Exception.Create('[RSA] Unable to init context: ' + ERR_GetErrorMessage_OpenSSL); + if EVP_DigestVerifyUpdate(LCtx, @AInput[0], Length(AInput)) <> 1 then + raise Exception.Create('[RSA] Unable to update context with payload: ' + ERR_GetErrorMessage_OpenSSL); + + Result := EVP_DigestVerifyFinal(LCtx, @ASignature[0], Length(ASignature)) = 1; + finally + _EVP_MD_CTX_destroy(LCtx); + end; + finally + BIO_free(LCertificateBIO); + end; +end; + +class function TRSA.VerifyCertificate(const ACertificate: TBytes): Boolean; +var + LCertificateBIO: pBIO; + LX509: pX509; + LPubKey: PEVP_PKEY; + AlgID: integer; +begin + LoadOpenSSL; + LCertificateBIO := BIO_new(BIO_s_mem); + try + BIO_write(LCertificateBIO, @ACertificate[0], Length(ACertificate)); + if not CompareMem(@PKCS1_X509_CERTIFICATE[1], @ACertificate[0], Length(PKCS1_X509_CERTIFICATE)) then + raise Exception.Create('[CERT] No X509 certificate received'); + + LX509 := PEM_read_bio_X509(LCertificateBIO, nil, nil, nil); + LPubKey := X509_PUBKEY_get(LX509.cert_info.key); + AlgID := OBJ_obj2nid(LX509.cert_info.key.algor.algorithm); + Result := Assigned(LX509) and Assigned(LPubKey) and (AlgID = NID_rsaEncryption); + finally + BIO_free(LCertificateBIO); + end; +end; + +class procedure TRSA.LoadOpenSSL; +begin + if not IdSSLOpenSSLHeaders.Load then + raise Exception.Create('[RSA] Unable to load OpenSSL libraries'); + + if @EVP_DigestVerifyInit = nil then + raise Exception.Create('[RSA] Please, use OpenSSL 1.0.0. or newer!'); + + if GetCryptLibHandle <> 0 then + begin + _PEM_read_bio_RSA_PUBKEY := GetProcAddress(GetCryptLibHandle, 'PEM_read_bio_RSA_PUBKEY'); + if @_PEM_read_bio_RSA_PUBKEY = nil then + raise Exception.Create('[RSA] Unable to get proc address for "PEM_read_bio_RSA_PUBKEY"'); + + _EVP_MD_CTX_create := GetProcAddress(GetCryptLibHandle, 'EVP_MD_CTX_create'); + if @_EVP_MD_CTX_create = nil then + raise Exception.Create('[RSA] Unable to get proc address for "EVP_MD_CTX_create"'); + + _EVP_MD_CTX_destroy := GetProcAddress(GetCryptLibHandle, 'EVP_MD_CTX_destroy'); + if @_EVP_MD_CTX_create = nil then + raise Exception.Create('[RSA] Unable to get proc address for "EVP_MD_CTX_destroy"'); + end; +end; + +initialization + TRSA._PEM_read_bio_RSA_PUBKEY := nil; + TRSA._EVP_MD_CTX_create := nil; + TRSA._EVP_MD_CTX_destroy := nil; + +end. diff --git a/ThirdParty/delphi-jose-jwt/Source/JOSE.Types.Arrays.pas b/ThirdParty/delphi-jose-jwt/Source/JOSE.Types.Arrays.pas new file mode 100644 index 00000000..7c990977 --- /dev/null +++ b/ThirdParty/delphi-jose-jwt/Source/JOSE.Types.Arrays.pas @@ -0,0 +1,273 @@ +{******************************************************************************} +{ } +{ Delphi JOSE Library } +{ Copyright (c) 2015-2017 Paolo Rossi } +{ https://github.com/paolo-rossi/delphi-jose-jwt } +{ } +{******************************************************************************} +{ } +{ Licensed under the Apache License, Version 2.0 (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.apache.org/licenses/LICENSE-2.0 } +{ } +{ Unless required by applicable law or agreed to in writing, software } +{ distributed under the License is distributed on an "AS IS" BASIS, } +{ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. } +{ See the License for the specific language governing permissions and } +{ limitations under the License. } +{ } +{******************************************************************************} + +/// +/// Handy TArray replacement +/// +unit JOSE.Types.Arrays; + +interface + +uses + System.SysUtils, + System.Classes, + System.Generics.Defaults; + +type + TJOSEArray = record + private + FPayload: TArray; + function GetSize: NativeInt; inline; + function GetLast: T; + procedure SetLast(const Value: T); + function GetAsArray: TArray; + procedure SetAsArray(const Value: TArray); + function GetFirst: T; + procedure SetFirst(const Value: T); + procedure SetSize(const Value: NativeInt); + public + class function Create: TJOSEArray; static; + public + class operator Implicit(const AValue: TArray): TJOSEArray; + class operator Implicit(const AValue: TJOSEArray): TArray; + class operator Add(const A: TJOSEArray; const B: T): TJOSEArray; + class operator Add(const A: T; const B: TJOSEArray): TJOSEArray; + class operator Add(const A, B: TJOSEArray): TJOSEArray; + public + procedure Empty; + function Push(AItem: T): Integer; + function Pop: T; + function Shift: T; + procedure Join(const AValue: TJOSEArray); overload; + procedure Join(const AValue: TArray); overload; + function Contains(const AValue: T): Boolean; + function IsEmpty: Boolean; + function ToString: string; + function ToStringPluralForm(const APluralPrefix: string): string; + + property Size: NativeInt read GetSize write SetSize; + property First: T read GetFirst write SetFirst; + property Last: T read GetLast write SetLast; + property AsArray: TArray read GetAsArray write SetAsArray; + end; + +implementation + +uses + System.Rtti; + +{ TJOSEArray } + +function TJOSEArray.Contains(const AValue: T): Boolean; +var + LComparer: IComparer; + LItem: T; +begin + LComparer := TComparer.Default; + Result := False; + for LItem in FPayload do + if LComparer.Compare(LItem, AValue) = 0 then + Exit(True); +end; + +class function TJOSEArray.Create: TJOSEArray; +begin + Result.Empty; +end; + +procedure TJOSEArray.Empty; +begin + SetLength(FPayload, 0); +end; + +function TJOSEArray.GetAsArray: TArray; +begin + Result := FPayload; +end; + +function TJOSEArray.GetFirst: T; +begin + if Size = 0 then + raise Exception.Create('Error Message'); + + Result := FPayload[0]; +end; + +function TJOSEArray.GetLast: T; +begin + if Size = 0 then + raise Exception.Create('Error Message'); + + Result := FPayload[Size - 1]; +end; + +function TJOSEArray.GetSize: NativeInt; +begin + Result := Length(FPayload); +end; + +class operator TJOSEArray.Implicit(const AValue: TArray): TJOSEArray; +begin + Result.FPayload := AValue; +end; + +class operator TJOSEArray.Implicit(const AValue: TJOSEArray): TArray; +begin + Result := AValue.FPayload; +end; + +class operator TJOSEArray.Add(const A: TJOSEArray; const B: T): TJOSEArray; +begin + Result.FPayload := A; + Result.Push(B); +end; + +class operator TJOSEArray.Add(const A: T; const B: TJOSEArray): TJOSEArray; +begin + Result.FPayload := B; + Result.Push(A); +end; + +class operator TJOSEArray.Add(const A, B: TJOSEArray): TJOSEArray; +var + LSizeSource: NativeInt; +begin + Result := A; + if B.Size = 0 then + Exit; + + Result.Join(B); +end; + +function TJOSEArray.IsEmpty: Boolean; +begin + Result := Length(FPayload) = 0; +end; + +procedure TJOSEArray.Join(const AValue: TJOSEArray); +var + LSizeSource, LSizeDest: NativeInt; +begin + LSizeSource := AValue.Size; + if LSizeSource = 0 then + Exit; + + LSizeDest := Size; + Size := LSizeDest + LSizeSource; + Move(AValue.FPayload[0], FPayload[LSizeDest], SizeOf(T) * LSizeSource); +end; + +procedure TJOSEArray.Join(const AValue: TArray); +var + LSizeSource, LSizeDest: NativeInt; +begin + LSizeSource := Length(AValue); + if LSizeSource = 0 then + Exit; + + LSizeDest := Size; + Size := LSizeDest + LSizeSource; + Move(AValue[0], FPayload[LSizeDest], SizeOf(T) * LSizeSource); +end; + +function TJOSEArray.Pop: T; +begin + if Size = 0 then + raise Exception.Create('Error Message'); + + Result := FPayload[Size - 1]; + SetLength(FPayload, Size - 1); +end; + +function TJOSEArray.Push(AItem: T): Integer; +begin + Size := Size + 1; + FPayload[Size - 1] := AItem; + Result := Size; +end; + +procedure TJOSEArray.SetAsArray(const Value: TArray); +begin + FPayload := Value; +end; + +procedure TJOSEArray.SetFirst(const Value: T); +begin + if Size = 0 then + raise Exception.Create('Error Message'); + + FPayload[0] := Value; +end; + +procedure TJOSEArray.SetLast(const Value: T); +begin + if Size = 0 then + raise Exception.Create('Error Message'); + + FPayload[Size - 1] := Value; +end; + +procedure TJOSEArray.SetSize(const Value: NativeInt); +begin + if Value < 0 then + raise Exception.Create('Error Message'); + SetLength(FPayload, Value); +end; + +function TJOSEArray.Shift: T; +var + LIndex: Integer; +begin + if Size = 0 then + raise Exception.Create('Error Message'); + + Result := FPayload[0]; + if Size > 1 then + for LIndex := 1 to Size - 1 do + FPayload[LIndex - 1] := FPayload[LIndex]; + + SetLength(FPayload, Size - 1); +end; + +function TJOSEArray.ToString: string; +var + LValue: TValue; + LItem: T; +begin + Result := ''; + for LItem in FPayload do + begin + LValue := TValue.From(LItem); + Result := Result + LValue.ToString + ','; + end; + Result := Result.TrimRight([',']); +end; + +function TJOSEArray.ToStringPluralForm(const APluralPrefix: string): string; +begin + if Self.Size > 1 then + Result := APluralPrefix + Self.ToString + else + Result := Self.ToString; +end; + +end. diff --git a/ThirdParty/delphi-jose-jwt/Source/JOSE.Types.Bytes.pas b/ThirdParty/delphi-jose-jwt/Source/JOSE.Types.Bytes.pas index 5ace3f70..06c99b68 100644 --- a/ThirdParty/delphi-jose-jwt/Source/JOSE.Types.Bytes.pas +++ b/ThirdParty/delphi-jose-jwt/Source/JOSE.Types.Bytes.pas @@ -1,7 +1,7 @@ {******************************************************************************} { } { Delphi JOSE Library } -{ Copyright (c) 2015 Paolo Rossi } +{ Copyright (c) 2015-2017 Paolo Rossi } { https://github.com/paolo-rossi/delphi-jose-jwt } { } {******************************************************************************} @@ -32,29 +32,29 @@ interface Classes; type - TSuperBytes = record + TJOSEBytes = record private FPayload: TBytes; procedure SetAsString(const Value: string); function GetAsString: string; inline; public - class operator Implicit(const AValue: TSuperBytes): string; - class operator Implicit(const AValue: TSuperBytes): TBytes; - class operator Implicit(const AValue: string): TSuperBytes; - class operator Implicit(const AValue: TBytes): TSuperBytes; + class operator Implicit(const AValue: TJOSEBytes): string; + class operator Implicit(const AValue: TJOSEBytes): TBytes; + class operator Implicit(const AValue: string): TJOSEBytes; + class operator Implicit(const AValue: TBytes): TJOSEBytes; - class operator Equal(const A: TSuperBytes; const B: TSuperBytes): Boolean; - class operator Equal(const A: TSuperBytes; const B: string): Boolean; - class operator Equal(const A: TSuperBytes; const B: TBytes): Boolean; + class operator Equal(const A: TJOSEBytes; const B: TJOSEBytes): Boolean; + class operator Equal(const A: TJOSEBytes; const B: string): Boolean; + class operator Equal(const A: TJOSEBytes; const B: TBytes): Boolean; - class operator Add(const A: TSuperBytes; const B: TSuperBytes): TSuperBytes; - class operator Add(const A: TSuperBytes; const B: Byte): TSuperBytes; - class operator Add(const A: TSuperBytes; const B: string): TSuperBytes; - class operator Add(const A: TSuperBytes; const B: TBytes): TSuperBytes; + class operator Add(const A: TJOSEBytes; const B: TJOSEBytes): TJOSEBytes; + class operator Add(const A: TJOSEBytes; const B: Byte): TJOSEBytes; + class operator Add(const A: TJOSEBytes; const B: string): TJOSEBytes; + class operator Add(const A: TJOSEBytes; const B: TBytes): TJOSEBytes; - class function Empty: TSuperBytes; static; - class function RandomBytes(ANumberOfBytes: Integer): TSuperBytes; static; + class function Empty: TJOSEBytes; static; + class function RandomBytes(ANumberOfBytes: Integer): TJOSEBytes; static; function IsEmpty: Boolean; function Size: Integer; @@ -62,7 +62,7 @@ TSuperBytes = record function Contains(const AByte: Byte): Boolean; overload; function Contains(const ABytes: TBytes): Boolean; overload; - function Contains(const ABytes: TSuperBytes): Boolean; overload; + function Contains(const ABytes: TJOSEBytes): Boolean; overload; property AsBytes: TBytes read FPayload write FPayload; property AsString: string read GetAsString write SetAsString; @@ -75,6 +75,7 @@ TBytesUtils = class implementation +{ CompareBytes } function CompareBytes(const A, B: TBytes): Boolean; var @@ -86,31 +87,30 @@ function CompareBytes(const A, B: TBytes): Boolean; Result := CompareMem(Pointer(A), Pointer(B), LLen); end; +{ TJOSEBytes } -{ TSuperBytes } - -class operator TSuperBytes.Add(const A: TSuperBytes; const B: Byte): TSuperBytes; +class operator TJOSEBytes.Add(const A: TJOSEBytes; const B: Byte): TJOSEBytes; begin SetLength(Result.FPayload, A.Size + 1); Move(A.FPayload[0], Result.FPayload[0], A.Size); Result.FPayload[Result.Size-1] := B; end; -class operator TSuperBytes.Add(const A, B: TSuperBytes): TSuperBytes; +class operator TJOSEBytes.Add(const A, B: TJOSEBytes): TJOSEBytes; begin SetLength(Result.FPayload, A.Size + B.Size); Move(A.FPayload[0], Result.FPayload[0], A.Size); Move(B.FPayload[0], Result.FPayload[A.Size], B.Size); end; -class operator TSuperBytes.Add(const A: TSuperBytes; const B: TBytes): TSuperBytes; +class operator TJOSEBytes.Add(const A: TJOSEBytes; const B: TBytes): TJOSEBytes; begin SetLength(Result.FPayload, A.Size + Length(B)); Move(A.FPayload[0], Result.FPayload[0], A.Size); Move(B[0], Result.FPayload[A.Size], Length(B)); end; -class operator TSuperBytes.Add(const A: TSuperBytes; const B: string): TSuperBytes; +class operator TJOSEBytes.Add(const A: TJOSEBytes; const B: string): TJOSEBytes; var LB: TBytes; begin @@ -121,72 +121,72 @@ function CompareBytes(const A, B: TBytes): Boolean; Move(LB[0], Result.FPayload[A.Size], Length(LB)); end; -procedure TSuperBytes.Clear; +procedure TJOSEBytes.Clear; begin SetLength(FPayload, 0); end; -function TSuperBytes.Contains(const AByte: Byte): Boolean; +function TJOSEBytes.Contains(const AByte: Byte): Boolean; begin Result := False; end; -function TSuperBytes.Contains(const ABytes: TBytes): Boolean; +function TJOSEBytes.Contains(const ABytes: TBytes): Boolean; begin Result := False; end; -function TSuperBytes.Contains(const ABytes: TSuperBytes): Boolean; +function TJOSEBytes.Contains(const ABytes: TJOSEBytes): Boolean; begin Result := False; end; -class function TSuperBytes.Empty: TSuperBytes; +class function TJOSEBytes.Empty: TJOSEBytes; begin SetLength(Result.FPayload, 0); end; -class operator TSuperBytes.Equal(const A: TSuperBytes; const B: string): Boolean; +class operator TJOSEBytes.Equal(const A: TJOSEBytes; const B: string): Boolean; begin Result := CompareBytes(A.FPayload, TEncoding.UTF8.GetBytes(B)); end; -class operator TSuperBytes.Equal(const A: TSuperBytes; const B: TBytes): Boolean; +class operator TJOSEBytes.Equal(const A: TJOSEBytes; const B: TBytes): Boolean; begin Result := CompareBytes(A.FPayload, B); end; -class operator TSuperBytes.Equal(const A: TSuperBytes; const B: TSuperBytes): Boolean; +class operator TJOSEBytes.Equal(const A: TJOSEBytes; const B: TJOSEBytes): Boolean; begin Result := CompareBytes(A.FPayload, B.FPayload); end; -function TSuperBytes.GetAsString: string; +function TJOSEBytes.GetAsString: string; begin Result := TEncoding.UTF8.GetString(FPayload); end; -class operator TSuperBytes.Implicit(const AValue: TSuperBytes): TBytes; +class operator TJOSEBytes.Implicit(const AValue: TJOSEBytes): TBytes; begin Result := AValue.AsBytes; end; -class operator TSuperBytes.Implicit(const AValue: TSuperBytes): string; +class operator TJOSEBytes.Implicit(const AValue: TJOSEBytes): string; begin Result := TEncoding.UTF8.GetString(AValue.FPayload); end; -class operator TSuperBytes.Implicit(const AValue: TBytes): TSuperBytes; +class operator TJOSEBytes.Implicit(const AValue: TBytes): TJOSEBytes; begin Result.FPayload := AValue; end; -function TSuperBytes.IsEmpty: Boolean; +function TJOSEBytes.IsEmpty: Boolean; begin Result := Size = 0; end; -class function TSuperBytes.RandomBytes(ANumberOfBytes: Integer): TSuperBytes; +class function TJOSEBytes.RandomBytes(ANumberOfBytes: Integer): TJOSEBytes; var LIndex: Integer; begin @@ -195,17 +195,17 @@ class function TSuperBytes.RandomBytes(ANumberOfBytes: Integer): TSuperBytes; Result.FPayload[LIndex] := Random(255); end; -function TSuperBytes.Size: Integer; +function TJOSEBytes.Size: Integer; begin Result := Length(FPayload); end; -class operator TSuperBytes.Implicit(const AValue: string): TSuperBytes; +class operator TJOSEBytes.Implicit(const AValue: string): TJOSEBytes; begin Result.FPayload := TEncoding.UTF8.GetBytes(AValue); end; -procedure TSuperBytes.SetAsString(const Value: string); +procedure TJOSEBytes.SetAsString(const Value: string); begin FPayload := TEncoding.UTF8.GetBytes(Value); end; diff --git a/ThirdParty/delphi-jose-jwt/Source/JOSE.Types.JSON.pas b/ThirdParty/delphi-jose-jwt/Source/JOSE.Types.JSON.pas new file mode 100644 index 00000000..cb7d1091 --- /dev/null +++ b/ThirdParty/delphi-jose-jwt/Source/JOSE.Types.JSON.pas @@ -0,0 +1,251 @@ +{******************************************************************************} +{ } +{ Delphi JOSE Library } +{ Copyright (c) 2015-2017 Paolo Rossi } +{ https://github.com/paolo-rossi/delphi-jose-jwt } +{ } +{******************************************************************************} +{ } +{ Licensed under the Apache License, Version 2.0 (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.apache.org/licenses/LICENSE-2.0 } +{ } +{ Unless required by applicable law or agreed to in writing, software } +{ distributed under the License is distributed on an "AS IS" BASIS, } +{ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. } +{ See the License for the specific language governing permissions and } +{ limitations under the License. } +{ } +{******************************************************************************} + +/// +/// Utility unit to deal with the JSON Delphi classes +/// +unit JOSE.Types.JSON; + +interface + +uses + System.SysUtils, + System.StrUtils, + System.DateUtils, + System.Rtti, + System.JSON; + +type + EJSONConversionException = class(Exception); + + TJSONAncestor = System.JSON.TJSONAncestor; + TJSONPair = System.JSON.TJSONPair; + TJSONValue = System.JSON.TJSONValue; + TJSONTrue = System.JSON.TJSONTrue; + TJSONString = System.JSON.TJSONString; + TJSONNumber = System.JSON.TJSONNumber; + TJSONObject = System.JSON.TJSONObject; + TJSONNull = System.JSON.TJSONNull; + TJSONFalse = System.JSON.TJSONFalse; + TJSONArray = System.JSON.TJSONArray; + + TJSONUtils = class + private + class procedure SetJSONValue(const AName: string; const AValue: TValue; AJSON: TJSONObject); overload; + public + class function ToJSON(AJSONValue: TJSONValue): string; static; + class function CheckPair(const AName: string; AJSON: TJSONObject): Boolean; + class function GetJSONValueInt(const AName: string; AJSON: TJSONObject): TValue; + class function GetJSONValueInt64(const AName: string; AJSON: TJSONObject): TValue; + class function GetJSONValueDouble(const AName: string; AJSON: TJSONObject): TValue; + class function GetJSONValue(const AName: string; AJSON: TJSONObject): TValue; + class function GetJSONValueAsDate(const AName: string; AJSON: TJSONObject): TDateTime; + class function GetJSONValueAsEpoch(const AName: string; AJSON: TJSONObject): TDateTime; + + class procedure SetJSONValueFrom(const AName: string; const AValue: T; AJSON: TJSONObject); + class procedure RemoveJSONNode(const AName: string; AJSON: TJSONObject); + end; + +implementation + +uses + System.TypInfo; + +{ TJSONUtils } + +class function TJSONUtils.CheckPair(const AName: string; AJSON: TJSONObject): Boolean; +begin + Result := Assigned(AJSON.GetValue(AName)); +end; + +class function TJSONUtils.GetJSONValue(const AName: string; AJSON: TJSONObject): TValue; +var + LJSONValue: TJSONValue; +begin + LJSONValue := AJSON.GetValue(AName); + + if not Assigned(LJSONValue) then + Result := TValue.Empty + else if LJSONValue is TJSONTrue then + Result := True + else if LJSONValue is TJSONFalse then + Result := False + else if LJSONValue is TJSONNumber then + Result := TJSONNumber(LJSONValue).AsDouble + else + Result := LJSONValue.Value; +end; + +class function TJSONUtils.GetJSONValueAsDate(const AName: string; AJSON: TJSONObject): TDateTime; +var + LJSONValue: string; +begin + LJSONValue := TJSONUtils.GetJSONValue(AName, AJSON).AsString; + if LJSONValue = '' then + Result := 0 + else + Result := ISO8601ToDate(LJSONValue) +end; + +class function TJSONUtils.GetJSONValueAsEpoch(const AName: string; AJSON: TJSONObject): TDateTime; +var + LJSONValue: Int64; +begin + LJSONValue := TJSONUtils.GetJSONValueInt64(AName, AJSON).AsInt64; + if LJSONValue = 0 then + Result := 0 + else + Result := UnixToDateTime(LJSONValue, False) +end; + +class function TJSONUtils.GetJSONValueDouble(const AName: string; AJSON: TJSONObject): TValue; +var + LJSONValue: TJSONValue; +begin + LJSONValue := AJSON.GetValue(AName); + + if not Assigned(LJSONValue) then + Result := TValue.Empty + else if LJSONValue is TJSONNumber then + Result := TJSONNumber(LJSONValue).AsDouble + else + raise EJSONConversionException.Create('JSON Incompatible type. Expected Double'); +end; + +class function TJSONUtils.GetJSONValueInt(const AName: string; AJSON: TJSONObject): TValue; +var + LJSONValue: TJSONValue; +begin + LJSONValue := AJSON.GetValue(AName); + + if not Assigned(LJSONValue) then + Result := TValue.Empty + else if LJSONValue is TJSONNumber then + Result := TJSONNumber(LJSONValue).AsInt + else + raise EJSONConversionException.Create('JSON Incompatible type. Expected Integer'); +end; + +class function TJSONUtils.GetJSONValueInt64(const AName: string; AJSON: TJSONObject): TValue; +var + LJSONValue: TJSONValue; +begin + LJSONValue := AJSON.GetValue(AName); + + if not Assigned(LJSONValue) then + Result := TValue.Empty + else if LJSONValue is TJSONNumber then + Result := TJSONNumber(LJSONValue).AsInt64 + else + raise EJSONConversionException.Create('JSON Incompatible type. Expected Int64'); +end; + +class procedure TJSONUtils.RemoveJSONNode(const AName: string; AJSON: TJSONObject); +var + LPair: TJSONPair; +begin + LPair := AJSON.RemovePair(AName); + if Assigned(LPair) then + LPair.Free; +end; + +class procedure TJSONUtils.SetJSONValueFrom(const AName: string; const AValue: T; AJSON: TJSONObject); +begin + SetJSONValue(AName, TValue.From(AValue), AJSON); +end; + +class function TJSONUtils.ToJSON(AJSONValue: TJSONValue): string; +var + LBytes: TBytes; +begin + SetLength(LBytes, AJSONValue.ToString.Length * 6); + SetLength(LBytes, AJSONValue.ToBytes(LBytes, 0)); + Result := TEncoding.Default.GetString(LBytes); +end; + +class procedure TJSONUtils.SetJSONValue(const AName: string; const AValue: TValue; AJSON: TJSONObject); +var + LPair: TJSONPair; + LValue: TJSONValue; +begin + LValue := nil; + + case AValue.Kind of + tkChar, + tkString, + tkWChar, + tkLString, + tkWString, + tkUString: + begin + LValue := TJSONString.Create(AValue.AsType); + end; + + tkEnumeration: + begin + if AValue.TypeInfo^.NameFld.ToString = 'Boolean' then + begin + if AValue.AsType then + LValue := TJSONTrue.Create + else + LValue := TJSONFalse.Create; + end; + end; + + tkInteger, + tkInt64, + tkFloat: + begin + if SameText(AValue.TypeInfo^.NameFld.ToString, 'TDateTime') or + SameText(AValue.TypeInfo^.NameFld.ToString, 'TDate') or + SameText(AValue.TypeInfo^.NameFld.ToString, 'TTime') then + LValue := TJSONNumber.Create(DateTimeToUnix(AValue.AsType, False)) + else + if AValue.Kind = tkFloat then + LValue := TJSONNumber.Create(AValue.AsType) + else + if AValue.Kind = tkInt64 then + LValue := TJSONNumber.Create(AValue.AsType) + else + LValue := TJSONNumber.Create(AValue.AsType) + end; + end; + + if not Assigned(LValue) then + Exit; + + LPair := AJSON.Get(AName); + if Assigned(LPair) then + begin + // Replace the JSON Value (the previous is freed by the TJSONPair object) + LPair.JsonValue := LValue; + end + else + begin + LPair := TJSONPair.Create(AName, LValue); + AJSON.AddPair(LPair); + end; + +end; + +end. + diff --git a/ThirdParty/mORMot/Source/SynCommons.pas b/ThirdParty/mORMot/Source/SynCommons.pas index 7ac057de..fb05ed9f 100644 --- a/ThirdParty/mORMot/Source/SynCommons.pas +++ b/ThirdParty/mORMot/Source/SynCommons.pas @@ -6,7 +6,7 @@ (* This file is part of Synopse framework. - Synopse framework. Copyright (C) 2018 Arnaud Bouchez + Synopse framework. Copyright (C) 2019 Arnaud Bouchez Synopse Informatique - https://synopse.info *** BEGIN LICENSE BLOCK ***** @@ -25,10 +25,11 @@ The Initial Developer of the Original Code is Arnaud Bouchez. - Portions created by the Initial Developer are Copyright (C) 2018 + Portions created by the Initial Developer are Copyright (C) 2019 the Initial Developer. All Rights Reserved. Contributor(s): + - Alan Chate - Aleksandr (sha) - Alfred Glaenzer (alf) - ASiwon @@ -74,13 +75,9 @@ interface uses -{$ifndef LVCL} -{$ifndef FPC} -{$ifndef HASFASTMM4} +{$ifdef WITH_FASTMM4STATS} FastMM4, {$endif} -{$endif} -{$endif} {$ifdef MSWINDOWS} Windows, Messages, @@ -88,26 +85,26 @@ interface Registry, {$endif} {$else MSWINDOWS} -{$ifdef KYLIX3} - Types, - LibC, - SynKylix, -{$endif} -{$ifdef FPC} - BaseUnix, -{$endif} + {$ifdef KYLIX3} + Types, + LibC, + SynKylix, + {$endif KYLIX3} + {$ifdef FPC} + BaseUnix, + {$endif FPC} {$endif MSWINDOWS} Classes, {$ifndef LVCL} SyncObjs, // for TEvent and TCriticalSection Contnrs, // for TObjectList -{$ifdef HASINLINE} - Types, -{$endif} -{$endif} + {$ifdef HASINLINE} + Types, + {$endif HASINLINE} +{$endif LVCL} {$ifndef NOVARIANTS} Variants, -{$endif} +{$endif NOVARIANTS} SynLZ, // needed for TSynMapFile .mab format SysUtils; @@ -121,9 +118,15 @@ interface /// a text including the version and the main active conditional options // - usefull for low-level debugging purpose SYNOPSE_FRAMEWORK_FULLVERSION = SYNOPSE_FRAMEWORK_VERSION - {$ifndef FPC} + {$ifdef FPC} + {$ifdef FPC_FASTMM4}+' FMM4'{$else} + {$ifdef FPC_SYNTBB}+' TBB'{$else} + {$ifdef FPC_SYNJEMALLOC}+' JM'{$else} + {$ifdef FPC_SYNCMEM}+' GM'{$else} + {$ifdef FPC_CMEM}+' CM'{$endif}{$endif}{$endif}{$endif}{$endif} + {$else} {$ifdef LVCL}+' LVCL'{$else} - {$ifdef ENHANCEDRTL}+' ERTL'{$endif}{$endif} + {$ifdef ENHANCEDRTL}+' ERTL'{$endif}{$endif} {$ifdef DOPATCHTRTL}+' PRTL'{$endif} {$ifdef FullDebugMode}+' FDM'{$endif} {$endif FPC}; @@ -170,17 +173,17 @@ interface PtrUInt = NativeUInt; {$else} /// a CPU-dependent signed integer type cast of a pointer / register - // - used for 64 bits compatibility, native under Free Pascal Compiler + // - used for 64-bit compatibility, native under Free Pascal Compiler PtrInt = integer; /// a CPU-dependent unsigned integer type cast of a pointer / register - // - used for 64 bits compatibility, native under Free Pascal Compiler + // - used for 64-bit compatibility, native under Free Pascal Compiler PtrUInt = cardinal; {$endif} /// a CPU-dependent unsigned integer type cast of a pointer of pointer - // - used for 64 bits compatibility, native under Free Pascal Compiler + // - used for 64-bit compatibility, native under Free Pascal Compiler PPtrUInt = ^PtrUInt; /// a CPU-dependent signed integer type cast of a pointer of pointer - // - used for 64 bits compatibility, native under Free Pascal Compiler + // - used for 64-bit compatibility, native under Free Pascal Compiler PPtrInt = ^PtrInt; /// unsigned Int64 doesn't exist under older Delphi, but is defined in FPC @@ -724,9 +727,10 @@ TSynAnsiUTF8 = class(TSynAnsiConvert) procedure UTF8BufferToAnsi(Source: PUTF8Char; SourceChars: Cardinal; var result: RawByteString); override; /// convert any UTF-8 encoded String into Ansi Text - // - internaly calls UTF8BufferToAnsi virtual method + // - directly assign the input as result, since no conversion is needed function UTF8ToAnsi(const UTF8: RawUTF8): RawByteString; override; /// convert any Ansi Text into an UTF-8 encoded String + // - directly assign the input as result, since no conversion is needed function AnsiToUTF8(const AnsiText: RawByteString): RawUTF8; override; /// direct conversion of a PAnsiChar buffer into a UTF-8 encoded string function AnsiBufferToRawUTF8(Source: PAnsiChar; SourceChars: Cardinal): RawUTF8; override; @@ -771,7 +775,10 @@ TSynAnsiUTF16 = class(TSynAnsiConvert) // - could be used e.g. to make a temporary copy when JSON is parsed in-place // - call one of the Init() overloaded methods, then Done to release its memory // - will avoid temporary memory allocation via the heap for up to 4KB of data - {$ifdef UNICODE}TSynTempBuffer = record{$else}TSynTempBuffer = object{$endif} + // - all Init() methods will allocate 16 more bytes, for a trailing #0 and + // to ensure our fast JSON parsing won't trigger any GPF (since it may read + // up to 4 bytes ahead via its PInteger() trick) or any SSE4.2 function + {$ifdef FPC_OR_UNICODE}TSynTempBuffer = record{$else}TSynTempBuffer = object{$endif} public /// the text/binary length, in bytes, excluding the trailing #0 len: integer; @@ -786,7 +793,7 @@ {$ifdef UNICODE}TSynTempBuffer = record{$else}TSynTempBuffer = object{$endif} /// initialize a temporary copy of the supplied text buffer procedure Init(Source: pointer; SourceLen: integer); overload; /// initialize a new temporary buffer of a given number of bytes - function Init(SourceLen: integer): pointer; overload; + function Init(SourceLen: integer): pointer; overload; {$ifdef HASINLINE}inline;{$endif} /// initialize the buffer returning the internal buffer size (4095 bytes) // - could be used e.g. for an API call, first trying with plain temp.Init // and using temp.buf and temp.len safely in the call, only calling @@ -795,7 +802,7 @@ {$ifdef UNICODE}TSynTempBuffer = record{$else}TSynTempBuffer = object{$endif} function Init: integer; overload; {$ifdef HASINLINE}inline;{$endif} /// initialize a new temporary buffer of a given number of random bytes // - will fill the buffer via FillRandom() calls - function InitRandom(RandomLen: integer): pointer; + function InitRandom(RandomLen: integer; forcegsl: boolean=true): pointer; /// initialize a new temporary buffer filled with integer increasing values function InitIncreasing(Count: integer; Start: integer=0): PIntegerArray; /// initialize a new temporary buffer of a given number of zero bytes @@ -811,8 +818,8 @@ {$ifdef UNICODE}TSynTempBuffer = record{$else}TSynTempBuffer = object{$endif} /// implements a stack-based writable storage of binary content // - memory allocation is performed via a TSynTempBuffer - {$ifdef UNICODE}TSynTempWriter = record{$else}TSynTempWriter = object{$endif} - private + {$ifdef FPC_OR_UNICODE}TSynTempWriter = record private + {$else}TSynTempWriter = object protected{$endif} tmp: TSynTempBuffer; public /// the current writable position in tmp.buf @@ -950,6 +957,11 @@ {$ifdef UNICODE}TSynTempWriter = record{$else}TSynTempWriter = object{$endif} /// can be used to avoid a memory allocation for res := 'null' NULL_STR_VAR: RawUTF8; +/// compute the new capacity when expanding an array of items +// - handle small, medium and large sizes properly to reduce memory usage and +// maximize performance +function NextGrow(capacity: integer): integer; + /// equivalence to SetString(s,nil,len) function // - faster especially under FPC procedure FastSetString(var s: RawUTF8; p: pointer; len: PtrInt); @@ -958,6 +970,11 @@ procedure FastSetString(var s: RawUTF8; p: pointer; len: PtrInt); // - faster especially under FPC procedure FastSetStringCP(var s; p: pointer; len, codepage: PtrInt); +/// initialize a RawByteString, ensuring returned "aligned" pointer is 16-bytes aligned +// - to be used e.g. for proper SSE process +procedure GetMemAligned(var s: RawByteString; p: pointer; len: PtrInt; + out aligned: pointer); + /// equivalence to @UTF8[1] expression to ensure a RawUTF8 variable is unique // - will ensure that the string refcount is 1, and return a pointer to the text // - under FPC, @UTF8[1] does not call UniqueString() as it does with Delphi @@ -1140,7 +1157,16 @@ function UTF8ToWideChar(dest: PWideChar; source: PUTF8Char; function Utf8ToUnicodeLength(source: PUTF8Char): PtrUInt; /// returns TRUE if the supplied buffer has valid UTF-8 encoding -function IsValidUTF8(source: PUTF8Char): Boolean; +// - will stop when the buffer contains #0 +function IsValidUTF8(source: PUTF8Char): Boolean; overload; + +/// returns TRUE if the supplied buffer has valid UTF-8 encoding +// - will also refuse #0 characters within the buffer +function IsValidUTF8(source: PUTF8Char; sourcelen: PtrInt): Boolean; overload; + +/// returns TRUE if the supplied buffer has valid UTF-8 encoding +// - will also refuse #0 characters within the buffer +function IsValidUTF8(const source: RawUTF8): Boolean; overload; /// returns TRUE if the supplied buffer has valid UTF-8 encoding with no #1..#31 // control characters @@ -1221,8 +1247,7 @@ function RawUnicodeToUtf8(WideChar: PWideChar; WideCharCount: integer; /// convert a RawUnicode UTF-16 PWideChar into a UTF-8 buffer // - replace system.UnicodeToUtf8 implementation, which is rather slow // since Delphi 2009+ -// - will append a trailing #0 to the ending PUTF8Char, unless -// ccfNoTrailingZero is set +// - append a trailing #0 to the ending PUTF8Char, unless ccfNoTrailingZero is set // - if ccfReplacementCharacterForUnmatchedSurrogate is set, this function will identify // unmatched surrogate pairs and replace them with EF BF BD / FFFD Unicode // Replacement character - see https://en.wikipedia.org/wiki/Specials_(Unicode_block) @@ -1355,6 +1380,14 @@ function ToUTF8({$IFDEF FPC_HAS_CONSTREF}constref{$ELSE}const{$ENDIF} guid: TGUI // - used in mORMot.pas unit e.g. by TDocVariantData.SortByValue TVariantCompare = function(const V1,V2: variant): PtrInt; +/// TVariantCompare-compatible case-sensitive comparison function +// - just a wrapper around SortDynArrayVariantComp(caseInsensitive=false) +function VariantCompare(const V1,V2: variant): PtrInt; + +/// TVariantCompare-compatible case-insensitive comparison function +// - just a wrapper around SortDynArrayVariantComp(caseInsensitive=true) +function VariantCompareI(const V1,V2: variant): PtrInt; + /// convert any Variant into UTF-8 encoded String // - use VariantSaveJSON() instead if you need a conversion to JSON with // custom parameters @@ -1371,7 +1404,8 @@ function ToUTF8(const V: Variant): RawUTF8; overload; // - use VariantSaveJSON() instead if you need a conversion to JSON with // custom parameters // - wasString is set if the V value was a text -// - custom variant types will be stored as JSON +// - empty and null variants will be stored as 'null' text - as expected by JSON +// - custom variant types (e.g. TDocVariant) will be stored as JSON procedure VariantToUTF8(const V: Variant; var result: RawUTF8; var wasString: boolean); overload; @@ -1379,7 +1413,8 @@ procedure VariantToUTF8(const V: Variant; var result: RawUTF8; // - use VariantSaveJSON() instead if you need a conversion to JSON with // custom parameters // - returns TRUE if the V value was a text, FALSE if was not (e.g. a number) -// - custom variant types will be stored as JSON +// - empty and null variants will be stored as 'null' text - as expected by JSON +// - custom variant types (e.g. TDocVariant) will be stored as JSON function VariantToUTF8(const V: Variant; var Text: RawUTF8): boolean; overload; /// convert any date/time Variant into a TDateTime value @@ -1419,7 +1454,7 @@ procedure VariantToInlineValue(const V: Variant; var result: RawUTF8); function VariantToVariantUTF8(const V: Variant): variant; /// faster alternative to Finalize(aVariantDynArray) -// - this function will take in account and optimize the release of a dynamic +// - this function will take account and optimize the release of a dynamic // array of custom variant types values // - for instance, an array of TDocVariant will be optimized for speed procedure VariantDynArrayClear(var Value: TVariantDynArray); @@ -1435,19 +1470,19 @@ function VariantHash(const value: variant; CaseInsensitive: boolean; { note: those VariantToInteger*() functions are expected to be there } -/// convert any numerical Variant into a 32 bit integer +/// convert any numerical Variant into a 32-bit integer // - it will expect true numerical Variant and won't convert any string nor // floating-pointer Variant, which will return FALSE and won't change the // Value variable content function VariantToInteger(const V: Variant; var Value: integer): boolean; -/// convert any numerical Variant into a 64 bit integer +/// convert any numerical Variant into a 64-bit integer // - it will expect true numerical Variant and won't convert any string nor // floating-pointer Variant, which will return FALSE and won't change the // Value variable content function VariantToInt64(const V: Variant; var Value: Int64): boolean; -/// convert any numerical Variant into a 64 bit integer +/// convert any numerical Variant into a 64-bit integer // - it will expect true numerical Variant and won't convert any string nor // floating-pointer Variant, which will return the supplied DefaultValue function VariantToInt64Def(const V: Variant; DefaultValue: Int64): Int64; @@ -1613,6 +1648,9 @@ procedure FormatUTF8(const Format: RawUTF8; const Args: array of const; procedure FormatShort(const Format: RawUTF8; const Args: array of const; var result: shortstring); +/// fast Format() function replacement, for UTF-8 content stored in shortstring +function FormatToShort(const Format: RawUTF8; const Args: array of const): shortstring; + /// fast Format() function replacement, tuned for small content // - use the same single token % (and implementation) than FormatUTF8() procedure FormatString(const Format: RawUTF8; const Args: array of const; @@ -1628,6 +1666,7 @@ function FormatString(const Format: RawUTF8; const Args: array of const): string // - such result type would avoid a string allocation on heap, so are highly // recommended e.g. when logging small pieces of information TShort16 = string[16]; + PShort16 = ^TShort16; /// fast Format() function replacement, for UTF-8 content stored in TShort16 // - truncate result if the text size exceeds 16 bytes @@ -1651,7 +1690,8 @@ function FormatUTF8(const Format: RawUTF8; const Args, Params: array of const; /// read and store text into values[] according to fmt specifiers // - %d as PInteger, %D as PInt64, %u as PCardinal, %U as PQWord, %f as PDouble, // %F as PCurrency, %x as 8 hexa chars to PInteger, %X as 16 hexa chars to PInt64, -// %s as PShortString (UTF-8 encoded) +// %s as PShortString (UTF-8 encoded), %S as PRawUTF8, %L as PRawUTF8 (getting +// all text until the end of the line) // - optionally, specifiers and any whitespace separated identifiers may be // extracted and stored into the ident[] array, e.g. '%dFirstInt %s %DOneInt64' // will store ['dFirstInt','s','DOneInt64'] into ident @@ -1732,18 +1772,18 @@ function VarRecAsChar(const V: TVarRec): integer; // - used in mORMot.pas unit during TSQLTable rows sort and by TSQLQuery TUTF8Compare = function(P1,P2: PUTF8Char): PtrInt; -/// convert the endianness of a given unsigned 32 bit integer into BigEndian +/// convert the endianness of a given unsigned 32-bit integer into BigEndian function bswap32(a: cardinal): cardinal; {$ifdef FPC}inline;{$endif} -/// convert the endianness of a given unsigned 64 bit integer into BigEndian +/// convert the endianness of a given unsigned 64-bit integer into BigEndian function bswap64(const a: QWord): QWord; {$ifdef FPC}inline;{$endif} -/// convert the endianness of an array of unsigned 64 bit integer into BigEndian +/// convert the endianness of an array of unsigned 64-bit integer into BigEndian // - n is required to be > 0 // - warning: on x86, a should be <> b -procedure bswap64array(a,b: PQWordArray; n: integer); +procedure bswap64array(a,b: PQWordArray; n: PtrInt); /// fast concatenation of several AnsiStrings function RawByteStringArrayConcat(const Values: array of RawByteString): RawByteString; @@ -1798,7 +1838,20 @@ function CompareMemFixed(P1, P2: Pointer; Length: PtrInt): Boolean; inline; function CompareMemSmall(P1, P2: Pointer; Length: PtrInt): Boolean; {$ifdef HASINLINE}inline;{$endif} /// convert an IPv4 'x.x.x.x' text into its 32-bit value -function IPToCardinal(const aIP: RawUTF8; out aValue: cardinal): boolean; +// - returns TRUE if the text was a valid IPv4 text, FALSE on parsing error +// - '' or '127.0.0.1' will also return false +function IPToCardinal(const aIP: RawUTF8; out aValue: cardinal): boolean; overload; + +/// convert an IPv4 'x.x.x.x' text into its 32-bit value +// - returns TRUE if the text was a valid IPv4 text, FALSE on parsing error +// - '' or '127.0.0.1' will also return false +function IPToCardinal(P: PUTF8Char; out aValue: cardinal): boolean; overload; + +/// convert an IPv4 'x.x.x.x' text into its 32-bit value, 0 or localhost +// - returns <> 0 value if the text was a valid IPv4 text, 0 on parsing error +// - '' or '127.0.0.1' will also return 0 +function IPToCardinal(const aIP: RawUTF8): cardinal; overload; + {$ifdef HASINLINE}inline;{$endif} /// convert some ASCII-7 text into binary, using Emile Baudot code // - as used in telegraphs, covering a-z 0-9 - ' , ! : ( + ) $ ? @ . / ; charset @@ -1854,13 +1907,13 @@ procedure UInt64ToUtf8(Value: QWord; var result: RawUTF8); /// use our fast RawUTF8 version of IntToStr() // - without any slow UnicodeString=String->AnsiString conversion for Delphi 2009 // - only useful if our Enhanced Runtime (or LVCL) library is not installed -function Int32ToUtf8(Value: integer): RawUTF8; overload; +function Int32ToUtf8(Value: PtrInt): RawUTF8; overload; {$ifdef PUREPASCAL}{$ifdef HASINLINE}inline;{$endif}{$endif} /// use our fast RawUTF8 version of IntToStr() // - without any slow UnicodeString=String->AnsiString conversion for Delphi 2009 // - result as var parameter saves a local assignment and a try..finally -procedure Int32ToUTF8(Value: integer; var result: RawUTF8); overload; +procedure Int32ToUTF8(Value: PtrInt; var result: RawUTF8); overload; {$ifdef HASINLINE}inline;{$endif} /// use our fast RawUTF8 version of IntToStr() @@ -1880,11 +1933,11 @@ function ToUTF8(Value: Int64): RawUTF8; overload; {$endif} /// optimized conversion of a cardinal into RawUTF8 -function UInt32ToUtf8(Value: cardinal): RawUTF8; overload; +function UInt32ToUtf8(Value: PtrUInt): RawUTF8; overload; {$ifdef HASINLINE}inline;{$endif} /// optimized conversion of a cardinal into RawUTF8 -procedure UInt32ToUtf8(Value: cardinal; var result: RawUTF8); overload; +procedure UInt32ToUtf8(Value: PtrUInt; var result: RawUTF8); overload; {$ifdef HASINLINE}inline;{$endif} /// faster version than default SysUtils.IntToStr implementation @@ -1909,7 +1962,7 @@ function Curr64ToString(Value: Int64): string; TSynAnsicharSet = set of AnsiChar; /// used to store a set of 8-bit unsigned integers TSynByteSet = set of Byte; - /// used to store a set of 8-bit unsigned integers as 256 bytes + /// used to store a set of 8-bit unsigned integers as 256 booleans TSynByteBoolean = array[byte] of boolean; /// returns the supplied text content, without any control char @@ -1971,6 +2024,29 @@ function SameValue(const A, B: Double; DoublePrec: double = 1E-12): Boolean; // - if you know the precision range of A and B, it's faster to check abs(A-B)QWord(B) is wrong on older versions of Delphi, so you +// should better use this function or SortDynArrayQWord() to properly compare +// two QWord values over CPUX86 +function CompareQWord(A, B: QWord): integer; + {$ifdef HASINLINE}inline;{$endif} + /// compute the sum of values, using a running compensation for lost low-order bits // - a naive "Sum := Sum + Data" will be restricted to 53 bits of resolution, // so will eventually result in an incorrect number @@ -2015,6 +2091,7 @@ procedure ExtendedToStr(Value: TSynExtended; Precision: integer; var result: Raw /// convert a floating-point value to its numerical text equivalency function DoubleToStr(Value: Double): RawUTF8; + {$ifdef HASINLINE}inline;{$endif} /// fast retrieve the position of a given character function PosChar(Str: PUTF8Char; Chr: AnsiChar): PUTF8Char; @@ -2042,19 +2119,21 @@ function PosIU(substr: PUTF8Char; const str: RawUTF8): Integer; // - expect the last available temporary char position in P // - return the last written char position (write in reverse order in P^) // - typical use: -// !function Int32ToUTF8(Value : integer): RawUTF8; -// !var tmp: array[0..15] of AnsiChar; +// !function Int32ToUTF8(Value: PtrInt): RawUTF8; +// !var tmp: array[0..23] of AnsiChar; // ! P: PAnsiChar; // !begin -// ! P := StrInt32(@tmp[15],Value); -// ! SetString(result,P,@tmp[15]-P); +// ! P := StrInt32(@tmp[23],Value); +// ! SetString(result,P,@tmp[23]-P); // !end; -// - not to be called directly: use IntToStr() instead +// - convert the input value as PtrInt, so as Int64 on 64-bit CPUs +// - not to be called directly: use IntToStr() or Int32ToUTF8() instead function StrInt32(P: PAnsiChar; val: PtrInt): PAnsiChar; /// internal fast unsigned integer val to text conversion // - expect the last available temporary char position in P // - return the last written char position (write in reverse order in P^) +// - convert the input value as PtrUInt, so as QWord on 64-bit CPUs function StrUInt32(P: PAnsiChar; val: PtrUInt): PAnsiChar; /// internal fast Int64 val to text conversion @@ -2084,7 +2163,7 @@ procedure AppendBuffersToRawUTF8(var Text: RawUTF8; const Buffers: array of PUTF // you may encounter buffer overflows and random memory errors function AppendRawUTF8ToBuffer(Buffer: PUTF8Char; const Text: RawUTF8): PUTF8Char; -/// fast add text conversion of a 32 bit signed integer value into a given buffer +/// fast add text conversion of a 32-bit signed integer value into a given buffer // - warning: the Buffer should contain enough space to store the text, otherwise // you may encounter buffer overflows and random memory errors function AppendUInt32ToBuffer(Buffer: PUTF8Char; Value: cardinal): PUTF8Char; @@ -2094,24 +2173,14 @@ function AppendUInt32ToBuffer(Buffer: PUTF8Char; Value: cardinal): PUTF8Char; // - pure pascal StrComp() won't access the memory beyond the string, but this // function is defined for compatibility with SSE 4.2 expectations function StrCompFast(Str1, Str2: pointer): PtrInt; + {$ifdef PUREPASCAL}{$ifdef HASINLINE}inline;{$endif}{$endif} /// fastest available version of StrComp(), to be used with PUTF8Char/PAnsiChar -// - will use SSE4.2 instructions on supported CPUs - and potentiall read up -// to 15 bytes beyond the string: use StrCompFast() for a safe memory read; -// if you want to disable StrCompSSE42 for your whole project, add in the -// initialization section of one of your units: -// ! StrComp := @StrCompFast; +// - won't use SSE4.2 instructions on supported CPUs by default, which may read +// some bytes beyond the s string, so should be avoided e.g. over memory mapped +// files - call explicitely StrCompSSE42() if you are confident on your input var StrComp: function (Str1, Str2: pointer): PtrInt = StrCompFast; -{$ifndef PUREPASCAL} -/// SSE 4.2 version of StrComp(), to be used with PUTF8Char/PAnsiChar -// - please note that this optimized version may read up to 15 bytes -// beyond the string; this is rarely a problem but it may in principle -// generate a protection violation (e.g. when used over memory mapped files) - -// you can use the slightly slower but safe StrCompFast() function instead -function StrCompSSE42(Str1, Str2: pointer): PtrInt; -{$endif PUREPASCAL} - /// pure pascal version of strspn(), to be used with PUTF8Char/PAnsiChar // - please note that this optimized version may read up to 3 bytes beyond // accept but never after s end, so is safe e.g. over memory mapped files @@ -2124,33 +2193,58 @@ function strspnpas(s,accept: pointer): integer; function strcspnpas(s,reject: pointer): integer; {$ifdef HASINLINE}inline;{$endif} +/// fastest available version of strspn(), to be used with PUTF8Char/PAnsiChar +// - returns size of initial segment of s which appears in accept chars, e.g. +// ! strspn('abcdef','debca')=5 +// - won't use SSE4.2 instructions on supported CPUs by default, which may read +// some bytes beyond the s string, so should be avoided e.g. over memory mapped +// files - call explicitely strspnsse42() if you are confident on your input +var strspn: function (s,accept: pointer): integer = strspnpas; + +/// fastest available version of strcspn(), to be used with PUTF8Char/PAnsiChar +// - returns size of initial segment of s which doesn't appears in reject chars, e.g. +// ! strcspn('1234,6789',',')=4 +// - won't use SSE4.2 instructions on supported CPUs by default, which may read +// some bytes beyond the s string, so should be avoided e.g. over memory mapped +// files - call explicitely strcspnsse42() if you are confident on your input +var strcspn: function (s,reject: pointer): integer = strcspnpas; + +{$ifndef ABSOLUTEPASCAL} {$ifdef CPUINTEL} +{$ifdef HASAESNI} +/// SSE 4.2 version of StrComp(), to be used with PUTF8Char/PAnsiChar +// - please note that this optimized version may read up to 15 bytes +// beyond the string; this is rarely a problem but it may generate protection +// violations, which could trigger fatal SIGABRT or SIGSEGV on Posix system +// - could be used instead of StrComp() when you are confident about your +// Str1/Str2 input buffers, checking if cfSSE42 in CpuFeatures +function StrCompSSE42(Str1, Str2: pointer): PtrInt; + +// - please note that this optimized version may read up to 15 bytes +// beyond the string; this is rarely a problem but it may generate protection +// violations, which could trigger fatal SIGABRT or SIGSEGV on Posix system +// - could be used instead of StrLen() when you are confident about your +// S input buffers, checking if cfSSE42 in CpuFeatures +function StrLenSSE42(S: pointer): PtrInt; +{$endif HASAESNI} + /// SSE 4.2 version of strspn(), to be used with PUTF8Char/PAnsiChar // - please note that this optimized version may read up to 15 bytes -// beyond the string, so should be avoided e.g. over memory mapped files +// beyond the string; this is rarely a problem but it may generate protection +// violations, which could trigger fatal SIGABRT or SIGSEGV on Posix system +// - could be used instead of strspn() when you are confident about your +// s/accept input buffers, checking if cfSSE42 in CpuFeatures function strspnsse42(s,accept: pointer): integer; /// SSE 4.2 version of strcspn(), to be used with PUTF8Char/PAnsiChar // - please note that this optimized version may read up to 15 bytes -// beyond the string, so should be avoided e.g. over memory mapped files +// beyond the string; this is rarely a problem but it may generate protection +// violations, which could trigger fatal SIGABRT or SIGSEGV on Posix system +// - could be used instead of strcspn() when you are confident about your +// s/reject input buffers, checking if cfSSE42 in CpuFeatures function strcspnsse42(s,reject: pointer): integer; -{$endif} - -/// fastest available version of strspn(), to be used with PUTF8Char/PAnsiChar -// - returns how many accept chars appear in the initial segment of s, e.g. -// ! strspn('abcdef','debca')=5 -// - will use SSE4.2 instructions on supported CPUs -// - please note that this function may read some bytes beyond the s string, so -// should be avoided e.g. over memory mapped files - use safe strspnpas instead -var strspn: function (s,accept: pointer): integer = strspnpas; - -/// fastest available version of strcspn(), to be used with PUTF8Char/PAnsiChar -// - returns how many reject chars do not appear in the initial segment of s, e.g. -// ! strcspn('1234,6789',',')=4 -// - will use SSE4.2 instructions on supported CPUs -// - please note that this function may read some bytes beyond the s string, so -// should be avoided e.g. over memory mapped files - use safe strcspnpas instead -var strcspn: function (s,reject: pointer): integer = strcspnpas; +{$endif CPUINTEL} +{$endif ABSOLUTEPASCAL} /// use our fast version of StrIComp(), to be used with PUTF8Char/PAnsiChar function StrIComp(Str1, Str2: pointer): PtrInt; @@ -2163,14 +2257,9 @@ function StrIComp(Str1, Str2: pointer): PtrInt; function StrLenPas(S: pointer): PtrInt; /// our fast version of StrLen(), to be used with PUTF8Char/PAnsiChar -// - this version will use fast SSE2/SSE4.2 instructions (if available), on both -// Win32 and Win64 platforms: please note that in this case, it may read up to -// 15 bytes before or beyond the string; this is rarely a problem but it can in -// principle generate a protection violation (e.g. when used over memory mapped files): -// you can use the slightly slower StrLenPas() function instead with such input; -// if you want to disable StrLenSSE42 for your whole project, add in the -// initialization section of one of your units: -// ! StrLen := @StrLenPas; +// - won't use SSE4.2 instructions on supported CPUs by default, which may read +// some bytes beyond the s string, so should be avoided e.g. over memory mapped +// files - call explicitely StrLenSSE42() if you are confident on your input var StrLen: function(S: pointer): PtrInt = StrLenPas; /// our fast version of FillChar() @@ -2304,11 +2393,11 @@ function GetQWord(P: PUTF8Char; var err: integer): QWord; // - set the err content to the index of any faulty character, 0 if conversion // was successful (same as the standard val function) function GetExtended(P: PUTF8Char; out err: integer): TSynExtended; overload; - {$ifdef FPC}inline;{$endif} // under Delphi XE4 64-bit compiler, it fails... /// get the extended floating point value stored in P^ // - this overloaded version returns 0 as a result if the content of P is invalid function GetExtended(P: PUTF8Char): TSynExtended; overload; + {$ifdef HASINLINE}inline;{$endif} /// get the WideChar stored in P^ (decode UTF-8 if necessary) // - any surrogate (UCS4>$ffff) will be returned as '?' @@ -2385,7 +2474,7 @@ function UrlDecode(U: PUTF8Char): RawUTF8; overload; // will return Next^='where=...' and V='*' // - if Upper is not found, Value is not modified, and result is FALSE // - if Upper is found, Value is modified with the supplied content, and result is TRUE -function UrlDecodeValue(U: PUTF8Char; Upper: PAnsiChar; var Value: RawUTF8; +function UrlDecodeValue(U: PUTF8Char; const Upper: RawUTF8; var Value: RawUTF8; Next: PPUTF8Char=nil): boolean; /// decode a specified parameter compatible with URI encoding into its original @@ -2394,7 +2483,7 @@ function UrlDecodeValue(U: PUTF8Char; Upper: PAnsiChar; var Value: RawUTF8; // will return Next^='where=...' and O=20 // - if Upper is not found, Value is not modified, and result is FALSE // - if Upper is found, Value is modified with the supplied content, and result is TRUE -function UrlDecodeInteger(U: PUTF8Char; Upper: PAnsiChar;var Value: integer; +function UrlDecodeInteger(U: PUTF8Char; const Upper: RawUTF8; var Value: integer; Next: PPUTF8Char=nil): boolean; /// decode a specified parameter compatible with URI encoding into its original @@ -2403,7 +2492,7 @@ function UrlDecodeInteger(U: PUTF8Char; Upper: PAnsiChar;var Value: integer; // will return Next^='where=...' and O=20 // - if Upper is not found, Value is not modified, and result is FALSE // - if Upper is found, Value is modified with the supplied content, and result is TRUE -function UrlDecodeCardinal(U: PUTF8Char; Upper: PAnsiChar;var Value: Cardinal; +function UrlDecodeCardinal(U: PUTF8Char; const Upper: RawUTF8; var Value: Cardinal; Next: PPUTF8Char=nil): boolean; /// decode a specified parameter compatible with URI encoding into its original @@ -2412,7 +2501,7 @@ function UrlDecodeCardinal(U: PUTF8Char; Upper: PAnsiChar;var Value: Cardinal; // will return Next^='where=...' and O=20 // - if Upper is not found, Value is not modified, and result is FALSE // - if Upper is found, Value is modified with the supplied content, and result is TRUE -function UrlDecodeInt64(U: PUTF8Char; Upper: PAnsiChar;var Value: Int64; +function UrlDecodeInt64(U: PUTF8Char; const Upper: RawUTF8; var Value: Int64; Next: PPUTF8Char=nil): boolean; /// decode a specified parameter compatible with URI encoding into its original @@ -2421,7 +2510,7 @@ function UrlDecodeInt64(U: PUTF8Char; Upper: PAnsiChar;var Value: Int64; // will return Next^='where=...' and P=20.45 // - if Upper is not found, Value is not modified, and result is FALSE // - if Upper is found, Value is modified with the supplied content, and result is TRUE -function UrlDecodeExtended(U: PUTF8Char; Upper: PAnsiChar; var Value: TSynExtended; +function UrlDecodeExtended(U: PUTF8Char; const Upper: RawUTF8; var Value: TSynExtended; Next: PPUTF8Char=nil): boolean; /// decode a specified parameter compatible with URI encoding into its original @@ -2430,7 +2519,7 @@ function UrlDecodeExtended(U: PUTF8Char; Upper: PAnsiChar; var Value: TSynExtend // will return Next^='where=...' and P=20.45 // - if Upper is not found, Value is not modified, and result is FALSE // - if Upper is found, Value is modified with the supplied content, and result is TRUE -function UrlDecodeDouble(U: PUTF8Char; Upper: PAnsiChar; var Value: double; +function UrlDecodeDouble(U: PUTF8Char; const Upper: RawUTF8; var Value: double; Next: PPUTF8Char=nil): boolean; /// returns TRUE if all supplied parameters do exist in the URI encoded text @@ -2506,30 +2595,30 @@ function JsonPropNameValid(P: PUTF8Char): boolean; // http://www.ietf.org/rfc/rfc4627.txt function NeedsJsonEscape(const Text: RawUTF8): boolean; -/// case unsensitive test of P1 and P2 content +/// case insensitive comparison of ASCII identifiers // - use it with property names values (i.e. only including A..Z,0..9,_ chars) function IdemPropName(const P1,P2: shortstring): boolean; overload; {$ifdef HASINLINE}inline;{$endif} -/// case unsensitive test of P1 and P2 content +/// case insensitive comparison of ASCII identifiers // - use it with property names values (i.e. only including A..Z,0..9,_ chars) // - this version expects P2 to be a PAnsiChar with a specified length function IdemPropName(const P1: shortstring; P2: PUTF8Char; P2Len: PtrInt): boolean; overload; {$ifdef HASINLINE}inline;{$endif} -/// case unsensitive test of P1 and P2 content +/// case insensitive comparison of ASCII identifiers // - use it with property names values (i.e. only including A..Z,0..9,_ chars) // - this version expects P1 and P2 to be a PAnsiChar with specified lengths function IdemPropName(P1,P2: PUTF8Char; P1Len,P2Len: PtrInt): boolean; overload; {$ifdef HASINLINE}inline;{$endif} -/// case unsensitive test of P1 and P2 content +/// case insensitive comparison of ASCII identifiers // - use it with property names values (i.e. only including A..Z,0..9,_ chars) // - this version expects P2 to be a PAnsiChar with specified length function IdemPropNameU(const P1: RawUTF8; P2: PUTF8Char; P2Len: PtrInt): boolean; overload; {$ifdef HASINLINE}inline;{$endif} -/// case unsensitive test of P1 and P2 content of same length +/// case insensitive comparison of ASCII identifiers of same length // - use it with property names values (i.e. only including A..Z,0..9,_ chars) // - this version expects P1 and P2 to be a PAnsiChar with an already checked // identical length, so may be used for a faster process, e.g. in a loop @@ -2539,7 +2628,7 @@ function IdemPropNameU(const P1: RawUTF8; P2: PUTF8Char; P2Len: PtrInt): boolean function IdemPropNameUSameLen(P1,P2: PUTF8Char; P1P2Len: PtrInt): boolean; {$ifndef ANDROID}{$ifdef PUREPASCAL}{$ifdef HASINLINE}inline;{$endif}{$endif}{$endif} -/// case unsensitive test of P1 and P2 content +/// case insensitive comparison of ASCII identifiers // - use it with property names values (i.e. only including A..Z,0..9,_ chars) function IdemPropNameU(const P1,P2: RawUTF8): boolean; overload; {$ifdef PUREPASCAL}{$ifdef HASINLINE}inline;{$endif}{$endif} @@ -2654,11 +2743,12 @@ function UpperCopy255(dest: PAnsiChar; const source: RawUTF8): PAnsiChar; overlo /// copy source^ into a 256 chars dest^ buffer with 7 bits upper case conversion // - used internally for short keys match or case-insensitive hash -// - will use SSE4.2 instructions on supported CPUs - and potentiall read up -// to 15 bytes beyond the string: use UpperCopy255BufPas() for a safer memory read // - returns final dest pointer // - will copy up to 255 AnsiChar (expect the dest buffer to be defined e.g. as // array[byte] of AnsiChar on the caller stack) +// - won't use SSE4.2 instructions on supported CPUs by default, which may read +// some bytes beyond the s string, so should be avoided e.g. over memory mapped +// files - call explicitely UpperCopy255BufSSE42() if you are confident on your input var UpperCopy255Buf: function(dest: PAnsiChar; source: PUTF8Char; sourceLen: PtrInt): PAnsiChar; /// copy source^ into a 256 chars dest^ buffer with 7 bits upper case conversion @@ -2672,19 +2762,16 @@ function UpperCopy255BufPas(dest: PAnsiChar; source: PUTF8Char; sourceLen: PtrIn {$ifndef PUREPASCAL} {$ifndef DELPHI5OROLDER} - -/// copy source^ into a 256 chars dest^ buffer with 7 bits upper case conversion -// - used internally for short keys match or case-insensitive hash -// - this version will use SSE4.2 instructions on supported CPUs - and potentiall -// read up to 15 bytes beyond the string -// - you should not have to call this function, but rely on UpperCopy255Buf() -// - returns final dest pointer -// - will copy up to 255 AnsiChar (expect the dest buffer to be defined e.g. as -// array[byte] of AnsiChar on the caller stack) +/// SSE 4.2 version of UpperCopy255Buf() +// - copy source^ into a 256 chars dest^ buffer with 7 bits upper case conversion +// - please note that this optimized version may read up to 15 bytes +// beyond the string; this is rarely a problem but it may generate protection +// violations, which could trigger fatal SIGABRT or SIGSEGV on Posix system +// - could be used instead of UpperCopy255Buf() when you are confident about your +// dest/source input buffers, checking if cfSSE42 in CpuFeatures function UpperCopy255BufSSE42(dest: PAnsiChar; source: PUTF8Char; sourceLen: PtrInt): PAnsiChar; - -{$endif} -{$endif} +{$endif DELPHI5OROLDER} +{$endif PUREPASCAL} /// copy source into dest^ with WinAnsi 8 bits upper case conversion // - used internally for short keys match or case-insensitive hash @@ -2785,39 +2872,47 @@ function ConvertCaseUTF8(P: PUTF8Char; const Table: TNormTableByte): PtrInt; {$endif USENORMTOUPPER} +/// check if the supplied text has some case-insentitive 'a'..'z','A'..'Z' chars +// - will therefore be correct with true UTF-8 content, but only for 7 bit +function IsCaseSensitive(const S: RawUTF8): boolean; overload; + +/// check if the supplied text has some case-insentitive 'a'..'z','A'..'Z' chars +// - will therefore be correct with true UTF-8 content, but only for 7 bit +function IsCaseSensitive(P: PUTF8Char; PLen: integer): boolean; overload; + /// fast conversion of the supplied text into uppercase // - this will only convert 'a'..'z' into 'A'..'Z' (no NormToUpper use), and -// will therefore by correct with true UTF-8 content, but only for 7 bit +// will therefore be correct with true UTF-8 content, but only for 7 bit function UpperCase(const S: RawUTF8): RawUTF8; /// fast conversion of the supplied text into uppercase // - this will only convert 'a'..'z' into 'A'..'Z' (no NormToUpper use), and -// will therefore by correct with true UTF-8 content, but only for 7 bit +// will therefore be correct with true UTF-8 content, but only for 7 bit procedure UpperCaseCopy(Text: PUTF8Char; Len: integer; var result: RawUTF8); overload; /// fast conversion of the supplied text into uppercase // - this will only convert 'a'..'z' into 'A'..'Z' (no NormToUpper use), and -// will therefore by correct with true UTF-8 content, but only for 7 bit +// will therefore be correct with true UTF-8 content, but only for 7 bit procedure UpperCaseCopy(const Source: RawUTF8; var Dest: RawUTF8); overload; /// fast in-place conversion of the supplied variable text into uppercase // - this will only convert 'a'..'z' into 'A'..'Z' (no NormToUpper use), and -// will therefore by correct with true UTF-8 content, but only for 7 bit +// will therefore be correct with true UTF-8 content, but only for 7 bit procedure UpperCaseSelf(var S: RawUTF8); /// fast conversion of the supplied text into lowercase // - this will only convert 'A'..'Z' into 'a'..'z' (no NormToLower use), and -// will therefore by correct with true UTF-8 content +// will therefore be correct with true UTF-8 content function LowerCase(const S: RawUTF8): RawUTF8; /// fast conversion of the supplied text into lowercase // - this will only convert 'A'..'Z' into 'a'..'z' (no NormToLower use), and -// will therefore by correct with true UTF-8 content +// will therefore be correct with true UTF-8 content procedure LowerCaseCopy(Text: PUTF8Char; Len: integer; var result: RawUTF8); /// fast in-place conversion of the supplied variable text into lowercase // - this will only convert 'A'..'Z' into 'a'..'z' (no NormToLower use), and -// will therefore by correct with true UTF-8 content, but only for 7 bit +// will therefore be correct with true UTF-8 content, but only for 7 bit procedure LowerCaseSelf(var S: RawUTF8); /// accurate conversion of the supplied UTF-8 content into the corresponding @@ -2871,10 +2966,8 @@ function FindIniEntryW(const Content: string; const Section, Name: RawUTF8): str {$ifdef PUREPASCAL} {$ifdef HASINLINE} -function PosEx(const SubStr, S: RawUTF8; Offset: PtrUInt=1): PtrInt; inline; -{$ifndef FPC} function PosExPas(pSub, p: PUTF8Char; Offset: PtrUInt): PtrInt; -{$endif} +function PosEx(const SubStr, S: RawUTF8; Offset: PtrUInt=1): PtrInt; inline; {$else} var PosEx: function(const SubStr, S: RawUTF8; Offset: PtrUInt=1): PtrInt; {$endif} @@ -2885,6 +2978,10 @@ function PosEx(const SubStr, S: RawUTF8; Offset: PtrUInt=1): integer; {$endif PUREPASCAL} +/// optimized version of PosEx() with search text as one AnsiChar +function PosExChar(Chr: AnsiChar; const Str: RawUTF8): PtrInt; + {$ifdef HASINLINE}inline;{$endif} + /// split a RawUTF8 string into two strings, according to SepStr separator // - if SepStr is not found, LeftStr=Str and RightStr='' // - if ToUpperCase is TRUE, then LeftStr and RightStr will be made uppercase @@ -2955,7 +3052,8 @@ procedure QuotedStr(Text: PUTF8Char; Quote: AnsiChar; var result: RawUTF8); over /// convert a buffered text content into a JSON string // - with proper escaping of the content, and surounding " characters -procedure QuotedStrJSON(const aText: RawUTF8; var result: RawUTF8); +procedure QuotedStrJSON(const aText: RawUTF8; var result: RawUTF8; + const aPrefix: RawUTF8=''; const aSuffix: RawUTF8=''); /// unquote a SQL-compatible string // - the first character in P^ must be either ', either " then double quotes @@ -3031,8 +3129,16 @@ function IdemPCharAndGetNextItem(var source: PUTF8Char; const searchUp: RawUTF8; function GotoNextLine(source: PUTF8Char): PUTF8Char; {$ifdef HASINLINE}inline;{$endif} +/// compute the line length from a size-delimited source array of chars +// - will use fast assembly on x86-64 CPU, and expects TextEnd to be not nil +// - is likely to read some bytes after the TextEnd buffer, so GetLineSize() +// may be preferred, e.g. on memory mapped files +function BufferLineLength(Text, TextEnd: PUTF8Char): PtrInt; + {$ifndef CPUX64}{$ifdef FPC}inline;{$endif}{$endif} + /// compute the line length from source array of chars -// - end counting at either #0, #13 or #10 +// - if PEnd = nil, end counting at either #0, #13 or #10 +// - otherwise, end counting at either #13 or #10 function GetLineSize(P,PEnd: PUTF8Char): PtrUInt; /// returns true if the line length from source array of chars is not less than @@ -3048,6 +3154,10 @@ function GetNextItem(var P: PUTF8Char; Sep: AnsiChar= ','): RawUTF8; overload; // - P=nil after call when end of text is reached procedure GetNextItem(var P: PUTF8Char; Sep: AnsiChar; var result: RawUTF8); overload; +/// return next CSV string (unquoted if needed) from P +// - P=nil after call when end of text is reached +procedure GetNextItem(var P: PUTF8Char; Sep, Quote: AnsiChar; var result: RawUTF8); overload; + /// return trimmed next CSV string from P // - P=nil after call when end of text is reached procedure GetNextItemTrimed(var P: PUTF8Char; Sep: AnsiChar; var result: RawUTF8); @@ -3111,19 +3221,27 @@ function GetNextTChar64(var P: PUTF8Char; Sep: AnsiChar; out Buf: TChar64): PtrI /// return next CSV string as unsigned integer from P, 0 if no more // - if Sep is #0, it won't be searched for -function GetNextItemCardinal(var P: PUTF8Char; Sep: AnsiChar= ','): PtrUInt; +function GetNextItemCardinal(var P: PUTF8Char; Sep: AnsiChar=','): PtrUInt; /// return next CSV string as signed integer from P, 0 if no more // - if Sep is #0, it won't be searched for -function GetNextItemInteger(var P: PUTF8Char; Sep: AnsiChar= ','): PtrInt; +function GetNextItemInteger(var P: PUTF8Char; Sep: AnsiChar=','): PtrInt; + +/// return next CSV string as 64-bit signed integer from P, 0 if no more +// - if Sep is #0, it won't be searched for +function GetNextItemInt64(var P: PUTF8Char; Sep: AnsiChar=','): Int64; -/// return next CSV string as 64 bit signed integer from P, 0 if no more +/// return next CSV string as 64-bit unsigned integer from P, 0 if no more // - if Sep is #0, it won't be searched for -function GetNextItemInt64(var P: PUTF8Char; Sep: AnsiChar= ','): Int64; +function GetNextItemQWord(var P: PUTF8Char; Sep: AnsiChar=','): QWord; -/// return next CSV string as 64 bit unsigned integer from P, 0 if no more +/// return next CSV hexadecimal string as 64-bit unsigned integer from P +// - returns 0 if no valid hexadecimal text is available in P // - if Sep is #0, it won't be searched for -function GetNextItemQWord(var P: PUTF8Char; Sep: AnsiChar= ','): QWord; +// - will first fill the 64-bit value with 0, then decode each two hexadecimal +// characters available in P +// - could be used to decode TTextWriter.AddBinToHexDisplayMinChars() output +function GetNextItemHexa(var P: PUTF8Char; Sep: AnsiChar=','): QWord; /// return next CSV string as unsigned integer from P, 0 if no more // - P^ will point to the first non digit character (the item separator, e.g. @@ -3132,28 +3250,31 @@ function GetNextItemCardinalStrict(var P: PUTF8Char): PtrUInt; /// return next CSV string as unsigned integer from P, 0 if no more // - this version expects P^ to point to an Unicode char array -function GetNextItemCardinalW(var P: PWideChar; Sep: WideChar= ','): PtrUInt; +function GetNextItemCardinalW(var P: PWideChar; Sep: WideChar=','): PtrUInt; /// return next CSV string as double from P, 0.0 if no more // - if Sep is #0, will return all characters until next whitespace char -function GetNextItemDouble(var P: PUTF8Char; Sep: AnsiChar= ','): double; +function GetNextItemDouble(var P: PUTF8Char; Sep: AnsiChar=','): double; /// return next CSV string as currency from P, 0.0 if no more // - if Sep is #0, will return all characters until next whitespace char -function GetNextItemCurrency(var P: PUTF8Char; Sep: AnsiChar= ','): currency; overload; +function GetNextItemCurrency(var P: PUTF8Char; Sep: AnsiChar=','): currency; overload; {$ifdef HASINLINE}inline;{$endif} /// return next CSV string as currency from P, 0.0 if no more // - if Sep is #0, will return all characters until next whitespace char -procedure GetNextItemCurrency(var P: PUTF8Char; out result: currency; Sep: AnsiChar= ','); overload; +procedure GetNextItemCurrency(var P: PUTF8Char; out result: currency; Sep: AnsiChar=','); overload; /// return n-th indexed CSV string in P, starting at Index=0 for first one -function GetCSVItem(P: PUTF8Char; Index: PtrUInt; Sep: AnsiChar = ','): RawUTF8; +function GetCSVItem(P: PUTF8Char; Index: PtrUInt; Sep: AnsiChar=','): RawUTF8; overload; + +/// return n-th indexed CSV string (unquoted if needed) in P, starting at Index=0 for first one +function GetUnQuoteCSVItem(P: PUTF8Char; Index: PtrUInt; Sep: AnsiChar=','; Quote: AnsiChar=''''): RawUTF8; overload; /// return n-th indexed CSV string in P, starting at Index=0 for first one // - this function return the generic string type of the compiler, and // therefore can be used with ready to be displayed text (i.e. the VCL) -function GetCSVItemString(P: PChar; Index: PtrUInt; Sep: Char = ','): string; +function GetCSVItemString(P: PChar; Index: PtrUInt; Sep: Char=','): string; /// return last CSV string in the supplied UTF-8 content function GetLastCSVItem(const CSV: RawUTF8; Sep: AnsiChar=','): RawUTF8; @@ -3227,7 +3348,7 @@ procedure AddRawUTF8(var Values: TRawUTF8DynArray; var ValuesCount: integer; type /// simple stack-allocated type for handling a type names list - {$ifdef UNICODE}TPropNameList = record{$else}TPropNameList = object{$endif} + {$ifdef FPC_OR_UNICODE}TPropNameList = record{$else}TPropNameList = object{$endif} public Values: TRawUTF8DynArray; Count: Integer; @@ -3487,9 +3608,14 @@ function DirectoryDeleteOlderFiles(const Directory: TFileName; TimePeriod: TDate /// creates a directory if not already existing // - returns the full expanded directory name, including trailing backslash +// - returns '' on error, unless RaiseExceptionOnCreationFailure=true function EnsureDirectoryExists(const Directory: TFileName; RaiseExceptionOnCreationFailure: boolean=false): TFileName; +/// check if the directory is writable for the current user +// - try to write a small file with a random name +function IsDirectoryWritable(const Directory: TFileName): boolean; + /// compute an unique temporary file name // - following 'exename_01234567.tmp' pattern, in the system temporary folder function TemporaryFileName: TFileName; @@ -3497,7 +3623,7 @@ function TemporaryFileName: TFileName; type {$A-} /// file found result item, as returned by FindFiles() - {$ifdef UNICODE}TFindFiles = record{$else}TFindFiles = object{$endif} + {$ifdef FPC_OR_UNICODE}TFindFiles = record{$else}TFindFiles = object{$endif} public /// the matching file name, including its folder name Name: TFileName; @@ -3617,8 +3743,8 @@ function FindObjectEntryWithoutExt(const Content, Name: RawUTF8): RawUTF8; // in a huge text buffer // - this version also handles french and spanish pronunciations on request, // which differs from default Soundex, i.e. English - {$ifdef UNICODE}TSynSoundEx = record{$else}TSynSoundEx = object{$endif} - private + {$ifdef FPC_OR_UNICODE}TSynSoundEx = record private + {$else}TSynSoundEx = object protected{$endif} Search, FirstChar: cardinal; fValues: PSoundExValues; public @@ -3697,8 +3823,13 @@ function TrimLeftLowerCaseShort(V: PShortString): RawUTF8; /// trim first lowercase chars ('otDone' will return 'Done' e.g.) // - return a shortstring: enumeration names are pure 7bit ANSI with Delphi 7 // to 2007, and UTF-8 encoded with Delphi 2009+ -function TrimLeftLowerCaseToShort(V: PShortString): ShortString; +function TrimLeftLowerCaseToShort(V: PShortString): ShortString; overload; + {$ifdef HASINLINE}inline;{$endif} +/// trim first lowercase chars ('otDone' will return 'Done' e.g.) +// - return a shortstring: enumeration names are pure 7bit ANSI with Delphi 7 +// to 2007, and UTF-8 encoded with Delphi 2009+ +procedure TrimLeftLowerCaseToShort(V: PShortString; out result: ShortString); overload; /// convert a CamelCase string into a space separated one // - 'OnLine' will return 'On line' e.g., and 'OnMyLINE' will return 'On my LINE' @@ -3729,13 +3860,13 @@ function UnCamelCase(D, P: PUTF8Char): integer; overload; /// convert a string into an human-friendly CamelCase identifier // - replacing spaces or punctuations by an uppercase character -// - it is not the reverse function to UnCamelCase() +// - as such, it is not the reverse function to UnCamelCase() procedure CamelCase(P: PAnsiChar; len: integer; var s: RawUTF8; const isWord: TSynByteSet=[ord('0')..ord('9'),ord('a')..ord('z'),ord('A')..ord('Z')]); overload; /// convert a string into an human-friendly CamelCase identifier // - replacing spaces or punctuations by an uppercase character -// - it is not the reverse function to UnCamelCase() +// - as such, it is not the reverse function to UnCamelCase() procedure CamelCase(const text: RawUTF8; var s: RawUTF8; const isWord: TSynByteSet=[ord('0')..ord('9'),ord('a')..ord('z'),ord('A')..ord('Z')]); overload; {$ifdef HASINLINE}inline;{$endif} @@ -3997,19 +4128,19 @@ function IntegerScan(P: PCardinalArray; Count: PtrInt; Value: cardinal): PCardin // - return -1 if Value was not found function IntegerScanIndex(P: PCardinalArray; Count: PtrInt; Value: cardinal): PtrInt; -/// fast search of an integer position in a 64 bit integer array +/// fast search of an integer position in a 64-bit integer array // - Count is the number of Int64 entries in P^ // - returns P where P^=Value // - returns nil if Value was not found function Int64Scan(P: PInt64Array; Count: PtrInt; const Value: Int64): PInt64; -/// fast search of an integer position in a signed 64 bit integer array +/// fast search of an integer position in a signed 64-bit integer array // - Count is the number of Int64 entries in P^ // - returns index of P^[index]=Value // - returns -1 if Value was not found function Int64ScanIndex(P: PInt64Array; Count: PtrInt; const Value: Int64): PtrInt; -/// fast search of an integer position in an unsigned 64 bit integer array +/// fast search of an integer position in an unsigned 64-bit integer array // - Count is the number of QWord entries in P^ // - returns index of P^[index]=Value // - returns -1 if Value was not found @@ -4021,7 +4152,7 @@ function QWordScanIndex(P: PQWordArray; Count: PtrInt; const Value: QWord): PtrI // - returns false if Value was not found function IntegerScanExists(P: PCardinalArray; Count: PtrInt; Value: cardinal): boolean; -/// fast search of an integer value in a 64 bit integer array +/// fast search of an integer value in a 64-bit integer array // - returns true if P^=Value within Count entries // - returns false if Value was not found function Int64ScanExists(P: PInt64Array; Count: PtrInt; const Value: Int64): boolean; @@ -4061,15 +4192,15 @@ procedure QuickSortInteger(var ID: TIntegerDynArray); overload; /// sort a 16 bit unsigned Integer array, low values first procedure QuickSortWord(ID: PWordArray; L, R: PtrInt); -/// sort a 64 bit signed Integer array, low values first +/// sort a 64-bit signed Integer array, low values first procedure QuickSortInt64(ID: PInt64Array; L, R: PtrInt); overload; -/// sort a 64 bit unsigned Integer array, low values first +/// sort a 64-bit unsigned Integer array, low values first // - QWord comparison are implemented correctly under FPC or Delphi 2009+ - // older compilers will use fast and exact SortDynArrayQWord() procedure QuickSortQWord(ID: PQWordArray; L, R: PtrInt); overload; -/// sort a 64 bit Integer array, low values first +/// sort a 64-bit Integer array, low values first procedure QuickSortInt64(ID,CoValues: PInt64Array; L, R: PtrInt); overload; type @@ -4106,13 +4237,13 @@ function FastFindIntegerSorted(const Values: TIntegerDynArray; Value: integer): /// fast O(log(n)) binary search of a 16 bit unsigned integer value in a sorted array function FastFindWordSorted(P: PWordArray; R: PtrInt; Value: Word): PtrInt; -/// fast O(log(n)) binary search of a 64 bit signed integer value in a sorted array +/// fast O(log(n)) binary search of a 64-bit signed integer value in a sorted array // - R is the last index of available integer entries in P^ (i.e. Count-1) // - return index of P^[result]=Value // - return -1 if Value was not found function FastFindInt64Sorted(P: PInt64Array; R: PtrInt; const Value: Int64): PtrInt; overload; -/// fast O(log(n)) binary search of a 64 bit unsigned integer value in a sorted array +/// fast O(log(n)) binary search of a 64-bit unsigned integer value in a sorted array // - R is the last index of available integer entries in P^ (i.e. Count-1) // - return index of P^[result]=Value // - return -1 if Value was not found @@ -4179,6 +4310,9 @@ procedure AddInteger(var Values: TIntegerDynArray; var ValuesCount: integer; Value: integer); overload; {$ifdef HASINLINE}inline;{$endif} +/// add an integer array at the end of a dynamic array of integer +function AddInteger(var Values: TIntegerDynArray; const Another: TIntegerDynArray): PtrInt; overload; + /// add an integer value at the end of a dynamic array of integers // - this overloaded function will use a separate Count variable (faster), // and would allow to search for duplicates @@ -4189,18 +4323,21 @@ function AddInteger(var Values: TIntegerDynArray; var ValuesCount: integer; Value: integer; NoDuplicates: boolean): boolean; overload; /// add a 16-bit integer value at the end of a dynamic array of integers -function AddWord(var Values: TWordDynArray; var ValuesCount: integer; Value: Word): integer; +function AddWord(var Values: TWordDynArray; var ValuesCount: integer; Value: Word): PtrInt; /// add a 64-bit integer value at the end of a dynamic array of integers -function AddInt64(var Values: TInt64DynArray; var ValuesCount: integer; Value: Int64): integer; overload; +function AddInt64(var Values: TInt64DynArray; var ValuesCount: integer; Value: Int64): PtrInt; overload; {$ifdef HASINLINE}inline;{$endif} -/// add a 64-bit integer value at the end of a dynamic array of integers -function AddInt64(var Values: TInt64DynArray; Value: Int64): integer; overload; +/// add a 64-bit integer value at the end of a dynamic array +function AddInt64(var Values: TInt64DynArray; Value: Int64): PtrInt; overload; {$ifdef HASINLINE}inline;{$endif} +/// add a 64-bit integer array at the end of a dynamic array +function AddInt64(var Values: TInt64DynArray; const Another: TInt64DynArray): PtrInt; overload; + /// if not already existing, add a 64-bit integer value to a dynamic array -function AddInt64Once(var Values: TInt64DynArray; Value: Int64): integer; +function AddInt64Once(var Values: TInt64DynArray; Value: Int64): PtrInt; /// if not already existing, add a 64-bit integer value to a sorted dynamic array procedure AddInt64Sorted(var Values: TInt64DynArray; Value: Int64); @@ -4217,6 +4354,12 @@ procedure DeleteInteger(var Values: TIntegerDynArray; var ValuesCount: Integer; procedure ExcludeInteger(var Values, Excluded: TIntegerDynArray; ExcludedSortSize: Integer=32); +/// ensure some 32-bit integer from Values[] will only contain Included[] +// - Included is declared as var, since it will be sorted in-place during process +// if it contains more than IncludedSortSize items (i.e. if the sort is worth it) +procedure IncludeInteger(var Values, Included: TIntegerDynArray; + IncludedSortSize: Integer=32); + /// sort and remove any 32-bit duplicated integer from Values[] procedure DeduplicateInteger(var Values: TIntegerDynArray); overload; @@ -4245,6 +4388,12 @@ procedure DeleteInt64(var Values: TInt64DynArray; var ValuesCount: Integer; Inde procedure ExcludeInt64(var Values, Excluded: TInt64DynArray; ExcludedSortSize: Integer=32); +/// ensure some 64-bit integer from Values[] will only contain Included[] +// - Included is declared as var, since it will be sorted in-place during process +// if it contains more than IncludedSortSize items (i.e. if the sort is worth it) +procedure IncludeInt64(var Values, Included: TInt64DynArray; + IncludedSortSize: Integer=32); + /// sort and remove any 64-bit duplicated integer from Values[] procedure DeduplicateInt64(var Values: TInt64DynArray); overload; @@ -4351,7 +4500,7 @@ function FromU64(const Values: array of QWord): TQWordDynArray; // - is defined either as an object either as a record, due to a bug // in Delphi 2009/2010 compiler (at least): this structure is not initialized // if defined as an object on the stack, but will be as a record :( - {$ifdef UNICODE}TSortedWordArray = record{$else}TSortedWordArray = object{$endif} + {$ifdef FPC_OR_UNICODE}TSortedWordArray = record{$else}TSortedWordArray = object{$endif} public Values: TWordDynArray; Count: integer; @@ -4537,6 +4686,20 @@ function FromVarBlob(Data: PByte): TValueResult; {$ifdef HASINLINE}inline;{$endi /// internal set to specify some standard Delphi arrays TDynArrayKinds = set of TDynArrayKind; + {$ifdef FPC} + /// map the Delphi/FPC dynamic array header (stored before each instance) + // - define globally for proper inlining with FPC + TDynArrayRec = packed record + /// dynamic array reference count (basic garbage memory mechanism) + refCnt: PtrInt; + high: tdynarrayindex; + function GetLength: sizeint; inline; + procedure SetLength(len: sizeint); inline; + property length: sizeint read GetLength write SetLength; + end; + PDynArrayRec = ^TDynArrayRec; + {$endif FPC} + function ToText(k: TDynArrayKind): PShortString; overload; const @@ -4580,13 +4743,8 @@ function ToText(k: TDynArrayKind): PShortString; overload; // - is defined either as an object either as a record, due to a bug // in Delphi 2009/2010 compiler (at least): this structure is not initialized // if defined as an object on the stack, but will be as a record :( - {$ifdef UNDIRECTDYNARRAY} - TDynArray = record - private - {$else} - TDynArray = object - protected - {$endif} + {$ifdef UNDIRECTDYNARRAY}TDynArray = record private + {$else}TDynArray = object protected{$endif} fValue: PPointer; fTypeInfo: pointer; fElemType: pointer; @@ -4812,12 +4970,18 @@ TDynArray = record /// sort the dynamic array elements, using the Compare property function // - it will change the dynamic array content, and exchange all elements // in order to be sorted in increasing order according to Compare function - procedure Sort(aCompare: TDynArraySortCompare=nil); + procedure Sort(aCompare: TDynArraySortCompare=nil); overload; /// sort some dynamic array elements, using the Compare property function // - this method allows to sort only some part of the items // - it will change the dynamic array content, and exchange all elements // in order to be sorted in increasing order according to Compare function procedure SortRange(aStart, aStop: integer; aCompare: TDynArraySortCompare=nil); + /// sort the dynamic array elements, using a Compare method (not function) + // - it will change the dynamic array content, and exchange all elements + // in order to be sorted in increasing order according to Compare function, + // unless aReverse is true + // - it won't mark the array as Sorted, since the comparer is local + procedure Sort(const aCompare: TEventDynArraySortCompare; aReverse: boolean=false); overload; /// search the elements range which match a given value in a sorted dynamic array // - this method will use the Compare property function for the search // - returns TRUE and the matching indexes, or FALSE if none found @@ -5003,17 +5167,19 @@ TDynArray = record /// set all content of one dynamic array to the current array // - both must be of the same exact type // - T*ObjArray will be reallocated and copied by content (using a temporary - // JSON serialization), unless ObjArrayByRef is true + // JSON serialization), unless ObjArrayByRef is true and pointers are copied procedure Copy(const Source: TDynArray; ObjArrayByRef: boolean=false); /// set all content of one dynamic array to the current array // - both must be of the same exact type // - T*ObjArray will be reallocated and copied by content (using a temporary - // JSON serialization), unless ObjArrayByRef is true + // JSON serialization), unless ObjArrayByRef is true and pointers are copied procedure CopyFrom(const Source; MaxElem: integer; ObjArrayByRef: boolean=false); /// set all content of the current dynamic array to another array variable // - both must be of the same exact type + // - resulting length(Dest) will match the exact items count, even if an + // external Count integer variable is used by this instance // - T*ObjArray will be reallocated and copied by content (using a temporary - // JSON serialization), unless ObjArrayByRef is true + // JSON serialization), unless ObjArrayByRef is true and pointers are copied procedure CopyTo(out Dest; ObjArrayByRef: boolean=false); {$endif DELPHI5OROLDER} /// returns a pointer to an element of the array @@ -5027,7 +5193,7 @@ TDynArray = record // - do nothing if index is out of range procedure ElemCopyAt(index: PtrInt; var Dest); {$ifdef FPC}inline;{$endif} /// will move one element content from its index into another variable - // - will erase the internal item ater copy + // - will erase the internal item after copy // - do nothing if index is out of range procedure ElemMoveTo(index: PtrInt; var Dest); /// will copy one variable content into an indexed element @@ -5126,8 +5292,8 @@ TDynArray = record /// allows to iterate over a TDynArray.SaveTo binary buffer // - may be used as alternative to TDynArray.LoadFrom, if you don't want // to allocate all items at once, but retrieve items one by one - {$ifdef UNICODE}TDynArrayLoadFrom = record{$else}TDynArrayLoadFrom = object{$endif} - private + {$ifdef FPC_OR_UNICODE}TDynArrayLoadFrom = record private + {$else}TDynArrayLoadFrom = object protected{$endif} DynArray: TDynArray; // used to access RTTI Hash: PCardinalArray; public @@ -5212,6 +5378,8 @@ TDynArrayHashed = record function KnownType: TDynArrayKind; inline; procedure Clear; inline; procedure ElemCopy(const A; var B); inline; + function ElemPtr(index: PtrInt): pointer; inline; + procedure ElemCopyAt(index: PtrInt; var Dest); inline; // warning: you shall call ReHash() after manual Add/Delete function Add(const Elem): integer; inline; procedure Delete(aIndex: PtrInt); inline; @@ -5609,13 +5777,13 @@ TSynPersistent = class(TObject) // for this class hierarchy: as a result, you would NOT be able to // implement an interface with a TSynPersistent descendent (but you should // not need to, but inherit from TInterfacedObject) - // - warning: under FPC, it won't initialize management operators + // - warning: under FPC, it won't initialize fields management operators class function NewInstance: TObject; override; {$ifndef FPC_OR_PUREPASCAL} /// optimized x86 asm finalization code // - warning: this version won't release either any allocated TMonitor // (as available since Delphi 2009) - do not use TMonitor with - // TSynPersistent, but rather the faster TSynPersistentLocked class + // TSynPersistent, but rather the faster TSynPersistentLock class procedure FreeInstance; override; {$endif} end; @@ -5630,9 +5798,12 @@ TSynPersistent = class(TObject) // @http://www.delphitools.info/2011/11/30/fixing-tcriticalsection // - internal padding is used to safely store up to 7 values protected // from concurrent access with a mutex - {$ifdef UNICODE}TSynLocker = record{$else}TSynLocker = object{$endif} - private + // - for object-level locking, see TSynPersistentLock which owns one such + // instance, or call low-level NewSynLocker function then DoneAndFreemem + {$ifdef FPC_OR_UNICODE}TSynLocker = record private + {$else}TSynLocker = object protected{$endif} fSection: TRTLCriticalSection; + fSectionPadding: PtrInt; // paranoid to avoid FUTEX_WAKE_PRIVATE=EAGAIN fLocked, fInitialized: boolean; {$ifndef NOVARIANTS} function GetVariant(Index: integer): Variant; @@ -5672,6 +5843,9 @@ {$ifdef UNICODE}TSynLocker = record{$else}TSynLocker = object{$endif} // the TSynLocker instance), otherwise you may encounter unexpected // behavior, like access violations or memory leaks procedure Done; + /// finalize the mutex, and call FreeMem() on the pointer of this instance + // - should have been initiazed with a NewSynLocker call + procedure DoneAndFreeMem; /// lock the instance for exclusive access // - use as such to avoid race condition (from a Safe: TSynLocker property): // ! Safe.Lock; @@ -5794,23 +5968,26 @@ {$ifdef UNICODE}TSynLocker = record{$else}TSynLocker = object{$endif} /// adding locking methods to a TSynPersistent with virtual constructor // - you may use this class instead of the RTL TCriticalSection, since it // would use a TSynLocker which does not suffer from CPU cache line conflit - TSynPersistentLocked = class(TSynPersistent) + TSynPersistentLock = class(TSynPersistent) protected - fSafe: TSynLocker; + fSafe: PSynLocker; // TSynLocker would increase inherited fields offset public - /// initialize the object instance, and its associated lock + /// initialize the instance, and its associated lock constructor Create; override; - /// release the instance (including the locking resource) + /// finalize the instance, and its associated lock destructor Destroy; override; - /// access to the locking methods of this instance - // - use Safe.Lock/TryLock with a try ... finally Safe.Unlock block - property Safe: TSynLocker read fSafe; + /// access to the associated instance critical section + // - call Safe.Lock/UnLock to protect multi-thread access on this storage + property Safe: PSynLocker read fSafe; end; + /// used for backward compatibility only with existing code + TSynPersistentLocked = class(TSynPersistentLock); + /// adding locking methods to a TInterfacedObject with virtual constructor TInterfacedObjectLocked = class(TInterfacedObjectWithCustomCreate) protected - fSafe: TSynLocker; + fSafe: PSynLocker; // TSynLocker would increase inherited fields offset public /// initialize the object instance, and its associated lock constructor Create; override; @@ -5818,7 +5995,7 @@ TInterfacedObjectLocked = class(TInterfacedObjectWithCustomCreate) destructor Destroy; override; /// access to the locking methods of this instance // - use Safe.Lock/TryLock with a try ... finally Safe.Unlock block - property Safe: TSynLocker read fSafe; + property Safe: PSynLocker read fSafe; end; /// used to determine the exact class type of a TInterfacedObjectWithCustomCreate @@ -5834,73 +6011,8 @@ TPersistentWithCustomCreateClass = class of TPersistentWithCustomCreate; TSynPersistentClass = class of TSynPersistent; - /// internal item definition, used by TPendingTaskList storage - TPendingTaskListItem = packed record - /// the task should be executed when TPendingTaskList.GetTimestamp reaches - // this value - Timestamp: Int64; - /// the associated task, stored by representation as raw binary - Task: RawByteString; - end; - /// internal list definition, used by TPendingTaskList storage - TPendingTaskListItemDynArray = array of TPendingTaskListItem; - - /// handle a list of tasks, stored as RawByteString, with a time stamp - // - internal time stamps would be GetTickCount64 by default, so have a - // resolution of about 16 ms under Windows - // - you can add tasks to the internal list, to be executed after a given - // delay, using a post/peek like algorithm - // - execution delays are not expected to be accurate, but are best guess, - // according to NextTask call - // - this implementation is thread-safe, thanks to the Safe internal locker - TPendingTaskList = class(TSynPersistent) - protected - fCount: Integer; - fTask: TPendingTaskListItemDynArray; - fTasks: TDynArray; - fSafe: TSynLocker; - function GetCount: integer; - function GetTimestamp: Int64; virtual; - public - /// initialize the list memory and resources - constructor Create; override; - /// finaalize the list memory and resources - destructor Destroy; override; - /// append a task, specifying a delay in milliseconds from current time - procedure AddTask(aMilliSecondsDelayFromNow: integer; const aTask: RawByteString); virtual; - /// append several tasks, specifying a delay in milliseconds between tasks - // - first supplied delay would be computed from the current time, then - // it would specify how much time to wait between the next supplied task - procedure AddTasks(const aMilliSecondsDelays: array of integer; - const aTasks: array of RawByteString); - /// retrieve the next pending task - // - returns '' if there is no scheduled task available at the current time - // - returns the next stack as defined corresponding to its specified delay - function NextPendingTask: RawByteString; virtual; - /// flush all pending tasks - procedure Clear; virtual; - /// access to the locking methods of this instance - // - use Safe.Lock/TryLock with a try ... finally Safe.Unlock block - property Safe: TSynlocker read fSafe; - /// access to the internal TPendingTaskListItem.Timestamp stored value - // - corresponding to the current time - // - default implementation is to return GetTickCount64, with a 16 ms - // typical resolution under Windows - property Timestamp: Int64 read GetTimestamp; - /// how many pending tasks are currently defined - property Count: integer read GetCount; - /// direct low-level access to the internal task list - // - warning: this dynamic array length is the list capacity: use Count - // property to retrieve the exact number of stored items - // - use Safe.Lock/TryLock with a try ... finally Safe.Unlock block for - // thread-safe access to this array - // - items are stored in increasing Timestamp, i.e. the first item is - // the next one which would be returned by the NextPendingTask method - property Task: TPendingTaskListItemDynArray read fTask; - end; - /// used to store one list of hashed RawUTF8 in TRawUTF8Interning pool - {$ifdef UNICODE}TRawUTF8InterningSlot = record{$else}TRawUTF8InterningSlot = object{$endif} + {$ifdef FPC_OR_UNICODE}TRawUTF8InterningSlot = record{$else}TRawUTF8InterningSlot = object{$endif} public /// actual RawUTF8 storage Value: TRawUTF8DynArray; @@ -5908,7 +6020,7 @@ {$ifdef UNICODE}TRawUTF8InterningSlot = record{$else}TRawUTF8InterningSlot = o Values: TDynArrayHashed; /// associated mutex for thread-safe process Safe: TSynLocker; - /// initialize the RawUTF8 slot + /// initialize the RawUTF8 slot (and its Safe mutex) procedure Init; /// finalize the RawUTF8 slot procedure Done; @@ -6027,8 +6139,8 @@ TSynNameValueItem = record // - is defined either as an object either as a record, due to a bug // in Delphi 2009/2010 compiler (at least): this structure is not initialized // if defined as an object on the stack, but will be as a record :( - {$ifdef UNICODE}TSynNameValue = record{$else}TSynNameValue = object{$endif} - private + {$ifdef FPC_OR_UNICODE}TSynNameValue = record private + {$else}TSynNameValue = object protected{$endif} fDynArray: TDynArrayHashed; fOnAdd: TSynNameValueNotify; function GetBlobData: RawByteString; @@ -6152,6 +6264,11 @@ {$ifdef UNICODE}TSynNameValue = record{$else}TSynNameValue = object{$endif} /// a reference pointer to a Name/Value RawUTF8 pairs storage PSynNameValue = ^TSynNameValue; +/// allocate and initialize a TSynLocker instance +// - caller should call result^.DoneAndFreemem when not used any more +function NewSynLocker: PSynLocker; + {$ifdef HASINLINE}inline;{$endif} + /// wrapper to add an item to a array of pointer dynamic array storage function PtrArrayAdd(var aPtrArray; aItem: pointer): integer; {$ifdef HASINLINE}inline;{$endif} @@ -6183,21 +6300,21 @@ function PtrArrayFind(var aPtrArray; aItem: pointer): integer; // ! ObjArrayClear(arr); // release all items // ! end; // - return the index of the item in the dynamic array -function ObjArrayAdd(var aObjArray; aItem: TObject): integer; +function ObjArrayAdd(var aObjArray; aItem: TObject): PtrInt; {$ifdef HASINLINE}inline;{$endif} /// wrapper to add items to a T*ObjArray dynamic array storage // - aSourceObjArray[] items will be owned by aDestObjArray[], therefore // aSourceObjArray is set to nil // - return the new number of the items in aDestObjArray -function ObjArrayAppend(var aDestObjArray, aSourceObjArray): integer; +function ObjArrayAppend(var aDestObjArray, aSourceObjArray): PtrInt; /// wrapper to add an item to a T*ObjArray dynamic array storage // - this overloaded function will use a separated variable to store the items // count, so will be slightly faster: but you should call SetLength() when done, // to have an array as expected by TJSONSerializer.RegisterObjArrayForJSON() // - return the index of the item in the dynamic array -function ObjArrayAddCount(var aObjArray; aItem: TObject; var aObjArrayCount: integer): integer; +function ObjArrayAddCount(var aObjArray; aItem: TObject; var aObjArrayCount: integer): PtrInt; /// wrapper to add once an item to a T*ObjArray dynamic array storage // - as expected by TJSONSerializer.RegisterObjArrayForJSON() @@ -6216,7 +6333,7 @@ procedure ObjArraySetLength(var aObjArray; aLength: integer); // - as expected by TJSONSerializer.RegisterObjArrayForJSON() // - search is performed by address/reference, not by content // - returns -1 if the item is not found in the dynamic array -function ObjArrayFind(const aObjArray; aItem: TObject): integer; +function ObjArrayFind(const aObjArray; aItem: TObject): PtrInt; {$ifdef HASINLINE}inline;{$endif} /// wrapper to count all not nil items in a T*ObjArray dynamic array storage @@ -6226,14 +6343,14 @@ function ObjArrayCount(const aObjArray): integer; /// wrapper to delete an item in a T*ObjArray dynamic array storage // - as expected by TJSONSerializer.RegisterObjArrayForJSON() // - do nothing if the index is out of range in the dynamic array -procedure ObjArrayDelete(var aObjArray; aItemIndex: integer; +procedure ObjArrayDelete(var aObjArray; aItemIndex: PtrInt; aContinueOnException: boolean=false); overload; /// wrapper to delete an item in a T*ObjArray dynamic array storage // - as expected by TJSONSerializer.RegisterObjArrayForJSON() // - search is performed by address/reference, not by content // - do nothing if the item is not found in the dynamic array -function ObjArrayDelete(var aObjArray; aItem: TObject): integer; overload; +function ObjArrayDelete(var aObjArray; aItem: TObject): PtrInt; overload; /// wrapper to sort the items stored in a T*ObjArray dynamic array // - as expected by TJSONSerializer.RegisterObjArrayForJSON() @@ -6274,7 +6391,7 @@ procedure ObjArraysClear(const aObjArray: array of pointer); {$ifndef DELPHI5OROLDER} /// wrapper to add an item to a T*InterfaceArray dynamic array storage -function InterfaceArrayAdd(var aInterfaceArray; const aItem: IUnknown): integer; +function InterfaceArrayAdd(var aInterfaceArray; const aItem: IUnknown): PtrInt; /// wrapper to add once an item to a T*InterfaceArray dynamic array storage procedure InterfaceArrayAddOnce(var aInterfaceArray; const aItem: IUnknown); @@ -6283,17 +6400,17 @@ procedure InterfaceArrayAddOnce(var aInterfaceArray; const aItem: IUnknown); // - search is performed by address/reference, not by content // - return -1 if the item is not found in the dynamic array, or the index of // the matching entry otherwise -function InterfaceArrayFind(const aInterfaceArray; const aItem: IUnknown): integer; +function InterfaceArrayFind(const aInterfaceArray; const aItem: IUnknown): PtrInt; {$ifdef HASINLINE}inline;{$endif} /// wrapper to delete an item in a T*InterfaceArray dynamic array storage // - search is performed by address/reference, not by content // - do nothing if the item is not found in the dynamic array -function InterfaceArrayDelete(var aInterfaceArray; const aItem: IUnknown): integer; overload; +function InterfaceArrayDelete(var aInterfaceArray; const aItem: IUnknown): PtrInt; overload; /// wrapper to delete an item in a T*InterfaceArray dynamic array storage // - do nothing if the item is not found in the dynamic array -procedure InterfaceArrayDelete(var aInterfaceArray; aItemIndex: integer); overload; +procedure InterfaceArrayDelete(var aInterfaceArray; aItemIndex: PtrInt); overload; {$endif DELPHI5OROLDER} @@ -6362,11 +6479,11 @@ procedure AppendShortComma(text: PAnsiChar; len: integer; var result: shortstrin /// fast search of an exact case-insensitive match of a RTTI's PShortString array function FindShortStringListExact(List: PShortString; MaxValue: integer; - aValue: PUTF8Char; aValueLen: integer): integer; + aValue: PUTF8Char; aValueLen: PtrInt): integer; /// fast search of an left-trimmed lowercase match of a RTTI's PShortString array function FindShortStringListTrimLowerCase(List: PShortString; MaxValue: integer; - aValue: PUTF8Char; aValueLen: integer): integer; + aValue: PUTF8Char; aValueLen: PtrInt): integer; /// retrieve the type name from its low-level RTTI function TypeInfoToName(aTypeInfo: pointer): RawUTF8; overload; @@ -6439,19 +6556,38 @@ function GUIDToRawUTF8({$IFDEF FPC_HAS_CONSTREF}constref{$ELSE}const{$ENDIF} gui // - this version is faster than the one supplied by SysUtils function GUIDToString({$IFDEF FPC_HAS_CONSTREF}constref{$ELSE}const{$ENDIF} guid: TGUID): string; +type + /// low-level object implementing a 32-bit Pierre L'Ecuyer software generator + // - as used by RandomGsl function, and Random32 if no RDRAND hardware is available + // - is not thread-safe, but cross-compiler and cross-platform, still very + // fast with a much better distribution than Delphi system's Random() function + {$ifdef FPC_OR_UNICODE}TLecuyer = record{$else}TLecuyer = object{$endif} + public + rs1, rs2, rs3, seedcount: cardinal; + /// force an immediate seed of the generator from current system state + // - should be called before any call to the Next method + procedure Seed(entropy: PByteArray; entropylen: PtrInt); + /// compute the next 32-bit generated value + // - will automatically reseed after around 65,000 generated values + function Next: cardinal; overload; + /// compute the next 32-bit generated value, in range [0..max-1] + // - will automatically reseed after around 65,000 generated values + function Next(max: cardinal): cardinal; overload; + end; + /// fast compute of some 32-bit random value -// - will use RDRAND Intel x86/x64 opcode if available, or fast gsl_rng_taus2 -// generator by Pierre L'Ecuyer (period is 2^88, i.e. about 10^26) -// - will fast generate some random-like 32-bit output +// - will use (slow but) hardware-derivated RDRAND Intel x86/x64 opcode if +// available, or fast gsl_rng_taus2 generator by Pierre L'Ecuyer (which period +// is 2^88, i.e. about 10^26) if the CPU doesn't support it // - use rather TAESPRNG.Main.FillRandom() for cryptographic-level randomness // - thread-safe function: each thread will maintain its own gsl_rng_taus2 table function Random32: cardinal; overload; /// fast compute of some 32-bit random value, with a maximum (excluded) upper value // - i.e. returns a value in range [0..max-1] -// - will use RDRAND Intel x86/x64 opcode if available, or fast gsl_rng_taus2 -// generator by Pierre L'Ecuyer (period is 2^88, i.e. about 10^26) -// - will fast generate some random-like 32-bit output +// - will use (slow but) hardware-derivated RDRAND Intel x86/x64 opcode if +// available, or fast gsl_rng_taus2 generator by Pierre L'Ecuyer (which period +// is 2^88, i.e. about 10^26) if the CPU doesn't support it // - use rather TAESPRNG.Main.FillRandom() for cryptographic-level randomness // - thread-safe function: each thread will maintain its own gsl_rng_taus2 table function Random32(max: cardinal): cardinal; overload; @@ -6459,8 +6595,8 @@ function Random32(max: cardinal): cardinal; overload; /// fast compute of some 32-bit random value, using the gsl_rng_taus2 generator // - plain Random32 may call RDRAND opcode on Intel CPUs, wherease this function // will use well documented (and proven) Pierre L'Ecuyer software generator -// - may be used if you don't want/trust RDRAND, or expect a well defined -// cross-platform generator +// - may be used if you don't want/trust RDRAND, if you expect a well defined +// cross-platform generator, or have higher performance expectations // - use rather TAESPRNG.Main.FillRandom() for cryptographic-level randomness // - thread-safe function: each thread will maintain its own gsl_rng_taus2 table function Random32gsl: cardinal; overload; @@ -6479,13 +6615,14 @@ function Random32gsl(max: cardinal): cardinal; overload; procedure Random32Seed(entropy: pointer=nil; entropylen: integer=0); /// fill some memory buffer with random values -// - the destination buffer is expected to be allocated as 32 bit items +// - the destination buffer is expected to be allocated as 32-bit items // - use internally crc32c() with some rough entropy source, and Random32 // gsl_rng_taus2 generator or hardware RDRAND Intel x86/x64 opcode if available -// (and forcegsl is kept to its default false value) +// (and ForceGsl is kept to its default false value) // - consider using instead the cryptographic secure TAESPRNG.Main.FillRandom() -// method from the SynCrypto unit -procedure FillRandom(Dest: PCardinalArray; CardinalCount: integer; forcegsl: boolean=false); +// method from the SynCrypto unit - in particular, RDRAND could be slow +// as reported by https://en.wikipedia.org/wiki/RdRand#Performance +procedure FillRandom(Dest: PCardinalArray; CardinalCount: integer; ForceGsl: boolean=false); /// compute a random GUID value procedure RandomGUID(out result: TGUID); overload; @@ -6562,6 +6699,10 @@ function RecordEquals(const RecA, RecB; TypeInfo: pointer; // WinAnsiString, SynUnicode or even RawUnicode/WideString function RecordSave(const Rec; TypeInfo: pointer): RawByteString; overload; +/// save a record content into a TBytes dynamic array +// - could be used as an alternative to RawByteString's RecordSave() +function RecordSaveBytes(const Rec; TypeInfo: pointer): TBytes; + /// save a record content into a destination memory buffer // - Dest must be at least RecordSaveLength() bytes long // - will return the Rec size, in bytes, into Len reference variable @@ -6595,6 +6736,10 @@ function RecordSave(const Rec; Dest: PAnsiChar; TypeInfo: pointer; function RecordSave(const Rec; Dest: PAnsiChar; TypeInfo: pointer): PAnsiChar; overload; {$ifdef HASINLINE}inline;{$endif} +/// save a record content into a destination memory buffer +// - caller should make Dest.Done once finished with Dest.buf/Dest.len buffer +procedure RecordSave(const Rec; var Dest: TSynTempBuffer; TypeInfo: pointer); overload; + /// save a record content into a Base-64 encoded UTF-8 text content // - will use RecordSave() format, with a left-sided binary CRC function RecordSaveBase64(const Rec; TypeInfo: pointer; UriCompatible: boolean=false): RawUTF8; @@ -6617,15 +6762,18 @@ function RecordSaveJSON(const Rec; TypeInfo: pointer; /// fill a record content from a memory buffer as saved by RecordSave() // - return nil if the Source buffer is incorrect -// - will return the Rec size, in bytes, into Len reference variable // - in case of success, return the memory buffer pointer just after the -// read content +// read content, and set the Rec size, in bytes, into Len reference variable // - will use a proprietary binary format, with some variable-length encoding // of the string length - note that if you change the type definition, any // previously-serialized content will fail, maybe triggering unexpected GPF: you // may use TypeInfoToHash() if you share this binary data accross executables function RecordLoad(var Rec; Source: PAnsiChar; TypeInfo: pointer; - Len: PInteger=nil): PAnsiChar; + Len: PInteger=nil): PAnsiChar; overload; + +/// fill a record content from a memory buffer as saved by RecordSave() +// - returns false if the Source buffer was incorrect, true on success +function RecordLoad(var Res; const Source: RawByteString; TypeInfo: pointer): boolean; overload; /// read a record content from a Base-64 encoded content // - expects RecordSaveBase64() format, with a left-sided binary CRC @@ -6683,6 +6831,8 @@ function DynArraySave(var Value; TypeInfo: pointer): RawByteString; // - Value shall be set to the target dynamic array field // - is just a wrapper around TDynArray.LoadFromJSON(), creating a temporary // TDynArray wrapper on the stack +// - return a pointer at the end of the data read from JSON, nil in case +// of an invalid input buffer // - to be used e.g. for custom record JSON unserialization, within a // TDynArrayJSONCustomReader callback // - warning: the JSON buffer will be modified in-place during process - use @@ -6729,6 +6879,7 @@ function DynArrayElementTypeName(TypeInfo: pointer; ElemTypeInfo: PPointer=nil; // - used internally to guess the associated item type name function DynArrayItemTypeLen(const aDynArrayTypeName: RawUTF8): integer; + /// compare two "array of boolean" elements function SortDynArrayBoolean(const A,B): integer; @@ -6746,30 +6897,27 @@ function SortDynArrayWord(const A,B): integer; /// compare two "array of integer" elements function SortDynArrayInteger(const A,B): integer; - {$ifndef CPUX86}{$ifdef HASINLINE}inline;{$endif}{$endif} /// compare two "array of cardinal" elements function SortDynArrayCardinal(const A,B): integer; /// compare two "array of Int64" or "array of Currency" elements function SortDynArrayInt64(const A,B): integer; - {$ifndef CPUX86}{$ifdef HASINLINE}inline;{$endif}{$endif} /// compare two "array of QWord" elements -// - note that QWordA>QWordB is wrong on older versions of Delphi, so you should -// better use this function to properly compare two QWord values over CPUX86 +// - note that QWord(A)>QWord(B) is wrong on older versions of Delphi, so you +// should better use this function or CompareQWord() to properly compare two +// QWord values over CPUX86 function SortDynArrayQWord(const A,B): integer; - {$ifndef CPUX86}{$ifdef HASINLINE}inline;{$endif}{$endif} /// compare two "array of THash128" elements function SortDynArray128(const A,B): integer; - {$ifdef FPC_OR_UNICODE}inline;{$endif} // C2096 Delphi 2007 internal error /// compare two "array of THash256" elements -function SortDynArray256(const A,B): integer; {$ifdef FPC_OR_UNICODE}inline;{$endif} +function SortDynArray256(const A,B): integer; /// compare two "array of THash512" elements -function SortDynArray512(const A,B): integer; {$ifdef FPC_OR_UNICODE}inline;{$endif} +function SortDynArray512(const A,B): integer; /// compare two "array of TObject/pointer" elements function SortDynArrayPointer(const A,B): integer; @@ -6813,16 +6961,15 @@ function SortDynArrayStringI(const A,B): integer; /// compare two "array of TFileName" elements, as file names // - i.e. with no case sensitivity, and grouped by file extension // - the expected string type is the generic RTL string, i.e. TFileName +// - calls internally GetFileNameWithoutExt() and AnsiCompareFileName() function SortDynArrayFileName(const A,B): integer; {$ifndef NOVARIANTS} /// compare two "array of variant" elements, with case sensitivity function SortDynArrayVariant(const A,B): integer; - {$ifdef HASINLINE}inline;{$endif} /// compare two "array of variant" elements, with no case sensitivity function SortDynArrayVariantI(const A,B): integer; - {$ifdef HASINLINE}inline;{$endif} /// compare two "array of variant" elements, with or without case sensitivity function SortDynArrayVariantComp(const A,B: TVarData; caseInsensitive: boolean): integer; @@ -6878,13 +7025,10 @@ function HashByte(const Elem; Hasher: THasher): cardinal; /// hash one Word value - simply return the value ignore Hasher() parameter function HashWord(const Elem; Hasher: THasher): cardinal; -/// hash one Integer value - simply return the value ignore Hasher() parameter +/// hash one Integer/cardinal value - simply return the value ignore Hasher() parameter function HashInteger(const Elem; Hasher: THasher): cardinal; -/// hash one Cardinal value - simply return the value ignore Hasher() parameter -function HashCardinal(const Elem; Hasher: THasher): cardinal; - -/// hash one Int64 value with the suppplied Hasher() function +/// hash one Int64/Qword value with the suppplied Hasher() function function HashInt64(const Elem; Hasher: THasher): cardinal; /// hash one THash128 value with the suppplied Hasher() function @@ -6930,7 +7074,7 @@ function HashPointer(const Elem; Hasher: THasher): cardinal; // - not to be used as such, but e.g. when inlining TDynArray methods DYNARRAY_HASHFIRSTFIELD: array[boolean,TDynArrayKind] of TDynArrayHashOne = ( (nil, HashByte, HashByte, HashWord, HashInteger, - HashCardinal, HashCardinal, HashInt64, HashInt64, HashInt64, + HashInteger, HashInteger, HashInt64, HashInt64, HashInt64, HashInt64, HashInt64, HashInt64, HashInt64, HashAnsiString, HashAnsiString, {$ifdef UNICODE}HashUnicodeString{$else}HashAnsiString{$endif}, @@ -6938,7 +7082,7 @@ function HashPointer(const Elem; Hasher: THasher): cardinal; Hash256, Hash512, HashPointer, {$ifndef NOVARIANTS}HashVariant,{$endif} nil), (nil, HashByte, HashByte, HashWord, HashInteger, - HashCardinal, HashCardinal, HashInt64, HashInt64, HashInt64, + HashInteger, HashInteger, HashInt64, HashInt64, HashInt64, HashInt64, HashInt64, HashInt64, HashInt64, HashAnsiStringI, HashAnsiStringI, {$ifdef UNICODE}HashUnicodeStringI{$else}HashAnsiStringI{$endif}, @@ -7585,7 +7729,7 @@ TTextWriterClass = class of TTextWriter; TTextWriterOptions = set of TTextWriterOption; /// may be used to allocate on stack a 8KB work buffer for a TTextWriter - // - via the CreateOwnedStream overloaded constructor + // - via the TTextWriter.CreateOwnedStream overloaded constructor TTextWriterStackBuffer = array[0..8191] of AnsiChar; /// simple writer to a Stream, specialized for the TEXT format @@ -7595,23 +7739,23 @@ TTextWriter = class protected B, BEnd: PUTF8Char; fStream: TStream; - fInitialStreamPosition: cardinal; - fTotalFileSize: cardinal; + fInitialStreamPosition: PtrUInt; + fTotalFileSize: PtrUInt; fCustomOptions: TTextWriterOptions; // internal temporary buffer fTempBufSize: Integer; fTempBuf: PUTF8Char; - fHumanReadableLevel: integer; - fEchoBuf: RawUTF8; - fEchoStart: integer; - fEchos: array of TOnTextWriterEcho; fOnWriteObject: TOnTextWriterObjectProp; /// used by WriteObjectAsString/AddDynArrayJSONAsString methods fInternalJSONWriter: TTextWriter; - function GetLength: cardinal; + fHumanReadableLevel: integer; + fEchoStart: PtrInt; + fEchoBuf: RawUTF8; + fEchos: array of TOnTextWriterEcho; + function GetTextLength: PtrUInt; procedure SetStream(aStream: TStream); procedure SetBuffer(aBuf: pointer; aBufSize: integer); - function EchoFlush: integer; + function EchoFlush: PtrInt; procedure InternalAddFixedAnsi(Source: PAnsiChar; SourceChars: Cardinal; const AnsiToWide: TWordDynArray; Escape: TTextWriterKind); function GetEndOfLineCRLF: boolean; @@ -7716,10 +7860,10 @@ TTextWriter = class procedure Add(c1,c2: AnsiChar); overload; {$ifdef HASINLINE}inline;{$endif} {$ifndef CPU64} // already implemented by Add(Value: PtrInt) method - /// append a 64 bit signed Integer Value as text + /// append a 64-bit signed Integer Value as text procedure Add(Value: Int64); overload; {$endif} - /// append a 32 bit signed Integer Value as text + /// append a 32-bit signed Integer Value as text procedure Add(Value: PtrInt); overload; /// append a boolean Value as text // - write either 'true' or 'false' @@ -7829,7 +7973,7 @@ TTextWriter = class /// append an Integer Value as a 2 digits String with comma procedure Add2(Value: integer); /// append the current UTC date and time, in a log-friendly format - // - e.g. append '20110325 19241502 ' + // - e.g. append '20110325 19241502' // - you may set LocalTime=TRUE to write the local date and time instead // - this method is very fast, and avoid most calculation or API calls procedure AddCurrentLogTime(LocalTime: boolean); @@ -7843,14 +7987,12 @@ TTextWriter = class procedure AddLine(const Text: shortstring); /// append an UTF-8 String, with no JSON escaping procedure AddString(const Text: RawUTF8); - {$ifdef HASINLINE}inline;{$endif} /// append several UTF-8 strings procedure AddStrings(const Text: array of RawUTF8); overload; /// append an UTF-8 string several times procedure AddStrings(const Text: RawUTF8; count: integer); overload; /// append a ShortString procedure AddShort(const Text: ShortString); - {$ifdef HASINLINE}inline;{$endif} /// append a sub-part of an UTF-8 String // - emulates AddString(copy(Text,start,len)) procedure AddStringCopy(const Text: RawUTF8; start,len: integer); @@ -7884,7 +8026,7 @@ TTextWriter = class // - Instance must be not nil // - overriden version in TJSONSerializer would implement IncludeUnitName procedure AddInstancePointer(Instance: TObject; SepChar: AnsiChar; - IncludeUnitName: boolean); virtual; + IncludeUnitName, IncludePointer: boolean); virtual; /// append a quoted string as JSON, with in-place decoding // - if QuotedString does not start with ' or ", it will written directly // (i.e. expects to be a number, or null/true/false constants) @@ -7905,13 +8047,17 @@ TTextWriter = class /// write some record content as binary, Base64 encoded with our magic prefix procedure WrRecord(const Rec; TypeInfo: pointer); /// write some #0 ended UTF-8 text, according to the specified format + // - if Escape is a constant, consider calling directly AddNoJSONEscape, + // AddJSONEscape or AddOnSameLine methods procedure Add(P: PUTF8Char; Escape: TTextWriterKind); overload; /// write some #0 ended UTF-8 text, according to the specified format + // - if Escape is a constant, consider calling directly AddNoJSONEscape, + // AddJSONEscape or AddOnSameLine methods procedure Add(P: PUTF8Char; Len: PtrInt; Escape: TTextWriterKind); overload; - {$ifdef HASINLINE}inline;{$endif} /// write some #0 ended Unicode text as UTF-8, according to the specified format + // - if Escape is a constant, consider calling directly AddNoJSONEscapeW, + // AddJSONEscapeW or AddOnSameLineW methods procedure AddW(P: PWord; Len: PtrInt; Escape: TTextWriterKind); - {$ifdef HASINLINE}inline;{$endif} /// append some UTF-8 encoded chars to the buffer, from the main AnsiString type // - use the current system code page for AnsiString parameter procedure AddAnsiString(const s: AnsiString; Escape: TTextWriterKind); overload; @@ -7990,8 +8136,12 @@ TTextWriter = class /// fast conversion from binary data into quoted MSB lowercase hexa chars // - up to the internal buffer bytes may be converted procedure AddBinToHexDisplayQuoted(Bin: pointer; BinBytes: integer); - /// add the pointer into hexa chars, ready to be displayed - procedure AddPointer(P: PtrUInt); + /// append a Value as significant hexadecimal text + // - append its minimal size, i.e. excluding highest bytes containing 0 + // - use GetNextItemHexa() to decode such a text value + procedure AddBinToHexDisplayMinChars(Bin: pointer; BinBytes: PtrInt); + /// add the pointer into significant hexa chars, ready to be displayed + procedure AddPointer(P: PtrUInt); {$ifdef HASINLINE}inline;{$endif} /// write a byte as hexa chars procedure AddByteToHex(Value: byte); /// write a Int18 value (0..262143) as 3 chars @@ -8010,8 +8160,7 @@ TTextWriter = class /// append some UTF-8 encoded chars to the buffer, from a generic string type // - faster than AddJSONEscape(pointer(StringToUTF8(string)) // - escapes chars according to the JSON RFC - procedure AddJSONEscapeString(const s: string); - {$ifdef HASINLINE}inline;{$endif} + procedure AddJSONEscapeString(const s: string); {$ifdef HASINLINE}inline;{$endif} /// append some UTF-8 encoded chars to the buffer, from the main AnsiString type // - escapes chars according to the JSON RFC procedure AddJSONEscapeAnsiString(const s: AnsiString); @@ -8019,8 +8168,7 @@ TTextWriter = class // - faster than AddNoJSONEscape(pointer(StringToUTF8(string)) // - don't escapes chars according to the JSON RFC // - will convert the Unicode chars into UTF-8 - procedure AddNoJSONEscapeString(const s: string); - {$ifdef UNICODE}inline;{$endif} + procedure AddNoJSONEscapeString(const s: string); {$ifdef UNICODE}inline;{$endif} /// append some Unicode encoded chars to the buffer // - if Len is 0, Len is calculated from zero-ended widechar // - escapes chars according to the JSON RFC @@ -8337,7 +8485,7 @@ TTextWriter = class /// how many bytes were currently written on disk // - excluding the bytes in the internal buffer // - see TextLength for the total number of bytes, on both disk and memory - property WrittenBytes: cardinal read fTotalFileSize; + property WrittenBytes: PtrUInt read fTotalFileSize; /// the last char appended is canceled procedure CancelLastChar; overload; {$ifdef HASINLINE}inline;{$endif} @@ -8355,7 +8503,7 @@ TTextWriter = class /// count of added bytes to the stream // - see PendingBytes for the number of bytes currently in the memory buffer // or WrittenBytes for the number of bytes already written to disk - property TextLength: cardinal read GetLength; + property TextLength: PtrUInt read GetTextLength; /// define how AddEndOfLine method stores its line feed characters // - by default (FALSE), it will append a LF (#10) char to the buffer // - you can set this property to TRUE, so that CR+LF (#13#10) chars will @@ -8392,129 +8540,6 @@ function SaveJSON(const Value; TypeInfo: pointer; EnumSetsAsText: boolean=false): RawUTF8; overload; {$ifdef HASINLINE}inline;{$endif} -type - /// abstract TSynPersistent class allowing safe storage of a password - // - the associated Password, e.g. for storage or transmission encryption - // will be persisted encrypted with a private key (which can be customized) - // - if default simple symmetric encryption is not enough, you may define - // a custom TSynPersistentWithPasswordUserCrypt callback, e.g. to - // SynCrypto's CryptDataForCurrentUser, for hardened password storage - // - a published property should be defined as such in inherited class: - // ! property PasswordPropertyName: RawUTF8 read fPassword write fPassword; - // - use the PassWordPlain property to access to its uncyphered value - TSynPersistentWithPassword = class(TSynPersistent) - protected - fPassWord: RawUTF8; - fKey: cardinal; - function GetKey: cardinal; - {$ifdef HASINLINE}inline;{$endif} - function GetPassWordPlain: RawUTF8; - function GetPassWordPlainInternal(AppSecret: RawUTF8): RawUTF8; - procedure SetPassWordPlain(const Value: RawUTF8); - public - /// finalize the instance - destructor Destroy; override; - /// this class method could be used to compute the encrypted password, - // ready to be stored as JSON, according to a given private key - class function ComputePassword(const PlainPassword: RawUTF8; - CustomKey: cardinal=0): RawUTF8; overload; - /// this class method could be used to compute the encrypted password from - // a binary digest, ready to be stored as JSON, according to a given private key - // - just a wrapper around ComputePassword(BinToBase64URI()) - class function ComputePassword(PlainPassword: pointer; PlainPasswordLen: integer; - CustomKey: cardinal=0): RawUTF8; overload; - /// this class method could be used to decrypt a password, stored as JSON, - // according to a given private key - // - may trigger a ESynException if the password was stored using a custom - // TSynPersistentWithPasswordUserCrypt callback, and the current user - // doesn't match the expected user stored in the field - class function ComputePlainPassword(const CypheredPassword: RawUTF8; - CustomKey: cardinal=0; const AppSecret: RawUTF8=''): RawUTF8; - /// low-level function used to identify if a given field is a Password - // - this method is used e.g. by TJSONSerializer.WriteObject to identify the - // password field, since its published name is set by the inherited classes - function GetPasswordFieldAddress: pointer; - {$ifdef HASINLINE}inline;{$endif} - /// the private key used to cypher the password storage on serialization - // - application can override the default 0 value at runtime - property Key: cardinal read GetKey write fKey; - /// access to the associated unencrypted Password value - // - read may trigger a ESynException if the password was stored using a - // custom TSynPersistentWithPasswordUserCrypt callback, and the current user - // doesn't match the expected user stored in the field - property PasswordPlain: RawUTF8 read GetPassWordPlain write SetPassWordPlain; - end; - - /// could be used to store a credential pair, as user name and password - // - password will be stored with TSynPersistentWithPassword encryption - TSynUserPassword = class(TSynPersistentWithPassword) - protected - fUserName: RawUTF8; - published - /// the associated user name - property UserName: RawUTF8 read FUserName write FUserName; - /// the associated encrypted password - // - use the PasswordPlain public property to access to the uncrypted password - property Password: RawUTF8 read FPassword write FPassword; - end; - - /// handle safe storage of any connection properties - // - would be used by SynDB.pas to serialize TSQLDBConnectionProperties, or - // by mORMot.pas to serialize TSQLRest instances - // - the password will be stored as Base64, after a simple encryption as - // defined by TSynPersistentWithPassword - // - typical content could be: - // $ { - // $ "Kind": "TSQLDBSQLite3ConnectionProperties", - // $ "ServerName": "server", - // $ "DatabaseName": "", - // $ "User": "", - // $ "Password": "PtvlPA==" - // $ } - // - the "Kind" value will be used to let the corresponding TSQLRest or - // TSQLDBConnectionProperties NewInstance*() class methods create the - // actual instance, from its class name - TSynConnectionDefinition = class(TSynPersistentWithPassword) - protected - fKind: string; - fServerName: RawUTF8; - fDatabaseName: RawUTF8; - fUser: RawUTF8; - public - /// unserialize the database definition from JSON - // - as previously serialized with the SaveToJSON method - // - you can specify a custom Key used for password encryption, if the - // default value is not safe enough for you - // - this method won't use JSONToObject() so avoid any dependency to mORMot.pas - constructor CreateFromJSON(const JSON: RawUTF8; Key: cardinal=0); virtual; - /// serialize the database definition as JSON - // - this method won't use ObjectToJSON() so avoid any dependency to mORMot.pas - function SaveToJSON: RawUTF8; virtual; - published - /// the class name implementing the connection or TSQLRest instance - // - will be used to instantiate the expected class type - property Kind: string read fKind write fKind; - /// the associated server name (or file, for SQLite3) to be connected to - property ServerName: RawUTF8 read fServerName write fServerName; - /// the associated database name (if any), or additional options - property DatabaseName: RawUTF8 read fDatabaseName write fDatabaseName; - /// the associated User Identifier (if any) - property User: RawUTF8 read fUser write fUser; - /// the associated Password, e.g. for storage or transmission encryption - // - will be persisted encrypted with a private key - // - use the PassWordPlain property to access to its uncyphered value - property Password: RawUTF8 read fPassword write fPassword; - end; - -var - /// function prototype to customize TSynPersistent class password storage - // - is called when 'user1:base64pass1,user2:base64pass2' layout is found, - // and the current user logged on the system is user1 or user2 - // - you should not call this low-level method, but assign e.g. from SynCrypto: - // $ TSynPersistentWithPasswordUserCrypt := CryptDataForCurrentUser; - TSynPersistentWithPasswordUserCrypt: - function(const Data,AppServer: RawByteString; Encrypt: boolean): RawByteString; - /// will serialize any TObject into its UTF-8 JSON representation /// - serialize as JSON the published integer, Int64, floating point values, // TDateTime (stored as ISO 8601 text), string, variant and enumerate @@ -8545,7 +8570,7 @@ function ObjectsToJSON(const Names: array of RawUTF8; const Values: array of TOb // - internally make use of an efficient hashing algorithm for fast response // (i.e. TSynNameValue will use the TDynArrayHashed wrapper mechanism) // - this class is thread-safe if you use properly the associated Safe lock - TSynCache = class + TSynCache = class(TSynPersistentLock) protected /// last index in fNameValue.List[] if was added by Find() // - contains -1 if no previous immediate call to Find() @@ -8556,7 +8581,6 @@ TSynCache = class fMaxRamUsed: cardinal; fTimeoutSeconds: cardinal; fTimeoutTix: cardinal; - fSafe: TSynLocker; procedure ResetIfNeeded; public /// initialize the internal storage @@ -8567,9 +8591,7 @@ TSynCache = class // - by default, there is no timeout period, but you may specify a number of // seconds of inactivity (i.e. no Add call) after which the cache is flushed constructor Create(aMaxCacheRamUsed: cardinal=16 shl 20; - aCaseSensitive: boolean=false; aTimeoutSeconds: cardinal=0); - /// finalize the internal storage - destructor Destroy; override; + aCaseSensitive: boolean=false; aTimeoutSeconds: cardinal=0); reintroduce; /// find a Key in the cache entries // - return '' if nothing found: you may call Add() just after to insert // the expected value in the cache @@ -8605,7 +8627,7 @@ TSynCache = class // ! finally // ! cache.Safe.Unlock; // ! end; - property Safe: TSynLocker read fSafe; + property Safe: PSynLocker read fSafe; /// the current global size of Values in RAM cache, in bytes property RamUsed: cardinal read fRamUsed; /// the maximum RAM to be used for values, in bytes @@ -9139,11 +9161,11 @@ TAlgoCompress = class(TSynPersistent) {$ifdef HASINLINE}inline;{$endif} /// compress a memory buffer with crc32c hashing to a RawByteString function Compress(const Plain: RawByteString; CompressionSizeTrigger: integer=100; - CheckMagicForCompressed: boolean=false): RawByteString; overload; + CheckMagicForCompressed: boolean=false; BufferOffset: integer=0): RawByteString; overload; {$ifdef HASINLINE}inline;{$endif} /// compress a memory buffer with crc32c hashing to a RawByteString function Compress(Plain: PAnsiChar; PlainLen: integer; CompressionSizeTrigger: integer=100; - CheckMagicForCompressed: boolean=false): RawByteString; overload; + CheckMagicForCompressed: boolean=false; BufferOffset: integer=0): RawByteString; overload; /// compress a memory buffer with crc32c hashing // - supplied Comp buffer should contain at least CompressDestLen(PlainLen) bytes function Compress(Plain, Comp: PAnsiChar; PlainLen, CompLen: integer; @@ -9156,7 +9178,8 @@ TAlgoCompress = class(TSynPersistent) function CompressToBytes(Plain: PAnsiChar; PlainLen: integer; CompressionSizeTrigger: integer=100; CheckMagicForCompressed: boolean=false): TByteDynArray; overload; /// uncompress a RawByteString memory buffer with crc32c hashing - function Decompress(const Comp: RawByteString; Load: TAlgoCompressLoad=aclNormal): RawByteString; overload; + function Decompress(const Comp: RawByteString; Load: TAlgoCompressLoad=aclNormal; + BufferOffset: integer=0): RawByteString; overload; {$ifdef HASINLINE}inline;{$endif} /// uncompress a RawByteString memory buffer with crc32c hashing // - returns TRUE on success @@ -9164,7 +9187,7 @@ TAlgoCompress = class(TSynPersistent) Load: TAlgoCompressLoad=aclNormal): boolean; /// uncompress a memory buffer with crc32c hashing procedure Decompress(Comp: PAnsiChar; CompLen: integer; out Result: RawByteString; - Load: TAlgoCompressLoad=aclNormal); overload; + Load: TAlgoCompressLoad=aclNormal; BufferOffset: integer=0); overload; /// uncompress a RawByteString memory buffer with crc32c hashing function Decompress(const Comp: TByteDynArray): RawByteString; overload; {$ifdef HASINLINE}inline;{$endif} @@ -9211,6 +9234,11 @@ TAlgoCompress = class(TSynPersistent) /// get the TAlgoCompress instance corresponding to the AlgoID stored // in the supplied compressed buffer // - returns nil if no algorithm was identified + // - also identifies "stored" content in IsStored variable + class function Algo(Comp: PAnsiChar; CompLen: integer; out IsStored: boolean): TAlgoCompress; overload; + /// get the TAlgoCompress instance corresponding to the AlgoID stored + // in the supplied compressed buffer + // - returns nil if no algorithm was identified class function Algo(const Comp: RawByteString): TAlgoCompress; overload; {$ifdef HASINLINE}inline;{$endif} /// get the TAlgoCompress instance corresponding to the AlgoID stored @@ -9222,6 +9250,11 @@ TAlgoCompress = class(TSynPersistent) // - returns nil if no algorithm was identified // - stored content is identified as TAlgoSynLZ class function Algo(AlgoID: byte): TAlgoCompress; overload; + /// quickly validate a compressed buffer content, without uncompression + // - extract the TAlgoCompress, and call DecompressHeader() to check the + // hash of the compressed data, and return then uncompressed size + // - returns 0 on error (e.g. unknown algorithm or incorrect hash) + class function UncompressedSize(const Comp: RawByteString): integer; /// returns the algorithm name, from its classname // - e.g. TAlgoSynLZ->'synlz' TAlgoLizard->'lizard' nil->'none' function AlgoName: TShort16; @@ -9293,7 +9326,7 @@ TAlgoCompressWithNoDestLen = class(TAlgoCompress) // - TDynArray is a wrapper which do not store anything, whereas this class // is able to store both keys and values, and provide convenient methods to // access the stored data, including JSON serialization and binary storage - TSynDictionary = class(TSynPersistentLocked) + TSynDictionary = class(TSynPersistentLock) protected fKeys: TDynArrayHashed; fValues: TDynArray; @@ -9417,6 +9450,14 @@ TSynDictionary = class(TSynPersistentLocked) // were not found // - this method is thread-safe, since it will lock the instance function FindInArray(const aKey, aArrayValue): boolean; + /// search of a stored key by its associated key, and return a key local copy + // - won't use any hashed index but TDynArray.IndexOf over fValues, + // so is much slower than FindAndCopy() + // - will update the associated timeout value of the entry, unless + // aUpdateTimeOut is set to false + // - so this method is thread-safe + // - returns TRUE if aValue was found, FALSE if no match exists + function FindKeyFromValue(const aValue; out aKey; aUpdateTimeOut: boolean=true): boolean; /// add aArrayValue item within a dynamic-array value associated via aKey // - expect the stored value to be a dynamic array itself // - would search for aKey as primary key, then use TDynArray.Add @@ -9451,8 +9492,9 @@ TSynDictionary = class(TSynPersistentLocked) {$ifndef DELPHI5OROLDER} /// make a copy of the stored values // - this method is thread-safe, since it will lock the instance during copy + // - resulting length(Dest) will match the exact values count // - T*ObjArray will be reallocated and copied by content (using a temporary - // JSON serialization), unless ObjArrayByRef is true + // JSON serialization), unless ObjArrayByRef is true and pointers are copied procedure CopyValues(out Dest; ObjArrayByRef: boolean=false); {$endif DELPHI5OROLDER} /// serialize the content as a "key":value JSON object @@ -9480,7 +9522,7 @@ TSynDictionary = class(TSynPersistentLocked) function LoadFromBinary(const binary: RawByteString): boolean; /// can be assigned to OnCanDeleteDeprecated to check TSynPersistentLock(aValue).Safe.IsLocked class function OnCanDeleteSynPersistentLock(const aKey, aValue; aIndex: integer): boolean; - /// can be assigned to OnCanDeleteDeprecated to check TSynPersistentLocked(aValue).Safe.IsLocked + /// can be assigned to OnCanDeleteDeprecated to check TSynPersistentLock(aValue).Safe.IsLocked class function OnCanDeleteSynPersistentLocked(const aKey, aValue; aIndex: integer): boolean; /// returns how many items are currently stored in this dictionary // - this method is thread-safe @@ -9512,19 +9554,23 @@ TSynDictionary = class(TSynPersistentLocked) /// thread-safe FIFO (First-In-First-Out) in-order queue of records // - uses internally a dynamic array storage, with a sliding algorithm // (more efficient than the FPC or Delphi TQueue) - TSynQueue = class(TSynPersistentLocked) + TSynQueue = class(TSynPersistentLock) protected fValues: TDynArray; fValueVar: pointer; fCount, fFirst, fLast: integer; + fWaitPopFlags: set of (wpfDestroying); + fWaitPopCounter: integer; procedure InternalGrow; + function InternalDestroying(incPopCounter: integer): boolean; + function InternalWaitDone(endtix: Int64; const idle: TThreadMethod): boolean; public /// initialize the queue storage // - aTypeInfo should be a dynamic array TypeInfo() RTTI pointer, which // would store the values within this TSynQueue instance constructor Create(aTypeInfo: pointer); reintroduce; virtual; /// finalize the storage - // - would release all internal stored values + // - would release all internal stored values, and call WaitPopFinalize destructor Destroy; override; /// store one item into the queue // - this method is thread-safe, since it will lock the instance @@ -9541,7 +9587,28 @@ TSynQueue = class(TSynPersistentLocked) // - returns false if the queue is empty // - this method is thread-safe, since it will lock the instance function Peek(out aValue): boolean; + /// waiting extract of one item from the queue, as FIFO (First-In-First-Out) + // - returns true if aValue has been filled with a pending item within the + // specified aTimeoutMS time + // - returns false if nothing was pushed into the queue in time, or if + // WaitPopFinalize has been called + // - aWhenIdle could be assigned e.g. to VCL/LCL Application.ProcessMessages + // - this method is thread-safe, but will lock the instance only if needed + function WaitPop(aTimeoutMS: integer; const aWhenIdle: TThreadMethod; out aValue): boolean; + /// waiting lookup of one item from the queue, as FIFO (First-In-First-Out) + // - returns a pointer to a pending item within the specified aTimeoutMS + // time - the Safe.Lock is still there, so that caller could check its content, + // then call Pop() if it is the expected one, and eventually always call Safe.Unlock + // - returns nil if nothing was pushed into the queue in time + // - this method is thread-safe, but will lock the instance only if needed + function WaitPeekLocked(aTimeoutMS: integer; const aWhenIdle: TThreadMethod): pointer; + /// ensure any pending or future WaitPop() returns immediately as false + // - is always called by Destroy destructor + // - could be also called e.g. from an UI OnClose event to avoid any lock + // - this method is thread-safe, but will lock the instance only if needed + procedure WaitPopFinalize; /// delete all items currently stored in this queue, and void its capacity + // - this method is thread-safe, since it will lock the instance procedure Clear; /// initialize a dynamic array with the stored queue items // - aDynArrayValues should be a variable defined as aTypeInfo from Create @@ -9575,8 +9642,8 @@ TSynQueue = class(TSynPersistentLocked) type /// handle memory mapping of a file content - {$ifdef UNICODE}TMemoryMap = record{$else}TMemoryMap = object{$endif} - private + {$ifdef FPC_OR_UNICODE}TMemoryMap = record private + {$else}TMemoryMap = object protected{$endif} fBuf: PAnsiChar; fBufSize: PtrUInt; fFile: THandle; @@ -9921,7 +9988,9 @@ TFileBufferWriter = class // ! TFileBufferWriter.Create(TRawByteStringStream) // - if algo is left to its default nil, will use global AlgoSynLZ // - features direct compression from internal buffer, if stream was not used - function FlushAndCompress(nocompression: boolean=false; algo: TAlgoCompress=nil): RawByteString; + // - BufferOffset could be set to reserve some bytes before the compressed buffer + function FlushAndCompress(nocompression: boolean=false; algo: TAlgoCompress=nil; + BufferOffset: integer=0): RawByteString; /// rewind the Stream to the position when Create() was called // - note that this does not clear the Stream content itself, just // move back its writing position to its initial place @@ -9939,7 +10008,7 @@ TFileBufferWriter = class /// this structure can be used to speed up reading from a file // - use internaly memory mapped files for a file up to 2 GB (Windows has // problems with memory mapped files bigger than this size limit - at least - // with 32 bit executables) - but sometimes, Windows fails to allocate + // with 32-bit executables) - but sometimes, Windows fails to allocate // more than 512 MB for a memory map, because it does lack of contiguous // memory space: in this case, we fall back on direct file reading // - maximum handled file size has no limit (but will use slower direct @@ -9949,8 +10018,8 @@ TFileBufferWriter = class // - is defined either as an object either as a record, due to a bug // in Delphi 2009/2010 compiler (at least): this structure is not initialized // if defined as an object on the stack, but will be as a record :( - {$ifdef UNICODE}TFileBufferReader = record{$else}TFileBufferReader = object{$endif} - private + {$ifdef FPC_OR_UNICODE}TFileBufferReader = record private + {$else}TFileBufferReader = object protected{$endif} fCurrentPos: PtrUInt; fMap: TMemoryMap; /// get Isize + buffer from current memory map or fBufTemp into (P,PEnd) @@ -10052,111 +10121,6 @@ {$ifdef UNICODE}TFileBufferReader = record{$else}TFileBufferReader = object{$e property MappedBuffer: PAnsiChar read fMap.fBuf; end; - /// safe decoding of a TFileBufferWriter content - // - similar to TFileBufferReader, but faster and only for in-memory buffer - // - is also safer, since will check for reaching end of buffer - // - raise a EFastReader exception on decoding error (e.g. if a buffer - // overflow may occur) or call OnErrorOverflow/OnErrorData event handlers - {$ifdef UNICODE}TFastReader = record{$else}TFastReader = object{$endif} - public - /// the current position in the memory - P: PAnsiChar; - /// the last position in the buffer - Last: PAnsiChar; - /// use this event to customize the ErrorOverflow process - OnErrorOverflow: procedure of object; - /// use this event to customize the ErrorData process - OnErrorData: procedure(const fmt: RawUTF8; const args: array of const) of object; - /// some opaque value, which may be a version number to define the binary layout - Tag: PtrInt; - /// initialize the reader from a memory block - procedure Init(Buffer: pointer; Len: integer); overload; - /// initialize the reader from a RawByteString content - procedure Init(const Buffer: RawByteString); overload; - /// raise a EFastReader with an "overflow" error message - procedure ErrorOverflow; - /// raise a EFastReader with an "incorrect data" error message - procedure ErrorData(const fmt: RawUTF8; const args: array of const); - /// read the next 32-bit signed value from the buffer - function VarInt32: integer; {$ifdef HASINLINE}inline;{$endif} - /// read the next 32-bit unsigned value from the buffer - function VarUInt32: cardinal; - /// try to read the next 32-bit signed value from the buffer - // - don't change the current position - function PeekVarInt32(out value: PtrInt): boolean; {$ifdef HASINLINE}inline;{$endif} - /// try to read the next 32-bit unsigned value from the buffer - // - don't change the current position - function PeekVarUInt32(out value: PtrUInt): boolean; - /// read the next 32-bit unsigned value from the buffer - // - this version won't call ErrorOverflow, but return false on error - // - returns true on read success - function VarUInt32Safe(out Value: cardinal): boolean; - /// read the next 64-bit signed value from the buffer - function VarInt64: Int64; {$ifdef HASINLINE}inline;{$endif} - /// read the next 64-bit unsigned value from the buffer - function VarUInt64: QWord; - /// read the next RawUTF8 value from the buffer - function VarUTF8: RawUTF8; overload; - /// read the next RawUTF8 value from the buffer - procedure VarUTF8(out result: RawUTF8); overload; - /// read the next RawUTF8 value from the buffer - // - this version won't call ErrorOverflow, but return false on error - // - returns true on read success - function VarUTF8Safe(out Value: RawUTF8): boolean; - /// read the next RawByteString value from the buffer - function VarString: RawByteString; {$ifdef HASINLINE}inline;{$endif} - /// read the next pointer and length value from the buffer - procedure VarBlob(out result: TValueResult); overload; {$ifdef HASINLINE}inline;{$endif} - /// read the next pointer and length value from the buffer - function VarBlob: TValueResult; overload; {$ifdef HASINLINE}inline;{$endif} - /// read the next ShortString value from the buffer - function VarShortString: shortstring; {$ifdef HASINLINE}inline;{$endif} - /// fast ignore the next VarUInt32/VarInt32/VarUInt64/VarInt64 value - // - don't raise any exception, so caller could check explicitly for any EOF - procedure VarNextInt; overload; {$ifdef HASINLINE}inline;{$endif} - /// fast ignore the next count VarUInt32/VarInt32/VarUInt64/VarInt64 values - // - don't raise any exception, so caller could check explicitly for any EOF - procedure VarNextInt(count: integer); overload; - /// read the next byte from the buffer - function NextByte: byte; {$ifdef HASINLINE}inline;{$endif} - /// read the next 4 bytes from the buffer as a 32-bit unsigned value - function Next4: cardinal; {$ifdef HASINLINE}inline;{$endif} - /// read the next 8 bytes from the buffer as a 64-bit unsigned value - function Next8: Qword; {$ifdef HASINLINE}inline;{$endif} - /// consumes the next byte from the buffer, if matches a given value - function NextByteEquals(Value: byte): boolean; {$ifdef HASINLINE}inline;{$endif} - /// returns the current position, and move ahead the specified bytes - function Next(DataLen: PtrInt): pointer; {$ifdef HASINLINE}inline;{$endif} - /// returns the current position, and move ahead the specified bytes - function NextSafe(out Data: Pointer; DataLen: PtrInt): boolean; {$ifdef HASINLINE}inline;{$endif} - {$ifndef NOVARIANTS} - /// read the next variant from the buffer - // - is a wrapper around VariantLoad(), so may suffer from buffer overflow - procedure NextVariant(var Value: variant; CustomVariantOptions: pointer); - /// read the JSON-serialized TDocVariant from the buffer - // - matches TFileBufferWriter.WriteDocVariantData format - procedure NextDocVariantData(out Value: variant; CustomVariantOptions: pointer); - {$ifdef FPC}inline;{$endif} - {$endif NOVARIANTS} - /// copy data from the current position, and move ahead the specified bytes - procedure Copy(out Dest; DataLen: PtrInt); {$ifdef HASINLINE}inline;{$endif} - /// copy data from the current position, and move ahead the specified bytes - // - this version won't call ErrorOverflow, but return false on error - // - returns true on read success - function CopySafe(out Dest; DataLen: PtrInt): boolean; - /// apply TDynArray.LoadFrom on the buffer - // - will unserialize a previously appended dynamic array, e.g. as - // ! aWriter.WriteDynArray(DA); - procedure Read(var DA: TDynArray; NoCheckHash: boolean=false); - /// retrieved cardinal values encoded with TFileBufferWriter.WriteVarUInt32Array - // - only supports wkUInt32, wkVarInt32, wkVarUInt32 kind of encoding - function ReadVarUInt32Array(var Values: TIntegerDynArray): PtrInt; - /// returns TRUE if the current position is the end of the input stream - function EOF: boolean; {$ifdef HASINLINE}inline;{$endif} - /// returns remaining length (difference between Last and P) - function RemainingLength: PtrUInt; {$ifdef HASINLINE}inline;{$endif} - end; - /// FileSeek() overloaded function, working with huge files // - Delphi FileSeek() is buggy -> use this function to safe access files > 2 GB // (thanks to sanyin for the report) @@ -10231,15 +10195,23 @@ procedure JSONEncodeNameSQLValue(const Name,SQLValue: RawUTF8; var result: RawUT type /// points to one value of raw UTF-8 content, decoded from a JSON buffer // - used e.g. by JSONDecode() overloaded function to returns names/values - {$ifdef UNICODE}TValuePUTF8Char = record{$else}TValuePUTF8Char = object{$endif} + {$ifdef FPC_OR_UNICODE}TValuePUTF8Char = record{$else}TValuePUTF8Char = object{$endif} public + /// a pointer to the actual UTF-8 text Value: PUTF8Char; + /// how many UTF-8 bytes are stored in Value ValueLen: PtrInt; + /// convert the value into a UTF-8 string procedure ToUTF8(var Text: RawUTF8); overload; {$ifdef HASINLINE}inline;{$endif} + /// convert the value into a UTF-8 string function ToUTF8: RawUTF8; overload; {$ifdef HASINLINE}inline;{$endif} + /// convert the value into a VCL/generic string function ToString: string; + /// convert the value into a signed integer function ToInteger: PtrInt; {$ifdef HASINLINE}inline;{$endif} + /// convert the value into an unsigned integer function ToCardinal: PtrUInt; {$ifdef HASINLINE}inline;{$endif} + /// will call IdemPropNameU() over the stored text Value function Idem(const Text: RawUTF8): boolean; {$ifdef HASINLINE}inline;{$endif} end; /// used e.g. by JSONDecode() overloaded function to returns values @@ -10249,9 +10221,13 @@ {$ifdef UNICODE}TValuePUTF8Char = record{$else}TValuePUTF8Char = object{$endif /// store one name/value pair of raw UTF-8 content, from a JSON buffer // - used e.g. by JSONDecode() overloaded function to returns names/values TNameValuePUTF8Char = record + /// a pointer to the actual UTF-8 name text Name: PUTF8Char; + /// a pointer to the actual UTF-8 value text Value: PUTF8Char; + /// how many UTF-8 bytes are stored in Name NameLen: integer; + /// how many UTF-8 bytes are stored in Value ValueLen: integer; end; /// used e.g. by JSONDecode() overloaded function to returns name/value pairs @@ -10896,7 +10872,7 @@ procedure BinToHexLower(Bin: PAnsiChar; BinBytes: integer; var result: RawUTF8); // enough space for at least BinBytes*2 chars // - using this function with Bin^ as an integer value will encode it // in big-endian order (most-signignifican byte first): use it for display -procedure BinToHexDisplayLower(Bin, Hex: PAnsiChar; BinBytes: integer); overload; +procedure BinToHexDisplayLower(Bin, Hex: PAnsiChar; BinBytes: PtrInt); overload; /// fast conversion from binary data into lowercase hexa chars function BinToHexDisplayLower(Bin: PAnsiChar; BinBytes: integer): RawUTF8; overload; @@ -10940,6 +10916,14 @@ function LogEscape(source: PAnsiChar; sourcelen: integer; var temp: TLogEscape; enabled: boolean=true): PAnsiChar; {$ifdef HASINLINE}inline;{$endif} +/// returns a text buffer with the (hexadecimal) chars of the input binary +// - is much slower than LogEscape/EscapeToShort, but has no size limitation +function LogEscapeFull(source: PAnsiChar; sourcelen: integer): RawUTF8; overload; + +/// returns a text buffer with the (hexadecimal) chars of the input binary +// - is much slower than LogEscape/EscapeToShort, but has no size limitation +function LogEscapeFull(const source: RawByteString): RawUTF8; overload; + /// fill a shortstring with the (hexadecimal) chars of the input text/binary function EscapeToShort(source: PAnsiChar; sourcelen: integer): shortstring; overload; @@ -11191,6 +11175,12 @@ function Base64uriToBin(sp: PAnsiChar; len: PtrInt): RawByteString; overload; // unsignificant characters, and replace '+' or '/' by '_' or '-' procedure Base64uriToBin(sp: PAnsiChar; len: PtrInt; var result: RawByteString); overload; +/// fast conversion from Base64-URI encoded text into binary data +// - caller should always execute temp.Done when finished with the data +// - in comparison to Base64 standard encoding, will trim any right-sided '=' +// unsignificant characters, and replace '+' or '/' by '_' or '-' +function Base64uriToBin(sp: PAnsiChar; len: PtrInt; var temp: TSynTempBuffer): boolean; overload; + /// fast conversion from Base64-URI encoded text into binary data // - in comparison to Base64 standard encoding, will trim any right-sided '=' // unsignificant characters, and replace '+' or '/' by '_' or '-' @@ -11211,6 +11201,7 @@ function Base64uriToBin(const base64: RawByteString; bin: PAnsiChar; binlen: Ptr {$ifdef HASINLINE}inline;{$endif} /// direct low-level decoding of a Base64-URI encoded buffer +// - the buffer is expected to be at least Base64uriToBinLength() bytes long // - returns true if the supplied sp[] buffer has been successfully decoded // into rp[] - will break at any invalid character, so is always safe to use // - in comparison to Base64 standard encoding, will trim any right-sided '=' @@ -11298,22 +11289,46 @@ function UInt2DigitsToShortFast(Value: byte): TShort4; /// compute CRC16-CCITT checkum on the supplied buffer -// - i.e. 16-bit CRC-CCITT, with polynomial x^16 + x^12 + x^5 + 1 ($1021) and -// $ffff as initial value +// - i.e. 16-bit CRC-CCITT, with polynomial x^16 + x^12 + x^5 + 1 ($1021) +// and $ffff as initial value // - this version is not optimized for speed, but for correctness function crc16(Data: PAnsiChar; Len: integer): cardinal; -// our custom hash function, specialized for Text comparaison -// - has less colision than Adler32 for short strings -// - is faster than CRC32 or Adler32, since use DQWord (128 bytes) aligned read: -// Hash32() is 2.5 GB/s, kr32() 0.9 GB/s, crc32c() 1.7 GB/s or 4.3 GB/s (SSE4.2) +// our custom hash/checksum function, specialized for Text comparaison +// - it is a checksum algorithm, not a hash function: has less colision than +// Adler32 for short strings, but more than xxhash32 or crc32/crc32c +// - written in simple plain pascal, with no L1 CPU cache pollution // - overloaded version for direct binary content hashing -function Hash32(Data: pointer; Len: integer): cardinal; overload; - -// our custom hash function, specialized for Text comparaison -// - has less colision than Adler32 for short strings -// - is faster than CRC32 or Adler32, since use DQWord (128 bytes) aligned read -// - uses RawByteString for binary content hashing, whatever the codepage is +// - crc32c() has less collision - but is faster only on a SSE4.2 x86_64 CPU; +// some numbers on FPC/Linux64, with a SSE4.2 enabled CPU: +// $ -- 8 bytes buffers +// $ crc32c 8B in 12us i.e. 41,666,666/s, aver. 0us, 317.8 MB/s +// $ xxhash32 8B in 10us i.e. 50,000,000/s, aver. 0us, 381.4 MB/s +// $ hash32 8B in 9us i.e. 55,555,555/s, aver. 0us, 423.8 MB/s +// $ -- 50 bytes buffers +// $ crc32c 50B in 11us i.e. 45,454,545/s, aver. 0us, 2.1 GB/s +// $ xxhash32 50B in 14us i.e. 35,714,285/s, aver. 0us, 1.6 GB/s +// $ hash32 50B in 10us i.e. 50,000,000/s, aver. 0us, 2.3 GB/s +// $ -- 100 bytes buffers +// $ crc32c 100B in 12us i.e. 41,666,666/s, aver. 0us, 3.8 GB/s +// $ xxhash32 100B in 19us i.e. 26,315,789/s, aver. 0us, 2.4 GB/s +// $ hash32 100B in 13us i.e. 38,461,538/s, aver. 0us, 3.5 GB/s +// $ -- 1000 bytes buffers +// $ crc32c 0.9KB in 37us i.e. 13,513,513/s, aver. 0us, 12.5 GB/s +// $ xxhash32 0.9KB in 96us i.e. 5,208,333/s, aver. 0us, 4.8 GB/s +// $ hash32 0.9KB in 62us i.e. 8,064,516/s, aver. 0us, 7.5 GB/s +// $ -- 10000 bytes buffers +// $ crc32c 9.7KB in 282us i.e. 1,773,049/s, aver. 0us, 16.5 GB/s +// $ xxhash32 9.7KB in 927us i.e. 539,374/s, aver. 1us, 5 GB/s +// $ hash32 9.7KB in 487us i.e. 1,026,694/s, aver. 0us, 9.5 GB/s +function Hash32(Data: PCardinalArray; Len: integer): cardinal; overload; + +// our custom hash/checsum function, specialized for Text comparaison +// - it is a checksum algorithm, not a hash function: has less colision than +// Adler32 for short strings, but more than xxhash32 or crc32/crc32c +// - is faster than CRC32 or Adler32, since uses DQWord (128-bit) aligned read +// - overloaded function using RawByteString for binary content hashing, +// whatever the codepage is function Hash32(const Text: RawByteString): cardinal; overload; {$ifdef HASINLINE}inline;{$endif} @@ -11404,6 +11419,12 @@ TQWordRec = record THash128 = array[0..15] of byte; /// pointer to a 128-bit hash value PHash128 = ^THash128; + /// store a 160-bit hash value + // - e.g. a SHA-1 digest + // - consumes 20 bytes of memory + THash160 = array[0..19] of byte; + /// pointer to a 160-bit hash value + PHash160 = ^THash160; /// store a 192-bit hash value // - consumes 24 bytes of memory THash192 = array[0..23] of byte; @@ -11503,8 +11524,10 @@ TQWordRec = record 3: (i0,i1,i2,i3,i4,i5,i6,i7,i8,i9,i10,i11,i12,i13,i14,i15: integer); 4: (c0,c1,c2,c3: TBlock128); 5: (b: THash512); - 6: (b3: THash384); - 7: (w: array[0..31] of word); + 6: (b160: THash160); + 7: (b384: THash384); + 8: (w: array[0..31] of word); + 9: (c: array[0..15] of cardinal); end; /// pointer to 512-bit hash map variable record PHash512Rec = ^THash512Rec; @@ -11568,6 +11591,23 @@ procedure IP6Text(ip6: PHash128; result: PShortString); overload; // - by design, such combined hashes cannot be cascaded procedure crc256c(buf: PAnsiChar; len: cardinal; out crc: THash256); +/// returns TRUE if all 20 bytes of this 160-bit buffer equal zero +// - e.g. a SHA-1 digest +function IsZero(const dig: THash160): boolean; overload; + {$ifdef HASINLINE}inline;{$endif} + +/// returns TRUE if all 20 bytes of both 160-bit buffers do match +// - e.g. a SHA-1 digest +// - this function is not sensitive to any timing attack, so is designed +// for cryptographic purpose +function IsEqual(const A,B: THash160): boolean; overload; + {$ifdef HASINLINE}inline;{$endif} + +/// fill all 20 bytes of this 160-bit buffer with zero +// - may be used to cleanup stack-allocated content +// ! ... finally FillZero(digest); end; +procedure FillZero(out dig: THash160); overload; + /// returns TRUE if all 32 bytes of this 256-bit buffer equal zero // - e.g. a SHA-256 digest, or a TECCSignature result function IsZero(const dig: THash256): boolean; overload; @@ -11650,6 +11690,7 @@ procedure FillZero(var dest; count: PtrInt); overload; /// fast computation of two 64-bit unsigned integers into a 128-bit value procedure mul64x64(const left, right: QWord; out product: THash128Rec); + {$ifdef FPC}{$ifndef CPUX64}inline;{$endif CPUX64}{$endif FPC} type /// the potential features, retrieved from an Intel CPU @@ -11670,11 +11711,11 @@ procedure mul64x64(const left, right: QWord; out product: THash128Rec); cfBMI2, cfERMS, cfINVPCID, cfRTM, cfPQM, cf_b13, cfMPX, cfPQE, cfAVX512F, cfAVX512DQ, cfRDSEED, cfADX, cfSMAP, cfAVX512IFMA, cfPCOMMIT, cfCLFLUSH, cfCLWB, cfIPT, cfAVX512PF, cfAVX512ER, cfAVX512CD, cfSHA, cfAVX512BW, cfAVX512VL, - cfPREFW1, cfAVX512VBMI, cfUMIP, cfPKU, cfOSPKE, cf_c05, cf_c06, cf_c07, - cf_c08, cf_c09, cf_c10, cf_c11, cf_c12, cf_c13, cfAVX512VPC, cf_c15, + cfPREFW1, cfAVX512VBMI, cfUMIP, cfPKU, cfOSPKE, cf_c05, cfAVX512VBMI2, cf_c07, + cfGFNI, cfVAES, cfVCLMUL, cfAVX512NNI, cfAVX512BITALG, cf_c13, cfAVX512VPC, cf_c15, cf_cc16, cf_c17, cf_c18, cf_c19, cf_c20, cf_c21, cfRDPID, cf_c23, cf_c24, cf_c25, cf_c26, cf_c27, cf_c28, cf_c29, cfSGXLC, cf_c31, - cf_d0, cf_d1, cfAVX512NNI, cfAVX512MAS, cf_d4, cf_d5, cf_d6, cf_d7); + cf_d0, cf_d1, cfAVX512NNIW, cfAVX512MAS, cf_d4, cf_d5, cf_d6, cf_d7); /// all features, as retrieved from an Intel CPU TIntelCpuFeatures = set of TIntelCpuFeature; @@ -11697,7 +11738,7 @@ function ToText(const aIntelCPUFeatures: TIntelCpuFeatures; function crc32csse42(crc: cardinal; buf: PAnsiChar; len: cardinal): cardinal; {$endif CPUINTEL} -/// naive symmetric encryption scheme using a 32 bit key +/// naive symmetric encryption scheme using a 32-bit key // - fast, but not very secure, since uses crc32ctab[] content as master cypher // key: consider using SynCrypto proven AES-based algorithms instead procedure SymmetricEncrypt(key: cardinal; var data: RawByteString); @@ -11707,7 +11748,9 @@ procedure SymmetricEncrypt(key: cardinal; var data: RawByteString); var /// compute CRC32C checksum on the supplied buffer - // - this variable will use the fastest mean available, e.g. SSE 4.2 + // - result is not compatible with zlib's crc32() - Intel/SCSI CRC32C is not + // the same polynom - but will use the fastest mean available, e.g. SSE 4.2, + // to achieve up to 16GB/s with the optimized implementation from SynCrypto.pas // - you should use this function instead of crc32cfast() or crc32csse42() crc32c: THasher; /// compute CRC32C checksum on one 32-bit unsigned integer @@ -11734,16 +11777,31 @@ function crc32cUTF8ToHex(const str: RawUTF8): RawUTF8; InterningHasher: THasher; /// retrieve a particular bit status from a bit array +// - this function can't be inlined, whereas GetBitPtr() function can function GetBit(const Bits; aIndex: PtrInt): boolean; - {$ifndef CPUINTEL}inline;{$endif} /// set a particular bit into a bit array +// - this function can't be inlined, whereas SetBitPtr() function can procedure SetBit(var Bits; aIndex: PtrInt); - {$ifndef CPUINTEL}inline;{$endif} /// unset/clear a particular bit into a bit array +// - this function can't be inlined, whereas UnSetBitPtr() function can procedure UnSetBit(var Bits; aIndex: PtrInt); - {$ifndef CPUINTEL}inline;{$endif} + +/// retrieve a particular bit status from a bit array +// - GetBit() can't be inlined, whereas this pointer-oriented function can +function GetBitPtr(Bits: pointer; aIndex: PtrInt): boolean; + {$ifdef HASINLINE}inline;{$endif} + +/// set a particular bit into a bit array +// - SetBit() can't be inlined, whereas this pointer-oriented function can +procedure SetBitPtr(Bits: pointer; aIndex: PtrInt); + {$ifdef HASINLINE}inline;{$endif} + +/// unset/clear a particular bit into a bit array +// - UnSetBit() can't be inlined, whereas this pointer-oriented function can +procedure UnSetBitPtr(Bits: pointer; aIndex: PtrInt); + {$ifdef HASINLINE}inline;{$endif} /// compute the number of bits set in a bit array // - Count is the bit count, not byte size @@ -11759,8 +11817,8 @@ function GetBitsCount(const Bits; Count: PtrInt): integer; 1 shl 25-1, 1 shl 26-1, 1 shl 27-1, 1 shl 28-1, 1 shl 29-1, 1 shl 30-1, $7fffffff, $ffffffff); -/// returns TRUE if all BitCount bits are set in the input cardinal -function GetAllBits(Bits: Cardinal; BitCount: Integer): boolean; +/// returns TRUE if all BitCount bits are set in the input 32-bit cardinal +function GetAllBits(Bits, BitCount: cardinal): boolean; {$ifdef HASINLINE}inline;{$endif} type @@ -11773,21 +11831,21 @@ function GetAllBits(Bits: Cardinal; BitCount: Integer): boolean; // - the compiler will generate bt/btr/bts opcodes TBits32 = set of 0..31; PBits32 = ^TBits32; - /// fast access to Int64 bits + /// fast access to 64-bit integer bits // - the compiler will generate bt/btr/bts opcodes // - as used by GetBit64/SetBit64/UnSetBit64 TBits64 = set of 0..63; PBits64 = ^TBits64; -/// retrieve a particular bit status from a Int64 bit array (max aIndex is 63) +/// retrieve a particular bit status from a 64-bit integer bits (max aIndex is 63) function GetBit64(const Bits: Int64; aIndex: PtrInt): boolean; {$ifdef HASINLINE}inline;{$endif} -/// set a particular bit into a Int64 bit array (max aIndex is 63) +/// set a particular bit into a 64-bit integer bits (max aIndex is 63) procedure SetBit64(var Bits: Int64; aIndex: PtrInt); {$ifdef HASINLINE}inline;{$endif} -/// unset/clear a particular bit into a Int64 bit array (max aIndex is 63) +/// unset/clear a particular bit into a 64-bit integer bits (max aIndex is 63) procedure UnSetBit64(var Bits: Int64; aIndex: PtrInt); {$ifdef HASINLINE}inline;{$endif} @@ -11855,283 +11913,6 @@ procedure SetThreadNameDefault(ThreadID: TThreadID; const Name: RawUTF8); SetThreadNameInternal: procedure(ThreadID: TThreadID; const Name: RawUTF8) = SetThreadNameDefault; -{$ifndef LVCL} // LVCL does not implement TEvent - -type - {$M+} - TSynBackgroundThreadAbstract = class; - TSynBackgroundThreadEvent = class; - {$M-} - - /// idle method called by TSynBackgroundThreadAbstract in the caller thread - // during remote blocking process in a background thread - // - typical use is to run Application.ProcessMessages, e.g. for - // TSQLRestClientURI.URI() to provide a responsive UI even in case of slow - // blocking remote access - // - provide the time elapsed (in milliseconds) from the request start (can be - // used e.g. to popup a temporary message to wait) - // - is call once with ElapsedMS=0 at request start - // - is call once with ElapsedMS=-1 at request ending - // - see TLoginForm.OnIdleProcess and OnIdleProcessForm in mORMotUILogin.pas - TOnIdleSynBackgroundThread = procedure(Sender: TSynBackgroundThreadAbstract; - ElapsedMS: Integer) of object; - - /// event prototype used e.g. by TSynBackgroundThreadAbstract callbacks - // - a similar signature is defined in SynCrtSock and LVCL.Classes - TNotifyThreadEvent = procedure(Sender: TThread) of object; - - /// abstract TThread with its own execution content - // - you should not use this class directly, but use either - // TSynBackgroundThreadMethodAbstract / TSynBackgroundThreadEvent / - // TSynBackgroundThreadMethod and provide a much more convenient callback - TSynBackgroundThreadAbstract = class(TThread) - protected - fProcessEvent: TEvent; - fOnBeforeExecute: TNotifyThreadEvent; - fOnAfterExecute: TNotifyThreadEvent; - fThreadName: RawUTF8; - fExecute: (exCreated,exRun,exFinished); - fExecuteLoopPause: boolean; - procedure SetExecuteLoopPause(dopause: boolean); - /// where the main process takes place - procedure Execute; override; - procedure ExecuteLoop; virtual; abstract; - public - /// initialize the thread - // - you could define some callbacks to nest the thread execution, e.g. - // assigned to TSQLRestServer.BeginCurrentThread/EndCurrentThread - constructor Create(const aThreadName: RawUTF8; OnBeforeExecute: TNotifyThreadEvent=nil; - OnAfterExecute: TNotifyThreadEvent=nil; CreateSuspended: boolean=false); reintroduce; - /// release used resources - destructor Destroy; override; - {$ifndef HASTTHREADSTART} - /// method to be called to start the thread - // - Resume is deprecated in the newest RTL, since some OS - e.g. Linux - - // do not implement this pause/resume feature; we define here this method - // for older versions of Delphi - procedure Start; - {$endif} - {$ifdef HASTTHREADTERMINATESET} - /// properly terminate the thread - // - called by TThread.Terminate - procedure TerminatedSet; override; - {$else} - /// properly terminate the thread - // - called by reintroduced Terminate - procedure TerminatedSet; virtual; - /// reintroduced to call TeminatedSet - procedure Terminate; reintroduce; - {$endif} - /// wait for Execute/ExecuteLoop to be ended (i.e. fExecute<>exRun) - procedure WaitForNotExecuting(maxMS: integer=500); - /// temporary stop the execution of ExecuteLoop, until set back to false - // - may be used e.g. by TSynBackgroundTimer to delay the process of - // background tasks - property Pause: boolean read fExecuteLoopPause write SetExecuteLoopPause; - /// access to the low-level associated event used to notify task execution - // to the background thread - // - you may call ProcessEvent.SetEvent to trigger the internal process loop - property ProcessEvent: TEvent read fProcessEvent; - /// defined as public since may be used to terminate the processing methods - property Terminated; - end; - - /// state machine status of the TSynBackgroundThreadAbstract process - TSynBackgroundThreadProcessStep = ( - flagIdle, flagStarted, flagFinished, flagDestroying); - - /// state machine statuses of the TSynBackgroundThreadAbstract process - TSynBackgroundThreadProcessSteps = set of TSynBackgroundThreadProcessStep; - - /// abstract TThread able to run a method in its own execution content - // - typical use is a background thread for processing data or remote access, - // while the UI will be still responsive by running OnIdle event in loop: see - // e.g. how TSQLRestClientURI.OnIdle handle this in mORMot.pas unit - // - you should not use this class directly, but inherit from it and override - // the Process method, or use either TSynBackgroundThreadEvent / - // TSynBackgroundThreadMethod and provide a much more convenient callback - TSynBackgroundThreadMethodAbstract = class(TSynBackgroundThreadAbstract) - protected - fCallerEvent: TEvent; - fParam: pointer; - fCallerThreadID: TThreadID; - fBackgroundException: Exception; - fOnIdle: TOnIdleSynBackgroundThread; - fOnBeforeProcess: TNotifyThreadEvent; - fOnAfterProcess: TNotifyThreadEvent; - fPendingProcessFlag: TSynBackgroundThreadProcessStep; - fPendingProcessLock: TSynLocker; - procedure ExecuteLoop; override; - function OnIdleProcessNotify(start: Int64): integer; - function GetOnIdleBackgroundThreadActive: boolean; - function GetPendingProcess: TSynBackgroundThreadProcessStep; - procedure SetPendingProcess(State: TSynBackgroundThreadProcessStep); - // returns flagIdle if acquired, flagDestroying if terminated - function AcquireThread: TSynBackgroundThreadProcessStep; - procedure WaitForFinished(start: Int64); - /// called by Execute method when fProcessParams<>nil and fEvent is notified - procedure Process; virtual; abstract; - public - /// initialize the thread - // - if aOnIdle is not set (i.e. equals nil), it will simply wait for - // the background process to finish until RunAndWait() will return - // - you could define some callbacks to nest the thread execution, e.g. - // assigned to TSQLRestServer.BeginCurrentThread/EndCurrentThread - constructor Create(aOnIdle: TOnIdleSynBackgroundThread; - const aThreadName: RawUTF8; OnBeforeExecute: TNotifyThreadEvent=nil; - OnAfterExecute: TNotifyThreadEvent=nil); reintroduce; - /// finalize the thread - destructor Destroy; override; - /// launch Process abstract method asynchronously in the background thread - // - wait until process is finished, calling OnIdle() callback in - // the meanwhile - // - any exception raised in background thread will be translated in the - // caller thread - // - returns false if self is not set, or if called from the same thread - // as it is currently processing (to avoid race condition from OnIdle() - // callback) - // - returns true when the background process is finished - // - OpaqueParam will be used to specify a thread-safe content for the - // background process - // - this method is thread-safe, that is it will wait for any started process - // already launch by another thread: you may call this method from any - // thread, even if its main purpose is to be called from the main UI thread - function RunAndWait(OpaqueParam: pointer): boolean; - /// set a callback event to be executed in loop during remote blocking - // process, e.g. to refresh the UI during a somewhat long request - // - you can assign a callback to this property, calling for instance - // Application.ProcessMessages, to execute the remote request in a - // background thread, but let the UI still be reactive: the - // TLoginForm.OnIdleProcess and OnIdleProcessForm methods of - // mORMotUILogin.pas will match this property expectations - // - if OnIdle is not set (i.e. equals nil), it will simply wait for - // the background process to finish until RunAndWait() will return - property OnIdle: TOnIdleSynBackgroundThread read fOnIdle write fOnIdle; - /// TRUE if the background thread is active, and OnIdle event is called - // during process - // - to be used e.g. to ensure no re-entrance from User Interface messages - property OnIdleBackgroundThreadActive: Boolean read GetOnIdleBackgroundThreadActive; - /// optional callback event triggered in Execute before each Process - property OnBeforeProcess: TNotifyThreadEvent read fOnBeforeProcess write fOnBeforeProcess; - /// optional callback event triggered in Execute after each Process - property OnAfterProcess: TNotifyThreadEvent read fOnAfterProcess write fOnAfterProcess; - end; - - /// background process method called by TSynBackgroundThreadEvent - // - will supply the OpaqueParam parameter as provided to RunAndWait() - // method when the Process virtual method will be executed - TOnProcessSynBackgroundThread = procedure(Sender: TSynBackgroundThreadEvent; - ProcessOpaqueParam: pointer) of object; - - /// allow background thread process of a method callback - TSynBackgroundThreadEvent = class(TSynBackgroundThreadMethodAbstract) - protected - fOnProcess: TOnProcessSynBackgroundThread; - /// just call the OnProcess handler - procedure Process; override; - public - /// initialize the thread - // - if aOnIdle is not set (i.e. equals nil), it will simply wait for - // the background process to finish until RunAndWait() will return - constructor Create(aOnProcess: TOnProcessSynBackgroundThread; - aOnIdle: TOnIdleSynBackgroundThread; const aThreadName: RawUTF8); reintroduce; - /// provide a method handler to be execute in the background thread - // - triggered by RunAndWait() method - which will wait until finished - // - the OpaqueParam as specified to RunAndWait() will be supplied here - property OnProcess: TOnProcessSynBackgroundThread read fOnProcess write fOnProcess; - end; - - /// allow background thread process of a variable TThreadMethod callback - TSynBackgroundThreadMethod = class(TSynBackgroundThreadMethodAbstract) - protected - /// just call the TThreadMethod, as supplied to RunAndWait() - procedure Process; override; - public - /// run once the supplied TThreadMethod callback - // - use this method, and not the inherited RunAndWait() - procedure RunAndWait(Method: TThreadMethod); reintroduce; - end; - - /// background process procedure called by TSynBackgroundThreadProcedure - // - will supply the OpaqueParam parameter as provided to RunAndWait() - // method when the Process virtual method will be executed - TOnProcessSynBackgroundThreadProc = procedure(ProcessOpaqueParam: pointer); - - /// allow background thread process of a procedure callback - TSynBackgroundThreadProcedure = class(TSynBackgroundThreadMethodAbstract) - protected - fOnProcess: TOnProcessSynBackgroundThreadProc; - /// just call the OnProcess handler - procedure Process; override; - public - /// initialize the thread - // - if aOnIdle is not set (i.e. equals nil), it will simply wait for - // the background process to finish until RunAndWait() will return - constructor Create(aOnProcess: TOnProcessSynBackgroundThreadProc; - aOnIdle: TOnIdleSynBackgroundThread; const aThreadName: RawUTF8); reintroduce; - /// provide a procedure handler to be execute in the background thread - // - triggered by RunAndWait() method - which will wait until finished - // - the OpaqueParam as specified to RunAndWait() will be supplied here - property OnProcess: TOnProcessSynBackgroundThreadProc read fOnProcess write fOnProcess; - end; - - /// an exception which would be raised by TSynParallelProcess - ESynParallelProcess = class(ESynException); - - /// callback implementing some parallelized process for TSynParallelProcess - // - if 0<=IndexStart<=IndexStop, it should execute some process - TSynParallelProcessMethod = procedure(IndexStart, IndexStop: integer) of object; - - /// thread executing process for TSynParallelProcess - TSynParallelProcessThread = class(TSynBackgroundThreadMethodAbstract) - protected - fMethod: TSynParallelProcessMethod; - fIndexStart, fIndexStop: integer; - procedure Start(Method: TSynParallelProcessMethod; IndexStart,IndexStop: integer); - /// executes fMethod(fIndexStart,fIndexStop) - procedure Process; override; - public - end; - - /// allow parallel execution of an index-based process in a thread pool - // - will create its own thread pool, then execute any method by spliting the - // work into each thread - TSynParallelProcess = class(TSynPersistentLocked) - protected - fThreadName: RawUTF8; - fPool: array of TSynParallelProcessThread; - fThreadPoolCount: integer; - fParallelRunCount: integer; - public - /// initialize the thread pool - // - you could define some callbacks to nest the thread execution, e.g. - // assigned to TSQLRestServer.BeginCurrentThread/EndCurrentThread - // - up to MaxThreadPoolCount=32 threads could be setup (you may allow a - // bigger value, but interrest of this thread pool is to have its process - // saturating each CPU core) - // - if ThreadPoolCount is 0, no thread would be created, and process - // would take place in the current thread - constructor Create(ThreadPoolCount: integer; const ThreadName: RawUTF8; - OnBeforeExecute: TNotifyThreadEvent=nil; OnAfterExecute: TNotifyThreadEvent=nil; - MaxThreadPoolCount: integer = 32); reintroduce; virtual; - /// finalize the thread pool - destructor Destroy; override; - /// run a method in parallel, and wait for the execution to finish - // - will split Method[0..MethodCount-1] execution over the threads - // - in case of any exception during process, an ESynParallelProcess - // exception would be raised by this method - procedure ParallelRunAndWait(Method: TSynParallelProcessMethod; MethodCount: integer); - published - /// how many threads have been activated - property ParallelRunCount: integer read fParallelRunCount; - /// how many threads are currently in this instance thread pool - property ThreadPoolCount: integer read fThreadPoolCount; - /// some text identifier, used to distinguish each owned thread - property ThreadName: RawUTF8 read fThreadName; - end; - -{$endif LVCL} // LVCL does not implement TEvent - /// low-level wrapper to add a callback to a dynamic list of events // - by default, you can assign only one callback to an Event: but by storing @@ -12211,8 +11992,9 @@ function EventEquals(const eventA,eventB): boolean; /// a cross-platform and cross-compiler TSystemTime structure // - FPC's TSystemTime in datih.inc does NOT match Windows TSystemTime fields! - // - also used to store a Date/Time in TSynTimeZone internal structures - {$ifdef UNICODE}TSynSystemTime = record{$else}TSynSystemTime = object{$endif} + // - also used to store a Date/Time in TSynTimeZone internal structures, or + // for fast conversion from TDateTime to its ready-to-display members + {$ifdef FPC_OR_UNICODE}TSynSystemTime = record{$else}TSynSystemTime = object{$endif} public Year, Month, DayOfWeek, Day, Hour, Minute, Second, MilliSecond: word; @@ -12228,12 +12010,32 @@ {$ifdef UNICODE}TSynSystemTime = record{$else}TSynSystemTime = object{$endif} procedure FromNowUTC; /// fill fields with the current Local time, using a 8-16ms thread-safe cache procedure FromNowLocal; + /// fill fields from the given value - but not DayOfWeek + procedure FromDateTime(const dt: TDateTime); + /// fill Year/Month/Day fields from the given value - but not DayOfWeek + // - faster than the RTL DecodeDate() function + procedure FromDate(const dt: TDateTime); + /// fill Hour/Minute/Second/Millisecond fields from the given value + // - faster than the RTL DecodeTime() function + procedure FromTime(const dt: TDateTime); /// encode the stored date/time as text function ToText(Expanded: boolean=true; FirstTimeChar: AnsiChar='T'; const TZD: RawUTF8=''): RawUTF8; /// append the stored date and time, in a log-friendly format - // - e.g. append '20110325 19241502 ' + // - e.g. append '20110325 19241502' - with no trailing space nor tab // - as called by TTextWriter.AddCurrentLogTime() procedure AddLogTime(WR: TTextWriter); + /// append the stored data and time, in apache-like format, to a TTextWriter + // - e.g. append '19/Feb/2019:06:18:55 ' - including a trailing space + procedure AddNCSAText(WR: TTextWriter); + /// append the stored data and time, in apache-like format, to a memory buffer + // - e.g. append '19/Feb/2019:06:18:55 ' - including a trailing space + // - returns the number of chars added to P, i.e. always 21 + function ToNCSAText(P: PUTF8Char): PtrInt; + /// convert the stored time into a TDateTime + function ToDateTime: TDateTime; + /// add some 1..999 milliseconds to the stored time + // - not to be used for computation, but e.g. for fast AddLogTime generation + procedure IncrementMS(ms: integer); end; PSynSystemTime = ^TSynSystemTime; @@ -12266,7 +12068,7 @@ {$ifdef UNICODE}TSynSystemTime = record{$else}TSynSystemTime = object{$endif} // temporary conversion in such case // - TTimeLogBits.Value has a 38-bit precision, so features exact representation // as JavaScript numbers (stored in a 52-bit mantissa) - {$ifdef UNICODE}TTimeLogBits = record{$else}TTimeLogBits = object{$endif} + {$ifdef FPC_OR_UNICODE}TTimeLogBits = record{$else}TTimeLogBits = object{$endif} public /// the bit-encoded value itself, which follows an abstract "year" of 16 // months of 32 days of 32 hours of 64 minutes of 64 seconds @@ -12498,7 +12300,7 @@ function TimeToIso8601(Time: TDateTime; Expanded: boolean; FirstChar: AnsiChar=' /// Write a Date to P^ Ansi buffer // - if Expanded is false, 'YYYYMMDD' date format is used // - if Expanded is true, 'YYYY-MM-DD' date format is used -procedure DateToIso8601PChar(P: PUTF8Char; Expanded: boolean; Y,M,D: cardinal); overload; +procedure DateToIso8601PChar(P: PUTF8Char; Expanded: boolean; Y,M,D: PtrUInt); overload; /// convert a date into 'YYYY-MM-DD' date format // - resulting text is compatible with all ISO-8601 functions @@ -12552,7 +12354,7 @@ procedure DateTimeToIso8601StringVar(DT: TDateTime; FirstChar: AnsiChar; var res // - if Expanded is true, 'Thh:mm:ss' time format is used // - you can custom the first char in from of the resulting text time // - if WithMS is TRUE, will append MS as '.sss' for milliseconds resolution -procedure TimeToIso8601PChar(P: PUTF8Char; Expanded: boolean; H,M,S,MS: cardinal; +procedure TimeToIso8601PChar(P: PUTF8Char; Expanded: boolean; H,M,S,MS: PtrUInt; FirstChar: AnsiChar = 'T'; WithMS: boolean=false); overload; /// Write a Time to P^ Ansi buffer @@ -12707,6 +12509,7 @@ function UnixMSTimeToDateTime(const UnixMSTime: TUnixMSTime): TDateTime; {$ifdef HASINLINE}inline;{$endif} /// convert a TDateTime into a millisecond-based c-encoded time (from Unix epoch 1/1/1970) +// - if AValue is 0, will return 0 (since is likely to be an error constant) function DateTimeToUnixMSTime(const AValue: TDateTime): TUnixMSTime; {$ifdef HASINLINE}inline;{$endif} @@ -12753,7 +12556,7 @@ TTimeZoneInfo = record TTimeZoneID = type RawUTF8; /// used to store Time Zone information for a single area in TSynTimeZone - {$ifdef UNICODE}TTimeZoneData = record{$else}TTimeZoneData = object{$endif} + {$ifdef FPC_OR_UNICODE}TTimeZoneData = record{$else}TTimeZoneData = object{$endif} public id: TTimeZoneID; display: RawUTF8; @@ -12902,7 +12705,8 @@ procedure LogToTextFile(Msg: RawUTF8); // - this version expects the filename to be specified // - format contains the current date and time, then the Msg on one line // - date and time format used is 'YYYYMMDD hh:mm:ss' -procedure AppendToTextFile(aLine: RawUTF8; const aFileName: TFileName; aMaxSize: Int64=MAXLOGSIZE); +procedure AppendToTextFile(aLine: RawUTF8; const aFileName: TFileName; aMaxSize: Int64=MAXLOGSIZE; + aUTCTimeStamp: boolean=false); { ************ fast low-level lookup types used by internal conversion routines } @@ -13047,7 +12851,7 @@ TFileVersion = class // ExeVersion global variable constructor Create(const aFileName: TFileName; aMajor: integer=0; aMinor: integer=0; aRelease: integer=0; aBuild: integer=0); - /// retrieve the version as a 32 bits integer with Major.Minor.Release + /// retrieve the version as a 32-bit integer with Major.Minor.Release // - following Major shl 16+Minor shl 8+Release bit pattern function Version32: integer; /// build date and time of this exe file, as plain text @@ -13059,6 +12863,11 @@ TFileVersion = class // - includes FileName (without path), Detailed and BuildDateTime properties // - e.g. 'myprogram.exe 3.1.0.123 2016-06-14 19:07:55' function VersionInfo: RawUTF8; + /// returns a ready-to-use User-Agent header with exe name, version and OS + // - e.g. 'myprogram/3.1.0.123W32' + // - here OS_INITIAL[] character is used to identify the OS, with '32' + // appended on 32-bit Windows + function UserAgent: RawUTF8; /// returns the version information of a specified exe file as text // - includes FileName (without path), Detailed and BuildDateTime properties // - e.g. 'myprogram.exe 3.1.0.123 2016-06-14 19:07:55' @@ -13090,7 +12899,7 @@ TFileVersion = class DateDelta = 693594; UnixDateDelta = 25569; -/// GetFileVersion returns the most significant 32 bits of a file's binary +/// GetFileVersion returns the most significant 32-bit of a file's binary // version number // - typically, this includes the major and minor version placed // together in one 32-bit integer @@ -13098,15 +12907,11 @@ TFileVersion = class // - returns Cardinal(-1) in case of failure function GetFileVersion(const FileName: TFileName): cardinal; -{$endif} - -/// returns a JSON object containing basic information about the computer -// - including Host, User, CPU, OS, freemem, freedisk... -function SystemInfoJson: RawUTF8; +{$endif DELPHI6OROLDER} type /// the recognized operating systems - // - it will also recognize some of the distributions + // - it will also recognize some Linux distributions TOperatingSystem = (osUnknown, osWindows, osLinux, osOSX, osBSD, osPOSIX, osArch, osAurox, osDebian, osFedora, osGentoo, osKnoppix, osMint, osMandrake, osMandriva, osNovell, osUbuntu, osSlackware, osSolaris, osSuse, osSynology, @@ -13120,7 +12925,7 @@ function SystemInfoJson: RawUTF8; wSeven, wSeven_64, wServer2008_R2, wServer2008_R2_64, wEight, wEight_64, wServer2012, wServer2012_64, wEightOne, wEightOne_64, wServer2012R2, wServer2012R2_64, - wTen, wTen_64, wServer2016, wServer2016_64); + wTen, wTen_64, wServer2016, wServer2016_64, wServer2019_64); /// the running Operating System, encoded as a 32-bit integer TOperatingSystemVersion = packed record case os: TOperatingSystem of @@ -13138,7 +12943,19 @@ function SystemInfoJson: RawUTF8; '7', '7 64bit', 'Server 2008 R2', 'Server 2008 R2 64bit', '8', '8 64bit', 'Server 2012', 'Server 2012 64bit', '8.1', '8.1 64bit', 'Server 2012 R2', 'Server 2012 R2 64bit', - '10', '10 64bit', 'Server 2016', 'Server 2016 64bit'); + '10', '10 64bit', 'Server 2016', 'Server 2016 64bit', 'Server 2019 64bit'); + /// the recognized Windows versions which are 32-bit + WINDOWS_32 = [w2000, wXP, wServer2003, wServer2003_R2, wVista, wServer2008, + wSeven, wServer2008_R2, wEight, wServer2012, wEightOne, wServer2012R2, + wTen, wServer2016]; + /// translate one operating system (and distribution) into a single character + // - may be used internally e.g. for a HTTP User-Agent header + OS_INITIAL: array[TOperatingSystem] of AnsiChar = + ('?', 'W', 'L', 'X', 'B', 'P', 'A', 'a', 'D', 'F', 'G', 'K', 'M', 'm', + 'n', 'N', 'U', 'S', 's', 'u', 'Y', 'T', 'C', 't', 'R', 'l', 'O', 'G', + 'c', 'd', 'x', 'Z', 'r', 'p'); + /// the operating systems items which actually are Linux distributions + OS_LINUX = [osLinux, osArch .. osAlpine]; /// the compiler family used COMP_TEXT = {$ifdef FPC}'Fpc'{$else}'Delphi'{$endif}; @@ -13154,6 +12971,8 @@ function SystemInfoJson: RawUTF8; {$ifdef CPU32}'32'{$else}'64'{$endif}{$endif}{$endif}; function ToText(os: TOperatingSystem): PShortString; overload; +function ToText(const osv: TOperatingSystemVersion): ShortString; overload; +function ToTextOS(osint32: integer): RawUTF8; var /// the target Operating System used for compilation, as TOperatingSystem @@ -13168,7 +12987,7 @@ function ToText(os: TOperatingSystem): PShortString; overload; CpuInfoText: RawUTF8; /// some textual information about the current computer hardware, from BIOS BiosInfoText: RawUTF8; - /// the running Operating System information, encoded as a 32-bit integer + /// the running Operating System OSVersion32: TOperatingSystemVersion; OSVersionInt32: integer absolute OSVersion32; @@ -13192,10 +13011,10 @@ TOSVersionInfoEx = record {$endif UNICODE} var - /// is set to TRUE if the current process is a 32 bit image running under WOW64 + /// is set to TRUE if the current process is a 32-bit image running under WOW64 // - WOW64 is the x86 emulator that allows 32-bit Windows-based applications // to run seamlessly on 64-bit Windows - // - equals always FALSE if the current executable is a 64 bit image + // - equals always FALSE if the current executable is a 64-bit image IsWow64: boolean; /// the current System information, as retrieved for the current process // - under a WOW64 process, it will use the GetNativeSystemInfo() new API @@ -13209,12 +13028,6 @@ TOSVersionInfoEx = record /// the current Operating System version, as retrieved for the current process OSVersion: TWindowsVersion; -/// a wrapper around EnumProcesses() PsAPI call -function EnumAllProcesses(out Count: Cardinal): TCardinalDynArray; - -/// a wrapper around QueryFullProcessImageNameW/GetModuleFileNameEx PsAPI call -function EnumProcessName(PID: Cardinal): RawUTF8; - /// this function can be used to create a GDI compatible window, able to // receive Windows Messages for fast local communication // - will return 0 on failure (window name already existing e.g.), or @@ -13246,7 +13059,9 @@ function SetAppUserModelID(const AppUserModelID: string): boolean; /// the number of milliseconds that have elapsed since the system was started // - compatibility function, to be implemented according to the running OS // - will use the corresponding native API function under Vista+, or - // will emulate it for older Windows versions + // will emulate it for older Windows versions (XP) + // - warning: FPC's SysUtils.GetTickCount64 or TThread.GetTickCount64 don't + // handle properly 49 days wrapping under XP -> always use this safe version GetTickCount64: function: Int64; stdcall; /// similar to Windows sleep() API call, to be truly cross-platform @@ -13286,15 +13101,17 @@ function FileTimeToUnixMSTime(const FT: TFileTime): TUnixMSTime; function GetCurrentThreadID: TThreadID; cdecl; external 'libpthread.so.0' name 'pthread_self'; -/// overloaded function using open64() to allow 64 bit positions +/// overloaded function using open64() to allow 64-bit positions function FileOpen(const FileName: string; Mode: LongWord): Integer; {$endif} /// compatibility function, to be implemented according to the running OS -// - expect more or less the same result as the homonymous Win32 API function -// - will call the corresponding function in SynKylix.pas or SynFPCLinux.pas -function GetTickCount64: Int64; {$ifdef HASINLINE}inline;{$endif} +// - expect more or less the same result as the homonymous Win32 API function, +// but usually with a better resolution (Windows has only around 10-16 ms) +// - will call the corresponding function in SynKylix.pas or SynFPCLinux.pas, +// using the very fast CLOCK_MONOTONIC_COARSE if available on the kernel +function GetTickCount64: Int64; {$endif MSWINDOWS} @@ -13415,7 +13232,8 @@ procedure SetExecutableVersion(const aVersionText: RawUTF8); overload; // - will return the full path of a given kind of private or shared folder, // depending on the underlying operating system // - will use SHGetFolderPath and the corresponding CSIDL constant under Windows -// - will return the $HOME folder (whatever kind value is given) otherwise +// - under POSIX, will return $TMP/$TMPDIR folder for spTempFolder, ~/.cache/appname +// for spUserData, /var/log for spLog, or the $HOME folder // - returned folder name contains the trailing path delimiter (\ or /) function GetSystemPath(kind: TSystemPath): TFileName; @@ -13445,15 +13263,6 @@ procedure RedirectCode(Func, RedirectFunc: Pointer; Backup: PPatchCode=nil); procedure RedirectCodeRestore(Func: pointer; const Backup: TPatchCode); {$endif CPUINTEL} -/// allow to fix TEvent.WaitFor() method for Kylix -// - under Windows or with FPC, will call original TEvent.WaitFor() method -function FixedWaitFor(Event: TEvent; Timeout: LongWord): TWaitResult; - -/// allow to fix TEvent.WaitFor(Event,INFINITE) method for Kylix -// - under Windows or with FPC, will call original TEvent.WaitFor() method -procedure FixedWaitForever(Event: TEvent); - - type /// to be used instead of TMemoryStream, for speed // - allocates memory from Delphi heap (i.e. FastMM4/SynScaleMM) @@ -13566,7 +13375,9 @@ function GetNextFieldProp(var P: PUTF8Char; var Prop: RawUTF8): boolean; { ************ variant-based process, including JSON/BSON document content } const - /// this variant type is not defined in older versions of Delphi + /// unsigned 64bit integer variant type + // - currently called varUInt64 in Delphi (not defined in older versions), + // and varQWord in FPC varWord64 = 21; /// this variant type will map the current SynUnicode type @@ -13583,6 +13394,7 @@ function GetNextFieldProp(var P: PUTF8Char; var Prop: RawUTF8): boolean; // ! VarClear(aVariant); // - equals private constant varDeepData in Delphi's Variants.pas and // varComplexType in FPC's variants.pp - seldom used on FPC + // - make some false positive to varBoolean and varError VTYPE_STATIC = $BFE8; /// same as Dest := TVarData(Source) for simple values @@ -14389,8 +14201,8 @@ TDocVariant = class(TSynInvokeableVariantType) // be a good idea to use DocVariantData(aVariant)^ or _Safe(aVariant)^ instead // of TDocVariantData(aVariant), if you are not sure how aVariant was allocated // (may be not _Obj/_Json, but retrieved as varByRef e.g. from late binding) - {$ifdef UNICODE}TDocVariantData = record{$else}TDocVariantData = object{$endif} - private + {$ifdef FPC_OR_UNICODE}TDocVariantData = record private + {$else}TDocVariantData = object protected{$endif} VType: TVarType; VOptions: TDocVariantOptions; (* this structure uses all TVarData available space: no filler needed! @@ -15010,8 +14822,15 @@ {$ifdef UNICODE}TDocVariantData = record{$else}TDocVariantData = object{$endif // methods for much faster O(log(n)) binary search procedure SortByName(Compare: TUTF8Compare=nil); /// sort the document object values by value - // - do nothing if the document is not a dvObject - procedure SortByValue(Compare: TVariantCompare); + // - work for both dvObject and dvArray documents + // - will sort by UTF-8 text (VariantCompare) if no custom aCompare is supplied + procedure SortByValue(Compare: TVariantCompare = nil); + /// sort the document array values by a field of some stored objet values + // - do nothing if the document is not a dvArray, or if the items are no dvObject + // - will sort by UTF-8 text (VariantCompare) if no custom aValueCompare is supplied + procedure SortArrayByField(const aItemPropName: RawUTF8; + aValueCompare: TVariantCompare=nil; aValueCompareReverse: boolean=false; + aNameSortedCompare: TUTF8Compare=nil); /// reverse the order of the document object or array items procedure Reverse; /// create a TDocVariant object, from a selection of properties of this @@ -15472,7 +15291,8 @@ procedure _UniqueFast(var DocVariant: variant); // arrays were created with: to be used on a value returned as varByRef // (e.g. by _() pseudo-method) // - for huge document with a big depth of nested objects or arrays, a full -// per-value copy may be time and resource consuming, but will be also safe +// per-value copy may be time and resource consuming, but will be also safe - +// consider using _ByRef() instead if a fast copy-by-reference is enough // - will raise an EDocVariant if the supplied variant is not a TDocVariant or // a varByRef pointing to a TDocVariant function _Copy(const DocVariant: variant): variant; @@ -15486,7 +15306,8 @@ function _Copy(const DocVariant: variant): variant; // arrays were created with: to be used on a value returned as varByRef // (e.g. by _() pseudo-method) // - for huge document with a big depth of nested objects or arrays, a full -// per-value copy may be time and resource consuming, but will be also safe +// per-value copy may be time and resource consuming, but will be also safe - +// consider using _ByRef() instead if a fast copy-by-reference is enough // - will raise an EDocVariant if the supplied variant is not a TDocVariant or // a varByRef pointing to a TDocVariant function _CopyFast(const DocVariant: variant): variant; @@ -15494,17 +15315,24 @@ function _CopyFast(const DocVariant: variant): variant; /// copy a TDocVariant to another variable, changing the options on the fly // - note that the content (items or properties) is copied by reference, -// so consider using _Copy() instead +// so consider using _Copy() instead if you expect to safely modify its content // - will return null if the supplied variant is not a TDocVariant function _ByRef(const DocVariant: variant; Options: TDocVariantOptions): variant; overload; /// copy a TDocVariant to another variable, changing the options on the fly // - note that the content (items or properties) is copied by reference, -// so consider using _Copy() instead +// so consider using _Copy() instead if you expect to safely modify its content // - will return null if the supplied variant is not a TDocVariant procedure _ByRef(const DocVariant: variant; out Dest: variant; Options: TDocVariantOptions); overload; +/// convert a TDocVariantData array or a string value into a CSV +// - will call either TDocVariantData.ToCSV, or return the string +// - returns '' if the supplied value is neither a TDocVariant or a string +// - could be used e.g. to store either a JSON CSV string or a JSON array of +// strings in a settings property +function _CSV(const DocVariantOrString: variant): RawUTF8; + /// will convert any TObject into a TDocVariant document instance // - a slightly faster alternative to Dest := _JsonFast(ObjectToJSON(Value)) // - this would convert the TObject by representation, using only serializable @@ -15795,17 +15623,19 @@ TCommandLine = class(TInterfacedObjectWithCustomCreate, ICommandLine) PPPrecisionTimer = ^PPrecisionTimer; /// high resolution timer (for accurate speed statistics) - // - WARNING: this record MUST be aligned to 32 bit, otherwise iFreq=0 - - // so you can use TLocalPrecisionTimer/ILocalPrecisionTimer if you want - // to alllocate a local timer instance on the stack - {$ifdef UNICODE}TPrecisionTimer = record{$else}TPrecisionTimer = object{$endif} - private - iStart,iStop,iResume,iLast: Int64; - iFreq: Int64; + // - WARNING: under Windows, this record MUST be aligned to 32-bit, otherwise + // iFreq=0 - so you can use TLocalPrecisionTimer/ILocalPrecisionTimer if you + // want to alllocate a local timer instance on the stack + {$ifdef FPC_OR_UNICODE}TPrecisionTimer = record private + {$else}TPrecisionTimer = object protected{$endif} + fStart,fStop,fResume,fLast: Int64; + {$ifndef LINUX} // use QueryPerformanceMicroSeconds() fast API + fWinFreq: Int64; + {$endif} /// contains the time elapsed in micro seconds between Start and Stop - iTime: TSynMonitorTotalMicroSec; + fTime: TSynMonitorTotalMicroSec; /// contains the time elapsed in micro seconds between Resume and Pause - iLastTime: TSynMonitorOneMicroSec; + fLastTime: TSynMonitorOneMicroSec; fPauseCount: TSynMonitorCount; public /// initialize the timer @@ -15814,11 +15644,10 @@ {$ifdef UNICODE}TPrecisionTimer = record{$else}TPrecisionTimer = object{$endif procedure Init; /// initialize and start the high resolution timer procedure Start; - /// returns TRUE if iStart is not 0 - function Started: boolean; - {$ifdef HASINLINE}inline;{$endif} + /// returns TRUE if fStart is not 0 + function Started: boolean; {$ifdef HASINLINE}inline;{$endif} /// stop the timer, setting the Time elapsed since last Start - procedure ComputeTime; + procedure ComputeTime; {$ifdef LINUX}{$ifdef HASINLINE}inline;{$endif}{$endif} /// stop the timer, returning the time elapsed as text with time resolution // (us,ms,s) // - is just a wrapper around ComputeTime + Time @@ -15840,6 +15669,7 @@ {$ifdef UNICODE}TPrecisionTimer = record{$else}TPrecisionTimer = object{$endif // - typical use is to declare a fTimeElapsed: TPrecisionTimer protected // member, then call fTimeElapsed.ProfileCurrentMethod at the beginning of // all process expecting some timing, then log/save fTimeElapsed.Stop content + // - FPC TIP: result should be assigned to a local variable of IUnknown type function ProfileCurrentMethod: IUnknown; /// low-level method to force values settings to allow thread safe timing // - by default, this timer is not thread safe: you can use this method to @@ -15861,20 +15691,22 @@ {$ifdef UNICODE}TPrecisionTimer = record{$else}TPrecisionTimer = object{$endif function PerSec(const Count: QWord): QWord; /// compute the time elapsed by count, with appened time resolution (us,ms,s) function ByCount(Count: QWord): TShort16; + /// returns e.g. '16.9 MB in 102.20ms i.e. 165.5 MB/s' + function SizePerSec(Size: QWord): shortstring; /// textual representation of time after counter stopped // - with appened time resolution (us,ms,s) // - not to be used in normal code, but e.g. for custom performance analysis function Time: TShort16; /// time elapsed in micro seconds after counter stopped // - not to be used in normal code, but e.g. for custom performance analysis - property TimeInMicroSec: TSynMonitorTotalMicroSec read iTime write iTime; + property TimeInMicroSec: TSynMonitorTotalMicroSec read fTime write fTime; /// textual representation of last process timing after counter stopped // - with appened time resolution (us,ms,s) // - not to be used in normal code, but e.g. for custom performance analysis function LastTime: TShort16; /// timing in micro seconds of the last process // - not to be used in normal code, but e.g. for custom performance analysis - property LastTimeInMicroSec: TSynMonitorOneMicroSec read iLastTime write iLastTime; + property LastTimeInMicroSec: TSynMonitorOneMicroSec read fLastTime write fLastTime; /// how many times the Pause method was called, i.e. the number of tasks // processeed property PauseCount: TSynMonitorCount read fPauseCount; @@ -15898,7 +15730,7 @@ {$ifdef UNICODE}TPrecisionTimer = record{$else}TPrecisionTimer = object{$endif end; /// reference counted high resolution timer (for accurate speed statistics) - // - since TPrecisionTimer shall be 32 bit aligned, you can use this class + // - since TPrecisionTimer shall be 32-bit aligned, you can use this class // to initialize a local auto-freeing ILocalPrecisionTimer variable on stack // - to be used as such: // ! var Timer: ILocalPrecisionTimer; @@ -15926,11 +15758,9 @@ TLocalPrecisionTimer = class(TInterfacedObject,ILocalPrecisionTimer) function ByCount(Count: cardinal): RawUTF8; end; - {$M+} - /// able to serialize any cumulative timing as raw micro-seconds number or text // - "cumulative" time would add each process value, e.g. SOA methods execution - TSynMonitorTime = class + TSynMonitorTime = class(TSynPersistent) protected fMicroSeconds: TSynMonitorTotalMicroSec; function GetAsText: TShort16; @@ -15947,7 +15777,7 @@ TSynMonitorTime = class /// able to serialize any immediate timing as raw micro-seconds number or text // - "immediate" size won't accumulate, i.e. may be e.g. last process time - TSynMonitorOneTime = class + TSynMonitorOneTime = class(TSynPersistent) protected fMicroSeconds: TSynMonitorOneMicroSec; function GetAsText: TShort16; @@ -15962,9 +15792,17 @@ TSynMonitorOneTime = class property Text: TShort16 read GetAsText; end; + TSynMonitorSizeParent = class(TSynPersistent) + protected + fTextNoSpace: boolean; + public + /// initialize the instance + constructor Create(aTextNoSpace: boolean); reintroduce; + end; + /// able to serialize any cumulative size as bytes number // - "cumulative" time would add each process value, e.g. global IO consumption - TSynMonitorSize = class + TSynMonitorSize = class(TSynMonitorSizeParent) protected fBytes: TSynMonitorTotalBytes; function GetAsText: TShort16; @@ -15978,7 +15816,7 @@ TSynMonitorSize = class /// able to serialize any immediate size as bytes number // - "immediate" size won't accumulate, i.e. may be e.g. computer free memory // at a given time - TSynMonitorOneSize = class + TSynMonitorOneSize = class(TSynMonitorSizeParent) protected fBytes: TSynMonitorOneBytes; function GetAsText: TShort16; @@ -15992,7 +15830,7 @@ TSynMonitorOneSize = class /// able to serialize any bandwith as bytes count per second // - is usually associated with TSynMonitorOneSize properties, // e.g. to monitor IO activity - TSynMonitorThroughput = class + TSynMonitorThroughput = class(TSynMonitorSizeParent) protected fBytesPerSec: QWord; function GetAsText: TShort16; @@ -16008,21 +15846,20 @@ TSynMonitorThroughput = class // process is to be monitored // - this class is thread-safe for its methods, but you should call explicitly // Lock/UnLock to access its individual properties - TSynMonitor = class(TSynPersistent) + TSynMonitor = class(TSynPersistentLock) protected fName: RawUTF8; - fProcessing: boolean; fTaskCount: TSynMonitorCount64; - fInternalErrors: TSynMonitorCount; - fLastInternalError: variant; fTotalTime: TSynMonitorTime; fLastTime: TSynMonitorOneTime; fMinimalTime: TSynMonitorOneTime; fAverageTime: TSynMonitorOneTime; fMaximalTime: TSynMonitorOneTime; fPerSec: QWord; + fInternalErrors: TSynMonitorCount; + fProcessing: boolean; fTaskStatus: (taskNotStarted,taskStarted); - fLock: TRTLCriticalSection; + fLastInternalError: variant; procedure LockedPerSecProperties; virtual; procedure LockedFromProcessTimer; virtual; procedure LockedSum(another: TSynMonitor); virtual; @@ -16041,12 +15878,10 @@ TSynMonitor = class(TSynPersistent) destructor Destroy; override; /// lock the instance for exclusive access // - needed only if you access directly the instance properties - procedure Lock; - {$ifdef HASINLINE}inline;{$endif} + procedure Lock; {$ifdef HASINLINE}inline;{$endif} /// release the instance for exclusive access // - needed only if you access directly the instance properties - procedure UnLock; - {$ifdef HASINLINE}inline;{$endif} + procedure UnLock; {$ifdef HASINLINE}inline;{$endif} /// create Count instances of this actual class in the supplied ObjArr[] class procedure InitializeObjArray(var ObjArr; Count: integer); virtual; /// should be called when the process starts, to resume the internal timer @@ -16090,7 +15925,7 @@ TSynMonitor = class(TSynPersistent) /// returns a TDocVariant with all published properties information // - thread-safe method function ComputeDetails: variant; - {$endif} + {$endif NOVARIANTS} /// used to allow thread safe timing // - by default, the internal TPrecisionTimer is not thread safe: you can // use this method to update the timing from many threads @@ -16220,8 +16055,6 @@ TSynMonitorServer = class(TSynMonitorInputOutput) property CurrentRequestCount: integer read fCurrentRequestCount; end; - {$M-} - /// a list of simple process statistics TSynMonitorObjArray = array of TSynMonitor; @@ -16234,118 +16067,6 @@ TSynMonitorServer = class(TSynMonitorInputOutput) /// class-reference type (metaclass) of a process statistic information TSynMonitorClass = class of TSynMonitor; - /// value object able to gather information about the current system memory - TSynMonitorMemory = class(TSynPersistent) - protected - FAllocatedUsed: TSynMonitorOneSize; - FAllocatedReserved: TSynMonitorOneSize; - FMemoryLoadPercent: integer; - FPhysicalMemoryFree: TSynMonitorOneSize; - FVirtualMemoryFree: TSynMonitorOneSize; - FPagingFileTotal: TSynMonitorOneSize; - FPhysicalMemoryTotal: TSynMonitorOneSize; - FVirtualMemoryTotal: TSynMonitorOneSize; - FPagingFileFree: TSynMonitorOneSize; - fLastMemoryInfoRetrievedTix: cardinal; - procedure RetrieveMemoryInfo; virtual; - function GetAllocatedUsed: TSynMonitorOneSize; - function GetAllocatedReserved: TSynMonitorOneSize; - function GetMemoryLoadPercent: integer; - function GetPagingFileFree: TSynMonitorOneSize; - function GetPagingFileTotal: TSynMonitorOneSize; - function GetPhysicalMemoryFree: TSynMonitorOneSize; - function GetPhysicalMemoryTotal: TSynMonitorOneSize; - function GetVirtualMemoryFree: TSynMonitorOneSize; - function GetVirtualMemoryTotal: TSynMonitorOneSize; - public - /// initialize the class, and its nested TSynMonitorOneSize instances - constructor Create; override; - /// finalize the class, and its nested TSynMonitorOneSize instances - destructor Destroy; override; - /// some text corresponding to current 'free/total' memory information - // - returns e.g. '10.3 GB / 15.6 GB' - class function FreeAsText: RawUTF8; - /// how many physical memory is currently installed, as text (e.g. '32GB'); - class function PhysicalAsText: RawUTF8; - /// returns a JSON object with the current system memory information - // - numbers would be given in KB (Bytes shl 10) - class function ToJSON: RawUTF8; - {$ifndef NOVARIANTS} - /// fill a TDocVariant with the current system memory information - // - numbers would be given in KB (Bytes shl 10) - class function ToVariant: variant; - {$endif} - published - /// Total of allocated memory used by the program - property AllocatedUsed: TSynMonitorOneSize read GetAllocatedUsed; - /// Total of allocated memory reserved by the program - property AllocatedReserved: TSynMonitorOneSize read GetAllocatedReserved; - /// Percent of memory in use for the system - property MemoryLoadPercent: integer read GetMemoryLoadPercent; - /// Total of physical memory for the system - property PhysicalMemoryTotal: TSynMonitorOneSize read GetPhysicalMemoryTotal; - /// Free of physical memory for the system - property PhysicalMemoryFree: TSynMonitorOneSize read GetPhysicalMemoryFree; - /// Total of paging file for the system - property PagingFileTotal: TSynMonitorOneSize read GetPagingFileTotal; - /// Free of paging file for the system - property PagingFileFree: TSynMonitorOneSize read GetPagingFileFree; - {$ifdef MSWINDOWS} - /// Total of virtual memory for the system - // - property not defined under Linux, since not applying to this OS - property VirtualMemoryTotal: TSynMonitorOneSize read GetVirtualMemoryTotal; - /// Free of virtual memory for the system - // - property not defined under Linux, since not applying to this OS - property VirtualMemoryFree: TSynMonitorOneSize read GetVirtualMemoryFree; - {$endif} - end; - - /// value object able to gather information about a system drive - TSynMonitorDisk = class(TSynPersistent) - protected - fName: TFileName; - {$ifdef MSWINDOWS} - fVolumeName: TFileName; - {$endif} - fAvailableSize: TSynMonitorOneSize; - fFreeSize: TSynMonitorOneSize; - fTotalSize: TSynMonitorOneSize; - fLastDiskInfoRetrievedTix: cardinal; - procedure RetrieveDiskInfo; virtual; - function GetName: TFileName; - function GetAvailable: TSynMonitorOneSize; - function GetFree: TSynMonitorOneSize; - function GetTotal: TSynMonitorOneSize; - public - /// initialize the class, and its nested TSynMonitorOneSize instances - constructor Create; override; - /// finalize the class, and its nested TSynMonitorOneSize instances - destructor Destroy; override; - /// some text corresponding to current 'free/total' disk information - // - could return e.g. 'D: 64.4 GB / 213.4 GB' - class function FreeAsText: RawUTF8; - published - /// the disk name - property Name: TFileName read GetName; - {$ifdef MSWINDOWS} - /// the volume name (only available on Windows) - property VolumeName: TFileName read fVolumeName write fVolumeName; - {$endif MSWINDOWS} - /// space currently available on this disk for the current user - // - may be less then FreeSize, if user quotas are specified - property AvailableSize: TSynMonitorOneSize read GetAvailable; - /// free space currently available on this disk - property FreeSize: TSynMonitorOneSize read GetFree; - /// total space - property TotalSize: TSynMonitorOneSize read GetTotal; - end; - -/// retrieve low-level information about a given disk partition -// - as used by TSynMonitorDisk -procedure GetDiskInfo(var aDriveFolderOrFile: TFileName; - out aAvailableBytes, aFreeBytes, aTotalBytes: QWord - {$ifdef MSWINDOWS}; aVolumeName: PFileName = nil{$endif}); - { ******************* cross-cutting classes and functions ***************** } @@ -16505,7 +16226,7 @@ TAutoFree = class(TInterfacedObject,IAutoFree) destructor Destroy; override; end; - {$ifdef DELPHI5OROLDER} // IAutoLocker -> internal error C3517 under Delphi 5 :( +{$ifdef DELPHI5OROLDER} // IAutoLocker -> internal error C3517 under Delphi 5 :( TAutoLocker = class protected fSafe: TSynLocker; @@ -16520,7 +16241,7 @@ TAutoLocker = class property Locker: TSynLocker read fSafe; end; IAutoLocker = TAutoLocker; - {$else} +{$else DELPHI5OROLDER} /// an interface used by TAutoLocker to protect multi-thread execution IAutoLocker = interface ['{97559643-6474-4AD3-AF72-B9BB84B4955D}'] @@ -16578,6 +16299,8 @@ TAutoLocker = class // as IAutoLocker so that this class may be automatically injected // - you may use the inherited TAutoLockerDebug class, as defined in SynLog.pas, // to debug unexpected race conditions due to such critical sections + // - consider inherit from high-level TSynPersistentLock or call low-level + // fSafe := NewSynLocker / fSafe^.DoneAndFreemem instead TAutoLocker = class(TInterfacedObjectWithCustomCreate,IAutoLocker) protected fSafe: TSynLocker; @@ -16634,119 +16357,8 @@ TAutoLocker = class(TInterfacedObjectWithCustomCreate,IAutoLocker) // - faster than IAutoLocker.Safe function property Locker: TSynLocker read fSafe; end; - {$endif DELPHI5OROLDER} - - /// the current state of a TBlockingProcess instance - TBlockingEvent = (evNone,evWaiting,evTimeOut,evRaised); - - {$M+} - /// a semaphore used to wait for some process to be finished - // - used e.g. by TBlockingCallback in mORMot.pas - // - once created, process would block via a WaitFor call, which would be - // released when NotifyFinished is called by the process background thread - TBlockingProcess = class(TEvent) - protected - fTimeOutMs: integer; - fEvent: TBlockingEvent; - fSafe: PSynLocker; - fOwnedSafe: TAutoLocker; - procedure ResetInternal; virtual; // override to reset associated params - public - /// initialize the semaphore instance - // - specify a time out millliseconds period after which blocking execution - // should be handled as failure (if 0 is set, default 3000 would be used) - // - an associated mutex shall be supplied - constructor Create(aTimeOutMs: integer; const aSafe: TSynLocker); reintroduce; overload; virtual; - /// initialize the semaphore instance - // - specify a time out millliseconds period after which blocking execution - // should be handled as failure (if 0 is set, default 3000 would be used) - // - an associated mutex would be created and owned by this instance - constructor Create(aTimeOutMs: integer); reintroduce; overload; virtual; - /// finalize the instance - destructor Destroy; override; - /// called to wait for NotifyFinished() to be called, or trigger timeout - // - returns the final state of the process, i.e. evRaised or evTimeOut - function WaitFor: TBlockingEvent; reintroduce; overload; virtual; - /// called to wait for NotifyFinished() to be called, or trigger timeout - // - returns the final state of the process, i.e. evRaised or evTimeOut - function WaitFor(TimeOutMS: integer): TBlockingEvent; reintroduce; overload; - /// should be called by the background process when it is finished - // - the caller would then let its WaitFor method return - // - returns TRUE on success (i.e. status was not evRaised or evTimeout) - // - if the instance is already locked (e.g. when retrieved from - // TBlockingProcessPool.FromCallLocked), you may set alreadyLocked=TRUE - function NotifyFinished(alreadyLocked: boolean=false): boolean; virtual; - /// just a wrapper to reset the internal Event state to evNone - // - may be used to re-use the same TBlockingProcess instance, after - // a successfull WaitFor/NotifyFinished process - // - returns TRUE on success (i.e. status was not evWaiting), setting - // the current state to evNone, and the Call property to 0 - // - if there is a WaitFor currently in progress, returns FALSE - function Reset: boolean; virtual; - /// just a wrapper around fSafe^.Lock - procedure Lock; - /// just a wrapper around fSafe^.Unlock - procedure Unlock; - published - /// the current state of process - // - use Reset method to re-use this instance after a WaitFor process - property Event: TBlockingEvent read fEvent; - /// the time out period, in ms, as defined at constructor level - property TimeOutMs: integer read fTimeOutMS; - end; - {$M-} - - /// used to identify each TBlockingProcessPool call - // - allow to match a given TBlockingProcessPoolItem semaphore - TBlockingProcessPoolCall = type integer; - - /// a semaphore used in the TBlockingProcessPool - // - such semaphore have a Call field to identify each execution - TBlockingProcessPoolItem = class(TBlockingProcess) - protected - fCall: TBlockingProcessPoolCall; - procedure ResetInternal; override; - published - /// an unique identifier, when owned by a TBlockingProcessPool - // - Reset would restore this field to its 0 default value - property Call: TBlockingProcessPoolCall read fCall; - end; +{$endif DELPHI5OROLDER} - /// class-reference type (metaclass) of a TBlockingProcess - TBlockingProcessPoolItemClass = class of TBlockingProcessPoolItem; - - /// manage a pool of TBlockingProcessPoolItem instances - // - each call will be identified via a TBlockingProcessPoolCall unique value - // - to be used to emulate e.g. blocking execution from an asynchronous - // event-driven DDD process - // - it would also allow to re-use TEvent system resources - TBlockingProcessPool = class(TSynPersistent) - protected - fClass: TBlockingProcessPoolItemClass; - fPool: TObjectListLocked; - fCallCounter: TBlockingProcessPoolCall; // set TBlockingProcessPoolItem.Call - public - /// initialize the pool, for a given implementation class - constructor Create(aClass: TBlockingProcessPoolItemClass=nil); reintroduce; - /// finalize the pool - // - would also force all pending WaitFor to trigger a evTimeOut - destructor Destroy; override; - /// book a TBlockingProcess from the internal pool - // - returns nil on error (e.g. the instance is destroying) - // - or returns the blocking process instance corresponding to this call; - // its Call property would identify the call for the asynchronous callback, - // then after WaitFor, the Reset method should be run to release the mutex - // for the pool - function NewProcess(aTimeOutMs: integer): TBlockingProcessPoolItem; virtual; - /// retrieve a TBlockingProcess from its call identifier - // - may be used e.g. from the callback of the asynchronous process - // to set some additional parameters to the inherited TBlockingProcess, - // then call NotifyFinished to release the caller WaitFor - // - if leavelocked is TRUE, the returned instance would be locked: caller - // should execute result.Unlock or NotifyFinished(true) after use - function FromCall(call: TBlockingProcessPoolCall; - locked: boolean=false): TBlockingProcessPoolItem; virtual; - end; {$ifndef DELPHI5OROLDER} // internal error C3517 under Delphi 5 :( {$ifndef NOVARIANTS} @@ -16893,155 +16505,6 @@ TLockedDocVariant = class(TInterfacedObjectWithCustomCreate,ILockedDocVariant) {$endif} type - TSynBackgroundThreadProcess = class; - - /// event callback executed periodically by TSynBackgroundThreadProcess - // - Event is wrTimeout after the OnProcessMS waiting period - // - Event is wrSignaled if ProcessEvent.SetEvent has been called - TOnSynBackgroundThreadProcess = procedure(Sender: TSynBackgroundThreadProcess; - Event: TWaitResult) of object; - - /// TThread able to run a method at a given periodic pace - TSynBackgroundThreadProcess = class(TSynBackgroundThreadAbstract) - protected - fOnProcess: TOnSynBackgroundThreadProcess; - fOnException: TNotifyEvent; - fOnProcessMS: cardinal; - fStats: TSynMonitor; - procedure ExecuteLoop; override; - public - /// initialize the thread for a periodic task processing - // - aOnProcess would be called when ProcessEvent.SetEvent is called or - // aOnProcessMS milliseconds period was elapse since last process - // - if aOnProcessMS is 0, will wait until ProcessEvent.SetEvent is called - // - you could define some callbacks to nest the thread execution, e.g. - // assigned to TSQLRestServer.BeginCurrentThread/EndCurrentThread - constructor Create(const aThreadName: RawUTF8; - aOnProcess: TOnSynBackgroundThreadProcess; aOnProcessMS: cardinal; - aOnBeforeExecute: TNotifyThreadEvent=nil; - aOnAfterExecute: TNotifyThreadEvent=nil; - aStats: TSynMonitorClass=nil; CreateSuspended: boolean=false); reintroduce; virtual; - /// finalize the thread - destructor Destroy; override; - /// access to the implementation event of the periodic task - property OnProcess: TOnSynBackgroundThreadProcess read fOnProcess; - /// event callback executed when OnProcess did raise an exception - // - supplied Sender parameter is the raised Exception instance - property OnException: TNotifyEvent read fOnException write fOnException; - published - /// access to the delay, in milliseconds, of the periodic task processing - property OnProcessMS: cardinal read fOnProcessMS write fOnProcessMS; - /// processing statistics - // - may be nil if aStats was nil in the class constructor - property Stats: TSynMonitor read fStats; - end; - - TSynBackgroundTimer = class; - - /// event callback executed periodically by TSynBackgroundThreadProcess - // - Event is wrTimeout after the OnProcessMS waiting period - // - Event is wrSignaled if ProcessEvent.SetEvent has been called - // - Msg is '' if there is no pending message in this task FIFO - // - Msg is set for each pending message in this task FIFO - TOnSynBackgroundTimerProcess = procedure(Sender: TSynBackgroundTimer; - Event: TWaitResult; const Msg: RawUTF8) of object; - - /// used by TSynBackgroundTimer internal registration list - TSynBackgroundTimerTask = record - OnProcess: TOnSynBackgroundTimerProcess; - Secs: cardinal; - NextTix: Int64; - FIFO: TRawUTF8DynArray; - end; - /// stores TSynBackgroundTimer internal registration list - TSynBackgroundTimerTaskDynArray = array of TSynBackgroundTimerTask; - - /// TThread able to run one or several tasks at a periodic pace - // - as used e.g. by TSQLRest.TimerEnable/TimerDisable methods, via the - // inherited TSQLRestBackgroundTimer - // - each process can have its own FIFO of text messages - TSynBackgroundTimer = class(TSynBackgroundThreadProcess) - protected - fTask: TSynBackgroundTimerTaskDynArray; - fTasks: TDynArray; - fTaskLock: TSynLocker; - procedure EverySecond(Sender: TSynBackgroundThreadProcess; Event: TWaitResult); - function Find(const aProcess: TMethod): integer; - function Add(aOnProcess: TOnSynBackgroundTimerProcess; - const aMsg: RawUTF8; aExecuteNow: boolean): boolean; - public - /// initialize the thread for a periodic task processing - // - you could define some callbacks to nest the thread execution, e.g. - // assigned to TSQLRestServer.BeginCurrentThread/EndCurrentThread, as - // made by TSQLRestBackgroundTimer.Create - constructor Create(const aThreadName: RawUTF8; - aOnBeforeExecute: TNotifyThreadEvent=nil; aOnAfterExecute: TNotifyThreadEvent=nil; - aStats: TSynMonitorClass=nil); reintroduce; virtual; - /// finalize the thread - destructor Destroy; override; - /// define a process method for a task running on a periodic number of seconds - // - for background process on a mORMot service, consider using TSQLRest - // TimerEnable/TimerDisable methods, and its associated BackgroundTimer thread - procedure Enable(aOnProcess: TOnSynBackgroundTimerProcess; aOnProcessSecs: cardinal); - /// undefine a task running on a periodic number of seconds - // - aOnProcess should have been registered by a previous call to Enable() method - // - returns true on success, false if the supplied task was not registered - // - for background process on a mORMot service, consider using TSQLRestServer - // TimerEnable/TimerDisable methods, and their TSynBackgroundTimer thread - function Disable(aOnProcess: TOnSynBackgroundTimerProcess): boolean; - /// add a message to be processed during the next execution of a task - // - supplied message will be added to the internal FIFO list associated - // with aOnProcess, then supplied to as aMsg parameter for each call - // - if aExecuteNow is true, won't wait for the next aOnProcessSecs occurence - // - aOnProcess should have been registered by a previous call to Enable() method - // - returns true on success, false if the supplied task was not registered - function EnQueue(aOnProcess: TOnSynBackgroundTimerProcess; - const aMsg: RawUTF8; aExecuteNow: boolean=false): boolean; overload; - /// add a message to be processed during the next execution of a task - // - supplied message will be added to the internal FIFO list associated - // with aOnProcess, then supplied to as aMsg parameter for each call - // - if aExecuteNow is true, won't wait for the next aOnProcessSecs occurence - // - aOnProcess should have been registered by a previous call to Enable() method - // - returns true on success, false if the supplied task was not registered - function EnQueue(aOnProcess: TOnSynBackgroundTimerProcess; - const aMsgFmt: RawUTF8; const Args: array of const; aExecuteNow: boolean=false): boolean; overload; - /// remove a message from the processing list - // - supplied message will be searched in the internal FIFO list associated - // with aOnProcess, then removed from the list if found - // - aOnProcess should have been registered by a previous call to Enable() method - // - returns true on success, false if the supplied message was not registered - function DeQueue(aOnProcess: TOnSynBackgroundTimerProcess; const aMsg: RawUTF8): boolean; - /// execute a task without waiting for the next aOnProcessSecs occurence - // - aOnProcess should have been registered by a previous call to Enable() method - // - returns true on success, false if the supplied task was not registered - function ExecuteNow(aOnProcess: TOnSynBackgroundTimerProcess): boolean; - /// returns true if there is currenly one task processed - function Processing: boolean; - /// wait until no background task is processed - procedure WaitUntilNotProcessing(timeoutsecs: integer=10); - /// low-level access to the internal task list - property Task: TSynBackgroundTimerTaskDynArray read fTask; - /// low-level access to the internal task mutex - property TaskLock: TSynLocker read fTaskLock; - end; - - /// an alternative to TSynPersistentLocked, but via PSynLocker and not TSynLocker - // - TSynLocker will increase inherited fields offset so it may be fine if - // you want to avoid allocation of a PSynLocker buffer, but you may - // prefer this implementation for general-purpose processing class - TSynPersistentLock = class(TSynPersistent) - protected - fSafe: PSynLocker; // TSynLocker will increase inherited fields offset - public - /// initialize the instance, and its associated lock - constructor Create; override; - /// finalize the instance, and its associated lock - destructor Destroy; override; - /// access to the associated instance critical section - // - call Safe.Lock/UnLock to protect multi-thread access on this storage - property Safe: PSynLocker read fSafe; - end; - /// class-reference type (metaclass) of an TSynPersistentLock class TSynPersistentLockClass = class of TSynPersistentLock; @@ -17088,429 +16551,30 @@ TObjectListSorted = class(TSynPersistentLock) property ObjArray: TSynPersistentLockDynArray read fObjArray; end; - /// abstract high-level handling of (SynLZ-)compressed persisted storage - // - LoadFromReader/SaveToWriter abstract methods should be overriden - // with proper binary persistence implementation - TSynPersistentStore = class(TSynPersistentLock) - protected - fName: RawUTF8; - fReader: TFastReader; - fReaderTemp: PRawByteString; - fLoadFromLastUncompressed, fSaveToLastUncompressed: integer; - fLoadFromLastAlgo: TAlgoCompress; - /// low-level virtual methods implementing the persistence reading - procedure LoadFromReader; virtual; - procedure SaveToWriter(aWriter: TFileBufferWriter); virtual; - public - /// initialize a void storage with the supplied name - constructor Create(const aName: RawUTF8); reintroduce; overload; virtual; - /// initialize a storage from a SaveTo persisted buffer - // - raise a EFastReader exception on decoding error - constructor CreateFrom(const aBuffer: RawByteString; - aLoad: TAlgoCompressLoad = aclNormal); - /// initialize a storage from a SaveTo persisted buffer - // - raise a EFastReader exception on decoding error - constructor CreateFromBuffer(aBuffer: pointer; aBufferLen: integer; - aLoad: TAlgoCompressLoad = aclNormal); - /// initialize a storage from a SaveTo persisted buffer - // - raise a EFastReader exception on decoding error - constructor CreateFromFile(const aFileName: TFileName; - aLoad: TAlgoCompressLoad = aclNormal); - /// fill the storage from a SaveTo persisted buffer - // - actually call the LoadFromReader() virtual method for persistence - // - raise a EFastReader exception on decoding error - procedure LoadFrom(const aBuffer: RawByteString; - aLoad: TAlgoCompressLoad = aclNormal); overload; - /// initialize the storage from a SaveTo persisted buffer - // - actually call the LoadFromReader() virtual method for persistence - // - raise a EFastReader exception on decoding error - procedure LoadFrom(aBuffer: pointer; aBufferLen: integer; - aLoad: TAlgoCompressLoad = aclNormal); overload; virtual; - /// initialize the storage from a SaveToFile content - // - actually call the LoadFromReader() virtual method for persistence - // - returns false if the file is not found, true if the file was loaded - // without any problem, or raise a EFastReader exception on decoding error - function LoadFromFile(const aFileName: TFileName; - aLoad: TAlgoCompressLoad = aclNormal): boolean; - /// persist the content as a SynLZ-compressed binary blob - // - to be retrieved later on via LoadFrom method - // - actually call the SaveToWriter() protected virtual method for persistence - // - you can specify ForcedAlgo if you want to override the default AlgoSynLZ - procedure SaveTo(out aBuffer: RawByteString; nocompression: boolean=false; - BufLen: integer=65536; ForcedAlgo: TAlgoCompress=nil); overload; virtual; - /// persist the content as a SynLZ-compressed binary blob - // - just an overloaded wrapper - function SaveTo(nocompression: boolean=false; BufLen: integer=65536; - ForcedAlgo: TAlgoCompress=nil): RawByteString; overload; - {$ifdef HASINLINE}inline;{$endif} - /// persist the content as a SynLZ-compressed binary file - // - to be retrieved later on via LoadFromFile method - // - returns the number of bytes of the resulting file - // - actually call the SaveTo method for persistence - function SaveToFile(const aFileName: TFileName; nocompression: boolean=false; - BufLen: integer=65536; ForcedAlgo: TAlgoCompress=nil): PtrUInt; - /// one optional text associated with this storage - // - you can define this field as published to serialize its value in log/JSON - property Name: RawUTF8 read fName; - /// after a LoadFrom(), contains the uncompressed data size read - property LoadFromLastUncompressed: integer read fLoadFromLastUncompressed; - /// after a SaveTo(), contains the uncompressed data size written - property SaveToLastUncompressed: integer read fSaveToLastUncompressed; - end; - - /// implement binary persistence and JSON serialization (not deserialization) - TSynPersistentStoreJson = class(TSynPersistentStore) - protected - // append "name" -> inherited should add properties to the JSON object - procedure AddJSON(W: TTextWriter); virtual; - public - /// serialize this instance as a JSON object - function SaveToJSON(reformat: TTextWriterJSONFormat = jsonCompact): RawUTF8; - end; - -type - /// 64-bit integer unique identifier, as computed by TSynUniqueIdentifierGenerator - // - they are increasing over time (so are much easier to store/shard/balance - // than UUID/GUID), and contain generation time and a 16-bit process ID - // - mapped by TSynUniqueIdentifierBits memory structure - // - may be used on client side for something similar to a MongoDB ObjectID, - // but compatible with TSQLRecord.ID: TID properties - TSynUniqueIdentifier = type Int64; - - /// 16-bit unique process identifier, used to compute TSynUniqueIdentifier - // - each TSynUniqueIdentifierGenerator instance is expected to have - // its own unique process identifier, stored as a 16 bit integer 1..65535 value - TSynUniqueIdentifierProcess = type word; - - {$A-} - /// map 64-bit integer unique identifier internal memory structure - // - as stored in TSynUniqueIdentifier = Int64 values, and computed by - // TSynUniqueIdentifierGenerator - // - bits 0..14 map a 15-bit increasing counter (collision-free) - // - bits 15..30 map a 16-bit process identifier - // - bits 31..63 map a 33-bit UTC time, encoded as seconds since Unix epoch - {$ifdef UNICODE}TSynUniqueIdentifierBits = record{$else}TSynUniqueIdentifierBits = object{$endif} - public - /// the actual 64-bit storage value - // - in practice, only first 63 bits are used - Value: TSynUniqueIdentifier; - /// 15-bit counter (0..32767), starting with a random value - function Counter: word; - {$ifdef HASINLINE}inline;{$endif} - /// 16-bit unique process identifier - // - as specified to TSynUniqueIdentifierGenerator constructor - function ProcessID: TSynUniqueIdentifierProcess; - {$ifdef HASINLINE}inline;{$endif} - /// low-endian 4-byte value representing the seconds since the Unix epoch - // - time is expressed in Coordinated Universal Time (UTC), not local time - // - it uses in fact a 33-bit resolution, so is "Year 2038" bug-free - function CreateTimeUnix: TUnixTime; - {$ifdef HASINLINE}inline;{$endif} - /// fill this unique identifier structure from its TSynUniqueIdentifier value - // - is just a wrapper around PInt64(@self)^ - procedure From(const AID: TSynUniqueIdentifier); - {$ifdef HASINLINE}inline;{$endif} - {$ifndef NOVARIANTS} - /// convert this identifier as an explicit TDocVariant JSON object - // - returns e.g. - // ! {"Created":"2016-04-19T15:27:58","Identifier":1,"Counter":1, - // ! "Value":3137644716930138113,"Hex":"2B8B273F00008001"} - function AsVariant: variant; {$ifdef HASINLINE}inline;{$endif} - /// convert this identifier to an explicit TDocVariant JSON object - // - returns e.g. - // ! {"Created":"2016-04-19T15:27:58","Identifier":1,"Counter":1, - // ! "Value":3137644716930138113,"Hex":"2B8B273F00008001"} - procedure ToVariant(out result: variant); - {$endif NOVARIANTS} - /// extract the UTC generation timestamp from the identifier as TDateTime - // - time is expressed in Coordinated Universal Time (UTC), not local time - function CreateDateTime: TDateTime; - {$ifdef HASINLINE}inline;{$endif} - /// extract the UTC generation timestamp from the identifier - // - time is expressed in Coordinated Universal Time (UTC), not local time - function CreateTimeLog: TTimeLog; - {$ifndef DELPHI5OROLDER} - /// compare two Identifiers - function Equal(const Another: TSynUniqueIdentifierBits): boolean; - {$ifdef HASINLINE}inline;{$endif} - {$endif DELPHI5OROLDER} - /// convert the identifier into a 16 chars hexadecimal string - function ToHexa: RawUTF8; - {$ifdef HASINLINE}inline;{$endif} - /// fill this unique identifier back from a 16 chars hexadecimal string - // - returns TRUE if the supplied hexadecimal is on the expected format - // - returns FALSE if the supplied text is invalid - function FromHexa(const hexa: RawUTF8): boolean; - /// fill this unique identifier with a fake value corresponding to a given - // timestamp - // - may be used e.g. to limit database queries on a particular time range - // - bits 0..30 would be 0, i.e. would set Counter = 0 and ProcessID = 0 - procedure FromDateTime(const aDateTime: TDateTime); - /// fill this unique identifier with a fake value corresponding to a given - // timestamp - // - may be used e.g. to limit database queries on a particular time range - // - bits 0..30 would be 0, i.e. would set Counter = 0 and ProcessID = 0 - procedure FromUnixTime(const aUnixTime: TUnixTime); - end; - {$A+} - - /// points to a 64-bit integer identifier, as computed by TSynUniqueIdentifierGenerator - // - may be used to access the identifier internals, from its stored - // Int64 or TSynUniqueIdentifier value - PSynUniqueIdentifierBits = ^TSynUniqueIdentifierBits; - - /// a 24 chars cyphered hexadecimal string, mapping a TSynUniqueIdentifier - // - has handled by TSynUniqueIdentifierGenerator.ToObfuscated/FromObfuscated - TSynUniqueIdentifierObfuscated = type RawUTF8; - - /// thread-safe 64-bit integer unique identifier computation - // - may be used on client side for something similar to a MongoDB ObjectID, - // but compatible with TSQLRecord.ID: TID properties, since it will contain - // a 63-bit unsigned integer, following our ORM expectations - // - each identifier would contain a 16-bit process identifier, which is - // supplied by the application, and should be unique for this process at a - // given time - // - identifiers may be obfuscated as hexadecimal text, using both encryption - // and digital signature - TSynUniqueIdentifierGenerator = class(TSynPersistent) - protected - fUnixCreateTime: cardinal; - fLatestCounterOverflowUnixCreateTime: cardinal; - fIdentifier: TSynUniqueIdentifierProcess; - fIdentifierShifted: cardinal; - fLastCounter: cardinal; - fCrypto: array[0..7] of cardinal; // only fCrypto[6..7] are used in practice - fCryptoCRC: cardinal; - fSafe: TSynLocker; - function GetComputedCount: Int64; - public - /// initialize the generator for the given 16-bit process identifier - // - you can supply an obfuscation key, which should be shared for the - // whole system, so that you may use FromObfuscated/ToObfuscated methods - constructor Create(aIdentifier: TSynUniqueIdentifierProcess; - const aSharedObfuscationKey: RawUTF8=''); reintroduce; - /// finalize the generator structure - destructor Destroy; override; - /// return a new unique ID - // - this method is very optimized, and would use very little CPU - procedure ComputeNew(out result: TSynUniqueIdentifierBits); overload; - /// return a new unique ID, type-casted to an Int64 - function ComputeNew: Int64; overload; - {$ifdef HASINLINE}inline;{$endif} - /// return an unique ID matching this generator pattern, at a given timestamp - // - may be used e.g. to limit database queries on a particular time range - procedure ComputeFromDateTime(const aDateTime: TDateTime; out result: TSynUniqueIdentifierBits); - /// return an unique ID matching this generator pattern, at a given timestamp - // - may be used e.g. to limit database queries on a particular time range - procedure ComputeFromUnixTime(const aUnixTime: TUnixTime; out result: TSynUniqueIdentifierBits); - /// map a TSynUniqueIdentifier as 24 chars cyphered hexadecimal text - // - cyphering includes simple key-based encryption and a CRC-32 digital signature - function ToObfuscated(const aIdentifier: TSynUniqueIdentifier): TSynUniqueIdentifierObfuscated; - /// retrieve a TSynUniqueIdentifier from 24 chars cyphered hexadecimal text - // - any file extension (e.g. '.jpeg') would be first deleted from the - // supplied obfuscated text - // - returns true if the supplied obfuscated text has the expected layout - // and a valid digital signature - // - returns false if the supplied obfuscated text is invalid - function FromObfuscated(const aObfuscated: TSynUniqueIdentifierObfuscated; - out aIdentifier: TSynUniqueIdentifier): boolean; - /// some 32-bit value, derivated from aSharedObfuscationKey as supplied - // to the class constructor - // - FromObfuscated and ToObfuscated methods will validate their hexadecimal - // content with this value to secure the associated CRC - // - may be used e.g. as system-depending salt - property CryptoCRC: cardinal read fCryptoCRC; - /// direct access to the associated mutex - property Safe: TSynLocker read fSafe; - published - /// the process identifier, associated with this generator - property Identifier: TSynUniqueIdentifierProcess read fIdentifier; - /// how many times ComputeNew method has been called - property ComputedCount: Int64 read GetComputedCount; - end; - -type - /// store CPU and RAM usage for a given process - // - as used by TSystemUse class - TSystemUseData = packed record - /// when the data has been sampled - Timestamp: TDateTime; - /// percent of current Kernel-space CPU usage for this process - Kernel: single; - /// percent of current User-space CPU usage for this process - User: single; - /// how many KB of working memory are used by this process - WorkKB: cardinal; - /// how many KB of virtual memory are used by this process - VirtualKB: cardinal; - end; - /// store CPU and RAM usage history for a given process - // - as returned by TSystemUse.History - TSystemUseDataDynArray = array of TSystemUseData; - - /// low-level structure used to compute process memory and CPU usage - {$ifdef UNICODE}TProcessInfo = record{$else}TProcessInfo = object{$endif} - private - fSysPrevIdle, fSysPrevKernel, fSysPrevUser, - fDiffIdle, fDiffKernel, fDiffUser, fDiffTotal: Int64; - public - /// initialize the system/process resource tracking - function Init: boolean; - /// to be called before PerSystem() or PerProcess() iteration - function Start: boolean; - /// percent of current Idle/Kernel/User CPU usage for all processes - function PerSystem(out Idle,Kernel,User: currency): boolean; - /// retrieve CPU and RAM usage for a given process - function PerProcess(PID: cardinal; Now: PDateTime; out Data: TSystemUseData; - var PrevKernel, PrevUser: Int64): boolean; - end; - - /// event handler which may be executed by TSystemUse.BackgroundExecute - // - called just after the measurement of each process CPU and RAM consumption - // - run from the background thread, so should not directly make VCL calls, - // unless BackgroundExecute is run from a VCL timer - TOnSystemUseMeasured = procedure(ProcessID: integer; const Data: TSystemUseData) of object; - - /// internal storage of CPU and RAM usage for one process - TSystemUseProcess = record - ID: integer; - Data: TSystemUseDataDynArray; - PrevKernel: Int64; - PrevUser: Int64; - end; - /// internal storage of CPU and RAM usage for a set of processes - TSystemUseProcessDynArray = array of TSystemUseProcess; - - /// monitor CPU and RAM usage of one or several processes - // - you should execute BackgroundExecute on a regular pace (e.g. every second) - // to gather low-level CPU and RAM information for the given set of processes - // - is able to keep an history of latest sample values - // - use Current class function to access a process-wide instance - TSystemUse = class(TSynPersistent) - protected - fProcess: TSystemUseProcessDynArray; - fProcesses: TDynArray; - fDataIndex: integer; - fProcessInfo: TProcessInfo; - fSafe: TAutoLocker; - fHistoryDepth: integer; - fOnMeasured: TOnSystemUseMeasured; - fTimer: TSynBackgroundTimer; - fUnsubscribeProcessOnAccessError: boolean; - function ProcessIndex(aProcessID: integer): integer; - public - /// a TSynBackgroundThreadProcess compatible event - // - matches TOnSynBackgroundTimerProcess callback signature - // - to be supplied e.g. to a TSynBackgroundTimer.Enable method so that it - // will run every few seconds and retrieve the CPU and RAM use - procedure BackgroundExecute(Sender: TSynBackgroundTimer; - Event: TWaitResult; const Msg: RawUTF8); - /// a VCL's TTimer.OnTimer compatible event - // - to be run every few seconds and retrieve the CPU and RAM use: - // ! tmrSystemUse.Interval := 10000; // every 10 seconds - // ! tmrSystemUse.OnTimer := TSystemUse.Current.OnTimerExecute; - procedure OnTimerExecute(Sender: TObject); - /// track the CPU and RAM usage of the supplied set of Process ID - // - any aProcessID[]=0 will be replaced by the current process ID - // - you can specify the number of sample values for the History() method - // - you should then execute the BackgroundExecute method of this instance - // in a VCL timer or from a TSynBackgroundTimer.Enable() registration - constructor Create(const aProcessID: array of integer; - aHistoryDepth: integer=60); reintroduce; overload; virtual; - /// track the CPU and RAM usage of the current process - // - you can specify the number of sample values for the History() method - // - you should then execute the BackgroundExecute method of this instance - // in a VCL timer or from a TSynBackgroundTimer.Enable() registration - constructor Create(aHistoryDepth: integer=60); reintroduce; overload; virtual; - /// finalize all internal data information - destructor Destroy; override; - /// add a Process ID to the internal tracking list - procedure Subscribe(aProcessID: integer); - /// remove a Process ID from the internal tracking list - function Unsubscribe(aProcessID: integer): boolean; - /// returns the total (Kernel+User) CPU usage percent of the supplied process - // - aProcessID=0 will return information from the current process - // - returns -1 if the Process ID was not registered via Create/Subscribe - function Percent(aProcessID: integer=0): single; overload; - /// returns the Kernel-space CPU usage percent of the supplied process - // - aProcessID=0 will return information from the current process - // - returns -1 if the Process ID was not registered via Create/Subscribe - function PercentKernel(aProcessID: integer=0): single; overload; - /// returns the User-space CPU usage percent of the supplied process - // - aProcessID=0 will return information from the current process - // - returns -1 if the Process ID was not registered via Create/Subscribe - function PercentUser(aProcessID: integer=0): single; overload; - /// returns the total (Work+Paged) RAM use of the supplied process, in KB - // - aProcessID=0 will return information from the current process - // - returns 0 if the Process ID was not registered via Create/Subscribe - function KB(aProcessID: integer=0): cardinal; overload; - /// percent of current Idle/Kernel/User CPU usage for all processes - function PercentSystem(out Idle,Kernel,User: currency): boolean; - /// returns the detailed CPU and RAM usage percent of the supplied process - // - aProcessID=0 will return information from the current process - // - returns -1 if the Process ID was not registered via Create/Subscribe - function Data(out aData: TSystemUseData; aProcessID: integer=0): boolean; overload; - /// returns the detailed CPU and RAM usage percent of the supplied process - // - aProcessID=0 will return information from the current process - // - returns Timestamp=0 if the Process ID was not registered via Create/Subscribe - function Data(aProcessID: integer=0): TSystemUseData; overload; - /// returns total (Kernel+User) CPU usage percent history of the supplied process - // - aProcessID=0 will return information from the current process - // - returns nil if the Process ID was not registered via Create/Subscribe - // - returns the sample values as an array, starting from the last to the oldest - // - you can customize the maximum depth, with aDepth < HistoryDepth - function History(aProcessID: integer=0; aDepth: integer=0): TSingleDynArray; overload; - /// returns total (Kernel+User) CPU usage percent history of the supplied - // process, as a string of two digits values - // - aProcessID=0 will return information from the current process - // - returns '' if the Process ID was not registered via Create/Subscribe - // - you can customize the maximum depth, with aDepth < HistoryDepth - // - the memory history (in MB) can be optionally returned in aDestMemoryMB - function HistoryText(aProcessID: integer=0; aDepth: integer=0; - aDestMemoryMB: PRawUTF8=nil): RawUTF8; - {$ifndef NOVARIANTS} - /// returns total (Kernel+User) CPU usage percent history of the supplied process - // - aProcessID=0 will return information from the current process - // - returns null if the Process ID was not registered via Create/Subscribe - // - returns the sample values as a TDocVariant array, starting from the - // last to the oldest, with two digits precision (as currency values) - // - you can customize the maximum depth, with aDepth < HistoryDepth - function HistoryVariant(aProcessID: integer=0; aDepth: integer=0): variant; - {$endif} - /// access to a global instance, corresponding to the current process - // - its HistoryDepth will be of 60 items - class function Current(aCreateIfNone: boolean=true): TSystemUse; - /// returns detailed CPU and RAM usage history of the supplied process - // - aProcessID=0 will return information from the current process - // - returns nil if the Process ID was not registered via Create/Subscribe - // - returns the sample values as an array, starting from the last to the oldest - // - you can customize the maximum depth, with aDepth < HistoryDepth - function HistoryData(aProcessID: integer=0; aDepth: integer=0): TSystemUseDataDynArray; overload; - /// if any unexisting (e.g. closed/killed) process should be unregistered - // - e.g. if OpenProcess() API call fails - property UnsubscribeProcessOnAccessError: boolean - read fUnsubscribeProcessOnAccessError write fUnsubscribeProcessOnAccessError; - /// how many items are stored internally, and returned by the History() method - property HistoryDepth: integer read fHistoryDepth; - /// executed when TSystemUse.BackgroundExecute finished its measurement - property OnMeasured: TOnSystemUseMeasured read fOnMeasured write fOnMeasured; - /// low-level access to the associated timer running BackgroundExecute - // - equals nil if has been associated to no timer - property Timer: TSynBackgroundTimer read fTimer write fTimer; - end; - /// convert a size to a human readable value power-of-two metric value -// - append EB, PB, TB, GB, MB, KB or B symbol +// - append EB, PB, TB, GB, MB, KB or B symbol with or without preceding space // - for EB, PB, TB, GB, MB and KB, add one fractional digit -procedure KB(bytes: Int64; out result: TShort16); overload; +procedure KB(bytes: Int64; out result: TShort16; nospace: boolean); overload; /// convert a size to a human readable value -// - append EB, PB, TB, GB, MB, KB or B symbol +// - append EB, PB, TB, GB, MB, KB or B symbol with preceding space // - for EB, PB, TB, GB, MB and KB, add one fractional digit function KB(bytes: Int64): TShort16; overload; {$ifdef FPC_OR_UNICODE}inline;{$endif} // Delphi 2007 is buggy as hell +/// convert a size to a human readable value +// - append EB, PB, TB, GB, MB, KB or B symbol without preceding space +// - for EB, PB, TB, GB, MB and KB, add one fractional digit +function KBNoSpace(bytes: Int64): TShort16; + {$ifdef FPC_OR_UNICODE}inline;{$endif} // Delphi 2007 is buggy as hell + +/// convert a size to a human readable value +// - append EB, PB, TB, GB, MB, KB or B symbol with or without preceding space +// - for EB, PB, TB, GB, MB and KB, add one fractional digit +function KB(bytes: Int64; nospace: boolean): TShort16; overload; + {$ifdef FPC_OR_UNICODE}inline;{$endif} // Delphi 2007 is buggy as hell + /// convert a string size to a human readable value // - append EB, PB, TB, GB, MB, KB or B symbol // - for EB, PB, TB, GB, MB and KB, add one fractional digit @@ -17523,20 +16587,20 @@ function KB(const buffer: RawByteString): TShort16; overload; procedure KBU(bytes: Int64; var result: RawUTF8); /// convert a micro seconds elapsed time into a human readable value -// - append 'us', 'ms' or 's' symbol -// - for 'us' and 'ms', add two fractional digits +// - append 'us', 'ms', 's', 'm', 'h' and 'd' symbol for the given value range, +// with two fractional digits function MicroSecToString(Micro: QWord): TShort16; overload; {$ifdef FPC_OR_UNICODE}inline;{$endif} // Delphi 2007 is buggy as hell /// convert a micro seconds elapsed time into a human readable value -// - append 'us', 'ms' or 's' symbol -// - for 'us' and 'ms', add two fractional digits +// - append 'us', 'ms', 's', 'm', 'h' and 'd' symbol for the given value range, +// with two fractional digits procedure MicroSecToString(Micro: QWord; out result: TShort16); overload; /// convert an integer value into its textual representation with thousands marked // - ThousandSep is the character used to separate thousands in numbers with // more than three digits to the left of the decimal separator -function IntToThousandString(Value: integer; const ThousandSep: RawUTF8=','): RawUTF8; +function IntToThousandString(Value: integer; const ThousandSep: TShort4=','): shortstring; /// return the Delphi Compiler Version // - returns 'Delphi 2007' or 'Delphi 2010' e.g. @@ -17697,30 +16761,29 @@ implementation {$ifdef FPC} uses - {$ifdef Linux} - SynFPCLinux, + {$ifdef LINUX} Unix, dynlibs, termio, {$ifdef BSD} - ctypes, sysctl, {$else} Linux, SysCall, - {$endif} - {$endif} - {$ifndef MSWINDOWS} - {$ifdef FPCUSEVERSIONINFO} // should be enabled in Synopse.inc + {$endif BSD} + {$ifdef FPCUSEVERSIONINFO} // to be enabled in Synopse.inc fileinfo, // FPC 3.0 and up - winpeimagereader, // winpe exe info - elfreader, // ELF executables - machoreader, // MACH-O executables - {$endif FPCUSEVERSIONINFO} - {$ifdef ISFPC271} - unixcp, - {$endif} - {$endif MSWINDOWS} + {$ifdef DARWIN} + machoreader, // MACH-O executables + {$else} + elfreader, // ELF executables + {$endif DARWIN} + {$endif FPCUSEVERSIONINFO} + {$ifdef ISFPC271} + unixcp, // for GetSystemCodePage + {$endif} + SynFPCLinux, + {$endif LINUX} SynFPCTypInfo; // small wrapper unit around FPC's TypInfo.pp {$endif FPC} @@ -17765,7 +16828,6 @@ function _LStrLenP(s: pointer): SizeInt; inline; begin // here caller ensured s<>'' result := PSizeInt(PAnsiChar(s)-SizeOf(SizeInt))^; end; - {$endif FPC} @@ -17829,7 +16891,7 @@ function TSynAnsiConvert.AnsiBufferToUnicode(Dest: PWideChar; widestringmanager.Ansi2UnicodeMoveProc(Source, {$ifdef ISFPC27}fCodePage,{$endif}tmp,SourceChars); MoveFast(Pointer(tmp)^,Dest^,length(tmp)*2); - result := Dest+SourceChars; + result := Dest+length(tmp); {$else} {$ifdef KYLIX3} result := Dest; // makes compiler happy @@ -17894,7 +16956,7 @@ function TSynAnsiConvert.AnsiBufferToUTF8(Dest: PUTF8Char; end; // UTF-8 is AT MOST 50% bigger than UTF-16 in bytes in range U+0800..U+FFFF -// see http://stackoverflow.com/a/7008095/458259 -> WideCharCount*3 below +// see http://stackoverflow.com/a/7008095 -> WideCharCount*3 below procedure TSynAnsiConvert.InternalAppendUTF8(Source: PAnsiChar; SourceChars: Cardinal; DestTextWriter: TObject; Escape: TTextWriterKind); @@ -18131,7 +17193,7 @@ function TSynAnsiConvert.Utf8ToAnsiBuffer(const S: RawUTF8; result := UTF8BufferToAnsi(tmp,pointer(s),result)-tmp; if result>=DestSize then result := DestSize-1; - MoveFast(tmp,Dest^,result); + {$ifdef FPC}Move{$else}MoveFast{$endif}(tmp,Dest^,result); end; Dest[result] := #0; end; @@ -18298,7 +17360,7 @@ function TSynAnsiFixedWidth.AnsiToRawUnicode(Source: PAnsiChar; SourceChars: Car 353, 8250, 339, 157, 382, 376); constructor TSynAnsiFixedWidth.Create(aCodePage: cardinal); -var i: integer; +var i: PtrInt; A256: array[0..256] of AnsiChar; U256: array[0..256] of WideChar; // AnsiBufferToUnicode() write a last #0 begin @@ -18337,7 +17399,7 @@ constructor TSynAnsiFixedWidth.Create(aCodePage: cardinal); end; function TSynAnsiFixedWidth.IsValidAnsi(WideText: PWideChar; Length: integer): boolean; -var i: integer; +var i: PtrInt; wc: cardinal; begin result := false; @@ -18545,7 +17607,7 @@ function TSynAnsiUTF8.AnsiBufferToUnicode(Dest: PWideChar; function TSynAnsiUTF8.AnsiBufferToUTF8(Dest: PUTF8Char; Source: PAnsiChar; SourceChars: Cardinal; NoTrailingZero: boolean): PUTF8Char; begin - MoveFast(Source^,Dest^,SourceChars); + {$ifdef FPC}Move{$else}MoveFast{$endif}(Source^,Dest^,SourceChars); if not NoTrailingZero then Dest[SourceChars] := #0; result := Dest+SourceChars; @@ -18599,7 +17661,7 @@ function TSynAnsiUTF8.UnicodeBufferToAnsi(Source: PWideChar; function TSynAnsiUTF8.UTF8BufferToAnsi(Dest: PAnsiChar; Source: PUTF8Char; SourceChars: Cardinal): PAnsiChar; begin - MoveFast(Source^,Dest^,SourceChars); + {$ifdef FPC}Move{$else}MoveFast{$endif}(Source^,Dest^,SourceChars); result := Dest+SourceChars; end; @@ -18636,7 +17698,7 @@ function TSynAnsiUTF8.AnsiBufferToRawUTF8(Source: PAnsiChar; SourceChars: Cardin function TSynAnsiUTF16.AnsiBufferToUnicode(Dest: PWideChar; Source: PAnsiChar; SourceChars: Cardinal; NoTrailingZero: boolean): PWideChar; begin - MoveFast(Source^,Dest^,SourceChars); + {$ifdef FPC}Move{$else}MoveFast{$endif}(Source^,Dest^,SourceChars); result := Pointer(PtrUInt(Dest)+SourceChars); if not NoTrailingZero then result^ := #0; @@ -18670,7 +17732,7 @@ function TSynAnsiUTF16.UnicodeBufferToAnsi(Dest: PAnsiChar; Source: PWideChar; SourceChars: Cardinal): PAnsiChar; begin SourceChars := SourceChars shl 1; // from WideChar count to byte count - MoveFast(Source^,Dest^,SourceChars); + {$ifdef FPC}Move{$else}MoveFast{$endif}(Source^,Dest^,SourceChars); result := Dest+SourceChars; end; @@ -18685,76 +17747,63 @@ function TSynAnsiUTF16.UTF8BufferToAnsi(Dest: PAnsiChar; Source: PUTF8Char; procedure TSynTempBuffer.Init(const Source: RawByteString); begin - len := length(Source); - if len=0 then - buf := nil else begin - if lennil then begin + {$ifdef FPC}Move{$else}MoveFast{$endif}(Source^,buf^,len); + PPtrInt(PAnsiChar(buf)+len)^ := 0; // init last 4/8 bytes (makes valgrid happy) + end; + end; end; function TSynTempBuffer.Init: integer; begin buf := @tmp; - result := SizeOf(tmp)-1; + result := SizeOf(tmp)-16; len := result; end; -function TSynTempBuffer.InitRandom(RandomLen: integer): pointer; +function TSynTempBuffer.InitRandom(RandomLen: integer; forcegsl: boolean): pointer; begin - result := Init(RandomLen+3); + Init(nil,RandomLen); if RandomLen>0 then - FillRandom(result,(RandomLen shr 2)+1); + FillRandom(buf,(RandomLen shr 2)+1,forcegsl); + result := buf; end; function TSynTempBuffer.InitIncreasing(Count, Start: integer): PIntegerArray; begin - result := Init((Count-Start)*4); - FillIncreasing(result,Start,Count); + Init(nil,(Count-Start)*4); + FillIncreasing(buf,Start,Count); + result := buf; end; function TSynTempBuffer.InitZero(ZeroLen: integer): pointer; begin - result := Init(ZeroLen); - FillCharFast(result^,ZeroLen,0); + Init(nil,ZeroLen-16); + {$ifdef FPC}FillChar{$else}FillCharFast{$endif}(buf^,ZeroLen,0); + result := buf; end; procedure TSynTempBuffer.Done; @@ -18778,7 +17827,7 @@ procedure TSynTempBuffer.Done(EndBuf: pointer; var Dest: RawUTF8); procedure TSynTempWriter.Init(maxsize: integer); begin if maxsize<=0 then - maxsize := SizeOf(tmp.tmp)-1; // -1 for trailing #0 + maxsize := SizeOf(tmp.tmp)-16; // TSynTempBuffer allocates +16 pos := tmp.Init(maxsize); end; @@ -18801,7 +17850,7 @@ procedure TSynTempWriter.wr(const val; len: integer); begin if pos-tmp.buf+len>tmp.len then raise ESynException.CreateUTF8('TSynTempWriter(%) overflow',[tmp.len]); - MoveFast(val,pos^,len); + {$ifdef FPC}Move{$else}MoveFast{$endif}(val,pos^,len); inc(pos,len); end; @@ -18839,7 +17888,7 @@ function TSynTempWriter.wrfillchar(count: integer; value: byte): PAnsiChar; begin if pos-tmp.buf+count>tmp.len then raise ESynException.CreateUTF8('TSynTempWriter(%) overflow',[tmp.len]); - FillCharFast(pos^,count,value); + {$ifdef FPC}FillChar{$else}FillCharFast{$endif}(pos^,count,value); result := pos; inc(pos,count); end; @@ -18934,7 +17983,7 @@ function TRawUTF8InterningSlot.Clean(aMaxRefCount: integer): integer; {$else} if PInteger(s^-8)^<=aMaxRefCount then begin PRawUTF8(s)^ := ''; - {$endif} + {$endif FPC} inc(result); end else begin if s<>d then begin @@ -19325,12 +18374,12 @@ function UTF8ToWinPChar(dest: PAnsiChar; source: PUTF8Char; count: integer): int function ShortStringToAnsi7String(const source: shortstring): RawByteString; begin - SetString(result,PAnsiChar(@source[1]),ord(source[0])); + FastSetString(RawUTF8(result),@source[1],ord(source[0])); end; procedure ShortStringToAnsi7String(const source: shortstring; var result: RawUTF8); begin - SetString(result,PAnsiChar(@source[1]),ord(source[0])); + FastSetString(result,@source[1],ord(source[0])); end; procedure UTF8ToShortString(var dest: shortstring; source: PUTF8Char); @@ -19547,6 +18596,34 @@ function IsValidUTF8(source: PUTF8Char): Boolean; result := true; end; +function IsValidUTF8(const source: RawUTF8): Boolean; +begin + result := IsValidUTF8(pointer(Source),length(Source)); +end; + +function IsValidUTF8(source: PUTF8Char; sourcelen: PtrInt): Boolean; +var extra, i: integer; + c: cardinal; +begin + result := false; + inc(sourcelen,PtrInt(source)); + if source<>nil then + while PtrInt(source)0 then begin + extra := UTF8_EXTRABYTES[c]; + if extra=0 then exit else // invalid leading byte + for i := 1 to extra do + if (PtrInt(source)>=sourcelen) or (byte(source^) and $c0<>$80) then + exit else + inc(source); // check valid UTF-8 content + end; + end; + result := true; +end; + function IsValidUTF8WithoutControlChars(source: PUTF8Char): Boolean; var extra, i: integer; c: cardinal; @@ -20094,14 +19171,14 @@ function ToUTF8({$IFDEF FPC_HAS_CONSTREF}constref{$ELSE}const{$ENDIF} guid: TGUI GUIDToText(pointer(result),@guid); end; -procedure Int32ToUTF8(Value: integer; var result: RawUTF8); -var tmp: array[0..15] of AnsiChar; +procedure Int32ToUTF8(Value: PtrInt; var result: RawUTF8); +var tmp: array[0..23] of AnsiChar; P: PAnsiChar; begin - if cardinal(Value)<=high(SmallUInt32UTF8) then + if PtrUInt(Value)<=high(SmallUInt32UTF8) then result := SmallUInt32UTF8[Value] else begin - P := StrInt32(@tmp[15],Value); - FastSetString(result,P,@tmp[15]-P); + P := StrInt32(@tmp[23],Value); + FastSetString(result,P,@tmp[23]-P); end; end; @@ -20109,9 +19186,12 @@ procedure Int64ToUtf8(Value: Int64; var result: RawUTF8); var tmp: array[0..23] of AnsiChar; P: PAnsiChar; begin + {$ifdef CPU64} + if PtrUInt(Value)<=high(SmallUInt32UTF8) then + {$else} // Int64Rec gives compiler internal error C4963 if (PCardinalArray(@Value)^[0]<=high(SmallUInt32UTF8)) and (PCardinalArray(@Value)^[1]=0) then - // Int64Rec gives compiler internal error C4963 + {$endif CPU64} result := SmallUInt32UTF8[Value] else begin P := StrInt64(@tmp[23],Value); FastSetString(result,P,@tmp[23]-P); @@ -20122,8 +19202,12 @@ procedure UInt64ToUtf8(Value: QWord; var result: RawUTF8); var tmp: array[0..23] of AnsiChar; P: PAnsiChar; begin + {$ifdef CPU64} + if Value<=high(SmallUInt32UTF8) then + {$else} // Int64Rec gives compiler internal error C4963 if (PCardinalArray(@Value)^[0]<=high(SmallUInt32UTF8)) and (PCardinalArray(@Value)^[1]=0) then + {$endif CPU64} result := SmallUInt32UTF8[Value] else begin P := StrUInt64(@tmp[23],Value); FastSetString(result,P,@tmp[23]-P); @@ -20212,14 +19296,11 @@ function VarRecToTempUTF8(const V: TVarRec; var Res: TTempUTF8): integer; exit; end; vtChar: begin - {$ifdef FPC} // alf: to circumvent FPC issue - RawUnicodeToUtf8(@V.VChar,1,RawUTF8(Res.TempRawUTF8)); - {$else} - Res.Text := @V.VChar; + Res.Temp[0] := V.VChar; // V may be on transient stack (alf: FPC) + Res.Text := @Res.Temp; Res.Len := 1; result := 1; exit; - {$endif} end; vtPWideChar: RawUnicodeToUtf8(V.VPWideChar,StrLenW(V.VPWideChar),RawUTF8(Res.TempRawUTF8)); @@ -20279,7 +19360,7 @@ function VarRecToTempUTF8(const V: TVarRec; var Res: TTempUTF8): integer; vtPointer,vtInterface: begin Res.Text := @Res.Temp; Res.Len := SizeOf(pointer)*2; - BinToHexDisplay(V.VPointer,@Res.Temp,SizeOf(Pointer)); + BinToHexDisplayLower(V.VPointer,@Res.Temp,SizeOf(Pointer)); result := SizeOf(pointer)*2; exit; end; @@ -20303,12 +19384,17 @@ function VarRecToTempUTF8(const V: TVarRec; var Res: TTempUTF8): integer; end; {$ifndef NOVARIANTS} vtVariant: - if VariantToInt64(V.VVariant^,v64) then begin - Res.Text := PUTF8Char(StrInt64(@Res.Temp[23],v64)); - Res.Len := @Res.Temp[23]-Res.Text; - result := Res.Len; - exit; - end else + if VariantToInt64(V.VVariant^,v64) then + if (PCardinalArray(@v64)^[0]<=high(SmallUInt32UTF8)) and + (PCardinalArray(@v64)^[1]=0) then begin + result := v64; + goto smlu32; + end else begin + Res.Text := PUTF8Char(StrInt64(@Res.Temp[23],v64)); + Res.Len := @Res.Temp[23]-Res.Text; + result := Res.Len; + exit; + end else VariantToUTF8(V.VVariant^,RawUTF8(Res.TempRawUTF8),isString); {$endif} else begin @@ -20585,17 +19671,17 @@ procedure UTF8ToSynUnicode(Text: PUTF8Char; Len: integer; var result: SynUnicode end; function StrInt32(P: PAnsiChar; val: PtrInt): PAnsiChar; -{$ifdef CPU64} -{$ifdef FPC} -begin // fallback to pure pascal version, since asm version below make GPFs for FPC +{$ifdef ABSOLUTEPASCALORNOTINTEL} +begin // fallback to pure pascal version for ARM or PIC if val<0 then begin result := StrUInt32(P,PtrUInt(-val))-1; result^ := '-'; end else result := StrUInt32(P,val); end; -{$else} {$ifdef FPC}nostackframe; assembler; asm {$else} -asm // rcx=P, rdx=val (Linux: rdi,rsi) +{$else} +{$ifdef CPUX64} {$ifdef FPC}nostackframe; assembler; asm {$else} +asm // rcx=P, rdx=val (Linux: rdi,rsi) - val is QWord on 64-bit Intel CPU .noframe {$endif FPC} {$ifndef win64} @@ -20640,16 +19726,6 @@ function StrInt32(P: PAnsiChar; val: PtrInt): PAnsiChar; mov [rcx - 1], dl lea rax, [rcx + r10 - 1] // includes '-' if val<0 end; -{$endif FPC} -{$else} -{$ifdef PUREPASCAL} -begin // this code is faster than the Borland's original str() or IntToStr() - if val<0 then begin - result := StrUInt32(P,PtrUInt(-val))-1; - result^ := '-'; - end else - result := StrUInt32(P,val); -end; {$else} asm // eax=P, edx=val mov ecx, edx @@ -20699,12 +19775,39 @@ function StrInt32(P: PAnsiChar; val: PtrInt): PAnsiChar; mov [eax], dl add eax, ecx // includes '-' if val<0 end; -{$endif CPU64} -{$endif PUREPASCAL} +{$endif CPUX64} +{$endif ABSOLUTEPASCALORNOTINTEL} function StrUInt32(P: PAnsiChar; val: PtrUInt): PAnsiChar; +{$ifdef ABSOLUTEPASCALORNOTINTEL} // fallback to pure pascal version for ARM or PIC +var c100: PtrUInt; // val/c100 are QWord on 64-bit CPU + tab: PWordArray; +begin // this code is faster than Borland's original str() or IntToStr() + tab := @TwoDigitLookupW; + repeat + if val<10 then begin + dec(P); + P^ := AnsiChar(val+ord('0')); + break; + end else + if val<100 then begin + dec(P,2); + PWord(P)^ := tab[val]; + break; + end; + dec(P,2); + c100 := val div 100; + dec(val,c100*100); + PWord(P)^ := tab[val]; + val := c100; + if c100=0 then + break; + until false; + result := P; +end; +{$else} {$ifdef CPUX64} {$ifdef FPC}nostackframe; assembler; asm {$else} -asm // rcx=P, rdx=val (Linux: rdi,rsi) +asm // rcx=P, rdx=val (Linux: rdi,rsi) - val is QWord on Intel 64-bit CPU .noframe {$endif FPC} {$ifndef win64} @@ -20744,32 +19847,6 @@ function StrUInt32(P: PAnsiChar; val: PtrUInt): PAnsiChar; mov [rax], dl end; {$else} -{$ifdef PUREPASCAL} -var c100: PtrUInt; - tab: PWordArray; -begin // this code is faster than the Borland's original str() or IntToStr() - tab := @TwoDigitLookupW; - repeat - if val<10 then begin - dec(P); - P^ := AnsiChar(val+ord('0')); - break; - end else - if val<100 then begin - dec(P,2); - PWord(P)^ := tab[val]; - break; - end; - dec(P,2); - c100 := val div 100; - dec(val,c100*100); - PWord(P)^ := tab[val]; - val := c100; - if c100=0 then break; - until false; - result := P; -end; -{$else} asm // eax=P, edx=val cmp edx, 10 jb @3 // direct process of common val=0 (or val<10) @@ -20807,20 +19884,20 @@ function StrUInt32(P: PAnsiChar; val: PtrUInt): PAnsiChar; mov [eax], dl end; {$endif CPU64} -{$endif PUREPASCAL} +{$endif ABSOLUTEPASCALORNOTINTEL} function StrUInt64(P: PAnsiChar; const val: QWord): PAnsiChar; {$ifdef CPU64} -begin // StrUInt32 aldready implemented PtrUInt=UInt64 - result := StrUInt32(P,val); +begin + result := StrUInt32(P,val); // StrUInt32 converts PtrUInt=QWord on 64-bit CPU end; {$else} var c,c100: QWord; - tab: {$ifdef CPUX86}TWordArray absolute TwoDigitLookupW{$else}PWordArray{$endif}; + tab: {$ifdef CPUX86NOTPIC}TWordArray absolute TwoDigitLookupW{$else}PWordArray{$endif}; begin - if Int64Rec(val).Hi=0 then - P := StrUInt32(P,Int64Rec(val).Lo) else begin - {$ifndef CPUX86}tab := @TwoDigitLookupW;{$endif} + if PInt64Rec(@val)^.Hi=0 then + P := StrUInt32(P,PCardinal(@val)^) else begin + {$ifndef CPUX86NOTPIC}tab := @TwoDigitLookupW;{$endif} c := val; repeat {$ifdef PUREPASCAL} @@ -20852,9 +19929,9 @@ function StrUInt64(P: PAnsiChar; const val: QWord): PAnsiChar; dec(P,2); PWord(P)^ := tab[c]; c := c100; - if Int64Rec(c).Hi=0 then begin - if Int64Rec(c).Lo<>0 then - P := StrUInt32(P,Int64Rec(c).Lo); + if PInt64Rec(@c)^.Hi=0 then begin + if PCardinal(@c)^<>0 then + P := StrUInt32(P,PCardinal(@c)^); break; end; until false; @@ -20865,23 +19942,25 @@ function StrUInt64(P: PAnsiChar; const val: QWord): PAnsiChar; function StrInt64(P: PAnsiChar; const val: Int64): PAnsiChar; begin + {$ifdef CPU64} + result := StrInt32(P,val); // StrInt32 converts PtrInt=Int64 on 64-bit CPU + {$else} if val<0 then begin P := StrUInt64(P,-val)-1; P^ := '-'; end else P := StrUInt64(P,val); result := P; + {$endif CPU64} end; -function IPToCardinal(const aIP: RawUTF8; out aValue: cardinal): boolean; -var P: PUTF8Char; - i,c: cardinal; +function IPToCardinal(P: PUTF8Char; out aValue: cardinal): boolean; +var i,c: cardinal; b: array[0..3] of byte absolute aValue; begin result := false; - if (aIP='') or (aIP='127.0.0.1') then + if (P=nil) or (IdemPChar(P,'127.0.0.1') and (P[9]=#0)) then exit; - P := pointer(aIP); for i := 0 to 3 do begin c := GetNextItemCardinal(P,'.'); if (c>255) or ((i<3) and (P=nil)) then @@ -20891,6 +19970,17 @@ function IPToCardinal(const aIP: RawUTF8; out aValue: cardinal): boolean; result := aValue<>$0100007f; end; +function IPToCardinal(const aIP: RawUTF8; out aValue: cardinal): boolean; +begin + result := IPToCardinal(pointer(aIP),aValue); +end; + +function IPToCardinal(const aIP: RawUTF8): cardinal; +begin + if not IPToCardinal(pointer(aIP),result) then + result := 0; +end; + const // see https://en.wikipedia.org/wiki/Baudot_code Baudot2Char: array[0..63] of AnsiChar = @@ -21009,7 +20099,7 @@ function TrimControlChars(const text: RawUTF8; const controls: TSynAnsicharSet): n := i-1; FastSetString(result,nil,len); P := pointer(result); - MoveFast(pointer(text)^,P^,n); + {$ifdef FPC}Move{$else}MoveFast{$endif}(pointer(text)^,P^,n); for j := i+1 to len do if not(text[j] in controls) then begin P[n] := text[j]; @@ -21052,7 +20142,7 @@ procedure Exchg16(P1,P2: PIntegerArray); {$endif} procedure Exchg(P1,P2: PAnsiChar; count: PtrInt); -{$ifdef PUREPASCAL} {$ifdef HASINLINE}inline;{$endif} + {$ifdef PUREPASCAL} {$ifdef HASINLINE}inline;{$endif} var i, c: PtrInt; u: AnsiChar; begin @@ -21100,6 +20190,93 @@ procedure Exchg(P1,P2: PAnsiChar; count: PtrInt); end; {$endif} +function GetAllBits(Bits, BitCount: Cardinal): boolean; +begin + if BitCount in [low(ALLBITS_CARDINAL)..high(ALLBITS_CARDINAL)] then begin + BitCount := ALLBITS_CARDINAL[BitCount]; + result := (Bits and BitCount)=BitCount; + end else + result := false; +end; + +// naive code gives the best performance - bts [Bits] has an overhead +function GetBit(const Bits; aIndex: PtrInt): boolean; +begin + result := TIntegerArray(Bits)[aIndex shr 5] and (1 shl (aIndex and 31)) <> 0; +end; + +procedure SetBit(var Bits; aIndex: PtrInt); +begin + TIntegerArray(Bits)[aIndex shr 5] := TIntegerArray(Bits)[aIndex shr 5] + or (1 shl (aIndex and 31)); +end; + +procedure UnSetBit(var Bits; aIndex: PtrInt); +begin + PIntegerArray(@Bits)^[aIndex shr 5] := PIntegerArray(@Bits)^[aIndex shr 5] + and not (1 shl (aIndex and 31)); +end; + +function GetBitPtr(Bits: pointer; aIndex: PtrInt): boolean; +begin + result := PIntegerArray(Bits)[aIndex shr 5] and (1 shl (aIndex and 31)) <> 0; +end; + +procedure SetBitPtr(Bits: pointer; aIndex: PtrInt); +begin + PIntegerArray(Bits)[aIndex shr 5] := PIntegerArray(Bits)[aIndex shr 5] + or (1 shl (aIndex and 31)); +end; + +procedure UnSetBitPtr(Bits: pointer; aIndex: PtrInt); +begin + PIntegerArray(Bits)^[aIndex shr 5] := PIntegerArray(Bits)^[aIndex shr 5] + and not (1 shl (aIndex and 31)); +end; + +function GetBit64(const Bits: Int64; aIndex: PtrInt): boolean; +begin + result := aIndex in TBits64(Bits); +end; + +procedure SetBit64(var Bits: Int64; aIndex: PtrInt); +begin + include(PBits64(@Bits)^,aIndex); +end; + +procedure UnSetBit64(var Bits: Int64; aIndex: PtrInt); +begin + exclude(PBits64(@Bits)^,aIndex); +end; + +function GetBitsCount(const Bits; Count: PtrInt): integer; +const POPCNTDATA: array[0..15+4] of integer = (0,1,1,2,1,2,2,3,1,2,2,3,2,3,3,4,0,1,3,7); +var P: PByte; + v: PtrUInt; + tab: {$ifdef CPUX86NOTPIC}TIntegerArray absolute POPCNTDATA{$else}PIntegerArray{$endif}; +begin + {$ifndef CPUX86NOTPIC} + tab := @POPCNTDATA; + {$endif CPUX86NOTPIC} + P := @Bits; + result := 0; + while Count>=8 do begin + dec(Count,8); + v := P^; + inc(result,tab[v and $f]); + inc(result,tab[v shr 4]); + inc(P); + end; + v := P^; + if Count>=4 then begin + dec(Count,4); + inc(result,tab[v and $f]); + v := v shr 4; + end; + if Count>0 then + inc(result,tab[v and tab[Count+16]]); +end; + {$ifdef FPC} type @@ -21120,7 +20297,7 @@ procedure Exchg(P1,P2: PAnsiChar; count: PtrInt); tkObject,tkRecord,tkDynArray,tkInterface,tkVariant]; // maps record or object types tkRecordTypes = [tkObject,tkRecord]; - tkRecordTypeOrSet = [tkObject,tkRecord]; + tkRecordKinds = [tkObject,tkRecord]; type // as defined in Delphi 6 and up @@ -21156,7 +20333,7 @@ procedure Exchg(P1,P2: PAnsiChar; count: PtrInt); const // maps record or object types tkRecordTypes = [tkRecord]; - tkRecordTypeOrSet = tkRecord; + tkRecordKinds = tkRecord; {$endif} @@ -21181,7 +20358,20 @@ TStrRec = record refCnt: SizeInt; length: SizeInt; {$else FPC} - TStrRec = packed record + /// map the Delphi/FPC dynamic array header (stored before each instance) + TDynArrayRec = packed record + /// dynamic array reference count (basic garbage memory mechanism) + {$ifdef CPUX64} + _Padding: LongInt; // Delphi/FPC XE2+ expects 16 byte alignment + {$endif} + refCnt: Longint; + /// length in element count + // - size in bytes = length*ElemSize + length: PtrInt; + end; + PDynArrayRec = ^TDynArrayRec; + + TStrRec = packed record {$ifdef UNICODE} {$ifdef CPU64} /// padding bytes for 16 byte alignment of the header @@ -21206,27 +20396,6 @@ TStrRec = record {$endif FPC} end; - /// map the Delphi/FPC dynamic array header (stored before each instance) - TDynArrayRec = packed record - /// dynamic array reference count (basic garbage memory mechanism) - {$ifdef FPC} - refCnt: PtrInt; - high: tdynarrayindex; - function GetLength: sizeint; inline; - procedure SetLength(len: sizeint); inline; - property length: sizeint read GetLength write SetLength; - {$else} - {$ifdef CPUX64} - _Padding: LongInt; // Delphi/FPC XE2+ expects 16 byte alignment - {$endif} - refCnt: Longint; - /// length in element count - // - size in bytes = length*ElemSize - length: PtrInt; - {$endif} - end; - PDynArrayRec = ^TDynArrayRec; - {$ifdef FPC} {$PACKRECORDS C} {$endif FPC} @@ -21235,10 +20404,10 @@ TStrRec = record {$ifdef HASDIRECTTYPEINFO} PTypeInfoStored = PTypeInfo; {$else} - PTypeInfoStored = ^PTypeInfo; + PTypeInfoStored = ^PTypeInfo; // = TypeInfoPtr macro in FPC typinfo.pp {$endif} - // note: FPC TRecInitData (and TInitManagedField) are taken from typinfo.pp + // note: FPC TRecInitData is taken from typinfo.pp via SynFPCTypInfo // since this information is evolving/breaking a lot in the current FPC trunk /// map the Delphi/FPC record field RTTI @@ -21259,7 +20428,7 @@ TStrRec = record /// map the Delphi record field enhanced RTTI (available since Delphi 2010) TEnhancedFieldInfo = packed record TypeInfo: PTypeInfoStored; - Offset: PtrUInt; + Offset: PtrUInt; // match TInitManagedField/TManagedField in FPC typinfo.pp {$ifdef ISDELPHI2010} Flags: Byte; NameLen: byte; // = Name[0] = length(Name) @@ -21359,12 +20528,15 @@ TStrRec = record EnumBaseType: PTypeInfoStored; {$ifdef FPC_ENUMHASINNER} end; - {$endif} + {$endif FPC_ENUMHASINNER} NameList: string[255]; ); tkInteger: ( IntegerType: TOrdType; ); + tkInt64: ( + MinInt64Value, MaxInt64Value: Int64; + ); tkSet: ( SetType: TOrdType; {$ifdef FPC} @@ -21435,7 +20607,8 @@ procedure FastSetStringCP(var s; p: pointer; len, codepage: PtrInt); var r: PAnsiChar; // s may = p -> stand-alone variable sr: PStrRec; // local copy of r, to use register begin - if len>0 then begin + if len<=0 then + r := nil else begin GetMem(r,len+(STRRECSIZE+2)); sr := pointer(r); sr^.codePage := codepage; @@ -21446,35 +20619,32 @@ procedure FastSetStringCP(var s; p: pointer; len, codepage: PtrInt); PWord(PAnsiChar(sr)+len)^ := 0; // ensure ends with two #0 r := pointer(sr); if p<>nil then - MoveFast(p^,sr^,len); - {$ifdef FPC}Finalize(RawByteString(s)){$else}RawByteString(s) := ''{$endif}; - pointer(s) := r; - end - else - {$ifdef FPC}Finalize(RawByteString(s)){$else}RawByteString(s) := ''{$endif}; + {$ifdef FPC}Move{$else}MoveFast{$endif}(p^,sr^,len); + end; + {$ifdef FPC}Finalize(RawByteString(s)){$else}RawByteString(s) := ''{$endif}; + pointer(s) := r; end; procedure FastSetString(var s: RawUTF8; p: pointer; len: PtrInt); var r: PAnsiChar; sr: PStrRec; begin - if len>0 then begin - GetMem(r,len+(STRRECSIZE+2)); + if len<=0 then + r := nil else begin + GetMem(r,len+(STRRECSIZE+4)); sr := pointer(r); sr^.codePage := CP_UTF8; sr^.elemSize := 1; sr^.refCnt := 1; sr^.length := len; inc(sr); - PWord(PAnsiChar(sr)+len)^ := 0; + PCardinal(PAnsiChar(sr)+len)^ := 0; // ends with four #0 r := pointer(sr); if p<>nil then - MoveFast(p^,sr^,len); - {$ifdef FPC}Finalize(s){$else}s := ''{$endif}; - pointer(s) := r; - end - else - {$ifdef FPC}Finalize(s){$else}s := ''{$endif}; + {$ifdef FPC}Move{$else}MoveFast{$endif}(p^,sr^,len); + end; + {$ifdef FPC}Finalize(s){$else}s := ''{$endif}; + pointer(s) := r; end; {$else} procedure FastSetStringCP(var s; p: pointer; len, codepage: PtrInt); @@ -21485,7 +20655,17 @@ procedure FastSetString(var s: RawUTF8; p: pointer; len: PtrInt); begin SetString(RawByteString(s),PAnsiChar(p),len); end; -{$endif} +{$endif HASCODEPAGE} + +procedure GetMemAligned(var s: RawByteString; p: pointer; len: PtrInt; + out aligned: pointer); +begin + SetString(s,nil,len+16); + aligned := pointer(s); + inc(PtrUInt(aligned),PtrUInt(aligned) and 15); + if p<>nil then + {$ifdef FPC}Move{$else}MoveFast{$endif}(p^,aligned^,len); +end; function ToText(k: TTypeKind): PShortString; overload; begin @@ -21665,7 +20845,7 @@ function TypeInfoToName(aTypeInfo: pointer): RawUTF8; function RecordTypeInfoSize(aRecordTypeInfo: pointer): integer; var info: PTypeInfo; begin - info := GetTypeInfo(aRecordTypeInfo,tkRecordTypeOrSet); + info := GetTypeInfo(aRecordTypeInfo,tkRecordKinds); if info=nil then result := 0 else result := info^.recSize; @@ -21767,7 +20947,7 @@ procedure GetCaptionFromTrimmed(PS: PShortString; var result: string); inc(PByte(PS)); while (L>0) and (PS^[0] in ['a'..'z']) do begin inc(PByte(PS)); dec(L); end; tmp[L] := #0; // as expected by GetCaptionFromPCharLen/UnCamelCase - MoveFast(PS^,tmp,L); + {$ifdef FPC}Move{$else}MoveFast{$endif}(PS^,tmp,L); GetCaptionFromPCharLen(tmp,result); end; @@ -21796,7 +20976,7 @@ function GetEnumName(aTypeInfo: pointer; aIndex: integer): PShortString; dec(aIndex); if aIndex=0 then break; - inc(PByte(result),ord(result^[0])+1); + inc(PByte(result),ord(result^[0])+1); // loop unrolled twice dec(aIndex); if aIndex=0 then break; @@ -21847,23 +21027,40 @@ function GetEnumName(aTypeInfo: pointer; aIndex: integer): PShortString; end; {$endif} +{$ifdef PUREPASCAL} // for proper inlining +function IdemPropNameUSameLen(P1,P2: PUTF8Char; P1P2Len: PtrInt): boolean; +var i,j: PtrInt; +begin + result := false; + j := 0; + for i := 1 to P1P2Len shr 2 do + if (PCardinalArray(P1)[j] xor PCardinalArray(P2)[j]) and $dfdfdfdf<>0 then + exit else + inc(j); + for i := j*4 to P1P2Len-1 do + if (ord(P1[i]) xor ord(P2[i])) and $df<>0 then + exit; + result := true; +end; +{$endif PUREPASCAL} + function FindShortStringListExact(List: PShortString; MaxValue: integer; - aValue: PUTF8Char; aValueLen: integer): integer; -var PLen: integer; + aValue: PUTF8Char; aValueLen: PtrInt): integer; +var PLen: PtrInt; begin if aValueLen<>0 then for result := 0 to MaxValue do begin PLen := ord(List^[0]); if (PLen=aValuelen) and IdemPropNameUSameLen(@List^[1],aValue,aValueLen) then - exit else - inc(PByte(List),PLen+1); // next short string + exit; + inc(PByte(List),PLen+1); // next short string end; result := -1; end; function FindShortStringListTrimLowerCase(List: PShortString; MaxValue: integer; - aValue: PUTF8Char; aValueLen: integer): integer; -var PLen: integer; + aValue: PUTF8Char; aValueLen: PtrInt): integer; +var PLen: PtrInt; begin if aValueLen<>0 then for result := 0 to MaxValue do begin @@ -21876,8 +21073,8 @@ function FindShortStringListTrimLowerCase(List: PShortString; MaxValue: integer; dec(PLen); until PLen=0; if (PLen=aValueLen) and IdemPropNameUSameLen(aValue,PUTF8Char(List),PLen) then - exit else - inc(PUTF8Char(List),PLen); + exit; + inc(PUTF8Char(List),PLen); end; result := -1; end; @@ -21920,7 +21117,7 @@ function GetSetName(aTypeInfo: pointer; const value): RawUTF8; result := ''; if GetSetInfo(aTypeInfo,max,PS) then begin for i := 0 to max do begin - if GetBit(value,i) then + if GetBitPtr(@value,i) then result := FormatUTF8('%%,',[result,PS^]); inc(PByte(PS),ord(PS^[0])+1); // next short string end; @@ -21941,7 +21138,7 @@ procedure AppendShortComma(text: PAnsiChar; len: integer; var result: shortstrin end; if integer(ord(result[0]))+len>=255 then exit; - MoveFast(text^,result[ord(result[0])+1],len); + {$ifdef FPC}Move{$else}MoveFast{$endif}(text^,result[ord(result[0])+1],len); inc(result[0],len+1); result[ord(result[0])] := ','; end; @@ -21954,7 +21151,7 @@ procedure GetSetNameShort(aTypeInfo: pointer; const value; out result: ShortStri result := ''; if GetSetInfo(aTypeInfo,max,PS) then begin for i := 0 to max do begin - if GetBit(value,i) then + if GetBitPtr(@value,i) then AppendShortComma(@PS^[1],ord(PS^[0]),result,trimlowercase); inc(PByte(PS),ord(PS^[0])+1); // next short string end; @@ -21995,7 +21192,7 @@ function GetSetNameValue(aTypeInfo: pointer; var P: PUTF8Char; if i<0 then i := FindShortStringListTrimLowerCase(names,MaxValue,Text,TextLen); if i>=0 then - SetBit(result,i); + SetBitPtr(@result,i); // unknown enum names (i=-1) would just be ignored until EndOfObject=']'; if P=nil then @@ -22032,7 +21229,7 @@ function VariantToInteger(const V: Variant; var Value: integer): boolean; varShortInt: Value := VShortInt; varWord: Value := VWord; varLongWord: - if (VLongWord>=cardinal(Low(integer))) and (VLongWord<=cardinal(High(integer))) then + if VLongWord<=cardinal(High(integer)) then Value := VLongWord else begin result := false; exit; @@ -22170,7 +21367,11 @@ function VariantToInt64(const V: Variant; var Value: Int64): boolean; {$endif} varByte: Value := VByte; varInteger: Value := VInteger; - varWord64, + varWord64: if VInt64>=0 then + Value := VInt64 else begin + result := false; + exit; + end; varInt64: Value := VInt64; else if SetVariantUnRefSimpleValue(V,tmp) then begin @@ -22278,9 +21479,12 @@ procedure VariantToUTF8(const V: Variant; var result: RawUTF8; varLongWord: UInt32ToUTF8(VLongWord,result); {$endif} - varByte, - varBoolean: + varByte: result := SmallUInt32UTF8[VByte]; + varBoolean: + if VBoolean then + result := SmallUInt32UTF8[1] else + result := SmallUInt32UTF8[0]; varInteger: Int32ToUTF8(VInteger,result); varInt64: @@ -22472,19 +21676,19 @@ function Pos(const substr, str: RawUTF8): Integer; overload; end; function IntToString(Value: integer): string; -var tmp: array[0..15] of AnsiChar; +var tmp: array[0..23] of AnsiChar; P: PAnsiChar; begin - P := StrInt32(@tmp[15],Value); - Ansi7ToString(PWinAnsiChar(P),@tmp[15]-P,result); + P := StrInt32(@tmp[23],Value); + Ansi7ToString(PWinAnsiChar(P),@tmp[23]-P,result); end; function IntToString(Value: cardinal): string; -var tmp: array[0..15] of AnsiChar; +var tmp: array[0..23] of AnsiChar; P: PAnsiChar; begin - P := StrUInt32(@tmp[15],Value); - Ansi7ToString(PWinAnsiChar(P),@tmp[15]-P,result); + P := StrUInt32(@tmp[23],Value); + Ansi7ToString(PWinAnsiChar(P),@tmp[23]-P,result); end; function IntToString(Value: Int64): string; @@ -22514,33 +21718,42 @@ function Curr64ToString(Value: Int64): string; {$ifdef PUREPASCAL} function IntToString(Value: integer): string; -var tmp: array[0..15] of AnsiChar; +var tmp: array[0..23] of AnsiChar; P: PAnsiChar; begin - P := StrInt32(@tmp[15],Value); - SetString(result,P,@tmp[15]-P); + if cardinal(Value)<=high(SmallUInt32UTF8) then + result := SmallUInt32UTF8[Value] else begin + P := StrInt32(@tmp[23],Value); + SetString(result,P,@tmp[23]-P); + end; end; {$else} function IntToString(Value: integer): string; asm jmp Int32ToUTF8 end; -{$endif} +{$endif PUREPASCAL} function IntToString(Value: cardinal): string; -var tmp: array[0..15] of AnsiChar; +var tmp: array[0..23] of AnsiChar; P: PAnsiChar; begin - P := StrUInt32(@tmp[15],Value); - SetString(result,P,@tmp[15]-P); + if Value<=high(SmallUInt32UTF8) then + result := SmallUInt32UTF8[Value] else begin + P := StrUInt32(@tmp[23],Value); + SetString(result,P,@tmp[23]-P); + end; end; function IntToString(Value: Int64): string; var tmp: array[0..31] of AnsiChar; P: PAnsiChar; begin - P := StrInt64(@tmp[31],Value); - SetString(result,P,@tmp[31]-P); + if (Value>=0) and (Value<=high(SmallUInt32UTF8)) then + result := SmallUInt32UTF8[Value] else begin + P := StrInt64(@tmp[31],Value); + SetString(result,P,@tmp[31]-P); + end; end; function DoubleToString(Value: Double): string; @@ -22558,8 +21771,8 @@ function Curr64ToString(Value: Int64): string; {$endif UNICODE} -{$ifdef CPUX86} -procedure bswap64array(a,b: PQWordArray; n: integer); +procedure bswap64array(a,b: PQWordArray; n: PtrInt); +{$ifdef CPUX86} {$ifdef FPC}nostackframe; assembler;{$endif} asm push ebx push esi @@ -22578,11 +21791,10 @@ procedure bswap64array(a,b: PQWordArray; n: integer); end; {$else} {$ifdef CPUX64} -procedure bswap64array(a,b: PQWordArray; n: integer); {$ifdef FPC}nostackframe; assembler; asm {$else} asm .noframe // rcx=@a rdx=@b r8=n (Linux: rdi,rsi,rdx) -{$endif} +{$endif FPC} @1: {$ifdef win64} mov rax, qword ptr[rcx] bswap rax @@ -22601,14 +21813,11 @@ procedure bswap64array(a,b: PQWordArray; n: integer); jnz @1 end; {$else} -{$ifdef FPC} -procedure bswap64array(a,b: PQWordArray; n: integer); -var i: integer; +var i: PtrInt; begin for i := 0 to n-1 do - b^[i] := SwapEndian(a^[i]); + b^[i] := {$ifdef FPC}SwapEndian{$else}bswap64{$endif}(a^[i]); end; -{$endif FPC} {$endif CPUX64} {$endif CPUX86} @@ -22660,7 +21869,7 @@ function bswap64(const a: QWord): QWord; bswap eax end; {$else} -function bswap32(a: cardinal): cardinal; {$ifdef HASINLINE}inline;{$endif} +function bswap32(a: cardinal): cardinal; begin result := ((a and $ff)shl 24)or((a and $ff00)shl 8)or ((a and $ff0000)shr 8)or((a and $ff000000)shr 24); @@ -22681,7 +21890,7 @@ function bswap64(const a: QWord): QWord; {$define DEFINED_INT32TOUTF8} -function Int32ToUTF8(Value : integer): RawUtf8; // 3x faster than SysUtils.IntToStr +function Int32ToUTF8(Value : PtrInt): RawUtf8; // 3x faster than SysUtils.IntToStr // from IntToStr32_JOH_IA32_6_a, adapted for Delphi 2009+ asm // eax=Value, edx=@result push ebx @@ -23055,7 +22264,7 @@ function CompareMemSmall(P1, P2: Pointer; Length: PtrInt): Boolean; {$ifdef HASINLINE} procedure FillZero(var dest; count: PtrInt); begin - FillCharFast(dest,count,0); + {$ifdef FPC}FillChar{$else}FillCharFast{$endif}(dest,count,0); end; {$else} procedure FillZero(var dest; count: PtrInt); @@ -23065,190 +22274,11 @@ procedure FillZero(var dest; count: PtrInt); end; {$endif} -function SplitRight(const Str: RawUTF8; SepChar: AnsiChar; LeftStr: PRawUTF8): RawUTF8; -var i: PtrInt; -begin - for i := length(Str) downto 1 do - if Str[i]=SepChar then begin - result := copy(Str,i+1,maxInt); - if LeftStr<>nil then - LeftStr^ := copy(Str,1,i-1); - exit; - end; - result := Str; - if LeftStr<>nil then - LeftStr^ := ''; -end; - -function SplitRights(const Str, SepChar: RawUTF8): RawUTF8; -var i, j, sep: PtrInt; - c: AnsiChar; -begin - sep := length(SepChar); - if sep > 0 then - if sep = 1 then - result := SplitRight(Str,SepChar[1]) else begin - for i := length(Str) downto 1 do begin - c := Str[i]; - for j := 1 to sep do - if c=SepChar[j] then begin - result := copy(Str,i+1,maxInt); - exit; - end; - end; - end; - result := Str; -end; - -function Split(const Str, SepStr: RawUTF8; StartPos: integer): RawUTF8; -var i: integer; -begin - i := PosEx(SepStr,Str,StartPos); - if i>0 then - result := Copy(Str,StartPos,i-StartPos) else - if StartPos=1 then - result := Str else - result := Copy(Str,StartPos,maxInt); -end; - -procedure Split(const Str, SepStr: RawUTF8; var LeftStr, RightStr: RawUTF8; ToUpperCase: boolean); -var i: integer; - tmp: RawUTF8; // may be called as Split(Str,SepStr,Str,RightStr) -begin - i := PosEx(SepStr,Str); - if i=0 then begin - LeftStr := Str; - RightStr := ''; - end else begin - tmp := copy(Str,1,i-1); - RightStr := copy(Str,i+length(SepStr),maxInt); - LeftStr := tmp; - end; - if ToUpperCase then begin - LeftStr := UpperCaseU(LeftStr); - RightStr := UpperCaseU(RightStr); - end; -end; - -function Split(const Str, SepStr: RawUTF8; var LeftStr: RawUTF8; ToUpperCase: boolean=false): RawUTF8; -begin - Split(Str,SepStr,LeftStr,result,ToUpperCase); -end; - -procedure Split(const Str: RawUTF8; const SepStr: array of RawUTF8; - const DestPtr: array of PRawUTF8); -var s,i,j,n: integer; -begin - j := 1; - n := 0; - s := 0; - if high(SepStr)>=0 then - while n<=high(DestPtr) do begin - i := PosEx(SepStr[s],Str,j); - if i=0 then begin - if DestPtr[n]<>nil then - DestPtr[n]^ := copy(Str,j,MaxInt); - inc(n); - break; - end; - if DestPtr[n]<>nil then - DestPtr[n]^ := copy(Str,j,i-j); - inc(n); - if snil then - DestPtr[i]^ := ''; -end; - -function StringReplaceAll(const S, OldPattern, NewPattern: RawUTF8): RawUTF8; - - procedure Process(found: integer); - var oldlen,newlen,i,last,posCount,sharedlen: integer; - pos: TIntegerDynArray; - src,dst: PAnsiChar; - begin - oldlen := length(OldPattern); - newlen := length(NewPattern); - SetLength(pos,64); - pos[0] := found; - posCount := 1; - repeat - found := PosEx(OldPattern,S,found+oldlen); - if found=0 then - break; - AddInteger(pos,posCount,found); - until false; - FastSetString(result,nil,Length(S)+(newlen-oldlen)*posCount); - last := 1; - src := pointer(s); - dst := pointer(result); - for i := 0 to posCount-1 do begin - sharedlen := pos[i]-last; - MoveFast(src^,dst^,sharedlen); - inc(src,sharedlen+oldlen); - inc(dst,sharedlen); - MoveFast(pointer(NewPattern)^,dst^,newlen); - inc(dst,newlen); - last := pos[i]+oldlen; - end; - MoveFast(src^,dst^,length(S)-last+1); - end; - -var j: integer; -begin - if (S='') or (OldPattern='') or (OldPattern=NewPattern) then - result := S else begin - j := PosEx(OldPattern, S, 1); // our PosEx() is faster than Pos() - if j=0 then - result := S else - Process(j); - end; -end; - -function StringReplaceTabs(const Source,TabText: RawUTF8): RawUTF8; - - procedure Process(S,D,T: PAnsiChar; TLen: integer); - begin - repeat - if S^=#0 then - break else - if S^<>#9 then begin - D^ := S^; - inc(D); - inc(S); - end else begin - MoveFast(T^,D^,TLen); - inc(D,TLen); - inc(S); - end; - until false; - end; - -var L,i,n,ttl: PtrInt; -begin - ttl := length(TabText); - L := Length(Source); - n := 0; - if ttl<>0 then - for i := 1 to L do - if Source[i]=#9 then - inc(n); - if n=0 then begin - result := Source; - exit; - end; - SetLength(result,L+n*pred(ttl)); - Process(pointer(Source),pointer(result),pointer(TabText),ttl); -end; - function PosCharAny(Str: PUTF8Char; Characters: PAnsiChar): PUTF8Char; var s: PAnsiChar; c: AnsiChar; begin - if (Str<>nil) and (Characters<>nil) then + if (Str<>nil) and (Characters<>nil) and (Characters^<>#0) then repeat c := Str^; if c=#0 then @@ -23293,7 +22323,7 @@ function IdemPChar2(table: PNormTable; p: PUTF8Char; up: PAnsiChar): boolean; u := up^; if u=#0 then break; - if u<>table^[up[PtrUInt(p)]] then + if table^[up[PtrUInt(p)]]<>u then exit; inc(up); until false; @@ -23302,14 +22332,14 @@ function IdemPChar2(table: PNormTable; p: PUTF8Char; up: PAnsiChar): boolean; function PosI(uppersubstr: PUTF8Char; const str: RawUTF8): PtrInt; var u: AnsiChar; - table: {$ifdef CPUX86}TNormTable absolute NormToUpperAnsi7{$else}PNormTable{$endif}; + table: {$ifdef CPUX86NOTPIC}TNormTable absolute NormToUpperAnsi7{$else}PNormTable{$endif}; begin if uppersubstr<>nil then begin - {$ifndef CPUX86}table := @NormToUpperAnsi7;{$endif} + {$ifndef CPUX86NOTPIC}table := @NormToUpperAnsi7;{$endif} u := uppersubstr^; for result := 1 to Length(str) do if table[str[result]]=u then - if {$ifdef CPUX86}IdemPChar({$else}IdemPChar2(table,{$endif} + if {$ifdef CPUX86NOTPIC}IdemPChar({$else}IdemPChar2(table,{$endif} @PUTF8Char(pointer(str))[result],PAnsiChar(uppersubstr)+1) then exit; end; @@ -23318,15 +22348,15 @@ function PosI(uppersubstr: PUTF8Char; const str: RawUTF8): PtrInt; function StrPosI(uppersubstr,str: PUTF8Char): PUTF8Char; var u: AnsiChar; - table: {$ifdef CPUX86}TNormTable absolute NormToUpperAnsi7{$else}PNormTable{$endif}; + table: {$ifdef CPUX86NOTPIC}TNormTable absolute NormToUpperAnsi7{$else}PNormTable{$endif}; begin if (uppersubstr<>nil) and (str<>nil) then begin - {$ifndef CPUX86}table := @NormToUpperAnsi7;{$endif} + {$ifndef CPUX86NOTPIC}table := @NormToUpperAnsi7;{$endif} u := uppersubstr^; result := str; while result^<>#0 do begin if table[result^]=u then - if {$ifdef CPUX86}IdemPChar({$else}IdemPChar2(table,{$endif} + if {$ifdef CPUX86NOTPIC}IdemPChar({$else}IdemPChar2(table,{$endif} result+1,PAnsiChar(uppersubstr)+1) then exit; inc(result); @@ -23366,7 +22396,7 @@ procedure AppendBufferToRawUTF8(var Text: RawUTF8; Buffer: pointer; BufferLen: P exit; L := length(Text); SetLength(Text,L+BufferLen); - MoveFast(Buffer^,pointer(PtrInt(Text)+L)^,BufferLen); + {$ifdef FPC}Move{$else}MoveFast{$endif}(Buffer^,pointer(PtrInt(Text)+L)^,BufferLen); end; procedure AppendBuffersToRawUTF8(var Text: RawUTF8; const Buffers: array of PUTF8Char); @@ -23387,7 +22417,7 @@ procedure AppendBuffersToRawUTF8(var Text: RawUTF8; const Buffers: array of PUTF inc(P,TextLen); for i := 0 to high(Buffers) do if Buffers[i]<>nil then begin - MoveFast(Buffers[i]^,P^,lens[i]); + {$ifdef FPC}Move{$else}MoveFast{$endif}(Buffers[i]^,P^,lens[i]); inc(P,lens[i]); end; end; @@ -23397,7 +22427,7 @@ function AppendRawUTF8ToBuffer(Buffer: PUTF8Char; const Text: RawUTF8): PUTF8Cha begin L := length(Text); if L<>0 then begin - MoveFast(Pointer(Text)^,Buffer^,L); + {$ifdef FPC}Move{$else}MoveFast{$endif}(Pointer(Text)^,Buffer^,L); inc(Buffer,L); end; result := Buffer; @@ -23405,14 +22435,14 @@ function AppendRawUTF8ToBuffer(Buffer: PUTF8Char; const Text: RawUTF8): PUTF8Cha function AppendUInt32ToBuffer(Buffer: PUTF8Char; Value: cardinal): PUTF8Char; var L: integer; - tmp: array[0..15] of AnsiChar; + tmp: array[0..23] of AnsiChar; P: PAnsiChar; begin if Value<=high(SmallUInt32UTF8) then result := AppendRawUTF8ToBuffer(Buffer,SmallUInt32UTF8[Value]) else begin - P := StrUInt32(@tmp[15],Value); - L := @tmp[15]-P; - MoveFast(P^,Buffer^,L); + P := StrUInt32(@tmp[23],Value); + L := @tmp[23]-P; + {$ifdef FPC}Move{$else}MoveFast{$endif}(P^,Buffer^,L); result := Buffer+L; end; end; @@ -23465,10 +22495,10 @@ procedure QuotedStr(Text: PUTF8Char; Quote: AnsiChar; var result: RawUTF8); P^ := Quote; inc(P); if n=0 then begin - MoveFast(Text^,P^,L); + {$ifdef FPC}Move{$else}MoveFast{$endif}(Text^,P^,L); inc(P,L); end else begin - MoveFast(Text^,P^,first); + {$ifdef FPC}Move{$else}MoveFast{$endif}(Text^,P^,first); n := first; L := first; goto quot; @@ -23508,26 +22538,24 @@ function GotoEndOfQuotedString(P: PUTF8Char): PUTF8Char; result := P; end; // P^='"' at function return -procedure QuotedStrJSON(const aText: RawUTF8; var result: RawUTF8); -var i: integer; - temp: TTextWriterStackBuffer; +procedure QuotedStrJSON(const aText: RawUTF8; var result: RawUTF8; + const aPrefix, aSuffix: RawUTF8); +var temp: TTextWriterStackBuffer; begin - for i := 1 to length(aText) do - case aText[i] of - #0..#31,'\','"': - with TTextWriter.CreateOwnedStream(temp) do - try - Add('"'); - AddJSONEscape(pointer(aText)); - Add('"'); - SetText(result); - exit; - finally - Free; - end; - end; - // if we reached here, no character needs to be escaped in this string - result := '"'+aText+'"'; + if NeedsJsonEscape(aText) then + with TTextWriter.CreateOwnedStream(temp) do + try + AddString(aPrefix); + Add('"'); + AddJSONEscape(pointer(aText)); + Add('"'); + AddString(aSuffix); + SetText(result); + exit; + finally + Free; + end else + result := aPrefix+'"'+aText+'"'+aSuffix; end; function GotoEndOfJSONString(P: PUTF8Char): PUTF8Char; @@ -23753,32 +22781,24 @@ function Base64MagicCheckAndDecode(Value: PUTF8Char; ValueLen: integer; {$ifndef DEFINED_INT32TOUTF8} -function Int32ToUtf8(Value: integer): RawUTF8; // faster than SysUtils.IntToStr -var tmp: array[0..15] of AnsiChar; +function Int32ToUtf8(Value: PtrInt): RawUTF8; // faster than SysUtils.IntToStr +var tmp: array[0..23] of AnsiChar; P: PAnsiChar; begin - if cardinal(Value)<=high(SmallUInt32UTF8) then + if PtrUInt(Value)<=high(SmallUInt32UTF8) then result := SmallUInt32UTF8[Value] else begin - P := StrInt32(@tmp[15],Value); - FastSetString(result,P,@tmp[15]-P); + P := StrInt32(@tmp[23],Value); + FastSetString(result,P,@tmp[23]-P); end; end; function Int64ToUtf8(Value: Int64): RawUTF8; // faster than SysUtils.IntToStr -var tmp: array[0..23] of AnsiChar; - P: PAnsiChar; begin - if (PCardinalArray(@Value)^[0]<=high(SmallUInt32UTF8)) and - (PCardinalArray(@Value)^[1]=0) then - // Int64Rec gives compiler internal error C4963 - result := SmallUInt32UTF8[Value] else begin - P := StrInt64(@tmp[23],Value); - FastSetString(result,P,@tmp[23]-P); - end; + Int64ToUtf8(Value,result); end; function Trim(const S: RawUTF8): RawUTF8; -var I,L: Integer; +var I,L: PtrInt; begin L := Length(S); I := 1; @@ -23805,38 +22825,38 @@ function ToUTF8(Value: Int64): RawUTF8; {$endif CPU64} function ToUTF8(Value: PtrInt): RawUTF8; -var tmp: array[0..15] of AnsiChar; +var tmp: array[0..23] of AnsiChar; P: PAnsiChar; begin - P := StrInt32(@tmp[15],Value); - FastSetString(result,P,@tmp[15]-P); + P := StrInt32(@tmp[23],Value); + FastSetString(result,P,@tmp[23]-P); end; -function UInt32ToUtf8(Value: cardinal): RawUTF8; -var tmp: array[0..15] of AnsiChar; +function UInt32ToUtf8(Value: PtrUInt): RawUTF8; +var tmp: array[0..23] of AnsiChar; P: PAnsiChar; begin if Value<=high(SmallUInt32UTF8) then result := SmallUInt32UTF8[Value] else begin - P := StrUInt32(@tmp[15],Value); - FastSetString(result,P,@tmp[15]-P); + P := StrUInt32(@tmp[23],Value); + FastSetString(result,P,@tmp[23]-P); end; end; -procedure UInt32ToUtf8(Value: cardinal; var result: RawUTF8); -var tmp: array[0..15] of AnsiChar; +procedure UInt32ToUtf8(Value: PtrUInt; var result: RawUTF8); +var tmp: array[0..23] of AnsiChar; P: PAnsiChar; begin if Value<=high(SmallUInt32UTF8) then result := SmallUInt32UTF8[Value] else begin - P := StrUInt32(@tmp[15],Value); - FastSetString(result,P,@tmp[15]-P); + P := StrUInt32(@tmp[23],Value); + FastSetString(result,P,@tmp[23]-P); end; end; {$ifndef EXTENDEDTOSTRING_USESTR} var // standard FormatSettings (US) - SettingsUS: TFormatSettings; + SettingsUS: TFormatSettings; {$endif} function ExtendedToStringNoExp(var S: ShortString; Value: TSynExtended; @@ -23872,7 +22892,7 @@ function ExtendedToStringNoExp(var S: ShortString; Value: TSynExtended; '9': begin S[prec] := '0'; if ((prec=2) and (S[1]='-')) or (prec=1) then begin - MoveFast(S[prec],S[prec+1],result); + {$ifdef FPC}Move{$else}MoveFast{$endif}(S[prec],S[prec+1],result); S[prec] := '1'; break; end; @@ -23901,6 +22921,14 @@ function ExtendedToString(var S: ShortString; Value: TSynExtended; {$ifdef EXTENDEDTOSTRING_USESTR} var scientificneeded: boolean; valueabs: TSynExtended; +const SINGLE_HI: TSynExtended = 1E9; // for proper Delphi 5 compilation + SINGLE_LO: TSynExtended = 1E-9; + DOUBLE_HI: TSynExtended = 1E14; + DOUBLE_LO: TSynExtended = 1E-14; + {$ifndef CPU64} + EXT_HI: TSynExtended = 1E17; + EXT_LO: TSynExtended = 1E-17; + {$endif} begin if Value=0 then begin s[1] := '0'; @@ -23910,16 +22938,16 @@ function ExtendedToString(var S: ShortString; Value: TSynExtended; scientificneeded := false; valueabs := abs(Value); if Precision<=SINGLE_PRECISION then begin - if (valueabs>1E9) or (valueabs<1E-9) then + if (valueabs>SINGLE_HI) or (valueabsDOUBLE_PRECISION then begin - if (valueabs>1E17) or (valueabs<1E-17) then + if (valueabs>EXT_HI) or (valueabs1E14) or (valueabs<1E-14) then + if (valueabs>DOUBLE_HI) or (valueabsnil then {$ifdef FPC}Finalize(RawUTF8(d^.TempRawUTF8)){$else}RawUTF8(d^.TempRawUTF8) := ''{$endif}; @@ -24080,7 +23104,7 @@ function TFormatUTF8.WriteMax(Dest: PUTF8Char; Max: PtrUInt): PUTF8Char; d := @blocks; repeat if PtrUInt(Dest)+PtrUInt(d^.Len)>Max then begin // avoid buffer overflow - MoveFast(d^.Text^,Dest^,Max-PtrUInt(Dest)); + {$ifdef FPC}Move{$else}MoveFast{$endif}(d^.Text^,Dest^,Max-PtrUInt(Dest)); repeat if d^.TempRawUTF8<>nil then {$ifdef FPC}Finalize(RawUTF8(d^.TempRawUTF8)){$else}RawUTF8(d^.TempRawUTF8) := ''{$endif}; @@ -24089,7 +23113,7 @@ function TFormatUTF8.WriteMax(Dest: PUTF8Char; Max: PtrUInt): PUTF8Char; result := PUTF8Char(Max); exit; end; - MoveFast(d^.Text^,Dest^,d^.Len); + {$ifdef FPC}Move{$else}MoveFast{$endif}(d^.Text^,Dest^,d^.Len); inc(Dest,d^.Len); if d^.TempRawUTF8<>nil then {$ifdef FPC}Finalize(RawUTF8(d^.TempRawUTF8)){$else}RawUTF8(d^.TempRawUTF8) := ''{$endif}; @@ -24125,6 +23149,13 @@ procedure FormatShort(const Format: RawUTF8; const Args: array of const; end; end; +function FormatToShort(const Format: RawUTF8; const Args: array of const): shortstring; +var process: TFormatUTF8; +begin // Delphi 5 has troubles compiling overloaded FormatShort() + process.Parse(Format,Args); + result[0] := AnsiChar(process.WriteMax(@result[1],255)-@result[1]); +end; + procedure FormatShort16(const Format: RawUTF8; const Args: array of const; var result: TShort16); var process: TFormatUTF8; @@ -24188,14 +23219,11 @@ function FormatUTF8(const Format: RawUTF8; const Args, Params: array of const; J end; result := ''; tmpN := 0; - FillcharFast(inlin,SizeOf(inlin),0); + {$ifdef FPC}FillChar{$else}FillCharFast{$endif}(inlin,SizeOf(inlin),0); L := 0; A := 0; P := 0; F := pointer(Format); - {$ifdef FPC} - try // alf: to circumvent FPC issues - {$endif} while F^<>#0 do begin if F^<>'%' then begin FDeb := F; @@ -24266,18 +23294,13 @@ function FormatUTF8(const Format: RawUTF8; const Args, Params: array of const; J inc(F,2); end; L := {$ifdef FPC}_LStrLen(tmp[i]){$else}PInteger(PtrInt(tmp[i])-SizeOf(integer))^{$endif}; - MoveFast(pointer(tmp[i])^,F^,L); + {$ifdef FPC}Move{$else}MoveFast{$endif}(pointer(tmp[i])^,F^,L); inc(F,L); if i in inlin then begin PWord(F)^ := ord(')')+ord(':')shl 8; inc(F,2); end; end; - {$ifdef FPC} - finally - finalize(tmp); - end; - {$endif} end; function ScanUTF8(P: PUTF8Char; PLen: integer; const fmt: RawUTF8; @@ -24285,6 +23308,7 @@ function ScanUTF8(P: PUTF8Char; PLen: integer; const fmt: RawUTF8; var v,w: PtrInt; F,FEnd,PEnd: PUTF8Char; +label next; begin result := 0; if (fmt='') or (P=nil) or (PLen<=0) or (high(values)<0) then @@ -24323,13 +23347,22 @@ function ScanUTF8(P: PUTF8Char; PLen: integer; const fmt: RawUTF8; exit; 'X': if not GetNextItemHexDisplayToBin(P,values[v],8,#0) then exit; - 's': begin + 's','S': begin w := 0; while (P[w]>' ') and (P+w<=PEnd) do inc(w); - SetString(PShortString(values[v])^,P,w); + if F^='s' then + SetString(PShortString(values[v])^,PAnsiChar(P),w) else + FastSetString(PRawUTF8(values[v])^,P,w); inc(P,w); while (P^<=' ') and (P^<>#0) and (P<=PEnd) do inc(P); end; + 'L': begin + w := 0; + while not(P[w] in [#0,#10,#13]) and (P+w<=PEnd) do inc(w); + FastSetString(PRawUTF8(values[v])^,P,w); + inc(P,w); + end; + '%': goto next; else raise ESynException.CreateUTF8('ScanUTF8: unknown ''%'' specifier [%]',[F^,fmt]); end; inc(result); @@ -24345,7 +23378,7 @@ function ScanUTF8(P: PUTF8Char; PLen: integer; const fmt: RawUTF8; exit; break; end else begin - while (P^<>F^) and (P<=PEnd) do inc(P); +next: while (P^<>F^) and (P<=PEnd) do inc(P); inc(F); inc(P); if (F>=FEnd) or (P>=PEnd) then @@ -24371,7 +23404,7 @@ function RawByteStringArrayConcat(const Values: array of RawByteString): RawByte P := pointer(Result); for i := 0 to high(Values) do begin L := length(Values[i]); - MoveFast(pointer(Values[i])^,P^,L); + {$ifdef FPC}Move{$else}MoveFast{$endif}(pointer(Values[i])^,P^,L); inc(P,L); end; end; @@ -24382,7 +23415,7 @@ procedure RawByteStringToBytes(const buf: RawByteString; out bytes: TBytes); L := Length(buf); if L<>0 then begin SetLength(bytes,L); - MoveFast(pointer(buf)^,pointer(bytes)^,L); + {$ifdef FPC}Move{$else}MoveFast{$endif}(pointer(buf)^,pointer(bytes)^,L); end; end; @@ -24660,18 +23693,24 @@ function CompareMem(P1, P2: Pointer; Length: PtrInt): Boolean; result := false; end; -// from Aleksandr Sharahov's PosEx_Sha_Pas_2() +{$ifdef HASINLINE} // to use directly the SubStr/S arguments registers +function PosEx(const SubStr, S: RawUTF8; Offset: PtrUInt): PtrInt; +begin + result := PosExPas(pointer(SubStr),pointer(S),Offset); +end; +{$endif HASINLINE} + +// from Aleksandr Sharahov's PosEx_Sha_Pas_2() - refactored for cross-platform function PosExPas(pSub, p: PUTF8Char; Offset: PtrUInt): PtrInt; var len, lenSub: PtrInt; ch: AnsiChar; pStart, pStop: PUTF8Char; -label Loop0, Loop4, TestT, Test0, Test1, Test2, Test3, Test4, +label Loop2, Loop6, TestT, Test0, Test1, Test2, Test3, Test4, AfterTestT, AfterTest0, Ret, Exit; begin - if (p=nil) or (pSub=nil) or (Offset<1) then begin - result := 0; + result := 0; + if (p=nil) or (pSub=nil) or (Offset<1) then goto Exit; - end; {$ifdef FPC} len := _LStrLenP(p); lenSub := _LStrLenP(pSub)-1; @@ -24679,71 +23718,61 @@ function PosExPas(pSub, p: PUTF8Char; Offset: PtrUInt): PtrInt; len := PInteger(p-4)^; lenSub := PInteger(pSub-4)^-1; {$endif} - if (len=pStop then goto Exit; + goto Loop2; +Test4: dec(p,2); +Test2: dec(p,2); + goto Test0; +Test3: dec(p,2); +Test1: dec(p,2); TestT: len := lenSub; if lenSub<>0 then - repeat - if (psub[len]<>p[len+1]) or (psub[len+1]<>p[len+2]) then - goto AfterTestT; - len := len+2; - until len>=0; - p := p+2; + repeat + if (psub[len]<>p[len+1]) or (psub[len+1]<>p[len+2]) then + goto AfterTestT; + inc(len,2); + until len>=0; + inc(p,2); if p<=pStop then goto Ret; - result := 0; goto Exit; -Test4: p := p-2; -Test2: p := p-2; Test0: len := lenSub; if lenSub<>0 then - repeat - if (psub[len]<>p[len]) or (psub[len+1]<>p[len+1]) then - goto AfterTest0; - len := len+2; - until len>=0; + repeat + if (psub[len]<>p[len]) or (psub[len+1]<>p[len+1]) then + goto AfterTest0; + inc(len,2); + until len>=0; inc(p); Ret: result := p-pStart; Exit: end; -{$ifdef HASINLINE} // to use directly the SubStr/S arguments registers -function PosEx(const SubStr, S: RawUTF8; Offset: PtrUInt): PtrInt; -begin - result := PosExPas(pointer(SubStr),pointer(S),Offset); -end; -{$endif HASINLINE} - function IdemPropNameU(const P1,P2: RawUTF8): boolean; var L: PtrInt; begin @@ -24753,21 +23782,6 @@ function IdemPropNameU(const P1,P2: RawUTF8): boolean; result := false; end; -function IdemPropNameUSameLen(P1,P2: PUTF8Char; P1P2Len: PtrInt): boolean; -var i,j: PtrInt; -begin - result := false; - j := 0; - for i := 1 to P1P2Len shr 2 do - if (PCardinalArray(P1)[j] xor PCardinalArray(P2)[j]) and $dfdfdfdf<>0 then - exit else - inc(j); - for i := j*4 to P1P2Len-1 do - if (ord(P1[i]) xor ord(P2[i])) and $df<>0 then - exit; - result := true; -end; - function StrIComp(Str1, Str2: pointer): PtrInt; var C1,C2: PtrInt; lookupper: PByteArray; // better x86-64 / PIC asm generation @@ -24819,17 +23833,20 @@ function StrLenPas(S: pointer): PtrInt; end; function StrCompFast(Str1, Str2: pointer): PtrInt; +var c: byte; begin if Str1<>Str2 then if Str1<>nil then if Str2<>nil then begin - if PByte(Str1)^=PByte(Str2)^ then + c := PByte(Str1)^; + if c=PByte(Str2)^ then repeat - if PByte(Str1)^=0 then break; + if c=0 then break; inc(PByte(Str1)); inc(PByte(Str2)); - until PByte(Str1)^<>PByte(Str2)^; - result := PByte(Str1)^-PByte(Str2)^; + c := PByte(Str1)^; + until c<>PByte(Str2)^; + result := c-PByte(Str2)^; exit; end else result := 1 else // Str2='' @@ -24847,6 +23864,14 @@ procedure YearToPChar(Y: PtrUInt; P: PUTF8Char); PWordArray(P)[1] := tab[Y-(d100*100)]; end; +procedure YearToPChar2(tab: PWordArray; Y: PtrUInt; P: PUTF8Char); {$ifdef HASINLINE}inline;{$endif} +var d100: PtrUInt; +begin + d100 := Y div 100; + PWordArray(P)[0] := tab[d100]; + PWordArray(P)[1] := tab[Y-(d100*100)]; +end; + function Iso8601ToTimeLog(const S: RawByteString): TTimeLog; begin result := Iso8601ToTimeLogPUTF8Char(pointer(S),length(S)); @@ -24938,15 +23963,15 @@ function crc32cfast(crc: cardinal; buf: PAnsiChar; len: cardinal): cardinal; inc(buf); until len=0; if len>=4 then - repeat - result := result xor PCardinal(buf)^; - inc(buf,4); - dec(len,4); - result := tab[3,ToByte(result)] xor - tab[2,ToByte(result shr 8)] xor - tab[1,ToByte(result shr 16)] xor - tab[0,result shr 24]; - until len<4; + repeat + result := result xor PCardinal(buf)^; + inc(buf,4); + dec(len,4); + result := tab[3,ToByte(result)] xor + tab[2,ToByte(result shr 8)] xor + tab[1,ToByte(result shr 16)] xor + tab[0,result shr 24]; + until len<4; while len>0 do begin result := tab[0,ToByte(result xor ord(buf^))] xor (result shr 8); dec(len); @@ -25032,6 +24057,15 @@ function SortDynArrayQWord(const A,B): integer; result := 0; end; +function CompareQWord(A, B: QWord): integer; +begin + if AB then + result := 1 else + result := 0; +end; + function SortDynArrayAnsiString(const A,B): integer; begin result := StrComp(pointer(A),pointer(B)); @@ -25078,8 +24112,7 @@ function SortDynArrayPUTF8Char(const A,B): integer; {$else PUREPASCAL} function IdemPChar(p: PUTF8Char; up: PAnsiChar): boolean; -// if the beginning of p^ is same as up^ (ignore case - up^ must be already Upper) -// eax=p edx=up +{$ifdef FPC}nostackframe; assembler;{$endif} asm test eax, eax jz @e // P=nil -> false @@ -25119,6 +24152,7 @@ function IdemPChar(p: PUTF8Char; up: PAnsiChar): boolean; end; function IntegerScanIndex(P: PCardinalArray; Count: PtrInt; Value: cardinal): PtrInt; +{$ifdef FPC}nostackframe; assembler;{$endif} asm push eax call IntegerScan @@ -25132,6 +24166,7 @@ function IntegerScanIndex(P: PCardinalArray; Count: PtrInt; Value: cardinal): Pt end; function IntegerScan(P: PCardinalArray; Count: PtrInt; Value: cardinal): PCardinal; +{$ifdef FPC}nostackframe; assembler;{$endif} asm // eax=P, edx=Count, Value=ecx test eax, eax jz @ok0 // avoid GPF @@ -25207,6 +24242,7 @@ function IntegerScan(P: PCardinalArray; Count: PtrInt; Value: cardinal): PCardin end; function IntegerScanExists(P: PCardinalArray; Count: PtrInt; Value: cardinal): boolean; +{$ifdef FPC}nostackframe; assembler;{$endif} asm // eax=P, edx=Count, Value=ecx test eax, eax jz @z // avoid GPF @@ -25256,6 +24292,7 @@ function IntegerScanExists(P: PCardinalArray; Count: PtrInt; Value: cardinal): b end; function PosChar(Str: PUTF8Char; Chr: AnsiChar): PUTF8Char; +{$ifdef FPC}nostackframe; assembler;{$endif} asm // faster version by AB - eax=Str dl=Chr test eax, eax jz @z @@ -25287,6 +24324,7 @@ function PosChar(Str: PUTF8Char; Chr: AnsiChar): PUTF8Char; end; function CompareMem(P1, P2: Pointer; Length: PtrInt): Boolean; +{$ifdef FPC}nostackframe; assembler;{$endif} asm // eax=P1 edx=P2 ecx=Length cmp eax, edx je @0 // P1=P2 @@ -25353,6 +24391,7 @@ function CompareMem(P1, P2: Pointer; Length: PtrInt): Boolean; end; function PosEx(const SubStr, S: RawUTF8; Offset: PtrUInt): integer; +{$ifdef FPC}nostackframe; assembler;{$endif} asm // eax=SubStr, edx=S, ecx=Offset push ebx push esi @@ -25430,6 +24469,7 @@ function PosEx(const SubStr, S: RawUTF8; Offset: PtrUInt): integer; end; function IdemPropNameU(const P1,P2: RawUTF8): boolean; +{$ifdef FPC}nostackframe; assembler;{$endif} asm // eax=p1, edx=p2 cmp eax, edx je @out1 @@ -25468,6 +24508,7 @@ function IdemPropNameU(const P1,P2: RawUTF8): boolean; end; function IdemPropNameUSameLen(P1,P2: PUTF8Char; P1P2Len: PtrInt): boolean; +{$ifdef FPC}nostackframe; assembler;{$endif} asm // eax=p1, edx=p2, ecx=P1P2Len cmp eax, edx je @out2 @@ -25505,6 +24546,7 @@ function IdemPropNameUSameLen(P1,P2: PUTF8Char; P1P2Len: PtrInt): boolean; end; function StrIComp(Str1, Str2: pointer): PtrInt; +{$ifdef FPC}nostackframe; assembler;{$endif} asm // faster version by AB, from Agner Fog's original mov ecx, eax test eax, edx @@ -25557,6 +24599,7 @@ function StrIComp(Str1, Str2: pointer): PtrInt; end; function StrLenPas(S: pointer): PtrInt; +{$ifdef FPC}nostackframe; assembler;{$endif} asm // slower than x86/SSE* StrLen(), but won't read any byte beyond the string mov edx, eax test eax, eax @@ -25581,6 +24624,7 @@ function StrLenPas(S: pointer): PtrInt; end; function StrCompFast(Str1, Str2: pointer): PtrInt; +{$ifdef FPC}nostackframe; assembler;{$endif} asm // no branch taken in case of not equal first char cmp eax, edx je @zero // same string or both nil @@ -25614,6 +24658,7 @@ function StrCompFast(Str1, Str2: pointer): PtrInt; NEGATIVE_POLARITY = 16; function StrCompSSE42(Str1, Str2: pointer): PtrInt; +{$ifdef FPC}nostackframe; assembler;{$endif} asm // warning: may read up to 15 bytes beyond the string itself test eax, edx jz @n @@ -25658,6 +24703,7 @@ function StrCompSSE42(Str1, Str2: pointer): PtrInt; end; function SortDynArrayAnsiStringSSE42(const A,B): integer; +{$ifdef FPC}nostackframe; assembler;{$endif} asm // warning: may read up to 15 bytes beyond the string itself mov eax, [eax] mov edx, [edx] @@ -25704,6 +24750,36 @@ function SortDynArrayAnsiStringSSE42(const A,B): integer; sub eax, edx end; +function StrLenSSE42(S: pointer): PtrInt; +{$ifdef FPC}nostackframe; assembler;{$endif} +asm // warning: may read up to 15 bytes beyond the string itself + mov edx, eax // copy pointer + test eax, eax + jz @null // returns 0 if S=nil + xor eax, eax + {$ifdef HASAESNI} + pxor xmm0, xmm0 + pcmpistri xmm0, dqword[edx], EQUAL_EACH // comparison result in ecx + {$else} + db $66, $0F, $EF, $C0 + db $66, $0F, $3A, $63, $02, EQUAL_EACH + {$endif} + jnz @loop + mov eax, ecx + ret + nop // for @loop alignment +@loop: add eax, 16 + {$ifdef HASAESNI} + pcmpistri xmm0, dqword[edx + eax], EQUAL_EACH // comparison result in ecx + {$else} + db $66, $0F, $3A, $63, $04, $10, EQUAL_EACH + {$endif} + jnz @loop +@ok: add eax, ecx + ret +@null: db $f3 // rep ret +end; + procedure YearToPChar(Y: PtrUInt; P: PUTF8Char); asm // eax=Y, edx=P push edx @@ -26158,6 +25234,19 @@ function SortDynArrayInt64(const A,B): integer; @p: mov eax, 1 end; +function CompareQWord(A, B: QWord): integer; +begin + {$ifdef FPC_OR_UNICODE} // recent compilers are able to generate correct code + if AB then + result := 1 else + result := 0; + {$else} + result := SortDynArrayQWord(A,B); // use correct x86 asm version below + {$endif} +end; + function SortDynArrayQWord(const A,B): integer; asm // Delphi x86 compiler is not efficient, and oldest even incorrect mov ecx, [eax] @@ -26252,6 +25341,199 @@ function SortDynArrayPUTF8Char(const A,B): integer; {$endif PUREPASCAL} +function PosExChar(Chr: AnsiChar; const Str: RawUTF8): PtrInt; +begin + {$ifdef FPC} + if Str<>'' then // // will use fast FPC SSE version + result := IndexByte(pointer(Str)^,_LStrLen(Str),byte(chr))+1 else + {$else} + if Str<>'' then + for result := 1 to PInteger(PtrInt(Str)-sizeof(Integer))^ do + if Str[result]=Chr then + exit; + {$endif FPC} + result := 0; +end; + +function SplitRight(const Str: RawUTF8; SepChar: AnsiChar; LeftStr: PRawUTF8): RawUTF8; +var i: PtrInt; +begin + for i := length(Str) downto 1 do + if Str[i]=SepChar then begin + result := copy(Str,i+1,maxInt); + if LeftStr<>nil then + LeftStr^ := copy(Str,1,i-1); + exit; + end; + result := Str; + if LeftStr<>nil then + LeftStr^ := ''; +end; + +function SplitRights(const Str, SepChar: RawUTF8): RawUTF8; +var i, j, sep: PtrInt; + c: AnsiChar; +begin + sep := length(SepChar); + if sep > 0 then + if sep = 1 then + result := SplitRight(Str,SepChar[1]) else begin + for i := length(Str) downto 1 do begin + c := Str[i]; + for j := 1 to sep do + if c=SepChar[j] then begin + result := copy(Str,i+1,maxInt); + exit; + end; + end; + end; + result := Str; +end; + +function Split(const Str, SepStr: RawUTF8; StartPos: integer): RawUTF8; +var i: integer; +begin + i := PosEx(SepStr,Str,StartPos); + if i>0 then + result := Copy(Str,StartPos,i-StartPos) else + if StartPos=1 then + result := Str else + result := Copy(Str,StartPos,maxInt); +end; + +procedure Split(const Str, SepStr: RawUTF8; var LeftStr, RightStr: RawUTF8; ToUpperCase: boolean); +var i: integer; + tmp: RawUTF8; // may be called as Split(Str,SepStr,Str,RightStr) +begin + i := PosEx(SepStr,Str); + if i=0 then begin + LeftStr := Str; + RightStr := ''; + end else begin + tmp := copy(Str,1,i-1); + RightStr := copy(Str,i+length(SepStr),maxInt); + LeftStr := tmp; + end; + if ToUpperCase then begin + LeftStr := UpperCaseU(LeftStr); + RightStr := UpperCaseU(RightStr); + end; +end; + +function Split(const Str, SepStr: RawUTF8; var LeftStr: RawUTF8; ToUpperCase: boolean=false): RawUTF8; +begin + Split(Str,SepStr,LeftStr,result,ToUpperCase); +end; + +procedure Split(const Str: RawUTF8; const SepStr: array of RawUTF8; + const DestPtr: array of PRawUTF8); +var s,i,j,n: integer; +begin + j := 1; + n := 0; + s := 0; + if high(SepStr)>=0 then + while n<=high(DestPtr) do begin + i := PosEx(SepStr[s],Str,j); + if i=0 then begin + if DestPtr[n]<>nil then + DestPtr[n]^ := copy(Str,j,MaxInt); + inc(n); + break; + end; + if DestPtr[n]<>nil then + DestPtr[n]^ := copy(Str,j,i-j); + inc(n); + if snil then + DestPtr[i]^ := ''; +end; + +function StringReplaceAll(const S, OldPattern, NewPattern: RawUTF8): RawUTF8; + + procedure Process(found: integer); + var oldlen,newlen,i,last,posCount,sharedlen: integer; + pos: TIntegerDynArray; + src,dst: PAnsiChar; + begin + oldlen := length(OldPattern); + newlen := length(NewPattern); + SetLength(pos,64); + pos[0] := found; + posCount := 1; + repeat + found := PosEx(OldPattern,S,found+oldlen); + if found=0 then + break; + AddInteger(pos,posCount,found); + until false; + FastSetString(result,nil,Length(S)+(newlen-oldlen)*posCount); + last := 1; + src := pointer(s); + dst := pointer(result); + for i := 0 to posCount-1 do begin + sharedlen := pos[i]-last; + {$ifdef FPC}Move{$else}MoveFast{$endif}(src^,dst^,sharedlen); + inc(src,sharedlen+oldlen); + inc(dst,sharedlen); + {$ifdef FPC}Move{$else}MoveFast{$endif}(pointer(NewPattern)^,dst^,newlen); + inc(dst,newlen); + last := pos[i]+oldlen; + end; + {$ifdef FPC}Move{$else}MoveFast{$endif}(src^,dst^,length(S)-last+1); + end; + +var j: integer; +begin + if (S='') or (OldPattern='') or (OldPattern=NewPattern) then + result := S else begin + j := PosEx(OldPattern, S, 1); // our PosEx() is faster than Pos() + if j=0 then + result := S else + Process(j); + end; +end; + +function StringReplaceTabs(const Source,TabText: RawUTF8): RawUTF8; + + procedure Process(S,D,T: PAnsiChar; TLen: integer); + begin + repeat + if S^=#0 then + break else + if S^<>#9 then begin + D^ := S^; + inc(D); + inc(S); + end else begin + {$ifdef FPC}Move{$else}MoveFast{$endif}(T^,D^,TLen); + inc(D,TLen); + inc(S); + end; + until false; + end; + +var L,i,n,ttl: PtrInt; +begin + ttl := length(TabText); + L := Length(Source); + n := 0; + if ttl<>0 then + for i := 1 to L do + if Source[i]=#9 then + inc(n); + if n=0 then begin + result := Source; + exit; + end; + SetLength(result,L+n*pred(ttl)); + Process(pointer(Source),pointer(result),pointer(TabText),ttl); +end; + function strspnpas(s,accept: pointer): integer; var p: PCardinal; c: AnsiChar; @@ -26328,8 +25610,9 @@ function strcspnpas(s,reject: pointer): integer; until false; end; +{$ifndef ABSOLUTEPASCAL} {$ifdef CPUINTEL} -{$ifdef CPUX64} +{$ifdef CPUX64} // inspired by Agner Fog's strspn64.asm function strcspnsse42(s,reject: pointer): integer; {$ifdef FPC}nostackframe; assembler; asm {$else} asm // rcx=s, rdx=reject (Linux: rdi,rsi) @@ -26414,7 +25697,7 @@ function strspnsse42(s,accept: pointer): integer; pop rsi pop rdi {$endif}ret -@4: and eax, edx // accumulate matches +@4: or eax, edx // accumulate matches @5: add rsi, 16 // the set is more than 16 bytes movdqu xmm1, [rsi] {$ifdef HASAESNI} @@ -26425,7 +25708,7 @@ function strspnsse42(s,accept: pointer): integer; movd edx, xmm0 jns @4 mov rsi, r8 // restore set pointer - and eax, edx // accumulate matches + or eax, edx // accumulate matches cmp eax, 65535 jne @3 add rdi, 16 // first 16 chars matched, continue with next 16 chars @@ -26520,7 +25803,7 @@ function strspnsse42(s,accept: pointer): integer; pop esi pop edi ret -@4: and eax, edx // accumulate matches +@4: or eax, edx // accumulate matches @5: add esi, 16 // the set is more than 16 bytes {$ifdef HASAESNI} movdqu xmm1, [esi] @@ -26533,7 +25816,7 @@ function strspnsse42(s,accept: pointer): integer; {$endif} jns @4 mov esi, ebx // restore set pointer - and eax, edx // accumulate matches + or eax, edx // accumulate matches cmp eax, 65535 jne @3 add edi, 16 // first 16 chars matched, continue with next 16 chars @@ -26550,6 +25833,7 @@ function StrLenSSE2(S: pointer): PtrInt; pxor xmm0, xmm0 // set to zero and ecx, 15 // lower 4 bits indicate misalignment and eax, -16 // align pointer by 16 + // will never read outside a memory page boundary, so won't trigger GPF movdqa xmm1, [eax] // read from nearest preceding boundary pcmpeqb xmm1, xmm0 // compare 16 bytes with zero pmovmskb edx, xmm1 // get one bit for each byte result @@ -26576,6 +25860,7 @@ function StrLenSSE2(S: pointer): PtrInt; {$endif DELPHI5OROLDER} {$endif CPUX86} {$endif CPUINTEL} +{$endif ABSOLUTEPASCAL} function IdemPropName(const P1,P2: shortstring): boolean; begin @@ -26610,6 +25895,23 @@ function ToText(os: TOperatingSystem): PShortString; result := GetEnumName(TypeInfo(TOperatingSystem),ord(os)); end; +function ToText(const osv: TOperatingSystemVersion): ShortString; +begin + if osv.os=osWindows then + FormatShort('Windows %', [WINDOWS_NAME[osv.win]], result) else + TrimLeftLowerCaseToShort(ToText(osv.os),result); +end; + +function ToTextOS(osint32: integer): RawUTF8; +var osv: TOperatingSystemVersion absolute osint32; + ost: ShortString; +begin + ost := ToText(osv); + if (osv.os>=osLinux) and (osv.utsrelease[2]<>0) then + result := FormatUTF8('% %.%.%',[ost,osv.utsrelease[2],osv.utsrelease[1],osv.utsrelease[0]]) else + result := ShortStringToUTF8(ost); +end; + {$ifdef MSWINDOWS} procedure FileTimeToInt64(const FT: TFileTime; out I64: Int64); begin @@ -26640,17 +25942,20 @@ function GetVersionEx(var lpVersionInformation: TOSVersionInfoEx): BOOL; stdcall external kernel32 name 'GetVersionExA'; {$endif} -function GetSystemTimeMillisecondsForXP: Int64; stdcall; -var fileTime: TFileTime; -begin - GetSystemTimeAsFileTime(fileTime); // very fast, with 100 ns unit - {$ifdef CPU64} // 64 bit XP ? not very likely - but who knows :) - FileTimeToInt64(fileTime,result); - result := result div 10000; - {$else} - result := trunc(PInt64(@fileTime)^/10000); // 100 ns unit - {$endif} -end; +var + GetTickXP: Int64Rec; + +function GetTickCount64ForXP: Int64; stdcall; +var t32: cardinal; + t64: Int64Rec absolute result; +begin // warning: GetSystemTimeAsFileTime() is fast, but not monotonic! + t32 := Windows.GetTickCount; + t64 := GetTickXP; // (almost) atomic read + if t32=wVista then begin - if OSVersionInfo.wProductType<>VER_NT_WORKSTATION then + if OSVersionInfo.wProductType<>VER_NT_WORKSTATION then begin // Server edition inc(Vers,2); // e.g. wEight -> wServer2012 - if SystemInfo.wProcessorArchitecture=PROCESSOR_ARCHITECTURE_AMD64 then + if (Vers=wServer2016) and (OSVersionInfo.dwBuildNumber>=17763) then + Vers := wServer2019_64; // https://stackoverflow.com/q/53393150 + end; + if (SystemInfo.wProcessorArchitecture=PROCESSOR_ARCHITECTURE_AMD64) and + (Vers < wServer2019_64) then inc(Vers); // e.g. wEight -> wEight64 - OpenProcessAccess := PROCESS_QUERY_LIMITED_INFORMATION; - end else - OpenProcessAccess := PROCESS_QUERY_INFORMATION or PROCESS_VM_READ; + end; OSVersion := Vers; with OSVersionInfo do if wServicePackMajor=0 then @@ -26796,45 +26069,11 @@ procedure RetrieveSystemInfo; cpu := GetEnvironmentVariable('PROCESSOR_IDENTIFIER'); cpu := SysUtils.Trim(cpu); FormatUTF8('% x % ('+CPU_ARCH_TEXT+')',[SystemInfo.dwNumberOfProcessors,cpu],CpuInfoText); - @GetSystemTimes := GetProcAddress(Kernel,'GetSystemTimes'); - @GetProcessTimes := GetProcAddress(Kernel,'GetProcessTimes'); - @QueryFullProcessImageNameW := GetProcAddress(Kernel,'QueryFullProcessImageNameW'); - Psapi := LoadLibrary('Psapi.dll'); - if Psapi>=32 then begin - @EnumProcesses := GetProcAddress(Psapi,'EnumProcesses'); - @GetModuleFileNameExW := GetProcAddress(Psapi,'GetModuleFileNameExW'); - @EnumProcessModules := GetProcAddress(Psapi, 'EnumProcessModules'); - @GetProcessMemoryInfo := GetProcAddress(Psapi,'GetProcessMemoryInfo'); - end; end; {$else} -{$ifdef BSD} -function fpsysctlhwint(hwid: cint): Int64; -var mib: array[0..1] of cint; - len: cint; -begin - result := 0; - mib[0] := CTL_HW; - mib[1] := hwid; - len := SizeOf(result); - fpsysctl(pointer(@mib),2,@result,@len,nil,0); -end; -function fpsysctlhwstr(hwid: cint; var temp: shortstring): PUTF8Char; -var mib: array[0..1] of cint; - len: cint; -begin - mib[0] := CTL_HW; - mib[1] := hwid; - FillChar(temp,SizeOf(temp),0); - len := SizeOf(temp); - fpsysctl(pointer(@mib),2,@result,@len,nil,0); - if temp[0]<>#0 then - result := @temp else - result := nil; -end; -{$else} +{$ifndef BSD} procedure SetLinuxDistrib(const release: RawUTF8); var distrib: TOperatingSystem; @@ -26865,7 +26104,7 @@ procedure RetrieveSystemInfo; {$ifdef BSD} fpuname(SystemInfo.uts); SystemInfo.dwNumberOfProcessors := fpsysctlhwint(HW_NCPU); - BiosInfoText := fpsysctlhwstr(HW_MACHINE,temp); + Utf8ToRawUTF8(fpsysctlhwstr(HW_MACHINE,temp),BiosInfoText); modname := fpsysctlhwstr(HW_MODEL,temp); with SystemInfo.uts do FormatUTF8('%-% %',[sysname,release,version],OSVersionText); @@ -26913,7 +26152,7 @@ procedure RetrieveSystemInfo; release := StringToUTF8(SR.Name); release := split(release,'-'); dist := split(trim(StringFromFile('/etc/'+SR.Name)),#10); - if (dist<>'') and (PosEx('=',dist)=0) and (PosEx(' ',dist)>0) then + if (dist<>'') and (PosExChar('=',dist)=0) and (PosExChar(' ',dist)>0) then SetLinuxDistrib(dist) // e.g. 'Red Hat Enterprise Linux Server release 6.7 (Santiago)' else dist := ''; @@ -27023,7 +26262,7 @@ function Elapsed(var PreviousTix: Int64; Interval: Integer): Boolean; begin if Interval<=0 then result := false else begin - now := GetTickCount64; + now := {$ifdef FPCLINUX}SynFPCLinux.{$endif}GetTickCount64; if now-PreviousTix>Interval then begin PreviousTix := now; result := true; @@ -27394,7 +26633,7 @@ function SoundExUTF8(U: PUTF8Char; next: PPUTF8Char; {$ifdef USENORMTOUPPER} -function AnsiICompW(u1, u2: PWideChar): PtrInt; +function AnsiICompW(u1, u2: PWideChar): PtrInt; {$ifdef HASINLINE}inline;{$endif} begin if u1<>u2 then if u1<>nil then @@ -27418,17 +26657,20 @@ function AnsiICompW(u1, u2: PWideChar): PtrInt; {$ifdef PUREPASCAL} function AnsiIComp(Str1, Str2: PWinAnsiChar): PtrInt; +var table: PNormTableByte; begin if Str1<>Str2 then if Str1<>nil then - if Str2<>nil then - repeat - result := NormToUpperByte[ord(Str1^)]-NormToUpperByte[pByte(Str2)^]; - if result<>0 then exit; - if (Str1^=#0) or (Str2^=#0) then break; - inc(Str1); - inc(Str2); - until false else + if Str2<>nil then begin + table := @NormToUpperByte; + repeat + result := table[ord(Str1^)]-table[pByte(Str2)^]; + if result<>0 then exit; + if (Str1^=#0) or (Str2^=#0) then break; + inc(Str1); + inc(Str2); + until false; + end else result := 1 else // Str2='' result := -1 else // Str1='' result := 0; // Str1=Str2 @@ -27525,7 +26767,7 @@ function ConvertCaseUTF8(P: PUTF8Char; const Table: TNormTableByte): PtrInt; S := P-1; inc(P,extra); inc(extra); - MoveFast(S^,D[result],extra); + {$ifdef FPC}Move{$else}MoveFast{$endif}(S^,D[result],extra); inc(result,extra); end; until false; @@ -27553,9 +26795,9 @@ function LowerCaseU(const S: RawUTF8): RawUTF8; function UTF8IComp(u1, u2: PUTF8Char): PtrInt; var c2: PtrInt; - table: {$ifdef CPUX86}TNormTableByte absolute NormToUpperByte{$else}PNormTableByte{$endif}; + table: {$ifdef CPUX86NOTPIC}TNormTableByte absolute NormToUpperByte{$else}PNormTableByte{$endif}; begin // fast UTF-8 comparaison using the NormToUpper[] array for all 8 bits values - {$ifndef CPUX86}table := @NormToUpperByte;{$endif} + {$ifndef CPUX86NOTPIC}table := @NormToUpperByte;{$endif} if u1<>u2 then if u1<>nil then if u2<>nil then @@ -27580,7 +26822,7 @@ function UTF8IComp(u1, u2: PUTF8Char): PtrInt; end else begin result := GetHighUTF8UCS4Inlined(u1); if result and $ffffff00=0 then - result := table[result]; // 8 bits to upper, 32 bits as is + result := table[result]; // 8 bits to upper, 32-bit as is end; if c2<=127 then begin if c2=0 then exit; // u1>u2 -> return u1^ @@ -27592,7 +26834,7 @@ function UTF8IComp(u1, u2: PUTF8Char): PtrInt; c2 := GetHighUTF8UCS4Inlined(u2); if c2<=255 then dec(result,table[c2]) else // 8 bits to upper - dec(result,c2); // 32 bits widechar returns diff + dec(result,c2); // 32-bit widechar returns diff if result<>0 then exit; end; until false else @@ -27604,10 +26846,10 @@ function UTF8IComp(u1, u2: PUTF8Char): PtrInt; function UTF8ILComp(u1, u2: PUTF8Char; L1,L2: cardinal): PtrInt; var c2: PtrInt; extra,i: integer; - table: {$ifdef CPUX86}TNormTableByte absolute NormToUpperByte{$else}PNormTableByte{$endif}; + table: {$ifdef CPUX86NOTPIC}TNormTableByte absolute NormToUpperByte{$else}PNormTableByte{$endif}; label neg,pos; begin // fast UTF-8 comparaison using the NormToUpper[] array for all 8 bits values - {$ifndef CPUX86}table := @NormToUpperByte;{$endif} + {$ifndef CPUX86NOTPIC}table := @NormToUpperByte;{$endif} if u1<>u2 then if (u1<>nil) and (L1<>0) then if (u2<>nil) and (L2<>0) then @@ -27642,7 +26884,7 @@ function UTF8ILComp(u1, u2: PUTF8Char; L1,L2: cardinal): PtrInt; dec(result,UTF8_EXTRA[extra].offset); inc(u1,extra); if result and $ffffff00=0 then - result := table[result]; // 8 bits to upper, 32 bits as is + result := table[result]; // 8 bits to upper, 32-bit as is end; // here result=NormToUpper[u1^] inc(u2); @@ -27661,7 +26903,7 @@ function UTF8ILComp(u1, u2: PUTF8Char; L1,L2: cardinal): PtrInt; inc(u2,extra); if c2 and $ffffff00=0 then dec(result,table[c2]) else // 8 bits to upper - dec(result,c2); // returns 32 bits diff + dec(result,c2); // returns 32-bit diff if result<>0 then exit; end; // here we have result=NormToUpper[u2^]-NormToUpper[u1^]=0 @@ -27686,7 +26928,7 @@ function SameTextU(const S1, S2: RawUTF8): Boolean; function AnsiIComp(Str1, Str2: PWinAnsiChar): integer; {$ifdef PUREPASCAL} begin - result := StrIComp(Str1,Str2); // fast enough + result := StrIComp(Str1,Str2); // fast enough, especially since inlined end; {$else} asm @@ -27838,8 +27080,9 @@ function FindUTF8(U: PUTF8Char; UpperValue: PAnsiChar): boolean; end; inc(UpperValue); until false; - // find beginning of next word -Next: +Next: // find beginning of next word + U := FindNextUTF8WordBegin(U); + until U=nil; {$else} // this tiny version only handles 7-bits ansi chars and ignore all UTF-8 chars ValueStart := UpperValue; @@ -27869,21 +27112,21 @@ function FindUTF8(U: PUTF8Char; UpperValue: PAnsiChar): boolean; if byte(U^)=0 then exit else if byte(U^) and $80<>0 then break; // 7 bits char check only until false; -{$endif} // find beginning of next word U := FindNextUTF8WordBegin(U); until U=nil; +{$endif} end; function HexDisplayToBin(Hex: PAnsiChar; Bin: PByte; BinBytes: integer): boolean; var B,C: PtrUInt; i: integer; - tab: {$ifdef CPUX86}TNormTableByte absolute ConvertHexToBin{$else}PNormTableByte{$endif}; + tab: {$ifdef CPUX86NOTPIC}TNormTableByte absolute ConvertHexToBin{$else}PNormTableByte{$endif}; begin result := false; // return false if any invalid char if (Hex=nil) or (Bin=nil) then exit; - {$ifndef CPUX86}tab := @ConvertHexToBin;{$endif} // faster on PIC and x86_64 + {$ifndef CPUX86NOTPIC}tab := @ConvertHexToBin;{$endif} // faster on PIC and x86_64 inc(Bin,BinBytes-1); for i := 1 to BinBytes do begin B := tab[Ord(Hex^)]; @@ -27922,12 +27165,12 @@ function HexDisplayToInt64(const Hex: RawByteString): Int64; function HexToBin(Hex: PAnsiChar; Bin: PByte; BinBytes: Integer): boolean; var I: Integer; B,C: PtrUInt; - tab: {$ifdef CPUX86}TNormTableByte absolute ConvertHexToBin{$else}PNormTableByte{$endif}; + tab: {$ifdef CPUX86NOTPIC}TNormTableByte absolute ConvertHexToBin{$else}PNormTableByte{$endif}; begin result := false; // return false if any invalid char if Hex=nil then exit; - {$ifndef CPUX86}tab := @ConvertHexToBin;{$endif} // faster on PIC and x86_64 + {$ifndef CPUX86NOTPIC}tab := @ConvertHexToBin;{$endif} // faster on PIC and x86_64 if Bin<>nil then for I := 1 to BinBytes do begin B := tab[Ord(Hex^)]; @@ -28224,13 +27467,13 @@ function BinToBase64(const data, Prefix, Suffix: RawByteString; WithMagic: boole if WithMagic then inc(len,3); SetLength(result,len); - MoveFast(pointer(Prefix)^,res[0],lenprefix); + {$ifdef FPC}Move{$else}MoveFast{$endif}(pointer(Prefix)^,res[0],lenprefix); if WithMagic then begin PInteger(@res[lenprefix])^ := JSON_BASE64_MAGIC; inc(lenprefix,3); end; Base64Encode(@res[lenprefix],pointer(data),lendata); - MoveFast(pointer(Suffix)^,res[len-lensuffix],lensuffix); + {$ifdef FPC}Move{$else}MoveFast{$endif}(pointer(Suffix)^,res[len-lensuffix],lensuffix); end; function BinToBase64WithMagic(const data: RawByteString): RawUTF8; @@ -28563,6 +27806,12 @@ procedure Base64uriToBin(sp: PAnsiChar; len: PtrInt; var result: RawByteString); result := ''; end; +function Base64uriToBin(sp: PAnsiChar; len: PtrInt; var temp: TSynTempBuffer): boolean; +begin + temp.Init(Base64uriToBinLength(len)); + result := (temp.len>0) and Base64AnyDecode(ConvertBase64URIToBin,sp,temp.buf,len); +end; + function Base64uriToBin(const base64: RawByteString; bin: PAnsiChar; binlen: PtrInt): boolean; begin result := Base64uriToBin(pointer(base64),bin,length(base64),binlen); @@ -28680,6 +27929,24 @@ function LowerCaseUnicode(const S: RawUTF8): RawUTF8; {$endif} end; +function IsCaseSensitive(const S: RawUTF8): boolean; +begin + result := IsCaseSensitive(pointer(S),length(S)); +end; + +function IsCaseSensitive(P: PUTF8Char; PLen: integer): boolean; +begin + result := true; + if (P<>nil) and (PLen>0) then + repeat + if ord(P^) in [ord('a')..ord('z'), ord('A')..ord('Z')] then + exit; + inc(P); + dec(PLen); + until PLen=0; + result := false; +end; + function UpperCase(const S: RawUTF8): RawUTF8; var L, i: PtrInt; begin @@ -28783,15 +28050,16 @@ function TrimRight(const S: RawUTF8): RawUTF8; TwoDigitsHexWBLower: array[byte] of word absolute TwoDigitsHexLower; procedure BinToHex(Bin, Hex: PAnsiChar; BinBytes: integer); -var j: cardinal; - {$ifdef PUREPASCAL}tab: ^TAnsiCharToWord;{$endif} +{$ifdef PUREPASCAL}var tab: ^TAnsiCharToWord;{$endif} begin {$ifdef PUREPASCAL}tab := @TwoDigitsHexW;{$endif} - for j := 1 to BinBytes do begin - PWord(Hex)^ := {$ifndef PUREPASCAL}TwoDigitsHexW{$else}tab{$endif}[Bin^]; - inc(Hex,2); - inc(Bin); - end; + if BinBytes>0 then + repeat + PWord(Hex)^ := {$ifndef PUREPASCAL}TwoDigitsHexW{$else}tab{$endif}[Bin^]; + inc(Bin); + inc(Hex,2); + dec(BinBytes); + until BinBytes=0; end; function BinToHex(const Bin: RawByteString): RawUTF8; @@ -28862,6 +28130,20 @@ function LogEscape(source: PAnsiChar; sourcelen: integer; var temp: TLogEscape; result := @temp; end; +function LogEscapeFull(const source: RawByteString): RawUTF8; +begin + result := LogEscapeFull(pointer(source),length(source)); +end; + +function LogEscapeFull(source: PAnsiChar; sourcelen: integer): RawUTF8; +begin + SetLength(result,sourcelen*3); // worse case + if sourcelen=0 then + exit; + sourcelen := EscapeBuffer(source,pointer(result),sourcelen,length(result))-pointer(result); + SetLength(result,sourcelen); +end; + function EscapeToShort(source: PAnsiChar; sourcelen: integer): shortstring; begin result[0] := AnsiChar(EscapeBuffer(source,@result[1],sourcelen,80)-@result[1]); @@ -28873,14 +28155,17 @@ function EscapeToShort(const source: RawByteString): shortstring; overload; end; procedure BinToHexDisplay(Bin, Hex: PAnsiChar; BinBytes: integer); -var j: integer; - {$ifdef PUREPASCAL}tab: ^TAnsiCharToWord;{$endif} +{$ifdef PUREPASCAL}var tab: ^TAnsiCharToWord;{$endif} begin {$ifdef PUREPASCAL}tab := @TwoDigitsHexW;{$endif} - for j := BinBytes-1 downto 0 do begin - PWord(Hex+j*2)^ := {$ifndef PUREPASCAL}TwoDigitsHexW{$else}tab{$endif}[Bin^]; - inc(Bin); - end; + inc(Hex,BinBytes*2); + if BinBytes>0 then + repeat + dec(Hex,2); + PWord(Hex)^ := {$ifndef PUREPASCAL}TwoDigitsHexW{$else}tab{$endif}[Bin^]; + inc(Bin); + dec(BinBytes); + until BinBytes=0; end; function BinToHexDisplay(Bin: PAnsiChar; BinBytes: integer): RawUTF8; @@ -28890,15 +28175,16 @@ function BinToHexDisplay(Bin: PAnsiChar; BinBytes: integer): RawUTF8; end; procedure BinToHexLower(Bin, Hex: PAnsiChar; BinBytes: integer); -var j: cardinal; - {$ifdef PUREPASCAL}tab: ^TAnsiCharToWord;{$endif} +{$ifdef PUREPASCAL}var tab: ^TAnsiCharToWord;{$endif} begin {$ifdef PUREPASCAL}tab := @TwoDigitsHexWLower;{$endif} - for j := 1 to BinBytes do begin - PWord(Hex)^ := {$ifndef PUREPASCAL}TwoDigitsHexWLower{$else}tab{$endif}[Bin^]; - inc(Hex,2); - inc(Bin); - end; + if BinBytes>0 then + repeat + PWord(Hex)^ := {$ifndef PUREPASCAL}TwoDigitsHexWLower{$else}tab{$endif}[Bin^]; + inc(Bin); + inc(Hex,2); + dec(BinBytes); + until BinBytes=0; end; function BinToHexLower(const Bin: RawByteString): RawUTF8; @@ -28917,15 +28203,18 @@ function BinToHexLower(Bin: PAnsiChar; BinBytes: integer): RawUTF8; BinToHexLower(Bin,BinBytes,result); end; -procedure BinToHexDisplayLower(Bin, Hex: PAnsiChar; BinBytes: integer); -var j: integer; - {$ifdef PUREPASCAL}tab: ^TAnsiCharToWord;{$endif} +procedure BinToHexDisplayLower(Bin, Hex: PAnsiChar; BinBytes: PtrInt); +{$ifdef PUREPASCAL}var tab: ^TAnsiCharToWord;{$endif} begin {$ifdef PUREPASCAL}tab := @TwoDigitsHexWLower;{$endif} - for j := BinBytes-1 downto 0 do begin - PWord(Hex+j*2)^ := {$ifndef PUREPASCAL}TwoDigitsHexWLower{$else}tab{$endif}[Bin^]; - inc(Bin); - end; + inc(Hex,BinBytes*2); + if BinBytes>0 then + repeat + dec(Hex,2); + PWord(Hex)^ := {$ifndef PUREPASCAL}TwoDigitsHexWLower{$else}tab{$endif}[Bin^]; + inc(Bin); + dec(BinBytes); + until BinBytes=0; end; function BinToHexDisplayLower(Bin: PAnsiChar; BinBytes: integer): RawUTF8; @@ -29104,20 +28393,18 @@ function UInt2DigitsToShortFast(Value: byte): TShort4; PWord(@result[1])^ := TwoDigitLookupW[Value]; end; -const - DOUBLE_RESOLUTION = 1E-12; // also for TSynExtended (FPC uses 1E-4!) - function SameValue(const A, B: Double; DoublePrec: double): Boolean; -var AbsA,AbsB: double; +var AbsA,AbsB,Res: double; begin if PInt64(@DoublePrec)^=0 then begin // Max(Min(Abs(A),Abs(B))*1E-12,1E-12) AbsA := Abs(A); AbsB := Abs(B); + Res := 1E-12; if AbsAB then + result := 1 else + result := 0; +end; + +function CompareInteger(const A, B: integer): integer; +begin + if AB then + result := 1 else + result := 0; +end; + +function CompareInt64(const A, B: Int64): integer; +begin + if AB then + result := 1 else + result := 0; +end; + +function CompareCardinal(const A, B: cardinal): integer; +begin + if AB then + result := 1 else + result := 0; +end; + procedure KahanSum(const Data: double; var Sum, Carry: double); var y, t: double; begin @@ -29229,15 +28553,27 @@ function AddRawUTF8(var Values: TRawUTF8DynArray; const Value: RawUTF8; result := true; end; +function NextGrow(capacity: integer): integer; +begin // algorithm similar to TFPList.Expand for the increasing ranges + result := capacity; + if result<128 shl 20 then + if result<8 shl 20 then + if result<=128 then + if result>8 then + inc(result,16) else + inc(result,4) else + inc(result,result shr 2) else + inc(result,result shr 3) else + inc(result,16 shl 20); +end; + procedure AddRawUTF8(var Values: TRawUTF8DynArray; var ValuesCount: integer; const Value: RawUTF8); var capacity: integer; begin capacity := Length(Values); - if ValuesCount=capacity then begin - inc(capacity,32+capacity shr 3); - SetLength(Values,capacity); - end; + if ValuesCount=capacity then + SetLength(Values,NextGrow(capacity)); Values[ValuesCount] := Value; inc(ValuesCount); end; @@ -29281,8 +28617,6 @@ procedure StringListToRawUTF8DynArray(Source: TStringList; var Result: TRawUTF8D StringToUTF8(Source[i],Result[i]); end; -/// find the position of the SEARCH] section in source -// - return true if SEARCH] was found, and store line after it in source function FindSectionFirstLine(var source: PUTF8Char; search: PAnsiChar): boolean; {$ifdef PUREPASCAL} begin @@ -29353,7 +28687,6 @@ function FindSectionFirstLine(var source: PUTF8Char; search: PAnsiChar): boolean {$ifdef USENORMTOUPPER} {$ifdef PUREPASCAL} function IdemPCharW(p: PWideChar; up: PUTF8Char): boolean; -// if the beginning of p^ is same as up^ (ignore case - up^ must be already Upper) begin result := false; if (p=nil) or (up=nil) then @@ -29368,9 +28701,7 @@ function IdemPCharW(p: PWideChar; up: PUTF8Char): boolean; end; {$else} function IdemPCharW(p: PWideChar; up: PUTF8Char): boolean; -// if the beginning of p^ is same as up^ (ignore case - up^ must be already Upper) -// eax=p edx=up -asm +asm // eax=p edx=up test eax, eax jz @e // P=nil -> false test edx, edx @@ -29399,7 +28730,7 @@ function IdemPCharW(p: PWideChar; up: PUTF8Char): boolean; pop esi pop ebx end; -{$endif} +{$endif PUREPASCAL} {$else} function IdemPCharW(p: PWideChar; up: PUTF8Char): boolean; // if the beginning of p^ is same as up^ (ignore case - up^ must be already Upper) @@ -29415,7 +28746,7 @@ function IdemPCharW(p: PWideChar; up: PUTF8Char): boolean; end; result := true; end; -{$endif} +{$endif USENORMTOUPPER} function FindSectionFirstLineW(var source: PWideChar; search: PUTF8Char): boolean; {$ifdef PUREPASCAL} @@ -29480,15 +28811,15 @@ function FindSectionFirstLineW(var source: PWideChar; search: PUTF8Char): boolea ret @z: pop edx // ignore source var, result := false end; -{$endif} +{$endif PUREPASCAL} function FindIniNameValue(P: PUTF8Char; UpperName: PAnsiChar): RawUTF8; var u, PBeg: PUTF8Char; by4: cardinal; - table: {$ifdef CPUX86}TNormTable absolute NormToUpperAnsi7{$else}PNormTable{$endif}; + table: {$ifdef CPUX86NOTPIC}TNormTable absolute NormToUpperAnsi7{$else}PNormTable{$endif}; begin // expect UpperName as 'NAME=' if (P<>nil) and (P^<>'[') and (UpperName<>nil) then begin - {$ifndef CPUX86}table := @NormToUpperAnsi7;{$endif} + {$ifndef CPUX86NOTPIC}table := @NormToUpperAnsi7;{$endif} PBeg := nil; u := P; repeat @@ -29775,7 +29106,6 @@ procedure UpdateIniEntry(var Content: RawUTF8; const Section,Name,Value: RawUTF8 i, UpperNameLength: PtrInt; V: RawUTF8; UpperSection, UpperName: array[byte] of AnsiChar; - // possible GPF if length(Section/Name)>255, but should be short const in code label Sec; begin UpperNameLength := length(Name); @@ -29834,7 +29164,7 @@ function StringFromFile(const FileName: TFileName; HasNoSize: boolean): RawByteS if Read<=0 then break; SetLength(result,Size+Read); - MoveFast(tmp,PByteArray(result)^[Size],Read); + {$ifdef FPC}Move{$else}MoveFast{$endif}(tmp,PByteArray(result)^[Size],Read); inc(Size,Read); until false; end else begin @@ -30104,35 +29434,36 @@ function FileInfoByHandle(aFileHandle: THandle; out FileId, FileSize, begin {$ifdef MSWINDOWS} result := GetFileInformationByHandle(aFileHandle,lp); - if result then begin - LastWriteAccess := FileTimeToUnixMSTime(lp.ftLastWriteTime); - FileCreateDateTime := FileTimeToUnixMSTime(lp.ftCreationTime); - lastreadaccess := FileTimeToUnixMSTime(lp.ftLastAccessTime); - PInt64Rec(@FileSize).lo := lp.nFileSizeLow; - PInt64Rec(@FileSize).hi := lp.nFileSizeHigh; - PInt64Rec(@FileId).lo := lp.nFileIndexLow; - PInt64Rec(@FileId).hi := lp.nFileIndexHigh; + if not result then + exit; + LastWriteAccess := FileTimeToUnixMSTime(lp.ftLastWriteTime); + FileCreateDateTime := FileTimeToUnixMSTime(lp.ftCreationTime); + lastreadaccess := FileTimeToUnixMSTime(lp.ftLastAccessTime); + PInt64Rec(@FileSize).lo := lp.nFileSizeLow; + PInt64Rec(@FileSize).hi := lp.nFileSizeHigh; + PInt64Rec(@FileId).lo := lp.nFileIndexLow; + PInt64Rec(@FileId).hi := lp.nFileIndexHigh; {$else} - r := {$ifdef FPC}FpFStat{$else}fstat64{$endif}(aFileHandle, lp); + r := {$ifdef FPC}FpFStat{$else}fstat64{$endif}(aFileHandle, lp); result := r >= 0; - if result then begin - FileId := lp.st_ino; - FileSize := lp.st_size; - lastreadaccess := lp.st_atime * MSecsPerSec; - LastWriteAccess := lp.st_mtime * MSecsPerSec; - {$ifdef OPENBSD} - if (lp.st_birthtime <> 0) and (lp.st_birthtime < lp.st_ctime) then - lp.st_ctime:= lp.st_birthtime; - {$endif} - FileCreateDateTime := lp.st_ctime * MSecsPerSec; + if not result then + exit; + FileId := lp.st_ino; + FileSize := lp.st_size; + lastreadaccess := lp.st_atime * MSecsPerSec; + LastWriteAccess := lp.st_mtime * MSecsPerSec; + {$ifdef OPENBSD} + if (lp.st_birthtime <> 0) and (lp.st_birthtime < lp.st_ctime) then + lp.st_ctime:= lp.st_birthtime; + {$endif} + FileCreateDateTime := lp.st_ctime * MSecsPerSec; {$endif MSWINDOWS} - if LastWriteAccess <> 0 then - if (FileCreateDateTime = 0) or (FileCreateDateTime > LastWriteAccess) then - FileCreateDateTime:= LastWriteAccess; - if lastreadaccess <> 0 then - if (FileCreateDateTime = 0) or (FileCreateDateTime > lastreadaccess) then - FileCreateDateTime:= lastreadaccess; - end; + if LastWriteAccess <> 0 then + if (FileCreateDateTime = 0) or (FileCreateDateTime > LastWriteAccess) then + FileCreateDateTime:= LastWriteAccess; + if lastreadaccess <> 0 then + if (FileCreateDateTime = 0) or (FileCreateDateTime > lastreadaccess) then + FileCreateDateTime:= lastreadaccess; end; function FileAgeToDateTime(const FileName: TFileName): TDateTime; @@ -30276,7 +29607,7 @@ procedure TFindFiles.FromSearchRec(const Directory: TFileName; const F: TSearchR {$ifdef MSWINDOWS} {$ifdef HASINLINE} // FPC or Delphi 2006+ Size := F.Size; - {$else} // F.Size was limited to 32 bits on older Delphi + {$else} // F.Size was limited to 32-bit on older Delphi Size := F.FindData.nFileSizeLow or Int64(F.FindData.nFileSizeHigh) shl 32; {$endif} {$else} @@ -30322,7 +29653,7 @@ function FindFiles(const Directory,Mask,IgnoreFileName: TFileName; if SearchRecValidFile(F) and ((IgnoreFileName='') or (AnsiCompareFileName(F.Name,IgnoreFileName)<>0)) then begin if n=length(result) then - SetLength(result,n+n shr 3+8); + SetLength(result,NextGrow(n)); if IncludesDir then result[n].FromSearchRec(Dir,F) else result[n].FromSearchRec('',F); @@ -30356,7 +29687,7 @@ function EnsureDirectoryExists(const Directory: TFileName; if not CreateDir(result) then if not RaiseExceptionOnCreationFailure then result := '' else - raise ESynException.CreateUTF8('Impossible to create "%" folder',[Directory]); + raise ESynException.CreateUTF8('Impossible to create folder %',[result]); end; var @@ -30370,10 +29701,23 @@ function TemporaryFileName: TFileName; TemporaryFileNameRandom := Random32; repeat // thread-safe unique file name generation FormatString('%%_%.tmp',[folder,ExeVersion.ProgramName, - CardinalToHexShort(InterlockedIncrement(TemporaryFileNameRandom))], string(result)); + CardinalToHexShort(InterlockedIncrement(TemporaryFileNameRandom))],string(result)); until not FileExists(result); end; +function IsDirectoryWritable(const Directory: TFileName): boolean; +var fn: TFileName; +begin + fn := ExcludeTrailingPathDelimiter(Directory); + result := {$ifndef DELPHI5OROLDER}not FileIsReadOnly{$else}DirectoryExists{$endif}(fn); + if not result then + exit; + fn := FormatString('%%.%%',[fn,PathDelim,CardinalToHexShort(integer(GetCurrentThreadID)), + BinToBase64uriShort(@ExeVersion.Hash,SizeOf(ExeVersion.Hash))]); + result := FileFromString('tobedeleted',fn); // actually try to write something + DeleteFile(fn); +end; + {$ifdef DELPHI5OROLDER} function DirectoryExists(const Directory: string): boolean; @@ -30585,7 +29929,7 @@ procedure AddInteger(var Values: TIntegerDynArray; var ValuesCount: integer; Value: integer); begin if ValuesCount=length(Values) then - SetLength(Values,ValuesCount+256+ValuesCount shr 3); + SetLength(Values,NextGrow(ValuesCount)); Values[ValuesCount] := Value; inc(ValuesCount); end; @@ -30598,37 +29942,61 @@ function AddInteger(var Values: TIntegerDynArray; var ValuesCount: integer; exit; end; if ValuesCount=length(Values) then - SetLength(Values,ValuesCount+256+ValuesCount shr 3); + SetLength(Values,NextGrow(ValuesCount)); Values[ValuesCount] := Value; inc(ValuesCount); - result := true + result := true; +end; + +function AddInteger(var Values: TIntegerDynArray; const Another: TIntegerDynArray): PtrInt; +var v,a: PtrInt; +begin + v := length(Values); + a := length(Another); + if a>0 then begin + SetLength(Values,v+a); + {$ifdef FPC}Move{$else}MoveFast{$endif}(Another[0],Values[v],a*SizeOf(Integer)); + end; + result := v+a; end; -function AddWord(var Values: TWordDynArray; var ValuesCount: integer; Value: Word): integer; +function AddWord(var Values: TWordDynArray; var ValuesCount: integer; Value: Word): PtrInt; begin result := ValuesCount; if result=length(Values) then - SetLength(Values,result+32+result shr 3); + SetLength(Values,NextGrow(result)); Values[result] := Value; inc(ValuesCount); end; -function AddInt64(var Values: TInt64DynArray; var ValuesCount: integer; Value: Int64): integer; +function AddInt64(var Values: TInt64DynArray; var ValuesCount: integer; Value: Int64): PtrInt; begin result := ValuesCount; if result=length(Values) then - SetLength(Values,result+256+result shr 3); + SetLength(Values,NextGrow(result)); Values[result] := Value; inc(ValuesCount); end; -function AddInt64(var Values: TInt64DynArray; Value: Int64): integer; +function AddInt64(var Values: TInt64DynArray; Value: Int64): PtrInt; begin result := length(Values); SetLength(Values,result+1); Values[result] := Value; end; +function AddInt64(var Values: TInt64DynArray; const Another: TInt64DynArray): PtrInt; +var v,a: PtrInt; +begin + v := length(Values); + a := length(Another); + if a>0 then begin + SetLength(Values,v+a); + {$ifdef FPC}Move{$else}MoveFast{$endif}(Another[0],Values[v],a*SizeOf(Int64)); + end; + result := v+a; +end; + procedure AddInt64Sorted(var Values: TInt64DynArray; Value: Int64); var last: integer; begin @@ -30641,7 +30009,7 @@ procedure AddInt64Sorted(var Values: TInt64DynArray; Value: Int64); end; end; -function AddInt64Once(var Values: TInt64DynArray; Value: Int64): integer; +function AddInt64Once(var Values: TInt64DynArray; Value: Int64): PtrInt; begin result := Int64ScanIndex(pointer(Values),length(Values),Value); if result<0 then @@ -30656,7 +30024,7 @@ procedure DeleteWord(var Values: TWordDynArray; Index: PtrInt); exit; // wrong Index dec(n); if n>Index then - MoveFast(Values[Index+1],Values[Index],(n-Index)*SizeOf(Word)); + {$ifdef FPC}Move{$else}MoveFast{$endif}(Values[Index+1],Values[Index],(n-Index)*SizeOf(Word)); SetLength(Values,n); end; @@ -30668,7 +30036,7 @@ procedure DeleteInteger(var Values: TIntegerDynArray; Index: PtrInt); exit; // wrong Index dec(n); if n>Index then - MoveFast(Values[Index+1],Values[Index],(n-Index)*SizeOf(Integer)); + {$ifdef FPC}Move{$else}MoveFast{$endif}(Values[Index+1],Values[Index],(n-Index)*SizeOf(Integer)); SetLength(Values,n); end; @@ -30680,7 +30048,7 @@ procedure DeleteInteger(var Values: TIntegerDynArray; var ValuesCount: Integer; exit; // wrong Index dec(n,Index+1); if n>0 then - MoveFast(Values[Index+1],Values[Index],n*SizeOf(Integer)); + {$ifdef FPC}Move{$else}MoveFast{$endif}(Values[Index+1],Values[Index],n*SizeOf(Integer)); dec(ValuesCount); end; @@ -30692,7 +30060,7 @@ procedure DeleteInt64(var Values: TInt64DynArray; Index: PtrInt); exit; // wrong Index dec(n); if n>Index then - MoveFast(Values[Index+1],Values[Index],(n-Index)*SizeOf(Int64)); + {$ifdef FPC}Move{$else}MoveFast{$endif}(Values[Index+1],Values[Index],(n-Index)*SizeOf(Int64)); SetLength(Values,n); end; @@ -30704,12 +30072,12 @@ procedure DeleteInt64(var Values: TInt64DynArray; var ValuesCount: Integer; Inde exit; // wrong Index dec(n,Index+1); if n>0 then - MoveFast(Values[Index+1],Values[Index],n*SizeOf(Int64)); + {$ifdef FPC}Move{$else}MoveFast{$endif}(Values[Index+1],Values[Index],n*SizeOf(Int64)); dec(ValuesCount); end; procedure ExcludeInteger(var Values, Excluded: TIntegerDynArray; ExcludedSortSize: integer); -var i,v,x,n: integer; +var i,v,x,n: PtrInt; begin if (Values=nil) or (Excluded=nil) then exit; // nothing to exclude @@ -30736,8 +30104,39 @@ procedure ExcludeInteger(var Values, Excluded: TIntegerDynArray; ExcludedSortSiz SetLength(Values,n); end; +procedure IncludeInteger(var Values, Included: TIntegerDynArray; + IncludedSortSize: Integer); +var i,v,x,n: PtrInt; +begin + if (Values=nil) or (Included=nil) then begin + Values := nil; + exit; + end; + v := length(Values); + n := 0; + x := Length(Included); + if (x>IncludedSortSize) or (v>IncludedSortSize) then begin // sort if worth it + dec(x); + QuickSortInteger(pointer(Included),0,x); + for i := 0 to v-1 do + if FastFindIntegerSorted(pointer(Included),x,Values[i])>=0 then begin + if n<>i then + Values[n] := Values[i]; + inc(n); + end; + end else + for i := 0 to v-1 do + if IntegerScanExists(pointer(Included),x,Values[i]) then begin + if n<>i then + Values[n] := Values[i]; + inc(n); + end; + if n<>v then + SetLength(Values,n); +end; + procedure ExcludeInt64(var Values, Excluded: TInt64DynArray; ExcludedSortSize: Integer); -var i,v,x,n: integer; +var i,v,x,n: PtrInt; begin if (Values=nil) or (Excluded=nil) then exit; // nothing to exclude @@ -30764,6 +30163,37 @@ procedure ExcludeInt64(var Values, Excluded: TInt64DynArray; ExcludedSortSize: I SetLength(Values,n); end; +procedure IncludeInt64(var Values, Included: TInt64DynArray; + IncludedSortSize: integer); +var i,v,x,n: PtrInt; +begin + if (Values=nil) or (Included=nil) then begin + Values := nil; + exit; + end; + v := length(Values); + n := 0; + x := Length(Included); + if (x>IncludedSortSize) or (v>IncludedSortSize) then begin // sort if worth it + dec(x); + QuickSortInt64(pointer(Included),0,x); + for i := 0 to v-1 do + if FastFindInt64Sorted(pointer(Included),x,Values[i])>=0 then begin + if n<>i then + Values[n] := Values[i]; + inc(n); + end; + end else + for i := 0 to v-1 do + if Int64ScanExists(pointer(Included),x,Values[i]) then begin + if n<>i then + Values[n] := Values[i]; + inc(n); + end; + if n<>v then + SetLength(Values,n); +end; + procedure DeduplicateInteger(var Values: TIntegerDynArray); begin DeduplicateInteger(Values, length(Values)); @@ -30857,7 +30287,7 @@ procedure CopyInteger(const Source: TIntegerDynArray; out Dest: TIntegerDynArray begin n := length(Source); SetLength(Dest,n); - MoveFast(Source[0],Dest[0],n*SizeOf(Integer)); + {$ifdef FPC}Move{$else}MoveFast{$endif}(Source[0],Dest[0],n*SizeOf(Integer)); end; procedure CopyInt64(const Source: TInt64DynArray; out Dest: TInt64DynArray); @@ -30865,7 +30295,7 @@ procedure CopyInt64(const Source: TInt64DynArray; out Dest: TInt64DynArray); begin n := length(Source); SetLength(Dest,n); - MoveFast(Source[0],Dest[0],n*SizeOf(Int64)); + {$ifdef FPC}Move{$else}MoveFast{$endif}(Source[0],Dest[0],n*SizeOf(Int64)); end; function MaxInteger(const Values: TIntegerDynArray; ValuesCount, MaxStart: integer): Integer; @@ -30987,7 +30417,7 @@ function IntegerDynArrayToCSV(Values: PIntegerArray; ValuesCount: integer; SetLength(result,Len); P := pointer(result); if Prefix<>'' then begin - MoveFast(pointer(Prefix)^,P^,length(Prefix)); + {$ifdef FPC}Move{$else}MoveFast{$endif}(pointer(Prefix)^,P^,length(Prefix)); inc(P,length(Prefix)); end; for i := 0 to ValuesCount do begin @@ -30995,7 +30425,7 @@ function IntegerDynArrayToCSV(Values: PIntegerArray; ValuesCount: integer; PWord(P)^ := ord(':')+ord('(')shl 8; inc(P,2); end; - MoveFast(ints[i][1],P^,ord(ints[i][0])); + {$ifdef FPC}Move{$else}MoveFast{$endif}(ints[i][1],P^,ord(ints[i][0])); inc(P,ord(ints[i][0])); if InlinedValue then begin PWord(P)^ := ord(')')+ord(':')shl 8; @@ -31003,7 +30433,7 @@ function IntegerDynArrayToCSV(Values: PIntegerArray; ValuesCount: integer; end; end; if Suffix<>'' then - MoveFast(pointer(Suffix)^,P^,length(Suffix)); + {$ifdef FPC}Move{$else}MoveFast{$endif}(pointer(Suffix)^,P^,length(Suffix)); finally tmpbuf.Done; end; @@ -31045,7 +30475,7 @@ function Int64DynArrayToCSV(Values: PInt64Array; ValuesCount: integer; SetLength(result,Len); P := pointer(result); if Prefix<>'' then begin - MoveFast(pointer(Prefix)^,P^,length(Prefix)); + {$ifdef FPC}Move{$else}MoveFast{$endif}(pointer(Prefix)^,P^,length(Prefix)); inc(P,length(Prefix)); end; int := tmp.buf; @@ -31055,7 +30485,7 @@ function Int64DynArrayToCSV(Values: PInt64Array; ValuesCount: integer; inc(P,2); end; L := int^.Len; - MoveFast(PAnsiChar(int)[21-L],P^,L); + {$ifdef FPC}Move{$else}MoveFast{$endif}(PAnsiChar(int)[21-L],P^,L); inc(P,L); if InlinedValue then begin PWord(P)^ := ord(')')+ord(':')shl 8; @@ -31069,7 +30499,7 @@ function Int64DynArrayToCSV(Values: PInt64Array; ValuesCount: integer; dec(ValuesCount); until false; if Suffix<>'' then - MoveFast(pointer(Suffix)^,P^,length(Suffix)); + {$ifdef FPC}Move{$else}MoveFast{$endif}(pointer(Suffix)^,P^,length(Suffix)); finally tmp.Done; end; @@ -31164,7 +30594,7 @@ function PtrUIntScanIndex(P: PPtrUIntArray; Count: PtrInt; Value: PtrUInt): PtrI function WordScanIndex(P: PWordArray; Count: PtrInt; Value: word): integer; begin {$ifdef FPC} - result := IndexWord(P^,Count,Value); + result := IndexWord(P^,Count,Value); // will use fast FPC SSE version {$else} for result := 0 to Count-1 do if P^[result]=Value then @@ -31191,10 +30621,16 @@ procedure QuickSortInteger(ID: PIntegerArray; L,R: PtrInt); inc(I); dec(J); end; until I > J; - if L < J then - QuickSortInteger(ID,L,J); - L := I; - until I >= R; + if J - L < R - I then begin // use recursion only for smaller range + if L < J then + QuickSortInteger(ID, L, J); + L := I; + end else begin + if I < R then + QuickSortInteger(ID, I, R); + R := J; + end; + until L >= R; end; procedure QuickSortInteger(var ID: TIntegerDynArray); @@ -31221,10 +30657,16 @@ procedure QuickSortInteger(ID,CoValues: PIntegerArray; L,R: PtrInt); inc(I); dec(J); end; until I > J; - if L < J then - QuickSortInteger(ID,CoValues,L,J); - L := I; - until I >= R; + if J - L < R - I then begin // use recursion only for smaller range + if L < J then + QuickSortInteger(ID, CoValues, L, J); + L := I; + end else begin + if I < R then + QuickSortInteger(ID, CoValues, I, R); + R := J; + end; + until L >= R; end; procedure QuickSortWord(ID: PWordArray; L, R: PtrInt); @@ -31245,10 +30687,16 @@ procedure QuickSortWord(ID: PWordArray; L, R: PtrInt); inc(I); dec(J); end; until I > J; - if L < J then - QuickSortWord(ID,L,J); - L := I; - until I >= R; + if J - L < R - I then begin // use recursion only for smaller range + if L < J then + QuickSortWord(ID, L, J); + L := I; + end else begin + if I < R then + QuickSortWord(ID, I, R); + R := J; + end; + until L >= R; end; procedure QuickSortInt64(ID: PInt64Array; L, R: PtrInt); @@ -31274,10 +30722,16 @@ procedure QuickSortInt64(ID: PInt64Array; L, R: PtrInt); inc(I); dec(J); end; until I > J; - if L < J then - QuickSortInt64(ID,L,J); - L := I; - until I >= R; + if J - L < R - I then begin // use recursion only for smaller range + if L < J then + QuickSortInt64(ID, L, J); + L := I; + end else begin + if I < R then + QuickSortInt64(ID, I, R); + R := J; + end; + until L >= R; end; procedure QuickSortQWord(ID: PQWordArray; L, R: PtrInt); @@ -31303,10 +30757,16 @@ procedure QuickSortQWord(ID: PQWordArray; L, R: PtrInt); inc(I); dec(J); end; until I > J; - if L < J then - QuickSortQWord(ID,L,J); - L := I; - until I >= R; + if J - L < R - I then begin // use recursion only for smaller range + if L < J then + QuickSortQWord(ID, L, J); + L := I; + end else begin + if I < R then + QuickSortQWord(ID, I, R); + R := J; + end; + until L >= R; end; procedure QuickSortInt64(ID,CoValues: PInt64Array; L, R: PtrInt); @@ -31333,10 +30793,16 @@ procedure QuickSortInt64(ID,CoValues: PInt64Array; L, R: PtrInt); inc(I); dec(J); end; until I > J; - if L < J then - QuickSortInt64(ID,L,J); - L := I; - until I >= R; + if J - L < R - I then begin // use recursion only for smaller range + if L < J then + QuickSortInt64(ID, CoValues, L, J); + L := I; + end else begin + if I < R then + QuickSortInt64(ID, CoValues, I, R); + R := J; + end; + until L >= R; end; procedure QuickSortPtrInt(P: PPtrIntArray; L, R: PtrInt); @@ -31404,7 +30870,7 @@ procedure CopyAndSortInteger(Values: PIntegerArray; ValuesCount: integer; begin if ValuesCount>length(Dest) then SetLength(Dest,ValuesCount); - MoveFast(Values^[0],Dest[0],ValuesCount*SizeOf(Integer)); + {$ifdef FPC}Move{$else}MoveFast{$endif}(Values^[0],Dest[0],ValuesCount*SizeOf(Integer)); QuickSortInteger(pointer(Dest),0,ValuesCount-1); end; @@ -31413,7 +30879,7 @@ procedure CopyAndSortInt64(Values: PInt64Array; ValuesCount: integer; begin if ValuesCount>length(Dest) then SetLength(Dest,ValuesCount); - MoveFast(Values^[0],Dest[0],ValuesCount*SizeOf(Int64)); + {$ifdef FPC}Move{$else}MoveFast{$endif}(Values^[0],Dest[0],ValuesCount*SizeOf(Int64)); QuickSortInt64(pointer(Dest),0,ValuesCount-1); end; @@ -31457,21 +30923,26 @@ function FastFindInt64Sorted(P: PInt64Array; R: PtrInt; const Value: Int64): Ptr if 0<=R then repeat result := (L + R) shr 1; - {$ifdef CPUX86} // circumvent Int64 comparison slowness + {$ifndef CPUX86} + if P^[result]=Value then + exit else + if P^[result]nil then SetLength(CoValues^,n); @@ -31570,9 +31046,9 @@ function InsertInteger(var Values: TIntegerDynArray; var ValuesCount: integer; n := ValuesCount; if PtrUInt(result)nil then - MoveFast(CoValues^[result],CoValues^[result+1],n); + {$ifdef FPC}Move{$else}MoveFast{$endif}(CoValues^[result],CoValues^[result+1],n); end else result := n; Values[result] := Value; @@ -31924,16 +31400,16 @@ procedure SetInt64(P: PUTF8Char; var result: Int64); c := byte(P^)-48; if c>9 then exit; - Int64Rec(result).Lo := c; + PCardinal(@result)^ := c; inc(P); - repeat // fast 32 bit loop + repeat // fast 32-bit loop c := byte(P^)-48; if c>9 then break else - Int64Rec(result).Lo := Int64Rec(result).Lo*10+c; + PCardinal(@result)^ := PCardinal(@result)^*10+c; inc(P); - if Int64Rec(result).Lo>=high(cardinal)div 10 then begin - repeat // 64 bit loop + if PCardinal(@result)^>=high(cardinal)div 10 then begin + repeat // 64-bit loop c := byte(P^)-48; if c>9 then break; @@ -31967,16 +31443,16 @@ procedure SetQWord(P: PUTF8Char; var result: QWord); c := byte(P^)-48; if c>9 then exit; - Int64Rec(result).Lo := c; + PCardinal(@result)^ := c; inc(P); - repeat // fast 32 bit loop + repeat // fast 32-bit loop c := byte(P^)-48; if c>9 then break else - Int64Rec(result).Lo := Int64Rec(result).Lo*10+c; + PCardinal(@result)^ := PCardinal(@result)^*10+c; inc(P); - if Int64Rec(result).Lo>=high(cardinal)div 10 then begin - repeat // 64 bit loop + if PCardinal(@result)^>=high(cardinal)div 10 then begin + repeat // 64-bit loop c := byte(P^)-48; if c>9 then break; @@ -32037,19 +31513,19 @@ function GetInt64(P: PUTF8Char; var err: integer): Int64; c := byte(P^)-48; if c>9 then exit; - Int64Rec(result).Lo := c; + PCardinal(@result)^ := c; inc(P); - repeat // fast 32 bit loop + repeat // fast 32-bit loop c := byte(P^); if c<>0 then begin dec(c,48); inc(err); if c>9 then exit; - Int64Rec(result).Lo := Int64Rec(result).Lo*10+c; + PCardinal(@result)^ := PCardinal(@result)^*10+c; inc(P); - if Int64Rec(result).Lo>=high(cardinal)div 10 then begin - repeat // 64 bit loop + if PCardinal(@result)^>=high(cardinal)div 10 then begin + repeat // 64-bit loop c := byte(P^); if c=0 then begin err := 0; // conversion success without error @@ -32082,30 +31558,44 @@ function GetInt64(P: PUTF8Char; var err: integer): Int64; {$endif} function GetQWord(P: PUTF8Char; var err: integer): QWord; -var c: cardinal; +var c: PtrUInt; begin - err := 0; + err := 1; // error result := 0; if P=nil then exit; while (P^<=' ') and (P^<>#0) do inc(P); - inc(err); c := byte(P^)-48; if c>9 then exit; - Int64Rec(result).Lo := c; + {$ifdef CPU64} + result := c; inc(P); - repeat // fast 32 bit loop + repeat + c := byte(P^); + if c=0 then + break; + dec(c,48); + if c>9 then + exit; + result := result*10+c; + inc(P); + until false; + err := 0; // success + {$else} + PByte(@result)^ := c; + inc(P); + repeat // fast 32-bit loop c := byte(P^); if c<>0 then begin dec(c,48); inc(err); if c>9 then exit; - Int64Rec(result).Lo := Int64Rec(result).Lo*10+c; + PCardinal(@result)^ := PCardinal(@result)^*10+c; inc(P); - if Int64Rec(result).Lo>=high(cardinal)div 10 then begin - repeat // 64 bit loop + if PCardinal(@result)^>=high(cardinal)div 10 then begin + repeat // 64-bit loop c := byte(P^); if c=0 then begin err := 0; // conversion success without error @@ -32130,6 +31620,7 @@ function GetQWord(P: PUTF8Char; var err: integer): QWord; break; end; until false; + {$endif CPU64} end; function GetExtended(P: PUTF8Char): TSynExtended; @@ -32140,129 +31631,123 @@ function GetExtended(P: PUTF8Char): TSynExtended; result := 0; end; -{$ifdef PUREPASCAL} - {$define GETEXTENDEDPASCAL} -{$endif} -{$ifdef FPC} - {$define GETEXTENDEDPASCAL} -{$endif} -{$ifdef PIC} - {$define GETEXTENDEDPASCAL} -{$endif} +function HugePower10(exponent: integer): TSynExtended; {$ifdef HASINLINE}inline;{$endif} +var pow10: TSynExtended; +begin + result := 1.0; + if exponent<0 then begin + pow10 := 0.1; + exponent := -exponent; + end else + pow10 := 10; + repeat + while exponent and 1=0 do begin + exponent := exponent shr 1; + pow10 := sqr(pow10); + end; + result := result*pow10; + dec(exponent); + until exponent=0; +end; function GetExtended(P: PUTF8Char; out err: integer): TSynExtended; -// inspired by ValExt_JOH_PAS_8_a and ValExt_JOH_IA32_8_a by John O'Harrow -{$ifdef GETEXTENDEDPASCAL} - const POW10: array[-31..31] of TSynExtended = ( - 1E-31,1E-30,1E-29,1E-28,1E-27,1E-26,1E-25,1E-24,1E-23,1E-22,1E-21,1E-20, - 1E-19,1E-18,1E-17,1E-16,1E-15,1E-14,1E-13,1E-12,1E-11,1E-10,1E-9,1E-8,1E-7, - 1E-6,1E-5,1E-4,1E-3,1E-2,1E-1,1E0,1E1,1E2,1E3,1E4,1E5,1E6,1E7,1E8,1E9,1E10, - 1E11,1E12,1E13,1E14,1E15,1E16,1E17,1E18,1E19,1E20,1E21,1E22,1E23,1E24,1E25, - 1E26,1E27,1E28,1E29,1E30,1E31); - function IntPower(Exponent: Integer): TSynExtended; - var Y: Cardinal; - LBase: Int64; - begin - Y := abs(Exponent); - LBase := 10; - result := 1.0; - repeat - while not odd(Y) do begin - Y := Y shr 1; - LBase := LBase*LBase; - end; - dec(Y); - result := result*LBase; - until Y=0; - if Exponent<0 then - result := 1.0/result; - end; -var Digits, ExpValue: PtrInt; - Ch: cardinal; - flags: set of (Neg, NegExp, Valid); +{$ifndef CPU32DELPHI} // inspired from ValExt_JOH_PAS_8_a by John O'Harrow +const POW10: array[-31..31] of TSynExtended = ( + 1E-31,1E-30,1E-29,1E-28,1E-27,1E-26,1E-25,1E-24,1E-23,1E-22,1E-21,1E-20, + 1E-19,1E-18,1E-17,1E-16,1E-15,1E-14,1E-13,1E-12,1E-11,1E-10,1E-9,1E-8,1E-7, + 1E-6,1E-5,1E-4,1E-3,1E-2,1E-1,1E0,1E1,1E2,1E3,1E4,1E5,1E6,1E7,1E8,1E9,1E10, + 1E11,1E12,1E13,1E14,1E15,1E16,1E17,1E18,1E19,1E20,1E21,1E22,1E23,1E24,1E25, + 1E26,1E27,1E28,1E29,1E30,1E31); +var digits, exp: PtrInt; + ch: byte; + flags: set of (fNeg, fNegExp, fValid); U: PByte; // Delphi Win64 doesn't like if P^ is used directly -{$ifdef CPUX86} -const ten: double = 10.0; // fast copy on x87 stack +{$ifndef CPUX86}ten: TSynExtended;{$endif} // stored in (e.g. xmm2) register begin + {$ifndef CPUX86} ten := 10.0; {$endif} result := 0; -{$else} - ten: TSynExtended; // stored in a local floating-point (e.g. xmm) register -begin - ten := 10.0; - PInt64(@result)^ := 0; -{$endif} if P=nil then begin err := 1; exit; end; - flags := []; + byte(flags) := 0; U := pointer(P); - while U^=32 do - inc(U); - Ch := U^; - if Ch=ord('+') then + if P^=' ' then + repeat + inc(U) + until U^<>32; // trailing spaces + ch := U^; + if ch=ord('+') then inc(U) else - if Ch=ord('-') then begin + if ch=ord('-') then begin inc(U); - include(flags,Neg); + include(flags,fNeg); end; - while true do begin - Ch := U^; + repeat + ch := U^; inc(U); - if (Chord('9')) then + if (chord('9')) then break; - dec(Ch,ord('0')); - result := (result*ten)+Ch; - include(flags,Valid); - end; - Digits := 0; - if Ch=ord('.') then begin - while true do begin - Ch := U^; + dec(ch,ord('0')); + {$ifdef CPUX86} + result := (result*10.0)+ch; + {$else} + result := result*ten; // better FPC+Delphi64 code generation in two steps + result := result+ch; + {$endif} + include(flags,fValid); + until false; + digits := 0; + if ch=ord('.') then + repeat + ch := U^; inc(U); - if (Chord('9')) then begin - if not (Valid in flags) then // starts with '.' - if Ch=0 then - dec(U); // U='.' + if (chord('9')) then begin + if not(fValid in flags) then // starts with '.' + if ch=0 then + dec(U); // U^='.' break; end; - dec(Ch,ord('0')); - result := (result*ten)+Ch; - dec(Digits); - include(flags,Valid); - end; - end; - ExpValue := 0; - if (Ch=ord('E')) or (Ch=ord('e')) then begin - exclude(flags,Valid); - Ch := U^; - if Ch=ord('+') then + dec(ch,ord('0')); + {$ifdef CPUX86} + result := (result*10.0)+ch; + {$else} + result := result*ten; + result := result+ch; + {$endif} + dec(digits); + include(flags,fValid); + until false; + if (ch=ord('E')) or (ch=ord('e')) then begin + exp := 0; + exclude(flags,fValid); + ch := U^; + if ch=ord('+') then inc(U) else - if Ch=ord('-') then begin + if ch=ord('-') then begin inc(U); - include(flags,NegExp); + include(flags,fNegExp); end; - while true do begin - Ch := U^; + repeat + ch := U^; inc(U); - if (Chord('9')) then + if (chord('9')) then break; - dec(Ch,ord('0')); - ExpValue := (ExpValue*10)+PtrInt(Ch); - include(flags,Valid); - end; - if NegExp in flags then - ExpValue := -ExpValue; - end; - inc(Digits,ExpValue); - case Digits of - 0: ; - low(POW10)..-1,1..high(POW10): result := result*POW10[Digits]; - else result := result*IntPower(Digits); - end; - if Neg in flags then + dec(ch,ord('0')); + exp := (exp*10)+PtrInt(ch); + include(flags,fValid); + until false; + if fNegExp in flags then + dec(digits,exp) else + inc(digits,exp); + end; + if digits<>0 then + if (digits>=low(POW10)) and (digits<=high(POW10)) then + result := result*POW10[digits] else + result := result*HugePower10(digits); + if fNeg in flags then result := -result; - if (Valid in flags) and (Ch=0) then + if (fValid in flags) and (ch=0) then err := 0 else err := PUTF8Char(U)-P+1; end; @@ -32387,7 +31872,7 @@ function GetExtended(P: PUTF8Char; out err: integer): TSynExtended; fchs // yes. negate result in fpu jmp @exit // exit setting result code end; -{$endif} +{$endif CPU32DELPHI} function GetUTF8Char(P: PUTF8Char): cardinal; begin @@ -32476,16 +31961,16 @@ function IdemPCharWithoutWhiteSpace(p: PUTF8Char; up: PAnsiChar): boolean; function IdemPCharArray(p: PUTF8Char; const upArray: array of PAnsiChar): integer; var w: word; - tab: {$ifdef CPUX86}TNormTableByte absolute NormToUpperAnsi7{$else}PNormTableByte{$endif}; + tab: {$ifdef CPUX86NOTPIC}TNormTableByte absolute NormToUpperAnsi7{$else}PNormTableByte{$endif}; up: ^PAnsiChar; begin if p<>nil then begin - {$ifndef CPUX86}tab := @NormToUpperAnsi7;{$endif} // faster on PIC and x86_64 + {$ifndef CPUX86NOTPIC}tab := @NormToUpperAnsi7;{$endif} // faster on PIC and x86_64 w := tab[ord(p[0])]+tab[ord(p[1])]shl 8; up := @upArray[0]; for result := 0 to high(upArray) do if (PWord(up^)^=w) and - {$ifdef CPUX86}IdemPChar({$else}IdemPChar2(pointer(tab),{$endif}p+2,up^+2) then + {$ifdef CPUX86NOTPIC}IdemPChar({$else}IdemPChar2(pointer(tab),{$endif}p+2,up^+2) then exit else inc(up); end; @@ -32518,14 +32003,14 @@ function IdemPCharU(p, up: PUTF8Char): boolean; end; function EndWith(const text, upText: RawUTF8): boolean; -var o: integer; +var o: PtrInt; begin o := length(text)-length(upText); result := (o>=0) and IdemPChar(PUTF8Char(pointer(text))+o,pointer(upText)); end; function EndWithArray(const text: RawUTF8; const upArray: array of RawUTF8): integer; -var t,o: integer; +var t,o: PtrInt; begin t := length(text); if t>0 then @@ -32577,14 +32062,14 @@ function UpperCopy255BufPas(dest: PAnsiChar; source: PUTF8Char; sourceLen: PtrIn function UpperCopyWin255(dest: PWinAnsiChar; const source: RawUTF8): PWinAnsiChar; var i, L: integer; - tab: {$ifdef CPUX86}TNormTableByte absolute NormToUpperByte{$else}PNormTableByte{$endif}; + tab: {$ifdef CPUX86NOTPIC}TNormTableByte absolute NormToUpperByte{$else}PNormTableByte{$endif}; begin L := {$ifdef FPC}_LStrLen(source){$else}PInteger(PtrInt(source)-SizeOf(integer))^{$endif}; if L>0 then begin if L>250 then L := 250; // avoid buffer overflow result := dest+L; - {$ifndef CPUX86}tab := @NormToUpperByte;{$endif} // faster on PIC and x86_64 + {$ifndef CPUX86NOTPIC}tab := @NormToUpperByte;{$endif} // faster on PIC and x86_64 for i := 0 to L-1 do dest[i] := AnsiChar(tab[PByteArray(source)[i]]); end else @@ -32641,7 +32126,7 @@ function UTF8UpperCopy(Dest, Source: PUTF8Char; SourceChars: Cardinal): PUTF8Cha S := Source-1; // leave UTF-8 encoding untouched inc(Source,extra); inc(extra); - MoveFast(S^,Dest^,extra); + {$ifdef FPC}Move{$else}MoveFast{$endif}(S^,Dest^,extra); inc(Dest,extra); if (PtrUInt(Source) and 3=0) and (Sourcenil then @@ -32835,17 +32320,120 @@ function GotoNextLine(source: PUTF8Char): PUTF8Char; until false else result := source; end; -{$WARNINGS ON} +{$ifdef FPC}{$pop}{$else}{$WARNINGS ON}{$endif} + +function BufferLineLength(Text, TextEnd: PUTF8Char): PtrInt; +{$ifdef CPUX64} +{$ifdef FPC} nostackframe; assembler; asm {$else} asm .NOFRAME {$endif} +{$ifdef MSWINDOWS} // Win64 ABI to System-V ABI + push rsi + push rdi + mov rdi, rcx + mov rsi, rdx +{$endif}mov r8, rsi + sub r8, rdi // rdi=Text, rsi=TextEnd, r8=TextLen + jz @fail + mov ecx, edi + movdqa xmm0, [rip + @for10] + movdqa xmm1, [rip + @for13] + and rdi, -16 // check first aligned 16 bytes + and ecx, 15 // lower 4 bits indicate misalignment + movdqa xmm2, [rdi] + movdqa xmm3, xmm2 + pcmpeqb xmm2, xmm0 + pcmpeqb xmm3, xmm1 + por xmm3, xmm2 + pmovmskb eax, xmm3 + shr eax, cl // shift out unaligned bytes + test eax, eax + jz @main + bsf eax, eax + add rax, rcx + add rax, rdi + sub rax, rsi + jae @fail // don't exceed TextEnd + add rax, r8 // rax = TextFound - TextEnd + (TextEnd - Text) = offset +{$ifdef MSWINDOWS} + pop rdi + pop rsi +{$endif}ret +@main: add rdi, 16 + sub rdi, rsi + jae @fail + jmp @by16 +{$ifdef FPC} align 16 {$else} .align 16 {$endif} +@for10: dq $0a0a0a0a0a0a0a0a + dq $0a0a0a0a0a0a0a0a +@for13: dq $0d0d0d0d0d0d0d0d + dq $0d0d0d0d0d0d0d0d +@by16: movdqa xmm2, [rdi + rsi] // check 16 bytes per loop + movdqa xmm3, xmm2 + pcmpeqb xmm2, xmm0 + pcmpeqb xmm3, xmm1 + por xmm3, xmm2 + pmovmskb eax, xmm3 + test eax, eax + jnz @found + add rdi, 16 + jnc @by16 +@fail: mov rax, r8 // returns TextLen if no CR/LF found +{$ifdef MSWINDOWS} + pop rdi + pop rsi +{$endif}ret +@found: bsf eax, eax + add rax, rdi + jc @fail + add rax, r8 +{$ifdef MSWINDOWS} + pop rdi + pop rsi +{$endif} +end; +{$else} {$ifdef FPC}inline;{$endif} +var c: cardinal; +begin + result := 0; + dec(PtrInt(TextEnd),PtrInt(Text)); // compute TextLen + if TextEnd<>nil then + repeat + c := ord(Text[result]); + if c>13 then begin + inc(result); + if result>=PtrInt(TextEnd) then + break; + continue; + end; + if (c=10) or (c=13) then + break; + inc(result); + if result>=PtrInt(TextEnd) then + break; + until false; +end; +{$endif CPUX64} function GetLineSize(P,PEnd: PUTF8Char): PtrUInt; +var c: cardinal; begin + if PEnd=nil then + dec(PtrUInt(PEnd)); result := PtrUInt(P); if P<>nil then - if PEnd=nil then - while P^ in ANSICHARNOT01310 do - inc(P) else - while (P13 then begin inc(P); + if P>=PEnd then + break; + continue; + end; + if (c=0) or (c=10) or (c=13) then + break; + inc(P); + if P>=PEnd then + break; + until false; result := PtrUInt(P)-result; end; @@ -32869,17 +32457,31 @@ procedure GetNextItem(var P: PUTF8Char; Sep: AnsiChar; var result: RawUTF8); end; end; +procedure GetNextItem(var P: PUTF8Char; Sep, Quote: AnsiChar; var result: RawUTF8); +begin + if P=nil then + result := '' + else if P^=Quote then begin + P := UnQuoteSQLStringVar(P,result); + if P=nil then + result := '' + else if P^<>#0 then + inc(P); + end else + GetNextItem(P,Sep,result); +end; + procedure GetNextItemTrimed(var P: PUTF8Char; Sep: AnsiChar; var result: RawUTF8); var S,E: PUTF8Char; begin if (P=nil) or (Sep<=' ') then result := '' else begin - while (P^<=' ') and (P^<>#0) do inc(P); + while (P^<=' ') and (P^<>#0) do inc(P); // trim left S := P; while (S^<>#0) and (S^<>Sep) do inc(S); E := S; - while (E>P) and (E[-1] in [#1..' ']) do dec(E); + while (E>P) and (E[-1] in [#1..' ']) do dec(E); // trim right FastSetString(result,P,E-P); if S^<>#0 then P := S+1 else @@ -33084,11 +32686,11 @@ function CSVOfValue(const Value: RawUTF8; Count: cardinal; const Sep: RawUTF8): P := pointer(result); i := 1; repeat - MoveFast(Pointer(Value)^,P^,ValueLen); + {$ifdef FPC}Move{$else}MoveFast{$endif}(Pointer(Value)^,P^,ValueLen); inc(P,ValueLen); if i=Count then break; - MoveFast(Pointer(Sep)^,P^,SepLen); + {$ifdef FPC}Move{$else}MoveFast{$endif}(Pointer(Sep)^,P^,SepLen); inc(P,SepLen); inc(i); until false; @@ -33103,14 +32705,14 @@ procedure SetBitCSV(var Bits; BitsCount: integer; var P: PUTF8Char); if bit>=cardinal(BitsCount) then break; // avoid GPF if (P=nil) or (P^=',') then - SetBit(Bits,bit) else + SetBitPtr(@Bits,bit) else if P^='-' then begin inc(P); last := GetNextItemCardinalStrict(P)-1; // '0' marks end of list if last>=Cardinal(BitsCount) then exit; while bit<=last do begin - SetBit(Bits,bit); + SetBitPtr(@Bits,bit); inc(bit); end; end; @@ -33127,9 +32729,9 @@ function GetBitCSV(const Bits; BitsCount: integer): RawUTF8; result := ''; i := 0; while i0) and (L and 1=0) then + if not HexDisplayToBin(@tmp,@result,L shr 1) then + result := 0; +end; + function GetNextItemDouble(var P: PUTF8Char; Sep: AnsiChar): double; var tmp: TChar64; err: integer; @@ -33275,6 +32888,15 @@ function GetCSVItem(P: PUTF8Char; Index: PtrUInt; Sep: AnsiChar): RawUTF8; GetNextItem(P,Sep,result); end; +function GetUnQuoteCSVItem(P: PUTF8Char; Index: PtrUInt; Sep, Quote: AnsiChar): RawUTF8; +var i: PtrUInt; +begin + if P=nil then + result := '' else + for i := 0 to Index do + GetNextItem(P,Sep,Quote,result); +end; + function GetLastCSVItem(const CSV: RawUTF8; Sep: AnsiChar): RawUTF8; var i: integer; begin @@ -33418,13 +33040,13 @@ function RawUTF8ArrayToCSV(const Values: array of RawUTF8; const Sep: RawUTF8): repeat L := length(Values[i]); if L>0 then begin - MoveFast(pointer(Values[i])^,P^,L); + {$ifdef FPC}Move{$else}MoveFast{$endif}(pointer(Values[i])^,P^,L); inc(P,L); end; if i=high(Values) then Break; if seplen>0 then begin - MoveFast(pointer(Sep)^,P^,seplen); + {$ifdef FPC}Move{$else}MoveFast{$endif}(pointer(Sep)^,P^,seplen); inc(P,seplen); end; inc(i); @@ -33806,8 +33428,8 @@ function UrlDecodeNextNameValue(U: PUTF8Char; var Name,Value: RawUTF8): PUTF8Cha result := U+1; // jump '&' to let decode the next name=value pair end; -function UrlDecodeValue(U: PUTF8Char; Upper: PAnsiChar; var Value: RawUTF8; - Next: PPUTF8Char=nil): boolean; +function UrlDecodeValue(U: PUTF8Char; const Upper: RawUTF8; var Value: RawUTF8; + Next: PPUTF8Char): boolean; begin // UrlDecodeValue('select=%2A&where=LastName%3D%27M%C3%B4net%27','SELECT=',V,@U) // -> U^='where=...' and V='*' @@ -33817,9 +33439,9 @@ function UrlDecodeValue(U: PUTF8Char; Upper: PAnsiChar; var Value: RawUTF8; Next^ := U; exit; end; - if IdemPChar(U,Upper) then begin + if IdemPChar(U,pointer(Upper)) then begin result := true; - inc(U,StrLen(PUTF8Char(Upper))); + inc(U,length(Upper)); U := UrlDecodeNextValue(U,Value); end; if Next=nil then @@ -33830,8 +33452,8 @@ function UrlDecodeValue(U: PUTF8Char; Upper: PAnsiChar; var Value: RawUTF8; Next^ := U+1; // jump '&' end; -function UrlDecodeInteger(U: PUTF8Char; Upper: PAnsiChar; - var Value: integer; Next: PPUTF8Char=nil): boolean; +function UrlDecodeInteger(U: PUTF8Char; const Upper: RawUTF8; + var Value: integer; Next: PPUTF8Char): boolean; var V: PtrInt; SignNeg: boolean; begin @@ -33843,8 +33465,8 @@ function UrlDecodeInteger(U: PUTF8Char; Upper: PAnsiChar; Next^ := U; exit; end; - if IdemPChar(U,Upper) then begin - inc(U,StrLen(PUTF8Char(Upper))); + if IdemPChar(U,pointer(Upper)) then begin + inc(U,length(Upper)); if U^='-' then begin SignNeg := True; Inc(U); @@ -33870,7 +33492,8 @@ function UrlDecodeInteger(U: PUTF8Char; Upper: PAnsiChar; Next^ := U+1; // jump '&' end; -function UrlDecodeCardinal(U: PUTF8Char; Upper: PAnsiChar;var Value: Cardinal; Next: PPUTF8Char=nil): boolean; +function UrlDecodeCardinal(U: PUTF8Char; const Upper: RawUTF8; + var Value: Cardinal; Next: PPUTF8Char): boolean; var V: PtrInt; begin // UrlDecodeInteger('offset=20&where=LastName%3D%27M%C3%B4net%27','OFFSET=',O,@Next) @@ -33881,8 +33504,8 @@ function UrlDecodeCardinal(U: PUTF8Char; Upper: PAnsiChar;var Value: Cardinal; N Next^ := U; exit; end; - if IdemPChar(U,Upper) then begin - inc(U,StrLen(PUTF8Char(Upper))); + if IdemPChar(U,pointer(Upper)) then begin + inc(U,length(Upper)); if U^ in ['0'..'9'] then begin V := 0; repeat @@ -33901,21 +33524,21 @@ function UrlDecodeCardinal(U: PUTF8Char; Upper: PAnsiChar;var Value: Cardinal; N Next^ := U+1; // jump '&' end; -function UrlDecodeInt64(U: PUTF8Char; Upper: PAnsiChar; - var Value: Int64; Next: PPUTF8Char=nil): boolean; +function UrlDecodeInt64(U: PUTF8Char; const Upper: RawUTF8; + var Value: Int64; Next: PPUTF8Char): boolean; var tmp: RawUTF8; begin - result := UrlDecodeValue(U, Upper, tmp, Next); + result := UrlDecodeValue(U,Upper,tmp,Next); if result then SetInt64(pointer(tmp),Value); end; -function UrlDecodeExtended(U: PUTF8Char; Upper: PAnsiChar; +function UrlDecodeExtended(U: PUTF8Char; const Upper: RawUTF8; var Value: TSynExtended; Next: PPUTF8Char=nil): boolean; var tmp: RawUTF8; err: integer; begin - result := UrlDecodeValue(U, Upper, tmp, Next); + result := UrlDecodeValue(U,Upper,tmp,Next); if result then begin Value := GetExtended(pointer(tmp),err); if err<>0 then @@ -33923,12 +33546,12 @@ function UrlDecodeExtended(U: PUTF8Char; Upper: PAnsiChar; end; end; -function UrlDecodeDouble(U: PUTF8Char; Upper: PAnsiChar; var Value: double; +function UrlDecodeDouble(U: PUTF8Char; const Upper: RawUTF8; var Value: double; Next: PPUTF8Char=nil): boolean; var tmp: RawUTF8; err: integer; begin - result := UrlDecodeValue(U, Upper, tmp, Next); + result := UrlDecodeValue(U,Upper,tmp,Next); if result then begin Value := GetExtended(pointer(tmp),err); if err<>0 then @@ -34075,12 +33698,12 @@ procedure FillZero(var Values: TRawUTF8DynArray); procedure FillZero(var Values: TIntegerDynArray); begin - FillCharFast(Values[0],length(Values)*SizeOf(integer),0); + {$ifdef FPC}FillChar{$else}FillCharFast{$endif}(Values[0],length(Values)*SizeOf(integer),0); end; procedure FillZero(var Values: TInt64DynArray); begin - FillCharFast(Values[0],length(Values)*SizeOf(Int64),0); + {$ifdef FPC}FillChar{$else}FillCharFast{$endif}(Values[0],length(Values)*SizeOf(Int64),0); end; @@ -34103,28 +33726,28 @@ function Hash32(const Text: RawByteString): cardinal; result := Hash32(pointer(Text),length(Text)); end; -function Hash32(Data: pointer; Len: integer): cardinal; +function Hash32(Data: PCardinalArray; Len: integer): cardinal; var s1,s2: cardinal; - i: PtrInt; + i: integer; begin if Data<>nil then begin s1 := 0; s2 := 0; - for i := 1 to Len shr 4 do begin // 16 bytes (4 DWORD) by loop - aligned read - inc(s1,PCardinalArray(Data)^[0]); + for i := 1 to Len shr 4 do begin // 16 bytes (128-bit) loop - aligned read + inc(s1,Data[0]); inc(s2,s1); - inc(s1,PCardinalArray(Data)^[1]); + inc(s1,Data[1]); inc(s2,s1); - inc(s1,PCardinalArray(Data)^[2]); + inc(s1,Data[2]); inc(s2,s1); - inc(s1,PCardinalArray(Data)^[3]); + inc(s1,Data[3]); inc(s2,s1); - inc(PByte(Data),16); + Data := @Data[4]; end; for i := 1 to (Len shr 2)and 3 do begin // 4 bytes (DWORD) by loop - inc(s1,PCardinalArray(Data)^[0]); + inc(s1,Data[0]); inc(s2,s1); - inc(PByte(Data),4); + Data := @Data[1]; end; case Len and 3 of // remaining 0..3 bytes 1: inc(s1,PByte(Data)^); @@ -34137,91 +33760,6 @@ function Hash32(Data: pointer; Len: integer): cardinal; result := 0; end; -function GetBit(const Bits; aIndex: PtrInt): boolean; -{$ifdef CPUINTEL}{$ifdef CPU64}{$ifdef FPC}nostackframe;assembler;asm{$else}asm .noframe{$endif}{$else}asm{$endif} - bt [Bits], aIndex - sbb eax, eax - and eax, 1 -end; -{$else} -begin - result := TIntegerArray(Bits)[aIndex shr 5] and (1 shl (aIndex and 31)) <> 0; -end; -{$endif CPUINTEL} - -function GetAllBits(Bits: Cardinal; BitCount: Integer): boolean; -begin - if BitCount in [low(ALLBITS_CARDINAL)..high(ALLBITS_CARDINAL)] then - result := (Bits and ALLBITS_CARDINAL[BitCount])=ALLBITS_CARDINAL[BitCount] else - result := false; -end; - -procedure SetBit(var Bits; aIndex: PtrInt); -{$ifdef CPUINTEL}{$ifdef CPU64}{$ifdef FPC}nostackframe;assembler;asm{$else}asm .noframe{$endif}{$else}asm{$endif} - bts [Bits], aIndex -end; -{$else} -begin - TIntegerArray(Bits)[aIndex shr 5] := TIntegerArray(Bits)[aIndex shr 5] - or (1 shl (aIndex and 31)); -end; -{$endif CPUINTEL} - -procedure UnSetBit(var Bits; aIndex: PtrInt); -{$ifdef CPUINTEL}{$ifdef CPU64}{$ifdef FPC}nostackframe;assembler;asm{$else}asm .noframe{$endif}{$else}asm{$endif} - btr [Bits], aIndex -end; -{$else} -begin - PIntegerArray(@Bits)^[aIndex shr 5] := PIntegerArray(@Bits)^[aIndex shr 5] - and not (1 shl (aIndex and 31)); -end; -{$endif CPUINTEL} - -function GetBit64(const Bits: Int64; aIndex: PtrInt): boolean; -begin - result := aIndex in TBits64(Bits); -end; - -procedure SetBit64(var Bits: Int64; aIndex: PtrInt); -begin - include(PBits64(@Bits)^,aIndex); -end; - -procedure UnSetBit64(var Bits: Int64; aIndex: PtrInt); -begin - exclude(PBits64(@Bits)^,aIndex); -end; - -function GetBitsCount(const Bits; Count: PtrInt): integer; -const POPCNTDATA: array[0..15+4] of integer = (0,1,1,2,1,2,2,3,1,2,2,3,2,3,3,4,0,1,3,7); -var P: PByte; - v: PtrUInt; -{$ifdef CPUX86}tab: TIntegerArray absolute POPCNTDATA; -begin // not enough registers on this CPU -{$else}tab: PIntegerArray; -begin - tab := @POPCNTDATA; -{$endif CPUX86} - P := @Bits; - result := 0; - while Count>=8 do begin - dec(Count,8); - v := P^; - inc(result,tab[v and $f]); - inc(result,tab[v shr 4]); - inc(P); - end; - v := P^; - if Count>=4 then begin - dec(Count,4); - inc(result,tab[v and $f]); - v := v shr 4; - end; - if Count>0 then - inc(result,tab[v and tab[Count+16]]); -end; - procedure OrMemory(Dest,Source: PByteArray; size: PtrInt); begin while size>=SizeOf(PtrInt) do begin @@ -34283,6 +33821,7 @@ procedure AndMemory(Dest,Source: PByteArray; size: PtrInt); {$ifdef CPUX86} function xxHash32(crc: cardinal; P: PAnsiChar; len: integer): cardinal; +{$ifdef FPC}nostackframe; assembler;{$endif} asm xchg edx, ecx push ebp @@ -34384,7 +33923,7 @@ function xxHash32(crc: cardinal; P: PAnsiChar; len: integer): cardinal; {$ifdef CPUX64} function xxHash32(crc: cardinal; P: PAnsiChar; len: integer): cardinal; -asm +{$ifdef FPC}nostackframe; assembler; asm {$else} asm .noframe{$endif} {$ifdef LINUX} // crc=rdi P=rsi len=rdx mov r8, rdi mov rcx, rsi @@ -34571,8 +34110,7 @@ TRegisters = record {$ifdef CPU64} procedure GetCPUID(Param: Cardinal; var Registers: TRegisters); {$ifdef FPC}nostackframe; assembler; asm {$else} -asm // ecx=param, rdx=Registers (Linux: edi,rsi) - .noframe +asm .noframe // ecx=param, rdx=Registers (Linux: edi,rsi) {$endif FPC} {$ifdef win64} mov eax, ecx @@ -34600,8 +34138,7 @@ procedure GetCPUID(Param: Cardinal; var Registers: TRegisters); function UpperCopy255BufSSE42(dest: PAnsiChar; source: PUTF8Char; sourceLen: PtrInt): PAnsiChar; {$ifdef FPC}nostackframe; assembler; asm {$else} -asm // rcx=dest, rdx=source, r8=len (Linux: rdi,rsi,rdx) - .noframe +asm .noframe // rcx=dest, rdx=source, r8=len (Linux: rdi,rsi,rdx) {$endif FPC} {$ifdef win64} mov rax, rcx @@ -34655,8 +34192,7 @@ function UpperCopy255BufSSE42(dest: PAnsiChar; source: PUTF8Char; sourceLen: Ptr function crc32csse42(crc: cardinal; buf: PAnsiChar; len: cardinal): cardinal; {$ifdef FPC}nostackframe; assembler; asm {$else} -asm // ecx=crc, rdx=buf, r8=len (Linux: edi,rsi,rdx) - .noframe +asm .noframe // ecx=crc, rdx=buf, r8=len (Linux: edi,rsi,rdx) {$endif FPC} {$ifdef win64} mov eax, ecx @@ -34711,10 +34247,8 @@ function crc32csse42(crc: cardinal; buf: PAnsiChar; len: cardinal): cardinal; function StrLenSSE2(S: pointer): PtrInt; {$ifdef FPC}nostackframe; assembler; asm {$else} -asm // rcx=S (Linux: rdi) - .noframe -{$endif FPC} - // from GPL strlen64.asm by Agner Fog - www.agner.org/optimize +asm .noframe // rcx=S (Linux: rdi) +{$endif FPC} // from GPL strlen64.asm by Agner Fog - www.agner.org/optimize {$ifdef win64} mov rax, rcx // get pointer to string from rcx mov r8, rcx // copy pointer @@ -34725,10 +34259,11 @@ function StrLenSSE2(S: pointer): PtrInt; test rdi, rdi {$endif} jz @null // returns 0 if S=nil - // rax = s,ecx = 32 bits of s + // rax=s,ecx=32-bit of s pxor xmm0, xmm0 // set to zero and ecx, 15 // lower 4 bits indicate misalignment and rax, -16 // align pointer by 16 + // will never read outside a memory page boundary, so won't trigger GPF movdqa xmm1, [rax] // read from nearest preceding boundary pcmpeqb xmm1, xmm0 // compare 16 bytes with zero pmovmskb edx, xmm1 // get one bit for each byte result @@ -34800,7 +34335,7 @@ function StrCompSSE42(Str1, Str2: pointer): PtrInt; {$else} mov rax, rdi mov rdx, rsi - test rdi, rsi + test rdi, rsi // is one of Str1/Str2 nil ? {$endif} jz @n @ok: sub rax, rdx @@ -34835,39 +34370,70 @@ function StrCompSSE42(Str1, Str2: pointer): PtrInt; sub rax, rdx end; {$endif HASAESNI} - {$endif CPU64} {$endif CPUINTEL} procedure crcblocks(crc128, data128: PBlock128; count: integer); +var oneblock: procedure(crc128, data128: PBlock128); + i: integer; begin - {$ifdef CPUX86} - if (cfSSE42 in CpuFeatures) and (count>0) then - asm - mov ecx, crc128 - mov edx, data128 -@s: mov eax, dword ptr[ecx] - db $F2, $0F, $38, $F1, $02 - mov dword ptr[ecx], eax - mov eax, dword ptr[ecx + 4] - db $F2, $0F, $38, $F1, $42, $04 - mov dword ptr[ecx + 4], eax - mov eax, dword ptr[ecx + 8] - db $F2, $0F, $38, $F1, $42, $08 - mov dword ptr[ecx + 8], eax - mov eax, dword ptr[ecx + 12] - db $F2, $0F, $38, $F1, $42, $0C - mov dword ptr[ecx + 12], eax - add edx, 16 - dec count - jnz @s - end else - {$endif CPUX86} - while count>0 do begin - crcblock(crc128,data128); - inc(data128); - dec(count); - end; + if count>0 then + {$ifndef DISABLE_SSE42} + {$ifdef CPUX86} + if cfSSE42 in CpuFeatures then + asm + mov ecx, crc128 + mov edx, data128 + @s: mov eax, dword ptr[ecx] + db $F2, $0F, $38, $F1, $02 // crc32 eax, dword ptr [edx] + mov dword ptr[ecx], eax + mov eax, dword ptr[ecx + 4] + db $F2, $0F, $38, $F1, $42, $04 // crc32 eax, dword ptr [edx+4] + mov dword ptr[ecx + 4], eax + mov eax, dword ptr[ecx + 8] + db $F2, $0F, $38, $F1, $42, $08 // crc32 eax, dword ptr [edx+8] + mov dword ptr[ecx + 8], eax + mov eax, dword ptr[ecx + 12] + db $F2, $0F, $38, $F1, $42, $0C // crc32 eax, dword ptr [edx+12] + mov dword ptr[ecx + 12], eax + add edx, 16 + dec count + jnz @s + end else + {$endif CPUX86} + {$ifdef CPUX64} + {$ifdef FPC} // only FPC is able to compile such inlined asm block + if cfSSE42 in CpuFeatures then + asm + mov rax, data128 + mov rdx, crc128 + mov ecx, count + mov r8d, dword ptr [rdx] + mov r9d, dword ptr [rdx + 4] + mov r10d, dword ptr [rdx + 8] + mov r11d, dword ptr [rdx + 12] + align 8 +@s: crc32 r8d, dword ptr [rax] + crc32 r9d, dword ptr [rax + 4] + crc32 r10d, dword ptr [rax + 8] + crc32 r11d, dword ptr [rax + 12] + add rax, 16 + dec ecx + jnz @s + mov dword ptr [rdx], r8d + mov dword ptr [rdx + 4], r9d + mov dword ptr [rdx + 8], r10d + mov dword ptr [rdx + 12], r11d + end else + {$endif FPC} + {$endif CPUX64} + {$endif DISABLE_SSE42} begin + oneblock := @crcblock; + for i := 1 to count do begin + oneblock(crc128,data128); + inc(data128); + end; + end; end; {$ifdef CPUINTEL} @@ -34884,7 +34450,7 @@ function crc32cBy4SSE42(crc, value: cardinal): cardinal; crc32 eax, edx {$endif} end; -{$else} +{$else} {$ifdef FPC}nostackframe; assembler;{$endif} asm // eax=crc, edx=value {$ifdef FPC_OR_UNICODE} crc32 eax, edx @@ -34927,7 +34493,7 @@ procedure crcblockSSE42(crc128, data128: PBlock128); mov dword ptr[rcx + 12], r10d {$endif Linux} end; -{$else} +{$else} {$ifdef FPC}nostackframe; assembler;{$endif} asm // eax=crc128, edx=data128 mov ecx, eax {$ifdef FPC_OR_UNICODE} @@ -34995,6 +34561,7 @@ function crc32cinlined(crc: cardinal; buf: PAnsiChar; len: cardinal): cardinal; {$ifdef CPUX86} procedure GetCPUID(Param: Cardinal; var Registers: TRegisters); +{$ifdef FPC}nostackframe; assembler;{$endif} asm push esi push edi @@ -35029,6 +34596,7 @@ procedure GetCPUID(Param: Cardinal; var Registers: TRegisters); end; function crc32csse42(crc: cardinal; buf: PAnsiChar; len: cardinal): cardinal; +{$ifdef FPC}nostackframe; assembler;{$endif} asm // eax=crc, edx=buf, ecx=len not eax test ecx, ecx @@ -35192,6 +34760,27 @@ function IP6Text(ip6: PHash128): shortstring; IP6Text(ip6, @result); end; +function IsZero(const dig: THash160): boolean; +var a: TIntegerArray absolute dig; +begin + result := (a[0]=0) and (a[1]=0) and (a[2]=0) and (a[3]=0) and (a[4]=0); +end; + +function IsEqual(const A,B: THash160): boolean; +var a_: TIntegerArray absolute A; + b_: TIntegerArray absolute B; +begin // uses anti-forensic time constant "xor/or" pattern + result := ((a_[0] xor b_[0]) or (a_[1] xor b_[1]) or (a_[2] xor b_[2]) or + (a_[3] xor b_[3]) or (a_[4] xor b_[4]))=0; +end; + +procedure FillZero(out dig: THash160); +begin + PInt64Array(@dig)^[0] := 0; + PInt64Array(@dig)^[1] := 0; + PIntegerArray(@dig)^[4] := 0; +end; + procedure crc256c(buf: PAnsiChar; len: cardinal; out crc: THash256); var h: THash256Rec absolute crc; h1,h2: cardinal; @@ -35327,16 +34916,16 @@ procedure FillZero(var secret: RawByteString); begin if secret<>'' then with PStrRec(Pointer(PtrInt(secret)-STRRECSIZE))^ do - if refCnt>0 then // avoid GPF if const - FillcharFast(pointer(secret)^,length,0); + if refCnt=1 then // avoid GPF if const + {$ifdef FPC}FillChar{$else}FillCharFast{$endif}(pointer(secret)^,length,0); end; procedure FillZero(var secret: RawUTF8); begin if secret<>'' then with PStrRec(Pointer(PtrInt(secret)-STRRECSIZE))^ do - if refCnt>0 then // avoid GPF if const - FillcharFast(pointer(secret)^,length,0); + if refCnt=1 then // avoid GPF if const + {$ifdef FPC}FillChar{$else}FillCharFast{$endif}(pointer(secret)^,length,0); end; procedure mul64x64(const left, right: QWord; out product: THash128Rec); @@ -35353,7 +34942,7 @@ procedure mul64x64(const left, right: QWord; out product: THash128Rec); mov qword ptr [r8+8], rdx end; {$else} -{$ifdef CPUX86} +{$ifdef CPU32DELPHI} asm // adapted from FPC compiler output, which is much better than Delphi's here mov ecx, eax mov eax, dword ptr [ebp+8H] @@ -35392,7 +34981,7 @@ procedure mul64x64(const left, right: QWord; out product: THash128Rec); product.H := QWord(l.H)*r.H+t2.H+t3.H; product.L := t3.V shl 32 or t1.L; end; -{$endif CPUX86} +{$endif CPU32DELPHI} {$endif CPUX64} procedure SymmetricEncrypt(key: cardinal; var data: RawByteString); @@ -35418,7 +35007,7 @@ procedure SymmetricEncrypt(key: cardinal; var data: RawByteString); function UnixTimeToDateTime(const UnixTime: TUnixTime): TDateTime; begin - result := (UnixTime / SecsPerDay + UnixDateDelta); + result := UnixTime / SecsPerDay + UnixDateDelta; end; function DateTimeToUnixTime(const AValue: TDateTime): TUnixTime; @@ -35504,51 +35093,34 @@ function UnixTimeToString(const UnixTime: TUnixTime; Expanded: boolean; FirstTimeChar,false); end; -{$ifdef FPC} // faster than current RTL version -procedure DecodeTime(dt: TDateTime; out HH, MM, SS, MS: word); -var t, t2: cardinal; -begin - t := round(abs(dt) * MSecsPerDay) mod MSecsPerDay; - t2 := t div 3600000; - HH := t2; - dec(t, t2 * 3600000); - t2 := t div 60000; - MM := t2; - dec(t, t2 * 60000); - t2 := t div 1000; - SS := t2; - MS := t - t2 * 1000; -end; -{$endif FPC} - function DateTimeToFileShort(const DateTime: TDateTime): TShort16; begin DateTimeToFileShort(DateTime,result); end; procedure DateTimeToFileShort(const DateTime: TDateTime; out result: TShort16); -var HH,MM,SS,MS,Y,M,D: word; - tab: {$ifdef CPUX86}TWordArray absolute TwoDigitLookupW{$else}PWordArray{$endif}; +var T: TSynSystemTime; + tab: {$ifdef CPUX86NOTPIC}TWordArray absolute TwoDigitLookupW{$else}PWordArray{$endif}; begin // use 'YYMMDDHHMMSS' format if DateTime<=0 then begin PWord(@result[0])^ := 1+ord('0') shl 8; exit; end; - DecodeDate(DateTime,Y,M,D); - if Y > 1999 then - if Y < 2100 then - dec(Y,2000) else - Y := 99 else - Y := 0; - DecodeTime(DateTime,HH,MM,SS,MS); - {$ifndef CPUX86}tab := @TwoDigitLookupW;{$endif} + T.FromDate(DateTime); + if T.Year > 1999 then + if T.Year < 2100 then + dec(T.Year,2000) else + T.Year := 99 else + T.Year := 0; + T.FromTime(DateTime); + {$ifndef CPUX86NOTPIC}tab := @TwoDigitLookupW;{$endif} result[0] := #12; - PWord(@result[1])^ := tab[Y]; - PWord(@result[3])^ := tab[M]; - PWord(@result[5])^ := tab[D]; - PWord(@result[7])^ := tab[HH]; - PWord(@result[9])^ := tab[MM]; - PWord(@result[11])^ := tab[SS]; + PWord(@result[1])^ := tab[T.Year]; + PWord(@result[3])^ := tab[T.Month]; + PWord(@result[5])^ := tab[T.Day]; + PWord(@result[7])^ := tab[T.Hour]; + PWord(@result[9])^ := tab[T.Minute]; + PWord(@result[11])^ := tab[T.Second]; end; procedure UnixTimeToFileShort(const UnixTime: TUnixTime; out result: TShort16); @@ -35577,7 +35149,7 @@ function UnixTimePeriodToString(const UnixTime: TUnixTime; FirstTimeChar: AnsiCh function UnixMSTimeToDateTime(const UnixMSTime: TUnixMSTime): TDateTime; begin - result := (UnixMSTime/MSecsPerDay + UnixDateDelta); + result := UnixMSTime/MSecsPerDay + UnixDateDelta; end; function UnixMSTimePeriodToString(const UnixTime: TUnixTime; FirstTimeChar: AnsiChar): RawUTF8; @@ -35589,7 +35161,9 @@ function UnixMSTimePeriodToString(const UnixTime: TUnixTime; FirstTimeChar: Ansi function DateTimeToUnixMSTime(const AValue: TDateTime): TUnixMSTime; begin - result := Round((AValue - UnixDateDelta) * MSecsPerDay); + if AValue=0 then + result := 0 else + result := Round((AValue - UnixDateDelta) * MSecsPerDay); end; function UnixMSTimeToString(const UnixMSTime: TUnixMSTime; Expanded: boolean; @@ -35714,7 +35288,7 @@ procedure Iso8601ToDateTimePUTF8CharVar(P: PUTF8Char; L: integer; var result: TD var B: cardinal; Y,M,D, H,MI,SS,MS: cardinal; d100: TDiv100Rec; - tab: {$ifdef CPUX86}TNormTableByte absolute ConvertHexToBin{$else}PNormTableByte{$endif}; + tab: {$ifdef CPUX86NOTPIC}TNormTableByte absolute ConvertHexToBin{$else}PNormTableByte{$endif}; // expect 'YYYYMMDDThhmmss[.sss]' format but handle also 'YYYY-MM-DDThh:mm:ss[.sss]' begin unaligned(result) := 0; @@ -35728,7 +35302,7 @@ procedure Iso8601ToDateTimePUTF8CharVar(P: PUTF8Char; L: integer; var result: TD dec(P,8); inc(L,8); end else begin - {$ifndef CPUX86}tab := @ConvertHexToBin;{$endif} // faster on PIC and x86_64 + {$ifndef CPUX86NOTPIC}tab := @ConvertHexToBin;{$endif} // faster on PIC and x86_64 B := tab[ord(P[0])]; // first digit if B>9 then exit else Y := B; // fast check '0'..'9' B := tab[ord(P[1])]; @@ -35861,58 +35435,64 @@ function TimeLogToUnixTime(const Timestamp: TTimeLog): TUnixTime; result := PTimeLogBits(@Timestamp)^.ToUnixTime; end; -procedure DateToIso8601PChar(P: PUTF8Char; Expanded: boolean; Y,M,D: cardinal); +procedure DateToIso8601PChar(P: PUTF8Char; Expanded: boolean; Y,M,D: PtrUInt); // use 'YYYYMMDD' format if not Expanded, 'YYYY-MM-DD' format if Expanded +var tab: {$ifdef CPUX86NOTPIC}TWordArray absolute TwoDigitLookupW{$else}PWordArray{$endif}; begin + {$ifdef CPUX86NOTPIC} YearToPChar(Y,P); + {$else} + tab := @TwoDigitLookupW; + YearToPChar2(tab,Y,P); + {$endif} inc(P,4); if Expanded then begin P^ := '-'; inc(P); end; - PWord(P)^ := TwoDigitLookupW[M]; + PWord(P)^ := tab[M]; inc(P,2); if Expanded then begin P^ := '-'; inc(P); end; - PWord(P)^ := TwoDigitLookupW[D]; + PWord(P)^ := tab[D]; end; -procedure TimeToIso8601PChar(P: PUTF8Char; Expanded: boolean; H,M,S,MS: cardinal; +procedure TimeToIso8601PChar(P: PUTF8Char; Expanded: boolean; H,M,S,MS: PtrUInt; FirstChar: AnsiChar; WithMS: boolean); -// use Thhmmss[.sss] format -begin +var tab: {$ifdef CPUX86NOTPIC}TWordArray absolute TwoDigitLookupW{$else}PWordArray{$endif}; +begin // use Thhmmss[.sss] format if FirstChar<>#0 then begin P^ := FirstChar; inc(P); end; - PWord(P)^ := TwoDigitLookupW[H]; + {$ifndef CPUX86NOTPIC}tab := @TwoDigitLookupW;{$endif} + PWord(P)^ := tab[H]; inc(P,2); if Expanded then begin P^ := ':'; inc(P); end; - PWord(P)^ := TwoDigitLookupW[M]; + PWord(P)^ := tab[M]; inc(P,2); if Expanded then begin P^ := ':'; inc(P); end; - PWord(P)^ := TwoDigitLookupW[S]; + PWord(P)^ := tab[S]; if WithMS then begin inc(P,2); - YearToPChar(MS,P); + {$ifdef CPUX86NOTPIC}YearToPChar(MS{$else}YearToPChar2(tab,MS{$endif},P); P^ := '.'; // override first digit end; end; procedure DateToIso8601PChar(Date: TDateTime; P: PUTF8Char; Expanded: boolean); -// use YYYYMMDD / YYYY-MM-DD date format -var Y,M,D: word; -begin - DecodeDate(Date,Y,M,D); - DateToIso8601PChar(P,Expanded,Y,M,D); +var T: TSynSystemTime; +begin // use YYYYMMDD / YYYY-MM-DD date format + T.FromDate(Date); + DateToIso8601PChar(P,Expanded,T.Year,T.Month,T.Day); end; function DateToIso8601Text(Date: TDateTime): RawUTF8; @@ -35926,10 +35506,10 @@ function DateToIso8601Text(Date: TDateTime): RawUTF8; procedure TimeToIso8601PChar(Time: TDateTime; P: PUTF8Char; Expanded: boolean; FirstChar: AnsiChar; WithMS: boolean); -var H,M,S,MS: word; +var T: TSynSystemTime; begin - DecodeTime(Time,H,M,S,MS); - TimeToIso8601PChar(P,Expanded,H,M,S,MS,FirstChar,WithMS); + T.FromTime(Time); + TimeToIso8601PChar(P,Expanded,T.Hour,T.Minute,T.Second,T.MilliSecond,FirstChar,WithMS); end; function DateTimeToIso8601(D: TDateTime; Expanded: boolean; @@ -36096,12 +35676,12 @@ procedure TTimeLogBits.From(P: PUTF8Char; L: integer); procedure TTimeLogBits.Expand(out Date: TSynSystemTime); begin Date.Year := (Value shr (6+6+5+5+4)) and 4095; - Date.Month := 1+(Int64Rec(Value).Lo shr (6+6+5+5)) and 15; - Date.Day := 1+(Int64Rec(Value).Lo shr (6+6+5)) and 31; + Date.Month := 1+(PCardinal(@Value)^ shr (6+6+5+5)) and 15; + Date.Day := 1+(PCardinal(@Value)^ shr (6+6+5)) and 31; Date.DayOfWeek := 0; - Date.Hour := (Int64Rec(Value).Lo shr (6+6)) and 31; - Date.Minute := (Int64Rec(Value).Lo shr 6) and 63; - Date.Second := Int64Rec(Value).Lo and 63; + Date.Hour := (PCardinal(@Value)^ shr (6+6)) and 31; + Date.Minute := (PCardinal(@Value)^ shr 6) and 63; + Date.Second := PCardinal(@Value)^ and 63; end; procedure TTimeLogBits.From(const S: RawUTF8); @@ -36112,29 +35692,29 @@ procedure TTimeLogBits.From(const S: RawUTF8); procedure TTimeLogBits.From(FileDate: integer); begin {$ifdef MSWINDOWS} - From(LongRec(FileDate).Hi shr 9+1980, - LongRec(FileDate).Hi shr 5 and 15, - LongRec(FileDate).Hi and 31, - LongRec(FileDate).Lo shr 11, - LongRec(FileDate).Lo shr 5 and 63, - LongRec(FileDate).Lo and 31 shl 1); + From(PInt64Rec(@FileDate)^.Hi shr 9+1980, + PInt64Rec(@FileDate)^.Hi shr 5 and 15, + PInt64Rec(@FileDate)^.Hi and 31, + PInt64Rec(@FileDate)^.Lo shr 11, + PInt64Rec(@FileDate)^.Lo shr 5 and 63, + PInt64Rec(@FileDate)^.Lo and 31 shl 1); {$else} // FileDate depends on the running OS From(FileDateToDateTime(FileDate)); {$endif} end; procedure TTimeLogBits.From(DateTime: TDateTime; DateOnly: Boolean=false); -var HH,MM,SS,MS,Y,M,D: word; +var T: TSynSystemTime; V: cardinal; begin + T.FromDate(DateTime); if DateOnly then - HH := 0 else - DecodeTime(DateTime,HH,MM,SS,MS); - DecodeDate(DateTime,Y,M,D); - V := HH+D shl 5+M shl 10+Y shl 14-(1 shl 5+1 shl 10); + T.Hour := 0 else + T.FromTime(DateTime); + V := T.Hour+T.Day shl 5+T.Month shl 10+T.Year shl 14-(1 shl 5+1 shl 10); if DateOnly then Value := Int64(V) shl 12 else - Value := SS+MM shl 6+Int64(V) shl 12; + Value := T.Second+T.Minute shl 6+Int64(V) shl 12; end; procedure TTimeLogBits.FromUnixTime(const UnixTime: TUnixTime); @@ -36209,7 +35789,7 @@ procedure RCU128(var src,dst); procedure RCU(var src,dst; len: integer); begin repeat - MoveFast(src,dst,len); + {$ifdef FPC}Move{$else}MoveFast{$endif}(src,dst,len); ReadBarrier; until CompareMem(@src,@dst,len); end; @@ -36219,7 +35799,8 @@ procedure FromGlobalTime(LocalTime: boolean; out NewTime: TSynSystemTime); newtimesys: TSystemTime absolute NewTime; begin with GlobalTime[LocalTime] do begin - tix := GetTickCount64 {$ifndef MSWINDOWS}shr 3{$endif}; // Linux: 8ms refresh + tix := {$ifdef FPCLINUX}SynFPCLinux.{$endif}GetTickCount64 + {$ifndef MSWINDOWS}shr 3{$endif}; // Linux: 8ms refresh if clock<>tix then begin // Windows: typically in range of 10-16 ms clock := tix; NewTime.Clear; @@ -36254,7 +35835,7 @@ procedure TTimeLogBits.FromNow; function TTimeLogBits.ToTime: TDateTime; var lo: PtrUInt; begin - lo := {$ifdef CPU64}Value{$else}Int64Rec(Value).Lo{$endif}; + lo := {$ifdef CPU64}Value{$else}PCardinal(@Value)^{$endif}; if lo and (1 shl (6+6+5)-1)=0 then result := 0 else result := EncodeTime((lo shr(6+6))and 31, (lo shr 6)and 63, lo and 63, 0); @@ -36291,7 +35872,7 @@ function TTimeLogBits.ToDate: TDateTime; Y := (lo shr (6+6+5+5+4)) and 4095; {$else} Y := (Value shr (6+6+5+5+4)) and 4095; - lo := Int64Rec(Value).Lo; + lo := PCardinal(@Value)^; {$endif} if (Y=0) or not TryEncodeDate(Y,1+(lo shr(6+6+5+5))and 15,1+(lo shr(6+6+5))and 31,result) then result := 0; @@ -36306,7 +35887,7 @@ function TTimeLogBits.ToDateTime: TDateTime; Y := (lo shr (6+6+5+5+4)) and 4095; {$else} Y := (Value shr (6+6+5+5+4)) and 4095; - lo := Int64Rec(Value).Lo; + lo := PCardinal(@Value)^; {$endif} if (Y=0) or not TryEncodeDate(Y,1+(lo shr(6+6+5+5))and 15,1+(lo shr(6+6+5))and 31,result) then result := 0; @@ -36322,27 +35903,27 @@ function TTimeLogBits.Year: Integer; function TTimeLogBits.Month: Integer; begin - result := 1+(Int64Rec(Value).Lo shr (6+6+5+5)) and 15; + result := 1+(PCardinal(@Value)^ shr (6+6+5+5)) and 15; end; function TTimeLogBits.Day: Integer; begin - result := 1+(Int64Rec(Value).Lo shr (6+6+5)) and 31; + result := 1+(PCardinal(@Value)^ shr (6+6+5)) and 31; end; function TTimeLogBits.Hour: Integer; begin - result := (Int64Rec(Value).Lo shr (6+6)) and 31; + result := (PCardinal(@Value)^ shr (6+6)) and 31; end; function TTimeLogBits.Minute: Integer; begin - result := (Int64Rec(Value).Lo shr 6) and 63; + result := (PCardinal(@Value)^ shr 6) and 63; end; function TTimeLogBits.Second: Integer; begin - result := Int64Rec(Value).Lo and 63; + result := PCardinal(@Value)^ and 63; end; function TTimeLogBits.ToUnixTime: TUnixTime; @@ -36362,7 +35943,7 @@ function TTimeLogBits.Text(Dest: PUTF8Char; Expanded: boolean; FirstTimeChar: An result := 0; exit; end; - lo := {$ifdef CPU64}Value{$else}Int64Rec(Value).Lo{$endif}; + lo := {$ifdef CPU64}Value{$else}PCardinal(@Value)^{$endif}; if lo and (1 shl (6+6+5)-1)=0 then begin // no Time: just convert date DateToIso8601PChar(Dest, Expanded, @@ -36443,13 +36024,13 @@ function NowUTCToString(Expanded: boolean=true; FirstTimeChar: AnsiChar = ' '): function DateTimeMSToString(DateTime: TDateTime; Expanded: boolean; FirstTimeChar: AnsiChar; const TZD: RawUTF8): RawUTF8; -var HH,MM,SS,MS,Y,M,D: word; +var T: TSynSystemTime; begin // 'YYYY-MM-DD hh:mm:ss.sssZ' or 'YYYYMMDD hhmmss.sssZ' format if DateTime=0 then result := '' else begin - DecodeDate(DateTime,Y,M,D); - DecodeTime(DateTime,HH,MM,SS,MS); - result := DateTimeMSToString(HH,MM,SS,MS,Y,M,D,Expanded,FirstTimeChar,TZD); + T.FromDateTime(DateTime); + result := DateTimeMSToString(T.Hour,T.Minute,T.Second,T.MilliSecond, + T.Year,T.Month,T.Day,Expanded,FirstTimeChar,TZD); end; end; @@ -36468,18 +36049,17 @@ function DateTimeMSToString(HH,MM,SS,MS,Y,M,D: cardinal; Expanded: boolean; ('Jan','Feb','Mar','Apr','May','Jun', 'Jul','Aug','Sep','Oct','Nov','Dec'); function DateTimeToHTTPDate(UTCDateTime: TDateTime): RawUTF8; -var HH,MM,SS,MS,Y,M,D: word; +var T: TSynSystemTime; begin if UTCDateTime=0 then begin result := ''; exit; end; - DecodeDate(UTCDateTime,Y,M,D); - DecodeTime(UTCDateTime,HH,MM,SS,MS); + T.FromDateTime(UTCDateTime); FormatUTF8('%, % % % %:%:% GMT', [HTML_WEEK_DAYS[DayOfWeek(UTCDateTime)], - UInt2DigitsToShortFast(D),HTML_MONTH_NAMES[M],UInt4DigitsToShort(Y), - UInt2DigitsToShortFast(HH),UInt2DigitsToShortFast(MM), - UInt2DigitsToShortFast(SS)], result); + UInt2DigitsToShortFast(T.Day),HTML_MONTH_NAMES[T.Month],UInt4DigitsToShort(T.Year), + UInt2DigitsToShortFast(T.Hour),UInt2DigitsToShortFast(T.Minute), + UInt2DigitsToShortFast(T.Second)], result); end; function TimeToString: RawUTF8; @@ -36574,6 +36154,51 @@ procedure TSynSystemTime.FromNowLocal; FromGlobalTime(true,self); end; +procedure TSynSystemTime.FromDateTime(const dt: TDateTime); +begin + FromDate(dt); + FromTime(dt); +end; + +procedure TSynSystemTime.FromDate(const dt: TDateTime); +var t,t2,t3: PtrUInt; +begin + t := Trunc(dt); + t := (t+693900)*4-1; + if PtrInt(t)>=0 then begin + t3 := t div 146097; + t2 := (t-t3*146097) and not 3; + t := PtrUInt(t2+3) div 1461; // PtrUInt() needed for FPC i386 + Year := t3*100+t; + t2 := ((t2+7-t*1461)shr 2)*5; + t3 := PtrUInt(t2-3) div 153; + Day := PtrUInt(t2+2-t3*153) div 5; + if t3<10 then + inc(t3,3) else begin + dec(t3,9); + inc(Year); + end; + Month := t3; + DayOfWeek := 0; + end else + PInt64(@Year)^ := 0; +end; + +procedure TSynSystemTime.FromTime(const dt: TDateTime); +var t,t2: PtrUInt; +begin + t := round(abs(dt)*MSecsPerDay) mod MSecsPerDay; + t2 := t div 3600000; + Hour := t2; + dec(t,t2*3600000); + t2 := t div 60000; + Minute := t2; + dec(t,t2*60000); + t2 := t div 1000; + Second := t2; + MilliSecond := t-t2*1000; +end; + function TSynSystemTime.ToText(Expanded: boolean; FirstTimeChar: AnsiChar; const TZD: RawUTF8): RawUTF8; begin @@ -36584,11 +36209,11 @@ function TSynSystemTime.ToText(Expanded: boolean; procedure TSynSystemTime.AddLogTime(WR: TTextWriter); var y,d100: PtrUInt; P: PUTF8Char; - tab: {$ifdef CPUX86}TWordArray absolute TwoDigitLookupW{$else}PWordArray{$endif}; + tab: {$ifdef CPUX86NOTPIC}TWordArray absolute TwoDigitLookupW{$else}PWordArray{$endif}; begin - if WR.BEnd-WR.B<=17 then + if WR.BEnd-WR.B<=18 then WR.FlushToStream; - {$ifndef CPUX86}tab := @TwoDigitLookupW;{$endif} + {$ifndef CPUX86NOTPIC}tab := @TwoDigitLookupW;{$endif} y := Year; d100 := y div 100; P := WR.B+1; @@ -36602,10 +36227,83 @@ procedure TSynSystemTime.AddLogTime(WR: TTextWriter); PWord(P+13)^ := tab[Second]; y := Millisecond; PWord(P+15)^ := tab[y shr 4]; - P[17] := ' '; - WR.B := P+16; + inc(WR.B,17); end; +function TSynSystemTime.ToNCSAText(P: PUTF8Char): PtrInt; +var y,d100: PtrUInt; + tab: {$ifdef CPUX86NOTPIC}TWordArray absolute TwoDigitLookupW{$else}PWordArray{$endif}; +begin + {$ifndef CPUX86NOTPIC}tab := @TwoDigitLookupW;{$endif} + PWord(P)^ := tab[Day]; + PCardinal(P+2)^ := PCardinal(@HTML_MONTH_NAMES[Month])^; + P[2] := '/'; // overwrite HTML_MONTH_NAMES[][0] + P[6] := '/'; + y := Year; + d100 := y div 100; + PWord(P+7)^ := tab[d100]; + PWord(P+9)^ := tab[y-(d100*100)]; + P[11] := ':'; + PWord(P+12)^ := tab[Hour]; + P[14] := ':'; + PWord(P+15)^ := tab[Minute]; + P[17] := ':'; + PWord(P+18)^ := tab[Second]; + P[20] := ' '; + result := 21; +end; + +procedure TSynSystemTime.AddNCSAText(WR: TTextWriter); +begin + if WR.BEnd-WR.B<=21 then + WR.FlushToStream; + inc(WR.B,ToNCSAText(WR.B+1)); +end; + +function TSynSystemTime.ToDateTime: TDateTime; +var time: TDateTime; +begin + if TryEncodeDate(Year,Month,Day,result) then + if TryEncodeTime(Hour,Minute,Second,MilliSecond,time) then + result := result+time else + result := 0 else + result := 0; +end; + +procedure TSynSystemTime.IncrementMS(ms: integer); +begin + inc(MilliSecond, ms); + if MilliSecond >= 1000 then + repeat + dec(MilliSecond, 1000); + if Second < 60 then + inc(Second) + else begin + Second := 0; + if Minute < 60 then + inc(Minute) + else begin + Minute := 0; + if Hour < 24 then + inc(Hour) + else begin + Hour := 0; + if Day < MonthDays[false, Month] then + inc(Day) + else begin + Day := 1; + if Month < 12 then + inc(Month) + else begin + Month := 1; + inc(Year); + end; + end; + end; + end; + end; + until MilliSecond < 1000; +end; { TTimeZoneData } @@ -36872,7 +36570,8 @@ function TSynTimeZone.Displays: TStrings; end; -procedure AppendToTextFile(aLine: RawUTF8; const aFileName: TFileName; aMaxSize: Int64); +procedure AppendToTextFile(aLine: RawUTF8; const aFileName: TFileName; + aMaxSize: Int64; aUTCTimeStamp: boolean); var F: THandle; Old: TFileName; Date: array[1..22] of AnsiChar; @@ -36901,7 +36600,9 @@ procedure AppendToTextFile(aLine: RawUTF8; const aFileName: TFileName; aMaxSize: exit; end; PWord(@Date)^ := 13+10 shl 8; // first go to next line - now.FromNowLocal; + if aUTCTimeStamp then + now.FromNowUTC else + now.FromNowLocal; DateToIso8601PChar(@Date[3],true,Now.Year,Now.Month,Now.Day); TimeToIso8601PChar(@Date[13],true,Now.Hour,Now.Minute,Now.Second,0,' '); Date[22] := ' '; @@ -37075,7 +36776,7 @@ function RdRand32: cardinal; {$ifdef CPU64}{$ifdef FPC}nostackframe; assembler; asm{$else} asm .noframe {$endif FPC} {$else} -asm +{$ifdef FPC}nostackframe; assembler;{$endif} asm {$endif} // rdrand eax: same opcodes for x86 and x64 db $0f,$c7,$f0 @@ -37083,34 +36784,33 @@ function RdRand32: cardinal; end; {$endif CPUINTEL} -type - {$ifdef UNICODE}TLecuyer = record{$else}TLecuyer = object{$endif} - public - rs1, rs2, rs3: cardinal; - seedcount: cardinal; - procedure Seed(entropy: PByteArray; entropylen: integer); - function Next: cardinal; - end; - threadvar _Lecuyer: TLecuyer; // uses only 16 bytes per thread -procedure TLecuyer.Seed(entropy: PByteArray; entropylen: integer); +procedure TLecuyer.Seed(entropy: PByteArray; entropylen: PtrInt); var time, crc: THash128Rec; - i: integer; + i, j: PtrInt; begin repeat QueryPerformanceCounter(time.Lo); - time.i2 := UnixTimeUTC; - time.i3 := integer(GetCurrentThreadID); + time.Hi := UnixMSTimeUTC xor Int64(GetCurrentThreadID); crcblock(@crc.b,@time.b); crcblock(@crc.b,@ExeVersion.Hash.b); if entropy<>nil then - for i := 0 to entropylen-1 do - crc.b[i and 15] := crc.b[i and 15] xor entropy^[i]; + for i := 0 to entropylen-1 do begin + j := i and 15; + crc.b[j] := crc.b[j] xor entropy^[i]; + end; rs1 := rs1 xor crc.c0; rs2 := rs2 xor crc.c1; rs3 := rs3 xor crc.c2; + {$ifdef CPUINTEL} + if cfRAND in CpuFeatures then begin // won't hurt e.g. from Random32gsl + rs1 := rs1 xor RdRand32; + rs2 := rs2 xor RdRand32; + rs3 := rs3 xor RdRand32; + end; + {$endif CPUINTEL} until (rs1>1) and (rs2>7) and (rs3>15); seedcount := 1; for i := 1 to crc.i3 and 15 do @@ -37131,6 +36831,11 @@ function TLecuyer.Next: cardinal; result := rs1 xor rs2 xor result; end; +function TLecuyer.Next(max: cardinal): cardinal; +begin + result := (QWord(Next)*max)shr 32; +end; + procedure Random32Seed(entropy: pointer; entropylen: integer); begin _Lecuyer.Seed(entropy,entropylen); @@ -37161,9 +36866,9 @@ function Random32gsl(max: cardinal): cardinal; end; procedure FillRandom(Dest: PCardinalArray; CardinalCount: integer; forcegsl: boolean); -var i: integer; +var i: PtrInt; c: cardinal; - timenow: Int64; + seed: TQWordRec; lecuyer: ^TLecuyer; begin {$ifdef CPUINTEL} @@ -37171,12 +36876,12 @@ procedure FillRandom(Dest: PCardinalArray; CardinalCount: integer; forcegsl: boo lecuyer := nil else {$endif} lecuyer := @_Lecuyer; - QueryPerformanceCounter(timenow); - c := crc32c(ExeVersion.Hash.c3,@timenow,SizeOf(timenow)); + QueryPerformanceCounter(PInt64(@seed)^); + c := crc32cBy4(seed.L,seed.H); {$ifdef CPUINTEL} if lecuyer=nil then for i := 0 to CardinalCount-1 do begin - c := c xor RdRand32 xor crc32ctab[0,(c+cardinal(i)) and 1023]; + c := crc32cBy4(c,RdRand32); // won't trust plain Intel values Dest^[i] := Dest^[i] xor c; end else {$endif} @@ -37229,6 +36934,7 @@ function StringToGUID(const text: string): TGUID; function StrCurr64(P: PAnsiChar; const Value: Int64): PAnsiChar; var c: QWord; + d: cardinal; {$ifndef CPU64}c64: Int64Rec absolute c;{$endif} begin if Value=0 then begin @@ -37245,7 +36951,8 @@ function StrCurr64(P: PAnsiChar; const Value: Int64): PAnsiChar; YearToPChar(c,PUTF8Char(P)-4); end else begin result := StrUInt64(P-1,c); - PCardinal(P-4)^ := PCardinal(P-5)^; + d := PCardinal(P-5)^; // in two explit steps for CPUARM (alf) + PCardinal(P-4)^ := d; P[-5] := '.'; // insert '.' just before last 4 decimals end; if Value<0 then begin @@ -37298,7 +37005,7 @@ function Curr64ToPChar(const Value: Int64; Dest: PUTF8Char): PtrInt; if Decim and $ffff0000=ord('0')shl 16+ord('0')shl 24 then dec(result,2); // 2 decimals end; - MoveFast(P^,Dest^,result); + {$ifdef FPC}Move{$else}MoveFast{$endif}(P^,Dest^,result); end; function StrToCurr64(P: PUTF8Char; NoDecimal: PBoolean=nil): Int64; @@ -37326,7 +37033,7 @@ function StrToCurr64(P: PUTF8Char; NoDecimal: PBoolean=nil): Int64; c := byte(P^)-48; if c>9 then exit; - Int64Rec(result).Lo := c; + PCardinal(@result)^ := c; inc(P); repeat if P^<>'.' then begin @@ -37422,6 +37129,11 @@ function TrimLeftLowerCase(const V: RawUTF8): PUTF8Char; end; function TrimLeftLowerCaseToShort(V: PShortString): ShortString; +begin + TrimLeftLowerCaseToShort(V,result); +end; + +procedure TrimLeftLowerCaseToShort(V: PShortString; out result: ShortString); var P: PAnsiChar; L: integer; begin @@ -37495,60 +37207,63 @@ function UnCamelCase(D, P: PUTF8Char): integer; Number: boolean; label Next; begin - Space := D; DBeg := D; - SpaceBeg := D; - if (D<>nil) and (P<>nil) then // avoid GPF - repeat - CapitalCount := 0; - Number := P^ in ['0'..'9']; - if Number then - repeat - inc(CapitalCount); - D^ := P^; - inc(P); + if (D<>nil) and (P<>nil) then begin // avoid GPF + Space := D; + SpaceBeg := D; + repeat + CapitalCount := 0; + Number := P^ in ['0'..'9']; + if Number then + repeat + inc(CapitalCount); + D^ := P^; + inc(P); + inc(D); + until not (P^ in ['0'..'9']) else + repeat + inc(CapitalCount); + D^ := P^; + inc(P); + inc(D); + until not (P^ in ['A'..'Z']); + if P^=#0 then break; // no lowercase conversion of last fully uppercased word + if (CapitalCount > 1) and not Number then begin + dec(P); + dec(D); + end; + while P^ in ['a'..'z'] do begin + D^ := P^; inc(D); - until not (P^ in ['0'..'9']) else - repeat - inc(CapitalCount); - D^ := P^; + inc(P); + end; + if P^='_' then + if P[1]='_' then begin + D^ := ':'; inc(P); inc(D); - until not (P^ in ['A'..'Z']); - if P^=#0 then break; // no lowercase conversion of last fully uppercased word - if (CapitalCount > 1) and not Number then begin - dec(P); - dec(D); - end; - while P^ in ['a'..'z'] do begin - D^ := P^; + goto Next; + end else begin + PWord(D)^ := ord(' ')+ord('-')shl 8; + inc(D,2); + Next: if Space=SpaceBeg then + SpaceBeg := D+1; + inc(P); + Space := D+1; + end else + Space := D; + if P^=#0 then break; + D^ := ' '; inc(D); - inc(P); + until false; + if Space>DBeg then + dec(Space); + while Space>SpaceBeg do begin + if Space^ in ['A'..'Z'] then + if not (Space[1] in ['A'..'Z',' ']) then + inc(Space^,32); // lowercase conversion of not last fully uppercased word + dec(Space); end; - if P^='_' then - if P[1]='_' then begin - D^ := ':'; - inc(P); - inc(D); - goto Next; - end else begin - PWord(D)^ := ord(' ')+ord('-')shl 8; - inc(D,2); -Next: if Space=SpaceBeg then - SpaceBeg := D+1; - inc(P); - Space := D+1; - end else - Space := D; - if P^=#0 then break; - D^ := ' '; - inc(D); - until false; - while Space>SpaceBeg do begin - if Space^ in ['A'..'Z'] then - if not (Space[1] in ['A'..'Z',' ']) then - inc(Space^,32); // lowercase conversion of not last fully uppercased word - dec(Space); end; result := D-DBeg; end; @@ -37563,7 +37278,7 @@ procedure CamelCase(P: PAnsiChar; len: integer; var s: RawUTF8; len := SizeOf(tmp); for i := 0 to len - 1 do if not (ord(P[i]) in isWord) then begin - MoveFast(P^,tmp,i); + {$ifdef FPC}Move{$else}MoveFast{$endif}(P^,tmp,i); inc(P,i); d := @tmp[i]; dec(len,i); @@ -38092,7 +37807,7 @@ function MultiPartFormDataEncode(const MultiPart: TMultiPartDynArray; with MultiPart[i] do begin if FileName='' then W.Add('--%'#13#10'Content-Disposition: form-data; name="%"'#13#10+ - 'Content-Type: %'#13#10#13#10'%'#10'--%'#13#10, + 'Content-Type: %'#13#10#13#10'%'#13#10'--%'#13#10, [bound,Name,ContentType,Content,bound]) else begin // if this is the first file, create the header for files if filescount=0 then begin @@ -38265,7 +37980,7 @@ function AddSortedRawUTF8(var Values: TRawUTF8DynArray; var ValuesCount: integer end; n := Length(Values); if ValuesCount=n then begin - inc(n,256+n shr 3); + n := NextGrow(n); SetLength(Values,n); if CoValues<>nil then SetLength(CoValues^,n); @@ -38273,11 +37988,11 @@ function AddSortedRawUTF8(var Values: TRawUTF8DynArray; var ValuesCount: integer n := ValuesCount; if resultnil then begin - {$ifdef CPU64}n := n shr 1;{$endif} // 64 bit pointer size is twice an integer - MoveFast(CoValues^[result],CoValues^[result+1],n); + {$ifdef CPU64}n := n shr 1;{$endif} // 64-bit pointer size is twice an integer + {$ifdef FPC}Move{$else}MoveFast{$endif}(CoValues^[result],CoValues^[result+1],n); end; end else result := n; @@ -38288,7 +38003,7 @@ function AddSortedRawUTF8(var Values: TRawUTF8DynArray; var ValuesCount: integer type /// used internaly for faster quick sort - {$ifdef UNICODE}TQuickSortRawUTF8 = record{$else}TQuickSortRawUTF8 = object{$endif} + {$ifdef FPC_OR_UNICODE}TQuickSortRawUTF8 = record{$else}TQuickSortRawUTF8 = object{$endif} public Values: PPointerArray; Compare: TUTF8Compare; @@ -38323,10 +38038,16 @@ procedure TQuickSortRawUTF8.Sort(L, R: PtrInt); Inc(I); Dec(J); end; until I > J; - if L < J then - Sort(L, J); - L := I; - until I >= R; + if J - L < R - I then begin // use recursion only for smaller range + if L < J then + Sort(L, J); + L := I; + end else begin + if I < R then + Sort(I, R); + R := J; + end; + until L >= R; end; procedure QuickSortRawUTF8(var Values: TRawUTF8DynArray; ValuesCount: integer; @@ -38352,7 +38073,8 @@ function DeleteRawUTF8(var Values: TRawUTF8DynArray; Index: integer): boolean; dec(n); Values[Index] := ''; // avoid GPF if n>Index then begin - MoveFast(pointer(Values[Index+1]),pointer(Values[Index]),(n-Index)*SizeOf(pointer)); + {$ifdef FPC}Move{$else}MoveFast{$endif}( + pointer(Values[Index+1]),pointer(Values[Index]),(n-Index)*SizeOf(pointer)); PtrUInt(Values[n]) := 0; // avoid GPF end; SetLength(Values,n); @@ -38373,8 +38095,10 @@ function DeleteRawUTF8(var Values: TRawUTF8DynArray; var ValuesCount: integer; dec(n,Index); if n>0 then begin if CoValues<>nil then - MoveFast(CoValues^[Index+1],CoValues^[Index],n*SizeOf(Integer)); - MoveFast(pointer(Values[Index+1]),pointer(Values[Index]),n*SizeOf(pointer)); + {$ifdef FPC}Move{$else}MoveFast{$endif}( + CoValues^[Index+1],CoValues^[Index],n*SizeOf(Integer)); + {$ifdef FPC}Move{$else}MoveFast{$endif}( + pointer(Values[Index+1]),pointer(Values[Index]),n*SizeOf(pointer)); PtrUInt(Values[ValuesCount]) := 0; // avoid GPF end; result := true; @@ -38399,21 +38123,6 @@ function ToText(const aIntelCPUFeatures: TIntelCpuFeatures; const Sep: RawUTF8): end; end; -function SystemInfoJson: RawUTF8; -var cpu,mem: RawUTF8; -begin - cpu := TSystemUse.Current(false).HistoryText(0,15,@mem); - with SystemInfo do - result := JSONEncode([ - 'host',ExeVersion.Host,'user',ExeVersion.User,'os',OSVersionText, - 'cpu',CpuInfoText,'bios',BiosInfoText, - {$ifdef MSWINDOWS}{$ifndef CPU64}'wow64',IsWow64,{$endif}{$endif MSWINDOWS} - {$ifdef CPUINTEL}'cpufeatures', LowerCase(ToText(CpuFeatures, ' ')),{$endif} - 'processcpu',cpu,'processmem',mem, - 'freemem',TSynMonitorMemory.FreeAsText, - 'freedisk',TSynMonitorDisk.FreeAsText]); -end; - {$ifdef MSWINDOWS} // wrapper around some low-level Windows-specific API @@ -38445,47 +38154,6 @@ function GetFileVersion(const FileName: TFileName): cardinal; end; {$endif DELPHI6OROLDER} -function EnumAllProcesses(out Count: Cardinal): TCardinalDynArray; -var n: cardinal; -begin - n := 2048; - repeat - SetLength(result, n); - if EnumProcesses(pointer(result), n * 4, Count) then - Count := Count shr 2 else - Count := 0; - if Count < n then begin - if Count = 0 then - result := nil; - exit; - end; - inc(n, 1024); // (very unlikely) too small buffer - until n>8192; -end; - -function EnumProcessName(PID: Cardinal): RawUTF8; -var h: THandle; - len: DWORD; - name: array[0..4095] of WideChar; -begin - result := ''; - if PID = 0 then - exit; - h := OpenProcess(OpenProcessAccess, false, PID); - if h <> 0 then - try - if Assigned(QueryFullProcessImageNameW) then begin - len := high(name); - if QueryFullProcessImageNameW(h, 0, name, @len) then - RawUnicodeToUtf8(name, len, result); - end else - if GetModuleFileNameExW(h,0,name,high(name))<>0 then - RawUnicodeToUtf8(name, StrLenW(name), result); - finally - CloseHandle(h); - end; -end; - function WndProcMethod(Hwnd: HWND; Msg,wParam,lParam: integer): integer; stdcall; var obj: TObject; dsp: TMessage; @@ -38512,7 +38180,7 @@ function CreateInternalWindow(const aWindowName: string; aObject: TObject): HWND result := 0; if GetClassInfo(HInstance, pointer(aWindowName), TempClass) then exit; // class name already registered -> fail - FillCharFast(TempClass,SizeOf(TempClass),0); + {$ifdef FPC}FillChar{$else}FillCharFast{$endif}(TempClass,SizeOf(TempClass),0); TempClass.hInstance := HInstance; TempClass.lpfnWndProc := @DefWindowProc; TempClass.lpszClassName := pointer(aWindowName); @@ -38576,13 +38244,6 @@ function SetAppUserModelID(const AppUserModelID: string): boolean; LastAppUserModelID := AppUserModelID; end; -{$else} - -// wrapper around some low-level OS (non Windows) specific API - -const - _SC_PAGE_SIZE = $1000; - {$endif MSWINDOWS} { TFileVersion } @@ -38747,6 +38408,18 @@ function TFileVersion.VersionInfo: RawUTF8; FormatUTF8('% % %',[ExtractFileName(fFileName),fDetailed,BuildDateTimeString],result); end; +function TFileVersion.UserAgent: RawUTF8; +begin + if self=nil then + result := '' else + FormatUTF8('%/%%',[GetFileNameWithoutExt(ExtractFileName(fFileName)), + DetailedOrVoid,OS_INITIAL[OS_KIND]],result); + {$ifdef MSWINDOWS} + if OSVersion in WINDOWS_32 then + result := result+'32'; + {$endif} +end; + class function TFileVersion.GetVersionInfo(const aFileName: TFileName): RawUTF8; begin with Create(aFileName,0,0,0,0) do @@ -38847,17 +38520,19 @@ function GetSystemPath(kind: TSystemPath): TFileName; CSIDL_COMMON_APPDATA = $0023; CSIDL_COMMON_DOCUMENTS = $002E; CSIDL: array[TSystemPath] of integer = ( - // spCommonData, spUserData, spCommonDocuments + // spCommonData, spUserData, spCommonDocuments CSIDL_COMMON_APPDATA, CSIDL_LOCAL_APPDATA, CSIDL_COMMON_DOCUMENTS, // spUserDocuments, spTempFolder, spLog - CSIDL_PERSONAL, 0, CSIDL_COMMON_APPDATA); + CSIDL_PERSONAL, 0, CSIDL_LOCAL_APPDATA); ENV: array[TSystemPath] of TFileName = ( - 'ALLUSERSAPPDATA', 'LOCALAPPDATA', '', '', 'TEMP', 'ALLUSERSAPPDATA'); + 'ALLUSERSAPPDATA', 'LOCALAPPDATA', '', '', 'TEMP', 'LOCALAPPDATA'); var tmp: array[0..MAX_PATH] of char; k: TSystemPath; begin - if _SystemPath[spCommonData]='' then begin + if _SystemPath[spCommonData]='' then for k := low(k) to high(k) do + if (k=spLog) and IsDirectoryWritable(ExeVersion.ProgramFilePath) then + _SystemPath[k] := EnsureDirectoryExists(ExeVersion.ProgramFilePath+'log') else if (CSIDL[k]<>0) and (SHGetFolderPath(0,CSIDL[k],0,0,@tmp)=S_OK) then _SystemPath[k] := IncludeTrailingPathDelimiter(tmp) else begin _SystemPath[k] := GetEnvironmentVariable(ENV[k]); @@ -38865,33 +38540,42 @@ function GetSystemPath(kind: TSystemPath): TFileName; _SystemPath[k] := GetEnvironmentVariable('APPDATA'); _SystemPath[k] := IncludeTrailingPathDelimiter(_SystemPath[k]); end; - _SystemPath[spTempFolder] := IncludeTrailingPathDelimiter(GetEnvironmentVariable('TEMP')); - end; result := _SystemPath[kind]; end; {$else MSWINDOWS} var - _HomePath, _TempPath, _LogPath: TFileName; + _HomePath, _TempPath, _UserPath, _LogPath: TFileName; function GetSystemPath(kind: TSystemPath): TFileName; begin case kind of spLog: begin if _LogPath='' then - if DirectoryExists('/var/log') then - _LogPath := '/var/log/' else - _LogPath := GetSystemPath(spUserDocuments); // fallback to HOME + if IsDirectoryWritable('/var/log') then + _LogPath := '/var/log/' else // may not be writable by not root on POSIX + if IsDirectoryWritable(ExeVersion.ProgramFilePath) then + _LogPath := ExeVersion.ProgramFilePath else + _LogPath := GetSystemPath(spUserData); result := _LogPath; end; + spUserData: begin + if _UserPath='' then begin // ~/.cache/appname + _UserPath := GetEnvironmentVariable('XDG_CACHE_HOME'); + if (_UserPath='') or not IsDirectoryWritable(_UserPath) then + _UserPath := EnsureDirectoryExists(GetSystemPath(spUserDocuments)+'.cache'); + _UserPath := EnsureDirectoryExists(_UserPath+UTF8ToString(ExeVersion.ProgramName)); + end; + result := _UserPath; + end; spTempFolder: begin if _TempPath='' then begin _TempPath := GetEnvironmentVariable('TMPDIR'); // POSIX if _TempPath='' then _TempPath := GetEnvironmentVariable('TMP'); if _TempPath='' then - if DirectoryExists('/var/tmp') then - _TempPath := '/var/tmp' else - _TempPath := '/tmp'; + if DirectoryExists('/tmp') then + _TempPath := '/tmp' else + _TempPath := '/var/tmp'; _TempPath := IncludeTrailingPathDelimiter(_TempPath); end; result := _TempPath; @@ -38941,10 +38625,10 @@ procedure PatchCode(Old,New: pointer; Size: integer; Backup: pointer=nil; if Backup<>nil then for i := 0 to Size-1 do // do not use Move() here PByteArray(Backup)^[i] := PByteArray(Old)^[i]; - PageSize := _SC_PAGE_SIZE; - AlignedAddr := PtrUInt(Old) and not (PageSize - 1); - while PtrUInt(Old) + PtrUInt(Size) >= AlignedAddr + PageSize do - Inc(PageSize,_SC_PAGE_SIZE); + PageSize := SystemInfo.dwPageSize; + AlignedAddr := PtrUInt(Old) and not (PageSize-1); + while PtrUInt(Old)+PtrUInt(Size)>=AlignedAddr+PageSize do + Inc(PageSize,SystemInfo.dwPageSize); {$ifdef USEMPROTECT} if mprotect(Pointer(AlignedAddr),PageSize,PROT_READ or PROT_WRITE or PROT_EXEC)=0 then {$else} @@ -38969,7 +38653,7 @@ procedure PatchCodePtrUInt(Code: PPtrUInt; Value: PtrUInt; procedure RedirectCode(Func, RedirectFunc: Pointer; Backup: PPatchCode=nil); var NewJump: packed record Code: byte; // $e9 = jmp {relative} - Distance: integer; // relative jump is 32 bit even on CPU64 + Distance: integer; // relative jump is 32-bit even on CPU64 end; begin if (Func=nil) or (RedirectFunc=nil) then @@ -39091,7 +38775,8 @@ function TSortedWordArray.Add(aValue: Word): PtrInt; if Count=length(Values) then SetLength(Values,Count+100); if result J; - if L < J then - QuickSortCompare(OnCompare,Index,L,J); - L := I; - until I >= R; + if J - L < R - I then begin // use recursion only for smaller range + if L < J then + QuickSortCompare(OnCompare, Index, L, J); + L := I; + end else begin + if I < R then + QuickSortCompare(OnCompare, Index, I, R); + R := J; + end; + until L >= R; end; procedure Exchg32(var A,B: integer); {$ifdef HASINLINE}inline;{$endif} @@ -39436,14 +39127,52 @@ function FromVarUInt32High(var Source: PByte): cardinal; result := result and $FFFFFFF or c; end; +function ToVarInt64(Value: Int64; Dest: PByte): PByte; +begin // 0=0,1=1,2=-1,3=2,4=-2... + {$ifdef CPU32} + if Value<=0 then + // 0->0, -1->2, -2->4.. + result := ToVarUInt64((-Value) shl 1,Dest) else + // 1->1, 2->3.. + result := ToVarUInt64((Value shl 1)-1,Dest); + {$else} + if Value<=0 then + // 0->0, -1->2, -2->4.. + Value := (-Value) shl 1 else + // 1->1, 2->3.. + Value := (Value shl 1)-1; + result := ToVarUInt64(Value,Dest); + {$endif} +end; + function ToVarUInt64(Value: QWord; Dest: PByte): PByte; +label _1,_2,_3; // ugly but fast var c: cardinal; begin - if {$ifdef CPU32}Int64Rec(Value).Hi=0{$else}Value shr 32=0{$endif} then begin - result := ToVarUInt32(Value,Dest); + c := Value; + if {$ifdef CPU32}PInt64Rec(@Value)^.Hi=0{$else}Value shr 32=0{$endif} then begin + if c>$7f then begin // inlined result := ToVarUInt32(Value,Dest); + if c<$80 shl 7 then goto _1 else + if c<$80 shl 14 then goto _2 else + if c<$80 shl 21 then goto _3; + Dest^ := (c and $7F) or $80; + c := c shr 7; + inc(Dest); + _3: Dest^ := (c and $7F) or $80; + c := c shr 7; + inc(Dest); + _2: Dest^ := (c and $7F) or $80; + c := c shr 7; + inc(Dest); + _1: Dest^ := (c and $7F) or $80; + c := c shr 7; + inc(Dest); + end; + Dest^ := c; + inc(Dest); + result := Dest; exit; end; - c := Value; PCardinal(Dest)^ := (c and $7F) or (((c shr 7)and $7F)shl 8) or (((c shr 14)and $7F)shl 16) or (((c shr 21)and $7F)shl 24) or $80808080; Value := Value shr 28; @@ -39488,24 +39217,6 @@ function FromVarUInt64(var Source: PByte): QWord; Source := p; end; -function ToVarInt64(Value: Int64; Dest: PByte): PByte; -begin // 0=0,1=1,2=-1,3=2,4=-2... - {$ifdef CPU32} - if Value<=0 then - // 0->0, -1->2, -2->4.. - result := ToVarUInt64((-Value) shl 1,Dest) else - // 1->1, 2->3.. - result := ToVarUInt64((Value shl 1)-1,Dest); - {$else} - if Value<=0 then - // 0->0, -1->2, -2->4.. - Value := (-Value) shl 1 else - // 1->1, 2->3.. - Value := (Value shl 1)-1; - result := ToVarUInt64(Value,Dest); - {$endif} -end; - function FromVarInt64(var Source: PByte): Int64; var c,n: PtrUInt; begin // 0=0,1=1,2=-1,3=2,4=-2... @@ -39545,7 +39256,7 @@ function FromVarInt64(var Source: PByte): Int64; inc(Source); until false; result := result or (Int64(c) shl n); - if Int64Rec(result).Lo and 1<>0 then + if PCardinal(@result)^ and 1<>0 then // 1->1, 3->2.. result := result shr 1+1 else // 0->0, 2->-1, 4->-2.. @@ -39585,7 +39296,7 @@ function FromVarInt64Value(Source: PByte): Int64; inc(Source); until false; result := result or (Int64(c) shl n); - if {$ifdef CPU64}result{$else}Int64Rec(result).Lo{$endif} and 1<>0 then + if {$ifdef CPU64}result{$else}PCardinal(@result)^{$endif} and 1<>0 then // 1->1, 3->2.. result := result shr 1+1 else // 0->0, 2->-1, 4->-2.. @@ -39604,7 +39315,10 @@ function FromVarInt64Value(Source: PByte): Int64; function GotoNextVarInt(Source: PByte): pointer; begin if Source<>nil then begin - while Source^>$7f do inc(Source); + if Source^>$7f then + repeat + inc(Source) + until Source^<=$7f; inc(Source); end; result := Source; @@ -39616,7 +39330,7 @@ function ToVarString(const Value: RawUTF8; Dest: PByte): PByte; Len := Length(Value); Dest := ToVarUInt32(Len,Dest); if Len>0 then begin - MoveFast(pointer(Value)^,Dest^,Len); + {$ifdef FPC}Move{$else}MoveFast{$endif}(pointer(Value)^,Dest^,Len); result := pointer(PAnsiChar(Dest)+Len); end else result := Dest; @@ -39697,7 +39411,7 @@ function OldRTTIManagedSize(typeInfo: Pointer): SizeInt; inline; procedure RecordCopy(var Dest; const Source; TypeInfo: pointer); begin // external name 'FPC_COPY' does not work as we need FPCFinalize(@Dest,TypeInfo); - MoveFast(Source,Dest,OldRTTIManagedSize(TypeInfo)); + Move(Source,Dest,OldRTTIManagedSize(TypeInfo)); FPCRecordAddRef(Dest,TypeInfo); end; {$else} @@ -39935,7 +39649,7 @@ function ManagedTypeSaveLength(data: PAnsiChar; info: PTypeInfo; result := DynArray.SaveToLength; end; tkInterface: begin - len := SizeOf(Int64); // consume 64 bits even on CPU32 + len := SizeOf(Int64); // consume 64-bit even on CPU32 result := SizeOf(PtrUInt); end; else @@ -39965,7 +39679,7 @@ function ManagedTypeSave(data, dest: PAnsiChar; info: PTypeInfo; itemsize := itemsize*2; {$endif} result := pointer(ToVarUInt32(itemsize,pointer(dest))); - MoveFast(pointer(P^)^,result^,itemsize); + {$ifdef FPC}Move{$else}MoveFast{$endif}(pointer(P^)^,result^,itemsize); inc(result,itemsize); len := SizeOf(PtrUInt); // size of tkLString+tkWString+tkUString in record end; @@ -39976,7 +39690,7 @@ function ManagedTypeSave(data, dest: PAnsiChar; info: PTypeInfo; if info=nil then result := nil else if itemtype=nil then begin - MoveFast(data^,dest^,len); + {$ifdef FPC}Move{$else}MoveFast{$endif}(data^,dest^,len); result := dest+len; end else begin for i := 1 to info^.elCount do begin @@ -40002,7 +39716,7 @@ function ManagedTypeSave(data, dest: PAnsiChar; info: PTypeInfo; {$ifndef DELPHI5OROLDER} tkInterface: begin PIInterface(dest)^ := PIInterface(data)^; // with proper refcount - result := dest+SizeOf(Int64); // consume 64 bits even on CPU32 + result := dest+SizeOf(Int64); // consume 64-bit even on CPU32 len := SizeOf(PtrUInt); end; {$endif} @@ -40050,7 +39764,7 @@ function ManagedTypeLoad(data: PAnsiChar; var source: PAnsiChar; info: PTypeInfo if info=nil then source := nil else if itemtype=nil then begin - MoveFast(source^,data^,result); + {$ifdef FPC}Move{$else}MoveFast{$endif}(source^,data^,result); inc(source,result); end else for i := 1 to info^.elCount do begin @@ -40073,7 +39787,7 @@ function ManagedTypeLoad(data: PAnsiChar; var source: PAnsiChar; info: PTypeInfo {$ifndef DELPHI5OROLDER} tkInterface: begin PIInterface(data)^ := PIInterface(source)^; // with proper refcount - inc(source,SizeOf(Int64)); // consume 64 bits even on CPU32 + inc(source,SizeOf(Int64)); // consume 64-bit even on CPU32 result := SizeOf(PtrUInt); end; {$endif} @@ -40109,7 +39823,7 @@ function RecordEquals(const RecA, RecB; TypeInfo: pointer; A := @RecA; B := @RecB; result := false; - info := GetTypeInfo(TypeInfo,tkRecordTypeOrSet); + info := GetTypeInfo(TypeInfo,tkRecordKinds); if info=nil then exit; // raise Exception.CreateUTF8('% is not a record',[Typ^.Name]); if PRecSize<>nil then @@ -40121,7 +39835,7 @@ function RecordEquals(const RecA, RecB; TypeInfo: pointer; offset := 0; for F := 1 to GetManagedFields(info,field) do begin fieldinfo := DeRef(field^.TypeInfo); - {$ifdef FPC_OLDRTTI} // FPC did include RTTI for unmanaged fields + {$ifdef FPC_OLDRTTI} // old FPC did include RTTI for unmanaged fields if not (fieldinfo^.Kind in tkManagedTypes) then begin inc(field); continue; // as with Delphi @@ -40156,7 +39870,7 @@ function RecordSaveLength(const Rec; TypeInfo: pointer; Len: PInteger): integer; R: PAnsiChar; begin R := @Rec; - info := GetTypeInfo(TypeInfo,tkRecordTypeOrSet); + info := GetTypeInfo(TypeInfo,tkRecordKinds); if (R=nil) or (info=nil) then begin result := 0; // should have been checked before exit; @@ -40166,7 +39880,7 @@ function RecordSaveLength(const Rec; TypeInfo: pointer; Len: PInteger): integer; Len^ := result; for F := 1 to GetManagedFields(info,field) do begin fieldinfo := DeRef(field^.TypeInfo); - {$ifdef FPC_OLDRTTI} // FPC did include RTTI for unmanaged fields! :) + {$ifdef FPC_OLDRTTI} // old FPC did include RTTI for unmanaged fields! :) if not (fieldinfo^.Kind in tkManagedTypes) then begin inc(field); continue; // as with Delphi @@ -40190,7 +39904,7 @@ function RecordSave(const Rec; Dest: PAnsiChar; TypeInfo: pointer; R: PAnsiChar; begin R := @Rec; - info := GetTypeInfo(TypeInfo,tkRecordTypeOrSet); + info := GetTypeInfo(TypeInfo,tkRecordKinds); if (R=nil) or (info=nil) then begin result := nil; // should have been checked before exit; @@ -40207,7 +39921,7 @@ function RecordSave(const Rec; Dest: PAnsiChar; TypeInfo: pointer; fieldinfo := DeRef(field^.TypeInfo); {$endif} {$endif} - {$ifdef FPC_OLDRTTI} // FPC did include RTTI for unmanaged fields! :) + {$ifdef FPC_OLDRTTI} // old FPC did include RTTI for unmanaged fields! :) if not (fieldinfo^.Kind in tkManagedTypes) then begin inc(field); continue; // as with Delphi @@ -40215,7 +39929,7 @@ function RecordSave(const Rec; Dest: PAnsiChar; TypeInfo: pointer; {$endif}; offset := integer(field^.Offset)-offset; if offset>0 then begin - MoveFast(R^,Dest^,offset); + {$ifdef FPC}Move{$else}MoveFast{$endif}(R^,Dest^,offset); inc(R,offset); inc(Dest,offset); end; @@ -40232,7 +39946,7 @@ function RecordSave(const Rec; Dest: PAnsiChar; TypeInfo: pointer; if offset<0 then raise ESynException.Create('RecordSave offset<0') else if offset<>0 then begin - MoveFast(R^,Dest^,offset); + {$ifdef FPC}Move{$else}MoveFast{$endif}(R^,Dest^,offset); result := Dest+offset; end else result := Dest; @@ -40245,48 +39959,72 @@ function RecordSave(const Rec; Dest: PAnsiChar; TypeInfo: pointer): PAnsiChar; end; function RecordSave(const Rec; TypeInfo: pointer): RawByteString; -var Len: integer; +var destlen,dummylen: integer; + dest: PAnsiChar; begin - Len := RecordSaveLength(Rec,TypeInfo); - SetString(result,nil,Len); - if Len<>0 then - RecordSave(Rec,pointer(result),TypeInfo,Len); + destlen := RecordSaveLength(Rec,TypeInfo); + SetString(result,nil,destlen); + if destlen<>0 then begin + dest := RecordSave(Rec,pointer(result),TypeInfo,dummylen); + if (dest=nil) or (dest-pointer(result)<>destlen) then // paranoid check + raise ESynException.CreateUTF8('RecordSave % len=%<>%', + [TypeInfoToShortString(TypeInfo)^,dest-pointer(result),destlen]); + end; +end; + +function RecordSaveBytes(const Rec; TypeInfo: pointer): TBytes; +var destlen,dummylen: integer; + dest: PAnsiChar; +begin + destlen := RecordSaveLength(Rec,TypeInfo); + result := nil; // don't reallocate TBytes data from a previous call + SetLength(result,destlen); + if destlen<>0 then begin + dest := RecordSave(Rec,pointer(result),TypeInfo,dummylen); + if (dest=nil) or (dest-pointer(result)<>destlen) then // paranoid check + raise ESynException.CreateUTF8('RecordSave % len=%<>%', + [TypeInfoToShortString(TypeInfo)^,dest-pointer(result),destlen]); + end; +end; + +procedure RecordSave(const Rec; var Dest: TSynTempBuffer; TypeInfo: pointer); +var dummy: integer; +begin + Dest.Init(RecordSaveLength(Rec,TypeInfo)); + RecordSave(Rec,Dest.buf,TypeInfo,dummy); end; function RecordSaveBase64(const Rec; TypeInfo: pointer; UriCompatible: boolean): RawUTF8; var len,dummy: integer; - data: RawByteString; - dat: PAnsiChar; + temp: TSynTempBuffer; begin result := ''; len := RecordSaveLength(Rec,TypeInfo); if len=0 then exit; - SetLength(data,len+4); - dat := PAnsiChar(pointer(data))+4; - RecordSave(Rec,dat,TypeInfo,dummy); - PCardinal(data)^ := crc32c(0,dat,len); - result := BinToBase64(data); + temp.Init(len+4); + RecordSave(Rec,PAnsiChar(temp.buf)+4,TypeInfo,dummy); + PCardinal(temp.buf)^ := crc32c(0,PAnsiChar(temp.buf)+4,len); if UriCompatible then - Base64ToURI(result); + result := BinToBase64uri(temp.buf,temp.len) else + result := BinToBase64(temp.buf,temp.len); + temp.Done; end; function RecordLoadBase64(Source: PAnsiChar; Len: integer; var Rec; TypeInfo: pointer; UriCompatible: boolean): boolean; -var data: RawByteString; +var temp: TSynTempBuffer; begin result := false; if Len<=6 then exit; if UriCompatible then - Base64uriToBin(Source,Len,data) else - Base64ToBin(Source,Len,data); - Len := length(data); - if Len<=4 then - exit; - Source := PAnsiChar(pointer(data))+4; - if crc32c(0,Source,Len-4)=PCardinal(data)^ then - result := RecordLoad(Rec,Source,TypeInfo)<>nil; + result := Base64uriToBin(Source,Len,temp) else + result := Base64ToBin(Source,Len,temp); + result := result and (temp.len>=4) and + (crc32c(0,PAnsiChar(temp.buf)+4,temp.len-4)=PCardinal(temp.buf)^) and + (RecordLoad(Rec,PAnsiChar(temp.buf)+4,TypeInfo)<>nil); + temp.Done; end; function RecordLoad(var Rec; Source: PAnsiChar; TypeInfo: pointer; @@ -40298,7 +40036,7 @@ function RecordLoad(var Rec; Source: PAnsiChar; TypeInfo: pointer; begin result := nil; // indicates error R := @Rec; - info := GetTypeInfo(TypeInfo,tkRecordTypeOrSet); + info := GetTypeInfo(TypeInfo,tkRecordKinds); if (R=nil) or (info=nil) then // should have been checked before exit; if Len<>nil then @@ -40322,7 +40060,7 @@ function RecordLoad(var Rec; Source: PAnsiChar; TypeInfo: pointer; fieldinfo := DeRef(field^.TypeInfo); {$endif} {$endif} - {$ifdef FPC_OLDRTTI} // FPC did include RTTI for unmanaged fields! :) + {$ifdef FPC_OLDRTTI} // old FPC did include RTTI for unmanaged fields! :) if not (fieldinfo^.Kind in tkManagedTypes) then begin inc(field); continue; // as with Delphi @@ -40330,7 +40068,7 @@ function RecordLoad(var Rec; Source: PAnsiChar; TypeInfo: pointer; {$endif}; offset := integer(field^.Offset)-offset; if offset<>0 then begin - MoveFast(Source^,R^,offset); + {$ifdef FPC}Move{$else}MoveFast{$endif}(Source^,R^,offset); inc(Source,offset); inc(R,offset); end; @@ -40345,12 +40083,19 @@ function RecordLoad(var Rec; Source: PAnsiChar; TypeInfo: pointer; if offset<0 then raise ESynException.Create('RecordLoad offset<0') else if offset<>0 then begin - MoveFast(Source^,R^,offset); + {$ifdef FPC}Move{$else}MoveFast{$endif}(Source^,R^,offset); result := Source+offset; end else result := Source; end; +function RecordLoad(var Res; const Source: RawByteString; TypeInfo: pointer): boolean; overload; +var P: PAnsiChar; +begin + P := RecordLoad(Res,pointer(Source),TypeInfo,nil); + result := (P<>nil) and (P-pointer(Source)=length(Source)); +end; + {$ifndef FPC} {$ifdef USEPACKAGES} @@ -41374,34 +41119,6 @@ procedure FillCharSSE2; @done: end; -function StrLenSSE42(S: pointer): PtrInt; -asm // warning: may read up to 15 bytes beyond the string itself - mov edx, eax // copy pointer - test eax, eax - jz @null // returns 0 if S=nil - xor eax, eax - pxor xmm0, xmm0 - {$ifdef HASAESNI} - pcmpistri xmm0, dqword[edx], EQUAL_EACH // comparison result in ecx - {$else} - db $66, $0F, $3A, $63, $02, EQUAL_EACH - {$endif} - jnz @loop - mov eax, ecx - ret - nop // for @loop alignment -@loop: add eax, 16 - {$ifdef HASAESNI} - pcmpistri xmm0, dqword[edx + eax], EQUAL_EACH // comparison result in ecx - {$else} - db $66, $0F, $3A, $63, $04, $10, EQUAL_EACH - {$endif} - jnz @loop -@ok: add eax, ecx - ret -@null: db $f3 // rep ret -end; - {$endif DELPHI5OROLDER} {$endif PUREPASCAL} @@ -41417,10 +41134,12 @@ procedure InitRedirectCode; {$else DELPHI5OROLDER} {$ifdef CPU64} {$ifdef HASAESNI} + {$ifdef FORCE_STRSSE42} if cfSSE42 in CpuFeatures then begin StrLen := @StrLenSSE42; StrComp := @StrCompSSE42; end else + {$endif FORCE_STRSSE42} {$endif HASAESNI} StrLen := @StrLenSSE2; {$ifdef WITH_ERMS}{$ifdef MSWINDOWS} // disabled (slower for small blocks) @@ -41434,8 +41153,10 @@ procedure InitRedirectCode; {$else CPU64} {$ifdef CPUINTEL} if cfSSE2 in CpuFeatures then begin + {$ifdef FORCE_STRSSE42} if cfSSE42 in CpuFeatures then StrLen := @StrLenSSE42 else + {$endif FORCE_STRSSE42} StrLen := @StrLenSSE2; FillcharFast := @FillCharSSE2; end else begin @@ -41447,7 +41168,7 @@ procedure InitRedirectCode; MoveFast := @MoveERMSB; FillcharFast := @FillCharERMSB; end else {$endif} - MoveFast := @MoveX87; // SSE2 is not faster than X87 version on 32 bit CPU + MoveFast := @MoveX87; // SSE2 is not faster than X87 version on 32-bit CPU {$endif CPUINTEL} {$endif CPU64} {$endif DELPHI5OROLDER} @@ -41600,7 +41321,7 @@ function TJSONCustomParsers.TryToGetFromRTTI(aDynArrayTypeInfo, RegRoot := TJSONCustomParserRTTI.CreateFromTypeName('',Reg.RecordTypeName); {$ifdef ISDELPHI2010} if RegRoot=nil then begin - info := GetTypeInfo(aRecordTypeInfo,tkRecordTypeOrSet); + info := GetTypeInfo(aRecordTypeInfo,tkRecordKinds); if info=nil then exit; // not enough RTTI inc(PByte(info),info^.ManagedCount*SizeOf(TFieldInfo)-SizeOf(TFieldInfo)); @@ -41766,7 +41487,7 @@ function TJSONCustomParsers.Search(aTypeInfo: pointer; begin if (aTypeInfo=nil) or (self=nil) then raise ESynException.CreateUTF8('%.Search(%)',[self,aTypeInfo]); - FillcharFast(Reg,SizeOf(Reg),0); + {$ifdef FPC}FillChar{$else}FillCharFast{$endif}(Reg,SizeOf(Reg),0); case PTypeKind(aTypeInfo)^ of tkDynArray: begin Reg.DynArrayTypeInfo := aTypeInfo; @@ -41911,12 +41632,12 @@ function ManagedTypeSaveRTTIHash(info: PTypeInfo; var crc: cardinal): integer; {$endif} tkRecord{$ifdef FPC},tkObject{$endif}: // first search from custom RTTI text if not GlobalJSONCustomParsers.RecordRTTITextHash(info,crc,result) then begin - itemtype := GetTypeInfo(info,tkRecordTypeOrSet); + itemtype := GetTypeInfo(info,tkRecordKinds); if itemtype<>nil then begin unmanagedsize := itemtype^.recsize; for i := 1 to GetManagedFields(itemtype,field) do begin info := DeRef(field^.TypeInfo); - {$ifdef FPC_OLDRTTI} // FPC did include RTTI for unmanaged fields + {$ifdef FPC_OLDRTTI} // old FPC did include RTTI for unmanaged fields if info^.Kind in tkManagedTypes then // as with Delphi {$endif} dec(unmanagedsize,ManagedTypeSaveRTTIHash(info,crc)); @@ -41964,6 +41685,7 @@ function RecordLoadJSON(var Rec; JSON: PUTF8Char; TypeInfo: pointer; Reader: TDynArrayJSONCustomReader; FirstChar,EndOfObj: AnsiChar; Val: PUTF8Char; + ValLen: integer; begin // code below must match TTextWriter.AddRecordJSON result := nil; // indicates error if JSON=nil then @@ -41975,10 +41697,10 @@ function RecordLoadJSON(var Rec; JSON: PUTF8Char; TypeInfo: pointer; if not (PTypeKind(TypeInfo)^ in tkRecordTypes) then raise ESynException.CreateUTF8('RecordLoadJSON(%/%)', [PShortString(@PTypeInfo(TypeInfo).NameLen)^,ToText(PTypeKind(TypeInfo)^)^]); - Val := GetJSONField(JSON,JSON,@wasString,@EndOfObj); - if (Val=nil) or not wasString or + Val := GetJSONField(JSON,JSON,@wasString,@EndOfObj,@ValLen); + if (Val=nil) or not wasString or (ValLen<3) or (PInteger(Val)^ and $00ffffff<>JSON_BASE64_MAGIC) or - (RecordLoad(Rec,pointer(Base64ToBin(Val+3)),TypeInfo)=nil) then + (RecordLoad(Rec,pointer(Base64ToBin(PAnsiChar(Val)+3,ValLen-3)),TypeInfo)=nil) then exit; // invalid content end else begin if not GlobalJSONCustomParsers.RecordSearch(TypeInfo,Reader) then @@ -42184,7 +41906,7 @@ function TJSONCustomParserCustomSimple.CustomReader(P: PUTF8Char; [self,fCustomTypeName]); ktSet: begin i32 := GetSetNameValue(fCustomTypeInfo,P,EndOfObject); - MoveFast(i32,aValue,fDataSize); + {$ifdef FPC}Move{$else}MoveFast{$endif}(i32,aValue,fDataSize); result := P; end; else begin // encoded as JSON strings or number @@ -42203,7 +41925,7 @@ function TJSONCustomParserCustomSimple.CustomReader(P: PUTF8Char; i32 := GetCardinal(PropValue); if i32<0 then exit; - MoveFast(i32,aValue,fDataSize); + {$ifdef FPC}Move{$else}MoveFast{$endif}(i32,aValue,fDataSize); result := P; end; ktFixedArray: @@ -42212,14 +41934,14 @@ function TJSONCustomParserCustomSimple.CustomReader(P: PUTF8Char; result := P; ktBinary: if wasString then begin // default hexa serialization - FillcharFast(aValue,fDataSize,0); + {$ifdef FPC}FillChar{$else}FillCharFast{$endif}(aValue,fDataSize,0); if (PropValueLen=0) or ((PropValueLen=fFixedSize*2) and HexDisplayToBin(PAnsiChar(PropValue),@aValue,fFixedSize)) then result := P; end else if fFixedSize<=SizeOf(u64) then begin // allow integer serialization SetQWord(PropValue,u64); - MoveFast(u64,aValue,fDataSize); + {$ifdef FPC}Move{$else}MoveFast{$endif}(u64,aValue,fDataSize); result := P; end; end; @@ -42420,7 +42142,9 @@ class function TJSONCustomParserRTTI.TypeInfoToSimpleRTTIType(Info: pointer; {$endif} end; tkInt64: - {$ifndef FPC}if Info=TypeInfo(QWord) then result := ptQWord else{$endif} + {$ifndef FPC} if Info=TypeInfo(QWord) then result := ptQWord else + {$ifdef UNICODE}with GetTypeInfo(Info)^ do // detect QWord/UInt64 + if MinInt64Value>MaxInt64Value then result := ptQWord else{$endif}{$endif} result := ptInt64; {$ifdef FPC} tkQWord: result := ptQWord; @@ -42505,7 +42229,7 @@ class function TJSONCustomParserRTTI.CreateFromTypeName( end; procedure TJSONCustomParserRTTI.ComputeFullPropertyName; -var i: integer; +var i: PtrInt; begin for i := 0 to high(NestedProperty) do begin NestedProperty[i].ComputeFullPropertyName; @@ -42516,7 +42240,7 @@ procedure TJSONCustomParserRTTI.ComputeFullPropertyName; end; procedure TJSONCustomParserRTTI.ComputeNestedDataSize; -var i: integer; +var i: PtrInt; begin assert(fNestedDataSize=0); fNestedDataSize := 0; @@ -42539,7 +42263,7 @@ procedure TJSONCustomParserRTTI.ComputeDataSizeAfterAdd; SizeOf(TGUID),SizeOf(Int64),SizeOf(TTimeLog), {$ifndef NOVARIANTS}SizeOf(Variant),{$endif} SizeOf(WideString),SizeOf(Word),0); -var i: integer; +var i: PtrInt; begin if fFullPropertyName='' then begin fFullPropertyName := fPropertyName; @@ -42562,7 +42286,7 @@ procedure TJSONCustomParserRTTI.ComputeDataSizeAfterAdd; end; procedure TJSONCustomParserRTTI.FinalizeNestedRecord(var Data: PByte); -var j: integer; +var j: PtrInt; begin for j := 0 to length(NestedProperty)-1 do begin case NestedProperty[j].PropertyType of @@ -42589,7 +42313,7 @@ procedure TJSONCustomParserRTTI.FinalizeNestedRecord(var Data: PByte); procedure TJSONCustomParserRTTI.FinalizeNestedArray(var Data: PtrUInt); var i: integer; - Rec: ^TDynArrayRec; + Rec: PDynArrayRec; ItemData: PByte; begin if Data=0 then @@ -42597,10 +42321,14 @@ procedure TJSONCustomParserRTTI.FinalizeNestedArray(var Data: PtrUInt); ItemData := pointer(Data); Rec := pointer(Data); dec(PtrUInt(Rec),SizeOf(TDynArrayRec)); - for i := 0 to Rec.length-1 do + Data := 0; + if Rec^.refCnt>1 then begin + InterlockedDecrement(PInteger(@Rec^.refCnt)^); // FPC has refCnt: PtrInt + exit; + end; + for i := 1 to Rec.length do FinalizeNestedRecord(ItemData); FreeMem(Rec); - Data := 0; end; procedure TJSONCustomParserRTTI.AllocateNestedArray(var Data: PtrUInt; @@ -42625,7 +42353,8 @@ procedure TJSONCustomParserRTTI.ReAllocateNestedArray(var Data: PtrUInt; ReAllocMem(pointer(Data),SizeOf(TDynArrayRec)+fNestedDataSize*NewLength); OldLength := PDynArrayRec(Data)^.length; if NewLength>OldLength then - FillcharFast(PByteArray(Data)[SizeOf(TDynArrayRec)+fNestedDataSize*OldLength], + {$ifdef FPC}FillChar{$else}FillCharFast{$endif}( + PByteArray(Data)[SizeOf(TDynArrayRec)+fNestedDataSize*OldLength], fNestedDataSize*(NewLength-OldLength),0); PDynArrayRec(Data)^.length := NewLength; inc(Data,SizeOf(TDynArrayRec)); @@ -42681,7 +42410,7 @@ function TJSONCustomParserRTTI.ReadOneLevel(var P: PUTF8Char; var Data: PByte; repeat inc(n); if (ArrayLen<0) and (n>ArrayCapacity) then begin - inc(ArrayCapacity,512+ArrayCapacity shr 3); + ArrayCapacity := NextGrow(ArrayCapacity); Prop.ReAllocateNestedArray(PPtrUInt(Data)^,ArrayCapacity); DynArray := PPointer(Data)^; inc(DynArray,pred(n)*Prop.fNestedDataSize); @@ -42881,7 +42610,7 @@ function Plural(const itemname: shortstring; itemcount: cardinal): shortstring; len := (AppendUInt32ToBuffer(@result[1],itemcount)-PUTF8Char(@result[1]))+1; result[len] := ' '; if ord(itemname[0])<240 then begin // avoid buffer overflow - MoveFast(itemname[1],result[len+1],ord(itemname[0])); + {$ifdef FPC}Move{$else}MoveFast{$endif}(itemname[1],result[len+1],ord(itemname[0])); inc(len,ord(itemname[0])); if itemcount>1 then begin inc(len); @@ -43687,8 +43416,8 @@ function VariantSave(const Value: variant; Dest: PAnsiChar): PAnsiChar; {$endif} end; Dest := pointer(ToVarUInt32(LenBytes,pointer(Dest))); - if LenBytes>0 then begin - MoveFast(PPtrUInt(VAny)^,Dest^,LenBytes); // direct raw copy + if LenBytes>0 then begin // direct raw copy + {$ifdef FPC}Move{$else}MoveFast{$endif}(PPtrUInt(VAny)^,Dest^,LenBytes); inc(Dest,LenBytes); end; end; @@ -44717,6 +44446,16 @@ function _Safe(const DocVariant: variant; ExpectedKind: TDocVariantKind): PDocVa raise EDocVariant.CreateUTF8('_Safe(%)<>%',[ToText(result^.Kind)^,ToText(ExpectedKind)^]); end; +function _CSV(const DocVariantOrString: variant): RawUTF8; +begin + with _Safe(DocVariantOrString)^ do + if dvoIsArray in VOptions then + result := ToCSV else + if (dvoIsObject in VOptions) or (TDocVariantData(DocVariantOrString).VType<=varNull) or + not VariantToUTF8(DocVariantOrString,result) then + result := ''; // VariantToUTF8() returns 'null' for empty/null +end; + function TDocVariantData.GetKind: TDocVariantKind; begin if dvoIsArray in VOptions then @@ -45210,12 +44949,12 @@ function TDocVariantData.InternalAdd(const aName: RawUTF8): integer; include(VOptions,dvoIsArray); end; end; - if VValue=nil then - SetLength(VValue,16) else - if VCount>=length(VValue) then - SetLength(VValue,VCount+VCount shr 3+32); + len := length(VValue); + if VCount>=len then begin + len := NextGrow(VCount); + SetLength(VValue,len); + end; if aName<>'' then begin - len := length(VValue); if Length(VName)<>len then SetLength(VName,len); if dvoInternNames in VOptions then begin // inlined InternNames method @@ -45428,10 +45167,16 @@ procedure QuickSortDocVariant(names: PPointerArray; values: PVariantArray; inc(I); dec(J); end; until I > J; - if L < J then - QuickSortDocVariant(names,values,L,J,Compare); - L := I; - until I >= R; + if J - L < R - I then begin // use recursion only for smaller range + if L < J then + QuickSortDocVariant(names, values, L, J, Compare); + L := I; + end else begin + if I < R then + QuickSortDocVariant(names, values, I, R, Compare); + R := J; + end; + until L >= R; end; procedure TDocVariantData.SortByName(Compare: TUTF8Compare=nil); @@ -45451,7 +45196,7 @@ procedure ExchgValues(v1,v2: PVarData); v1^ := v; end; -procedure ExchgNames(n1,n2: PPointer); +procedure ExchgNames(n1,n2: PPointer); {$ifdef HASINLINE}inline;{$endif} var n: pointer; begin n := n2^; @@ -45482,16 +45227,102 @@ procedure QuickSortDocVariantValues(var Doc: TDocVariantData; inc(I); dec(J); end; until I > J; - if L < J then - QuickSortDocVariantValues(Doc,L,J,Compare); - L := I; - until I >= R; + if J - L < R - I then begin // use recursion only for smaller range + if L < J then + QuickSortDocVariantValues(Doc, L, J, Compare); + L := I; + end else begin + if I < R then + QuickSortDocVariantValues(Doc, I, R, Compare); + R := J; + end; + until L >= R; end; procedure TDocVariantData.SortByValue(Compare: TVariantCompare); begin - if VCount>0 then - QuickSortDocVariantValues(self,0,VCount-1,Compare); + if VCount<=0 then + exit; + if not Assigned(Compare) then + Compare := VariantCompare; + QuickSortDocVariantValues(self,0,VCount-1,Compare); +end; + +type + {$ifdef FPC_OR_UNICODE}TQuickSortDocVariantValuesByField = record + {$else}TQuickSortDocVariantValuesByField = object{$endif} + Lookup: array of PVariant; + Compare: TVariantCompare; + Doc: PDocVariantData; + Reverse: boolean; + procedure Sort(L, R: PtrInt); + end; + +procedure TQuickSortDocVariantValuesByField.Sort(L, R: PtrInt); +var I, J, P: PtrInt; + pivot: PVariant; +begin + if L0 do Dec(J); + end + else begin + while Compare(Lookup[I]^,pivot^)>0 do Inc(I); + while Compare(Lookup[J]^,pivot^)<0 do Dec(J); + end; + if I <= J then begin + if I <> J then begin + if Doc.VName<>nil then + ExchgNames(@Doc.VName[I],@Doc.VName[J]); + ExchgValues(@Doc.VValue[I],@Doc.VValue[J]); + pivot := Lookup[I]; + Lookup[I] := Lookup[J]; + Lookup[J] := pivot; + end; + if P = I then P := J else if P = J then P := I; + inc(I); dec(J); + end; + until I > J; + if J - L < R - I then begin // use recursion only for smaller range + if L < J then + Sort(L,J); + L := I; + end else begin + if I < R then + Sort(I,R); + R := J; + end; + until L >= R; +end; + +procedure TDocVariantData.SortArrayByField(const aItemPropName: RawUTF8; + aValueCompare: TVariantCompare; aValueCompareReverse: boolean; aNameSortedCompare: TUTF8Compare); +var + QS: TQuickSortDocVariantValuesByField; + p: pointer; + row: PtrInt; +begin + if (VCount<=0) or (aItemPropName='') or not (dvoIsArray in VOptions) then + exit; + if not Assigned(aValueCompare) then + QS.Compare := VariantCompare else + QS.Compare := aValueCompare; + QS.Reverse := aValueCompareReverse; + SetLength(QS.Lookup,VCount); + for row := 0 to VCount-1 do begin // resolve GetPVariantByName(aIdemPropName) once + p := _Safe(VValue[row])^.GetVarData(aItemPropName,aNameSortedCompare); + if p = nil then + p := @NullVarData; + QS.Lookup[row] := p; + end; + QS.Doc := @self; + QS.Sort(0,VCount-1); end; procedure TDocVariantData.Reverse; @@ -45649,10 +45480,12 @@ function TDocVariantData.Delete(Index: integer): boolean; VarClear(VValue[Index]); if Indexnil then begin - MoveFast(VName[Index+1],VName[Index],(VCount-Index)*SizeOf(pointer)); + {$ifdef FPC}Move{$else}MoveFast{$endif}( + VName[Index+1],VName[Index],(VCount-Index)*SizeOf(pointer)); PtrUInt(VName[VCount]) := 0; // avoid GPF end; - MoveFast(VValue[Index+1],VValue[Index],(VCount-Index)*SizeOf(variant)); + {$ifdef FPC}Move{$else}MoveFast{$endif}( + VValue[Index+1],VValue[Index],(VCount-Index)*SizeOf(variant)); TVarData(VValue[VCount]).VType := varEmpty; // avoid GPF end; result := true; @@ -45813,7 +45646,7 @@ function TDocVariantData.GetAsInteger(const aName: RawUTF8; out aValue: integer; found := GetVarData(aName,aSortedCompare); if found=nil then result := false else - result := VariantToInteger(PVariant(found)^,aValue) + result := VariantToInteger(PVariant(found)^,aValue); end; function TDocVariantData.GetAsInt64(const aName: RawUTF8; out aValue: Int64; @@ -45844,7 +45677,8 @@ function TDocVariantData.GetAsRawUTF8(const aName: RawUTF8; out aValue: RawUTF8; found := GetVarData(aName,aSortedCompare); if found=nil then result := false else begin - VariantToUTF8(PVariant(found)^,aValue,wasString); + if found^.VType>varNull then // default VariantToUTF8(null)='null' + VariantToUTF8(PVariant(found)^,aValue,wasString); result := true; end; end; @@ -47229,7 +47063,7 @@ function SortDynArrayPUTF8CharI(const A,B): integer; function SortDynArrayString(const A,B): integer; begin {$ifdef UNICODE} - result := SysUtils.StrComp(PChar(A),PChar(B)); + result := StrCompW(PWideChar(A),PWideChar(B)); {$else} result := StrComp(PUTF8Char(A),PUTF8Char(B)); {$endif} @@ -47299,6 +47133,16 @@ function SortDynArray512(const A,B): integer; {$ifndef NOVARIANTS} +function VariantCompare(const V1,V2: variant): PtrInt; +begin + result := SortDynArrayVariantComp(TVarData(V1), TVarData(V2), false); +end; + +function VariantCompareI(const V1,V2: variant): PtrInt; +begin + result := SortDynArrayVariantComp(TVarData(V1), TVarData(V2), true); +end; + function SortDynArrayVariantCompareAsString(const A,B: variant): integer; var UA,UB: RawUTF8; wasString: boolean; @@ -47367,6 +47211,8 @@ function SortDynArrayVariantComp(const A,B: TVarData; caseInsensitive: boolean): result := ICMP[VarCompareValue(variant(A),variant(B))] else result := CMP[caseInsensitive](variant(A),variant(B)); end else + if (A.VType<=varNull) or (B.VType<=varNull) then + result := ord(A.VType>varNull)-ord(B.VType>varNull) else if (A.VType and VTYPE_STATIC=0) and (B.VType and VTYPE_STATIC=0) then result := ICMP[VarCompareValue(variant(A),variant(B))] else @@ -47416,14 +47262,14 @@ function TDynArray.GetCount: integer; procedure TDynArray.ElemCopy(const A; var B); begin if ElemType=nil then - MoveFast(A,B,ElemSize) else begin + {$ifdef FPC}Move{$else}MoveFast{$endif}(A,B,ElemSize) else begin {$ifdef FPC} {$ifdef FPC_OLDRTTI} FPCFinalize(@B,ElemType); // inlined CopyArray() - MoveFast(A,B,ElemSize); + Move(A,B,ElemSize); FPCRecordAddRef(B,ElemType); {$else} - FPCRecordCopy(A,B,ElemType); + FPCRecordCopy(A,B,ElemType); // works for any kind of ElemTyp {$endif FPC_OLDRTTI} {$else} CopyArray(@B,@A,ElemType,1); @@ -47440,7 +47286,7 @@ function TDynArray.Add(const Elem): PtrInt; SetCount(result+1); p := PtrUInt(fValue^)+PtrUInt(result)*ElemSize; if ElemType=nil then - MoveFast(Elem,pointer(p)^,ElemSize) else + {$ifdef FPC}Move{$else}MoveFast{$endif}(Elem,pointer(p)^,ElemSize) else {$ifdef FPC} FPCRecordCopy(Elem,pointer(p)^,ElemType); {$else} @@ -47486,9 +47332,9 @@ procedure TDynArray.Insert(Index: PtrInt; const Elem); SetCount(n+1); if PtrUInt(Index)nil then - FillcharFast(P^,ElemSize,0); // avoid GPF in ElemCopy() below + {$ifdef FPC}Move{$else}MoveFast{$endif}(P[0],P[ElemSize],PtrUInt(n-Index)*ElemSize); + if ElemType<>nil then // avoid GPF in ElemCopy() below + {$ifdef FPC}FillChar{$else}FillCharFast{$endif}(P^,ElemSize,0); end else // Index>=Count -> add at the end P := pointer(PtrUInt(fValue^)+PtrUInt(n)*ElemSize); @@ -47522,7 +47368,6 @@ function TDynArray.GetIsObjArray: boolean; procedure TDynArray.Delete(aIndex: PtrInt); var n, len: PtrInt; P: PAnsiChar; - zerolast: boolean; begin if fValue=nil then exit; // avoid GPF if void @@ -47531,21 +47376,16 @@ procedure TDynArray.Delete(aIndex: PtrInt); exit; // out of range dec(n); P := pointer(PtrUInt(fValue^)+PtrUInt(aIndex)*ElemSize); - if ElemType<>nil then begin - {$ifdef FPC}FPCFinalize{$else}_Finalize{$endif}(P,ElemType); - zerolast := true; - end else - if (fIsObjArray=oaTrue) or ((fIsObjArray=oaUnknown) and ComputeIsObjArray) then begin + if ElemType<>nil then + {$ifdef FPC}FPCFinalize{$else}_Finalize{$endif}(P,ElemType) else + if (fIsObjArray=oaTrue) or ((fIsObjArray=oaUnknown) and ComputeIsObjArray) then FreeAndNil(PObject(P)^); - zerolast := true; - end else - zerolast := false; if n>aIndex then begin len := PtrUInt(n-aIndex)*ElemSize; - MoveFast(P[ElemSize],P[0],len); - if zerolast then // avoid GPF - FillcharFast(P[len],ElemSize,0); - end; + {$ifdef FPC}Move{$else}MoveFast{$endif}(P[ElemSize],P[0],len); + {$ifdef FPC}FillChar{$else}FillCharFast{$endif}(P[len],ElemSize,0); + end else + {$ifdef FPC}FillChar{$else}FillCharFast{$endif}(P^,ElemSize,0); SetCount(n); end; @@ -47580,9 +47420,9 @@ procedure TDynArray.ElemCopyAt(index: PtrInt; var Dest); p := ElemPtr(index); if p<>nil then if ElemType=nil then - MoveFast(p^,Dest,ElemSize) else + {$ifdef FPC}Move{$else}MoveFast{$endif}(p^,Dest,ElemSize) else {$ifdef FPC} - FPCRecordCopy(p^,Dest,ElemType); + FPCRecordCopy(p^,Dest,ElemType); // works for any kind of ElemTyp {$else} CopyArray(@Dest,p,ElemType,1); {$endif} @@ -47595,8 +47435,8 @@ procedure TDynArray.ElemMoveTo(index: PtrInt; var Dest); if (p=nil) or (@Dest=nil) then exit; ElemClear(Dest); - MoveFast(p^,Dest,ElemSize); - FillCharFast(p^,ElemSize,0); // ElemType=nil for ObjArray + {$ifdef FPC}Move{$else}MoveFast{$endif}(p^,Dest,ElemSize); + {$ifdef FPC}FillChar{$else}FillCharFast{$endif}(p^,ElemSize,0); // ElemType=nil for ObjArray end; procedure TDynArray.ElemCopyFrom(const Source; index: PtrInt; ClearBeforeCopy: boolean); @@ -47605,7 +47445,7 @@ procedure TDynArray.ElemCopyFrom(const Source; index: PtrInt; ClearBeforeCopy: b p := ElemPtr(index); if p<>nil then if ElemType=nil then - MoveFast(Source,p^,ElemSize) else begin + {$ifdef FPC}Move{$else}MoveFast{$endif}(Source,p^,ElemSize) else begin if ClearBeforeCopy then // safer if Source is a copy of p^ {$ifdef FPC}FPCFinalize{$else}_Finalize{$endif}(p,ElemType); {$ifdef FPC} @@ -47639,7 +47479,7 @@ procedure TDynArray.Reverse; end; end; 4: begin - // optimized version for TIntegerDynArray + TRawUTF8DynArray and such + // optimized version for TIntegerDynArray and such P2 := P1+n*SizeOf(Integer); while P1nil then // may be nil if RecordSave/ManagedTypeSave failed - PCardinal(result-SizeOf(Cardinal))^ := Hash32(result,Dest-result); + PCardinal(result-SizeOf(Cardinal))^ := Hash32(pointer(result),Dest-result); result := Dest; end; @@ -48004,7 +47844,7 @@ function TDynArray.ElemCopyFirstField(Source,Dest: Pointer): boolean; ToKnownType(false); case fKnownType of djBoolean..djDateTimeMS,djHash128..djHash512: // no managed field - MoveFast(Source^,Dest^,fKnownSize); + {$ifdef FPC}Move{$else}MoveFast{$endif}(Source^,Dest^,fKnownSize); djRawUTF8, djWinAnsi, djRawByteString: PRawByteString(Dest)^ := PRawByteString(Source)^; djSynUnicode: @@ -48028,7 +47868,7 @@ function TDynArray.LoadKnownType(Data,Source: PAnsiChar): boolean; if fKnownType=djNone then ToKnownType({exacttype=}false); // set fKnownType and fKnownSize if fKnownType in [djBoolean..djDateTimeMS] then begin - MoveFast(Source^,Data^,fKnownSize); + {$ifdef FPC}Move{$else}MoveFast{$endif}(Source^,Data^,fKnownSize); result := true; end else begin info := KINDTYPE_INFO[fKnownType]; @@ -48258,7 +48098,7 @@ function TDynArrayLoadFrom.Step(out Elem): boolean; result := false; if (Position<>nil) and (CurrentJ then + {$ifndef PUREPASCAL} // inlined Exchg() is just fine if ElemSize=SizeOf(pointer) then begin // optimized version e.g. for TRawUTF8DynArray/TObjectDynArray tmp := PPointer(IP)^; PPointer(IP)^ := PPointer(JP)^; PPointer(JP)^ := tmp; end else + {$endif} // generic exchange of row element data Exchg(IP,JP,ElemSize); if P = I then P := J else @@ -48628,10 +48473,94 @@ procedure TDynArrayQuickSort.QuickSort(L, R: PtrInt); Inc(I); Dec(J); end; until I > J; - if L < J then - QuickSort(L, J); - L := I; - until I >= R; + if J - L < R - I then begin // use recursion only for smaller range + if L < J then + QuickSort(L, J); + L := I; + end else begin + if I < R then + QuickSort(I, R); + R := J; + end; + until L >= R; +end; + +procedure TDynArrayQuickSort.QuickSortEvent(L, R: PtrInt); +var I, J: PtrInt; +begin + if L0 do begin + dec(J); + dec(JP,ElemSize); + end; + if I <= J then begin + if I<>J then + Exchg(IP,JP,ElemSize); + if P = I then P := J else + if P = J then P := I; + Inc(I); Dec(J); + end; + until I > J; + if J - L < R - I then begin // use recursion only for smaller range + if L < J then + QuickSortEvent(L, J); + L := I; + end else begin + if I < R then + QuickSortEvent(I, R); + R := J; + end; + until L >= R; +end; + +procedure TDynArrayQuickSort.QuickSortEventReverse(L, R: PtrInt); +var I, J: PtrInt; +begin + if L0 do begin + inc(I); + inc(IP,ElemSize); + end; + while CompareEvent(JP^,Pivot^)<0 do begin + dec(J); + dec(JP,ElemSize); + end; + if I <= J then begin + if I<>J then + Exchg(IP,JP,ElemSize); + if P = I then P := J else + if P = J then P := I; + Inc(I); Dec(J); + end; + until I > J; + if J - L < R - I then begin // use recursion only for smaller range + if L < J then + QuickSortEventReverse(L, J); + L := I; + end else begin + if I < R then + QuickSortEventReverse(I, R); + R := J; + end; + until L >= R; end; procedure TDynArrayQuickSort.QuickSortIndexed(L, R: PtrInt); @@ -48657,15 +48586,56 @@ procedure TDynArrayQuickSort.QuickSortIndexed(L, R: PtrInt); Inc(I); Dec(J); end; until I > J; - if L < J then - QuickSortIndexed(L, J); - L := I; - until I >= R; + if J - L < R - I then begin // use recursion only for smaller range + if L < J then + QuickSortIndexed(L, J); + L := I; + end else begin + if I < R then + QuickSortIndexed(I, R); + R := J; + end; + until L >= R; end; procedure TDynArray.Sort(aCompare: TDynArraySortCompare); begin - SortRange(0,Count-1,aCompare) + SortRange(0,Count-1,aCompare); + fSorted := true; +end; + +procedure QuickSortPtr(L, R: PtrInt; Compare: TDynArraySortCompare; V: PPointerArray); +var I, J, P: PtrInt; + tmp: pointer; +begin + if L0 do + dec(J); + if I <= J then begin + tmp := V[I]; + V[I] := V[J]; + V[J] := tmp; + if P = I then P := J else + if P = J then P := I; + Inc(I); Dec(J); + end; + until I > J; + if J - L < R - I then begin // use recursion only for smaller range + if L < J then + QuickSortPtr(L, J, Compare, V); + L := I; + end else begin + if I < R then + QuickSortPtr(I, R, Compare, V); + R := J; + end; + until L >= R; end; procedure TDynArray.SortRange(aStart, aStop: integer; aCompare: TDynArraySortCompare); @@ -48676,12 +48646,28 @@ procedure TDynArray.SortRange(aStart, aStop: integer; aCompare: TDynArraySortCom if @aCompare=nil then Quicksort.Compare := @fCompare else Quicksort.Compare := aCompare; - if (@Quicksort.Compare<>nil) and (fValue<>nil) and (fValue^<>nil) then begin - Quicksort.Value := fValue^; - Quicksort.ElemSize := ElemSize; - Quicksort.QuickSort(aStart,aStop); - fSorted := true; - end; + if (@Quicksort.Compare<>nil) and (fValue<>nil) and (fValue^<>nil) then + if ElemSize=SizeOf(pointer) then + QuickSortPtr(aStart,aStop,QuickSort.Compare,fValue^) else begin + Quicksort.Value := fValue^; + Quicksort.ElemSize := ElemSize; + Quicksort.QuickSort(aStart,aStop); + end; +end; + +procedure TDynArray.Sort(const aCompare: TEventDynArraySortCompare; aReverse: boolean); +var QuickSort: TDynArrayQuickSort; + R: PtrInt; +begin + if not Assigned(aCompare) or (fValue = nil) or (fValue^=nil) then + exit; // nothing to sort + Quicksort.CompareEvent := aCompare; + Quicksort.Value := fValue^; + Quicksort.ElemSize := ElemSize; + R := Count-1; + if aReverse then + Quicksort.QuickSortEventReverse(0,R) else + Quicksort.QuickSortEvent(0,R); end; procedure TDynArray.CreateOrderedIndex(var aIndex: TIntegerDynArray; @@ -48732,7 +48718,7 @@ procedure TDynArray.CreateOrderedIndexAfterAdd(var aIndex: TIntegerDynArray; exit; if aIndex<>nil then begin // whole FillIncreasing(aIndex[]) for first time if ndx>=length(aIndex) then - SetLength(aIndex,ndx+ndx shr 3+64); // grow aIndex[] if needed + SetLength(aIndex,NextGrow(ndx)); // grow aIndex[] if needed aIndex[ndx] := ndx; end; CreateOrderedIndex(aIndex,aCompare); @@ -48857,7 +48843,7 @@ procedure TDynArray.Copy(const Source: TDynArray; ObjArrayByRef: boolean); if ElemType=nil then if not ObjArrayByRef and GetIsObjArray then LoadFromJSON(pointer(Source.SaveToJSON)) else - MoveFast(Source.fValue^^,fValue^^,n*ElemSize) else + {$ifdef FPC}Move{$else}MoveFast{$endif}(Source.fValue^^,fValue^^,n*ElemSize) else CopyArray(fValue^,Source.fValue^,ElemType,n); end; @@ -48890,22 +48876,22 @@ function TDynArray.IndexOf(const Elem): PtrInt; P := fValue^; if @Elem<>nil then if ElemType=nil then - case ElemSize of - // optimized versions for arrays of byte,word,integer,Int64,Currency,Double - 1: for result := 0 to max do - if PByteArray(P)^[result]=byte(Elem) then exit; - 2: for result := 0 to max do - if PWordArray(P)^[result]=word(Elem) then exit; - 4: for result := 0 to max do // integer,single,32bitPointer - if PIntegerArray(P)^[result]=integer(Elem) then exit; - 8: for result := 0 to max do // Int64,Currency,Double,64bitPointer - if PInt64Array(P)^[result]=Int64(Elem) then exit; - else // generic binary comparison (fast with our overloaded CompareMemFixed) - for result := 0 to max do - if CompareMemFixed(P,@Elem,ElemSize) then - exit else - inc(PByte(P),ElemSize); - end else + case ElemSize of + // optimized versions for arrays of byte,word,integer,Int64,Currency,Double + 1: for result := 0 to max do + if PByteArray(P)^[result]=byte(Elem) then exit; + 2: for result := 0 to max do + if PWordArray(P)^[result]=word(Elem) then exit; + 4: for result := 0 to max do // integer,single,32bitPointer + if PIntegerArray(P)^[result]=integer(Elem) then exit; + 8: for result := 0 to max do // Int64,Currency,Double,64bitPointer + if PInt64Array(P)^[result]=Int64(Elem) then exit; + else // generic binary comparison (fast with our overloaded CompareMemFixed) + for result := 0 to max do + if CompareMemFixed(P,@Elem,ElemSize) then + exit else + inc(PByte(P),ElemSize); + end else case PTypeKind(ElemType)^ of tkLString{$ifdef FPC},tkLStringOld{$endif}: for result := 0 to max do @@ -49034,7 +49020,6 @@ procedure TDynArray.SetIsObjArray(aValue: boolean); procedure TDynArray.InternalSetLength(NewLength: PtrUInt); var p: PDynArrayRec; - pa: PAnsiChar absolute p; OldLength, NeededSize, minLength: PtrUInt; pp: pointer; i: integer; @@ -49052,13 +49037,6 @@ procedure TDynArray.InternalSetLength(NewLength: PtrUInt); {$ifdef FPC}FPCDynArrayClear{$else}_DynArrayClear{$endif}(fValue^,ArrayType); exit; end; - // retrieve old length - p := fValue^; - if p<>nil then begin - dec(PtrUInt(p),SizeOf(TDynArrayRec)); // p^ = start of heap object - OldLength := p^.length; - end else - OldLength := 0; // calculate the needed size of the resulting memory structure on heap NeededSize := NewLength*ElemSize+SizeOf(TDynArrayRec); {$ifndef CPU64} @@ -49067,29 +49045,38 @@ procedure TDynArray.InternalSetLength(NewLength: PtrUInt); [ArrayTypeShort^,NewLength]); {$endif} // if not shared (refCnt=1), resize; if shared, create copy (not thread safe) - if (p=nil) or (p^.refCnt=1) then begin - if NewLengthnil then - {$ifdef FPC}FPCFinalizeArray{$else}_FinalizeArray{$endif}( - pa+NeededSize,ElemType,OldLength-NewLength) else - if GetIsObjArray then begin // FreeAndNil() of resized objects list - for i := NewLength to OldLength-1 do - PObjectArray(fValue^)^[i].Free; - FillCharFast(pa[NeededSize],(OldLength-NewLength) shl POINTERSHR,0); - end; - ReallocMem(p,neededSize); + p := fValue^; + if p=nil then begin + p := AllocMem(NeededSize); + OldLength := NewLength; // no FillcharFast() below end else begin - InterlockedDecrement(PInteger(@p^.refCnt)^); // FPC has refCnt: PtrInt - GetMem(p,neededSize); - minLength := oldLength; - if minLength>newLength then - minLength := newLength; - if ElemType<>nil then begin - pp := pa+SizeOf(TDynArrayRec); - FillcharFast(pp^,minLength*elemSize,0); - CopyArray(pp,fValue^,ElemType,minLength); - end else - MoveFast(fValue^,pa[SizeOf(TDynArrayRec)],minLength*elemSize); + dec(PtrUInt(p),SizeOf(TDynArrayRec)); // p^ = start of heap object + OldLength := p^.length; + if p^.refCnt=1 then begin + if NewLengthnil then // release managed types in trailing items + {$ifdef FPC}FPCFinalizeArray{$else}_FinalizeArray{$endif}( + PAnsiChar(p)+NeededSize,ElemType,OldLength-NewLength) else + if GetIsObjArray then begin // FreeAndNil() of resized objects list + for i := NewLength to OldLength-1 do + PObjectArray(fValue^)^[i].Free; + {$ifdef FPC}FillChar{$else}FillCharFast{$endif}( + PAnsiChar(p)[NeededSize],(OldLength-NewLength) shl POINTERSHR,0); + end; + ReallocMem(p,NeededSize); + end else begin // make copy + InterlockedDecrement(PInteger(@p^.refCnt)^); // FPC has refCnt: PtrInt + GetMem(p,NeededSize); + minLength := oldLength; + if minLength>newLength then + minLength := newLength; + pp := PAnsiChar(p)+SizeOf(TDynArrayRec); + if ElemType<>nil then begin + {$ifdef FPC}FillChar{$else}FillCharFast{$endif}(pp^,minLength*elemSize,0); + CopyArray(pp,fValue^,ElemType,minLength); + end else + {$ifdef FPC}Move{$else}MoveFast{$endif}(fValue^^,pp^,minLength*elemSize); + end; end; // set refCnt=1 and new length to the heap memory structure with p^ do begin @@ -49100,11 +49087,12 @@ procedure TDynArray.InternalSetLength(NewLength: PtrUInt); length := newLength; {$endif} end; - inc(PByte(p),SizeOf(p^)); + inc(PByte(p),SizeOf(p^)); // p^ = start of dynamic aray items // reset new allocated elements content to zero if NewLength>OldLength then begin OldLength := OldLength*elemSize; - FillcharFast(pa[OldLength],neededSize-OldLength-SizeOf(TDynArrayRec),0); + {$ifdef FPC}FillChar{$else}FillCharFast{$endif}( + PAnsiChar(p)[OldLength],NewLength*ElemSize-OldLength,0); end; fValue^ := p; end; @@ -49139,7 +49127,7 @@ procedure TDynArray.SetCount(aCount: integer); c := PInteger(c)^; if capa>=c then exit; // no need to grow - inc(capa,capa shr 2); // growth factor = 1.5 + capa := NextGrow(capa); if capa0 then begin P := PAnsiChar(fValue^)+aFirstIndex*ElemSize; if ElemType=nil then - MoveFast(P^,D^^,aCount*ElemSize) else + {$ifdef FPC}Move{$else}MoveFast{$endif}(P^,D^^,aCount*ElemSize) else CopyArray(D^,P,ElemType,aCount); end; end; @@ -49222,7 +49210,7 @@ function TDynArray.AddArray(const DynArrayVar; aStartIndex, aCount: integer): in PS := pointer(PtrUInt(DynArrayVar)+cardinal(aStartIndex)*ElemSize); PD := pointer(PtrUInt(fValue^)+cardinal(n)*ElemSize); if ElemType=nil then - MoveFast(PS^,PD^,cardinal(aCount)*ElemSize) else + {$ifdef FPC}Move{$else}MoveFast{$endif}(PS^,PD^,cardinal(aCount)*ElemSize) else CopyArray(PD,PS,ElemType,aCount); end; @@ -49234,7 +49222,7 @@ procedure TDynArray.ElemClear(var Elem); {$ifdef FPC}FPCFinalize{$else}_Finalize{$endif}(@Elem,ElemType) else if (fIsObjArray=oaTrue) or ((fIsObjArray=oaUnknown) and ComputeIsObjArray) then TObject(Elem).Free; - FillcharFast(Elem,ElemSize,0); // always fill with zero binary content + {$ifdef FPC}FillChar{$else}FillCharFast{$endif}(Elem,ElemSize,0); // always end; function TDynArray.ElemLoad(Source: PAnsiChar): RawByteString; @@ -49242,7 +49230,7 @@ function TDynArray.ElemLoad(Source: PAnsiChar): RawByteString; if (Source<>nil) and (ElemType=nil) then SetString(result,Source,ElemSize) else begin SetString(result,nil,ElemSize); - FillcharFast(pointer(result)^,ElemSize,0); + {$ifdef FPC}FillChar{$else}FillCharFast{$endif}(pointer(result)^,ElemSize,0); ElemLoad(Source,pointer(result)^); end; end; @@ -49257,7 +49245,7 @@ procedure TDynArray.ElemLoad(Source: PAnsiChar; var Elem); begin if Source<>nil then // avoid GPF if ElemType=nil then - MoveFast(Source^,Elem,ElemSize) else + {$ifdef FPC}Move{$else}MoveFast{$endif}(Source^,Elem,ElemSize) else ManagedTypeLoad(@Elem,Source,ElemType); end; @@ -49281,7 +49269,7 @@ function TDynArray.ElemLoadFind(Source: PAnsiChar): integer; exit; if ElemType=nil then data := Source else begin - FillCharFast(tmp,ElemSize,0); + {$ifdef FPC}FillChar{$else}FillCharFast{$endif}(tmp,ElemSize,0); ManagedTypeLoad(@tmp,Source,ElemType); if Source=nil then exit; @@ -49350,6 +49338,14 @@ procedure TDynArrayHashed.ElemCopy(const A; var B); begin InternalDynArray.ElemCopy(A,B); end; +function TDynArrayHashed.ElemPtr(index: PtrInt): pointer; +begin + result := InternalDynArray.ElemPtr(index); +end; +procedure TDynArrayHashed.ElemCopyAt(index: PtrInt; var Dest); +begin + InternalDynArray.ElemCopyAt(index,Dest); +end; function TDynArrayHashed.KnownType: TDynArrayKind; begin result := InternalDynArray.KnownType; @@ -49696,11 +49692,6 @@ function HashInteger(const Elem; Hasher: THasher): cardinal; result := Hasher(0,@Elem,SizeOf(integer)); end; -function HashCardinal(const Elem; Hasher: THasher): cardinal; -begin - result := Hasher(0,@Elem,SizeOf(cardinal)); -end; - function HashInt64(const Elem; Hasher: THasher): cardinal; begin result := Hasher(0,@Elem,SizeOf(Int64)); @@ -49747,7 +49738,7 @@ function VariantHash(const value: variant; CaseInsensitive: boolean; result := Hasher(VType,@VWord,2); varLongWord, varInteger, varSingle: result := Hasher(VType,@VLongWord,4); - varInt64, varDouble, varDate, varCurrency: + varInt64, varDouble, varDate, varCurrency, varWord64: result := Hasher(VType,@VInt64,SizeOf(Int64)); varString: if CaseInsensitive then @@ -50046,12 +50037,8 @@ function TObjectDynArrayWrapper.Add(Instance: TObject): integer; var cap: integer; begin cap := length(TObjectDynArray(fValue^)); - if cap<=fCount then begin - if cap<256 then - inc(cap,64) else - inc(cap,256+cap shr 3); - SetLength(TObjectDynArray(fValue^),cap); - end; + if cap<=fCount then + SetLength(TObjectDynArray(fValue^),NextGrow(cap)); result := fCount; TObjectDynArray(fValue^)[result] := Instance; inc(fCount); @@ -50065,7 +50052,8 @@ procedure TObjectDynArrayWrapper.Delete(Index: integer); TObjectDynArray(fValue^)[Index].Free; dec(fCount); if fCount>Index then - MoveFast(TObjectDynArray(fValue^)[Index+1],TObjectDynArray(fValue^)[Index], + {$ifdef FPC}Move{$else}MoveFast{$endif}( + TObjectDynArray(fValue^)[Index+1],TObjectDynArray(fValue^)[Index], (fCount-Index)*SizeOf(pointer)); end; @@ -50104,14 +50092,15 @@ function TObjectDynArrayWrapper.Capacity: integer; end; procedure TObjectDynArrayWrapper.Sort(Compare: TDynArraySortCompare); -var QuickSort: TDynArrayQuickSort; begin - if (@Compare<>nil) and (fCount>0) then begin - Quicksort.Compare := @Compare; - Quicksort.Value := fValue^; - Quicksort.ElemSize := SizeOf(pointer); - Quicksort.QuickSort(0,fCount-1); - end; + if (@Compare<>nil) and (fCount>0) then + QuickSortPtr(0,fCount-1,Compare,fValue^); +end; + +function NewSynLocker: PSynLocker; +begin + result := AllocMem(SizeOf(result^)); + result^.Init; end; function PtrArrayAdd(var aPtrArray; aItem: pointer): integer; @@ -50145,7 +50134,8 @@ function PtrArrayDelete(var aPtrArray; aItem: pointer): integer; exit; dec(n); if n>result then - MoveFast(a[result+1],a[result],(n-result)*SizeOf(pointer)); + {$ifdef FPC}Move{$else}MoveFast{$endif}( + a[result+1],a[result],(n-result)*SizeOf(pointer)); SetLength(a,n); end; @@ -50157,7 +50147,7 @@ function PtrArrayFind(var aPtrArray; aItem: pointer): integer; { wrapper functions to T*ObjArr types } -function ObjArrayAdd(var aObjArray; aItem: TObject): integer; +function ObjArrayAdd(var aObjArray; aItem: TObject): PtrInt; var a: TObjectDynArray absolute aObjArray; begin result := length(a); @@ -50165,32 +50155,32 @@ function ObjArrayAdd(var aObjArray; aItem: TObject): integer; a[result] := aItem; end; -function ObjArrayAppend(var aDestObjArray, aSourceObjArray): integer; -var n: integer; +function ObjArrayAppend(var aDestObjArray, aSourceObjArray): PtrInt; +var n: PtrInt; s: TObjectDynArray absolute aSourceObjArray; d: TObjectDynArray absolute aDestObjArray; begin result := length(d); n := length(s); SetLength(d,result+n); - MoveFast(s[0],d[result],n*SizeOf(pointer)); + {$ifdef FPC}Move{$else}MoveFast{$endif}(s[0],d[result],n*SizeOf(pointer)); s := nil; // s[] will be owned by d[] inc(result,n); end; -function ObjArrayAddCount(var aObjArray; aItem: TObject; var aObjArrayCount: integer): integer; +function ObjArrayAddCount(var aObjArray; aItem: TObject; var aObjArrayCount: integer): PtrInt; var a: TObjectDynArray absolute aObjArray; begin result := aObjArrayCount; if result=length(a) then - SetLength(a,result+result shr 3+16); + SetLength(a,NextGrow(result)); a[result] := aItem; inc(aObjArrayCount); end; procedure ObjArrayAddOnce(var aObjArray; aItem: TObject); var a: TObjectDynArray absolute aObjArray; - n: integer; + n: PtrInt; begin n := length(a); if not PtrUIntScanExists(pointer(a),n,PtrUInt(aItem)) then begin @@ -50204,14 +50194,14 @@ procedure ObjArraySetLength(var aObjArray; aLength: integer); SetLength(TObjectDynArray(aObjArray),aLength); end; -function ObjArrayFind(const aObjArray; aItem: TObject): integer; +function ObjArrayFind(const aObjArray; aItem: TObject): PtrInt; begin result := PtrUIntScanIndex(pointer(aObjArray), length(TObjectDynArray(aObjArray)),PtrUInt(aItem)); end; function ObjArrayCount(const aObjArray): integer; -var i: integer; +var i: PtrInt; a: TObjectDynArray absolute aObjArray; begin result := 0; @@ -50220,9 +50210,9 @@ function ObjArrayCount(const aObjArray): integer; inc(result); end; -procedure ObjArrayDelete(var aObjArray; aItemIndex: integer; +procedure ObjArrayDelete(var aObjArray; aItemIndex: PtrInt; aContinueOnException: boolean); -var n: integer; +var n: PtrInt; a: TObjectDynArray absolute aObjArray; begin n := length(a); @@ -50236,11 +50226,12 @@ procedure ObjArrayDelete(var aObjArray; aItemIndex: integer; a[aItemIndex].Free; dec(n); if n>aItemIndex then - MoveFast(a[aItemIndex+1],a[aItemIndex],(n-aItemIndex)*SizeOf(TObject)); + {$ifdef FPC}Move{$else}MoveFast{$endif}( + a[aItemIndex+1],a[aItemIndex],(n-aItemIndex)*SizeOf(TObject)); SetLength(a,n); end; -function ObjArrayDelete(var aObjArray; aItem: TObject): integer; +function ObjArrayDelete(var aObjArray; aItem: TObject): PtrInt; begin result := ObjArrayFind(aObjArray,aItem); if result>=0 then @@ -50248,16 +50239,9 @@ function ObjArrayDelete(var aObjArray; aItem: TObject): integer; end; procedure ObjArraySort(var aObjArray; Compare: TDynArraySortCompare); -var QuickSort: TDynArrayQuickSort; - n: integer; begin - n := length(TObjectDynArray(aObjArray)); - if (@Compare<>nil) and (n>0) then begin - Quicksort.Compare := @Compare; - Quicksort.Value := pointer(aObjArray); - Quicksort.ElemSize := SizeOf(pointer); - Quicksort.QuickSort(0,n-1); - end; + if @Compare<>nil then + QuickSortPtr(0,length(TObjectDynArray(aObjArray))-1,Compare,pointer(aObjArray)); end; procedure RawObjectsClear(o: PObject; n: integer); @@ -50293,7 +50277,7 @@ procedure ObjArrayClear(var aObjArray; aCount: integer); end; procedure ObjArrayClear(var aObjArray; aContinueOnException: boolean); -var n,i: integer; +var n,i: PtrInt; a: TObjectDynArray absolute aObjArray; begin n := length(a); @@ -50326,7 +50310,7 @@ function ObjArrayToJSON(const aObjArray; aOptions: TTextWriterWriteObjectOptions procedure ObjArrayObjArrayClear(var aObjArray); -var i: integer; +var i: PtrInt; a: TPointerDynArray absolute aObjArray; begin if a<>nil then begin @@ -50337,7 +50321,7 @@ procedure ObjArrayObjArrayClear(var aObjArray); end; procedure ObjArraysClear(const aObjArray: array of pointer); -var i: integer; +var i: PtrInt; begin for i := 0 to high(aObjArray) do if aObjArray[i]<>nil then @@ -50346,7 +50330,7 @@ procedure ObjArraysClear(const aObjArray: array of pointer); {$ifndef DELPHI5OROLDER} -function InterfaceArrayAdd(var aInterfaceArray; const aItem: IUnknown): integer; +function InterfaceArrayAdd(var aInterfaceArray; const aItem: IUnknown): PtrInt; var a: TInterfaceDynArray absolute aInterfaceArray; begin result := length(a); @@ -50356,7 +50340,7 @@ function InterfaceArrayAdd(var aInterfaceArray; const aItem: IUnknown): integer; procedure InterfaceArrayAddOnce(var aInterfaceArray; const aItem: IUnknown); var a: TInterfaceDynArray absolute aInterfaceArray; - n: integer; + n: PtrInt; begin if PtrUIntScanExists(pointer(aInterfaceArray), length(TInterfaceDynArray(aInterfaceArray)),PtrUInt(aItem)) then @@ -50366,28 +50350,29 @@ procedure InterfaceArrayAddOnce(var aInterfaceArray; const aItem: IUnknown); a[n] := aItem; end; -function InterfaceArrayFind(const aInterfaceArray; const aItem: IUnknown): integer; +function InterfaceArrayFind(const aInterfaceArray; const aItem: IUnknown): PtrInt; begin result := PtrUIntScanIndex(pointer(aInterfaceArray), length(TInterfaceDynArray(aInterfaceArray)),PtrUInt(aItem)); end; -procedure InterfaceArrayDelete(var aInterfaceArray; aItemIndex: integer); -var n: integer; +procedure InterfaceArrayDelete(var aInterfaceArray; aItemIndex: PtrInt); +var n: PtrInt; a: TInterfaceDynArray absolute aInterfaceArray; begin n := length(a); - if cardinal(aItemIndex)>=cardinal(n) then + if PtrUInt(aItemIndex)>=PtrUInt(n) then exit; // out of range a[aItemIndex] := nil; dec(n); if n>aItemIndex then - MoveFast(a[aItemIndex+1],a[aItemIndex],(n-aItemIndex)*SizeOf(IInterface)); + {$ifdef FPC}Move{$else}MoveFast{$endif}( + a[aItemIndex+1],a[aItemIndex],(n-aItemIndex)*SizeOf(IInterface)); TPointerDynArray(aInterfaceArray)[n] := nil; // avoid GPF in SetLength() SetLength(a,n); end; -function InterfaceArrayDelete(var aInterfaceArray; const aItem: IUnknown): integer; +function InterfaceArrayDelete(var aInterfaceArray; const aItem: IUnknown): PtrInt; begin result := InterfaceArrayFind(aInterfaceArray,aItem); if result>=0 then @@ -50558,6 +50543,7 @@ destructor TAutoLock.Destroy; procedure TSynLocker.Init; begin + fSectionPadding := 0; InitializeCriticalSection(fSection); PaddingMaxUsedIndex := -1; fLocked := false; @@ -50574,6 +50560,12 @@ procedure TSynLocker.Done; fInitialized := false; end; +procedure TSynLocker.DoneAndFreeMem; +begin + Done; + FreeMem(@self); +end; + procedure TSynLocker.Lock; begin EnterCriticalSection(fSection); @@ -50838,13 +50830,13 @@ function TSynLocker.LockedPointerExchange(Index: integer; Value: pointer): point constructor TInterfacedObjectLocked.Create; begin inherited Create; - fSafe.Init; + fSafe := NewSynLocker; end; destructor TInterfacedObjectLocked.Destroy; begin inherited Destroy; - fSafe.Done; + fSafe^.DoneAndFreeMem; end; @@ -50884,12 +50876,9 @@ procedure TSynPersistent.Assign(Source: TSynPersistent); {$ifdef FPC_OR_PUREPASCAL} class function TSynPersistent.NewInstance: TObject; -var p: pointer; begin // bypass vmtIntfTable and vmt^.vInitTable (management operators) - GetMem(p, InstanceSize); - FillCharFast(p^, InstanceSize, 0); - PPointer(p)^ := pointer(self); // store VMT - result := p; + result := AllocMem(InstanceSize); // will zero memory + PPointer(result)^ := pointer(self); // store VMT end; {$else} class function TSynPersistent.NewInstance: TObject; @@ -50942,398 +50931,21 @@ procedure TSynPersistent.FreeInstance; {$endif FPC_OR_PUREPASCAL} -{ TSynPersistentLocked } - -constructor TSynPersistentLocked.Create; -begin - inherited Create; - fSafe.Init; -end; - -destructor TSynPersistentLocked.Destroy; -begin - inherited Destroy; - fSafe.Done; -end; - - { TSynPersistentLock } constructor TSynPersistentLock.Create; begin inherited Create; - GetMem(fSafe,SizeOf(TSynLocker)); - FillCharFast(fSafe^,SizeOf(TSynLocker),0); - fSafe^.Init; + fSafe := NewSynLocker; end; destructor TSynPersistentLock.Destroy; begin - fSafe^.Done; - FreeMem(fSafe); + fSafe^.DoneAndFreeMem; inherited; end; -{ TSynPersistentStore } - -constructor TSynPersistentStore.Create(const aName: RawUTF8); -begin - Create; - fName := aName; -end; - -constructor TSynPersistentStore.CreateFrom(const aBuffer: RawByteString; - aLoad: TAlgoCompressLoad); -begin - CreateFromBuffer(pointer(aBuffer),length(aBuffer),aLoad); -end; - -constructor TSynPersistentStore.CreateFromBuffer( - aBuffer: pointer; aBufferLen: integer; aLoad: TAlgoCompressLoad); -begin - Create(''); - LoadFrom(aBuffer,aBufferLen,aLoad); -end; - -constructor TSynPersistentStore.CreateFromFile(const aFileName: TFileName; - aLoad: TAlgoCompressLoad); -begin - Create(''); - LoadFromFile(aFileName,aLoad); -end; - -procedure TSynPersistentStore.LoadFromReader; -begin - fReader.VarUTF8(fName); -end; - -procedure TSynPersistentStore.SaveToWriter(aWriter: TFileBufferWriter); -begin - aWriter.Write(fName); -end; - -procedure TSynPersistentStore.LoadFrom(const aBuffer: RawByteString; - aLoad: TAlgoCompressLoad); -begin - if aBuffer <> '' then - LoadFrom(pointer(aBuffer),length(aBuffer),aLoad); -end; - -procedure TSynPersistentStore.LoadFrom(aBuffer: pointer; aBufferLen: integer; - aLoad: TAlgoCompressLoad); -var localtemp: RawByteString; - p: pointer; - temp: PRawByteString; -begin - if (aBuffer=nil) or (aBufferLen<=0) then - exit; // nothing to load - fLoadFromLastAlgo := TAlgoCompress.Algo(aBuffer,aBufferLen); - if fLoadFromLastAlgo = nil then - fReader.ErrorData('%.LoadFrom unknown TAlgoCompress AlgoID=%', - [self,PByteArray(aBuffer)[4]]); - temp := fReaderTemp; - if temp=nil then - temp := @localtemp; - p := fLoadFromLastAlgo.Decompress(aBuffer,aBufferLen,fLoadFromLastUncompressed,temp^,aLoad); - if p=nil then - fReader.ErrorData('%.LoadFrom %.Decompress failed',[self,fLoadFromLastAlgo]); - fReader.Init(p,fLoadFromLastUncompressed); - LoadFromReader; -end; - -function TSynPersistentStore.LoadFromFile(const aFileName: TFileName; - aLoad: TAlgoCompressLoad): boolean; -var temp: RawByteString; -begin - temp := StringFromFile(aFileName); - result := temp<>''; - if result then - LoadFrom(temp,aLoad); -end; - -procedure TSynPersistentStore.SaveTo(out aBuffer: RawByteString; nocompression: boolean; - BufLen: integer; ForcedAlgo: TAlgoCompress); -var writer: TFileBufferWriter; - temp: array[word] of byte; -begin - if BufLen<=SizeOf(temp) then - writer := TFileBufferWriter.Create(TRawByteStringStream,@temp,SizeOf(temp)) else - writer := TFileBufferWriter.Create(TRawByteStringStream,BufLen); - try - SaveToWriter(writer); - fSaveToLastUncompressed := writer.TotalWritten; - aBuffer := writer.FlushAndCompress(nocompression,ForcedAlgo); - finally - writer.Free; - end; -end; - -function TSynPersistentStore.SaveTo(nocompression: boolean; BufLen: integer; - ForcedAlgo: TAlgoCompress): RawByteString; -begin - SaveTo(result,nocompression,BufLen,ForcedAlgo); -end; - -function TSynPersistentStore.SaveToFile(const aFileName: TFileName; - nocompression: boolean; BufLen: integer; ForcedAlgo: TAlgoCompress): PtrUInt; -var temp: RawByteString; -begin - SaveTo(temp,nocompression,BufLen,ForcedAlgo); - if FileFromString(temp,aFileName) then - result := length(temp) else - result := 0; -end; - - -{ TSynPersistentStoreJson } - -procedure TSynPersistentStoreJson.AddJSON(W: TTextWriter); -begin - W.AddPropJSONString('name', fName); -end; - -function TSynPersistentStoreJson.SaveToJSON(reformat: TTextWriterJSONFormat): RawUTF8; -var - W: TTextWriter; -begin - W := TTextWriter.CreateOwnedStream(65536); - try - W.Add('{'); - AddJSON(W); - W.CancelLastComma; - W.Add('}'); - W.SetText(result, reformat); - finally - W.Free; - end; -end; - - -{ TSynUniqueIdentifierBits } - -function TSynUniqueIdentifierBits.Counter: word; -begin - result := PWord(@Value)^ and $7fff; -end; - -function TSynUniqueIdentifierBits.ProcessID: TSynUniqueIdentifierProcess; -begin - result := (PCardinal(@Value)^ shr 15) and $ffff; -end; - -function TSynUniqueIdentifierBits.CreateTimeUnix: TUnixTime; -begin - result := Value shr 31; -end; - -{$ifndef NOVARIANTS} -function TSynUniqueIdentifierBits.AsVariant: variant; -begin - ToVariant(result); -end; - -procedure TSynUniqueIdentifierBits.ToVariant(out result: variant); -begin - TDocVariantData(result).InitObject(['Created',DateTimeToIso8601Text(CreateDateTime), - 'Identifier',ProcessID,'Counter',Counter,'Value',Value, - 'Hex',Int64ToHex(Value)],JSON_OPTIONS_FAST); -end; -{$endif NOVARIANTS} - -{$ifndef DELPHI5OROLDER} -function TSynUniqueIdentifierBits.Equal(const Another: TSynUniqueIdentifierBits): boolean; -begin - result := Value=Another.Value; -end; -{$endif} - -procedure TSynUniqueIdentifierBits.From(const AID: TSynUniqueIdentifier); -begin - Value := AID; -end; - -function TSynUniqueIdentifierBits.CreateTimeLog: TTimeLog; -begin - PTimeLogBits(@result)^.From(UnixTimeToDateTime(Value shr 31)); -end; - -function TSynUniqueIdentifierBits.CreateDateTime: TDateTime; -begin - result := UnixTimeToDateTime(Value shr 31); -end; - -function TSynUniqueIdentifierBits.ToHexa: RawUTF8; -begin - Int64ToHex(Value,result); -end; - -function TSynUniqueIdentifierBits.FromHexa(const hexa: RawUTF8): boolean; -begin - result := (Length(hexa)=16) and HexDisplayToBin(pointer(hexa),@Value,SizeOf(Value)); -end; - -procedure TSynUniqueIdentifierBits.FromDateTime(const aDateTime: TDateTime); -begin - Value := DateTimeToUnixTime(aDateTime) shl 31; -end; - -procedure TSynUniqueIdentifierBits.FromUnixTime(const aUnixTime: TUnixTime); -begin - Value := aUnixTime shl 31; -end; - - -{ TSynUniqueIdentifierGenerator } - -const // fSafe.Padding[] slots - SYNUNIQUEGEN_COMPUTECOUNT = 0; - -procedure TSynUniqueIdentifierGenerator.ComputeNew( - out result: TSynUniqueIdentifierBits); -var currentTime: cardinal; -begin - currentTime := UnixTimeUTC; // fast API (under Windows, faster than GetTickCount64) - fSafe.Lock; - try - if currentTime>fUnixCreateTime then begin - fUnixCreateTime := currentTime; - fLastCounter := 0; // reset - end; - if fLastCounter=$7fff then begin // collision (unlikely) -> cheat on timestamp - inc(fUnixCreateTime); - fLastCounter := 0; - end else - inc(fLastCounter); - result.Value := Int64(fLastCounter or fIdentifierShifted) or - (Int64(fUnixCreateTime) shl 31); - inc(fSafe.Padding[SYNUNIQUEGEN_COMPUTECOUNT].VInt64); - finally - fSafe.UnLock; - end; -end; - -function TSynUniqueIdentifierGenerator.ComputeNew: Int64; -begin - ComputeNew(PSynUniqueIdentifierBits(@result)^); -end; - -function TSynUniqueIdentifierGenerator.GetComputedCount: Int64; -begin - {$ifdef NOVARIANTS} - fSafe.Lock; - result := fSafe.Padding[SYNUNIQUEGEN_COMPUTECOUNT].VInt64; - fSafe.Unlock; - {$else} - result := fSafe.LockedInt64[SYNUNIQUEGEN_COMPUTECOUNT]; - {$endif} -end; - -procedure TSynUniqueIdentifierGenerator.ComputeFromDateTime(const aDateTime: TDateTime; - out result: TSynUniqueIdentifierBits); -begin // assume fLastCounter=0 - ComputeFromUnixTime(DateTimeToUnixTime(aDateTime),result); -end; - -procedure TSynUniqueIdentifierGenerator.ComputeFromUnixTime(const aUnixTime: TUnixTime; - out result: TSynUniqueIdentifierBits); -begin // assume fLastCounter=0 - result.Value := aUnixTime shl 31; - if self<>nil then - result.Value := result.Value or fIdentifierShifted; -end; - -constructor TSynUniqueIdentifierGenerator.Create(aIdentifier: TSynUniqueIdentifierProcess; - const aSharedObfuscationKey: RawUTF8); -var i, len: integer; - crc: cardinal; -begin - fIdentifier := aIdentifier; - fIdentifierShifted := aIdentifier shl 15; - fSafe.Init; - {$ifdef NOVARIANTS} - variant(fSafe.Padding[SYNUNIQUEGEN_COMPUTECOUNT]) := 0; - {$else} - fSafe.LockedInt64[SYNUNIQUEGEN_COMPUTECOUNT] := 0; - {$endif} - // compute obfuscation key using hash diffusion of the supplied text - len := length(aSharedObfuscationKey); - crc := crc32ctab[0,len and 1023]; - for i := 0 to high(fCrypto)+1 do begin - crc := crc32ctab[0,crc and 1023] xor crc32ctab[3,i] xor - kr32(crc,pointer(aSharedObfuscationKey),len) xor - crc32c(crc,pointer(aSharedObfuscationKey),len) xor - fnv32(crc,pointer(aSharedObfuscationKey),len); - // do not modify those hashes above or you will break obfuscation pattern! - if i<=high(fCrypto) then - fCrypto[i] := crc else - fCryptoCRC := crc; - end; - // due to the weakness of the hash algorithms used, this approach is a bit - // naive and would be broken easily with brute force - but point here is to - // hide/obfuscate public values at end-user level (e.g. when publishing URIs), - // not implement strong security, so it sounds good enough for our purpose -end; - -destructor TSynUniqueIdentifierGenerator.Destroy; -begin - fSafe.Done; - FillcharFast(fCrypto,SizeOf(fCrypto),0); - fCryptoCRC := 0; - inherited Destroy; -end; - -type // compute a 24 hexadecimal chars (96 bits) obfuscated pseudo file name - TSynUniqueIdentifierObfuscatedBits = packed record - crc: cardinal; - id: TSynUniqueIdentifierBits; - end; - -function TSynUniqueIdentifierGenerator.ToObfuscated( - const aIdentifier: TSynUniqueIdentifier): TSynUniqueIdentifierObfuscated; -var bits: TSynUniqueIdentifierObfuscatedBits; - key: cardinal; -begin - result := ''; - if aIdentifier=0 then - exit; - bits.id.Value := aIdentifier; - if self=nil then - key := 0 else - key := crc32ctab[0,bits.id.ProcessID and 1023] xor fCryptoCRC; - bits.crc := crc32c(bits.id.ProcessID,@bits.id,SizeOf(bits.id)) xor key; - if self<>nil then - bits.id.Value := bits.id.Value xor PInt64(@fCrypto[high(fCrypto)-1])^; - result := BinToHex(@bits,SizeOf(bits)); -end; - -function TSynUniqueIdentifierGenerator.FromObfuscated( - const aObfuscated: TSynUniqueIdentifierObfuscated; - out aIdentifier: TSynUniqueIdentifier): boolean; -var bits: TSynUniqueIdentifierObfuscatedBits; - len: integer; - key: cardinal; -begin - result := false; - len := PosEx('.',aObfuscated); - if len=0 then - len := Length(aObfuscated) else - dec(len); // trim right '.jpg' - if (len<>SizeOf(bits)*2) or - not SynCommons.HexToBin(pointer(aObfuscated),@bits,SizeOf(bits)) then - exit; - if self=nil then - key := 0 else begin - bits.id.Value := bits.id.Value xor PInt64(@fCrypto[high(fCrypto)-1])^; - key := crc32ctab[0,bits.id.ProcessID and 1023] xor fCryptoCRC; - end; - if crc32c(bits.id.ProcessID,@bits.id,SizeOf(bits.id)) xor key=bits.crc then begin - aIdentifier := bits.id.Value; - result := true; - end; -end; - - { TObjectListSorted } destructor TObjectListSorted.Destroy; @@ -51377,9 +50989,10 @@ procedure TObjectListSorted.InsertNew(Item: TSynPersistentLock; Index: integer); begin if fCount=length(fObjArray) then - SetLength(fObjArray,fCount+256+fCount shr 3); + SetLength(fObjArray,NextGrow(fCount)); if cardinal(Index)i then - MoveFast(fObjArray[i+1],fObjArray[i],(fCount-i)*SizeOf(TObject)); + {$ifdef FPC}Move{$else}MoveFast{$endif}( + fObjArray[i+1],fObjArray[i],(fCount-i)*SizeOf(TObject)); result := true; end; finally @@ -51440,6 +51054,36 @@ function TObjectListSorted.FindOrAddLocked(const Value; out added: boolean): poi { TTextWriter } +procedure TTextWriter.CancelLastChar; +begin + if B>=fTempBuf then // Add() methods append at B+1 + dec(B); +end; + +function TTextWriter.LastChar: AnsiChar; +begin + if B>=fTempBuf then + result := B^ else + result := #0; // returns #0 if no char has been written yet +end; + +procedure TTextWriter.CancelLastChar(aCharToCancel: AnsiChar); +begin + if (B>=fTempBuf) and (B^=aCharToCancel) then + dec(B); +end; + +function TTextWriter.PendingBytes: PtrUInt; +begin + result := B-fTempBuf+1; +end; + +procedure TTextWriter.CancelLastComma; +begin + if (B>=fTempBuf) and (B^=',') then + dec(B); +end; + procedure TTextWriter.Add(Value: PtrInt); var tmp: array[0..23] of AnsiChar; P: PAnsiChar; @@ -51454,7 +51098,7 @@ procedure TTextWriter.Add(Value: PtrInt); P := StrInt32(@tmp[23],value); Len := @tmp[23]-P; end; - MoveFast(P[0],B[1],Len); + {$ifdef FPC}Move{$else}MoveFast{$endif}(P[0],B[1],Len); inc(B,Len); end; @@ -51476,7 +51120,7 @@ procedure TTextWriter.AddCurr64(const Value: Int64); dec(Len,3) else dec(Len,2) else dec(Len); - MoveFast(P[0],B[1],Len); + {$ifdef FPC}Move{$else}MoveFast{$endif}(P[0],B[1],Len); inc(B,Len); end; @@ -51533,7 +51177,7 @@ procedure TTextWriter.AddDateTime(Value: PDateTime; FirstChar: AnsiChar; end; end; -procedure TTextWriter.AddDateTime(const Value: TDateTime; WithMS: boolean=false); +procedure TTextWriter.AddDateTime(const Value: TDateTime; WithMS: boolean); begin if Value=0 then exit; @@ -51553,34 +51197,34 @@ procedure TTextWriter.AddDateTime(const Value: TDateTime; WithMS: boolean=false) dec(B); end; -procedure TTextWriter.AddDateTimeMS(const Value: TDateTime; Expanded: boolean=true; - FirstTimeChar: AnsiChar='T'; const TZD: RawUTF8='Z'); -var HH,MM,SS,MS,Y,M,D: word; +procedure TTextWriter.AddDateTimeMS(const Value: TDateTime; Expanded: boolean; + FirstTimeChar: AnsiChar; const TZD: RawUTF8); +var T: TSynSystemTime; begin if Value=0 then exit; - DecodeDate(Value,Y,M,D); - DecodeTime(Value,HH,MM,SS,MS); - Add(DTMS_FMT[Expanded], [UInt4DigitsToShort(Y),UInt2DigitsToShortFast(M), - UInt2DigitsToShortFast(D),FirstTimeChar,UInt2DigitsToShortFast(HH), - UInt2DigitsToShortFast(MM),UInt2DigitsToShortFast(SS),UInt3DigitsToShort(MS),TZD]); + T.FromDateTime(Value); + Add(DTMS_FMT[Expanded], [UInt4DigitsToShort(T.Year), + UInt2DigitsToShortFast(T.Month),UInt2DigitsToShortFast(T.Day),FirstTimeChar, + UInt2DigitsToShortFast(T.Hour),UInt2DigitsToShortFast(T.Minute), + UInt2DigitsToShortFast(T.Second),UInt3DigitsToShort(T.MilliSecond),TZD]); end; procedure TTextWriter.AddU(Value: cardinal); -var tmp: array[0..15] of AnsiChar; +var tmp: array[0..23] of AnsiChar; P: PAnsiChar; Len: integer; begin - if BEnd-B<=16 then + if BEnd-B<=24 then FlushToStream; if Value<=high(SmallUInt32UTF8) then begin P := pointer(SmallUInt32UTF8[Value]); Len := {$ifdef FPC}_LStrLenP(P){$else}PInteger(P-4)^{$endif}; end else begin - P := StrUInt32(@tmp[15],Value); - Len := @tmp[15]-P; + P := StrUInt32(@tmp[23],Value); + Len := @tmp[23]-P; end; - MoveFast(P[0],B[1],Len); + {$ifdef FPC}Move{$else}MoveFast{$endif}(P[0],B[1],Len); inc(B,Len); end; @@ -51599,7 +51243,7 @@ procedure TTextWriter.AddQ(Value: QWord); P := StrUInt64(@tmp[23],Value); Len := @tmp[23]-P; end; - MoveFast(P[0],B[1],Len); + {$ifdef FPC}Move{$else}MoveFast{$endif}(P[0],B[1],Len); inc(B,Len); end; @@ -51664,10 +51308,10 @@ procedure TTextWriter.Add(Value: Int64); P := StrUInt64(@tmp[23],Value); Len := @tmp[23]-P; end; - MoveFast(P[0],B[1],Len); + {$ifdef FPC}Move{$else}MoveFast{$endif}(P[0],B[1],Len); inc(B,Len); end; -{$endif} +{$endif CPU64} procedure TTextWriter.Add(Value: boolean); begin @@ -51695,7 +51339,7 @@ procedure TTextWriter.AddFloatStr(P: PUTF8Char); B^ := '0'; // '.5' -> '0.5' inc(B); end; - MoveFast(P^,B^,L); + {$ifdef FPC}Move{$else}MoveFast{$endif}(P^,B^,L); inc(B,L-1); end; end; @@ -51768,7 +51412,7 @@ procedure TTextWriter.AddCRAndIndent; if BEnd-B<=Integer(ntabs)+1 then FlushToStream; PWord(B+1)^ := 13+10 shl 8; // CR + LF - FillcharFast(B[3],ntabs,9); // indentation using tabs + {$ifdef FPC}FillChar{$else}FillCharFast{$endif}(B[3],ntabs,9); // #9=tab inc(B,ntabs+2); end; @@ -51780,7 +51424,7 @@ procedure TTextWriter.AddChars(aChar: AnsiChar; aCount: integer); if aCount99 then MS := 99; - PWord(B)^:= TwoDigitLookupW[MS]; + PWord(B)^:= W[MS]; inc(B,9); end; @@ -52151,7 +51801,7 @@ procedure TTextWriter.AddVoidRecordJSON(TypeInfo: pointer); var tmp: TBytes; info: PTypeInfo; begin - info := GetTypeInfo(TypeInfo,tkRecordTypeOrSet); + info := GetTypeInfo(TypeInfo,tkRecordKinds); if (self=nil) or (info=nil) then raise ESynException.CreateUTF8('Invalid %.AddVoidRecordJSON(%)',[self,TypeInfo]); SetLength(tmp,info^.recSize {$ifdef FPC}and $7FFFFFFF{$endif}); @@ -52300,7 +51950,7 @@ procedure TTextWriter.AddTypedJSON(aTypeInfo: pointer; const aValue); if twoEnumSetsAsBooleanInRecord in fCustomOptions then begin Add('{'); for i := 0 to max do begin - AddPS(GetBit(aValue,i)); + AddPS(GetBitPtr(@aValue,i)); Add(','); inc(PByte(PS),ord(PS^[0])+1); // next short string end; @@ -52313,7 +51963,7 @@ procedure TTextWriter.AddTypedJSON(aTypeInfo: pointer; const aValue); GetAllBits(cardinal(aValue),max+1) then AddShort('"*"') else begin for i := 0 to max do begin - if GetBit(aValue,i) then begin + if GetBitPtr(@aValue,i) then begin AddPS; Add(','); end; @@ -52703,34 +52353,12 @@ procedure TTextWriter.AddLine(const Text: shortstring); if BEnd-B<=ord(Text[0])+2 then FlushToStream; inc(B); - MoveFast(Text[1],B[0],ord(Text[0])); + {$ifdef FPC}Move{$else}MoveFast{$endif}(Text[1],B[0],ord(Text[0])); inc(B,ord(Text[0])); PWord(B)^ := 13+10 shl 8; // CR + LF inc(B); end; -procedure TTextWriter.AddPointer(P: PtrUInt); - procedure Pointer4ToHex(B: PWordArray; P: PtrUInt); - begin - B[3] := TwoDigitsHexWB[ToByte(P)]; P := P shr 8; - B[2] := TwoDigitsHexWB[ToByte(P)]; P := P shr 8; - B[1] := TwoDigitsHexWB[ToByte(P)]; P := P shr 8; - B[0] := TwoDigitsHexWB[P]; - end; -begin - if BEnd-B<=SizeOf(P)*2 then - FlushToStream; - {$ifdef CPU64} // truncate to for most heap-allocated 4 bytes pointers - if P and $ffffffff00000000<>0 then begin - BinToHexDisplay(@P,PAnsiChar(B+1),8); - inc(B,16); - exit; - end; - {$endif} - Pointer4ToHex(@B[1],P); - inc(B,8); -end; - procedure TTextWriter.AddBinToHexDisplay(Bin: pointer; BinBytes: integer); begin if cardinal(BinBytes*2-1)>=cardinal(fTempBufSize) then @@ -52764,8 +52392,27 @@ procedure TTextWriter.AddBinToHexDisplayQuoted(Bin: pointer; BinBytes: integer); inc(B,2); end; +procedure TTextWriter.AddBinToHexDisplayMinChars(Bin: pointer; BinBytes: PtrInt); +begin + if (BinBytes<=0) or (cardinal(BinBytes*2-1)>=cardinal(fTempBufSize)) then + exit; + repeat // append hexa chars up to the last non zero byte + dec(BinBytes); + until (BinBytes=0) or (PByteArray(Bin)[BinBytes]<>0); + inc(BinBytes); + if BEnd-B<=BinBytes*2 then + FlushToStream; + BinToHexDisplayLower(Bin,PAnsiChar(B+1),BinBytes); + inc(B,BinBytes*2); +end; + +procedure TTextWriter.AddPointer(P: PtrUInt); +begin + AddBinToHexDisplayMinChars(@P,SizeOf(P)); +end; + procedure TTextWriter.AddBinToHex(Bin: Pointer; BinBytes: integer); -var ChunkBytes: integer; +var ChunkBytes: PtrInt; begin if BinBytes<=0 then exit; @@ -53095,7 +52742,7 @@ procedure TTextWriter.AddNoJSONEscape(P: Pointer; Len: PtrInt); if Len2) and (PInteger(s)^ and $ffffff=JSON_BASE64_MAGIC) then begin AddNoJSONEscape(pointer(s),L); // identified as a BLOB content exit; end; @@ -53264,11 +52911,15 @@ procedure TTextWriter.AddAnyAnsiBuffer(P: PAnsiChar; Len: integer; JSON_ESCAPE_BYTE: TSynByteBoolean; function NeedsJsonEscape(const Text: RawUTF8): boolean; -var i: integer; +var tab: ^TSynByteBoolean; + P: PByteArray; + i: PtrInt; begin result := true; - for i := 1 to length(Text) do - if byte(Text[i]) in JSON_ESCAPE then + tab := @JSON_ESCAPE_BYTE; + P := pointer(Text); + for i := 0 to length(Text)-1 do + if tab[P^[i]] then exit; result := false; end; @@ -53379,7 +53030,7 @@ procedure TTextWriter.AddOnSameLineW(P: PWord; Len: PtrInt); procedure TTextWriter.AddJSONEscape(P: Pointer; Len: PtrInt); var i,c: PtrInt; - {$ifndef CPUX86}tab: ^TSynByteBoolean;{$endif} + {$ifndef CPUX86NOTPIC}tab: ^TSynByteBoolean;{$endif} label noesc; begin if P=nil then @@ -53387,8 +53038,8 @@ procedure TTextWriter.AddJSONEscape(P: Pointer; Len: PtrInt); if Len=0 then Len := MaxInt; i := 0; - {$ifdef CPUX86} - while i=Len) or (PByteArray(P)[i] in JSON_ESCAPE); {$else} tab := @JSON_ESCAPE_BYTE; - while i=Len) or tab^[PByteArray(P)[i]]; - {$endif} + {$endif CPUX86NOTPIC} inc(PByte(P),c); dec(i,c); dec(Len,c); if BEnd-B<=i then AddNoJSONEscape(P,i) else begin - MoveFast(P^,B[1],i); + {$ifdef FPC}Move{$else}MoveFast{$endif}(P^,B[1],i); inc(B,i); end; if i>=Len then - break; + exit; end; repeat c := PByteArray(P)[i]; @@ -53439,7 +53090,7 @@ procedure TTextWriter.AddJSONEscape(P: Pointer; Len: PtrInt); if i>=Len then exit; until false; - end; + until i>=Len; end; procedure TTextWriter.AddJSONEscapeW(P: PWord; Len: PtrInt); @@ -53543,7 +53194,7 @@ procedure TTextWriter.Add(const V: TVarRec; Escape: TTextWriterKind; {$endif} vtString: if VString^[0]<>#0 then Add(@VString^[1],ord(VString^[0]),Escape); vtInterface, - vtPointer: AddPointer(PtrUInt(VPointer)); + vtPointer: AddBinToHexDisplayMinChars(@VPointer,SizeOf(VPointer)); vtPChar: Add(PUTF8Char(VPChar),Escape); vtObject: WriteObject(VObject,WriteObjectOptions); vtClass: AddClassName(VClass); @@ -53653,7 +53304,7 @@ procedure TTextWriter.AddNoJSONEscapeString(const s: string); begin if s<>'' then {$ifdef UNICODE} - AddNoJSONEscapeW(pointer(s),length(s)); + AddNoJSONEscapeW(pointer(s),0); {$else} AddAnsiString(s,twNone); {$endif} @@ -53681,12 +53332,12 @@ procedure TTextWriter.AddPropName(const PropName: ShortString); if BEnd-B<=ord(PropName[0])+3 then FlushToStream; if twoForceJSONExtended in CustomOptions then begin - MoveFast(PropName[1],B[1],ord(PropName[0])); + {$ifdef FPC}Move{$else}MoveFast{$endif}(PropName[1],B[1],ord(PropName[0])); inc(B,ord(PropName[0])+1); B^ := ':'; end else begin B[1] := '"'; - MoveFast(PropName[1],B[2],ord(PropName[0])); + {$ifdef FPC}Move{$else}MoveFast{$endif}(PropName[1],B[2],ord(PropName[0])); inc(B,ord(PropName[0])+2); PWord(B)^ := ord('"')+ord(':')shl 8; inc(B); @@ -53717,7 +53368,7 @@ procedure TTextWriter.AddFieldName(FieldName: PUTF8Char; FieldNameLen: integer); if BEnd-B<=FieldNameLen+3 then FlushToStream; B[1] := '"'; - MoveFast(FieldName^,B[2],FieldNameLen); + {$ifdef FPC}Move{$else}MoveFast{$endif}(FieldName^,B[2],FieldNameLen); inc(B,FieldNameLen+2); PWord(B)^ := ord('"')+ord(':')shl 8; inc(B); @@ -53736,19 +53387,21 @@ procedure TTextWriter.AddInstanceName(Instance: TObject; SepChar: AnsiChar); AddShort('void') else AddShort(PShortString(PPointer(PPtrInt(Instance)^+vmtClassName)^)^); Add('('); - AddPointer(PtrUInt(Instance)); + AddBinToHexDisplayMinChars(@Instance,SizeOf(Instance)); Add(')','"'); if SepChar<>#0 then Add(SepChar); end; procedure TTextWriter.AddInstancePointer(Instance: TObject; SepChar: AnsiChar; - IncludeUnitName: boolean); + IncludeUnitName, IncludePointer: boolean); begin AddShort(PShortString(PPointer(PPtrInt(Instance)^+vmtClassName)^)^); - Add('('); - AddPointer(PtrUInt(Instance)); - Add(')'); + if IncludePointer then begin + Add('('); + AddBinToHexDisplayMinChars(@Instance,SizeOf(Instance)); + Add(')'); + end; if SepChar<>#0 then Add(SepChar); end; @@ -53759,7 +53412,7 @@ procedure TTextWriter.AddShort(const Text: ShortString); exit; if BEnd-B<=ord(Text[0]) then FlushToStream; - MoveFast(Text[1],B[1],ord(Text[0])); + {$ifdef FPC}Move{$else}MoveFast{$endif}(Text[1],B[1],ord(Text[0])); inc(B,ord(Text[0])); end; @@ -53816,7 +53469,7 @@ procedure TTextWriter.AddString(const Text: RawUTF8); if Lnil then begin EchoFlush; fEchoStart := 0; end; i := B-fTempBuf+1; + if i<=0 then + exit; fStream.WriteBuffer(fTempBuf^,i); inc(fTotalFileSize,i); if not (twoFlushToStreamNoAutoResize in fCustomOptions) and - not (twoBufferIsExternal in fCustomOptions) and - (fTempBufSize<49152) and - (fTotalFileSize-fInitialStreamPosition>1 shl 18) then begin - FreeMem(fTempBuf); // with big content (256KB) comes bigger buffer (64KB) - fTempBufSize := 65536; - GetMem(fTempBuf,65536); - BEnd := fTempBuf+(65536-2); + not (twoBufferIsExternal in fCustomOptions) then begin + written := fTotalFileSize-fInitialStreamPosition; + if (fTempBufSize<49152) and (written>1 shl 18) then // 256KB -> 64KB buffer + written := 65536 else + if (fTempBufSize<1 shl 20) and (written>40 shl 20) then // 40MB -> 1MB buffer + written := 1 shl 20 else + written := 0; + if written>0 then begin + fTempBufSize := written; + FreeMem(fTempBuf); // with big content comes bigger buffer + GetMem(fTempBuf,fTempBufSize); + BEnd := fTempBuf+(fTempBufSize-2); + end; end; B := fTempBuf-1; end; @@ -54028,11 +53665,11 @@ procedure TTextWriter.SetEndOfLineCRLF(aEndOfLineCRLF: boolean); exclude(fCustomOptions,twoEndOfLineCRLF); end; -function TTextWriter.GetLength: cardinal; +function TTextWriter.GetTextLength: PtrUInt; begin if self=nil then result := 0 else - result := cardinal(B-fTempBuf+1)+fTotalFileSize-fInitialStreamPosition; + result := PtrUInt(B-fTempBuf+1)+fTotalFileSize-fInitialStreamPosition; end; function TTextWriter.Text: RawUTF8; @@ -54157,8 +53794,8 @@ procedure TTextWriter.EchoRemove(const aEcho: TOnTextWriterEcho); MultiEventRemove(fEchos,TMethod(aEcho)); end; -function TTextWriter.EchoFlush: integer; -var L,LI: Integer; +function TTextWriter.EchoFlush: PtrInt; +var L,LI: PtrInt; P: PByteArray; begin result := B-fTempBuf+1; @@ -54168,7 +53805,7 @@ function TTextWriter.EchoFlush: integer; dec(L); LI := length(fEchoBuf); // fast append to fEchoBuf SetLength(fEchoBuf,LI+L); - MoveFast(P^,PByteArray(fEchoBuf)[LI],L); + {$ifdef FPC}Move{$else}MoveFast{$endif}(P^,PByteArray(fEchoBuf)[LI],L); end; procedure TTextWriter.EchoReset; @@ -54354,7 +53991,7 @@ function JSONDecode(P: PUTF8Char; const Names: array of RawUTF8; if Values=nil then exit; // avoid GPF n := length(Names); - FillcharFast(Values[0],n*SizeOf(Values[0]),0); + {$ifdef FPC}FillChar{$else}FillCharFast{$endif}(Values[0],n*SizeOf(Values[0]),0); dec(n); if P=nil then exit; @@ -54371,7 +54008,7 @@ function JSONDecode(P: PUTF8Char; const Names: array of RawUTF8; if not(EndOfObject in [',','}']) then exit; // invalid item separator for i := 0 to n do - if IdemPropNameU(Names[i],name,namelen) then begin + if (Values[i].Value=nil) and IdemPropNameU(Names[i],name,namelen) then begin Values[i].Value := value; Values[i].ValueLen := valuelen; break; @@ -54479,7 +54116,7 @@ function GetJSONField(P: PUTF8Char; out PDest: PUTF8Char; wasString: PBoolean; EndOfObject: PUTF8Char; Len: PInteger): PUTF8Char; var D: PUTF8Char; b,c4,surrogate,j: integer; - tab: {$ifdef CPUX86}TNormTableByte absolute ConvertHexToBin{$else}PNormTableByte{$endif}; + tab: {$ifdef CPUX86NOTPIC}TNormTableByte absolute ConvertHexToBin{$else}PNormTableByte{$endif}; label slash,num; begin if wasString<>nil then @@ -54562,7 +54199,7 @@ function GetJSONField(P: PUTF8Char; out PDest: PUTF8Char; 'f': D^ := #$0c; 'r': D^ := #$0d; 'u': begin // inlined decoding of '\u0123' UTF-16 codepoint into UTF-8 - {$ifndef CPUX86}tab := @ConvertHexToBin;{$endif} // faster on PIC and x86_64 + {$ifndef CPUX86NOTPIC}tab := @ConvertHexToBin;{$endif} // faster on PIC and x86_64 c4 := tab[ord(P[1])]; if c4<=15 then begin b := tab[ord(P[2])]; @@ -55180,7 +54817,7 @@ function JSONArrayDecode(P: PUTF8Char; out Values: TPUTF8CharDynArray): boolean; if P^<>']' then repeat if max=n then begin - inc(max,max shr 3+16); + max := NextGrow(max); SetLength(Values,max); end; Values[n] := P; @@ -55643,141 +55280,6 @@ function JSONReformatToFile(const JSON: RawUTF8; const Dest: TFileName; end; -{ TSynPersistentWithPassword } - -destructor TSynPersistentWithPassword.Destroy; -begin - UniqueRawUTF8(fPassword); - FillZero(fPassword); - inherited Destroy; -end; - -class function TSynPersistentWithPassword.ComputePassword(const PlainPassword: RawUTF8; - CustomKey: cardinal): RawUTF8; -var instance: TSynPersistentWithPassword; -begin - instance := TSynPersistentWithPassword.Create; - try - instance.Key := CustomKey; - instance.SetPassWordPlain(PlainPassword); - result := instance.fPassWord; - finally - instance.Free; - end; -end; - -class function TSynPersistentWithPassword.ComputePassword(PlainPassword: pointer; - PlainPasswordLen: integer; CustomKey: cardinal): RawUTF8; -begin - result := ComputePassword(BinToBase64uri(PlainPassword,PlainPasswordLen)); -end; - -class function TSynPersistentWithPassword.ComputePlainPassword(const CypheredPassword: RawUTF8; - CustomKey: cardinal; const AppSecret: RawUTF8): RawUTF8; -var instance: TSynPersistentWithPassword; -begin - instance := TSynPersistentWithPassword.Create; - try - instance.Key := CustomKey; - instance.fPassWord := CypheredPassword; - result := instance.GetPassWordPlainInternal(AppSecret); - finally - instance.Free; - end; -end; - -function TSynPersistentWithPassword.GetPasswordFieldAddress: pointer; -begin - result := @fPassword; -end; - -function TSynPersistentWithPassword.GetKey: cardinal; -begin - if self=nil then - result := 0 else - result := fKey xor $A5abba5A; -end; - -function TSynPersistentWithPassword.GetPassWordPlain: RawUTF8; -begin - result := GetPassWordPlainInternal(''); -end; - -function TSynPersistentWithPassword.GetPassWordPlainInternal(AppSecret: RawUTF8): RawUTF8; -var value,pass: RawByteString; - usr: RawUTF8; - i,j: integer; -begin - result := ''; - if (self=nil) or (fPassWord='') then - exit; - if Assigned(TSynPersistentWithPasswordUserCrypt) then begin - if AppSecret='' then - ToText(ClassType,AppSecret); - usr := ExeVersion.User+':'; - i := PosEx(usr,fPassword); - if (i=1) or ((i>0) and (fPassword[i-1]=',')) then begin - inc(i,length(usr)); - j := PosEx(',',fPassword,i); - if j=0 then - j := length(fPassword)+1; - Base64ToBin(@fPassword[i],j-i,pass); - if pass<>'' then - result := TSynPersistentWithPasswordUserCrypt(pass,AppSecret,false); - end else begin - i := PosEx(':',fPassword); - if i>0 then - raise ESynException.CreateUTF8('%.GetPassWordPlain unable to retrieve the '+ - 'stored value: current user is "%", but password in % was encoded for "%"', - [self,ExeVersion.User,AppSecret,copy(fPassword,1,i-1)]); - end; - end; - if result='' then begin - value := Base64ToBin(fPassWord); - SymmetricEncrypt(GetKey,value); - result := value; - end; -end; - -procedure TSynPersistentWithPassword.SetPassWordPlain(const value: RawUTF8); -var tmp: RawByteString; -begin - if self=nil then - exit; - if value='' then begin - fPassWord := ''; - exit; - end; - SetString(tmp,PAnsiChar(value),Length(value)); // private copy - SymmetricEncrypt(GetKey,tmp); - fPassWord := BinToBase64(tmp); -end; - - -{ TSynConnectionDefinition } - -constructor TSynConnectionDefinition.CreateFromJSON(const JSON: RawUTF8; - Key: cardinal); -var privateCopy: RawUTF8; - values: array[0..4] of TValuePUTF8Char; -begin - fKey := Key; - privateCopy := JSON; - JSONDecode(privateCopy,['Kind','ServerName','DatabaseName','User','Password'],@values); - fKind := values[0].ToString; - values[1].ToUTF8(fServerName); - values[2].ToUTF8(fDatabaseName); - values[3].ToUTF8(fUser); - values[4].ToUTF8(fPassWord); -end; - -function TSynConnectionDefinition.SaveToJSON: RawUTF8; -begin - result := JSONEncode(['Kind',fKind,'ServerName',fServerName, - 'DatabaseName',fDatabaseName,'User',fUser,'Password',fPassword]); -end; - - { ************ some console functions } var @@ -55876,9 +55378,8 @@ procedure TextColor(Color: TConsoleColor); exit; TextAttr := ord(color); if ord(color)>=8 then - write(#27'[1;3') else - write(#27'[0;3'); - write(AnsiTbl[(ord(color) and 7)+1],'m'); + write(#27'[1;3',AnsiTbl[(ord(color) and 7)+1],'m') else + write(#27'[0;3',AnsiTbl[(ord(color) and 7)+1],'m'); ioresult; end; {$I+} @@ -56119,40 +55620,43 @@ function TCommandLine.AsString(const Switch: RawUTF8; const Default, Prompt: str { ************ Unit-Testing classes and functions } -procedure KB(bytes: Int64; out result: TShort16); -const _B: array[0..5] of string[3] = (' KB',' MB',' GB',' TB',' PB',' EB'); -var hi,rem,b: cardinal; +procedure KB(bytes: Int64; out result: TShort16; nospace: boolean); +type TUnits = (kb,mb,gb,tb,pb,eb,b); +const TXT: array[boolean,TUnits] of RawUTF8 = + ((' KB',' MB',' GB',' TB',' PB',' EB','% B'), ('KB','MB','GB','TB','PB','EB','%B')); +var hi,rem: cardinal; + u: TUnits; begin if bytes<1 shl 10-(1 shl 10) div 10 then begin - FormatShort16('% B',[integer(bytes)],result); + FormatShort16(TXT[nospace,b],[integer(bytes)],result); exit; end; if bytes<1 shl 20-(1 shl 20) div 10 then begin - b := 0; + u := kb; rem := bytes; hi := bytes shr 10; end else if bytes<1 shl 30-(1 shl 30) div 10 then begin - b := 1; + u := mb; rem := bytes shr 10; hi := bytes shr 20; end else if bytes0 then - FormatShort16('%.%%',[hi,rem,_B[b]],result) else - FormatShort16('%%',[hi,_B[b]],result); + FormatShort16('%.%%',[hi,rem,TXT[nospace,u]],result) else + FormatShort16('%%',[hi,TXT[nospace,u]],result); end; function KB(bytes: Int64): TShort16; begin - KB(bytes,result); + KB(bytes,result,{nospace=}false); +end; + +function KBNoSpace(bytes: Int64): TShort16; +begin + KB(bytes,result,{nospace=}true); +end; + +function KB(bytes: Int64; nospace: boolean): TShort16; +begin + KB(bytes,result,nospace); end; function KB(const buffer: RawByteString): TShort16; begin - KB(length(buffer), result); + KB(length(buffer),result,{nospace=}false); end; procedure KBU(bytes: Int64; var result: RawUTF8); var tmp: TShort16; begin - KB(bytes,tmp); + KB(bytes,tmp,{nospace=}false); FastSetString(result,@tmp[1],ord(tmp[0])); end; -function IntToThousandString(Value: integer; const ThousandSep: RawUTF8): RawUTF8; +function IntToThousandString(Value: integer; const ThousandSep: TShort4): shortstring; var i,L,Len: cardinal; begin - Int32ToUtf8(value,result); - L := length(Result); + str(Value,result); + L := length(result); Len := L+1; if Value<0 then dec(L,2) else // ignore '-' sign dec(L); for i := 1 to L div 3 do - insert(ThousandSep,Result,Len-i*3); + insert(ThousandSep,result,Len-i*3); end; function MicroSecToString(Micro: QWord): TShort16; @@ -56227,12 +55741,14 @@ procedure MicroSecToString(Micro: QWord; out result: TShort16); if Micro<1000 then FormatShort16('%us',[Micro],result) else if Micro<1000000 then - TwoDigitToString({$ifdef CPU32}Int64Rec(Micro).Lo{$else}Micro{$endif} div 10,'ms',result) else + TwoDigitToString({$ifdef CPU32}PCardinal(@Micro)^{$else}Micro{$endif} div 10,'ms',result) else if Micro<60000000 then - TwoDigitToString({$ifdef CPU32}Int64Rec(Micro).Lo{$else}Micro{$endif} div 10000,'s',result) else - if Micro<3600000000 then - TimeToString({$ifdef CPU32}Int64Rec(Micro).Lo{$else}Micro{$endif} div 1000000,'m',result) else - TimeToString(Micro div 60000000,'h',result); + TwoDigitToString({$ifdef CPU32}PCardinal(@Micro)^{$else}Micro{$endif} div 10000,'s',result) else + if Micro 0; + result := fStart <> 0; end; procedure TPrecisionTimer.ComputeTime; begin - QueryPerformanceCounter(iStop); - if iFreq=0 then begin - QueryPerformanceFrequency(iFreq); - if iFreq=0 then begin - iTime := 0; - iLastTime := 0; + {$ifdef LINUX} + QueryPerformanceMicroSeconds(fStop); + fTime := fStop-fStart; + fLastTime := fStop-fLast; + {$else} + QueryPerformanceCounter(fStop); + if fWinFreq=0 then begin + QueryPerformanceFrequency(fWinFreq); + if fWinFreq=0 then begin + fTime := 0; + fLastTime := 0; exit; end; end; - iTime := ((iStop-iStart)*Int64(1000*1000))div iFreq; - iLastTime := ((iStop-iLast)*Int64(1000*1000))div iFreq; + {$ifdef DELPHI5OROLDER} // circumvent C1093 Error + fTime := ((fStop-fStart)*1000000) div fWinFreq; + if fLast=fStart then + fLastTime := fTime else + fLastTime := ((fStop-fLast)*1000000) div fWinFreq; + {$else} + fTime := (QWord(fStop-fStart)*QWord(1000000)) div QWord(fWinFreq); + if fLast=fStart then + fLastTime := fTime else + fLastTime := (QWord(fStop-fLast)*QWord(1000000)) div QWord(fWinFreq); + {$endif DELPHI5OROLDER} + {$endif LINUX} end; procedure TPrecisionTimer.FromExternalMicroSeconds(const MicroSeconds: QWord); begin - iLastTime := MicroSeconds; - inc(iTime,MicroSeconds); + fLastTime := MicroSeconds; + inc(fTime,MicroSeconds); end; function TPrecisionTimer.FromExternalQueryPerformanceCounters(const CounterDiff: QWord): QWord; -begin // very close to ComputeTime - if iFreq=0 then begin - iTime := 0; - QueryPerformanceFrequency(iFreq); +begin // mimics ComputeTime from already known elapsed time + {$ifdef LINUX} + FromExternalMicroSeconds(CounterDiff); + {$else} + if fWinFreq=0 then begin + fTime := 0; + fLastTime := 0; + QueryPerformanceFrequency(fWinFreq); end; - if iFreq=0 then - iLastTime := 0 else - FromExternalMicroSeconds((Int64(CounterDiff)*Int64(1000*1000))div iFreq); - result := iLastTime; + if fWinFreq<>0 then + FromExternalMicroSeconds((CounterDiff*QWord(1000000))div PQWord(@fWinFreq)^); + {$endif LINUX} + result := fLastTime; end; function TPrecisionTimer.Stop: TShort16; begin ComputeTime; - MicroSecToString(iTime,result); + MicroSecToString(fTime,result); end; procedure TPrecisionTimer.Pause; begin - QueryPerformanceCounter(iResume); - dec(iResume,iStart); + {$ifdef LINUX}QueryPerformanceMicroSeconds{$else}QueryPerformanceCounter{$endif}(fResume); + dec(fResume,fStart); inc(fPauseCount); end; procedure TPrecisionTimer.Resume; begin - QueryPerformanceCounter(iStart); - iLast := iStart; - dec(iStart,iResume); - iResume := 0; + {$ifdef LINUX}QueryPerformanceMicroSeconds{$else}QueryPerformanceCounter{$endif}(fStart); + fLast := fStart; + dec(fStart,fResume); + fResume := 0; end; function TPrecisionTimer.Time: TShort16; begin - MicroSecToString(iTime,result); + MicroSecToString(fTime,result); end; function TPrecisionTimer.LastTime: TShort16; begin - MicroSecToString(iLastTime,result); + MicroSecToString(fLastTime,result); end; @@ -56381,7 +55921,7 @@ destructor TPrecisionTimerProfiler.Destroy; function TPrecisionTimer.ProfileCurrentMethod: IUnknown; begin - if iStart=0 then + if fStart=0 then Start else Resume; result := TPrecisionTimerProfiler.Create(@self); @@ -56435,9 +55975,9 @@ function TSynMonitorTime.GetAsText: TShort16; function TSynMonitorTime.PerSecond(const Count: QWord): QWord; begin - if PInt64(@fMicroSeconds)^<=0 then // avoid negative or div per 0 - result := 0 else - result := (Count*QWord(1000*1000)) div fMicroSeconds; + if {$ifdef FPC}Int64(fMicroSeconds){$else}PInt64(@fMicroSeconds)^{$endif}<=0 then + result := 0 else // avoid negative or div per 0 + result := (Count*QWord(1000000)) div fMicroSeconds; end; @@ -56450,31 +55990,39 @@ function TSynMonitorOneTime.GetAsText: TShort16; function TSynMonitorOneTime.PerSecond(const Count: QWord): QWord; begin - if PInt64(@fMicroSeconds)^<=0 then // avoid negative or div per 0 + if {$ifdef FPC}Int64(fMicroSeconds){$else}PInt64(@fMicroSeconds)^{$endif}<=0 then result := 0 else - result := (Count*QWord(1000*1000)) div fMicroSeconds; + result := (Count*QWord(1000000)) div fMicroSeconds; end; +{ TSynMonitorSizeParent } + +constructor TSynMonitorSizeParent.Create(aTextNoSpace: boolean); +begin + inherited Create; + fTextNoSpace := aTextNoSpace; +end; + { TSynMonitorSize } function TSynMonitorSize.GetAsText: TShort16; begin - KB(fBytes,result); + KB(fBytes,result,fTextNoSpace); end; { TSynMonitorOneSize } function TSynMonitorOneSize.GetAsText: TShort16; begin - KB(fBytes,result); + KB(fBytes,result,fTextNoSpace); end; { TSynMonitorThroughput } function TSynMonitorThroughput.GetAsText: TShort16; begin - FormatShort16('%/s',[KB(fBytesPerSec)],result); + FormatShort16('%/s',[KB(fBytesPerSec,fTextNoSpace)],result); end; @@ -56488,7 +56036,6 @@ constructor TSynMonitor.Create; fMinimalTime := TSynMonitorOneTime.Create; fAverageTime := TSynMonitorOneTime.Create; fMaximalTime := TSynMonitorOneTime.Create; - InitializeCriticalSection(fLock); end; constructor TSynMonitor.Create(const aName: RawUTF8); @@ -56504,18 +56051,17 @@ destructor TSynMonitor.Destroy; fMinimalTime.Free; fLastTime.Free; fTotalTime.Free; - DeleteCriticalSection(fLock); inherited Destroy; end; procedure TSynMonitor.Lock; begin - EnterCriticalSection(fLock); + fSafe^.Lock; end; procedure TSynMonitor.UnLock; begin - LeaveCriticalSection(fLock); + fSafe^.UnLock; end; procedure TSynMonitor.Changed; @@ -56526,25 +56072,25 @@ procedure TSynMonitor.ProcessStart; begin if fProcessing then raise ESynException.CreateUTF8('Reentrant %.ProcessStart',[self]); - EnterCriticalSection(fLock); + fSafe^.Lock; try InternalTimer.Resume; fTaskStatus := taskNotStarted; fProcessing := true; finally - LeaveCriticalSection(fLock); + fSafe^.UnLock; end; end; procedure TSynMonitor.ProcessDoTask; begin - EnterCriticalSection(fLock); + fSafe^.Lock; try inc(fTaskCount); fTaskStatus := taskStarted; Changed; finally - LeaveCriticalSection(fLock); + fSafe^.UnLock; end; end; @@ -56552,7 +56098,7 @@ procedure TSynMonitor.ProcessStartTask; begin if fProcessing then raise ESynException.CreateUTF8('Reentrant %.ProcessStart',[self]); - EnterCriticalSection(fLock); + fSafe^.Lock; try InternalTimer.Resume; fProcessing := true; @@ -56560,19 +56106,19 @@ procedure TSynMonitor.ProcessStartTask; fTaskStatus := taskStarted; Changed; finally - LeaveCriticalSection(fLock); + fSafe^.UnLock; end; end; procedure TSynMonitor.ProcessEnd; begin - EnterCriticalSection(fLock); + fSafe^.Lock; try InternalTimer.Pause; InternalTimer.ComputeTime; LockedFromProcessTimer; finally - LeaveCriticalSection(fLock); + fSafe^.UnLock; end; end; @@ -56595,27 +56141,27 @@ procedure TSynMonitor.LockedFromProcessTimer; function TSynMonitor.FromExternalQueryPerformanceCounters(const CounterDiff: QWord): QWord; begin - EnterCriticalSection(fLock); + fSafe^.Lock; try // thread-safe ProcessStart+ProcessDoTask+ProcessEnd inc(fTaskCount); fTaskStatus := taskStarted; result := InternalTimer.FromExternalQueryPerformanceCounters(CounterDiff); LockedFromProcessTimer; finally - LeaveCriticalSection(fLock); + fSafe^.UnLock; end; end; procedure TSynMonitor.FromExternalMicroSeconds(const MicroSecondsElapsed: QWord); begin - EnterCriticalSection(fLock); + fSafe^.Lock; try // thread-safe ProcessStart+ProcessDoTask+ProcessEnd inc(fTaskCount); fTaskStatus := taskStarted; InternalTimer.FromExternalMicroSeconds(MicroSecondsElapsed); LockedFromProcessTimer; finally - LeaveCriticalSection(fLock); + fSafe^.UnLock; end; end; @@ -56630,14 +56176,14 @@ class procedure TSynMonitor.InitializeObjArray(var ObjArr; Count: integer); procedure TSynMonitor.ProcessError(const info: variant); begin - EnterCriticalSection(fLock); + fSafe^.Lock; try if not VarIsEmptyOrNull(info) then inc(fInternalErrors); fLastInternalError := info; Changed; finally - LeaveCriticalSection(fLock); + fSafe^.UnLock; end; end; @@ -56663,20 +56209,20 @@ procedure TSynMonitor.LockedPerSecProperties; if fTaskCount=0 then exit; // avoid division per zero fPerSec := fTotalTime.PerSecond(fTaskCount); - fAverageTime.MicroSec := Round(fTotalTime.MicroSec/fTaskCount); + fAverageTime.MicroSec := fTotalTime.MicroSec div fTaskCount; end; procedure TSynMonitor.Sum(another: TSynMonitor); begin if (self=nil) or (another=nil) then exit; - EnterCriticalSection(fLock); - EnterCriticalSection(another.fLock); + fSafe^.Lock; + another.fSafe^.Lock; try LockedSum(another); finally - LeaveCriticalSection(another.fLock); - LeaveCriticalSection(fLock); + another.fSafe^.UnLock; + fSafe^.UnLock; end; end; @@ -56696,22 +56242,22 @@ procedure TSynMonitor.LockedSum(another: TSynMonitor); procedure TSynMonitor.WriteDetailsTo(W: TTextWriter); begin - EnterCriticalSection(fLock); + fSafe^.Lock; try W.WriteObject(self); finally - LeaveCriticalSection(fLock); + fSafe^.UnLock; end; end; procedure TSynMonitor.ComputeDetailsTo(W: TTextWriter); begin - EnterCriticalSection(fLock); + fSafe^.Lock; try LockedPerSecProperties; // may not have been calculated after Sum() WriteDetailsTo(W); finally - LeaveCriticalSection(fLock); + fSafe^.UnLock; end; end; @@ -56741,8 +56287,8 @@ function TSynMonitor.ComputeDetails: variant; constructor TSynMonitorWithSize.Create; begin inherited Create; - fSize := TSynMonitorSize.Create; - fThroughput := TSynMonitorThroughput.Create; + fSize := TSynMonitorSize.Create({nospace=}false); + fThroughput := TSynMonitorThroughput.Create({nospace=}false); end; destructor TSynMonitorWithSize.Destroy; @@ -56760,11 +56306,11 @@ procedure TSynMonitorWithSize.LockedPerSecProperties; procedure TSynMonitorWithSize.AddSize(const Bytes: QWord); begin - EnterCriticalSection(fLock); + fSafe^.Lock; try fSize.Bytes := fSize.Bytes+Bytes; finally - LeaveCriticalSection(fLock); + fSafe^.UnLock; end; end; @@ -56781,10 +56327,10 @@ procedure TSynMonitorWithSize.LockedSum(another: TSynMonitor); constructor TSynMonitorInputOutput.Create; begin inherited Create; - fInput := TSynMonitorSize.Create; - fOutput := TSynMonitorSize.Create; - fInputThroughput := TSynMonitorThroughput.Create; - fOutputThroughput := TSynMonitorThroughput.Create; + fInput := TSynMonitorSize.Create({nospace=}false); + fOutput := TSynMonitorSize.Create({nospace=}false); + fInputThroughput := TSynMonitorThroughput.Create({nospace=}false); + fOutputThroughput := TSynMonitorThroughput.Create({nospace=}false); end; destructor TSynMonitorInputOutput.Destroy; @@ -56805,12 +56351,12 @@ procedure TSynMonitorInputOutput.LockedPerSecProperties; procedure TSynMonitorInputOutput.AddSize(const Incoming, Outgoing: QWord); begin - EnterCriticalSection(fLock); + fSafe^.Lock; try fInput.Bytes := fInput.Bytes+Incoming; fOutput.Bytes := fOutput.Bytes+Outgoing; finally - LeaveCriticalSection(fLock); + fSafe^.UnLock; end; end; @@ -56830,14 +56376,14 @@ procedure TSynMonitorServer.ClientConnect; begin if self=nil then exit; - EnterCriticalSection(fLock); + fSafe^.Lock; try inc(fClientsCurrent); if fClientsCurrent>fClientsMax then fClientsMax := fClientsCurrent; Changed; finally - LeaveCriticalSection(fLock); + fSafe^.UnLock; end; end; @@ -56845,13 +56391,13 @@ procedure TSynMonitorServer.ClientDisconnect; begin if self=nil then exit; - EnterCriticalSection(fLock); + fSafe^.Lock; try if fClientsCurrent>0 then dec(fClientsCurrent); Changed; finally - LeaveCriticalSection(fLock); + fSafe^.UnLock; end; end; @@ -56859,12 +56405,12 @@ procedure TSynMonitorServer.ClientDisconnectAll; begin if self=nil then exit; - EnterCriticalSection(fLock); + fSafe^.Lock; try fClientsCurrent := 0; Changed; finally - LeaveCriticalSection(fLock); + fSafe^.UnLock; end; end; @@ -56874,11 +56420,11 @@ function TSynMonitorServer.GetClientsCurrent: TSynMonitorOneCount; result := 0; exit; end; - EnterCriticalSection(fLock); + fSafe^.Lock; try result := fClientsCurrent; finally - LeaveCriticalSection(fLock); + fSafe^.UnLock; end; end; @@ -56888,375 +56434,16 @@ function TSynMonitorServer.AddCurrentRequestCount(diff: integer): integer; result := 0; exit; end; - EnterCriticalSection(fLock); + fSafe^.Lock; try inc(fCurrentRequestCount,diff); result := fCurrentRequestCount; finally - LeaveCriticalSection(fLock); - end; -end; - -{ TSynMonitorMemory } - -constructor TSynMonitorMemory.Create; -begin - FAllocatedUsed := TSynMonitorOneSize.create; - FAllocatedReserved := TSynMonitorOneSize.create; - FPhysicalMemoryFree := TSynMonitorOneSize.Create; - FVirtualMemoryFree := TSynMonitorOneSize.Create; - FPagingFileTotal := TSynMonitorOneSize.Create; - FPhysicalMemoryTotal := TSynMonitorOneSize.Create; - FVirtualMemoryTotal := TSynMonitorOneSize.Create; - FPagingFileFree := TSynMonitorOneSize.Create; -end; - -destructor TSynMonitorMemory.Destroy; -begin - FAllocatedReserved.Free; - FAllocatedUsed.Free; - FPhysicalMemoryFree.Free; - FVirtualMemoryFree.Free; - FPagingFileTotal.Free; - FPhysicalMemoryTotal.Free; - FVirtualMemoryTotal.Free; - FPagingFileFree.Free; - inherited Destroy; -end; - -class function TSynMonitorMemory.FreeAsText: RawUTF8; -begin - with TSynMonitorMemory.Create do - try - RetrieveMemoryInfo; - FormatUTF8('% / %',[fPhysicalMemoryFree.Text,fPhysicalMemoryTotal.Text],result); - finally - Free; - end; -end; - -var - PhysicalAsTextCache: RawUTF8; // this value doesn't change usually - -class function TSynMonitorMemory.PhysicalAsText: RawUTF8; -begin - if PhysicalAsTextCache='' then - with TSynMonitorMemory.Create do - try - PhysicalAsTextCache := PhysicalMemoryTotal.Text; - finally - Free; - end; - result := PhysicalAsTextCache; -end; - -class function TSynMonitorMemory.ToJSON: RawUTF8; -begin - with TSynMonitorMemory.Create do - try - RetrieveMemoryInfo; - FormatUTF8('{Allocated:{reserved:%,used:%},Physical:{total:%,free:%,percent:%},'+ - {$ifdef MSWINDOWS}'Virtual:{total:%,free:%},'+{$endif}'Paged:{total:%,free:%}}', - [fAllocatedReserved.Bytes shr 10,fAllocatedUsed.Bytes shr 10, - fPhysicalMemoryTotal.Bytes shr 10,fPhysicalMemoryFree.Bytes shr 10, fMemoryLoadPercent, - {$ifdef MSWINDOWS}fVirtualMemoryTotal.Bytes shr 10,fVirtualMemoryFree.Bytes shr 10,{$endif} - fPagingFileTotal.Bytes shr 10,fPagingFileFree.Bytes shr 10],result); - finally - Free; - end; -end; - -{$ifndef NOVARIANTS} -class function TSynMonitorMemory.ToVariant: variant; -begin - result := _JsonFast(ToJSON); -end; -{$endif} - -function TSynMonitorMemory.GetAllocatedUsed: TSynMonitorOneSize; -begin - RetrieveMemoryInfo; - result := FAllocatedUsed; -end; - -function TSynMonitorMemory.GetAllocatedReserved: TSynMonitorOneSize; -begin - RetrieveMemoryInfo; - result := FAllocatedReserved; -end; - -function TSynMonitorMemory.GetMemoryLoadPercent: integer; -begin - RetrieveMemoryInfo; - result := FMemoryLoadPercent; -end; - -function TSynMonitorMemory.GetPagingFileFree: TSynMonitorOneSize; -begin - RetrieveMemoryInfo; - result := FPagingFileFree; -end; - -function TSynMonitorMemory.GetPagingFileTotal: TSynMonitorOneSize; -begin - RetrieveMemoryInfo; - result := FPagingFileTotal; -end; - -function TSynMonitorMemory.GetPhysicalMemoryFree: TSynMonitorOneSize; -begin - RetrieveMemoryInfo; - result := FPhysicalMemoryFree; -end; - -function TSynMonitorMemory.GetPhysicalMemoryTotal: TSynMonitorOneSize; -begin - RetrieveMemoryInfo; - result := FPhysicalMemoryTotal; -end; - -function TSynMonitorMemory.GetVirtualMemoryFree: TSynMonitorOneSize; -begin - RetrieveMemoryInfo; - result := FVirtualMemoryFree; -end; - -function TSynMonitorMemory.GetVirtualMemoryTotal: TSynMonitorOneSize; -begin - RetrieveMemoryInfo; - result := FVirtualMemoryTotal; -end; - -{$ifdef MSWINDOWS} -{$ifndef UNICODE} // missing API for oldest Delphi -type - DWORDLONG = Int64; - TMemoryStatusEx = record - dwLength: DWORD; - dwMemoryLoad: DWORD; - ullTotalPhys: DWORDLONG; - ullAvailPhys: DWORDLONG; - ullTotalPageFile: DWORDLONG; - ullAvailPageFile: DWORDLONG; - ullTotalVirtual: DWORDLONG; - ullAvailVirtual: DWORDLONG; - ullAvailExtendedVirtual: DWORDLONG; - end; - -// information about the system's current usage of both physical and virtual memory -function GlobalMemoryStatusEx(var lpBuffer: TMemoryStatusEx): BOOL; - stdcall; external kernel32; -{$endif} -{$endif} - -procedure TSynMonitorMemory.RetrieveMemoryInfo; -procedure RetrieveInfo; -{$ifndef FPC} -var Heap: TMemoryManagerState; - sb: integer; - tot,res: QWord; -{$endif} -{$ifdef MSWINDOWS} -var global: TMemoryStatusEx; - {$ifdef FPC}mem: TProcessMemoryCounters;{$endif} -begin - FillCharFast(global,SizeOf(global),0); - global.dwLength := SizeOf(global); - GlobalMemoryStatusEx(global); - FMemoryLoadPercent := global.dwMemoryLoad; - FPhysicalMemoryTotal.fBytes := global.ullTotalPhys; - FPhysicalMemoryFree.fBytes := global.ullAvailPhys; - FPagingFileTotal.fBytes := global.ullTotalPageFile; - FPagingFileFree.fBytes := global.ullAvailPageFile; - FVirtualMemoryTotal.fBytes := global.ullTotalVirtual; - FVirtualMemoryFree.fBytes := global.ullAvailVirtual; - {$ifdef FPC} // GetHeapStatus is only about current thread -> use WinAPI - if Assigned(GetProcessMemoryInfo) then begin - FillcharFast(mem,SizeOf(mem),0); - mem.cb := SizeOf(mem); - GetProcessMemoryInfo(GetCurrentProcess,mem,SizeOf(mem)); - FAllocatedReserved.fBytes := mem.PeakWorkingSetSize; - FAllocatedUsed.fBytes := mem.WorkingSetSize; - end; - {$endif FPC} -{$else} -{$ifdef BSD} -begin - FPhysicalMemoryTotal.fBytes := fpsysctlhwint( - {$ifdef DARWIN}HW_MEMSIZE{$else}HW_PHYSMEM{$endif}); - FPhysicalMemoryFree.fBytes := FPhysicalMemoryTotal.fBytes-fpsysctlhwint(HW_USERMEM); - if FPhysicalMemoryTotal.fBytes<>0 then // avoid div per 0 exception - FMemoryLoadPercent := ((FPhysicalMemoryTotal.fBytes-FPhysicalMemoryFree.fBytes)*100)div FPhysicalMemoryTotal.fBytes; -{$else} -var si: TSysInfo; // Linuxism - P: PUTF8Char; - {$ifdef FPC}mu: cardinal{$else}const mu=1{$endif}; -begin - {$ifdef FPC} - SysInfo(@si); - mu := si.mem_unit; - {$else} - SysInfo(si); // missing field in Kylix' Libc - {$endif} - if si.totalram<>0 then // avoid div per 0 exception - FMemoryLoadPercent := ((si.totalram-si.freeram)*100)div si.totalram; - FPhysicalMemoryTotal.fBytes := si.totalram*mu; - FPhysicalMemoryFree.fBytes := si.freeram*mu; - FPagingFileTotal.fBytes := si.totalswap*mu; - FPagingFileFree.fBytes := si.freeswap*mu; - // virtual memory information is not available under Linux - P := pointer(StringFromFile('/proc/self/statm',true)); - FAllocatedReserved.fBytes := GetNextItemCardinal(P,' ')*SystemInfo.dwPageSize; // VmSize - FAllocatedUsed.fBytes := GetNextItemCardinal(P,' ')*SystemInfo.dwPageSize; // VmRSS - // GetHeapStatus is only about current thread -> use /proc/[pid]/statm -{$endif BSD} -{$endif MSWINDOWS} -{$ifndef FPC} -{$ifdef LVCL} - tot := 0; - res := 0; -{$else} - GetMemoryManagerState(Heap); // direct access to FastMM4 statistics - tot := Heap.TotalAllocatedMediumBlockSize+Heap.TotalAllocatedLargeBlockSize; - res := Heap.ReservedMediumBlockAddressSpace+Heap.ReservedLargeBlockAddressSpace; - for sb := 0 to high(Heap.SmallBlockTypeStates) do - with Heap.SmallBlockTypeStates[sb] do begin - inc(tot,UseableBlockSize*AllocatedBlockCount); - inc(res,ReservedAddressSpace); - end; -{$endif LVCL} - FAllocatedUsed.fBytes := tot; - FAllocatedReserved.fBytes := res; -{$endif FPC} -end; -var tix: cardinal; -begin - tix := GetTickCount64 shr 7; // allow 128 ms resolution for updates - if fLastMemoryInfoRetrievedTix<>tix then begin - fLastMemoryInfoRetrievedTix := tix; - RetrieveInfo; - end; -end; - - -{ TSynMonitorDisk } - -constructor TSynMonitorDisk.Create; -begin - fAvailableSize := TSynMonitorOneSize.Create; - fFreeSize := TSynMonitorOneSize.Create; - fTotalSize := TSynMonitorOneSize.Create; -end; - -destructor TSynMonitorDisk.Destroy; -begin - fAvailableSize.Free; - fFreeSize.Free; - fTotalSize.Free; - inherited; -end; - -function TSynMonitorDisk.GetName: TFileName; -begin - RetrieveDiskInfo; - result := fName; -end; - -function TSynMonitorDisk.GetAvailable: TSynMonitorOneSize; -begin - RetrieveDiskInfo; - result := fAvailableSize; -end; - -function TSynMonitorDisk.GetFree: TSynMonitorOneSize; -begin - RetrieveDiskInfo; - result := fFreeSize; -end; - -function TSynMonitorDisk.GetTotal: TSynMonitorOneSize; -begin - RetrieveDiskInfo; - result := fTotalSize; -end; - -class function TSynMonitorDisk.FreeAsText: RawUTF8; -var name: TFileName; - avail,free,total: QWord; -begin - GetDiskInfo(name,avail,free,total); - FormatUTF8('% % / %',[name, KB(free),KB(total)],result); -end; - -{$ifdef MSWINDOWS} -function GetDiskFreeSpaceExA(lpDirectoryName: PAnsiChar; - var lpFreeBytesAvailableToCaller, lpTotalNumberOfBytes, - lpTotalNumberOfFreeBytes: QWord): LongBool; stdcall; external kernel32; -function GetDiskFreeSpaceExW(lpDirectoryName: PWideChar; - var lpFreeBytesAvailableToCaller, lpTotalNumberOfBytes, - lpTotalNumberOfFreeBytes: QWord): LongBool; stdcall; external kernel32; -{$endif} - -procedure GetDiskInfo(var aDriveFolderOrFile: TFileName; - out aAvailableBytes, aFreeBytes, aTotalBytes: QWord - {$ifdef MSWINDOWS}; aVolumeName: PFileName = nil{$endif}); -{$ifdef MSWINDOWS} -var tmp: array[0..MAX_PATH-1] of Char; - dummy,flags: DWORD; - dn: TFileName; -begin - if aDriveFolderOrFile='' then - aDriveFolderOrFile := SysUtils.UpperCase(ExtractFileDrive(ExeVersion.ProgramFilePath)); - dn := aDriveFolderOrFile; - if (dn<>'') and (dn[2]=':') and (dn[3]=#0) then - dn := dn+'\'; - if (aVolumeName<>nil) and (aVolumeName^='') then begin - tmp[0] := #0; - GetVolumeInformation(pointer(dn),tmp,MAX_PATH,nil,dummy,flags,nil,0); - aVolumeName^ := tmp; - end; - {$ifdef UNICODE}GetDiskFreeSpaceExW{$else}GetDiskFreeSpaceExA{$endif}( - pointer(dn),aAvailableBytes,aTotalBytes,aFreeBytes); -{$else} -{$ifdef KYLIX3} -var fs: TStatFs64; - h: THandle; -begin - if aDriveFolderOrFile='' then - aDriveFolderOrFile := '.'; - h := FileOpen(aDriveFolderOrFile,fmShareDenyNone); - fstatfs64(h,fs); - FileClose(h); - aAvailableBytes := fs.f_bavail*fs.f_bsize; - aFreeBytes := aAvailableBytes; - aTotalBytes := fs.f_blocks*fs.f_bsize; -{$endif} -{$ifdef FPC} -var fs: tstatfs; -begin - if aDriveFolderOrFile='' then - aDriveFolderOrFile := '.'; - fpStatFS(aDriveFolderOrFile,@fs); - aAvailableBytes := QWord(fs.bavail)*QWord(fs.bsize); - aFreeBytes := aAvailableBytes; // no user Quota involved here - aTotalBytes := QWord(fs.blocks)*QWord(fs.bsize); -{$endif FPC} -{$endif MSWINDOWS} -end; - -procedure TSynMonitorDisk.RetrieveDiskInfo; -var tix: cardinal; -begin - tix := GetTickCount64 shr 7; // allow 128 ms resolution for updates - if fLastDiskInfoRetrievedTix<>tix then begin - fLastDiskInfoRetrievedTix := tix; - GetDiskInfo(fName,PQWord(@fAvailableSize.fBytes)^,PQWord(@fFreeSize.fBytes)^, - PQWord(@fTotalSize.fBytes)^{$ifdef MSWINDOWS},@fVolumeName{$endif}); + fSafe^.UnLock; end; end; - { ******************* cross-cutting classes and functions ***************** } { TSynInterfacedObject } @@ -57672,7 +56859,7 @@ function GetDelphiCompilerVersion: RawUTF8; {$ifdef VER3_0_1}+' 3.0.1'{$endif} {$ifdef VER3_0_2}+' 3.0.2'{$endif} {$ifdef VER3_1_1}+' 3.1.1'{$endif} - {$ifdef VER3_2}+' 3.2'{$endif} + {$ifdef VER3_2} +' 3.2' {$endif} {$ifdef VER3_3_1}+' 3.3.1'{$endif} {$else} {$ifdef VER130} 'Delphi 5'{$endif} @@ -57712,18 +56899,12 @@ function GetDelphiCompilerVersion: RawUTF8; constructor TSynCache.Create(aMaxCacheRamUsed: cardinal; aCaseSensitive: boolean; aTimeoutSeconds: cardinal); begin + inherited Create; fNameValue.Init(aCaseSensitive); fNameValue.fDynArray.Capacity := 200; // some space for future cached entries fMaxRamUsed := aMaxCacheRamUsed; fFindLastAddedIndex := -1; fTimeoutSeconds := aTimeoutSeconds; - fSafe.Init; -end; - -destructor TSynCache.Destroy; -begin - inherited Destroy; - fSafe.Done; end; procedure TSynCache.ResetIfNeeded; @@ -57732,7 +56913,7 @@ procedure TSynCache.ResetIfNeeded; if fRamUsed>fMaxRamUsed then Reset; if fTimeoutSeconds>0 then begin - tix := GetTickCount64 shr 10; + tix := {$ifdef FPCLINUX}SynFPCLinux.{$endif}GetTickCount64 shr 10; if fTimeoutTix>tix then Reset; fTimeoutTix := tix+fTimeoutSeconds; @@ -57851,10 +57032,8 @@ function TRawUTF8List.Add(const aText: RawUTF8): PtrInt; if fObjects=nil then begin capacity := length(fList); result := fCount; - if result>=capacity then begin - inc(capacity,256+fCount shr 3); - SetLength(fList,capacity); - end; + if result>=capacity then + SetLength(fList,NextGrow(capacity)); fList[result] := aText; inc(fCount); Changed; @@ -57897,13 +57076,13 @@ function TRawUTF8List.AddObject(const aText: RawUTF8; aObject: TObject): PtrInt; capacity := length(fList); result := fCount; if result>=capacity then begin - inc(capacity,256+fCount shr 3); + capacity := NextGrow(capacity); SetLength(fList,capacity); if (fObjects<>nil) or (aObject<>nil) then SetLength(fObjects,capacity); end else if (aObject<>nil) and (fObjects=nil) then - SetLength(fObjects,capacity); + SetLength(fObjects,capacity); // first time we got aObject<>nil fList[result] := aText; if aObject<>nil then fObjects[result] := aObject; @@ -57971,10 +57150,12 @@ procedure TRawUTF8List.Delete(Index: PtrInt); // swap the string/object arrays dec(fCount); if Indexnil then begin - MoveFast(fObjects[Index+1],fObjects[Index],(fCount-Index)*SizeOf(fObjects[0])); + {$ifdef FPC}Move{$else}MoveFast{$endif}( + fObjects[Index+1],fObjects[Index],(fCount-Index)*SizeOf(fObjects[0])); fObjects[fCount] := nil; // avoid GPF if fObjectsOwned is set end; end; @@ -58044,15 +57225,14 @@ function TRawUTF8List.GetObjectPtr: PPointerArray; end; function TRawUTF8List.GetName(Index: PtrInt): RawUTF8; -var Sep: PUTF8Char; begin result := Get(Index); if result='' then exit; - Sep := PosChar(pointer(result),NameValueSep); - if Sep=nil then + Index := PosExChar(NameValueSep,result); + if Index=0 then result := '' else - SetLength(result,Sep-pointer(result)); + SetLength(result,Index-1); end; function TRawUTF8List.GetObject(Index: PtrInt): TObject; @@ -58097,13 +57277,13 @@ function TRawUTF8List.GetText(const Delimiter: RawUTF8): RawUTF8; repeat Len := length(fList[i]); if Len>0 then begin - MoveFast(pointer(fList[i])^,P^,Len); + {$ifdef FPC}Move{$else}MoveFast{$endif}(pointer(fList[i])^,P^,Len); inc(P,Len); end; inc(i); if i>=fCount then Break; - MoveFast(pointer(Delimiter)^,P^,DelimLen); + {$ifdef FPC}Move{$else}MoveFast{$endif}(pointer(Delimiter)^,P^,DelimLen); inc(P,DelimLen); until false; end; @@ -58153,17 +57333,16 @@ function TRawUTF8List.GetValue(const Name: RawUTF8): RawUTF8; end; function TRawUTF8List.GetValueAt(Index: PtrInt): RawUTF8; -var Sep: PUTF8Char; begin if (self=nil) or (PtrUInt(Index)>=PtrUInt(fCount)) then result := '' else result := Get(Index); if result='' then exit; - Sep := PosChar(pointer(result),NameValueSep); - if Sep=nil then + Index := PosExChar(NameValueSep,result); + if Index=0 then result := '' else - result := Sep+1; // get 'Value' from 'Name=Value' + result := copy(result,Index+1,maxInt); end; function TRawUTF8List.IndexOf(const aText: RawUTF8): PtrInt; @@ -58939,7 +58118,8 @@ procedure TRawUTF8MethodList.Delete(Index: PtrInt); begin inherited Delete(Index); if Index0 then - result := cardinal(GetTickCount64 shr 10)+result; + result := cardinal({$ifdef FPCLINUX}SynFPCLinux.{$endif}GetTickCount64 shr 10)+result; end; function TSynDictionary.GetCapacity: integer; @@ -59049,7 +58229,7 @@ function TSynDictionary.GetTimeOutSeconds: cardinal; end; procedure TSynDictionary.SetTimeouts; -var i: integer; +var i: PtrInt; timeout: cardinal; begin if fSafe.Padding[DIC_TIMESEC].VInteger=0 then @@ -59061,14 +58241,14 @@ procedure TSynDictionary.SetTimeouts; end; function TSynDictionary.DeleteDeprecated: integer; -var i: integer; +var i: PtrInt; now: cardinal; begin result := 0; if (self=nil) or (fSafe.Padding[DIC_TIMECOUNT].VInteger=0) or // no entry (fSafe.Padding[DIC_TIMESEC].VInteger=0) then // nothing in fTimeOut[] exit; - now := GetTickCount64 shr 10; + now := {$ifdef FPCLINUX}SynFPCLinux.{$endif}GetTickCount64 shr 10; if fSafe.Padding[DIC_TIMETIX].VInteger=integer(now) then exit; // no need to search more often than every second fSafe.Lock; @@ -59076,8 +58256,8 @@ function TSynDictionary.DeleteDeprecated: integer; fSafe.Padding[DIC_TIMETIX].VInteger := now; for i := fSafe.Padding[DIC_TIMECOUNT].VInteger-1 downto 0 do if (now>fTimeOut[i]) and (fTimeOut[i]<>0) and - (not Assigned(fOnCanDelete) or fOnCanDelete(fKeys.{$ifdef UNDIRECTDYNARRAY} - InternalDynArray.{$endif}ElemPtr(i)^,fValues.ElemPtr(i)^,i)) then begin + (not Assigned(fOnCanDelete) or + fOnCanDelete(fKeys.ElemPtr(i)^,fValues.ElemPtr(i)^,i)) then begin fKeys.Delete(i); fValues.Delete(i); fTimeOuts.Delete(i); @@ -59218,7 +58398,7 @@ function TSynDictionary.InArray(const aKey, aArrayValue; ndx := fKeys.FindHashed(aKey); if ndx<0 then exit; - nested.Init(fValues.ElemType, fValues.ElemPtr(ndx)^); + nested.Init(fValues.ElemType,fValues.ElemPtr(ndx)^); case aAction of iaFind: result := nested.Find(aArrayValue)>=0; @@ -59241,6 +58421,24 @@ function TSynDictionary.FindInArray(const aKey, aArrayValue): boolean; result := InArray(aKey,aArrayValue,iaFind); end; +function TSynDictionary.FindKeyFromValue(const aValue; out aKey; + aUpdateTimeOut: boolean): boolean; +var ndx: integer; +begin + fSafe.Lock; + try + ndx := fValues.IndexOf(aValue); + result := ndx>=0; + if result then begin + fKeys.ElemCopyAt(ndx,aKey); + if aUpdateTimeOut then + SetTimeoutAtIndex(ndx); + end; + finally + fSafe.UnLock; + end; +end; + function TSynDictionary.DeleteInArray(const aKey, aArrayValue): boolean; begin result := InArray(aKey,aArrayValue,iaFindAndDelete); @@ -59270,7 +58468,7 @@ function TSynDictionary.Find(const aKey; aUpdateTimeOut: boolean): integer; if aUpdateTimeOut and (result>=0) then begin tim := fSafe.Padding[DIC_TIMESEC].VInteger; if tim>0 then // inlined fTimeout[result] := GetTimeout - fTimeout[result] := cardinal(GetTickCount64 shr 10)+tim; + fTimeout[result] := cardinal({$ifdef FPCLINUX}SynFPCLinux.{$endif}GetTickCount64 shr 10)+tim; end; end; @@ -59292,7 +58490,7 @@ function TSynDictionary.FindValueOrAdd(const aKey; var added: boolean; begin tim := fSafe.Padding[DIC_TIMESEC].VInteger; // inlined tim := GetTimeout if tim<>0 then - tim := cardinal(GetTickCount64 shr 10)+tim; + tim := cardinal({$ifdef FPCLINUX}SynFPCLinux.{$endif}GetTickCount64 shr 10)+tim; ndx := fKeys.FindHashedForAdding(aKey,added); if added then begin with fKeys{$ifdef UNDIRECTDYNARRAY}.InternalDynArray{$endif} do @@ -59430,7 +58628,7 @@ procedure TSynDictionary.SetTimeoutAtIndex(aIndex: integer); exit; tim := fSafe.Padding[DIC_TIMESEC].VInteger; if tim > 0 then - fTimeOut[aIndex] := cardinal(GetTickCount64 shr 10)+tim; + fTimeOut[aIndex] := cardinal({$ifdef FPCLINUX}SynFPCLinux.{$endif}GetTickCount64 shr 10)+tim; end; function TSynDictionary.Count: integer; @@ -59545,7 +58743,7 @@ class function TSynDictionary.OnCanDeleteSynPersistentLock(const aKey, aValue; class function TSynDictionary.OnCanDeleteSynPersistentLocked(const aKey, aValue; aIndex: integer): boolean; begin - result := not TSynPersistentLocked(aValue).Safe.IsLocked; + result := not TSynPersistentLock(aValue).Safe.IsLocked; end; function TSynDictionary.SaveToBinary(NoCompression: boolean): RawByteString; @@ -59583,6 +58781,7 @@ constructor TSynQueue.Create(aTypeInfo: pointer); destructor TSynQueue.Destroy; begin + WaitPopFinalize; fValues.Clear; inherited Destroy; end; @@ -59706,6 +58905,80 @@ function TSynQueue.Pop(out aValue): boolean; end; end; +function TSynQueue.InternalDestroying(incPopCounter: integer): boolean; +begin + fSafe.Lock; + try + result := wpfDestroying in fWaitPopFlags; + inc(fWaitPopCounter, incPopCounter); + finally + fSafe.UnLock; + end; +end; + +function TSynQueue.InternalWaitDone(endtix: Int64; const idle: TThreadMethod): boolean; +begin + Sleep(1); + if Assigned(idle) then + idle; // e.g. Application.ProcessMessages + result := InternalDestroying(0) or (GetTickCount64>endtix); +end; + +function TSynQueue.WaitPop(aTimeoutMS: integer; const aWhenIdle: TThreadMethod; + out aValue): boolean; +var endtix: Int64; +begin + result := false; + if not InternalDestroying(+1) then + try + endtix := GetTickCount64+aTimeoutMS; + repeat + result := Pop(aValue); + until result or InternalWaitDone(endtix,aWhenIdle); + finally + InternalDestroying(-1); + end; +end; + +function TSynQueue.WaitPeekLocked(aTimeoutMS: integer; const aWhenIdle: TThreadMethod): pointer; +var endtix: Int64; +begin + result := nil; + if not InternalDestroying(+1) then + try + endtix := GetTickCount64+aTimeoutMS; + repeat + fSafe.Lock; + try + if fFirst>=0 then + result := fValues.ElemPtr(fFirst); + finally + if result=nil then + fSafe.UnLock; // caller should always Unlock once done + end; + until (result<>nil) or InternalWaitDone(endtix,aWhenIdle); + finally + InternalDestroying(-1); + end; +end; + +procedure TSynQueue.WaitPopFinalize; +var endtix: Int64; // never wait forever +begin + fSafe.Lock; + try + include(fWaitPopFlags,wpfDestroying); + if fWaitPopCounter = 0 then + exit; + finally + fSafe.UnLock; + end; + endtix := GetTickCount64 + 100; + repeat + Sleep(1); // ensure WaitPos() is actually finished + until (fWaitPopCounter=0) or (GetTickCount64>endtix); +end; + procedure TSynQueue.Save(out aDynArrayValues; aDynArray: PDynArray); var n: integer; DA: TDynArray; @@ -59759,11 +59032,11 @@ function TMemoryMap.Map(aFile: THandle; aCustomSize: PtrUInt; aCustomOffset: Int fBufSize := aCustomSize; end; {$ifdef MSWINDOWS} - with Int64Rec(fFileSize) do + with PInt64Rec(@fFileSize)^ do fMap := CreateFileMapping(fFile,nil,PAGE_READONLY,Hi,Lo,nil); if fMap=0 then raise ESynException.Create('TMemoryMap.Map: CreateFileMapping()=0'); - with Int64Rec(aCustomOffset) do + with PInt64Rec(@aCustomOffset)^ do fBuf := MapViewOfFile(fMap,FILE_MAP_READ,Hi,Lo,fBufSize); if fBuf=nil then begin // Windows failed to find a contiguous VA space -> fall back on direct read @@ -59771,10 +59044,10 @@ function TMemoryMap.Map(aFile: THandle; aCustomSize: PtrUInt; aCustomOffset: Int fMap := 0; {$else} if aCustomOffset<>0 then - if aCustomOffset and (_SC_PAGE_SIZE-1)<>0 then - raise ESynException.CreateUTF8('fpmmap(aCustomOffset=%) with pagesize=%', - [aCustomOffset,_SC_PAGE_SIZE]) else - aCustomOffset := aCustomOffset div _SC_PAGE_SIZE; + if aCustomOffset and (SystemInfo.dwPageSize-1)<>0 then + raise ESynException.CreateUTF8('fpmmap(aCustomOffset=%) with SystemInfo.dwPageSize=%', + [aCustomOffset,SystemInfo.dwPageSize]) else + aCustomOffset := aCustomOffset div SystemInfo.dwPageSize; fBuf := {$ifdef KYLIX3}mmap{$else}fpmmap{$endif}( nil,fBufSize,PROT_READ,MAP_SHARED,fFile,aCustomOffset); if fBuf=MAP_FAILED then begin @@ -60001,7 +59274,7 @@ procedure TFileBufferWriter.Write(Data: pointer; DataLen: integer); exit; end; end; - MoveFast(Data^,fBuffer^[fPos],DataLen); + {$ifdef FPC}Move{$else}MoveFast{$endif}(Data^,fBuffer^[fPos],DataLen); inc(fPos,DataLen); end; @@ -60017,7 +59290,7 @@ procedure TFileBufferWriter.WriteN(Data: Byte; Count: integer); fStream.WriteBuffer(fBuffer^,fPos); fPos := 0; end; - FillcharFast(fBuffer^[fPos],len,Data); + {$ifdef FPC}FillChar{$else}FillCharFast{$endif}(fBuffer^[fPos],len,Data); inc(fPos,len); dec(Count,len); end; @@ -60058,7 +59331,7 @@ procedure TFileBufferWriter.Write4(Data: integer); procedure TFileBufferWriter.Write4BigEndian(Data: integer); begin - Write4(bswap32(Data)); + Write4({$ifdef FPC}SwapEndian{$else}bswap32{$endif}(Data)); end; procedure TFileBufferWriter.Write8(const Data8Bytes); @@ -60243,7 +59516,7 @@ procedure TFileBufferWriter.WriteRawUTF8DynArray(const Values: TRawUTF8DynArray; break; // avoid buffer overflow end; P := ToVarUInt32(len,P); - MoveFast(pointer(PI^[i])^,P^,len); + {$ifdef FPC}Move{$else}MoveFast{$endif}(pointer(PI^[i])^,P^,len); inc(P,len); end else // fixed size strings case @@ -60252,7 +59525,7 @@ procedure TFileBufferWriter.WriteRawUTF8DynArray(const Values: TRawUTF8DynArray; n := i; break; // avoid buffer overflow end; - MoveFast(pointer(PI^[i])^,P^,fixedsize); + {$ifdef FPC}Move{$else}MoveFast{$endif}(pointer(PI^[i])^,P^,fixedsize); inc(P,fixedsize); end; len := PAnsiChar(P)-PBeg; // format: Isize+varUInt32s*strings @@ -60467,7 +59740,7 @@ procedure TFileBufferWriter.WriteVarUInt32Values(Values: PIntegerArray; n := (fBufLen-fPos)shr 2; if ValuesCountDataLen then len := DataLen; if Data<>nil then - MoveFast(fMap.fBuf[fCurrentPos],Data^,len); + {$ifdef FPC}Move{$else}MoveFast{$endif}(fMap.fBuf[fCurrentPos],Data^,len); inc(fCurrentPos,len); result := len; end else @@ -61172,476 +60446,6 @@ function TFileBufferReader.Seek(Offset: PtrInt): boolean; end; -{ TFastReader } - -procedure TFastReader.Init(Buffer: pointer; Len: integer); -begin - P := Buffer; - Last := P+Len; - OnErrorOverflow := nil; - OnErrorData := nil; - Tag := 0; -end; - -procedure TFastReader.Init(const Buffer: RawByteString); -begin - Init(pointer(Buffer),length(Buffer)); -end; - -procedure TFastReader.ErrorOverflow; -begin - if Assigned(OnErrorOverflow) then - OnErrorOverflow else - raise EFastReader.Create('Reached End of Input'); -end; - -procedure TFastReader.ErrorData(const fmt: RawUTF8; const args: array of const); -begin - if Assigned(OnErrorData) then - OnErrorData(fmt,args) else - raise EFastReader.CreateUTF8('Incorrect Data: '+fmt,args); -end; - -function TFastReader.EOF: boolean; -begin - result := P>=Last; -end; - -function TFastReader.RemainingLength: PtrUInt; -begin - result := PtrUInt(Last)-PtrUInt(P); -end; - -function TFastReader.NextByte: byte; -begin - if P>=Last then - ErrorOverflow; - result := ord(P^); - inc(P); -end; - -function TFastReader.Next4: cardinal; -begin - if P+3>=Last then - ErrorOverflow; - result := PCardinal(P)^; - inc(P,4); -end; - -function TFastReader.Next8: QWord; -begin - if P+7>=Last then - ErrorOverflow; - result := PQWord(P)^; - inc(P,8); -end; - -function TFastReader.NextByteEquals(Value: byte): boolean; -begin - if P>=Last then - ErrorOverflow; - if ord(P^) = Value then begin - inc(P); - result := true; - end else - result := false; -end; - -function TFastReader.Next(DataLen: PtrInt): pointer; -begin - if P+DataLen>Last then - ErrorOverflow; - result := P; - inc(P,DataLen); -end; - -function TFastReader.NextSafe(out Data: Pointer; DataLen: PtrInt): boolean; -begin - if P+DataLen>Last then - result := false else begin - Data := P; - inc(P,DataLen); - result := true; - end; -end; - -procedure TFastReader.Copy(out Dest; DataLen: PtrInt); -begin - if P+DataLen>Last then - ErrorOverflow; - MoveFast(P^,Dest,DataLen); - inc(P,DataLen); -end; - -function TFastReader.CopySafe(out Dest; DataLen: PtrInt): boolean; -begin - if P+DataLen>Last then - result := false else begin - MoveFast(P^,Dest,DataLen); - inc(P,DataLen); - result := true; - end; -end; - -procedure TFastReader.VarBlob(out result: TValueResult); -begin - result.Len := VarUInt32; - if P+result.Len>Last then - ErrorOverflow; - result.Ptr := P; - inc(P,result.Len); -end; - -function TFastReader.VarBlob: TValueResult; -begin - result.Len := VarUInt32; - if P+result.Len>Last then - ErrorOverflow; - result.Ptr := P; - inc(P,result.Len); -end; - -{$ifndef NOVARIANTS} -procedure TFastReader.NextVariant(var Value: variant; CustomVariantOptions: pointer); -begin - P := VariantLoad(Value, P, CustomVariantOptions); - if P=nil then - ErrorData('VariantLoad=nil',[]) else - if P>Last then - ErrorOverFlow; -end; - -procedure FastReaderDocVariant(var Value: TDocVariantData; - const JSON: TValueResult; CustomVariantOptions: PDocVariantOptions); -var - temp: TSynTempBuffer; -begin - temp.Init(JSON.Ptr,JSON.Len); // parsing will modify input buffer in-place - try - if CustomVariantOptions=nil then - CustomVariantOptions := @JSON_OPTIONS[true]; - Value.InitJSONInPlace(temp.buf,CustomVariantOptions^); - finally - temp.Done; - end; -end; - -procedure TFastReader.NextDocVariantData(out Value: variant; CustomVariantOptions: pointer); -var json: TValueResult; -begin - VarBlob(json); - if json.Len > 0 then - FastReaderDocVariant(TDocVariantData(Value), json, CustomVariantOptions); -end; -{$endif NOVARIANTS} - -function TFastReader.VarInt32: integer; -begin - result := VarUInt32; - if result and 1<>0 then - // 1->1, 3->2.. - result := result shr 1+1 else - // 0->0, 2->-1, 4->-2.. - result := -(result shr 1); -end; - -function TFastReader.VarInt64: Int64; -begin - result := VarUInt64; - if result and 1<>0 then - // 1->1, 3->2.. - result := result shr 1+1 else - // 0->0, 2->-1, 4->-2.. - result := -(result shr 1); -end; - -function TFastReader.VarUInt32: cardinal; -var c: cardinal; -{$ifdef CPUX86} // not enough CPU registers -label err; -begin - result := ord(P^); - if P>=Last then - goto err; - inc(P); - if result<=$7f then - exit; - if P>=Last then - goto err; - c := ord(P^) shl 7; - inc(P); - result := result and $7F or c; - if c<=$7f shl 7 then - exit; // Values between 128 and 16256 - if P>=Last then - goto err; - c := ord(P^) shl 14; - inc(P); - result := result and $3FFF or c; - if c<=$7f shl 14 then - exit; // Values between 16257 and 2080768 - if P>=Last then - goto err; - c := ord(P^) shl 21; - inc(P); - result := result and $1FFFFF or c; - if c<=$7f shl 21 then - exit; // Values between 2080769 and 266338304 - if P>=Last then -err:ErrorOverflow; - c := ord(P^) shl 28; - inc(P); - result := result and $FFFFFFF or c; -{$else} - s,l: PByte; -label err,fin; -begin - s := pointer(P); - l := pointer(Last); - result := s^; - if PAnsiChar(s)>=PAnsiChar(l) then - goto err; - inc(s); - if result<=$7f then - goto fin; - if PAnsiChar(s)>=PAnsiChar(l) then - goto err; - c := s^ shl 7; - inc(s); - result := result and $7F or c; - if c<=$7f shl 7 then - goto fin; // Values between 128 and 16256 - if PAnsiChar(s)>=PAnsiChar(l) then - goto err; - c := s^ shl 14; - inc(s); - result := result and $3FFF or c; - if c<=$7f shl 14 then - goto fin; // Values between 16257 and 2080768 - if PAnsiChar(s)>=PAnsiChar(l) then - goto err; - c := s^ shl 21; - inc(s); - result := result and $1FFFFF or c; - if c<=$7f shl 21 then - goto fin; // Values between 2080769 and 266338304 - if PAnsiChar(s)>=PAnsiChar(l) then -err:ErrorOverflow; - c := s^ shl 28; - inc(s); - result := result and $FFFFFFF or c; -fin: - P := pointer(s); -{$endif} -end; - -procedure TFastReader.VarNextInt; -{$ifdef CPUX86} // not enough CPU registers -begin - repeat - if P>=Last then - break; // reached end of input - if P^<=#$7f then - break; // reached end of VarUInt32/VarUInt64 - inc(P); - until false; - inc(P); -{$else} -var s: PAnsiChar; -begin - s := P; - repeat - if s>=Last then - break; // reached end of input - if s^<=#$7f then - break; // reached end of VarUInt32/VarUInt64 - inc(s); - until false; - P := s+1; -{$endif CPUX86} -end; - -procedure TFastReader.VarNextInt(count: integer); -{$ifdef CPUX86} // not enough CPU registers -begin - if count=0 then - exit; - repeat - if P>=Last then - break; // reached end of input - if P^>#$7f then begin - inc(P); - continue; // didn't reach end of VarUInt32/VarUInt64 - end; - inc(P); - dec(count); - if count=0 then - break; - until false; -{$else} -var s, max: PAnsiChar; -begin - if count=0 then - exit; - s := P; - max := Last; - repeat - if s>=max then - break; // reached end of input - if s^>#$7f then begin - inc(s); - continue; // didn't reach end of VarUInt32/VarUInt64 - end; - inc(s); - dec(count); - if count=0 then - break; - until false; - P := s; -{$endif CPUX86} -end; - -function TFastReader.PeekVarInt32(out value: PtrInt): boolean; -begin - result := PeekVarUInt32(PtrUInt(value)); - if result then - if value and 1<>0 then - // 1->1, 3->2.. - value := value shr 1+1 else - // 0->0, 2->-1, 4->-2.. - value := -(value shr 1); -end; - -function TFastReader.PeekVarUInt32(out value: PtrUInt): boolean; -var s: PAnsiChar; -begin - result := false; - s := P; - repeat - if s>=Last then - exit; // reached end of input -> returns false - if s^<=#$7f then - break; // reached end of VarUInt32 - inc(s); - until false; - s := P; - value := VarUInt32; // fast value decode - P := s; // rewind - result := true; -end; - -function TFastReader.VarUInt32Safe(out Value: cardinal): boolean; -var c, n, v: cardinal; -begin - result := false; - if P>=Last then - exit; - v := ord(P^); - inc(P); - if v>$7f then begin - n := 0; - v := v and $7F; - repeat - if P>=Last then - exit; - c := ord(P^); - inc(P); - inc(n,7); - if c<=$7f then break; - v := v or ((c and $7f) shl n); - until false; - v := v or (c shl n); - end; - Value := v; - result := true; // success -end; - -function TFastReader.VarUInt64: QWord; -label err; -var c, n: PtrUInt; -begin - if P>=Last then -err: ErrorOverflow; - c := ord(P^); - inc(P); - if c>$7f then begin - result := c and $7F; - n := 0; - repeat - if P>=Last then - goto err; - c := ord(P^); - inc(P); - inc(n,7); - if c<=$7f then break; - result := result or (QWord(c and $7f) shl n); - until false; - result := result or (QWord(c) shl n); - end else - result := c; -end; - -function TFastReader.VarString: RawByteString; -begin - with VarBlob do - SetString(result,Ptr,Len); -end; - -procedure TFastReader.VarUTF8(out result: RawUTF8); -begin - with VarBlob do - FastSetString(result,Ptr,Len); -end; - -function TFastReader.VarUTF8: RawUTF8; -begin - with VarBlob do - FastSetString(result,Ptr,Len); -end; - -function TFastReader.VarShortString: shortstring; -begin - with VarBlob do - SetString(result,Ptr,Len); -end; - -function TFastReader.VarUTF8Safe(out Value: RawUTF8): boolean; -var len: cardinal; -begin - if VarUInt32Safe(len) and (P+len<=Last) then begin - FastSetString(Value,P,len); - inc(P,len); - result := true; - end else - result := false; -end; - -procedure TFastReader.Read(var DA: TDynArray; NoCheckHash: boolean); -begin - P := DA.LoadFrom(P,nil,NoCheckHash); - if P=nil then - ErrorData('TDynArray.LoadFrom %',[DA.ArrayTypeName]); -end; - -function TFastReader.ReadVarUInt32Array(var Values: TIntegerDynArray): PtrInt; -var i: integer; - k: TFileBufferWriterKind; -begin - result := VarUInt32; - SetLength(Values,result); - Copy(k,1); - if k=wkUInt32 then begin - Copy(Values[0],result*4); - exit; - end; - Next(4); // format: Isize+varUInt32s - case k of - wkVarInt32: for i := 0 to result-1 do Values[i] := VarInt32; - wkVarUInt32: for i := 0 to result-1 do Values[i] := VarUInt32; - else ErrorData('ReadVarUInt32Array: unhandled kind=%', [ord(k)]); - end; -end; - function PropNameValid(P: PUTF8Char): boolean; begin @@ -61731,10 +60535,10 @@ function StrCompL(P1,P2: PUTF8Char; L, Default: Integer): PtrInt; function StrCompIL(P1,P2: PUTF8Char; L, Default: Integer): PtrInt; var i: PtrInt; - tab: {$ifdef CPUX86}TNormTable absolute NormToUpperAnsi7{$else}PNormTable{$endif}; + tab: {$ifdef CPUX86NOTPIC}TNormTable absolute NormToUpperAnsi7{$else}PNormTable{$endif}; begin i := 0; - {$ifndef CPUX86}tab := @NormToUpperAnsi7;{$endif} // faster on PIC and x86_64 + {$ifndef CPUX86NOTPIC}tab := @NormToUpperAnsi7;{$endif} // faster on PIC and x86_64 repeat if tab[P1[i]]=tab[P2[i]] then begin inc(i); @@ -62114,7 +60918,7 @@ function StreamUnSynLZ(Source: TStream; Magic: cardinal): TMemoryStream; if not stored then if SynLZdecompressdestlen(S)<>Head.UnCompressedSize then exit; - if Hash32(S,Head.CompressedSize)<>Head.HashCompressed then + if Hash32(pointer(S),Head.CompressedSize)<>Head.HashCompressed then exit; if result=nil then result := THeapMemoryStream.Create else begin @@ -62129,10 +60933,10 @@ function StreamUnSynLZ(Source: TStream; Magic: cardinal): TMemoryStream; D := PAnsiChar(result.Memory)+resultSize; inc(resultSize,Head.UnCompressedSize); if stored then - MoveFast(S^,D^,Head.CompressedSize) else + {$ifdef FPC}Move{$else}MoveFast{$endif}(S^,D^,Head.CompressedSize) else if SynLZDecompress1(S,Head.CompressedSize,D)<>Head.UnCompressedSize then FreeAndNil(result) else - if Hash32(D,Head.UnCompressedSize)<>Head.HashUncompressed then + if Hash32(pointer(D),Head.UnCompressedSize)<>Head.HashUncompressed then FreeAndNil(result); until (result=nil) or (sourcePosition>=sourceSize); end; @@ -62174,12 +60978,23 @@ class function TAlgoCompress.Algo(const Comp: TByteDynArray): TAlgoCompress; class function TAlgoCompress.Algo(Comp: PAnsiChar; CompLen: integer): TAlgoCompress; begin if (Comp<>nil) and (CompLen>9) then - if ord(Comp[4])<=1{inlinedCOMPRESS_SYNLZ} then // "stored" maps SynLZ - result := AlgoSynLZ else + if ord(Comp[4])<=1 then // inline-friendly Comp[4]<=COMPRESS_SYNLZ + result := AlgoSynLZ else // COMPRESS_STORED is also handled as SynLZ result := Algo(ord(Comp[4])) else result := nil; end; +class function TAlgoCompress.Algo(Comp: PAnsiChar; CompLen: integer; out IsStored: boolean): TAlgoCompress; +begin + if (Comp<>nil) and (CompLen>9) then begin + IsStored := Comp[4]=COMPRESS_STORED; + result := Algo(ord(Comp[4])); + end else begin + IsStored := false; + result := nil; + end; +end; + class function TAlgoCompress.Algo(AlgoID: byte): TAlgoCompress; var i: integer; ptr: ^TAlgoCompress; @@ -62200,12 +61015,17 @@ class function TAlgoCompress.Algo(AlgoID: byte): TAlgoCompress; end; end; +class function TAlgoCompress.UncompressedSize(const Comp: RawByteString): integer; +begin + result := Algo(Comp).DecompressHeader(pointer(Comp),length(Comp)); +end; + function TAlgoCompress.AlgoName: TShort16; var s: PShortString; i: integer; begin if self=nil then - result := 'stored' else begin + result := 'none' else begin s := ClassNameShort(self); if IdemPChar(@s^[1],'TALGO') then begin result[0] := AnsiChar(ord(s^[0])-5); @@ -62224,57 +61044,61 @@ function TAlgoCompress.AlgoHash(Previous: cardinal; Data: pointer; DataLen: inte result := crc32c(Previous,Data,DataLen); end; -function TAlgoCompress.Compress(const Plain: RawByteString; - CompressionSizeTrigger: integer; CheckMagicForCompressed: boolean): RawByteString; +function TAlgoCompress.Compress(const Plain: RawByteString; CompressionSizeTrigger: integer; + CheckMagicForCompressed: boolean; BufferOffset: integer): RawByteString; begin - result := Compress(pointer(Plain),Length(Plain),CompressionSizeTrigger,CheckMagicForCompressed); + result := Compress(pointer(Plain),Length(Plain),CompressionSizeTrigger, + CheckMagicForCompressed,BufferOffset); end; -function TAlgoCompress.Compress(Plain: PAnsiChar; PlainLen: integer; - CompressionSizeTrigger: integer; CheckMagicForCompressed: boolean): RawByteString; +function TAlgoCompress.Compress(Plain: PAnsiChar; PlainLen: integer; CompressionSizeTrigger: integer; + CheckMagicForCompressed: boolean; BufferOffset: integer): RawByteString; var len: integer; R: PAnsiChar; crc: cardinal; - tmp: array[0..16383] of AnsiChar; // will resize Result in-place + tmp: array[0..16383] of AnsiChar; // big enough to resize Result in-place begin - if (self=nil) or (PlainLen=0) or (Plain=nil) then + if (self=nil) or (PlainLen=0) or (Plain=nil) then begin + result := ''; exit; + end; crc := AlgoHash(0,Plain,PlainLen); if (PlainLenSizeOf(tmp) then begin SetString(result,nil,len); R := pointer(result); end else R := @tmp; + inc(R,BufferOffset); PCardinal(R)^ := crc; len := AlgoCompress(Plain,PlainLen,R+9); - if len>=PlainLen then begin // store if compression not worth it + if len+64>=PlainLen then begin // store if compression was not worth it R[4] := COMPRESS_STORED; PCardinal(R+5)^ := crc; - MoveFast(Plain^,R[9],PlainLen); + {$ifdef FPC}Move{$else}MoveFast{$endif}(Plain^,R[9],PlainLen); len := PlainLen; end else begin R[4] := AnsiChar(AlgoID); PCardinal(R+5)^ := AlgoHash(0,R+9,len); end; - if R=@tmp then - SetString(result,tmp,len+9) else - SetLength(result,len+9); // resize in-place may not move any data + if R=@tmp[BufferOffset] then + SetString(result,tmp,len+BufferOffset+9) else + SetLength(result,len+BufferOffset+9); // MM may not move the data end; end; -function TAlgoCompress.Compress(Plain, Comp: PAnsiChar; PlainLen, - CompLen: integer; CompressionSizeTrigger: integer; - CheckMagicForCompressed: boolean): integer; +function TAlgoCompress.Compress(Plain, Comp: PAnsiChar; PlainLen, CompLen: integer; + CompressionSizeTrigger: integer; CheckMagicForCompressed: boolean): integer; var len: integer; begin result := 0; @@ -62296,7 +61120,7 @@ function TAlgoCompress.Compress(Plain, Comp: PAnsiChar; PlainLen, end; Comp[4] := COMPRESS_STORED; PCardinal(Comp+5)^ := PCardinal(Comp)^; - MoveFast(Plain^,Comp[9],PlainLen); + {$ifdef FPC}Move{$else}MoveFast{$endif}(Plain^,Comp[9],PlainLen); result := PlainLen+9; end; @@ -62322,7 +61146,7 @@ function TAlgoCompress.CompressToBytes(Plain: PAnsiChar; PlainLen: integer; PCardinal(R)^ := crc; R[4] := COMPRESS_STORED; PCardinal(R+5)^ := crc; - MoveFast(Plain^,R[9],PlainLen); + {$ifdef FPC}Move{$else}MoveFast{$endif}(Plain^,R[9],PlainLen); end else begin SetLength(result,CompressDestLen(PlainLen)); R := pointer(result); @@ -62331,7 +61155,7 @@ function TAlgoCompress.CompressToBytes(Plain: PAnsiChar; PlainLen: integer; if len>=PlainLen then begin // store if compression not worth it R[4] := COMPRESS_STORED; PCardinal(R+5)^ := crc; - MoveFast(Plain^,R[9],PlainLen); + {$ifdef FPC}Move{$else}MoveFast{$endif}(Plain^,R[9],PlainLen); len := PlainLen; end else begin R[4] := AnsiChar(AlgoID); @@ -62354,21 +61178,23 @@ function TAlgoCompress.Decompress(const Comp: TByteDynArray): RawByteString; end; procedure TAlgoCompress.Decompress(Comp: PAnsiChar; CompLen: integer; - out Result: RawByteString; Load: TAlgoCompressLoad); + out Result: RawByteString; Load: TAlgoCompressLoad; BufferOffset: integer); var len: integer; + dec: PAnsiChar; begin len := DecompressHeader(Comp,CompLen,Load); if len=0 then exit; - SetString(result,nil,len); - if not DecompressBody(Comp,pointer(result),CompLen,len,Load) then + SetString(result,nil,len+BufferOffset); + dec := pointer(result); + if not DecompressBody(Comp,dec+BufferOffset,CompLen,len,Load) then result := ''; end; function TAlgoCompress.Decompress(const Comp: RawByteString; - Load: TAlgoCompressLoad): RawByteString; + Load: TAlgoCompressLoad; BufferOffset: integer): RawByteString; begin - Decompress(pointer(Comp),length(Comp),result,Load); + Decompress(pointer(Comp),length(Comp),result,Load,BufferOffset); end; function TAlgoCompress.TryDecompress(const Comp: RawByteString; @@ -62426,7 +61252,7 @@ function TAlgoCompress.DecompressPartial(Comp, Partial: PAnsiChar; if PartialLen>BodyLen then PartialLen := BodyLen; if Comp[4]=COMPRESS_STORED then - MoveFast(Comp[9],Partial[0],PartialLen) else + {$ifdef FPC}Move{$else}MoveFast{$endif}(Comp[9],Partial[0],PartialLen) else if AlgoDecompressPartial(Comp+9,CompLen-9,Partial,PartialLen,PartialLenMax)=pEnd then break; i := ord(p^); - if not (AnsiChar(i) in ANSICHARNOT01310) then break; + if i in [10,13] then break; if NormToUpperAnsi7Byte[i]=ord(up^) then goto Fnd; inc(p); if p>=pEnd then break; i := ord(p^); - if not (AnsiChar(i) in ANSICHARNOT01310) then break; + if i in [10,13] then break; if NormToUpperAnsi7Byte[i]=ord(up^) then goto Fnd; inc(p); if p>=pEnd then break; i := ord(p^); - if not (AnsiChar(i) in ANSICHARNOT01310) then break; + if i in [10,13] then break; if NormToUpperAnsi7Byte[i]=ord(up^) then goto Fnd; inc(p); if p>=pEnd then break; i := ord(p^); - if not (AnsiChar(i) in ANSICHARNOT01310) then break; + if i in [10,13] then break; if NormToUpperAnsi7Byte[i]<>ord(up^) then begin inc(p); continue; @@ -62803,7 +61629,7 @@ function TMemoryMapText.LineSize(aIndex: integer): integer; function GetLineSizeSmallerThan(P,PEnd: PUTF8Char; aMinimalCount: integer): boolean; begin if P<>nil then - while (PCount then Result := Count; - MoveFast(PByteArray(fDataString)[fPosition],Buffer,Result); + {$ifdef FPC}Move{$else}MoveFast{$endif}(PByteArray(fDataString)[fPosition],Buffer,Result); inc(fPosition, Result); end; end; @@ -62924,7 +61750,7 @@ function TRawByteStringStream.Write(const Buffer; Count: Integer): Longint; Result := 0 else begin Result := Count; SetLength(fDataString,fPosition+Result); - MoveFast(Buffer,PByteArray(fDataString)[fPosition],Result); + {$ifdef FPC}Move{$else}MoveFast{$endif}(Buffer,PByteArray(fDataString)[fPosition],Result); inc(FPosition,Result); end; end; @@ -62948,109 +61774,6 @@ function TFakeWriterStream.Seek(Offset: Longint; Origin: Word): Longint; end; - -{ TPendingTaskList } - -constructor TPendingTaskList.Create; -begin - fSafe.Init; - fTasks.InitSpecific(TypeInfo(TPendingTaskListItemDynArray),fTask,djInt64,@fCount); -end; - -destructor TPendingTaskList.Destroy; -begin - fSafe.Done; - inherited Destroy; -end; - -function TPendingTaskList.GetTimestamp: Int64; -begin - result := GetTickCount64; -end; - -procedure TPendingTaskList.AddTask(aMilliSecondsDelayFromNow: integer; - const aTask: RawByteString); -var item: TPendingTaskListItem; - ndx: integer; -begin - item.Timestamp := GetTimestamp+aMilliSecondsDelayFromNow; - item.Task := aTask; - fSafe.Lock; - try - if fTasks.FastLocateSorted(item,ndx) then - inc(ndx); // always insert just after any existing timestamp - fTasks.FastAddSorted(ndx,item); - finally - fSafe.UnLock; - end; -end; - -procedure TPendingTaskList.AddTasks( - const aMilliSecondsDelays: array of integer; - const aTasks: array of RawByteString); -var item: TPendingTaskListItem; - i,ndx: integer; -begin - if length(aTasks)<>length(aMilliSecondsDelays) then - exit; - item.Timestamp := GetTimestamp; - fSafe.Lock; - try - for i := 0 to High(aTasks) do begin - inc(item.Timestamp,aMilliSecondsDelays[i]); - item.Task := aTasks[i]; - if fTasks.FastLocateSorted(item,ndx) then - inc(ndx); // always insert just after any existing timestamp - fTasks.FastAddSorted(ndx,item); - end; - finally - fSafe.UnLock; - end; -end; - -function TPendingTaskList.GetCount: integer; -begin - if self=nil then - result := 0 else begin - fSafe.Lock; - try - result := fCount; - finally - fSafe.UnLock; - end; - end; -end; - -function TPendingTaskList.NextPendingTask: RawByteString; -begin - result := ''; - if (self=nil) or (fCount=0) then - exit; - fSafe.Lock; - try - if fCount>0 then - if GetTimestamp>=fTask[0].Timestamp then begin - result := fTask[0].Task; - fTasks.FastDeleteSorted(0); - end; - finally - fSafe.UnLock; - end; -end; - -procedure TPendingTaskList.Clear; -begin - if (self=nil) or (fCount=0) then - exit; - fSafe.Lock; - try - fTasks.Clear; - finally - fSafe.UnLock; - end; -end; - - { TSynNameValue } procedure TSynNameValue.Add(const aName, aValue: RawUTF8; aTag: PtrInt); @@ -63077,7 +61800,7 @@ procedure TSynNameValue.InitFromIniSection(Section: PUTF8Char; fOnAdd := OnAdd; while (Section<>nil) and (Section^<>'[') do begin s := GetNextLine(Section,Section); - i := PosEx('=',s); + i := PosExChar('=',s); if (i>1) and not(s[1] in [';','[']) then if Assigned(OnTheFlyConvert) then Add(copy(s,1,i-1),OnTheFlyConvert(copy(s,i+1,1000))) else @@ -63149,7 +61872,7 @@ procedure TSynNameValue.Init(aCaseSensitive: boolean); List := nil; fDynArray.HashInvalidate; // initialize hashed storage - FillcharFast(self,SizeOf(self),0); + {$ifdef FPC}FillChar{$else}FillCharFast{$endif}(self,SizeOf(self),0); fDynArray.InitSpecific(TypeInfo(TSynNameValueItemDynArray),List, djRawUTF8,@Count,not aCaseSensitive); end; @@ -63411,8 +62134,6 @@ function TSynNameValue.MergeDocVariant(var DocVariant: variant; {$endif NOVARIANTS} -{ TSynBackgroundThreadAbstract } - {$ifdef MSWINDOWS} function IsDebuggerPresent: BOOL; stdcall; external kernel32; // since XP {$endif} @@ -63474,1357 +62195,6 @@ procedure SetThreadNameDefault(ThreadID: TThreadID; const Name: RawUTF8); {$endif FPC} end; -{$ifdef KYLIX3} -type - // see http://stackoverflow.com/a/3085509/458259 about this known Kylix bug - TEventHack = class(THandleObject) // should match EXACTLY SyncObjs.pas source! - private - FEvent: TSemaphore; - FManualReset: Boolean; - end; - -function FixedWaitFor(Event: TEvent; Timeout: LongWord): TWaitResult; -var E: TEventHack absolute Event; - procedure SetResult(res: integer); - begin - if res=0 then - result := wrSignaled else - if errno in [EAGAIN,ETIMEDOUT] then - result := wrTimeOut else begin - write(TimeOut,':',errno,' '); - result := wrError; - end; - end; -{.$define USESEMTRYWAIT} -// sem_timedwait() is slower than sem_trywait(), but consuming much less CPU -{$ifdef USESEMTRYWAIT} -var time: timespec; -{$else} -var start,current: Int64; - elapsed: LongWord; -{$endif} -begin - if Timeout=INFINITE then begin - SetResult(sem_wait(E.FEvent)); - exit; - end; - if TimeOut=0 then begin - SetResult(sem_trywait(E.FEvent)); - exit; - end; - {$ifdef USESEMTRYWAIT} - clock_gettime(CLOCK_REALTIME,time); - inc(time.tv_sec,TimeOut div 1000); - inc(time.tv_nsec,(TimeOut mod 1000)*1000000); - while time.tv_nsec>1000000000 do begin - inc(time.tv_sec); - dec(time.tv_nsec,1000000000); - end; - SetResult(sem_timedwait(E.FEvent,time)); - {$else} - start := GetTickCount64; - repeat - if sem_trywait(E.FEvent)=0 then begin - result := wrSignaled; - break; - end; - current := GetTickCount64; - elapsed := current-start; - if elapsed=0 then - sched_yield else - if elapsed>TimeOut then begin - result := wrTimeOut; - break; - end else - if elapsed<5 then - usleep(50) else - usleep(1000); - until false; - {$endif} - if E.FManualReset then begin - repeat until sem_trywait(E.FEvent)<>0; // reset semaphore state - sem_post(E.FEvent); - end; -end; - -{$else KYLIX3} // original FPC or Windows implementation is OK - -function FixedWaitFor(Event: TEvent; Timeout: LongWord): TWaitResult; -begin - result := Event.WaitFor(TimeOut); -end; - -{$endif KYLIX3} - -procedure FixedWaitForever(Event: TEvent); -begin - FixedWaitFor(Event,INFINITE); -end; - -constructor TSynBackgroundThreadAbstract.Create(const aThreadName: RawUTF8; - OnBeforeExecute,OnAfterExecute: TNotifyThreadEvent; CreateSuspended: boolean); -begin - fProcessEvent := TEvent.Create(nil,false,false,''); - fThreadName := aThreadName; - fOnBeforeExecute := OnBeforeExecute; - fOnAfterExecute := OnAfterExecute; - inherited Create(CreateSuspended{$ifdef FPC},512*1024{$endif}); // DefaultStackSize=512KB -end; - -{$ifndef HASTTHREADSTART} -procedure TSynBackgroundThreadAbstract.Start; -begin - Resume; -end; -{$endif} - -{$ifndef HASTTHREADTERMINATESET} -procedure TSynBackgroundThreadAbstract.Terminate; -begin - inherited Terminate; // FTerminated := True - TerminatedSet; -end; -{$endif} - -procedure TSynBackgroundThreadAbstract.TerminatedSet; -begin - fProcessEvent.SetEvent; // ExecuteLoop should handle Terminated flag -end; - -procedure TSynBackgroundThreadAbstract.WaitForNotExecuting(maxMS: integer); -var endtix: Int64; -begin - if fExecute = exRun then begin - endtix := GetTickCount64+maxMS; - repeat - Sleep(1); // wait for Execute to finish - until (fExecute <> exRun) or (GetTickCount64>=endtix); - end; -end; - -destructor TSynBackgroundThreadAbstract.Destroy; -begin - if fExecute = exRun then begin - Terminate; - WaitForNotExecuting(100); - end; - inherited Destroy; - FreeAndNil(fProcessEvent); -end; - -procedure TSynBackgroundThreadAbstract.SetExecuteLoopPause(dopause: boolean); -begin - if Terminated or (dopause=fExecuteLoopPause) or (fExecute=exFinished) then - exit; - fExecuteLoopPause := dopause; - fProcessEvent.SetEvent; // notify Execute main loop -end; - -procedure TSynBackgroundThreadAbstract.Execute; -begin - try - if fThreadName='' then - SetCurrentThreadName('%(%)',[self,pointer(self)]) else - SetCurrentThreadName('%',[fThreadName]); - if Assigned(fOnBeforeExecute) then - fOnBeforeExecute(self); - try - fExecute := exRun; - while not Terminated do - if fExecuteLoopPause then - FixedWaitFor(fProcessEvent,100) else - ExecuteLoop; - finally - if Assigned(fOnAfterExecute) then - fOnAfterExecute(self); - end; - finally - fExecute := exFinished; - end; -end; - -{ TSynBackgroundThreadMethodAbstract } - -constructor TSynBackgroundThreadMethodAbstract.Create(aOnIdle: TOnIdleSynBackgroundThread; - const aThreadName: RawUTF8; OnBeforeExecute,OnAfterExecute: TNotifyThreadEvent); -begin - fOnIdle := aOnIdle; // cross-platform may run Execute as soon as Create is called - fCallerEvent := TEvent.Create(nil,false,false,''); - fPendingProcessLock.Init; - inherited Create(aThreadName,OnBeforeExecute,OnAfterExecute); -end; - -destructor TSynBackgroundThreadMethodAbstract.Destroy; -begin - SetPendingProcess(flagDestroying); - fProcessEvent.SetEvent; // notify terminated - FixedWaitForever(fCallerEvent); // wait for actual termination - FreeAndNil(fCallerEvent); - inherited Destroy; - fPendingProcessLock.Done; -end; - -function TSynBackgroundThreadMethodAbstract.GetPendingProcess: TSynBackgroundThreadProcessStep; -begin - fPendingProcessLock.Lock; - result := fPendingProcessFlag; - fPendingProcessLock.UnLock; -end; - -procedure TSynBackgroundThreadMethodAbstract.SetPendingProcess(State: TSynBackgroundThreadProcessStep); -begin - fPendingProcessLock.Lock; - fPendingProcessFlag := State; - fPendingProcessLock.UnLock; -end; - -procedure TSynBackgroundThreadMethodAbstract.ExecuteLoop; -{$ifndef DELPHI5OROLDER} -var E: TObject; -{$endif} -begin - case FixedWaitFor(fProcessEvent,INFINITE) of - wrSignaled: - case GetPendingProcess of - flagDestroying: begin - fCallerEvent.SetEvent; // abort caller thread process - Terminate; // forces Execute loop ending - exit; - end; - flagStarted: - if not Terminated then - if fExecuteLoopPause then // pause -> try again later - fProcessEvent.SetEvent else - try - fBackgroundException := nil; - try - if Assigned(fOnBeforeProcess) then - fOnBeforeProcess(self); - try - Process; - finally - if Assigned(fOnAfterProcess) then - fOnAfterProcess(self); - end; - except - {$ifdef DELPHI5OROLDER} - on E: Exception do - fBackgroundException := ESynException.CreateUTF8( - 'Redirected %: "%"',[E,E.Message]); - {$else} - E := AcquireExceptionObject; - if E.InheritsFrom(Exception) then - fBackgroundException := Exception(E); - {$endif} - end; - finally - SetPendingProcess(flagFinished); - fCallerEvent.SetEvent; - end; - end; - end; -end; - -function TSynBackgroundThreadMethodAbstract.AcquireThread: TSynBackgroundThreadProcessStep; -begin - fPendingProcessLock.Lock; - try - result := fPendingProcessFlag; - if result=flagIdle then begin // we just acquired the thread! congrats! - fPendingProcessFlag := flagStarted; // atomic set "started" flag - fCallerThreadID := ThreadID; - end; - finally - fPendingProcessLock.UnLock; - end; -end; - -function TSynBackgroundThreadMethodAbstract.OnIdleProcessNotify(start: Int64): integer; -begin - result := GetTickCount64-start; - if result<0 then - result := MaxInt; // should happen only under XP -> ignore - if Assigned(fOnIdle) then - fOnIdle(self,result) ; -end; - -procedure TSynBackgroundThreadMethodAbstract.WaitForFinished(start: Int64); -var E: Exception; -begin - if (self=nil) or not (fPendingProcessFlag in [flagStarted, flagFinished]) then - exit; // nothing to wait for - try - {$ifdef MSWINDOWS} // do process the OnIdle only if UI - if Assigned(fOnIdle) then begin - while FixedWaitFor(fCallerEvent,100)=wrTimeout do - OnIdleProcessNotify(start); - end else - {$endif} - FixedWaitForever(fCallerEvent); - if fPendingProcessFlag<>flagFinished then - ESynException.CreateUTF8('%.WaitForFinished: flagFinished?',[self]); - if fBackgroundException<>nil then begin - E := fBackgroundException; - fBackgroundException := nil; - raise E; // raise background exception in the calling scope - end; - finally - fParam := nil; - fCallerThreadID := 0; - FreeAndNil(fBackgroundException); - SetPendingProcess(flagIdle); - if Assigned(fOnIdle) then - fOnIdle(self,-1); // notify finished - end; -end; - -function TSynBackgroundThreadMethodAbstract.RunAndWait(OpaqueParam: pointer): boolean; -var start: Int64; - ThreadID: TThreadID; -begin - result := false; - ThreadID := GetCurrentThreadId; - if (self=nil) or (ThreadID=fCallerThreadID) then - // avoid endless loop when waiting in same thread (e.g. UI + OnIdle) - exit; - // 1. wait for any previous request to be finished (should not happen often) - if Assigned(fOnIdle) then - fOnIdle(self,0); // notify started - start := GetTickCount64; - repeat - case AcquireThread of - flagDestroying: - exit; - flagIdle: - break; // we acquired the background thread - end; - case OnIdleProcessNotify(start) of // Windows.GetTickCount64 res is 10-16 ms - 0..20: SleepHiRes(0); - 21..100: SleepHiRes(1); - 101..900: SleepHiRes(5); - else SleepHiRes(50); - end; - until false; - // 2. process execution in the background thread - fParam := OpaqueParam; - fProcessEvent.SetEvent; // notify background thread for Call pending process - WaitForFinished(start); // wait for flagFinished, then set flagIdle - result := true; -end; - -function TSynBackgroundThreadMethodAbstract.GetOnIdleBackgroundThreadActive: boolean; -begin - result := (self<>nil) and Assigned(fOnIdle) and (GetPendingProcess<>flagIdle); -end; - - -{ TSynBackgroundThreadEvent } - -constructor TSynBackgroundThreadEvent.Create(aOnProcess: TOnProcessSynBackgroundThread; - aOnIdle: TOnIdleSynBackgroundThread; const aThreadName: RawUTF8); -begin - inherited Create(aOnIdle,aThreadName); - fOnProcess := aOnProcess; -end; - -procedure TSynBackgroundThreadEvent.Process; -begin - if not Assigned(fOnProcess) then - raise ESynException.CreateUTF8('Invalid %.RunAndWait() call',[self]); - fOnProcess(self,fParam); -end; - - -{ TSynBackgroundThreadMethod } - -procedure TSynBackgroundThreadMethod.Process; -var Method: ^TThreadMethod; -begin - if fParam=nil then - raise ESynException.CreateUTF8('Invalid %.RunAndWait() call',[self]); - Method := fParam; - Method^(); -end; - -procedure TSynBackgroundThreadMethod.RunAndWait(Method: TThreadMethod); -var Met: TMethod absolute Method; -begin - inherited RunAndWait(@Met); -end; - - -{ TSynBackgroundThreadProcedure } - -constructor TSynBackgroundThreadProcedure.Create(aOnProcess: TOnProcessSynBackgroundThreadProc; - aOnIdle: TOnIdleSynBackgroundThread; const aThreadName: RawUTF8); -begin - inherited Create(aOnIdle,aThreadName); - fOnProcess := aOnProcess; -end; - -procedure TSynBackgroundThreadProcedure.Process; -begin - if not Assigned(fOnProcess) then - raise ESynException.CreateUTF8('Invalid %.RunAndWait() call',[self]); - fOnProcess(fParam); -end; - - -{ TSynParallelProcessThread } - -procedure TSynParallelProcessThread.Process; -begin - if not Assigned(fMethod) then - exit; - fMethod(fIndexStart,fIndexStop); - fMethod := nil; -end; - -procedure TSynParallelProcessThread.Start( - Method: TSynParallelProcessMethod; IndexStart, IndexStop: integer); -begin - fMethod := Method; - fIndexStart := IndexStart; - fIndexStop := IndexStop; - fProcessEvent.SetEvent; // notify execution -end; - - -{ TSynBackgroundThreadProcess } - -constructor TSynBackgroundThreadProcess.Create(const aThreadName: RawUTF8; - aOnProcess: TOnSynBackgroundThreadProcess; aOnProcessMS: cardinal; - aOnBeforeExecute, aOnAfterExecute: TNotifyThreadEvent; - aStats: TSynMonitorClass; CreateSuspended: boolean); -begin - if not Assigned(aOnProcess) then - raise ESynException.CreateUTF8('%.Create(aOnProcess=nil)',[self]); - if aStats<>nil then - fStats := aStats.Create(aThreadName); - fOnProcess := aOnProcess; - fOnProcessMS := aOnProcessMS; - if fOnProcessMS=0 then - fOnProcessMS := INFINITE; // wait until ProcessEvent.SetEvent or Terminated - inherited Create(aThreadName,aOnBeforeExecute,aOnAfterExecute,CreateSuspended); -end; - -destructor TSynBackgroundThreadProcess.Destroy; -begin - if fExecute=exRun then begin - Terminate; - WaitForNotExecuting(10000); // expect the background task to be finished - end; - inherited Destroy; - fStats.Free; -end; - -procedure TSynBackgroundThreadProcess.ExecuteLoop; -var wait: TWaitResult; -begin - wait := FixedWaitFor(fProcessEvent,fOnProcessMS); - if not Terminated and (wait in [wrSignaled,wrTimeout]) then - if fExecuteLoopPause then // pause -> try again later - fProcessEvent.SetEvent else - try - if fStats<>nil then - fStats.ProcessStartTask; - try - fOnProcess(self,wait); - finally - if fStats<>nil then - fStats.ProcessEnd; - end; - except - on E: Exception do begin - if fStats<>nil then - fStats.ProcessErrorRaised(E); - if Assigned(fOnException) then - fOnException(E); - end; - end; -end; - - -{ TSynBackgroundTimer } - -var - ProcessSystemUse: TSystemUse; - -constructor TSynBackgroundTimer.Create(const aThreadName: RawUTF8; - aOnBeforeExecute, aOnAfterExecute: TNotifyThreadEvent; aStats: TSynMonitorClass); -begin - fTasks.Init(TypeInfo(TSynBackgroundTimerTaskDynArray),fTask); - fTaskLock.Init; - {$ifndef NOVARIANTS} - fTaskLock.LockedBool[0] := false; - {$endif} - inherited Create(aThreadName,EverySecond,1000,aOnBeforeExecute,aOnAfterExecute,aStats); -end; - -destructor TSynBackgroundTimer.Destroy; -begin - if (ProcessSystemUse<>nil) and (ProcessSystemUse.fTimer=self) then - ProcessSystemUse.fTimer := nil; // allows processing by another background timer - inherited Destroy; - fTaskLock.Done; -end; - -const - TIXPRECISION = 32; // GetTickCount64 resolution (for aOnProcessSecs=1) - -procedure TSynBackgroundTimer.EverySecond( - Sender: TSynBackgroundThreadProcess; Event: TWaitResult); -var tix: Int64; - i,f,n: integer; - t: ^TSynBackgroundTimerTask; - todo: TSynBackgroundTimerTaskDynArray; // avoid lock contention -begin - if (fTask=nil) or Terminated then - exit; - tix := GetTickCount64; - n := 0; - fTaskLock.Lock; - try - variant(fTaskLock.Padding[0]) := true; - try - for i := 0 to length(fTask)-1 do begin - t := @fTask[i]; - if tix>=t^.NextTix then begin - SetLength(todo,n+1); - todo[n] := t^; - inc(n); - t^.FIFO := nil; // now owned by todo[n].FIFO - t^.NextTix := tix+((t^.Secs*1000)-TIXPRECISION); - end; - end; - finally - fTaskLock.UnLock; - end; - for i := 0 to n-1 do - with todo[i] do - if FIFO<>nil then - for f := 0 to length(FIFO)-1 do - try - OnProcess(self,Event,FIFO[f]); - except - end - else - try - OnProcess(self,Event,''); - except - end; - finally - {$ifdef NOVARIANTS} - fTaskLock.Lock; - variant(fTaskLock.Padding[0]) := false; - fTaskLock.UnLock; - {$else} - fTaskLock.LockedBool[0] := false; - {$endif} - end; -end; - -function TSynBackgroundTimer.Find(const aProcess: TMethod): integer; -begin // caller should have made fTaskLock.Lock; - for result := length(fTask)-1 downto 0 do - with TMethod(fTask[result].OnProcess) do - if (Code=aProcess.Code) and (Data=aProcess.Data) then - exit; - result := -1; -end; - -procedure TSynBackgroundTimer.Enable( - aOnProcess: TOnSynBackgroundTimerProcess; aOnProcessSecs: cardinal); -var task: TSynBackgroundTimerTask; - found: integer; -begin - if (self=nil) or Terminated or not Assigned(aOnProcess) then - exit; - if aOnProcessSecs=0 then begin - Disable(aOnProcess); - exit; - end; - task.OnProcess := aOnProcess; - task.Secs := aOnProcessSecs; - task.NextTix := GetTickCount64+(aOnProcessSecs*1000-TIXPRECISION); - fTaskLock.Lock; - try - found := Find(TMethod(aOnProcess)); - if found>=0 then - fTask[found] := task else - fTasks.Add(task); - finally - fTaskLock.UnLock; - end; -end; - -function TSynBackgroundTimer.Processing: boolean; -begin - {$ifdef NOVARIANTS} - with fTaskLock.Padding[0] do - result := (VType=varBoolean) and VBoolean; - {$else} - result := fTaskLock.LockedBool[0]; - {$endif} -end; - -procedure TSynBackgroundTimer.WaitUntilNotProcessing(timeoutsecs: integer); -var timeout: Int64; -begin - timeout := GetTickCount64+timeoutsecs*1000; - while Processing and (GetTickcount64=0 then begin - with fTask[found] do begin - if aExecuteNow then - NextTix := 0; - if aMsg<>#0 then - AddRawUTF8(FIFO,aMsg); - end; - if aExecuteNow then - ProcessEvent.SetEvent; - result := true; - end; - finally - fTaskLock.UnLock; - end; -end; - -function TSynBackgroundTimer.DeQueue(aOnProcess: TOnSynBackgroundTimerProcess; - const aMsg: RawUTF8): boolean; -var found: integer; -begin - result := false; - if (self=nil) or Terminated or not Assigned(aOnProcess) then - exit; - fTaskLock.Lock; - try - found := Find(TMethod(aOnProcess)); - if found>=0 then - with fTask[found] do - result := DeleteRawUTF8(FIFO,FindRawUTF8(FIFO,aMsg)); - finally - fTaskLock.UnLock; - end; -end; - -function TSynBackgroundTimer.Disable(aOnProcess: TOnSynBackgroundTimerProcess): boolean; -var found: integer; -begin - result := false; - if (self=nil) or Terminated or not Assigned(aOnProcess) then - exit; - fTaskLock.Lock; - try - found := Find(TMethod(aOnProcess)); - if found>=0 then begin - fTasks.Delete(found); - result := true; - end; - finally - fTaskLock.UnLock; - end; -end; - - -{ TProcessInfo } - -{$ifdef MSWINDOWS} -function TProcessInfo.Init: boolean; -begin - FillCharFast(self,SizeOf(self),0); - result := Assigned(GetSystemTimes) and Assigned(GetProcessTimes) and - Assigned(GetProcessMemoryInfo); // no monitoring API under oldest Windows -end; - -function TProcessInfo.Start: boolean; -var ftidl,ftkrn,ftusr: TFileTime; - sidl,skrn,susr: Int64; -begin - result := Assigned(GetSystemTimes) and GetSystemTimes(ftidl,ftkrn,ftusr); - if not result then - exit; - FileTimeToInt64(ftidl,sidl); - FileTimeToInt64(ftkrn,skrn); - FileTimeToInt64(ftusr,susr); - fDiffIdle := sidl-fSysPrevIdle; - fDiffKernel := skrn-fSysPrevKernel; - fDiffUser := susr-fSysPrevUser; - fDiffTotal := fDiffKernel+fDiffUser; // kernel time also includes idle time - dec(fDiffKernel, fDiffIdle); - fSysPrevIdle := sidl; - fSysPrevKernel := skrn; - fSysPrevUser := susr; -end; - -function TProcessInfo.PerProcess(PID: cardinal; Now: PDateTime; - out Data: TSystemUseData; var PrevKernel, PrevUser: Int64): boolean; -var - h: THandle; - ftkrn,ftusr,ftp,fte: TFileTime; - pkrn,pusr: Int64; - mem: TProcessMemoryCounters; -begin - result := false; - FillCharFast(Data,SizeOf(Data),0); - h := OpenProcess(OpenProcessAccess,false,PID); - if h<>0 then - try - if GetProcessTimes(h,ftp,fte,ftkrn,ftusr) then begin - if Now<>nil then - Data.Timestamp := Now^; - FileTimeToInt64(ftkrn,pkrn); - FileTimeToInt64(ftusr,pusr); - if (PrevKernel<>0) and (fDiffTotal>0) then begin - Data.Kernel := ((pkrn-PrevKernel)*100)/fDiffTotal; - Data.User := ((pusr-PrevUser)*100)/fDiffTotal; - end; - PrevKernel := pkrn; - PrevUser := pusr; - FillCharFast(mem,SizeOf(mem),0); - mem.cb := SizeOf(mem); - if GetProcessMemoryInfo(h,mem,SizeOf(mem)) then begin - Data.WorkKB := mem.WorkingSetSize shr 10; - Data.VirtualKB := mem.PagefileUsage shr 10; - end; - result := true; - end; - finally - CloseHandle(h); - end; -end; - -function TProcessInfo.PerSystem(out Idle,Kernel,User: currency): boolean; -begin - if fDiffTotal<=0 then begin - Idle := 0; - Kernel := 0; - User := 0; - result := false; - end else begin - Kernel := SimpleRoundTo2Digits((fDiffKernel*100)/fDiffTotal); - User := SimpleRoundTo2Digits((fDiffUser*100)/fDiffTotal); - Idle := 100-Kernel-User; // ensure sum is always 100% - result := true; - end; -end; -{$else} // not implemented yet (use /proc ?) -function TProcessInfo.Init: boolean; -begin - FillZero(self,SizeOf(self)); - result := false; -end; - -function TProcessInfo.Start: boolean; -begin - result := false; -end; - -function TProcessInfo.PerProcess(PID: cardinal; Now: PDateTime; - out Data: TSystemUseData; var PrevKernel, PrevUser: Int64): boolean; -begin - result := false; -end; - -function TProcessInfo.PerSystem(out Idle,Kernel,User: currency): boolean; -var P: PUTF8Char; - U, K, I, S: cardinal; -begin // see http://www.linuxhowtos.org/System/procstat.htm - result := false; - P := pointer(StringFromFile('/proc/stat', {nosize=}true)); - if P=nil then - exit; - U := GetNextItemCardinal(P,' '){=user}+GetNextItemCardinal(P,' '){=nice}; - K := GetNextItemCardinal(P,' '){=system}; - I := GetNextItemCardinal(P,' '){=idle}; - S := U+K+I; - Kernel := SimpleRoundTo2Digits((K*100)/S); - User := SimpleRoundTo2Digits((U*100)/S); - Idle := 100-Kernel-User; // ensure sum is always 100% - result := S<>0; -end; { TODO : use a diff approach for TProcessInfo.PerSystem on Linux } -{$endif MSWINDOWS} - - -{ TSystemUse } - -procedure TSystemUse.BackgroundExecute(Sender: TSynBackgroundTimer; - Event: TWaitResult; const Msg: RawUTF8); -var i: integer; - now: TDateTime; -begin - if (fProcess=nil) or (fHistoryDepth=0) or not fProcessInfo.Start then - exit; - fTimer := Sender; - now := NowUTC; - fSafe.Enter; - try - inc(fDataIndex); - if fDataIndex>=fHistoryDepth then - fDataIndex := 0; - for i := high(fProcess) downto 0 do // backwards for fProcesses.Delete(i) - with fProcess[i] do - if fProcessInfo.PerProcess(ID,@now,Data[fDataIndex],PrevKernel,PrevUser) then begin - if Assigned(fOnMeasured) then - fOnMeasured(ID,Data[fDataIndex]); - end else - if UnsubscribeProcessOnAccessError then - // if GetLastError=ERROR_INVALID_PARAMETER then - fProcesses.Delete(i); - finally - fSafe.Leave; - end; -end; - -procedure TSystemUse.OnTimerExecute(Sender: TObject); -begin - BackgroundExecute(nil,wrSignaled,''); -end; - -constructor TSystemUse.Create(const aProcessID: array of integer; - aHistoryDepth: integer); -var i: integer; -begin - inherited Create; - fSafe := TAutoLocker.Create; - fProcesses.Init(TypeInfo(TSystemUseProcessDynArray),fProcess); - {$ifdef MSWINDOWS} - if not Assigned(GetSystemTimes) or not Assigned(GetProcessTimes) or - not Assigned(GetProcessMemoryInfo) then - exit; // no system monitoring API under oldest Windows - {$else} - exit; // not implemented yet - {$endif} - if aHistoryDepth<=0 then - aHistoryDepth := 1; - fHistoryDepth := aHistoryDepth; - SetLength(fProcess,length(aProcessID)); - for i := 0 to high(aProcessID) do begin - {$ifdef MSWINDOWS} - if aProcessID[i]=0 then - fProcess[i].ID := GetCurrentProcessID else - {$endif} - fProcess[i].ID := aProcessID[i]; - SetLength(fProcess[i].Data,fHistoryDepth); - end; -end; - -constructor TSystemUse.Create(aHistoryDepth: integer); -begin - Create([0],aHistoryDepth); -end; - -destructor TSystemUse.Destroy; -begin - inherited Destroy; - fSafe.Free; -end; - -procedure TSystemUse.Subscribe(aProcessID: integer); -var i,n: integer; -begin - if self=nil then - exit; - {$ifdef MSWINDOWS} - if aProcessID=0 then - aProcessID := GetCurrentProcessID; - {$endif} - fSafe.Enter; - try - n := length(fProcess); - for i := 0 to n-1 do - if fProcess[i].ID=aProcessID then - exit; // already subscribed - SetLength(fProcess,n+1); - fProcess[n].ID := aProcessID; - SetLength(fProcess[n].Data,fHistoryDepth); - finally - fSafe.Leave; - end; -end; - -function TSystemUse.Unsubscribe(aProcessID: integer): boolean; -var i: integer; -begin - result := false; - if self=nil then - exit; - fSafe.Enter; - try - i := ProcessIndex(aProcessID); - if i>=0 then begin - fProcesses.Delete(i); - result := true; - end; - finally - fSafe.Leave; - end; -end; - -function TSystemUse.ProcessIndex(aProcessID: integer): integer; -begin // caller should have made fSafe.Enter - {$ifdef MSWINDOWS} - if aProcessID=0 then - aProcessID := GetCurrentProcessID; - {$endif} - if self<>nil then - for result := 0 to high(fProcess) do - if fProcess[result].ID=aProcessID then - exit; - result := -1; -end; - -function TSystemUse.Data(out aData: TSystemUseData; aProcessID: integer=0): boolean; -var i: integer; -begin - result := false; - if self<>nil then begin - fSafe.Enter; - try - i := ProcessIndex(aProcessID); - if i>=0 then begin - with fProcess[i] do - aData := Data[fDataIndex]; - result := aData.Timestamp<>0; - if result then - exit; - end; - finally - fSafe.Leave; - end; - end; - FillCharFast(aData,SizeOf(aData),0); -end; - -function TSystemUse.Data(aProcessID: integer): TSystemUseData; -begin - Data(result,aProcessID); -end; - -function TSystemUse.KB(aProcessID: integer=0): cardinal; -begin - with Data(aProcessID) do - result := WorkKB+VirtualKB; -end; - -function TSystemUse.Percent(aProcessID: integer): single; -begin - with Data(aProcessID) do - result := Kernel+User; -end; - -function TSystemUse.PercentKernel(aProcessID: integer): single; -begin - result := Data(aProcessID).Kernel; -end; - -function TSystemUse.PercentUser(aProcessID: integer): single; -begin - result := Data(aProcessID).User; -end; - -function TSystemUse.PercentSystem(out Idle,Kernel,User: currency): boolean; -begin - result := fProcessInfo.PerSystem(Idle,Kernel,User); -end; - -function TSystemUse.HistoryData(aProcessID,aDepth: integer): TSystemUseDataDynArray; -var i,n,last: integer; -begin - result := nil; - if self=nil then - exit; - fSafe.Enter; - try - i := ProcessIndex(aProcessID); - if i>=0 then - with fProcess[i] do begin - n := length(Data); - last := n-1; - if (aDepth>0) and (n>aDepth) then - n := aDepth; - SetLength(result,n); // make ordered copy - for i := 0 to n-1 do begin - if i<=fDataIndex then - result[i] := Data[fDataIndex-i] else begin - result[i] := Data[last]; - dec(last); - end; - if PInt64(@result[i].Timestamp)^=0 then begin - SetLength(result,i); // truncate to latest available sample - break; - end; - end; - end; - finally - fSafe.Leave; - end; -end; - -function TSystemUse.History(aProcessID,aDepth: integer): TSingleDynArray; -var i,n: integer; - data: TSystemUseDataDynArray; -begin - data := HistoryData(aProcessID,aDepth); - n := length(data); - SetLength(result,n); - for i := 0 to n-1 do - result[i] := data[i].Kernel+data[i].User; -end; - -class function TSystemUse.Current(aCreateIfNone: boolean): TSystemUse; -begin - if (ProcessSystemUse=nil) and aCreateIfNone then - GarbageCollectorFreeAndNil(ProcessSystemUse,TSystemUse.Create(60)); - result := ProcessSystemUse; -end; - -function TSystemUse.HistoryText(aProcessID,aDepth: integer; - aDestMemoryMB: PRawUTF8): RawUTF8; -var data: TSystemUseDataDynArray; - mem: RawUTF8; - i: integer; -begin - result := ''; - data := HistoryData(aProcessID,aDepth); - {$ifdef LINUXNOTBSD} // https://www.retro11.de/ouxr/211bsd/usr/src/lib/libc/gen/getloadavg.c.html - if data = nil then - result := StringFromFile('/proc/loadavg',{HasNoSize=}true) else - {$endif LINUX} - for i := 0 to high(data) do - with data[i] do begin - result := FormatUTF8('%% ',[result,TruncTo2Digits(Kernel+User)]); - if aDestMemoryMB<>nil then - mem := FormatUTF8('%% ',[mem,TruncTo2Digits(WorkKB/1024)]); - end; - result := trim(result); - if aDestMemoryMB<>nil then - aDestMemoryMB^ := trim(mem); -end; - -{$ifndef NOVARIANTS} - -function TSystemUse.HistoryVariant(aProcessID,aDepth: integer): variant; -var res: TDocVariantData absolute result; - data: TSystemUseDataDynArray; - i: integer; -begin - VarClear(result); - data := HistoryData(aProcessID,aDepth); - res.InitFast(length(data),dvArray); - for i := 0 to high(data) do - res.AddItem(TruncTo2Digits(data[i].Kernel+data[i].User)); -end; - -{$endif NOVARIANTS} - - -{ TSynParallelProcess } - -constructor TSynParallelProcess.Create(ThreadPoolCount: integer; const ThreadName: RawUTF8; - OnBeforeExecute, OnAfterExecute: TNotifyThreadEvent; - MaxThreadPoolCount: integer); -var i: integer; -begin - inherited Create; - if ThreadPoolCount<0 then - raise ESynParallelProcess.CreateUTF8('%.Create(%,%)',[Self,ThreadPoolCount,ThreadName]); - if ThreadPoolCount>MaxThreadPoolCount then - ThreadPoolCount := MaxThreadPoolCount; - fThreadPoolCount := ThreadPoolCount; - fThreadName := ThreadName; - SetLength(fPool,fThreadPoolCount); - for i := 0 to fThreadPoolCount-1 do - fPool[i] := TSynParallelProcessThread.Create(nil,FormatUTF8('%#%/%', - [fThreadName,i+1,fThreadPoolCount]),OnBeforeExecute,OnAfterExecute); -end; - -destructor TSynParallelProcess.Destroy; -begin - ObjArrayClear(fPool); - inherited; -end; - -procedure TSynParallelProcess.ParallelRunAndWait(Method: TSynParallelProcessMethod; - MethodCount: integer); -var use,t,n,perthread: integer; - error: RawUTF8; -begin - if (MethodCount<=0) or not Assigned(Method) then - exit; - if (self=nil) or (MethodCount=1) or (fThreadPoolCount=0) then begin - Method(0,MethodCount-1); // no need (or impossible) to use background thread - exit; - end; - use := MethodCount; - if use>fThreadPoolCount+1 then // +1 to include current thread - use := fThreadPoolCount+1; - try - // start secondary threads - perthread := MethodCount div use; - if perthread=0 then - use := 1; - n := 0; - for t := 0 to use-2 do begin - repeat - case fPool[t].AcquireThread of - flagDestroying: // should not happen - raise ESynParallelProcess.CreateUTF8( - '%.ParallelRunAndWait [%] destroying',[self,fPool[t].fThreadName]); - flagIdle: - break; // acquired (should always be the case) - end; - Sleep(1); - until false; - fPool[t].Start(Method,n,n+perthread-1); - inc(n,perthread); - inc(fParallelRunCount); - end; - // run remaining items in the current thread - if n'' then - raise ESynParallelProcess.CreateUTF8('%.ParallelRunAndWait: %',[self,error]); - end; -end; - - -{ TBlockingProcess } - -constructor TBlockingProcess.Create(aTimeOutMs: integer; const aSafe: TSynLocker); -begin - inherited Create(nil,false,false,''); - if aTimeOutMs<=0 then - fTimeOutMs := 3000 else // never wait for ever - fTimeOutMs := aTimeOutMs; - fSafe := @aSafe; -end; - -constructor TBlockingProcess.Create(aTimeOutMs: integer); -begin - fOwnedSafe := TAutoLocker.Create; - Create(aTimeOutMS,fOwnedSafe.fSafe); -end; - -destructor TBlockingProcess.Destroy; -begin - fOwnedSafe.Free; - inherited Destroy; -end; - -function TBlockingProcess.WaitFor: TBlockingEvent; -begin - fSafe^.Lock; - try - result := fEvent; - if fEvent in [evRaised,evTimeOut] then - exit; - fEvent := evWaiting; - finally - fSafe^.UnLock; - end; - FixedWaitFor(self,fTimeOutMs); - fSafe^.Lock; - try - if fEvent<>evRaised then - fEvent := evTimeOut; - result := fEvent; - finally - fSafe^.UnLock; - end; -end; - -function TBlockingProcess.WaitFor(TimeOutMS: integer): TBlockingEvent; -begin - if TimeOutMS <= 0 then - fTimeOutMs := 3000 // never wait for ever - else - fTimeOutMs := TimeOutMS; - result := WaitFor; -end; - -function TBlockingProcess.NotifyFinished(alreadyLocked: boolean): boolean; -begin - result := false; - if not alreadyLocked then - fSafe^.Lock; - try - if fEvent in [evRaised,evTimeOut] then - exit; // ignore if already notified - fEvent := evRaised; - SetEvent; // notify caller to unlock "WaitFor" method - result := true; - finally - fSafe^.UnLock; - end; -end; - -procedure TBlockingProcess.ResetInternal; -begin - ResetEvent; - fEvent := evNone; -end; - -function TBlockingProcess.Reset: boolean; -begin - fSafe^.Lock; - try - result := fEvent<>evWaiting; - if result then - ResetInternal; - finally - fSafe^.UnLock; - end; -end; - -procedure TBlockingProcess.Lock; -begin - fSafe^.Lock; -end; - -procedure TBlockingProcess.Unlock; -begin - fSafe^.Unlock; -end; - - -{ TBlockingProcessPoolItem } - -procedure TBlockingProcessPoolItem.ResetInternal; -begin - inherited ResetInternal; // set fEvent := evNone - fCall := 0; -end; - - -{ TBlockingProcessPool } - -constructor TBlockingProcessPool.Create(aClass: TBlockingProcessPoolItemClass); -begin - inherited Create; - if aClass=nil then - fClass := TBlockingProcessPoolItem else - fClass := aClass; - fPool := TObjectListLocked.Create(true); -end; - -const - CALL_DESTROYING = -1; - -destructor TBlockingProcessPool.Destroy; -var i: integer; - someWaiting: boolean; -begin - fCallCounter := CALL_DESTROYING; - someWaiting := false; - for i := 0 to fPool.Count-1 do - with TBlockingProcessPoolItem(fPool.List[i]) do - if Event=evWaiting then begin - SetEvent; // release WaitFor (with evTimeOut) - someWaiting := true; - end; - if someWaiting then - sleep(10); // propagate the pending evTimeOut to the WaitFor threads - fPool.Free; - inherited; -end; - -function TBlockingProcessPool.NewProcess(aTimeOutMs: integer): TBlockingProcessPoolItem; -var i: integer; - p: ^TBlockingProcessPoolItem; -begin - result := nil; - if fCallCounter=CALL_DESTROYING then - exit; - if aTimeOutMs<=0 then - aTimeOutMs := 3000; // never wait for ever - fPool.Safe.Lock; - try - p := pointer(fPool.List); - for i := 1 to fPool.Count do - if p^.Call=0 then begin - result := p^; // found a non-used entry - result.fTimeOutMs := aTimeOutMS; - break; - end else - inc(p); - if result=nil then begin - result := fClass.Create(aTimeOutMS); - fPool.Add(result); - end; - inc(fCallCounter); // 1,2,3,... - result.fCall := fCallCounter; - finally - fPool.Safe.UnLock; - end; -end; - -function TBlockingProcessPool.FromCall(call: TBlockingProcessPoolCall; - locked: boolean): TBlockingProcessPoolItem; -var i: integer; - p: ^TBlockingProcessPoolItem; -begin - result := nil; - if (fCallCounter=CALL_DESTROYING) or (call<=0) then - exit; - fPool.Safe.Lock; - try - p := pointer(fPool.List); - for i := 1 to fPool.Count do - if p^.Call=call then begin - result := p^; - if locked then - result.Lock; - exit; - end else - inc(p); - finally - fPool.Safe.UnLock; - end; -end; - { MultiEvent* functions } @@ -64865,7 +62235,8 @@ procedure MultiEventRemove(var EventList; Index: Integer); max := length(Events); if cardinal(index)=64)and(MAX_SQLFIELDS<=256)); - {$warnings ON} + {$ifndef FPC}{$warnings ON}{$endif} Assert(SizeOf(THash128Rec)=SizeOf(THash128)); Assert(SizeOf(THash256Rec)=SizeOf(THash256)); Assert(SizeOf(TBlock128)=SizeOf(THash128)); diff --git a/ThirdParty/mORMot/Source/SynCrypto.pas b/ThirdParty/mORMot/Source/SynCrypto.pas index 99e64a80..c882c9b3 100644 --- a/ThirdParty/mORMot/Source/SynCrypto.pas +++ b/ThirdParty/mORMot/Source/SynCrypto.pas @@ -8,7 +8,7 @@ (* This file is part of Synopse framework. - Synopse framework. Copyright (C) 2018 Arnaud Bouchez + Synopse framework. Copyright (C) 2019 Arnaud Bouchez Synopse Informatique - https://synopse.info *** BEGIN LICENSE BLOCK ***** @@ -27,7 +27,7 @@ The Initial Developer of the Original Code is Arnaud Bouchez. - Portions created by the Initial Developer are Copyright (C) 2018 + Portions created by the Initial Developer are Copyright (C) 2019 the Initial Developer. All Rights Reserved. Contributor(s): @@ -297,7 +297,8 @@ interface {$endif LVCL} Classes, SynLZ, // already included in SynCommons, and used by CompressShaAes() - SynCommons; + SynCommons, + SynTable; // for TSynUniqueIdentifierGenerator {$ifdef DELPHI5OROLDER} @@ -322,11 +323,11 @@ interface {$ifdef MSWINDOWS} {$define SHA512_X86} // external sha512-x86.obj/.o {$endif} - {$ifdef FPC_PIC} + {$ifdef ABSOLUTEPASCAL} {$define AES_PASCAL} // x86 AES asm below is not PIC-safe {$else} {$define CPUX86_NOTPIC} - {$endif FPC_PIC} + {$endif ABSOLUTEPASCAL} {$ifdef FPC} {$ifdef DARWIN} {$define AES_PASCAL} // as reported by alf @@ -427,7 +428,7 @@ {$ifdef UNICODE}THash128History = record{$else}THash128History = object{$endif // - this class will use VIA PadLock instructions, if available {$endif} // - we defined a record instead of a class, to allow stack allocation and - // thread-safe reuse of one initialized instance, if needed + // thread-safe reuse of one initialized instance (warning: not for Padlock) {$ifdef UNICODE}TAES = record{$else}TAES = object{$endif} private Context: packed array[1..AESContextSize] of byte; @@ -778,7 +779,7 @@ TAESAbstract = class(TSynPersistent) property IVHistoryDepth: integer read fIVHistoryDec.Depth write SetIVHistory; end; - /// handle AES cypher/uncypher with chaining + /// handle AES cypher/uncypher with chaining with out own optimized code // - use any of the inherited implementation, corresponding to the chaining // mode required - TAESECB, TAESCBC, TAESCFB, TAESOFB and TAESCTR classes to // handle in ECB, CBC, CFB, OFB and CTR mode (including PKCS7-like padding) @@ -790,11 +791,10 @@ TAESAbstractSyn = class(TAESAbstract) fIn, fOut: PAESBlock; fCV: TAESBlock; AES: TAES; - fCount: Cardinal; fAESInit: (initNone, initEncrypt, initDecrypt); procedure EncryptInit; procedure DecryptInit; - procedure TrailerBytes; + procedure TrailerBytes(count: cardinal); public /// creates a new instance with the very same values // - by design, our classes will use stateless context, so this method @@ -1074,13 +1074,10 @@ procedure AESIVCtrEncryptDecrypt(const BI; var BO; DoEncrypt: boolean); type /// thread-safe class containing a TAES encryption/decryption engine - TAESLocked = class(TSynPersistent) + TAESLocked = class(TSynPersistentLock) protected fAES: TAES; - fLock: TRTLCriticalSection; public - /// initialize the internal lock, but not the TAES instance - constructor Create; override; /// finalize all used memory and resources destructor Destroy; override; end; @@ -1268,8 +1265,9 @@ procedure SetMainAESPRNG; {$endif} /// low-level function returning some random binary using standard API -// - will call /dev/urandom under POSIX, and CryptGenRandom API on Windows, -// and fallback to SynCommons.FillRandom if the system is not supported +// - will call /dev/urandom or /dev/random under POSIX, and CryptGenRandom API +// on Windows, and fallback to SynCommons.FillRandom if the system API failed +// or for padding if more than 32 bytes is retrieved from /dev/urandom // - you should not have to call this procedure, but faster and safer TAESPRNG procedure FillSystemRandom(Buffer: PByteArray; Len: integer; AllowBlocking: boolean); @@ -1277,7 +1275,7 @@ procedure FillSystemRandom(Buffer: PByteArray; Len: integer; AllowBlocking: bool type PSHA1Digest = ^TSHA1Digest; /// 160 bits memory block for SHA-1 hash digest storage - TSHA1Digest = packed array[0..19] of byte; + TSHA1Digest = THash160; PSHA1 = ^TSHA1; /// handle SHA-1 hashing @@ -1538,9 +1536,6 @@ {$ifdef UNICODE}TSHA3 = record{$else}TSHA3 = object{$endif} procedure Done; end; - /// 64 bytes buffer, used internally during HMAC process - TByte64 = array[0..15] of cardinal; - TMD5In = array[0..15] of cardinal; PMD5In = ^TMD5In; /// 128 bits memory block for MD5 hash digest storage @@ -1700,19 +1695,6 @@ TAESWriteStream = class(TStream) procedure Finish; end; -/// overwrite a 64-byte buffer with zeros -// - may be used to cleanup stack-allocated content -// ! ... finally FillZero(temp); end; -procedure FillZero(var hash: TByte64); overload; - -/// overwrite a SHA-1 digest buffer with zeros -// - may be used to cleanup stack-allocated content -// ! ... finally FillZero(temp); end; -procedure FillZero(var hash: TSHA1Digest); overload; - -/// compare two SHA-1 digest buffers -function IsEqual(const A,B: TSHA1Digest): boolean; overload; - /// direct MD5 hash calculation of some data function MD5Buf(const Buffer; Len: Cardinal): TMD5Digest; @@ -1740,7 +1722,7 @@ function SHA512(const s: RawByteString): RawUTF8; {$ifdef UNICODE}THMAC_SHA1 = record{$else}THMAC_SHA1 = object{$endif} private sha: TSHA1; - step7data: TByte64; + step7data: THash512Rec; public /// prepare the HMAC authentication with the supplied key // - content of this record is stateless, so you can prepare a HMAC for a @@ -1915,7 +1897,7 @@ procedure SHA256Weak(const s: RawByteString; out Digest: TSHA256Digest); {$ifdef UNICODE}THMAC_SHA256 = record{$else}THMAC_SHA256 = object{$endif} private sha: TSha256; - step7data: TByte64; + step7data: THash512Rec; public /// prepare the HMAC authentication with the supplied key // - content of this record is stateless, so you can prepare a HMAC for a @@ -2145,7 +2127,7 @@ procedure HMAC_CRC256C(const key,msg: RawByteString; out result: THash256); over {$ifdef UNICODE}THMAC_CRC32C = record{$else}THMAC_CRC32C = object{$endif} private seed: cardinal; - step7data: TByte64; + step7data: THash512Rec; public /// prepare the HMAC authentication with the supplied key // - consider using Compute to re-use a prepared HMAC instance @@ -2386,16 +2368,16 @@ function AESSelfTest(onlytables: Boolean): boolean; /// self test of RC4 routines function RC4SelfTest: boolean; -/// entry point of the MD5 transform function - may be used from outside +/// entry point of the raw MD5 transform function - may be used for low-level use procedure RawMd5Compress(var Hash; Data: pointer); -/// entry point of the SHA-1 transform function - may be used from outside +/// entry point of the raw SHA-1 transform function - may be used for low-level use procedure RawSha1Compress(var Hash; Data: pointer); -/// entry point of the SHA-256 transform function - may be used from outside +/// entry point of the raw SHA-256 transform function - may be used for low-level use procedure RawSha256Compress(var Hash; Data: pointer); -/// entry point of the SHA-512 transform function - may be used from outside +/// entry point of the raw SHA-512 transform function - may be used for low-level use procedure RawSha512Compress(var Hash; Data: pointer); // little endian fast conversion @@ -3277,7 +3259,7 @@ procedure ComputeAesStaticTables; {$ifdef CPU32} -procedure bswap256(s,d: PIntegerArray); +procedure bswap256(s,d: PIntegerArray); {$ifdef FPC}nostackframe; assembler;{$endif} asm push ebx mov ecx,eax // ecx=s, edx=d @@ -3288,7 +3270,7 @@ procedure bswap256(s,d: PIntegerArray); pop ebx end; -procedure bswap160(s,d: PIntegerArray); +procedure bswap160(s,d: PIntegerArray); {$ifdef FPC}nostackframe; assembler;{$endif} asm push ebx mov ecx,eax // ecx=s, edx=d @@ -3473,40 +3455,22 @@ function SHA512(const s: RawByteString): RawUTF8; { THMAC_SHA1 } -procedure FillZero(var hash: TSHA1Digest); overload; -begin - FillCharFast(hash,sizeof(hash),0); -end; - -procedure FillZero(var hash: TByte64); -begin - FillCharFast(hash,sizeof(hash),0); -end; - -function IsEqual(const A,B: TSHA1Digest): boolean; -var a_: TIntegerArray absolute A; - b_: TIntegerArray absolute B; -begin // uses anti-forensic time constant "xor/or" pattern - result := ((a_[0] xor b_[0]) or (a_[1] xor b_[1]) or (a_[2] xor b_[2]) or - (a_[3] xor b_[3]) or (a_[4] xor b_[4]))=0; -end; - procedure THMAC_SHA1.Init(key: pointer; keylen: integer); var i: integer; - k0,k0xorIpad: TByte64; + k0,k0xorIpad: THash512Rec; begin - FillZero(k0); + FillZero(k0.b); if keylen>sizeof(k0) then - sha.Full(key,keylen,PSHA1Digest(@k0)^) else + sha.Full(key,keylen,k0.b160) else MoveFast(key^,k0,keylen); for i := 0 to 15 do - k0xorIpad[i] := k0[i] xor $36363636; + k0xorIpad.c[i] := k0.c[i] xor $36363636; for i := 0 to 15 do - step7data[i] := k0[i] xor $5c5c5c5c; + step7data.c[i] := k0.c[i] xor $5c5c5c5c; sha.Init; sha.Update(@k0xorIpad,sizeof(k0xorIpad)); - FillZero(k0); - FillZero(k0xorIpad); + FillZero(k0.b); + FillZero(k0xorIpad.b); end; procedure THMAC_SHA1.Update(msg: pointer; msglen: integer); @@ -3521,7 +3485,7 @@ procedure THMAC_SHA1.Done(out result: TSHA1Digest; NoInit: boolean); sha.Update(@result,sizeof(result)); sha.Final(result,NoInit); if not NoInit then - FillZero(step7data); + FillZero(step7data.b); end; procedure THMAC_SHA1.Done(out result: RawUTF8; NoInit: boolean); @@ -3587,20 +3551,20 @@ procedure PBKDF2_HMAC_SHA1(const password,salt: RawByteString; count: Integer; procedure THMAC_SHA256.Init(key: pointer; keylen: integer); var i: integer; - k0,k0xorIpad: TByte64; + k0,k0xorIpad: THash512Rec; begin - FillZero(k0); + FillZero(k0.b); if keylen>sizeof(k0) then - sha.Full(key,keylen,PSHA256Digest(@k0)^) else + sha.Full(key,keylen,k0.Lo) else MoveFast(key^,k0,keylen); for i := 0 to 15 do - k0xorIpad[i] := k0[i] xor $36363636; + k0xorIpad.c[i] := k0.c[i] xor $36363636; for i := 0 to 15 do - step7data[i] := k0[i] xor $5c5c5c5c; + step7data.c[i] := k0.c[i] xor $5c5c5c5c; sha.Init; sha.Update(@k0xorIpad,sizeof(k0xorIpad)); - FillZero(k0); - FillZero(k0xorIpad); + FillZero(k0.b); + FillZero(k0xorIpad.b); end; procedure THMAC_SHA256.Update(msg: pointer; msglen: integer); @@ -3630,7 +3594,7 @@ procedure THMAC_SHA256.Done(out result: TSHA256Digest; NoInit: boolean); sha.Update(@result,sizeof(result)); sha.Final(result,NoInit); if not NoInit then - FillZero(step7data); + FillZero(step7data.b); end; procedure THMAC_SHA256.Done(out result: RawUTF8; NoInit: boolean); @@ -3957,16 +3921,16 @@ procedure crc256cmix(h1,h2: cardinal; h: PCardinalArray); procedure HMAC_CRC256C(key,msg: pointer; keylen,msglen: integer; out result: THash256); var i: integer; h1,h2: cardinal; - k0,k0xorIpad,step7data: TByte64; + k0,k0xorIpad,step7data: THash512Rec; begin FillCharFast(k0,sizeof(k0),0); if keylen>sizeof(k0) then - crc256c(key,keylen,PHash256(@k0)^) else + crc256c(key,keylen,k0.Lo) else MoveFast(key^,k0,keylen); for i := 0 to 15 do - k0xorIpad[i] := k0[i] xor $36363636; + k0xorIpad.c[i] := k0.c[i] xor $36363636; for i := 0 to 15 do - step7data[i] := k0[i] xor $5c5c5c5c; + step7data.c[i] := k0.c[i] xor $5c5c5c5c; h1 := crc32c(crc32c(0,@k0xorIpad,sizeof(k0xorIpad)),msg,msglen); h2 := crc32c(crc32c(h1,@k0xorIpad,sizeof(k0xorIpad)),msg,msglen); crc256cmix(h1,h2,@result); @@ -3998,16 +3962,16 @@ procedure THMAC_CRC32C.Init(const key: RawByteString); procedure THMAC_CRC32C.Init(key: pointer; keylen: integer); var i: integer; - k0,k0xorIpad: TByte64; + k0,k0xorIpad: THash512Rec; begin FillCharFast(k0,sizeof(k0),0); if keylen>sizeof(k0) then - crc256c(key,keylen,PHash256(@k0)^) else + crc256c(key,keylen,k0.Lo) else MoveFast(key^,k0,keylen); for i := 0 to 15 do - k0xorIpad[i] := k0[i] xor $36363636; + k0xorIpad.c[i] := k0.c[i] xor $36363636; for i := 0 to 15 do - step7data[i] := k0[i] xor $5c5c5c5c; + step7data.c[i] := k0.c[i] xor $5c5c5c5c; seed := crc32c(0,@k0xorIpad,sizeof(k0xorIpad)); FillCharFast(k0,sizeof(k0),0); FillCharFast(k0xorIpad,sizeof(k0xorIpad),0); @@ -4836,11 +4800,7 @@ procedure aesencryptx64(const ctxt: TAESContext; bi, bo: PWA4); sub r13, 1 add r12, 16 lea r14, [rip+Te0] - {$ifdef FPC} - align 16 - {$else} - nop; nop; nop; nop; nop; nop - {$endif} + {$ifdef FPC} align 16 {$else} .align 16 {$endif} @round: mov esi, eax mov edi, edx movzx r8d, al @@ -6476,15 +6436,9 @@ procedure Sha256ExpandMessageBlocks(W, Buf: PIntegerArray); // optimized unrolled version from Intel's sha256_sse4.asm // Original code is released as Copyright (c) 2012, Intel Corporation var - K256Aligned: RawByteString; // movdqa + paddd do expect 16 bytes alignment - + K256AlignedStore: RawByteString; + K256Aligned: pointer; // movdqa + paddd do expect 16 bytes alignment const - PSHUFFLE_BYTE_FLIP_MASK: array[0..1] of QWord = - (QWord($0405060700010203),QWord($0C0D0E0F08090A0B)); - _SHUF_00BA: array[0..1] of QWord = - (QWord($0B0A090803020100),QWord($FFFFFFFFFFFFFFFF)); - _SHUF_DC00: array[0..1] of QWord = - (QWord($FFFFFFFFFFFFFFFF),QWord($B0A090803020100)); STACK_SIZE = 32{$ifndef LINUX}+7*16{$endif}; procedure sha256_sse4(var input_data; var digest; num_blks: PtrUInt); @@ -6527,9 +6481,9 @@ procedure sha256_sse4(var input_data; var digest; num_blks: PtrUInt); mov r9d,[rdx+14H] mov r10d,[rdx+18H] mov r11d,[rdx+1CH] - movdqu xmm12,[rip+PSHUFFLE_BYTE_FLIP_MASK] - movdqu xmm10,[rip+_SHUF_00BA] - movdqu xmm11,[rip+_SHUF_DC00] + movdqa xmm12,[rip+@flip] + movdqa xmm10,[rip+@00BA] + movdqa xmm11,[rip+@DC00] @loop0: mov rbp,[rip+K256Aligned] movdqu xmm4,[rcx] pshufb xmm4,xmm12 @@ -6541,7 +6495,6 @@ procedure sha256_sse4(var input_data; var digest; num_blks: PtrUInt); pshufb xmm7,xmm12 mov [rsp+8H],rcx mov rcx,3 - nop; nop; nop; nop; nop // manual align 16 @loop1: movdqa xmm9,[rbp] paddd xmm9,xmm4 movdqa [rsp+10H],xmm9 @@ -7434,10 +7387,18 @@ procedure sha256_sse4(var input_data; var digest; num_blks: PtrUInt); pop rsi {$endif} pop rbx + ret +{$ifdef FPC} align 16 {$else} .align 16 {$endif} +@flip: dq $0405060700010203 + dq $0C0D0E0F08090A0B +@00BA: dq $0B0A090803020100 + dq $FFFFFFFFFFFFFFFF +@DC00: dq $FFFFFFFFFFFFFFFF + dq $0B0A090803020100 end; {$endif CPUX64} -procedure sha256Compress(var Hash: TSHAHash; Data: pointer); +procedure Sha256CompressPas(var Hash: TSHAHash; Data: pointer); // Actual hashing function var H: TSHAHash; W: array[0..63] of cardinal; @@ -7446,16 +7407,6 @@ procedure sha256Compress(var Hash: TSHAHash; Data: pointer); t1, t2: cardinal; {$endif} begin - {$ifdef CPUX64} - if cfSSE41 in CpuFeatures then begin - if K256Aligned='' then - SetString(K256Aligned,PAnsiChar(@K256),SizeOf(K256)); - if PtrUInt(K256ALigned)and 15=0 then begin - sha256_sse4(Data^,Hash,1); - exit; - end; // if K256Aligned[] is not properly aligned -> fallback to pascal - end; - {$endif CPUX64} // calculate "expanded message blocks" Sha256ExpandMessageBlocks(@W,Data); // assign old working hash to local variables A..H @@ -7563,7 +7514,11 @@ procedure sha256Compress(var Hash: TSHAHash; Data: pointer); procedure RawSha256Compress(var Hash; Data: pointer); begin - sha256Compress(TSHAHash(Hash), Data); + {$ifdef CPUX64} + if K256AlignedStore<>'' then // use optimized Intel's sha256_sse4.asm + sha256_sse4(Data^,Hash,1) else + {$endif CPUX64} + Sha256CompressPas(TSHAHash(Hash),Data); end; procedure TSHA256.Final(out Digest: TSHA256Digest; NoInit: boolean); @@ -7575,14 +7530,14 @@ procedure TSHA256.Final(out Digest: TSHA256Digest; NoInit: boolean); FillcharFast(Data.Buffer[Data.Index+1],63-Data.Index,0); // compress if more than 448 bits (no space for 64 bit length storage) if Data.Index>=56 then begin - sha256Compress(Data.Hash,@Data.Buffer); + RawSha256Compress(Data.Hash,@Data.Buffer); FillcharFast(Data.Buffer,56,0); end; // write 64 bit Buffer length into the last bits of the last block // (in big endian format) and do a final compress PInteger(@Data.Buffer[56])^ := bswap32(TQWordRec(Data.MLen).H); PInteger(@Data.Buffer[60])^ := bswap32(TQWordRec(Data.MLen).L); - sha256Compress(Data.Hash,@Data.Buffer); + RawSha256Compress(Data.Hash,@Data.Buffer); // Hash -> Digest to little endian format bswap256(@Data.Hash,@Digest); // clear Data and internally stored Digest @@ -7637,10 +7592,10 @@ procedure TSHA256.Update(Buffer: pointer; Len: integer); if aLen<=Len then begin if Data.Index<>0 then begin MoveFast(Buffer^,Data.Buffer[Data.Index],aLen); - sha256Compress(Data.Hash,@Data.Buffer); + RawSha256Compress(Data.Hash,@Data.Buffer); Data.Index := 0; end else - sha256Compress(Data.Hash,Buffer); // avoid temporary copy + RawSha256Compress(Data.Hash,Buffer); // avoid temporary copy dec(Len,aLen); inc(PtrInt(Buffer),aLen); end else begin @@ -7801,6 +7756,19 @@ procedure sha512_compress(state: PQWord; block: PByteArray); cdecl; external; procedure sha512_sse4(data, hash: pointer; blocks: Int64); {$ifdef FPC}cdecl;{$endif} external; {$endif SHA512_X64} +procedure RawSha512Compress(var Hash; Data: pointer); +begin + {$ifdef SHA512_X86} + if cfSSSE3 in CpuFeatures then + sha512_compress(@Hash,Data) else + {$endif} + {$ifdef SHA512_X64} + if cfSSE41 in CpuFeatures then + sha512_sse4(Data,@Hash,1) else + {$endif} + sha512_compresspas(TSHA512Hash(Hash), Data); +end; + { TSHA384 } @@ -7809,28 +7777,12 @@ procedure TSHA384.Final(out Digest: TSHA384Digest; NoInit: boolean); Data[Index] := $80; FillcharFast(Data[Index+1],127-Index,0); if Index>=112 then begin - {$ifdef SHA512_X86} - if cfSSSE3 in CpuFeatures then - sha512_compress(@Hash,@Data) else - {$endif} - {$ifdef SHA512_X64} - if cfSSE41 in CpuFeatures then - sha512_sse4(@Data,@Hash,1) else - {$endif} - sha512_compresspas(Hash,@Data); + RawSha512Compress(Hash,@Data); FillcharFast(Data,112,0); end; PQWord(@Data[112])^ := bswap64(MLen shr 61); PQWord(@Data[120])^ := bswap64(MLen shl 3); - {$ifdef SHA512_X86} - if cfSSSE3 in CpuFeatures then - sha512_compress(@Hash,@Data) else - {$endif} - {$ifdef SHA512_X64} - if cfSSE41 in CpuFeatures then - sha512_sse4(@Data,@Hash,1) else - {$endif} - sha512_compresspas(Hash,@Data); + RawSha512Compress(Hash,@Data); bswap64array(@Hash,@Digest,6); if not NoInit then Init; @@ -7873,26 +7825,10 @@ procedure TSHA384.Update(Buffer: pointer; Len: integer); if aLen<=Len then begin if Index<>0 then begin MoveFast(Buffer^,Data[Index],aLen); - {$ifdef SHA512_X86} - if cfSSSE3 in CpuFeatures then - sha512_compress(@Hash,@Data) else - {$endif} - {$ifdef SHA512_X64} - if cfSSE41 in CpuFeatures then - sha512_sse4(@Data,@Hash,1) else - {$endif} - sha512_compresspas(Hash,@Data); + RawSha512Compress(Hash,@Data); Index := 0; end else // avoid temporary copy - {$ifdef SHA512_X86} - if cfSSSE3 in CpuFeatures then - sha512_compress(@Hash,Buffer) else - {$endif} - {$ifdef SHA512_X64} - if cfSSE41 in CpuFeatures then - sha512_sse4(Buffer,@Hash,1) else - {$endif} - sha512_compresspas(Hash,Buffer); + RawSha512Compress(Hash,Buffer); dec(Len,aLen); inc(PByte(Buffer),aLen); end else begin @@ -7916,28 +7852,12 @@ procedure TSHA512.Final(out Digest: TSHA512Digest; NoInit: boolean); Data[Index] := $80; FillcharFast(Data[Index+1],127-Index,0); if Index>=112 then begin - {$ifdef SHA512_X86} - if cfSSSE3 in CpuFeatures then - sha512_compress(@Hash,@Data) else - {$endif} - {$ifdef SHA512_X64} - if cfSSE41 in CpuFeatures then - sha512_sse4(@Data,@Hash,1) else - {$endif} - sha512_compresspas(Hash,@Data); + RawSha512Compress(Hash,@Data); FillcharFast(Data,112,0); end; PQWord(@Data[112])^ := bswap64(MLen shr 61); PQWord(@Data[120])^ := bswap64(MLen shl 3); - {$ifdef SHA512_X86} - if cfSSSE3 in CpuFeatures then - sha512_compress(@Hash,@Data) else - {$endif} - {$ifdef SHA512_X64} - if cfSSE41 in CpuFeatures then - sha512_sse4(@Data,@Hash,1) else - {$endif} - sha512_compresspas(Hash,@Data); + RawSha512Compress(Hash,@Data); bswap64array(@Hash,@Digest,8); if not NoInit then Init; @@ -7980,26 +7900,10 @@ procedure TSHA512.Update(Buffer: pointer; Len: integer); if aLen<=Len then begin if Index<>0 then begin MoveFast(Buffer^,Data[Index],aLen); - {$ifdef SHA512_X86} - if cfSSSE3 in CpuFeatures then - sha512_compress(@Hash,@Data) else - {$endif} - {$ifdef SHA512_X64} - if cfSSE41 in CpuFeatures then - sha512_sse4(@Data,@Hash,1) else - {$endif} - sha512_compresspas(Hash,@Data); + RawSha512Compress(Hash,@Data); Index := 0; end else // avoid temporary copy - {$ifdef SHA512_X86} - if cfSSSE3 in CpuFeatures then - sha512_compress(@Hash,Buffer) else - {$endif} - {$ifdef SHA512_X64} - if cfSSE41 in CpuFeatures then - sha512_sse4(Buffer,@Hash,1) else - {$endif} - sha512_compresspas(Hash,Buffer); + RawSha512Compress(Hash,Buffer); dec(Len,aLen); inc(PByte(Buffer),aLen); end else begin @@ -8010,19 +7914,6 @@ procedure TSHA512.Update(Buffer: pointer; Len: integer); until Len<=0; end; -procedure RawSha512Compress(var Hash; Data: pointer); -begin - {$ifdef SHA512_X86} - if cfSSSE3 in CpuFeatures then - sha512_compress(@Hash,Data) else - {$endif} - {$ifdef SHA512_X64} - if cfSSE41 in CpuFeatures then - sha512_sse4(Data,@Hash,1) else - {$endif} - sha512_compresspas(TSHA512Hash(Hash), Data); -end; - procedure TSHA512.Update(const Buffer: RawByteString); begin Update(pointer(Buffer),length(Buffer)); @@ -8189,6 +8080,7 @@ procedure KeccakPermutation(A: PQWordArray); procedure KeccakPermutationKernel(B, A, C: Pointer); {$ifdef CPU32} // Eric Grange's MMX version (PIC-safe) +{$ifdef FPC}nostackframe; assembler;{$endif} asm add edx, 128 add eax, 128 @@ -8530,7 +8422,7 @@ procedure KeccakPermutationKernel(B, A, C: Pointer); movq [edx + 64], mm0 {$else} {$ifdef FPC}nostackframe; assembler; asm{$else} -// Synopse's x64 asm, optimized for both in/out-order pipelined CPUs +// Synopse's x64 asm, optimized for both in+out-order pipelined CPUs asm // input: rcx=B, rdx=A, r8=C (Linux: rdi,rsi,rdx) .noframe {$endif}{$ifndef win64} @@ -9385,9 +9277,9 @@ procedure TSynSigner.Update(aBuffer: pointer; aLen: integer); procedure TSynSigner.Final(out aSignature: THash512Rec; aNoInit: boolean); begin case fAlgo of - saSha1: PHMAC_SHA1(@ctxt)^.Done(PSHA1Digest(@aSignature)^,aNoInit); + saSha1: PHMAC_SHA1(@ctxt)^.Done(aSignature.b160,aNoInit); saSha256: PHMAC_SHA256(@ctxt)^.Done(aSignature.Lo,aNoInit); - saSha384: PHMAC_SHA384(@ctxt)^.Done(aSignature.b3,aNoInit); + saSha384: PHMAC_SHA384(@ctxt)^.Done(aSignature.b384,aNoInit); saSha512: PHMAC_SHA512(@ctxt)^.Done(aSignature.b,aNoInit); saSha3224..saSha3S256: PSHA3(@ctxt)^.Final(@aSignature,fSignatureSize shl 3,aNoInit); end; @@ -10117,7 +10009,7 @@ procedure XorBlock(p: PIntegerArray; Count, Cod: integer); inc(PByte(p),16); end; Cod := (Cod shl 11) xor integer(Td0[cod shr 21]); - for i := 1 to (Count and 15)shr 2 do begin // last 4 bytes blocs + for i := 1 to (Count and AESBlockMod)shr 2 do begin // last 4 bytes blocs p^[0] := p^[0] xor Cod; inc(PByte(p),4); end; @@ -10160,7 +10052,7 @@ procedure XorConst(P: PIntegerArray; Count: integer); P^[3] := P^[3] xor Code; inc(PByte(P),16); end; - for i := 0 to ((Count and 15)shr 2)-1 do // last 4 bytes blocs + for i := 0 to ((Count and AESBlockMod)shr 2)-1 do // last 4 bytes blocs P^[i] := P^[i] xor Code; end; @@ -12109,9 +12001,6 @@ procedure TSHA1.Update(const Buffer: RawByteString); { TAESAbstract } -const - sAESException = 'AES engine initialization failure'; - var aesivctr: array[boolean] of TAESLocked; @@ -12125,17 +12014,16 @@ procedure AESIVCtrEncryptDecrypt(const BI; var BO; DoEncrypt: boolean); DecryptInit(AESIVCTR_KEY,128); end; with aesivctr[DoEncrypt] do begin - EnterCriticalSection(fLock); + fSafe^.Lock; TAESContext(fAES.Context).DoBlock(fAES.Context,BI,BO); - LeaveCriticalSection(fLock); + fSafe^.UnLock; end; end; constructor TAESAbstract.Create(const aKey; aKeySize: cardinal); begin if (aKeySize<>128) and (aKeySize<>192) and (aKeySize<>256) then - raise ESynCrypto.CreateUTF8( - '%.Create key size = %; should be either 128, 192 or 256',[self,aKeySize]); + raise ESynCrypto.CreateUTF8('%.Create(aKeySize=%): 128/192/256 required',[self,aKeySize]); fKeySize := aKeySize; fKeySizeBytes := fKeySize shr 3; MoveFast(aKey,fKey,fKeySizeBytes); @@ -12536,21 +12424,18 @@ destructor TAESAbstractSyn.Destroy; function TAESAbstractSyn.Clone: TAESAbstract; begin - {$ifdef USEPADLOCK} - if TAESContext(AES).initialized and (TAESContext(AES).ViaCtx<>nil) then begin - result := inherited Clone; - exit; + if (fIVHistoryDec.Count<>0) {$ifdef USEPADLOCK} or + TAESContext(AES).initialized and (TAESContext(AES).ViaCtx<>nil){$endif} then + result := inherited Clone else begin + result := NewInstance as TAESAbstractSyn; + MoveFast(pointer(self)^,pointer(result)^,InstanceSize); end; - {$endif} - result := NewInstance as TAESAbstractSyn; - MoveFast(pointer(self)^,pointer(result)^,InstanceSize); end; procedure TAESAbstractSyn.Decrypt(BufIn, BufOut: pointer; Count: cardinal); begin fIn := BufIn; fOut := BufOut; - fCount := Count; fCV := fIV; end; @@ -12558,14 +12443,13 @@ procedure TAESAbstractSyn.DecryptInit; begin if AES.DecryptInit(fKey,fKeySize) then fAESInit := initDecrypt else - raise ESynCrypto.Create(sAESException); + raise ESynCrypto.CreateUTF8('%.DecryptInit',[self]); end; procedure TAESAbstractSyn.Encrypt(BufIn, BufOut: pointer; Count: cardinal); begin fIn := BufIn; fOut := BufOut; - fCount := Count; fCV := fIV; end; @@ -12573,15 +12457,15 @@ procedure TAESAbstractSyn.EncryptInit; begin if AES.EncryptInit(fKey,fKeySize) then fAESInit := initEncrypt else - raise ESynCrypto.Create(sAESException); + raise ESynCrypto.CreateUTF8('%.EncryptInit',[self]); end; -procedure TAESAbstractSyn.TrailerBytes; +procedure TAESAbstractSyn.TrailerBytes(count: cardinal); begin if fAESInit<>initEncrypt then EncryptInit; TAESContext(AES.Context).DoBlock(AES.Context,fCV,fCV); - XorMemory(pointer(fOut),pointer(fIn),@fCV,fCount); + XorMemory(pointer(fOut),pointer(fIn),@fCV,count); end; @@ -12590,7 +12474,7 @@ procedure TAESAbstractSyn.TrailerBytes; procedure TAESECB.Decrypt(BufIn, BufOut: pointer; Count: cardinal); var i: integer; begin - inherited; // CV := IV + set fIn,fOut,fCount + inherited; // CV := IV + set fIn,fOut if fAESInit<>initDecrypt then DecryptInit; for i := 1 to Count shr 4 do begin @@ -12598,15 +12482,15 @@ procedure TAESECB.Decrypt(BufIn, BufOut: pointer; Count: cardinal); inc(fIn); inc(fOut); end; - fCount := fCount and AESBlockMod; - if fCount<>0 then - TrailerBytes; + Count := Count and AESBlockMod; + if Count<>0 then + TrailerBytes(Count); end; procedure TAESECB.Encrypt(BufIn, BufOut: pointer; Count: cardinal); var i: integer; begin - inherited; // CV := IV + set fIn,fOut,fCount + inherited; // CV := IV + set fIn,fOut if fAESInit<>initEncrypt then EncryptInit; for i := 1 to Count shr 4 do begin @@ -12614,9 +12498,9 @@ procedure TAESECB.Encrypt(BufIn, BufOut: pointer; Count: cardinal); inc(fIn); inc(fOut); end; - fCount := fCount and AESBlockMod; - if fCount<>0 then - TrailerBytes; + Count := Count and AESBlockMod; + if Count<>0 then + TrailerBytes(Count); end; @@ -12626,7 +12510,7 @@ procedure TAESCBC.Decrypt(BufIn, BufOut: pointer; Count: cardinal); var i: integer; tmp: TAESBlock; begin - inherited; // CV := IV + set fIn,fOut,fCount + inherited; // CV := IV + set fIn,fOut if Count>=sizeof(TAESBlock) then begin if fAESInit<>initDecrypt then DecryptInit; @@ -12639,15 +12523,15 @@ procedure TAESCBC.Decrypt(BufIn, BufOut: pointer; Count: cardinal); inc(fOut); end; end; - fCount := fCount and AESBlockMod; - if fCount<>0 then - TrailerBytes; + Count := Count and AESBlockMod; + if Count<>0 then + TrailerBytes(Count); end; procedure TAESCBC.Encrypt(BufIn, BufOut: pointer; Count: cardinal); var i: integer; begin - inherited; // CV := IV + set fIn,fOut,fCount + inherited; // CV := IV + set fIn,fOut if fAESInit<>initEncrypt then EncryptInit; for i := 1 to Count shr 4 do begin @@ -12657,9 +12541,9 @@ procedure TAESCBC.Encrypt(BufIn, BufOut: pointer; Count: cardinal); inc(fIn); inc(fOut); end; - fCount := fCount and AESBlockMod; - if fCount<>0 then - TrailerBytes; + Count := Count and AESBlockMod; + if Count<>0 then + TrailerBytes(Count); end; { TAESAbstractEncryptOnly } @@ -12731,7 +12615,7 @@ procedure TAESCFB.Decrypt(BufIn, BufOut: pointer; Count: cardinal); pxor xmm7,xmm7 // for safety end else {$endif} begin - inherited; // CV := IV + set fIn,fOut,fCount + inherited; // CV := IV + set fIn,fOut for i := 1 to Count shr 4 do begin tmp := fIn^; TAESContext(AES.Context).DoBlock(AES.Context,fCV,fCV); @@ -12740,9 +12624,9 @@ procedure TAESCFB.Decrypt(BufIn, BufOut: pointer; Count: cardinal); inc(fIn); inc(fOut); end; - fCount := fCount and AESBlockMod; - if fCount<>0 then - TrailerBytes; + Count := Count and AESBlockMod; + if Count<>0 then + TrailerBytes(Count); end; end; @@ -12780,7 +12664,7 @@ procedure TAESCFB.Encrypt(BufIn, BufOut: pointer; Count: cardinal); pxor xmm7,xmm7 // for safety end else {$endif} begin - inherited; // CV := IV + set fIn,fOut,fCount + inherited; // CV := IV + set fIn,fOut for i := 1 to Count shr 4 do begin TAESContext(AES.Context).DoBlock(AES.Context,fCV,fCV); XorBlock16(pointer(fIn),pointer(fOut),pointer(@fCV)); @@ -12788,9 +12672,9 @@ procedure TAESCFB.Encrypt(BufIn, BufOut: pointer; Count: cardinal); inc(fIn); inc(fOut); end; - fCount := fCount and AESBlockMod; - if fCount<>0 then - TrailerBytes; + Count := Count and AESBlockMod; + if Count<>0 then + TrailerBytes(Count); end; end; @@ -12883,7 +12767,7 @@ procedure TAESCFBCRC.Decrypt(BufIn, BufOut: pointer; Count: cardinal); pxor xmm7,xmm7 // for safety end else {$endif} begin - inherited; // CV := IV + set fIn,fOut,fCount + inherited; // CV := IV + set fIn,fOut for i := 1 to Count shr 4 do begin tmp := fIn^; crcblock(@fMAC.encrypted,pointer(fIn)); // fIn may be = fOut @@ -12894,11 +12778,11 @@ procedure TAESCFBCRC.Decrypt(BufIn, BufOut: pointer; Count: cardinal); inc(fIn); inc(fOut); end; - fCount := fCount and AESBlockMod; - if fCount<>0 then begin - TrailerBytes; + Count := Count and AESBlockMod; + if Count<>0 then begin + TrailerBytes(Count); with fMAC do // includes trailing bytes to the plain crc - PCardinal(@plain)^ := crc32c(PCardinal(@plain)^,pointer(fOut),fCount); + PCardinal(@plain)^ := crc32c(PCardinal(@plain)^,pointer(fOut),Count); end; end; end; @@ -12940,7 +12824,7 @@ procedure TAESCFBCRC.Encrypt(BufIn, BufOut: pointer; Count: cardinal); pxor xmm7,xmm7 // for safety end else {$endif} begin - inherited; // CV := IV + set fIn,fOut,fCount + inherited; // CV := IV + set fIn,fOut for i := 1 to Count shr 4 do begin TAESContext(AES.Context).DoBlock(AES.Context,fCV,fCV); crcblock(@fMAC.plain,pointer(fIn)); // fOut may be = fIn @@ -12950,11 +12834,11 @@ procedure TAESCFBCRC.Encrypt(BufIn, BufOut: pointer; Count: cardinal); inc(fIn); inc(fOut); end; - fCount := fCount and AESBlockMod; - if fCount<>0 then begin - TrailerBytes; + Count := Count and AESBlockMod; + if Count<>0 then begin with fMAC do // includes trailing bytes to the plain crc - PCardinal(@plain)^ := crc32c(PCardinal(@plain)^,pointer(fIn),fCount); + PCardinal(@plain)^ := crc32c(PCardinal(@plain)^,pointer(fIn),Count); + TrailerBytes(Count); end; end; end; @@ -12999,7 +12883,7 @@ procedure TAESOFBCRC.Decrypt(BufIn, BufOut: pointer; Count: cardinal); pxor xmm7,xmm7 // for safety end else {$endif} begin - inherited Encrypt(BufIn,BufOut,Count); // CV := IV + set fIn,fOut,fCount + inherited Encrypt(BufIn,BufOut,Count); // CV := IV + set fIn,fOut for i := 1 to Count shr 4 do begin TAESContext(AES.Context).DoBlock(AES.Context,fCV,fCV); crcblock(@fMAC.encrypted,pointer(fIn)); // fOut may be = fIn @@ -13008,11 +12892,11 @@ procedure TAESOFBCRC.Decrypt(BufIn, BufOut: pointer; Count: cardinal); inc(fIn); inc(fOut); end; - fCount := fCount and AESBlockMod; - if fCount<>0 then begin - TrailerBytes; + Count := Count and AESBlockMod; + if Count<>0 then begin + TrailerBytes(Count); with fMAC do // includes trailing bytes to the plain crc - PCardinal(@plain)^ := crc32c(PCardinal(@plain)^,pointer(fOut),fCount); + PCardinal(@plain)^ := crc32c(PCardinal(@plain)^,pointer(fOut),Count); end; end; end; @@ -13054,7 +12938,7 @@ procedure TAESOFBCRC.Encrypt(BufIn, BufOut: pointer; Count: cardinal); pxor xmm7,xmm7 // for safety end else {$endif} begin - inherited Encrypt(BufIn,BufOut,Count); // CV := IV + set fIn,fOut,fCount + inherited Encrypt(BufIn,BufOut,Count); // CV := IV + set fIn,fOut for i := 1 to Count shr 4 do begin TAESContext(AES.Context).DoBlock(AES.Context,fCV,fCV); crcblock(@fMAC.plain,pointer(fIn)); // fOut may be = fIn @@ -13063,11 +12947,11 @@ procedure TAESOFBCRC.Encrypt(BufIn, BufOut: pointer; Count: cardinal); inc(fIn); inc(fOut); end; - fCount := fCount and AESBlockMod; - if fCount<>0 then begin - TrailerBytes; + Count := Count and AESBlockMod; + if Count<>0 then begin with fMAC do // includes trailing bytes to the plain crc - PCardinal(@plain)^ := crc32c(PCardinal(@plain)^,pointer(fIn),fCount); + PCardinal(@plain)^ := crc32c(PCardinal(@plain)^,pointer(fIn),Count); + TrailerBytes(Count); end; end; end; @@ -13131,6 +13015,7 @@ procedure AesNiEncryptOFB_128(self: TAESOFB; source, dest: pointer; blockcount: movdqu xmm9,[rdi+16*8] movdqu xmm10,[rdi+16*9] movdqu xmm11,[rdi+16*10] + {$ifdef FPC} align 16 {$else} .align 16 {$endif} @s: movdqu xmm15,dqword ptr [rsi] pxor xmm7,xmm0 aesenc xmm7,xmm1 @@ -13221,6 +13106,7 @@ procedure AesNiEncryptOFB_256(self: TAESOFB; source, dest: pointer; blockcount: movdqu xmm13,[rdi+16*12] movdqu xmm14,[rdi+16*13] add rdi, 16*14 + {$ifdef FPC} align 16 {$else} .align 16 {$endif} @s: movdqu xmm15,[rdi] pxor xmm7,xmm0 aesenc xmm7,xmm1 @@ -13302,16 +13188,16 @@ procedure TAESOFB.Encrypt(BufIn, BufOut: pointer; Count: cardinal); pxor xmm7,xmm7 // for safety end else {$endif} begin - inherited; // CV := IV + set fIn,fOut,fCount + inherited; // CV := IV + set fIn,fOut for i := 1 to Count shr 4 do begin TAESContext(AES.Context).DoBlock(AES.Context,fCV,fCV); XorBlock16(pointer(fIn),pointer(fOut),pointer(@fCV)); inc(fIn); inc(fOut); end; - fCount := fCount and AESBlockMod; - if fCount<>0 then - TrailerBytes; + Count := Count and AESBlockMod; + if Count<>0 then + TrailerBytes(Count); end; end; @@ -13327,7 +13213,7 @@ procedure TAESCTR.Encrypt(BufIn, BufOut: pointer; Count: cardinal); var i,j: integer; tmp: TAESBlock; begin - inherited; // CV := IV + set fIn,fOut,fCount + inherited; // CV := IV + set fIn,fOut for i := 1 to Count shr 4 do begin TAESContext(AES.Context).DoBlock(AES.Context,fCV,tmp); inc(fCV[7]); // counter is in the lower 64 bits, nonce in the upper 64 bits @@ -13539,7 +13425,7 @@ procedure TAESCBC_API.InternalSetMode; procedure TAESCFB_API.InternalSetMode; begin - raise ESynCrypto.Create('CRYPT_MODE_CFB does not work'); + raise ESynCrypto.CreateUTF8('%: CRYPT_MODE_CFB does not work',[self]); fInternalMode := CRYPT_MODE_CFB; end; @@ -13547,7 +13433,7 @@ procedure TAESCFB_API.InternalSetMode; procedure TAESOFB_API.InternalSetMode; begin - raise ESynCrypto.Create('CRYPT_MODE_OFB not implemented by PROV_RSA_AES'); + raise ESynCrypto.CreateUTF8('%: CRYPT_MODE_OFB not implemented by PROV_RSA_AES',[self]); fInternalMode := CRYPT_MODE_OFB; end; @@ -13558,17 +13444,10 @@ procedure TAESOFB_API.InternalSetMode; { TAESLocked } -constructor TAESLocked.Create; -begin - inherited Create; - InitializeCriticalSection(fLock); -end; - destructor TAESLocked.Destroy; begin inherited Destroy; fAES.Done; // mandatory for Padlock - also fill AES buffer with 0 for safety - DeleteCriticalSection(fLock); end; @@ -13604,10 +13483,9 @@ procedure FillSystemRandom(Buffer: PByteArray; Len: integer; AllowBlocking: bool if dev>0 then try i := Len; - repeat - dec(i,FileRead(dev,Buffer^[Len-i],i)); - until i<=0; - fromos := i=0; + if i>32 then + i := 32; // up to 256 bits - see "man urandom" Usage paragraph + fromos := (FileRead(dev,Buffer[0],i)=i) and (Len<=32); // will XOR up to Len finally FileClose(dev); end; @@ -13622,8 +13500,8 @@ procedure FillSystemRandom(Buffer: PByteArray; Len: integer; AllowBlocking: bool if fromos then exit; i := Len; - repeat - SynCommons.FillRandom(@tmp,SizeOf(tmp) shr 2); // SynCommons as fallback + repeat // call Random32() (=RdRand32 or Lecuyer) as fallback/padding + SynCommons.FillRandom(@tmp,SizeOf(tmp) shr 2); if i<=SizeOf(tmp) then begin XorMemory(@Buffer^[Len-i],@tmp,i); break; @@ -13656,7 +13534,7 @@ class function TAESPRNG.GetEntropy(Len: integer; SystemOnly: boolean): RawByteSt try // retrieve some initial entropy from OS SetLength(fromos,Len); - FillSystemRandom(pointer(fromos),len,true); + FillSystemRandom(pointer(fromos),len,{allowblocking=}true); if SystemOnly then begin result := fromos; fromos := ''; @@ -13676,7 +13554,7 @@ class function TAESPRNG.GetEntropy(Len: integer; SystemOnly: boolean): RawByteSt data.i0 := integer(HInstance); // override data.d0d1/h0 data.i1 := integer(GetCurrentThreadId); data.i2 := integer(MainThreadID); - data.i3 := integer(UnixTimeUTC); + data.i3 := integer(UnixMSTimeUTC); SleepHiRes(0); // force non deterministic time shift sha3update; sha3.Update(OSVersionText); @@ -13695,11 +13573,14 @@ procedure TAESPRNG.Seed; try entropy := GetEntropy(128); // 128 bytes is the HMAC_SHA512 key block size PBKDF2_HMAC_SHA512(entropy,ExeVersion.User,fSeedPBKDF2Rounds,key.b); - EnterCriticalSection(fLock); - fAES.EncryptInit(key.Lo,fAESKeySize); - crcblocks(@fCTR,@key.Hi,2); - fBytesSinceSeed := 0; - LeaveCriticalSection(fLock); + fSafe^.Lock; + try + fAES.EncryptInit(key.Lo,fAESKeySize); + crcblocks(@fCTR,@key.Hi,2); + fBytesSinceSeed := 0; + finally + fSafe^.UnLock; + end; finally FillZero(key.b); // avoid the key appear in clear on stack FillZero(entropy); @@ -13729,12 +13610,12 @@ procedure TAESPRNG.FillRandom(out Block: TAESBlock); begin if fBytesSinceSeed>fSeedAfterBytes then Seed; - EnterCriticalSection(fLock); + fSafe^.Lock; TAESContext(fAES.Context).DoBlock(fAES.Context,fCTR.b,Block); IncrementCTR; inc(fBytesSinceSeed,SizeOf(Block)); inc(fTotalBytes,SizeOf(Block)); - LeaveCriticalSection(fLock); + fSafe^.UnLock; end; procedure TAESPRNG.FillRandom(out Buffer: THash256); @@ -13751,7 +13632,7 @@ procedure TAESPRNG.FillRandom(Buffer: pointer; Len: integer); exit; if fBytesSinceSeed>fSeedAfterBytes then Seed; - EnterCriticalSection(fLock); + fSafe^.Lock; for i := 1 to Len shr 4 do begin TAESContext(fAES.Context).DoBlock(fAES.Context,fCTR.b,buf^); IncrementCTR; @@ -13765,7 +13646,7 @@ procedure TAESPRNG.FillRandom(Buffer: pointer; Len: integer); IncrementCTR; MoveFast(rnd,buf^,Len); end; - LeaveCriticalSection(fLock); + fSafe^.UnLock; end; function TAESPRNG.FillRandom(Len: integer): RawByteString; @@ -13813,19 +13694,11 @@ function TAESPRNG.Random64: QWord; end; function TAESPRNG.RandomExt: TSynExtended; -{$ifdef FPC_OR_UNICODE} -const coeff: double = (1.0/$100000000)/$100000000; // 2^-64 -{$else} // circumvent QWord bug on oldest Delphi revisions const coeff: double = (1.0/$80000000)/$100000000; // 2^-63 -{$endif} var block: THash128Rec; begin FillRandom(block.b); - {$ifdef FPC_OR_UNICODE} - result := (block.L xor block.H)*coeff; - {$else} - result := abs(block.Lo xor block.Hi)*coeff; - {$endif} + result := ((block.Lo xor block.Hi) and $7fffffffffffffff)*coeff; end; function TAESPRNG.RandomPassword(Len: integer): RawUTF8; @@ -14268,7 +14141,7 @@ function CryptDataForCurrentUserDPAPI(const Data,AppSecret: RawByteString; Encry end else result := ''; end; -{$endif} +{$endif MSWINDOWS} var __h: THash256; @@ -15060,7 +14933,7 @@ function crc32c_sse42_aesni(crc: cardinal; buf: PAnsiChar; len: cardinal): cardi ja @intel // only call Intel code if worth it shr r8, 3 jz @2 - {$ifdef FPC}align 8{$endif} + {$ifdef FPC} align 8 {$else} .align 8 {$endif} @1: {$ifdef FPC} crc32 rax, qword [rdx] // hash 8 bytes per opcode {$else} @@ -15111,6 +14984,14 @@ initialization if (cfSSE42 in CpuFeatures) and (cfAesNi in CpuFeatures) then crc32c := @crc32c_sse42_aesni; {$endif} +{$ifdef CPUX64} + if cfSSE41 in CpuFeatures then begin // optimized Intel's sha256_sse4.asm ? + if K256AlignedStore='' then + GetMemAligned(K256AlignedStore,@K256,SizeOf(K256),K256Aligned); + if PtrUInt(K256Aligned) and 15<>0 then + K256AlignedStore := ''; // if not properly aligned -> fallback to pascal + end; +{$endif CPUX64} TTextWriter.RegisterCustomJSONSerializerFromTextSimpleType(TypeInfo(TSignAlgo)); TTextWriter.RegisterCustomJSONSerializerFromText(TypeInfo(TSynSignerParams), 'algo:TSignAlgo secret,salt:RawUTF8 rounds:integer'); diff --git a/ThirdParty/mORMot/Source/SynEcc.pas b/ThirdParty/mORMot/Source/SynEcc.pas index 2e4dd430..25cd4459 100644 --- a/ThirdParty/mORMot/Source/SynEcc.pas +++ b/ThirdParty/mORMot/Source/SynEcc.pas @@ -6,7 +6,7 @@ (* This file is part of Synopse framework. - Synopse framework. Copyright (C) 2018 Arnaud Bouchez + Synopse framework. Copyright (C) 2019 Arnaud Bouchez Synopse Informatique - https://synopse.info *** BEGIN LICENSE BLOCK ***** @@ -25,7 +25,7 @@ The Initial Developer of the Original Code is Arnaud Bouchez. - Portions created by the Initial Developer are Copyright (C) 2018 + Portions created by the Initial Developer are Copyright (C) 2019 the Initial Developer. All Rights Reserved. Contributor(s): @@ -91,19 +91,20 @@ interface uses {$ifdef MSWINDOWS} - Windows, // for CriticalSection API inling + Windows, // for CriticalSection API inling {$else} // for GetFileSize emulated API - {$ifdef KYLIX3} - SynKylix, - {$endif} - {$ifdef FPC} - SynFPCLinux, - {$endif} + {$ifdef KYLIX3} + SynKylix, + {$endif} + {$ifdef FPC} + SynFPCLinux, + {$endif} {$endif MSWINDOWS} SysUtils, Classes, Contnrs, SynCommons, + SynTable, SynCrypto; @@ -1259,7 +1260,7 @@ TECCSignatureCertifiedFile = class(TECCSignatureCertified) // based on JSON objects or even plain base-64 encoded JSON strings // - consider using TECCCertificateChainFile from mORMot.pas if you want // to use convenient human-readable JSON serialization in files - TECCCertificateChain = class(TSynPersistentLocked) + TECCCertificateChain = class(TSynPersistentLock) protected fItems: TECCCertificateObjArray; fIsValidCached: boolean; @@ -2086,6 +2087,12 @@ function _isZero(const VLI: TVLI): boolean; {$ifdef HASINLINE}inline;{$endif} result := (VLI[0]=0) and (VLI[1]=0) and (VLI[2]=0) and (VLI[3]=0); end; +function _equals(const Left, Right: TVLI): boolean; {$ifdef HASINLINE}inline;{$endif} +begin + result := (Left[0]=Right[0]) and (Left[1]=Right[1]) and (Left[2]=Right[2]) and + (Left[3]=Right[3]); +end; + // counts the number of bits required for VLI function _numBits(const VLI: TVLI): integer; var i: integer; @@ -2636,7 +2643,7 @@ procedure EccPointDoubleJacobian(var X1, Y1, Z1: TVLI); _modMult_fast(X1, X1, Z1); // t1 = x1^2 - z1^4 _modAdd(Z1, X1, X1, Curve_P_32); // t3 = 2*(x1^2 - z1^4) _modAdd(X1, X1, Z1, Curve_P_32); // t1 = 3*(x1^2 - z1^4) - if GetBit(X1, 0) then begin + if GetBitPtr(@X1, 0) then begin carry := _add(X1, X1, Curve_P_32); _rshift1(X1); X1[NUM_ECC_DIGITS-1] := X1[NUM_ECC_DIGITS-1] or (carry shl 63); @@ -2740,13 +2747,13 @@ procedure EccPointMult(out Output: TEccPoint; const Point: TEccPoint; Scalar: TV Ry[1] := Point.y; _XYcZ_initial_double(Rx[1], Ry[1], Rx[0], Ry[0], InitialZ); for i := _numBits(Scalar) - 2 downto 1 do begin - if GetBit(Scalar, i) then + if GetBitPtr(@Scalar, i) then nb := 0 else nb := 1; _XYcZ_addC(Rx[1-nb], Ry[1-nb], Rx[nb], Ry[nb]); _XYcZ_add(Rx[nb], Ry[nb], Rx[1-nb], Ry[1-nb]); end; - if GetBit(Scalar, 0) then + if GetBitPtr(@Scalar, 0) then nb := 0 else nb := 1; _XYcZ_addC(Rx[1-nb], Ry[1-nb], Rx[nb], Ry[nb]); @@ -2783,7 +2790,7 @@ procedure ModSqrt(var a: TVLI); _add(p1, Curve_P_32, _1); // p1 = curve_p + 1 for i := _numBits(p1) - 1 downto 2 do begin _modSquare_fast(result, result, false); - if GetBit(p1, i) then + if GetBitPtr(@p1, i) then _modMult_fast(result, result, a); end; a := result; @@ -2813,7 +2820,7 @@ function ecc_make_key_pas(out PublicKey: TECCPublicKey; out PrivateKey: TECCPriv TAESPRNG.Fill(THash256(PrivateK)); if tries >= MAX_TRIES then exit; - if _isZero(PrivateK) or (_cmp(PrivateK, _1) = 0) or (_cmp(PrivateK, _11) = 0) then + if _isZero(PrivateK) or _equals(PrivateK, _1) or _equals(PrivateK, _11) then continue; // Make sure the private key is in the range [1, n-1] // For the supported curves, n is always large enough that we only need @@ -2930,7 +2937,7 @@ function ecdsa_sign_pas(const PrivateKey: TECCPrivateKey; const Hash: TEccHash; TAESPRNG.Fill(THash256(k)); if Tries >= MAX_TRIES then exit; - if _isZero(k) or (_cmp(k, _1) = 0) or (_cmp(k, _11) = 0) then + if _isZero(k) or _equals(k, _1) or _equals(k, _11) then continue; if _cmp(Curve_N_32, k) <> 1 then _sub(k, k, Curve_N_32); @@ -2989,10 +2996,10 @@ function ecdsa_verify_pas(const PublicKey: TECCPublicKeyUncompressed; Index := _numBits(u2); if Index > NumBits then NumBits := Index; - if GetBit(u1, NumBits-1) then + if GetBitPtr(@u1, NumBits-1) then Index := 1 else Index := 0; - if GetBit(u2, NumBits-1) then + if GetBitPtr(@u2, NumBits-1) then inc(Index, 2); Point := Points[Index]; rx := Point.x; @@ -3000,10 +3007,10 @@ function ecdsa_verify_pas(const PublicKey: TECCPublicKeyUncompressed; z := _1; for i := NumBits - 2 downto 0 do begin EccPointDoubleJacobian(rx, ry, z); - if GetBit(u1, i) then + if GetBitPtr(@u1, i) then Index := 1 else Index := 0; - if GetBit(u2, i) then + if GetBitPtr(@u2, i) then inc(Index, 2); Point := Points[Index]; if Point <> nil then begin @@ -3705,7 +3712,7 @@ function TECCCertificate.Encrypt(const Plain: RawByteString; finally FillZero(aeskey); FillZero(mackey); - FillcharFast(rndpriv,sizeof(rndpriv),0); + {$ifdef FPC}FillChar{$else}FillCharFast{$endif}(rndpriv,sizeof(rndpriv),0); if dec<>Plain then FillZero(dec); if content<>Plain then @@ -4828,9 +4835,12 @@ function TECCCertificateChain.LoadFromJson(const json: RawUTF8): boolean; tmp: TSynTempBuffer; // private copy begin tmp.Init(json); - result := (DynArrayLoadJSON(values,tmp.buf,TypeInfo(TRawUTF8DynArray))<>nil) and - LoadFromArray(values); - tmp.Done; + try + result := (DynArrayLoadJSON(values,tmp.buf,TypeInfo(TRawUTF8DynArray))<>nil) and + LoadFromArray(values); + finally + tmp.Done; + end; end; function TECCCertificateChain.LoadFromArray(const values: TRawUTF8DynArray): boolean; @@ -5318,7 +5328,7 @@ procedure TECDHEProtocolClient.ComputeHandshake(out aClient: TECDHEFrameClient); begin if fAES[false]<>nil then raise EECCException.CreateUTF8('%.ComputeHandshake already called',[self]); - FillCharFast(aClient,sizeof(aClient),0); + {$ifdef FPC}FillChar{$else}FillCharFast{$endif}(aClient,sizeof(aClient),0); aClient.algo := fAlgo; TAESPRNG.Main.FillRandom(fRndA); aClient.RndA := fRndA; @@ -5417,7 +5427,7 @@ function TECDHEProtocolServer.ComputeHandshake(const aClient: TECDHEFrameClient; if fAlgo.auth<>authServer then if not Verify(@aClient,sizeof(aClient),aClient.QCA,result) then exit; - FillCharFast(aServer,sizeof(aServer),0); + {$ifdef FPC}FillChar{$else}FillCharFast{$endif}(aServer,sizeof(aServer),0); aServer.algo := fAlgo; aServer.RndA := fRndA; TAESPRNG.Main.FillRandom(fRndB); diff --git a/ThirdParty/mORMot/Source/SynEcc32asm.inc b/ThirdParty/mORMot/Source/SynEcc32asm.inc index d7536eb8..b86e347b 100644 --- a/ThirdParty/mORMot/Source/SynEcc32asm.inc +++ b/ThirdParty/mORMot/Source/SynEcc32asm.inc @@ -6,7 +6,7 @@ This file is part of Synopse framework. - Synopse framework. Copyright (C) 2018 Arnaud Bouchez + Synopse framework. Copyright (C) 2019 Arnaud Bouchez Synopse Informatique - https://synopse.info Using secp256r1 curve from "simple and secure ECDH and ECDSA library" diff --git a/ThirdParty/mORMot/Source/SynLZ.pas b/ThirdParty/mORMot/Source/SynLZ.pas index b661fabb..ffba737c 100644 --- a/ThirdParty/mORMot/Source/SynLZ.pas +++ b/ThirdParty/mORMot/Source/SynLZ.pas @@ -5,7 +5,7 @@ { This file is part of Synopse SynLZ Compression. - Synopse SynLZ Compression. Copyright (C) 2018 Arnaud Bouchez + Synopse SynLZ Compression. Copyright (C) 2019 Arnaud Bouchez Synopse Informatique - https://synopse.info *** BEGIN LICENSE BLOCK ***** @@ -24,7 +24,7 @@ The Initial Developer of the Original Code is Arnaud Bouchez. - Portions created by the Initial Developer are Copyright (C) 2018 + Portions created by the Initial Developer are Copyright (C) 2019 the Initial Developer. All Rights Reserved. Contributor(s): diff --git a/ThirdParty/mORMot/Source/SynMustache.pas b/ThirdParty/mORMot/Source/SynMustache.pas new file mode 100644 index 00000000..05cbddad --- /dev/null +++ b/ThirdParty/mORMot/Source/SynMustache.pas @@ -0,0 +1,1441 @@ +/// Logic-less {{mustache}} template rendering +// - this unit is a part of the freeware Synopse mORMot framework, +// licensed under a MPL/GPL/LGPL tri-license; version 1.18 +unit SynMustache; + +{ + This file is part of Synopse mORMot framework. + + Synopse mORMot framework. Copyright (C) 2019 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) 2019 + the Initial Developer. All Rights Reserved. + + Contributor(s): + - shura1990 + + 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 ***** + + + Version 1.18 + - initial revision + +} + + +{$I Synopse.inc} // define HASINLINE USETYPEINFO CPU32 CPU64 OWNNORMTOUPPER + +interface + +uses + {$ifdef HASINLINENOTX86} + {$ifdef MSWINDOWS}Windows,{$endif} // for Lock/UnLock inlining + {$endif} + Variants, + SysUtils, + SynCommons; + + +type + /// exception raised during process of a {{mustache}} template + ESynMustache = class(ESynException); + + /// identify the {{mustache}} tag kind + // - mtVariable if the tag is a variable - e.g. {{myValue}} - or an Expression + // Helper - e.g. {{helperName valueName}} + // - mtVariableUnescaped to unescape the variable HTML - e.g. + // {{{myRawValue}}} or {{& name}} + // - mtSection and mtInvertedSection for sections beginning - e.g. + // {{#person}} or {{^person}} + // - mtSectionEnd for sections ending - e.g. {{/person}} + // - mtComment for comments - e.g. {{! ignore me}} + // - mtPartial for partials - e.g. {{> next_more}} + // - mtSetPartial for setting an internal partial - e.g. + // {{=}} - + // Warning: current implementation only supports two character delimiters + // - mtTranslate for content i18n via a callback - e.g. {{"English text}} + // - mtText for all text that appears outside a symbol + TSynMustacheTagKind = ( + mtVariable, mtVariableUnescape, + mtSection, mtInvertedSection, mtSectionEnd, + mtComment, mtPartial, mtSetPartial, mtSetDelimiter, mtTranslate, mtText); + + /// store a {{mustache}} tag + TSynMustacheTag = record + /// the kind of the tag + Kind: TSynMustacheTagKind; + /// points to the mtText buffer start + // - main template's text is not allocated as a separate string during + // parsing, but will rather be copied directly from the template memory + TextStart: PUTF8Char; + /// stores the mtText buffer length + TextLen: integer; + /// the index in Tags[] of the other end of this section + // - either the index of mtSectionEnd for mtSection/mtInvertedSection + // - or the index of mtSection/mtInvertedSection for mtSectionEnd + SectionOppositeIndex: integer; + /// the tag content, excluding trailing {{ }} and corresponding symbol + // - is not set for mtText nor mtSetDelimiter + Value: RawUTF8; + end; + + /// store all {{mustache}} tags of a given template + TSynMustacheTagDynArray = array of TSynMustacheTag; + + /// states the section content according to a given value + // - msNothing for false values or empty lists + // - msSingle for non-false values but not a list + // - msList for non-empty lists + TSynMustacheSectionType = (msNothing,msSingle,msSinglePseudo,msList); + + TSynMustache = class; + + /// callback signature used to process an Expression Helper variable + // - i.e. {{helperName value}} tags + // - returned value will be used to process as replacement of a single {{tag}} + TSynMustacheHelperEvent = procedure(const Value: variant; out result: variant) of object; + + /// used to store a registered Expression Helper implementation + TSynMustacheHelper = record + /// the Expression Helper name + Name: RawUTF8; + /// the corresponding callback to process the tag + Event: TSynMustacheHelperEvent; + end; + + /// used to store all registered Expression Helpers + // - i.e. {{helperName value}} tags + // - use TSynMustache.HelperAdd/HelperDelete class methods to manage the list + // or retrieve standard helpers via TSynMustache.HelpersGetStandardList + TSynMustacheHelpers = array of TSynMustacheHelper; + + /// handle {{mustache}} template rendering context, i.e. all values + // - this abstract class should not be used directly, but rather any + // other overridden class + TSynMustacheContext = class + protected + fContextCount: integer; + fWriter: TTextWriter; + fOwner: TSynMustache; + fEscapeInvert: boolean; + fHelpers: TSynMustacheHelpers; + fOnStringTranslate: TOnStringTranslate; + procedure TranslateBlock(Text: PUTF8Char; TextLen: Integer); virtual; + procedure PopContext; virtual; abstract; + procedure AppendValue(const ValueName: RawUTF8; UnEscape: boolean); + virtual; abstract; + function AppendSection(const ValueName: RawUTF8): TSynMustacheSectionType; + virtual; abstract; + function GotoNextListItem: boolean; + virtual; abstract; + public + /// initialize the rendering context for the given text writer + constructor Create(Owner: TSynMustache; WR: TTextWriter); + /// the registered Expression Helpers, to handle {{helperName value}} tags + // - use TSynMustache.HelperAdd/HelperDelete class methods to manage the list + // or retrieve standard helpers via TSynMustache.HelpersGetStandardList + property Helpers: TSynMustacheHelpers read fHelpers write fHelpers; + /// access to the {{"English text}} translation callback + property OnStringTranslate: TOnStringTranslate + read fOnStringTranslate write fOnStringTranslate; + /// read-only access to the associated text writer instance + property Writer: TTextWriter read fWriter; + /// invert the HTML characters escaping process + // - by default, {{value}} will escape value chars, and {{{value}} won't + // - set this property to true to force {{value}} NOT to escape HTML chars + // and {{{value}} escaping chars (may be useful e.g. for code generation) + property EscapeInvert: boolean read fEscapeInvert write fEscapeInvert; + end; + + /// handle {{mustache}} template rendering context from a custom variant + // - the context is given via a custom variant type implementing + // TSynInvokeableVariantType.Lookup, e.g. TDocVariant or TSMVariant + TSynMustacheContextVariant = class(TSynMustacheContext) + protected + fContext: array of record + Document: TVarData; + DocumentType: TSynInvokeableVariantType; + ListCount: integer; + ListCurrent: integer; + ListCurrentDocument: TVarData; + ListCurrentDocumentType: TSynInvokeableVariantType; + end; + fTempGetValueFromContextHelper: TVariantDynArray; + procedure PushContext(aDoc: TVarData); + procedure PopContext; override; + procedure AppendValue(const ValueName: RawUTF8; UnEscape: boolean); override; + function AppendSection(const ValueName: RawUTF8): TSynMustacheSectionType; override; + function GotoNextListItem: boolean; override; + function GetDocumentType(const aDoc: TVarData): TSynInvokeableVariantType; + function GetValueFromContext(const ValueName: RawUTF8; var Value: TVarData): TSynMustacheSectionType; + function GetValueCopyFromContext(const ValueName: RawUTF8): variant; + procedure AppendVariant(const Value: variant; UnEscape: boolean); + public + /// initialize the context from a custom variant document + // - note that the aDocument instance shall be available during all + // lifetime of this TSynMustacheContextVariant instance + // - you should not use this constructor directly, but the + // corresponding TSynMustache.Render*() methods + constructor Create(Owner: TSynMustache; WR: TTextWriter; SectionMaxCount: integer; + const aDocument: variant); + end; + + /// maintain a list of {{mustache}} partials + // - this list of partials template could be supplied to TSynMustache.Render() + // method, to render {{>partials}} as expected + // - using a dedicated class allows to share the partials between execution + // context, without recurring to non SOLID global variables + // - you may also define "internal" partials, e.g. {{partialName}} template + procedure Add(const aName,aTemplate: RawUTF8); overload; + /// register a {{>partialName}} template + procedure Add(const aName: RawUTF8; aTemplateStart,aTemplateEnd: PUTF8Char); overload; + /// delete the partials + destructor Destroy; override; + end; + + /// stores one {{mustache}} pre-rendered template + // - once parsed, a template will be stored in this class instance, to be + // rendered lated via the Render() method + // - you can use the Parse() class function to maintain a shared cache of + // parsed templates + // - implements all official mustache specifications, and some extensions + // - handles {{.}} pseudo-variable for the current context object (very + // handy when looping through a simple list, for instance) + // - handles {{-index}} pseudo-variable for the current context array index + // (1-based value) so that e.g. + // "My favorite things:\n{{#things}}{{-index}}. {{.}}\n{{/things}}" + // over {things:["Peanut butter", "Pen spinning", "Handstands"]} renders as + // "My favorite things:\n1. Peanut butter\n2. Pen spinning\n3. Handstands\n" + // - you could use {{-index0}} for 0-based index value + // - handles -first -last and -odd pseudo-section keys, e.g. + // "{{#things}}{{^-first}}, {{/-first}}{{.}}{{/things}}" + // over {things:["one", "two", "three"]} renders as 'one, two, three' + // - allows inlined partial templates , to be defined e.g. as + // {{ <= >= <> operators over two values: + // $ {{#if .,"=",123}} {{#if Total,">",1000}} {{#if info,"<>",""}} + // which may be shortened as such: + // $ {{#if .=123}} {{#if Total>1000}} {{#if info<>""}} + class function HelpersGetStandardList: TSynMustacheHelpers; overload; + /// returns a list of most used static Expression Helpers, adding some + // custom callbacks + // - is just a wrapper around HelpersGetStandardList and HelperAdd() + class function HelpersGetStandardList(const aNames: array of RawUTF8; + const aEvents: array of TSynMustacheHelperEvent): TSynMustacheHelpers; overload; + + /// renders the {{mustache}} template into a destination text buffer + // - the context is given via our abstract TSynMustacheContext wrapper + // - the rendering extended in fTags[] is supplied as parameters + // - you can specify a list of partials via TSynMustachePartials.CreateOwned + procedure RenderContext(Context: TSynMustacheContext; TagStart,TagEnd: integer; + Partials: TSynMustachePartials; NeverFreePartials: boolean); + /// renders the {{mustache}} template from a variant defined context + // - the context is given via a custom variant type implementing + // TSynInvokeableVariantType.Lookup, e.g. TDocVariant or TSMVariant + // - you can specify a list of partials via TSynMustachePartials.CreateOwned, + // a list of Expression Helpers, or a custom {{"English text}} callback + // - can be used e.g. via a TDocVariant: + // !var mustache := TSynMustache; + // ! doc: variant; + // ! html: RawUTF8; + // !begin + // ! mustache := TSynMustache.Parse( + // ! 'Hello {{name}}'#13#10'You have just won {{value}} dollars!'); + // ! TDocVariant.New(doc); + // ! doc.name := 'Chris'; + // ! doc.value := 10000; + // ! html := mustache.Render(doc); + // ! // here html='Hello Chris'#13#10'You have just won 10000 dollars!' + // - you can also retrieve the context from an ORM query of mORMot.pas: + // ! dummy := TSynMustache.Parse( + // ! '{{#items}}'#13#10'{{Int}}={{Test}}'#13#10'{{/items}}').Render( + // ! aClient.RetrieveDocVariantArray(TSQLRecordTest,'items','Int,Test')); + // - set EscapeInvert = true to force {{value}} NOT to escape HTML chars + // and {{{value}} escaping chars (may be useful e.g. for code generation) + function Render(const Context: variant; Partials: TSynMustachePartials=nil; + Helpers: TSynMustacheHelpers=nil; OnTranslate: TOnStringTranslate=nil; + EscapeInvert: boolean=false): RawUTF8; + /// renders the {{mustache}} template from JSON defined context + // - the context is given via a JSON object, defined from UTF-8 buffer + // - you can specify a list of partials via TSynMustachePartials.CreateOwned, + // a list of Expression Helpers, or a custom {{"English text}} callback + // - is just a wrapper around Render(_JsonFast()) + // - you can write e.g. with the extended JSON syntax: + // ! html := mustache.RenderJSON('{things:["one", "two", "three"]}'); + // - set EscapeInvert = true to force {{value}} NOT to escape HTML chars + // and {{{value}} escaping chars (may be useful e.g. for code generation) + function RenderJSON(const JSON: RawUTF8; Partials: TSynMustachePartials=nil; + Helpers: TSynMustacheHelpers=nil; OnTranslate: TOnStringTranslate=nil; + EscapeInvert: boolean=false): RawUTF8; overload; + /// renders the {{mustache}} template from JSON defined context + // - the context is given via a JSON object, defined with parameters + // - you can specify a list of partials via TSynMustachePartials.CreateOwned, + // a list of Expression Helpers, or a custom {{"English text}} callback + // - is just a wrapper around Render(_JsonFastFmt()) + // - you can write e.g. with the extended JSON syntax: + // ! html := mustache.RenderJSON('{name:?,value:?}',[],['Chris',10000]); + // - set EscapeInvert = true to force {{value}} NOT to escape HTML chars + // and {{{value}} escaping chars (may be useful e.g. for code generation) + function RenderJSON(const JSON: RawUTF8; const Args,Params: array of const; + Partials: TSynMustachePartials=nil; Helpers: TSynMustacheHelpers=nil; + OnTranslate: TOnStringTranslate=nil; + EscapeInvert: boolean=false): RawUTF8; overload; + + /// read-only access to the raw {{mustache}} template content + property Template: RawUTF8 read fTemplate; + /// the maximum possible number of nested contexts + property SectionMaxCount: Integer read fSectionMaxCount; + end; + + +const + /// this constant can be used to define as JSON a tag value + NULL_OR_TRUE: array[boolean] of RawUTF8 = ('null','true'); + + /// this constant can be used to define as JSON a tag value as separator + NULL_OR_COMMA: array[boolean] of RawUTF8 = ('null','","'); + + +implementation + +function KindToText(Kind: TSynMustacheTagKind): PShortString; +begin + result := GetEnumName(TypeInfo(TSynMustacheTagKind),ord(Kind)); +end; + +type + TSynMustacheParser = class + protected + fTagStart, fTagStop: word; + fPos, fPosMin, fPosMax, fPosTagStart: PUTF8Char; + fTagCount: integer; + fTemplate: TSynMustache; + fScanStart, fScanEnd: PUTF8Char; + function Scan(ExpectedTag: Word): boolean; + procedure AddTag(aKind: TSynMustacheTagKind; + aStart: PUTF8Char=nil; aEnd: PUTF8Char=nil); + public + constructor Create(Template: TSynMustache; const DelimiterStart, DelimiterStop: RawUTF8); + procedure Parse(P,PEnd: PUTF8Char); + end; + + TSynMustacheCache = class(TRawUTF8ListHashedLocked) + public + function Parse(const aTemplate: RawUTF8): TSynMustache; + function UnParse(const aTemplate: RawUTF8): boolean; + end; + +var + SynMustacheCache: TSynMustacheCache = nil; + + + +{ TSynMustacheParser } + +procedure TSynMustacheParser.AddTag(aKind: TSynMustacheTagKind; + aStart, aEnd: PUTF8Char); +begin + if (aStart=nil) or (aEnd=nil) then begin + aStart := fScanStart; + aEnd := fScanEnd; + case aKind of + mtComment, mtSection, mtSectionEnd, mtInvertedSection, mtSetDelimiter, mtPartial: begin + // (indented) standalone lines should be removed from the template + if aKind<>mtPartial then + while (fPosTagStart>fPosMin) and (fPosTagStart[-1] in [' ',#9]) do + dec(fPosTagStart); // ignore any indentation chars + if (fPosTagStart=fPosMin) or (fPosTagStart[-1]=#$0A) then + // tag starts on a new line -> check if ends on the same line + if (fPos>fPosMax) or (fPos^=#$0A) or (PWord(fPos)^=$0A0D) then begin + if fPos<=fPosMax then + if fPos^=#$0A then + inc(fPos) else + if PWord(fPos)^=$0A0D then + inc(fPos,2); + if fTagCount>0 then // remove any indentation chars from previous text + with fTemplate.fTags[fTagCount-1] do + if Kind=mtText then + while (TextLen>0) and (TextStart[TextLen-1] in [' ',#9]) do + dec(TextLen); + end; + end; + end; + end; + if aEnd<=aStart then + exit; + if fTagCount>=length(fTemplate.fTags) then + SetLength(fTemplate.fTags,NextGrow(fTagCount)); + with fTemplate.fTags[fTagCount] do begin + Kind := aKind; + SectionOppositeIndex := -1; + case aKind of + mtText, mtComment, mtTranslate: begin + TextStart := aStart; + TextLen := aEnd-aStart; + end; + else begin + TextStart := fPosTagStart; + TextLen := aEnd-fPosTagStart; + // superfluous in-tag whitespace should be ignored + while (aStartaStart) and (aEnd[-1]<=' ') do dec(aEnd); + if aEnd=aStart then + raise ESynMustache.CreateFmt('Void %s identifier',[KindToText(aKind)^]); + SetString(Value,PAnsiChar(aStart),aEnd-aStart); + end; + end; + end; + inc(fTagCount); +end; + +constructor TSynMustacheParser.Create(Template: TSynMustache; + const DelimiterStart, DelimiterStop: RawUTF8); +begin + fTemplate := Template; + if length(DelimiterStart)<>2 then + raise ESynMustache.CreateFmt('DelimiterStart="%s"',[DelimiterStart]); + if length(DelimiterStop)<>2 then + raise ESynMustache.CreateFmt('DelimiterStop="%s"',[DelimiterStop]); + fTagStart := PWord(DelimiterStart)^; + fTagStop := PWord(DelimiterStop)^; +end; + +function GotoNextTag(P,PMax: PUTF8Char; ExpectedTag: Word): PUTF8Char; +begin + if PExpectedTag then begin + inc(P); + if P0) and IdemPropNameU(finish,pointer(start),i-1); + end; +end; + +procedure TSynMustacheParser.Parse(P, PEnd: PUTF8Char); +var Kind: TSynMustacheTagKind; + Symbol: AnsiChar; + i,j,secCount,secLevel: integer; +begin + secCount := 0; + if P=nil then + exit; + fPos := P; + fPosMin := P; + fPosMax := PEnd-1; + repeat + if not Scan(fTagStart) then + break; + fPosTagStart := fScanEnd; + AddTag(mtText); + if fPos>=fPosMax then + break; + Symbol := fPos^; + case Symbol of + '=': Kind := mtSetDelimiter; + '{', + '&': Kind := mtVariableUnescape; + '#': Kind := mtSection; + '^': Kind := mtInvertedSection; + '/': Kind := mtSectionEnd; + '!': Kind := mtComment; + '>': Kind := mtPartial; + '<': Kind := mtSetPartial; + '"': Kind := mtTranslate; + else Kind := mtVariable; + end; + if Kind<>mtVariable then + inc(fPos); + if not Scan(fTagStop) then + raise ESynMustache.CreateFmt('Unfinished {{tag "%s"',[fPos]); + case Kind of + mtSetDelimiter: begin + if (fScanEnd-fScanStart<>6) or (fScanEnd[-1]<>'=') then + raise ESynMustache.Create('mtSetDelimiter syntax is e.g. {{=<% %>=}}'); + fTagStart := PWord(fScanStart)^; + fTagStop := PWord(fScanStart+3)^; + continue; // do not call AddTag(mtSetDelimiter) + end; + mtVariableUnescape: + if (Symbol='{') and (fTagStop=32125) and (PWord(fPos-1)^=32125) then + inc(fPos); // {{{name}}} -> point after }}} + end; + AddTag(Kind); + until false; + AddTag(mtText,fPos,fPosMax+1); + for i := 0 to fTagCount-1 do + with fTemplate.fTags[i] do + case Kind of + mtSection, mtInvertedSection, mtSetPartial: begin + inc(secCount); + if secCount>fTemplate.fSectionMaxCount then + fTemplate.fSectionMaxCount := secCount; + secLevel := 1; + for j := i+1 to fTagCount-1 do + case fTemplate.fTags[j].Kind of + mtSection, mtInvertedSection, mtSetPartial: + inc(secLevel); + mtSectionEnd: begin + dec(secLevel); + if secLevel=0 then + if SectionNameMatch(Value,fTemplate.fTags[j].Value) then begin + fTemplate.fTags[j].SectionOppositeIndex := i; + SectionOppositeIndex := j; + if Kind=mtSetPartial then begin + if fTemplate.fInternalPartials=nil then + fTemplate.fInternalPartials := TSynMustachePartials.Create; + fTemplate.fInternalPartials.Add(Value, + TextStart+TextLen+2,fTemplate.fTags[j].TextStart); + end; + break; + end else + raise ESynMustache.CreateFmt('Got {{/%s}}, expected {{/%s}}', + [Value,fTemplate.fTags[j].Value]); + end; + end; + if SectionOppositeIndex<0 then + raise ESynMustache.CreateFmt('Missing section end {{/%s}}',[Value]); + end; + mtSectionEnd: begin + dec(secCount); + if SectionOppositeIndex<0 then + raise ESynMustache.CreateFmt('Unexpected section end {{/%s}}',[Value]); + end; + end; + SetLength(fTemplate.fTags,fTagCount); +end; + + +{ TSynMustacheCache } + +function TSynMustacheCache.Parse(const aTemplate: RawUTF8): TSynMustache; +var i: integer; +begin + fSafe.Lock; + try + i := IndexOf(aTemplate); // fast instance retrieval from shared cache + if i>=0 then begin + result := TSynMustache(Objects[i]); + exit; + end; + result := TSynMustache.Create(aTemplate); + AddObject(aTemplate,result); + finally + fSafe.UnLock; + end; +end; + +function TSynMustacheCache.UnParse(const aTemplate: RawUTF8): boolean; +var i: integer; +begin + result := false; + if self=nil then + exit; + fSafe.Lock; + try + i := IndexOf(aTemplate); + if i>=0 then begin + Delete(i); + result := true; + end; + finally + fSafe.UnLock; + end; +end; + + +{ TSynMustache } + +class function TSynMustache.Parse(const aTemplate: RawUTF8): TSynMustache; +begin + if SynMustacheCache=nil then + GarbageCollectorFreeAndNil(SynMustacheCache,TSynMustacheCache.Create(true)); + result := SynMustacheCache.Parse(aTemplate); +end; + +class function TSynMustache.UnParse(const aTemplate: RawUTF8): boolean; +begin + result := SynMustacheCache.UnParse(aTemplate); +end; + +class function TSynMustache.TryRenderJson(const aTemplate, aJSON: RawUTF8; + out aContent: RawUTF8): boolean; +var mus: TSynMustache; +begin + if aTemplate<>'' then + try + mus := Parse(aTemplate); + aContent := mus.RenderJSON(aJSON); + result := true; + except + result := false; + end else + result := false; +end; + +constructor TSynMustache.Create(const aTemplate: RawUTF8); +begin + Create(pointer(aTemplate),length(aTemplate)); +end; + +constructor TSynMustache.Create(aTemplate: PUTF8Char; aTemplateLen: integer); +begin + inherited Create; + fTemplate := aTemplate; + with TSynMustacheParser.Create(self,'{{','}}') do + try + Parse(aTemplate,aTemplate+aTemplateLen); + finally + Free; + end; +end; + +type + TSynMustacheProcessSection = procedure of object; + +procedure TSynMustache.RenderContext(Context: TSynMustacheContext; + TagStart,TagEnd: integer; Partials: TSynMustachePartials; NeverFreePartials: boolean); +var partial: TSynMustache; +begin + try + while TagStart<=TagEnd do begin + with fTags[TagStart] do + case Kind of + mtText: + if TextLen<>0 then // may be 0 e.g. for standalone without previous Line + Context.fWriter.AddNoJSONEscape(TextStart,TextLen); + mtVariable: + Context.AppendValue(Value,false); + mtVariableUnescape: + Context.AppendValue(Value,true); + mtSection: + case Context.AppendSection(Value) of + msNothing: begin // e.g. for no key, false value, or empty list + TagStart := SectionOppositeIndex; + continue; // ignore whole section + end; + msList: begin + while Context.GotoNextListItem do + RenderContext(Context,TagStart+1,SectionOppositeIndex-1,Partials,true); + TagStart := SectionOppositeIndex; + continue; // ignore whole section since we just rendered it as a list + end; + // msSingle,msSinglePseudo: process the section once with current context + end; + mtInvertedSection: // display section for no key, false value, or empty list + if Context.AppendSection(Value)<>msNothing then begin + TagStart := SectionOppositeIndex; + continue; // ignore whole section + end; + mtSectionEnd: + if (fTags[SectionOppositeIndex].Kind in [mtSection,mtInvertedSection]) and + (Value[1]<>'-') and (PosEx(' ',fTags[SectionOppositeIndex].Value)=0) then + Context.PopContext; + mtComment: + ; // just ignored + mtPartial: begin + partial := fInternalPartials.GetPartial(Value); + if (partial=nil) and (Context.fOwner<>self) then // recursive call + partial := Context.fOwner.fInternalPartials.GetPartial(Value); + if (partial=nil) and (Partials<>nil) then + partial := Partials.GetPartial(Value); + if partial<>nil then + partial.RenderContext(Context,0,high(partial.fTags),Partials,true); + end; + mtSetPartial: + TagStart := SectionOppositeIndex; // ignore whole internal {{0 then + Context.TranslateBlock(TextStart,TextLen); + else + raise ESynMustache.CreateFmt('Kind=%s not implemented yet', + [KindToText(fTags[TagStart].Kind)^]); + end; + inc(TagStart); + end; + finally + if (Partials<>nil) and (Partials.fOwned) and not NeverFreePartials then + Partials.Free; + end; +end; + +function TSynMustache.Render(const Context: variant; + Partials: TSynMustachePartials; Helpers: TSynMustacheHelpers; + OnTranslate: TOnStringTranslate; EscapeInvert: boolean): RawUTF8; +var W: TTextWriter; + Ctxt: TSynMustacheContext; +begin + W := TTextWriter.CreateOwnedStream(4096); + try + Ctxt := TSynMustacheContextVariant.Create(self,W,SectionMaxCount,Context); + try + Ctxt.Helpers := Helpers; + Ctxt.OnStringTranslate := OnTranslate; + Ctxt.EscapeInvert := EscapeInvert; + RenderContext(Ctxt,0,high(fTags),Partials,false); + W.SetText(result); + finally + Ctxt.Free; + end; + finally + W.Free; + end; +end; + +function TSynMustache.RenderJSON(const JSON: RawUTF8; + Partials: TSynMustachePartials; Helpers: TSynMustacheHelpers; + OnTranslate: TOnStringTranslate; EscapeInvert: boolean): RawUTF8; +var context: variant; +begin + _Json(JSON,context,JSON_OPTIONS[true]); + result := Render(context,Partials,Helpers,OnTranslate,EscapeInvert); +end; + +function TSynMustache.RenderJSON(const JSON: RawUTF8; const Args, + Params: array of const; Partials: TSynMustachePartials; + Helpers: TSynMustacheHelpers; OnTranslate: TOnStringTranslate; + EscapeInvert: boolean): RawUTF8; +var context: variant; +begin + _Json(FormatUTF8(JSON,Args,Params,true),context,JSON_OPTIONS[true]); + result := Render(context,Partials,Helpers,OnTranslate,EscapeInvert); +end; + +destructor TSynMustache.Destroy; +begin + FreeAndNil(fInternalPartials); + inherited; +end; + +class procedure TSynMustache.HelperAdd(var Helpers: TSynMustacheHelpers; + const aName: RawUTF8; aEvent: TSynMustacheHelperEvent); +var n,i: integer; +begin + n := length(Helpers); + for i := 0 to n-1 do + if IdemPropNameU(Helpers[i].Name,aName) then begin + Helpers[i].Event := aEvent; + exit; + end; + SetLength(Helpers,n+1); + Helpers[n].Name := aName; + Helpers[n].Event := aEvent; +end; + +class procedure TSynMustache.HelperAdd(var Helpers: TSynMustacheHelpers; + const aNames: array of RawUTF8; const aEvents: array of TSynMustacheHelperEvent); +var n,count,i: integer; +begin + n := length(aNames); + if n<>length(aEvents) then + exit; + count := length(Helpers); + SetLength(Helpers,count+n); + for i := 0 to n-1 do + with Helpers[count+i] do begin + Name := aNames[i]; + Event := aEvents[i]; + end; +end; + +class procedure TSynMustache.HelperDelete(var Helpers: TSynMustacheHelpers; + const aName: RawUTF8); +var n,i,j: integer; +begin + n := length(Helpers); + for i := 0 to n-1 do + if IdemPropNameU(Helpers[i].Name,aName) then begin + for j := i to n-2 do + Helpers[j] := Helpers[j+1]; + SetLength(Helpers,n-1); + exit; + end; +end; + +class function TSynMustache.HelperFind(const Helpers: TSynMustacheHelpers; + aName: PUTF8Char; aNameLen: integer): integer; +begin + for result := 0 to length(Helpers)-1 do + if IdemPropNameU(Helpers[result].Name,aName,aNameLen) then + exit; + result := -1; +end; + +var + HelpersStandardList: TSynMustacheHelpers; + +class function TSynMustache.HelpersGetStandardList: TSynMustacheHelpers; +begin + if HelpersStandardList=nil then + HelperAdd(HelpersStandardList, + ['DateTimeToText','DateToText','DateFmt','TimeLogToText','JSONQuote','JSONQuoteURI', + 'ToJSON','WikiToHtml','BlobToBase64','EnumTrim','EnumTrimRight','PowerOfTwo', + 'Equals','If','NewGUID','ExtractFileName','Lower','Upper'], + [DateTimeToText,DateToText,DateFmt,TimeLogToText,JSONQuote,JSONQuoteURI, + ToJSON,WikiToHtml,BlobToBase64,EnumTrim,EnumTrimRight,PowerOfTwo, + Equals_,If_,NewGUID,ExtractFileName,Lower,Upper]); + result := HelpersStandardList; +end; + +class function TSynMustache.HelpersGetStandardList(const aNames: array of RawUTF8; + const aEvents: array of TSynMustacheHelperEvent): TSynMustacheHelpers; +begin + result := HelpersGetStandardList; + HelperAdd(result,aNames,aEvents); +end; + +class procedure TSynMustache.DateTimeToText(const Value: variant; out result: variant); +var Time: TTimeLogBits; + dt: TDateTime; +begin + if VariantToDateTime(Value,dt) then begin + Time.From(dt,false); + result := Time.i18nText; + end else + SetVariantNull(result); +end; + +class procedure TSynMustache.DateToText(const Value: variant; out result: variant); +var Time: TTimeLogBits; + dt: TDateTime; +begin + if VariantToDateTime(Value,dt) then begin + Time.From(dt,true); + result := Time.i18nText; + end else + SetVariantNull(result); +end; + +class procedure TSynMustache.DateFmt(const Value: variant; out result: variant); +var dt: TDateTime; +begin // {{DateFmt DateValue,"dd/mm/yyy"}} + with _Safe(Value)^ do + if (Kind=dvArray) and (Count=2) and VariantToDateTime(Values[0],dt) then + result := FormatDateTime(Values[1],dt) else + SetVariantNull(result); +end; + +class procedure TSynMustache.TimeLogToText(const Value: variant; out result: variant); +var Time: TTimeLogBits; +begin + if VariantToInt64(Value,Time.Value) then + result := Time.i18nText else + SetVariantNull(result); +end; + +class procedure TSynMustache.ToJSON(const Value: variant; out result: variant); +begin + RawUTF8ToVariant(JSONReformat(VariantToUTF8(Value)),result); +end; + +class procedure TSynMustache.JSONQuote(const Value: variant; out result: variant); +var json: RawUTF8; +begin + QuotedStrJSON(VariantToUTF8(Value),json); + RawUTF8ToVariant(json,result); +end; + +class procedure TSynMustache.JSONQuoteURI(const Value: variant; out result: variant); +var json: RawUTF8; +begin + QuotedStrJSON(VariantToUTF8(Value),json); + RawUTF8ToVariant(UrlEncode(json),result); +end; + +class procedure TSynMustache.WikiToHtml(const Value: variant; out result: variant); +var txt: RawUTF8; +begin + txt := VariantToUTF8(Value); + if txt<>'' then + with TTextWriter.CreateOwnedStream do + try + AddHtmlEscapeWiki(pointer(txt)); + SetText(txt); + finally + Free; + end; + RawUTF8ToVariant(txt,result); +end; + +class procedure TSynMustache.BlobToBase64(const Value: variant; out result: variant); +var tmp: RawUTF8; + wasString: boolean; +begin + VariantToUTF8(Value,tmp,wasString); + if wasString and (pointer(tmp)<>nil) then begin + if PInteger(tmp)^ and $00ffffff=JSON_BASE64_MAGIC then + delete(tmp,1,3); + RawUTF8ToVariant(tmp,result); + end else + result := Value; +end; + +class procedure TSynMustache.EnumTrim(const Value: variant; out result: variant); +var tmp: RawUTF8; + wasString: boolean; + short: PUTF8Char; +begin + VariantToUTF8(Value,tmp,wasString); + short := TrimLeftLowerCase(tmp); + RawUTF8ToVariant(short,StrLen(short),result); +end; + +class procedure TSynMustache.EnumTrimRight(const Value: variant; out result: variant); +var tmp: RawUTF8; + wasString: boolean; + i,L: integer; +begin + VariantToUTF8(Value,tmp,wasString); + L := length(tmp); + for i := 1 to L do + if not (tmp[i] in ['a'..'z']) then begin + L := i-1; + break; + end; + RawUTF8ToVariant(Pointer(tmp),L,result); +end; + +class procedure TSynMustache.PowerOfTwo(const Value: variant; out result: variant); +var V: Int64; +begin + if TVarData(Value).VType>varNull then + if VariantToInt64(Value,V) then + result := Int64(1) shl V; +end; + +class procedure TSynMustache.Equals_(const Value: variant; out result: variant); +begin // {{#Equals .,12}} + with _Safe(Value)^ do + if (Kind=dvArray) and (Count=2) and + (SortDynArrayVariantComp(TVarData(Values[0]),TVarData(Values[1]),false)=0) then + result := true else + SetVariantNull(result); +end; + +class procedure TSynMustache.If_(const Value: variant; out result: variant); +var cmp: integer; + oper: RawUTF8; + wasString: boolean; +begin // {{#if .<>""}} or {{#if .,"=",123}} + SetVariantNull(result); + with _Safe(Value)^ do + if (Kind=dvArray) and (Count=3) then begin + VariantToUTF8(Values[1],oper,wasString); + if wasString and (oper<>'') then begin + cmp := SortDynArrayVariantComp(TVarData(Values[0]),TVarData(Values[2]),false); + case PWord(oper)^ of + ord('='): if cmp=0 then result := True; + ord('>'): if cmp>0 then result := True; + ord('<'): if cmp<0 then result := True; + ord('>')+ord('=')shl 8: if cmp>=0 then result := True; + ord('<')+ord('=')shl 8: if cmp<=0 then result := True; + ord('<')+ord('>')shl 8: if cmp<>0 then result := True; + end; + end; + end; +end; + +class procedure TSynMustache.NewGUID(const Value: variant; out result: variant); +var g: TGUID; +begin + CreateGUID(g); + RawUTF8ToVariant(GUIDToRawUTF8(g),result); +end; + +class procedure TSynMustache.ExtractFileName(const Value: variant; out result: variant); +begin + result := SysUtils.ExtractFileName(Value); +end; + +class procedure TSynMustache.Lower(const Value: variant; out result: variant); +begin + result := SysUtils.LowerCase(Value); +end; + +class procedure TSynMustache.Upper(const Value: variant; out result: variant); +begin + result := SysUtils.UpperCase(Value); +end; + + +{ TSynMustacheContext } + +constructor TSynMustacheContext.Create(Owner: TSynMustache; WR: TTextWriter); +begin + fOwner := Owner; + fWriter := WR; +end; + +procedure TSynMustacheContext.TranslateBlock(Text: PUTF8Char; TextLen: Integer); +var s: string; +begin + if Assigned(OnStringTranslate) then begin + UTF8DecodeToString(Text,TextLen,s); + OnStringTranslate(s); + fWriter.AddNoJSONEscapeString(s); + end else + fWriter.AddNoJSONEscape(Text,TextLen); +end; + + +{ TSynMustacheContextVariant } + +constructor TSynMustacheContextVariant.Create(Owner: TSynMustache; WR: TTextWriter; + SectionMaxCount: integer; const aDocument: variant); +begin + inherited Create(Owner,WR); + SetLength(fContext,SectionMaxCount+1); + PushContext(TVarData(aDocument)); // weak copy +end; + +function TSynMustacheContextVariant.GetDocumentType( + const aDoc: TVarData): TSynInvokeableVariantType; +begin + result := nil; + if aDoc.VType<=varAny then + exit; + if (fContextCount>0) and (fContext[0].DocumentType<>nil) and + (aDoc.VType=fContext[0].DocumentType.VarType) then + result := fContext[0].DocumentType else + if not (FindCustomVariantType(aDoc.VType,TCustomVariantType(result)) and + result.InheritsFrom(TSynInvokeableVariantType)) then + result := nil; +end; + +procedure TSynMustacheContextVariant.PushContext(aDoc: TVarData); +begin + if fContextCount>=length(fContext) then + SetLength(fContext,fContextCount+32); // roughtly set by SectionMaxCount + with fContext[fContextCount] do begin + Document := aDoc; + DocumentType := GetDocumentType(aDoc); + ListCurrent := -1; + if DocumentType=nil then + ListCount := -1 else + ListCount := DocumentType.IterateCount(aDoc); + end; + inc(fContextCount); +end; + +procedure TSynMustacheContextVariant.PopContext; +begin + if fContextCount>1 then + dec(fContextCount); +end; + +function TSynMustacheContextVariant.GetValueCopyFromContext( + const ValueName: RawUTF8): variant; +var tmp: TVarData; +begin + if (ValueName='') or (ValueName[1] in ['0'..'9','"','{','[']) or + (ValueName='true') or (ValueName='false') or (ValueName='null') then + VariantLoadJSON(result,ValueName,@JSON_OPTIONS[true]) else begin + GetValueFromContext(ValueName,tmp); + SetVariantByValue(variant(tmp),result); // copy value + end; +end; + +function TSynMustacheContextVariant.GetValueFromContext( + const ValueName: RawUTF8; var Value: TVarData): TSynMustacheSectionType; +var i,space,helper: Integer; + + procedure ProcessHelper; + var valnam: RawUTF8; + val: TVarData; + valArr: TDocVariantData absolute val; + valFree: boolean; + names: TRawUTF8DynArray; + res: PVarData; + j,k,n: integer; + begin + valnam := Copy(ValueName,space+1,maxInt); + valFree := false; + if valnam<>'' then begin + if valnam='.' then + GetValueFromContext(valnam,val) else + if ((valnam<>'') and (valnam[1] in ['1'..'9','"','{','['])) or + (valnam='true') or (valnam='false') or (valnam='null') then begin + // {{helper 123}} or {{helper "constant"}} or {{helper [1,2,3]}} + val.VType := varEmpty; + VariantLoadJson(variant(val),pointer(valnam),nil,@JSON_OPTIONS[true]); + valFree := true; + end else begin + for j := 1 to length(valnam) do + case valnam[j] of + ' ': break; // allows {{helper1 helper2 value}} recursive calls + ',': begin // {{helper value,123,"constant"}} + CSVToRawUTF8DynArray(Pointer(valnam),names,',',true); // TODO: handle 123,"a,b,c" + valArr.InitFast; + for k := 0 to High(names) do + valArr.AddItem(GetValueCopyFromContext(names[k])); + valFree := true; + break; + end; + '<','>','=': begin // {{#if .=123}} -> {{#if .,"=",123}} + k := j+1; + if valnam[k] in ['=','>'] then + inc(k); + valArr.InitArray([GetValueCopyFromContext(Copy(valnam,1,j-1)), + Copy(valnam,j,k-j),GetValueCopyFromContext(Copy(valnam,k,maxInt))],JSON_OPTIONS[true]); + valFree := true; + break; + end; + end; + if not valFree then + GetValueFromContext(valnam,val); + end; + end; + n := fContextCount+4; + if length(fTempGetValueFromContextHelper)0 then + Value := ListCurrentDocument else + Value := Document; + exit; + end; + space := PosEx(' ',ValueName); + if space>1 then begin // {{helper value}} + helper := TSynMustache.HelperFind(Helpers,pointer(ValueName),space-1); + if helper>=0 then begin + ProcessHelper; + result := msSinglePseudo; + exit; + end; // if helper not found, will return the unprocessed value + end; + for i := fContextCount-1 downto 0 do // recursive search of {{value}} + with fContext[i] do + if DocumentType<>nil then + if ListCount<0 then begin // single item context + DocumentType.Lookup(Value,Document,pointer(ValueName)); + if Value.VType>=varNull then + exit; + end else + if IdemPChar(pointer(ValueName),'-INDEX') then begin // {{-index}} + Value.VType := varInteger; + if ValueName[7]='0' then + Value.VInteger := ListCurrent else + Value.VInteger := ListCurrent+1; + exit; + end else + if (ListCurrentnil) then begin + ListCurrentDocumentType.Lookup(Value,ListCurrentDocument,pointer(ValueName)); + if Value.VType>=varNull then + exit; + end; + if space=0 then begin + space := length(ValueName); // {{helper}} + helper := TSynMustache.HelperFind(Helpers,pointer(ValueName),space); + if helper>=0 then begin + ProcessHelper; + result := msSinglePseudo; + end; + end; +end; + +procedure TSynMustacheContextVariant.AppendValue(const ValueName: RawUTF8; + UnEscape: boolean); +var Value: TVarData; +begin + GetValueFromContext(ValueName,Value); + AppendVariant(variant(Value),UnEscape); +end; + +procedure TSynMustacheContextVariant.AppendVariant(const Value: variant; + UnEscape: boolean); +var ValueText: RawUTF8; + wasString: boolean; +begin + if TVarData(Value).VType>varNull then + if VarIsNumeric(Value) then // avoid RawUTF8 conversion for plain numbers + fWriter.AddVariant(Value,twNone) else begin + if fEscapeInvert then + UnEscape := not UnEscape; + VariantToUTF8(Value,ValueText,wasString); + if UnEscape then + fWriter.AddNoJSONEscape(pointer(ValueText),length(ValueText)) else + fWriter.AddHtmlEscape(pointer(ValueText)); + end; +end; + +function TSynMustacheContextVariant.AppendSection( + const ValueName: RawUTF8): TSynMustacheSectionType; +var Value: TVarData; +begin + result := msNothing; + if (fContextCount>0) and (ValueName[1]='-') then + with fContext[fContextCount-1] do + if ListCount>=0 then begin + if ((ValueName='-first') and (ListCurrent=0)) or + ((ValueName='-last') and (ListCurrent=ListCount-1)) or + ((ValueName='-odd') and (ListCurrent and 1=0)) then + result := msSinglePseudo; + exit; + end; + result := GetValueFromContext(ValueName,Value); + if result<>msNothing then begin + if (Value.VType<=varNull) or + ((Value.VType=varBoolean) and not Value.VBoolean) then + result := msNothing; + exit; + end; + PushContext(Value); + if (Value.VType<=varNull) or + ((Value.VType=varBoolean) and not Value.VBoolean) then + exit; // null or false value will not display the section + with fContext[fContextCount-1] do + if ListCount<0 then + result := msSingle else // single item + if ListCount=0 then // empty list will not display the section + exit else + result := msList; // non-empty list +end; + +function TSynMustacheContextVariant.GotoNextListItem: boolean; +begin + result := false; + if fContextCount>0 then + with fContext[fContextCount-1] do begin + ListCurrentDocument.VType := varEmpty; + ListCurrentDocumentType := nil; + inc(ListCurrent); + if ListCurrent>=ListCount then + exit; + DocumentType.Iterate(ListCurrentDocument,Document,ListCurrent); + ListCurrentDocumentType := GetDocumentType(ListCurrentDocument); + result := true; + end; +end; + + +{ TSynMustachePartials } + +procedure TSynMustachePartials.Add(const aName, aTemplate: RawUTF8); +begin + fList.AddObject(aName,TSynMustache.Parse(aTemplate)); +end; + +procedure TSynMustachePartials.Add(const aName: RawUTF8; + aTemplateStart, aTemplateEnd: PUTF8Char); +var aTemplate: RawUTF8; +begin + SetString(aTemplate,PAnsiChar(aTemplateStart),aTemplateEnd-aTemplateStart); + Add(aName,aTemplate); +end; + +constructor TSynMustachePartials.Create; +begin + fList := TRawUTF8ListHashed.Create(false); +end; + +constructor TSynMustachePartials.CreateOwned( + const NameTemplatePairs: array of RawUTF8); +var A: integer; +begin + Create; + fOwned := true; + for A := 0 to high(NameTemplatePairs) div 2 do + Add(NameTemplatePairs[A*2],NameTemplatePairs[A*2+1]); +end; + +class function TSynMustachePartials.CreateOwned( + const Partials: variant): TSynMustachePartials; +var p: integer; +begin + result := nil; + if DocVariantType.IsOfType(Partials) then + with TDocVariantData(partials) do + if (Kind=dvObject) and (Count>0) then begin + result := TSynMustachePartials.Create; + result.fOwned := true; + for p := 0 to Count-1 do + result.Add(Names[p],VariantToUTF8(Values[p])); + end; +end; + +destructor TSynMustachePartials.Destroy; +begin + FreeAndNil(fList); + inherited; +end; + +function TSynMustachePartials.GetPartial( + const PartialName: RawUTF8): TSynMustache; +var i: integer; +begin + if self=nil then begin + result := nil; + exit; + end; + i := fList.IndexOf(PartialName); + if i<0 then + result := nil else + result := TSynMustache(fList.Objects[i]); +end; + +end. diff --git a/ThirdParty/mORMot/Source/SynTable.pas b/ThirdParty/mORMot/Source/SynTable.pas new file mode 100644 index 00000000..b59b2602 --- /dev/null +++ b/ThirdParty/mORMot/Source/SynTable.pas @@ -0,0 +1,13273 @@ +/// implement TSynTable/TSynTableStatement and TSynFilter/TSynValidate process +// - licensed under a MPL/GPL/LGPL tri-license; version 1.18 +unit SynTable; + +(* + This file is part of Synopse framework. + + Synopse framework. Copyright (C) 2019 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 framework. + + The Initial Developer of the Original Code is Arnaud Bouchez. + + Portions created by the Initial Developer are Copyright (C) 2019 + 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 ***** + + + Version 1.18 + - initial release + - removed from SynCommons.pas, for better code clarity, and to reduce the + number of source code lines of the unit, and circumvent the Delphi 5/6/7 + limitation of 65535 lines (internal error PRO-3006) + + +*) + +interface + +{$I Synopse.inc} // define HASINLINE USETYPEINFO CPU32 CPU64 + +uses + {$ifdef MSWINDOWS} + Windows, + {$else} + {$ifdef KYLIX3} + Types, + LibC, + SynKylix, + {$endif KYLIX3} + {$ifdef FPC} + Unix, + {$endif FPC} + {$endif MSWINDOWS} + SysUtils, + Classes, + {$ifndef LVCL} + SyncObjs, // for TEvent and TCriticalSection + Contnrs, // for TObjectList + {$endif} + {$ifndef NOVARIANTS} + Variants, + {$endif} + SynCommons; + + + +{ ************ filtering and validation classes and functions ************** } + +/// return TRUE if the supplied content is a valid email address +// - follows RFC 822, to validate local-part@domain email format +function IsValidEmail(P: PUTF8Char): boolean; + +/// return TRUE if the supplied content is a valid IP v4 address +function IsValidIP4Address(P: PUTF8Char): boolean; + +/// return TRUE if the supplied content matchs a glob pattern +// - ? Matches any single characer +// - * Matches any contiguous characters +// - [abc] Matches a or b or c at that position +// - [^abc] Matches anything but a or b or c at that position +// - [!abc] Matches anything but a or b or c at that position +// - [a-e] Matches a through e at that position +// - [abcx-z] Matches a or b or c or x or y or or z, as does [a-cx-z] +// - 'ma?ch.*' would match match.exe, mavch.dat, march.on, etc.. +// - 'this [e-n]s a [!zy]est' would match 'this is a test', but would not +// match 'this as a test' nor 'this is a zest' +// - consider using TMatch or TMatchs if you expect to reuse the pattern +function IsMatch(const Pattern, Text: RawUTF8; CaseInsensitive: boolean=false): boolean; + +/// return TRUE if the supplied content matchs a glob pattern, using VCL strings +// - is a wrapper around IsMatch() with fast UTF-8 conversion +function IsMatchString(const Pattern, Text: string; CaseInsensitive: boolean=false): boolean; + +type + PMatch = ^TMatch; + TMatchSearchFunction = function(aMatch: PMatch; aText: PUTF8Char; aTextLen: PtrInt): boolean; + + /// low-level structure used by IsMatch() for actual glob search + // - you can use this object to prepare a given pattern, e.g. in a loop + // - implemented as a fast brute-force state-machine without any heap allocation + // - some common patterns ('exactmatch', 'startwith*', '*endwith', '*contained*') + // are handled with dedicated code, optionally with case-insensitive search + // - consider using TMatchs (or SetMatchs/TMatchDynArray) if you expect to + // search for several patterns, or even TExprParserMatch for expression search + {$ifdef UNICODE}TMatch = record{$else}TMatch = object{$endif} + private + Pattern, Text: PUTF8Char; + P, T, PMax, TMax: PtrInt; + Upper: PNormTable; + State: (sNONE, sABORT, sEND, sLITERAL, sPATTERN, sRANGE, sVALID); + procedure MatchAfterStar; + procedure MatchMain; + public + /// published for proper inlining + Search: TMatchSearchFunction; + /// initialize the internal fields for a given glob search pattern + // - note that the aPattern instance should remain in memory, since it will + // be pointed to by the Pattern private field of this object + procedure Prepare(const aPattern: RawUTF8; aCaseInsensitive, aReuse: boolean); overload; + /// initialize the internal fields for a given glob search pattern + // - note that the aPattern buffer should remain in memory, since it will + // be pointed to by the Pattern private field of this object + procedure Prepare(aPattern: PUTF8Char; aPatternLen: integer; + aCaseInsensitive, aReuse: boolean); overload; + /// initialize low-level internal fields for'*aPattern*' search + // - this method is faster than a regular Prepare('*' + aPattern + '*') + // - warning: the supplied aPattern variable may be modified in-place to be + // filled with some lookup buffer, for length(aPattern) in [2..31] range + procedure PrepareContains(var aPattern: RawUTF8; aCaseInsensitive: boolean); overload; + /// initialize low-level internal fields for a custom search algorithm + procedure PrepareRaw(aPattern: PUTF8Char; aPatternLen: integer; + aSearch: TMatchSearchFunction); + /// returns TRUE if the supplied content matches the prepared glob pattern + // - this method is not thread-safe + function Match(const aText: RawUTF8): boolean; overload; + {$ifdef FPC}inline;{$endif} + /// returns TRUE if the supplied content matches the prepared glob pattern + // - this method is not thread-safe + function Match(aText: PUTF8Char; aTextLen: PtrInt): boolean; overload; + {$ifdef HASINLINE}inline;{$endif} + /// returns TRUE if the supplied content matches the prepared glob pattern + // - this method IS thread-safe, and won't lock + function MatchThreadSafe(const aText: RawUTF8): boolean; + /// returns TRUE if the supplied VCL/LCL content matches the prepared glob pattern + // - this method IS thread-safe, will use stack to UTF-8 temporary conversion + // if possible, and won't lock + function MatchString(const aText: string): boolean; + /// returns TRUE if this search pattern matches another + function Equals(const aAnother{$ifndef DELPHI5OROLDER}: TMatch{$endif}): boolean; + {$ifdef HASINLINE}inline;{$endif} + /// access to the pattern length as stored in PMax + 1 + function PatternLength: integer; {$ifdef HASINLINE}inline;{$endif} + /// access to the pattern text as stored in Pattern + function PatternText: PUTF8Char; {$ifdef HASINLINE}inline;{$endif} + end; + /// use SetMatchs() to initialize such an array from a CSV pattern text + TMatchDynArray = array of TMatch; + + /// TMatch descendant owning a copy of the Pattern string to avoid GPF issues + TMatchStore = record + /// access to the research criteria + // - defined as a nested record (and not an object) to circumvent Delphi bug + Pattern: TMatch; + /// Pattern.Pattern PUTF8Char will point to this instance + PatternInstance: RawUTF8; + end; + TMatchStoreDynArray = array of TMatchStore; + + /// stores several TMatch instances, from a set of glob patterns + TMatchs = class(TSynPersistent) + protected + fMatch: TMatchStoreDynArray; + fMatchCount: integer; + public + /// add once some glob patterns to the internal TMach list + // - aPatterns[] follows the IsMatch() syntax + constructor Create(const aPatterns: TRawUTF8DynArray; CaseInsensitive: Boolean); reintroduce; overload; + /// add once some glob patterns to the internal TMach list + // - aPatterns[] follows the IsMatch() syntax + procedure Subscribe(const aPatterns: TRawUTF8DynArray; CaseInsensitive: Boolean); overload; virtual; + /// add once some glob patterns to the internal TMach list + // - each CSV item in aPatterns follows the IsMatch() syntax + procedure Subscribe(const aPatternsCSV: RawUTF8; CaseInsensitive: Boolean); overload; + /// search patterns in the supplied UTF-8 text + // - returns -1 if no filter has been subscribed + // - returns -2 if there is no match on any previous pattern subscription + // - returns fMatch[] index, i.e. >= 0 number on first matching pattern + // - this method is thread-safe + function Match(const aText: RawUTF8): integer; overload; {$ifdef HASINLINE}inline;{$endif} + /// search patterns in the supplied UTF-8 text buffer + function Match(aText: PUTF8Char; aLen: integer): integer; overload; + /// search patterns in the supplied VCL/LCL text + // - could be used on a TFileName for instance + // - will avoid any memory allocation if aText is small enough + function MatchString(const aText: string): integer; + end; + +/// fill the Match[] dynamic array with all glob patterns supplied as CSV +// - returns how many patterns have been set in Match[|] +// - note that the CSVPattern instance should remain in memory, since it will +// be pointed to by the Match[].Pattern private field +function SetMatchs(const CSVPattern: RawUTF8; CaseInsensitive: boolean; + out Match: TMatchDynArray): integer; overload; + +/// fill the Match[0..MatchMax] static array with all glob patterns supplied as CSV +// - note that the CSVPattern instance should remain in memory, since it will +// be pointed to by the Match[].Pattern private field +function SetMatchs(CSVPattern: PUTF8Char; CaseInsensitive: boolean; + Match: PMatch; MatchMax: integer): integer; overload; + +/// search if one TMach is already registered in the Several[] dynamic array +function MatchExists(const One: TMatch; const Several: TMatchDynArray): boolean; + +/// add one TMach if not already registered in the Several[] dynamic array +function MatchAdd(const One: TMatch; var Several: TMatchDynArray): boolean; + +/// returns TRUE if Match=nil or if any Match[].Match(Text) is TRUE +function MatchAny(const Match: TMatchDynArray; const Text: RawUTF8): boolean; + +/// apply the CSV-supplied glob patterns to an array of RawUTF8 +// - any text not maching the pattern will be deleted from the array +procedure FilterMatchs(const CSVPattern: RawUTF8; CaseInsensitive: boolean; + var Values: TRawUTF8DynArray); + + +type + TSynFilterOrValidate = class; + + TSynFilterOrValidateObjArray = array of TSynFilterOrValidate; + TSynFilterOrValidateObjArrayArray = array of TSynFilterOrValidateObjArray; + + /// will define a filter (transformation) or a validation process to be + // applied to a database Record content (typicaly a TSQLRecord) + // - the optional associated parameters are to be supplied JSON-encoded + TSynFilterOrValidate = class + protected + fParameters: RawUTF8; + /// children must override this method in order to parse the JSON-encoded + // parameters, and store it in protected field values + procedure SetParameters(const Value: RawUTF8); virtual; + public + /// add the filter or validation process to a list, checking if not present + // - if an instance with the same class type and parameters is already + // registered, will call aInstance.Free and return the exising instance + // - if there is no similar instance, will add it to the list and return it + function AddOnce(var aObjArray: TSynFilterOrValidateObjArray; + aFreeIfAlreadyThere: boolean=true): TSynFilterOrValidate; + public + /// initialize the filter (transformation) or validation instance + // - most of the time, optional parameters may be specified as JSON, + // possibly with the extended MongoDB syntax + constructor Create(const aParameters: RawUTF8=''); overload; virtual; + /// initialize the filter or validation instance + /// - this overloaded constructor will allow to easily set the parameters + constructor CreateUTF8(const Format: RawUTF8; const Args, Params: array of const); overload; + /// the optional associated parameters, supplied as JSON-encoded + property Parameters: RawUTF8 read fParameters write SetParameters; + end; + + /// will define a validation to be applied to a Record (typicaly a TSQLRecord) + // field content + // - a typical usage is to validate an email or IP adress e.g. + // - the optional associated parameters are to be supplied JSON-encoded + TSynValidate = class(TSynFilterOrValidate) + public + /// perform the validation action to the specified value + // - the value is expected by be UTF-8 text, as generated by + // TPropInfo.GetValue e.g. + // - if the validation failed, must return FALSE and put some message in + // ErrorMsg (translated into the current language: you could e.g. use + // a resourcestring and a SysUtils.Format() call for automatic translation + // via the mORMoti18n unit - you can leave ErrorMsg='' to trigger a + // generic error message from clas name ('"Validate email" rule failed' + // for TSynValidateEmail class e.g.) + // - if the validation passed, will return TRUE + function Process(FieldIndex: integer; const Value: RawUTF8; var ErrorMsg: string): boolean; + virtual; abstract; + end; + + /// points to a TSynValidate variable + // - used e.g. as optional parameter to TSQLRecord.Validate/FilterAndValidate + PSynValidate = ^TSynValidate; + + /// IP v4 address validation to be applied to a Record field content + // (typicaly a TSQLRecord) + // - this versions expect no parameter + TSynValidateIPAddress = class(TSynValidate) + protected + public + /// perform the IP Address validation action to the specified value + function Process(aFieldIndex: integer; const Value: RawUTF8; + var ErrorMsg: string): boolean; override; + end; + + /// IP address validation to be applied to a Record field content + // (typicaly a TSQLRecord) + // - optional JSON encoded parameters are "AllowedTLD" or "ForbiddenTLD", + // expecting a CSV lis of Top-Level-Domain (TLD) names, e.g. + // $ '{"AllowedTLD":"com,org,net","ForbiddenTLD":"fr"}' + // $ '{AnyTLD:true,ForbiddenDomains:"mailinator.com,yopmail.com"}' + // - this will process a validation according to RFC 822 (calling the + // IsValidEmail() function) then will check for the TLD to be in one of + // the Top-Level domains ('.com' and such) or a two-char country, and + // then will check the TLD according to AllowedTLD and ForbiddenTLD + TSynValidateEmail = class(TSynValidate) + private + fAllowedTLD: RawUTF8; + fForbiddenTLD: RawUTF8; + fForbiddenDomains: RawUTF8; + fAnyTLD: boolean; + protected + /// decode all published properties from their JSON representation + procedure SetParameters(const Value: RawUTF8); override; + public + /// perform the Email Address validation action to the specified value + // - call IsValidEmail() function and check for the supplied TLD + function Process(aFieldIndex: integer; const Value: RawUTF8; var ErrorMsg: string): boolean; override; + /// allow any TLD to be allowed, even if not a generic TLD (.com,.net ...) + // - this may be mandatory since already over 1,300 new gTLD names or + // "strings" could become available in the next few years: there is a + // growing list of new gTLDs available at + // @http://newgtlds.icann.org/en/program-status/delegated-strings + // - the only restriction is that it should be ascii characters + property AnyTLD: boolean read fAnyTLD write fAnyTLD; + /// a CSV list of allowed TLD + // - if accessed directly, should be set as lower case values + // - e.g. 'com,org,net' + property AllowedTLD: RawUTF8 read fAllowedTLD write fAllowedTLD; + /// a CSV list of forbidden TLD + // - if accessed directly, should be set as lower case values + // - e.g. 'fr' + property ForbiddenTLD: RawUTF8 read fForbiddenTLD write fForbiddenTLD; + /// a CSV list of forbidden domain names + // - if accessed directly, should be set as lower case values + // - not only the TLD, but whole domains like 'cracks.ru,hotmail.com' or such + property ForbiddenDomains: RawUTF8 read fForbiddenDomains write fForbiddenDomains; + end; + + /// glob case-sensitive pattern validation of a Record field content + // - parameter is NOT JSON encoded, but is some basic TMatch glob pattern + // - ? Matches any single characer + // - * Matches any contiguous characters + // - [abc] Matches a or b or c at that position + // - [^abc] Matches anything but a or b or c at that position + // - [!abc] Matches anything but a or b or c at that position + // - [a-e] Matches a through e at that position + // - [abcx-z] Matches a or b or c or x or y or or z, as does [a-cx-z] + // - 'ma?ch.*' would match match.exe, mavch.dat, march.on, etc.. + // - 'this [e-n]s a [!zy]est' would match 'this is a test', but would not + // match 'this as a test' nor 'this is a zest' + // - pattern check IS case sensitive (TSynValidatePatternI is not) + // - this class is not as complete as PCRE regex for example, + // but code overhead is very small, and speed good enough in practice + TSynValidatePattern = class(TSynValidate) + protected + fMatch: TMatch; + procedure SetParameters(const Value: RawUTF8); override; + public + /// perform the pattern validation to the specified value + // - pattern can be e.g. '[0-9][0-9]:[0-9][0-9]:[0-9][0-9]' + // - this method will implement both TSynValidatePattern and + // TSynValidatePatternI, checking the current class + function Process(aFieldIndex: integer; const Value: RawUTF8; + var ErrorMsg: string): boolean; override; + end; + + /// glob case-insensitive pattern validation of a text field content + // (typicaly a TSQLRecord) + // - parameter is NOT JSON encoded, but is some basic TMatch glob pattern + // - same as TSynValidatePattern, but is NOT case sensitive + TSynValidatePatternI = class(TSynValidatePattern); + + /// text validation to ensure that to any text field would not be '' + TSynValidateNonVoidText = class(TSynValidate) + public + /// perform the non void text validation action to the specified value + function Process(aFieldIndex: integer; const Value: RawUTF8; + var ErrorMsg: string): boolean; override; + end; + + TSynValidateTextProps = array[0..15] of cardinal; + +{$M+} // to have existing RTTI for published properties + /// text validation to be applied to any Record field content + // - default MinLength value is 1, MaxLength is maxInt: so a blank + // TSynValidateText.Create('') is the same as TSynValidateNonVoidText + // - MinAlphaCount, MinDigitCount, MinPunctCount, MinLowerCount and + // MinUpperCount allow you to specify the minimal count of respectively + // alphabetical [a-zA-Z], digit [0-9], punctuation [_!;.,/:?%$="#@(){}+-*], + // lower case or upper case characters + // - expects optional JSON parameters of the allowed text length range as + // $ '{"MinLength":5,"MaxLength":10,"MinAlphaCount":1,"MinDigitCount":1, + // $ "MinPunctCount":1,"MinLowerCount":1,"MinUpperCount":1} + TSynValidateText = class(TSynValidate) + private + /// used to store all associated validation properties by index + fProps: TSynValidateTextProps; + fUTF8Length: boolean; + protected + /// use sInvalidTextChar resourcestring to create a translated error message + procedure SetErrorMsg(fPropsIndex, InvalidTextIndex, MainIndex: integer; + var result: string); + /// decode "MinLength", "MaxLength", and other parameters into fProps[] + procedure SetParameters(const Value: RawUTF8); override; + public + /// perform the text length validation action to the specified value + function Process(aFieldIndex: integer; const Value: RawUTF8; + var ErrorMsg: string): boolean; override; + published + /// Minimal length value allowed for the text content + // - the length is calculated with UTF-16 Unicode codepoints, unless + // UTF8Length has been set to TRUE so that the UTF-8 byte count is checked + // - default is 1, i.e. a void text will not pass the validation + property MinLength: cardinal read fProps[0] write fProps[0]; + /// Maximal length value allowed for the text content + // - the length is calculated with UTF-16 Unicode codepoints, unless + // UTF8Length has been set to TRUE so that the UTF-8 byte count is checked + // - default is maxInt, i.e. no maximum length is set + property MaxLength: cardinal read fProps[1] write fProps[1]; + /// Minimal alphabetical character [a-zA-Z] count + // - default is 0, i.e. no minimum set + property MinAlphaCount: cardinal read fProps[2] write fProps[2]; + /// Maximal alphabetical character [a-zA-Z] count + // - default is maxInt, i.e. no Maximum set + property MaxAlphaCount: cardinal read fProps[10] write fProps[10]; + /// Minimal digit character [0-9] count + // - default is 0, i.e. no minimum set + property MinDigitCount: cardinal read fProps[3] write fProps[3]; + /// Maximal digit character [0-9] count + // - default is maxInt, i.e. no Maximum set + property MaxDigitCount: cardinal read fProps[11] write fProps[11]; + /// Minimal punctuation sign [_!;.,/:?%$="#@(){}+-*] count + // - default is 0, i.e. no minimum set + property MinPunctCount: cardinal read fProps[4] write fProps[4]; + /// Maximal punctuation sign [_!;.,/:?%$="#@(){}+-*] count + // - default is maxInt, i.e. no Maximum set + property MaxPunctCount: cardinal read fProps[12] write fProps[12]; + /// Minimal alphabetical lower case character [a-z] count + // - default is 0, i.e. no minimum set + property MinLowerCount: cardinal read fProps[5] write fProps[5]; + /// Maximal alphabetical lower case character [a-z] count + // - default is maxInt, i.e. no Maximum set + property MaxLowerCount: cardinal read fProps[13] write fProps[13]; + /// Minimal alphabetical upper case character [A-Z] count + // - default is 0, i.e. no minimum set + property MinUpperCount: cardinal read fProps[6] write fProps[6]; + /// Maximal alphabetical upper case character [A-Z] count + // - default is maxInt, i.e. no Maximum set + property MaxUpperCount: cardinal read fProps[14] write fProps[14]; + /// Minimal space count inside the value text + // - default is 0, i.e. any space number allowed + property MinSpaceCount: cardinal read fProps[7] write fProps[7]; + /// Maximal space count inside the value text + // - default is maxInt, i.e. any space number allowed + property MaxSpaceCount: cardinal read fProps[15] write fProps[15]; + /// Maximal space count allowed on the Left side + // - default is maxInt, i.e. any Left space allowed + property MaxLeftTrimCount: cardinal read fProps[8] write fProps[8]; + /// Maximal space count allowed on the Right side + // - default is maxInt, i.e. any Right space allowed + property MaxRightTrimCount: cardinal read fProps[9] write fProps[9]; + /// defines if lengths parameters expects UTF-8 or UTF-16 codepoints number + // - with default FALSE, the length is calculated with UTF-16 Unicode + // codepoints - MaxLength may not match the UCS4 glyphs number, in case of + // UTF-16 surrogates + // - you can set this property to TRUE so that the UTF-8 byte count would + // be used for truncation againts the MaxLength parameter + property UTF8Length: boolean read fUTF8Length write fUTF8Length; + end; +{$M-} + + /// strong password validation for a Record field content (typicaly a TSQLRecord) + // - the following parameters are set by default to + // $ '{"MinLength":5,"MaxLength":20,"MinAlphaCount":1,"MinDigitCount":1, + // $ "MinPunctCount":1,"MinLowerCount":1,"MinUpperCount":1,"MaxSpaceCount":0}' + // - you can specify some JSON encoded parameters to change this default + // values, which will validate the text field only if it contains from 5 to 10 + // characters, with at least one digit, one upper case letter, one lower case + // letter, and one ponctuation sign, with no space allowed inside + TSynValidatePassWord = class(TSynValidateText) + protected + /// set password specific parameters + procedure SetParameters(const Value: RawUTF8); override; + end; + + { C++Builder doesn't support array elements as properties (RSP-12595). + For now, simply exclude the relevant classes from C++Builder. } + {$NODEFINE TSynValidateTextProps} + {$NODEFINE TSynValidateText } + {$NODEFINE TSynValidatePassWord } + + /// will define a transformation to be applied to a Record field content + // (typicaly a TSQLRecord) + // - here "filter" means that content would be transformed according to a + // set of defined rules + // - a typical usage is to convert to lower or upper case, or + // trim any time or date value in a TDateTime field + // - the optional associated parameters are to be supplied JSON-encoded + TSynFilter = class(TSynFilterOrValidate) + protected + public + /// perform the transformation to the specified value + // - the value is converted into UTF-8 text, as expected by + // TPropInfo.GetValue / TPropInfo.SetValue e.g. + procedure Process(aFieldIndex: integer; var Value: RawUTF8); virtual; abstract; + end; + + /// class-refrence type (metaclass) for a TSynFilter or a TSynValidate + TSynFilterOrValidateClass = class of TSynFilterOrValidate; + + /// class-reference type (metaclass) of a record filter (transformation) + TSynFilterClass = class of TSynFilter; + + /// convert the value into ASCII Upper Case characters + // - UpperCase conversion is made for ASCII-7 only, i.e. 'a'..'z' characters + // - this version expects no parameter + TSynFilterUpperCase = class(TSynFilter) + public + /// perform the case conversion to the specified value + procedure Process(aFieldIndex: integer; var Value: RawUTF8); override; + end; + + /// convert the value into WinAnsi Upper Case characters + // - UpperCase conversion is made for all latin characters in the WinAnsi + // code page only, e.g. 'e' acute will be converted to 'E' + // - this version expects no parameter + TSynFilterUpperCaseU = class(TSynFilter) + public + /// perform the case conversion to the specified value + procedure Process(aFieldIndex: integer; var Value: RawUTF8); override; + end; + + /// convert the value into ASCII Lower Case characters + // - LowerCase conversion is made for ASCII-7 only, i.e. 'A'..'Z' characters + // - this version expects no parameter + TSynFilterLowerCase = class(TSynFilter) + public + /// perform the case conversion to the specified value + procedure Process(aFieldIndex: integer; var Value: RawUTF8); override; + end; + + /// convert the value into WinAnsi Lower Case characters + // - LowerCase conversion is made for all latin characters in the WinAnsi + // code page only, e.g. 'E' acute will be converted to 'e' + // - this version expects no parameter + TSynFilterLowerCaseU = class(TSynFilter) + public + /// perform the case conversion to the specified value + procedure Process(aFieldIndex: integer; var Value: RawUTF8); override; + end; + + /// trim any space character left or right to the value + // - this versions expect no parameter + TSynFilterTrim = class(TSynFilter) + public + /// perform the space triming conversion to the specified value + procedure Process(aFieldIndex: integer; var Value: RawUTF8); override; + end; + + /// truncate a text above a given maximum length + // - expects optional JSON parameters of the allowed text length range as + // $ '{MaxLength":10} + TSynFilterTruncate = class(TSynFilter) + protected + fMaxLength: cardinal; + fUTF8Length: boolean; + /// decode the MaxLength: and UTF8Length: parameters + procedure SetParameters(const Value: RawUTF8); override; + public + /// perform the length truncation of the specified value + procedure Process(aFieldIndex: integer; var Value: RawUTF8); override; + /// Maximal length value allowed for the text content + // - the length is calculated with UTF-16 Unicode codepoints, unless + // UTF8Length has been set to TRUE so that the UTF-8 byte count is checked + // - default is 0, i.e. no maximum length is forced + property MaxLength: cardinal read fMaxLength write fMaxLength; + /// defines if MaxLength is stored as UTF-8 or UTF-16 codepoints number + // - with default FALSE, the length is calculated with UTF-16 Unicode + // codepoints - MaxLength may not match the UCS4 glyphs number, in case of + // UTF-16 surrogates + // - you can set this property to TRUE so that the UTF-8 byte count would + // be used for truncation againts the MaxLength parameter + property UTF8Length: boolean read fUTF8Length write fUTF8Length; + end; + + +{ ************ Database types and classes ************************** } + +type + /// handled field/parameter/column types for abstract database access + // - this will map JSON-compatible low-level database-level access types, not + // high-level Delphi types as TSQLFieldType defined in mORMot.pas + // - it does not map either all potential types as defined in DB.pas (which + // are there for compatibility with old RDBMS, and are not abstract enough) + // - those types can be mapped to standard SQLite3 generic types, i.e. + // NULL, INTEGER, REAL, TEXT, BLOB (with the addition of a ftCurrency and + // ftDate type, for better support of most DB engines) + // see @http://www.sqlite.org/datatype3.html + // - the only string type handled here uses UTF-8 encoding (implemented + // using our RawUTF8 type), for cross-Delphi true Unicode process + TSQLDBFieldType = + (ftUnknown, ftNull, ftInt64, ftDouble, ftCurrency, ftDate, ftUTF8, ftBlob); + + /// set of field/parameter/column types for abstract database access + TSQLDBFieldTypes = set of TSQLDBFieldType; + + /// array of field/parameter/column types for abstract database access + TSQLDBFieldTypeDynArray = array of TSQLDBFieldType; + + /// array of field/parameter/column types for abstract database access + // - this array as a fixed size, ready to handle up to MAX_SQLFIELDS items + TSQLDBFieldTypeArray = array[0..MAX_SQLFIELDS-1] of TSQLDBFieldType; + + /// how TSQLVar may be processed + // - by default, ftDate will use seconds resolution unless svoDateWithMS is set + TSQLVarOption = (svoDateWithMS); + + /// defines how TSQLVar may be processed + TSQLVarOptions = set of TSQLVarOption; + + /// memory structure used for database values by reference storage + // - used mainly by SynDB, mORMot, mORMotDB and mORMotSQLite3 units + // - defines only TSQLDBFieldType data types (similar to those handled by + // SQLite3, with the addition of ftCurrency and ftDate) + // - cleaner/lighter dedicated type than TValue or variant/TVarData, strong + // enough to be marshalled as JSON content + // - variable-length data (e.g. UTF-8 text or binary BLOB) are never stored + // within this record, but VText/VBlob will point to an external (temporary) + // memory buffer + // - date/time is stored as ISO-8601 text (with milliseconds if svoDateWithMS + // option is set and the database supports it), and currency as double or BCD + // in most databases + TSQLVar = record + /// how this value should be processed + Options: TSQLVarOptions; + /// the type of the value stored + case VType: TSQLDBFieldType of + ftInt64: ( + VInt64: Int64); + ftDouble: ( + VDouble: double); + ftDate: ( + VDateTime: TDateTime); + ftCurrency: ( + VCurrency: Currency); + ftUTF8: ( + VText: PUTF8Char); + ftBlob: ( + VBlob: pointer; + VBlobLen: Integer) + end; + + /// dynamic array of database values by reference storage + TSQLVarDynArray = array of TSQLVar; + + /// used to store bit set for all available fields in a Table + // - with current MAX_SQLFIELDS value, 64 bits uses 8 bytes of memory + // - see also IsZero() and IsEqual() functions + // - you can also use ALL_FIELDS as defined in mORMot.pas + TSQLFieldBits = set of 0..MAX_SQLFIELDS-1; + + /// used to store a field index in a Table + // - note that -1 is commonly used for the ID/RowID field so the values should + // be signed + // - even if ShortInt (-128..127) may have been enough, we define a 16 bit + // safe unsigned integer to let the source compile with Delphi 5 + TSQLFieldIndex = SmallInt; // -32768..32767 + + /// used to store field indexes in a Table + // - same as TSQLFieldBits, but allowing to store the proper order + TSQLFieldIndexDynArray = array of TSQLFieldIndex; + + /// points to a bit set used for all available fields in a Table + PSQLFieldBits = ^TSQLFieldBits; + + /// generic parameter types, as recognized by SQLParamContent() and + // ExtractInlineParameters() functions + TSQLParamType = (sptUnknown, sptInteger, sptFloat, sptText, sptBlob, sptDateTime); + + /// array of parameter types, as recognized by SQLParamContent() and + // ExtractInlineParameters() functions + TSQLParamTypeDynArray = array of TSQLParamType; + + /// simple writer to a Stream, specialized for the JSON format and SQL export + // - use an internal buffer, faster than string+string + TJSONWriter = class(TTextWriter) + protected + /// used to store output format + fExpand: boolean; + /// used to store output format for TSQLRecord.GetJSONValues() + fWithID: boolean; + /// used to store field for TSQLRecord.GetJSONValues() + fFields: TSQLFieldIndexDynArray; + /// if not Expanded format, contains the Stream position of the first + // useful Row of data; i.e. ',val11' position in: + // & { "fieldCount":1,"values":["col1","col2",val11,"val12",val21,..] } + fStartDataPosition: integer; + public + /// used internally to store column names and count for AddColumns + ColNames: TRawUTF8DynArray; + /// the data will be written to the specified Stream + // - if no Stream is supplied, a temporary memory stream will be created + // (it's faster to supply one, e.g. any TSQLRest.TempMemoryStream) + constructor Create(aStream: TStream; Expand, withID: boolean; + const Fields: TSQLFieldBits; aBufSize: integer=8192); overload; + /// the data will be written to the specified Stream + // - if no Stream is supplied, a temporary memory stream will be created + // (it's faster to supply one, e.g. any TSQLRest.TempMemoryStream) + constructor Create(aStream: TStream; Expand, withID: boolean; + const Fields: TSQLFieldIndexDynArray=nil; aBufSize: integer=8192); overload; + /// rewind the Stream position and write void JSON object + procedure CancelAllVoid; + /// write or init field names for appropriate JSON Expand later use + // - ColNames[] must have been initialized before calling this procedure + // - if aKnownRowsCount is not null, a "rowCount":... item will be added + // to the generated JSON stream (for faster unserialization of huge content) + procedure AddColumns(aKnownRowsCount: integer=0); + /// allow to change on the fly an expanded format column layout + // - by definition, a non expanded format will raise a ESynException + // - caller should then set ColNames[] and run AddColumns() + procedure ChangeExpandedFields(aWithID: boolean; const aFields: TSQLFieldIndexDynArray); overload; + /// end the serialized JSON object + // - cancel last ',' + // - close the JSON object ']' or ']}' + // - write non expanded postlog (,"rowcount":...), if needed + // - flush the internal buffer content if aFlushFinal=true + procedure EndJSONObject(aKnownRowsCount,aRowsCount: integer; aFlushFinal: boolean=true); + {$ifdef HASINLINE}inline;{$endif} + /// the first data row is erased from the content + // - only works if the associated storage stream is TMemoryStream + // - expect not Expanded format + procedure TrimFirstRow; + /// is set to TRUE in case of Expanded format + property Expand: boolean read fExpand write fExpand; + /// is set to TRUE if the ID field must be appended to the resulting JSON + // - this field is used only by TSQLRecord.GetJSONValues + // - this field is ignored by TSQLTable.GetJSONValues + property WithID: boolean read fWithID; + /// Read-Only access to the field bits set for each column to be stored + property Fields: TSQLFieldIndexDynArray read fFields; + /// if not Expanded format, contains the Stream position of the first + // useful Row of data; i.e. ',val11' position in: + // & { "fieldCount":1,"values":["col1","col2",val11,"val12",val21,..] } + property StartDataPosition: integer read fStartDataPosition; + end; + +/// returns TRUE if no bit inside this TSQLFieldBits is set +// - is optimized for 64, 128, 192 and 256 max bits count (i.e. MAX_SQLFIELDS) +// - will work also with any other value +function IsZero(const Fields: TSQLFieldBits): boolean; overload; + {$ifdef HASINLINE}inline;{$endif} + +/// fast comparison of two TSQLFieldBits values +// - is optimized for 64, 128, 192 and 256 max bits count (i.e. MAX_SQLFIELDS) +// - will work also with any other value +function IsEqual(const A,B: TSQLFieldBits): boolean; overload; + {$ifdef HASINLINE}inline;{$endif} + +/// fast initialize a TSQLFieldBits with 0 +// - is optimized for 64, 128, 192 and 256 max bits count (i.e. MAX_SQLFIELDS) +// - will work also with any other value +procedure FillZero(var Fields: TSQLFieldBits); overload; + {$ifdef HASINLINE}inline;{$endif} + +/// convert a TSQLFieldBits set of bits into an array of integers +procedure FieldBitsToIndex(const Fields: TSQLFieldBits; + var Index: TSQLFieldIndexDynArray; MaxLength: integer=MAX_SQLFIELDS; + IndexStart: integer=0); overload; + +/// convert a TSQLFieldBits set of bits into an array of integers +function FieldBitsToIndex(const Fields: TSQLFieldBits; + MaxLength: integer=MAX_SQLFIELDS): TSQLFieldIndexDynArray; overload; + {$ifdef HASINLINE}inline;{$endif} + +/// add a field index to an array of field indexes +// - returns the index in Indexes[] of the newly appended Field value +function AddFieldIndex(var Indexes: TSQLFieldIndexDynArray; Field: integer): integer; + +/// convert an array of field indexes into a TSQLFieldBits set of bits +procedure FieldIndexToBits(const Index: TSQLFieldIndexDynArray; var Fields: TSQLFieldBits); overload; + +// search a field index in an array of field indexes +// - returns the index in Indexes[] of the given Field value, -1 if not found +function SearchFieldIndex(var Indexes: TSQLFieldIndexDynArray; Field: integer): integer; + +/// convert an array of field indexes into a TSQLFieldBits set of bits +function FieldIndexToBits(const Index: TSQLFieldIndexDynArray): TSQLFieldBits; overload; + {$ifdef HASINLINE}inline;{$endif} + +/// returns the stored size of a TSQLVar database value +// - only returns VBlobLen / StrLen(VText) size, 0 otherwise +function SQLVarLength(const Value: TSQLVar): integer; + +{$ifndef NOVARIANTS} + +/// convert any Variant into a database value +// - ftBlob kind won't be handled by this function +// - complex variant types would be converted into ftUTF8 JSON object/array +procedure VariantToSQLVar(const Input: variant; var temp: RawByteString; + var Output: TSQLVar); + +/// guess the correct TSQLDBFieldType from a variant value +function VariantTypeToSQLDBFieldType(const V: Variant): TSQLDBFieldType; + +{$endif NOVARIANTS} + + /// convert a date to a ISO-8601 string format for SQL '?' inlined parameters +// - will return the date encoded as '\uFFF1YYYY-MM-DD' - therefore +// ':("\uFFF12012-05-04"):' pattern will be recognized as a sftDateTime +// inline parameter in SQLParamContent() / ExtractInlineParameters() functions +// (JSON_SQLDATE_MAGIC will be used as prefix to create '\uFFF1...' pattern) +// - to be used e.g. as in: +// ! aRec.CreateAndFillPrepare(Client,'Datum=?',[DateToSQL(EncodeDate(2012,5,4))]); +function DateToSQL(Date: TDateTime): RawUTF8; overload; + +/// convert a date to a ISO-8601 string format for SQL '?' inlined parameters +// - will return the date encoded as '\uFFF1YYYY-MM-DD' - therefore +// ':("\uFFF12012-05-04"):' pattern will be recognized as a sftDateTime +// inline parameter in SQLParamContent() / ExtractInlineParameters() functions +// (JSON_SQLDATE_MAGIC will be used as prefix to create '\uFFF1...' pattern) +// - to be used e.g. as in: +// ! aRec.CreateAndFillPrepare(Client,'Datum=?',[DateToSQL(2012,5,4)]); +function DateToSQL(Year,Month,Day: cardinal): RawUTF8; overload; + +/// convert a date/time to a ISO-8601 string format for SQL '?' inlined parameters +// - if DT=0, returns '' +// - if DT contains only a date, returns the date encoded as '\uFFF1YYYY-MM-DD' +// - if DT contains only a time, returns the time encoded as '\uFFF1Thh:mm:ss' +// - otherwise, returns the ISO-8601 date and time encoded as '\uFFF1YYYY-MM-DDThh:mm:ss' +// (JSON_SQLDATE_MAGIC will be used as prefix to create '\uFFF1...' pattern) +// - if WithMS is TRUE, will append '.sss' for milliseconds resolution +// - to be used e.g. as in: +// ! aRec.CreateAndFillPrepare(Client,'Datum<=?',[DateTimeToSQL(Now)]); +// - see TimeLogToSQL() if you are using TTimeLog/TModTime/TCreateTime values +function DateTimeToSQL(DT: TDateTime; WithMS: boolean=false): RawUTF8; + +/// decode a SQL '?' inlined parameter (i.e. with JSON_SQLDATE_MAGIC prefix) +// - as generated by DateToSQL/DateTimeToSQL/TimeLogToSQL functions +function SQLToDateTime(const ParamValueWithMagic: RawUTF8): TDateTime; + +/// convert a TTimeLog value into a ISO-8601 string format for SQL '?' inlined +// parameters +// - handle TTimeLog bit-encoded Int64 format +// - follows the same pattern as DateToSQL or DateTimeToSQL functions, i.e. +// will return the date or time encoded as '\uFFF1YYYY-MM-DDThh:mm:ss' - +// therefore ':("\uFFF12012-05-04T20:12:13"):' pattern will be recognized as a +// sftDateTime inline parameter in SQLParamContent() / ExtractInlineParameters() +// (JSON_SQLDATE_MAGIC will be used as prefix to create '\uFFF1...' pattern) +// - to be used e.g. as in: +// ! aRec.CreateAndFillPrepare(Client,'Datum<=?',[TimeLogToSQL(TimeLogNow)]); +function TimeLogToSQL(const Timestamp: TTimeLog): RawUTF8; + +/// convert a Iso8601 encoded string into a ISO-8601 string format for SQL +// '?' inlined parameters +// - follows the same pattern as DateToSQL or DateTimeToSQL functions, i.e. +// will return the date or time encoded as '\uFFF1YYYY-MM-DDThh:mm:ss' - +// therefore ':("\uFFF12012-05-04T20:12:13"):' pattern will be recognized as a +// sftDateTime inline parameter in SQLParamContent() / ExtractInlineParameters() +// (JSON_SQLDATE_MAGIC will be used as prefix to create '\uFFF1...' pattern) +// - in practice, just append the JSON_SQLDATE_MAGIC prefix to the supplied text +function Iso8601ToSQL(const S: RawByteString): RawUTF8; + + +/// guess the content type of an UTF-8 SQL value, in :(....): format +// - will be used e.g. by ExtractInlineParameters() to un-inline a SQL statement +// - sftInteger is returned for an INTEGER value, e.g. :(1234): +// - sftFloat is returned for any floating point value (i.e. some digits +// separated by a '.' character), e.g. :(12.34): or :(12E-34): +// - sftUTF8Text is returned for :("text"): or :('text'):, with double quoting +// inside the value +// - sftBlob will be recognized from the ':("\uFFF0base64encodedbinary"):' +// pattern, and return raw binary (for direct blob parameter assignment) +// - sftDateTime will be recognized from ':(\uFFF1"2012-05-04"):' pattern, +// i.e. JSON_SQLDATE_MAGIC-prefixed string as returned by DateToSQL() or +// DateTimeToSQL() functions +// - sftUnknown is returned on invalid content, or if wasNull is set to TRUE +// - if ParamValue is not nil, the pointing RawUTF8 string is set with the +// value inside :(...): without double quoting in case of sftUTF8Text +// - wasNull is set to TRUE if P was ':(null):' and ParamType is sftUnknwown +function SQLParamContent(P: PUTF8Char; out ParamType: TSQLParamType; out ParamValue: RawUTF8; + out wasNull: boolean): PUTF8Char; + +/// this function will extract inlined :(1234): parameters into Types[]/Values[] +// - will return the generic SQL statement with ? place holders for inlined +// parameters and setting Values with SQLParamContent() decoded content +// - will set maxParam=0 in case of no inlined parameters +// - recognized types are sptInteger, sptFloat, sptDateTime ('\uFFF1...'), +// sptUTF8Text and sptBlob ('\uFFF0...') +// - sptUnknown is returned on invalid content +function ExtractInlineParameters(const SQL: RawUTF8; + var Types: TSQLParamTypeDynArray; var Values: TRawUTF8DynArray; + var maxParam: integer; var Nulls: TSQLFieldBits): RawUTF8; + +/// returns a 64-bit value as inlined ':(1234):' text +function InlineParameter(ID: Int64): shortstring; overload; + +/// returns a string value as inlined ':("value"):' text +function InlineParameter(const value: RawUTF8): RawUTF8; overload; + +type + /// SQL Query comparison operators + // - used e.g. by CompareOperator() functions in SynTable.pas or vt_BestIndex() + // in mORMotSQLite3.pas + TCompareOperator = ( + soEqualTo, + soNotEqualTo, + soLessThan, + soLessThanOrEqualTo, + soGreaterThan, + soGreaterThanOrEqualTo, + soBeginWith, + soContains, + soSoundsLikeEnglish, + soSoundsLikeFrench, + soSoundsLikeSpanish); + +const + /// convert identified field types into high-level ORM types + // - as will be implemented in unit mORMot.pas + SQLDBFIELDTYPE_TO_DELPHITYPE: array[TSQLDBFieldType] of RawUTF8 = ( + '???','???', 'Int64', 'Double', 'Currency', 'TDateTime', 'RawUTF8', 'TSQLRawBlob'); + + + +{ ************ TSynTable types and classes ************************** } + +{$define SORTCOMPAREMETHOD} +{ if defined, the field content comparison will use a method instead of fixed + functions - could be mandatory for tftArray field kind } + +type + /// exception raised by all TSynTable related code + ETableDataException = class(ESynException); + + /// the available types for any TSynTable field property + // - this is used in our so-called SBF compact binary format + // (similar to BSON or Protocol Buffers) + // - those types are used for both storage and JSON conversion + // - basic types are similar to SQLite3, i.e. Int64/Double/UTF-8/Blob + // - storage can be of fixed size, or of variable length + // - you can specify to use WinAnsi encoding instead of UTF-8 for string storage + // (it can use less space on disk than UTF-8 encoding) + // - BLOB fields can be either internal (i.e. handled by TSynTable like a + // RawByteString text storage), either external (i.e. must be stored in a dedicated + // storage structure - e.g. another TSynBigTable instance) + TSynTableFieldType = + (// unknown or not defined field type + tftUnknown, + // some fixed-size field value + tftBoolean, tftUInt8, tftUInt16, tftUInt24, tftInt32, tftInt64, + tftCurrency, tftDouble, + // some variable-size field value + tftVarUInt32, tftVarInt32, tftVarUInt64, + // text storage + tftWinAnsi, tftUTF8, + // BLOB fields + tftBlobInternal, tftBlobExternal, + // other variable-size field value + tftVarInt64); + + /// set of available field types for TSynTable + TSynTableFieldTypes = set of TSynTableFieldType; + + /// available option types for a field property + // - tfoIndex is set if an index must be created for this field + // - tfoUnique is set if field values must be unique (if set, the tfoIndex + // will be always forced) + // - tfoCaseInsensitive can be set to make no difference between 'a' and 'A' + // (by default, comparison is case-sensitive) - this option has an effect + // not only if tfoIndex or tfoUnique is set, but also for iterating search + TSynTableFieldOption = ( + tfoIndex, tfoUnique, tfoCaseInsensitive); + + /// set of option types for a field + TSynTableFieldOptions = set of TSynTableFieldOption; + + /// used to store bit set for all available fiels in a Table + // - with current format, maximum field count is 64 + TSynTableFieldBits = set of 0..63; + + /// an custom RawByteString type used to store internaly a data in + // our SBF compact binary format + TSBFString = type RawByteString; + + /// function prototype used to retrieve the index of a specified property name + // - 'ID' is handled separately: here must be available only the custom fields + TSynTableFieldIndex = function(const PropName: RawUTF8): integer of object; + + /// the recognized operators for a TSynTableStatement where clause + TSynTableStatementOperator = ( + opEqualTo, + opNotEqualTo, + opLessThan, + opLessThanOrEqualTo, + opGreaterThan, + opGreaterThanOrEqualTo, + opIn, + opIsNull, + opIsNotNull, + opLike, + opContains, + opFunction); + + TSynTableFieldProperties = class; + + /// one recognized SELECT expression for TSynTableStatement + TSynTableStatementSelect = record + /// the column SELECTed for the SQL statement, in the expected order + // - contains 0 for ID/RowID, or the RTTI field index + 1 + Field: integer; + /// an optional integer to be added + // - recognized from .. +123 .. -123 patterns in the select + ToBeAdded: integer; + /// the optional column alias, e.g. 'MaxID' for 'max(id) as MaxID' + Alias: RawUTF8; + /// the optional function applied to the SELECTed column + // - e.g. Max(RowID) would store 'Max' and SelectField[0]=0 + // - but Count(*) would store 'Count' and SelectField[0]=0, and + // set FunctionIsCountStart = TRUE + FunctionName: RawUTF8; + /// if the function needs a special process + // - e.g. funcCountStar for the special Count(*) expression or + // funcDistinct, funcMax for distinct(...)/max(...) aggregation + FunctionKnown: (funcNone, funcCountStar, funcDistinct, funcMax); + end; + + /// the recognized SELECT expressions for TSynTableStatement + TSynTableStatementSelectDynArray = array of TSynTableStatementSelect; + + /// one recognized WHERE expression for TSynTableStatement + TSynTableStatementWhere = record + /// any '(' before the actual expression + ParenthesisBefore: RawUTF8; + /// any ')' after the actual expression + ParenthesisAfter: RawUTF8; + /// expressions are evaluated as AND unless this field is set to TRUE + JoinedOR: boolean; + /// if this expression is preceded by a NOT modifier + NotClause: boolean; + /// the index of the field used for the WHERE expression + // - WhereField=0 for ID, 1 for field # 0, 2 for field #1, + // and so on... (i.e. WhereField = RTTI field index +1) + Field: integer; + /// the operator of the WHERE expression + Operator: TSynTableStatementOperator; + /// the SQL function name associated to a Field and Value + // - e.g. 'INTEGERDYNARRAYCONTAINS' and Field=0 for + // IntegerDynArrayContains(RowID,10) and ValueInteger=10 + // - Value does not contain anything + FunctionName: RawUTF8; + /// the value used for the WHERE expression + Value: RawUTF8; + /// the raw value SQL buffer used for the WHERE expression + ValueSQL: PUTF8Char; + /// the raw value SQL buffer length used for the WHERE expression + ValueSQLLen: integer; + /// an integer representation of WhereValue (used for ID check e.g.) + ValueInteger: integer; + /// used to fast compare with SBF binary compact formatted data + ValueSBF: TSBFString; + {$ifndef NOVARIANTS} + /// the value used for the WHERE expression, encoded as Variant + // - may be a TDocVariant for the IN operator + ValueVariant: variant; + {$endif} + end; + + /// the recognized WHERE expressions for TSynTableStatement + TSynTableStatementWhereDynArray = array of TSynTableStatementWhere; + + /// used to parse a SELECT SQL statement, following the SQlite3 syntax + // - handle basic REST commands, i.e. a SELECT over a single table (no JOIN) + // with its WHERE clause, and result column aliases + // - handle also aggregate functions like "SELECT Count(*) FROM TableName" + // - will also parse any LIMIT, OFFSET, ORDER BY, GROUP BY statement clause + TSynTableStatement = class + protected + fSQLStatement: RawUTF8; + fSelect: TSynTableStatementSelectDynArray; + fSelectFunctionCount: integer; + fTableName: RawUTF8; + fWhere: TSynTableStatementWhereDynArray; + fOrderByField: TSQLFieldIndexDynArray; + fGroupByField: TSQLFieldIndexDynArray; + fWhereHasParenthesis: boolean; + fOrderByDesc: boolean; + fLimit: integer; + fOffset: integer; + fWriter: TJSONWriter; + public + /// parse the given SELECT SQL statement and retrieve the corresponding + // parameters into this class read-only properties + // - the supplied GetFieldIndex() method is used to populate the + // SelectedFields and Where[].Field properties + // - SimpleFieldsBits is used for '*' field names + // - SQLStatement is left '' if the SQL statement is not correct + // - if SQLStatement is set, the caller must check for TableName to match + // the expected value, then use the Where[] to retrieve the content + // - if FieldProp is set, then the Where[].ValueSBF property is initialized + // with the SBF equivalence of the Where[].Value + constructor Create(const SQL: RawUTF8; GetFieldIndex: TSynTableFieldIndex; + SimpleFieldsBits: TSQLFieldBits=[0..MAX_SQLFIELDS-1]; + FieldProp: TSynTableFieldProperties=nil); + /// compute the SELECT column bits from the SelectFields array + procedure SelectFieldBits(var Fields: TSQLFieldBits; var withID: boolean); + + /// the SELECT SQL statement parsed + // - equals '' if the parsing failed + property SQLStatement: RawUTF8 read fSQLStatement; + /// the column SELECTed for the SQL statement, in the expected order + property Select: TSynTableStatementSelectDynArray read fSelect; + /// if the SELECTed expression of this SQL statement have any function defined + property SelectFunctionCount: integer read fSelectFunctionCount; + /// the retrieved table name + property TableName: RawUTF8 read fTableName; + /// the WHERE clause of this SQL statement + property Where: TSynTableStatementWhereDynArray read fWhere; + /// if the WHERE clause contains any ( ) parenthesis expression + property WhereHasParenthesis: boolean read fWhereHasParenthesis; + /// recognize an GROUP BY clause with one or several fields + // - here 0 = ID, otherwise RTTI field index +1 + property GroupByField: TSQLFieldIndexDynArray read fGroupByField; + /// recognize an ORDER BY clause with one or several fields + // - here 0 = ID, otherwise RTTI field index +1 + property OrderByField: TSQLFieldIndexDynArray read fOrderByField; + /// false for default ASC order, true for DESC attribute + property OrderByDesc: boolean read fOrderByDesc; + /// the number specified by the optional LIMIT ... clause + // - set to 0 by default (meaning no LIMIT clause) + property Limit: integer read fLimit; + /// the number specified by the optional OFFSET ... clause + // - set to 0 by default (meaning no OFFSET clause) + property Offset: integer read fOffset; + /// optional associated writer + property Writer: TJSONWriter read fWriter write fWriter; + end; + + /// function prototype used to retrieve the RECORD data of a specified Index + // - the index is not the per-ID index, but the "physical" index, i.e. the + // index value used to retrieve data from low-level (and faster) method + // - should return nil if Index is out of range + // - caller must provide a temporary storage buffer to be used optionally + TSynTableGetRecordData = function( + Index: integer; var aTempData: RawByteString): pointer of object; + + TSynTable = class; + + {$ifdef SORTCOMPAREMETHOD} + /// internal value used by TSynTableFieldProperties.SortCompare() method to + // avoid stack allocation + TSortCompareTmp = record + PB1, PB2: PByte; + L1,L2: integer; + end; + {$endif} + + /// store the type properties of a given field / database column + TSynTableFieldProperties = class + protected + /// used during OrderedIndexSort to prevent stack usage + SortPivot: pointer; + {$ifdef SORTCOMPAREMETHOD} + /// internal value used by SortCompare() method to avoid stack allocation + SortCompareTmp: TSortCompareTmp; + {$endif} + /// these two temporary buffers are used to call TSynTableGetRecordData + DataTemp1, DataTemp2: RawByteString; + /// the associated table which own this field property + Owner: TSynTable; + /// the global size of a default field value, as encoded + // in our SBF compact binary format + fDefaultFieldLength: integer; + /// a default field data, as encoded in our SBF compact binary format + fDefaultFieldData: TSBFString; + /// last >=0 value returned by the last OrderedIndexFindAdd() call + fOrderedIndexFindAdd: integer; + /// used for internal QuickSort of OrderedIndex[] + // - call SortCompare() for sorting the items + procedure OrderedIndexSort(L,R: PtrInt); + /// retrieve an index from OrderedIndex[] of the given value + // - call SortCompare() to compare to the reference value + function OrderedIndexFind(Value: pointer): PtrInt; + /// retrieve an index where a Value must be added into OrderedIndex[] + // - call SortCompare() to compare to the reference value + // - returns -1 if Value is there, or the index where to insert + // - the returned value (if >= 0) will be stored in fOrderedIndexFindAdd + function OrderedIndexFindAdd(Value: pointer): PtrInt; + /// set OrderedIndexReverse[OrderedIndex[aOrderedIndex]] := aOrderedIndex; + procedure OrderedIndexReverseSet(aOrderedIndex: integer); + public + /// the field name + Name: RawUTF8; + /// kind of field (defines both value type and storage to be used) + FieldType: TSynTableFieldType; + /// the fixed-length size, or -1 for a varInt, -2 for a variable string + FieldSize: integer; + /// options of this field + Options: TSynTableFieldOptions; + /// contains the offset of this field, in case of fixed-length field + // - normaly, fixed-length fields are stored in the beginning of the record + // storage: in this case, a value >= 0 will point to the position of the + // field value of this field + // - if the value is < 0, its absolute will be the field number to be counted + // after TSynTable.fFieldVariableOffset (-1 for first item) + Offset: integer; + /// number of the field in the table (starting at 0) + FieldNumber: integer; + /// if allocated, contains the storage indexes of every item, in sorted order + // - only available if tfoIndex is in Options + // - the index is not the per-ID index, but the "physical" index, i.e. the + // index value used to retrieve data from low-level (and faster) method + OrderedIndex: TIntegerDynArray; + /// if allocated, contains the reverse storage index of OrderedIndex + // - i.e. OrderedIndexReverse[OrderedIndex[i]] := i; + // - used to speed up the record update procedure with huge number of + // records + OrderedIndexReverse: TIntegerDynArray; + /// number of items in OrderedIndex[] + // - is set to 0 when the content has been modified (mark force recreate) + OrderedIndexCount: integer; + /// if set to TRUE after an OrderedIndex[] refresh but with not sorting + // - OrderedIndexSort(0,OrderedIndexCount-1) must be called before using + // the OrderedIndex[] array + // - you should call OrderedIndexRefresh method to ensure it is sorted + OrderedIndexNotSorted: boolean; + /// all TSynValidate instances registered per each field + Filters: TObjectList; + /// all TSynValidate instances registered per each field + Validates: TObjectList; + /// low-level binary comparison used by IDSort and TSynTable.IterateJSONValues + // - P1 and P2 must point to the values encoded in our SBF compact binary format + {$ifdef SORTCOMPAREMETHOD} + function SortCompare(P1,P2: PUTF8Char): PtrInt; + {$else} + SortCompare: TUTF8Compare; + {$endif} + + /// read entry from a specified file reader + constructor CreateFrom(var RD: TFileBufferReader); + /// release associated memory and objects + destructor Destroy; override; + /// save entry to a specified file writer + procedure SaveTo(WR: TFileBufferWriter); + /// decode the value from our SBF compact binary format into UTF-8 JSON + // - returns the next FieldBuffer value + function GetJSON(FieldBuffer: pointer; W: TTextWriter): pointer; + /// decode the value from our SBF compact binary format into UTF-8 text + // - this method does not check for FieldBuffer to be not nil -> caller + // should check this explicitely + function GetValue(FieldBuffer: pointer): RawUTF8; + /// decode the value from a record buffer into an Boolean + // - will call Owner.GetData to retrieve then decode the field SBF content + function GetBoolean(RecordBuffer: pointer): Boolean; + {$ifdef HASINLINE}inline;{$endif} + /// decode the value from a record buffer into an integer + // - will call Owner.GetData to retrieve then decode the field SBF content + function GetInteger(RecordBuffer: pointer): Integer; + /// decode the value from a record buffer into an Int64 + // - will call Owner.GetData to retrieve then decode the field SBF content + function GetInt64(RecordBuffer: pointer): Int64; + /// decode the value from a record buffer into an floating-point value + // - will call Owner.GetData to retrieve then decode the field SBF content + function GetDouble(RecordBuffer: pointer): Double; + /// decode the value from a record buffer into an currency value + // - will call Owner.GetData to retrieve then decode the field SBF content + function GetCurrency(RecordBuffer: pointer): Currency; + /// decode the value from a record buffer into a RawUTF8 string + // - will call Owner.GetData to retrieve then decode the field SBF content + function GetRawUTF8(RecordBuffer: pointer): RawUTF8; + {$ifndef NOVARIANTS} + /// decode the value from our SBF compact binary format into a Variant + function GetVariant(FieldBuffer: pointer): Variant; overload; + {$ifdef HASINLINE}inline;{$endif} + /// decode the value from our SBF compact binary format into a Variant + procedure GetVariant(FieldBuffer: pointer; var result: Variant); overload; + {$endif} + /// retrieve the binary length (in bytes) of some SBF compact binary format + function GetLength(FieldBuffer: pointer): Integer; + {$ifdef HASINLINE}inline;{$endif} + /// create some SBF compact binary format from a Delphi binary value + // - will return '' if the field type doesn't match a boolean + function SBF(const Value: Boolean): TSBFString; overload; + /// create some SBF compact binary format from a Delphi binary value + // - will encode any byte, word, integer, cardinal, Int64 value + // - will return '' if the field type doesn't match an integer + function SBF(const Value: Int64): TSBFString; overload; + /// create some SBF compact binary format from a Delphi binary value + // - will encode any byte, word, integer, cardinal value + // - will return '' if the field type doesn't match an integer + function SBF(const Value: Integer): TSBFString; overload; + /// create some SBF compact binary format from a Delphi binary value + // - will return '' if the field type doesn't match a currency + // - we can't use SBF() method name because of Currency/Double ambiguity + function SBFCurr(const Value: Currency): TSBFString; + /// create some SBF compact binary format from a Delphi binary value + // - will return '' if the field type doesn't match a floating-point + // - we can't use SBF() method name because of Currency/Double ambiguity + function SBFFloat(const Value: Double): TSBFString; + /// create some SBF compact binary format from a Delphi binary value + // - expect a RawUTF8 string: will be converted to WinAnsiString + // before storage, for tftWinAnsi + // - will return '' if the field type doesn't match a string + function SBF(const Value: RawUTF8): TSBFString; overload; + /// create some SBF compact binary format from a BLOB memory buffer + // - will return '' if the field type doesn't match tftBlobInternal + function SBF(Value: pointer; ValueLen: integer): TSBFString; overload; + /// convert any UTF-8 encoded value into our SBF compact binary format + // - can be used e.g. from a WHERE clause, for fast comparison in + // TSynTableStatement.WhereValue content using OrderedIndex[] + // - is the reverse of GetValue/GetRawUTF8 methods above + function SBFFromRawUTF8(const aValue: RawUTF8): TSBFString; + {$ifndef NOVARIANTS} + /// create some SBF compact binary format from a Variant value + function SBF(const Value: Variant): TSBFString; overload; + {$endif} + + /// will update then sort the array of indexes used for the field index + // - the OrderedIndex[] array is first refreshed according to the + // aOldIndex, aNewIndex parameters: aOldIndex=-1 for Add, aNewIndex=-1 for + // Delete, or both >= 0 for update + // - call with both indexes = -1 will sort the existing OrderedIndex[] array + // - GetData property must have been set with a method returning a pointer + // to the field data for a given index (this index is not the per-ID index, + // but the "physical" index, i.e. the index value used to retrieve data + // from low-level (and fast) GetData method) + // - aOldRecordData and aNewRecordData can be specified in order to guess + // if the field data has really been modified (speed up the update a lot + // to only sort indexed fields if its content has been really modified) + // - returns FALSE if any parameter is invalid + function OrderedIndexUpdate(aOldIndex, aNewIndex: integer; + aOldRecordData, aNewRecordData: pointer): boolean; + /// retrieve one or more "physical" indexes matching a WHERE Statement + // - is faster than O(1) GetIteraring(), because will use O(log(n)) binary + // search using the OrderedIndex[] array + // - returns the resulting indexes as a a sorted list in MatchIndex/MatchIndexCount + // - if the indexes are already present in the list, won't duplicate them + // - WhereSBFValue must be a valid SBF formated field buffer content + // - the Limit parameter is similar to the SQL LIMIT clause: if greater than 0, + // an upper bound on the number of rows returned is placed (e.g. set Limit=1 + // to only retrieve the first match) + // - GetData property must have been set with a method returning a pointer + // to the field data for a given index (this index is not the per-ID index, + // but the "physical" index, i.e. the index value used to retrieve data + // from low-level (and fast) GetData method) + // - in this method, indexes are not the per-ID indexes, but the "physical" + // indexes, i.e. each index value used to retrieve data from low-level + // (and fast) GetData method + function OrderedIndexMatch(WhereSBFValue: pointer; + var MatchIndex: TIntegerDynArray; var MatchIndexCount: integer; + Limit: Integer=0): Boolean; + /// will force refresh the OrderedIndex[] array + // - to be called e.g. if OrderedIndexNotSorted = TRUE, if you want to + // access to the OrderedIndex[] array + procedure OrderedIndexRefresh; + /// register a custom filter or validation rule to the class for this field + // - this will be used by Filter() and Validate() methods + // - will return the specified associated TSynFilterOrValidate instance + // - a TSynValidateTableUniqueField is always added by + // TSynTable.AfterFieldModif if tfoUnique is set in Options + function AddFilterOrValidate(aFilter: TSynFilterOrValidate): TSynFilterOrValidate; + /// check the registered constraints + // - returns '' on success + // - returns an error message e.g. if a tftUnique constraint failed + // - RecordIndex=-1 in case of adding, or the physical index of the updated record + function Validate(RecordBuffer: pointer; RecordIndex: integer): string; + /// some default SBF compact binary format content + property SBFDefault: TSBFString read fDefaultFieldData; + end; + + +{$ifndef DELPHI5OROLDER} + + /// a pointer to structure used to store a TSynTable record + PSynTableData = ^TSynTableData; + + {$A-} { packet object not allowed since Delphi 2009 :( } + /// used to store a TSynTable record using our SBF compact binary format + // - this object can be created on the stack + // - it is mapped into a variant TVarData, to be retrieved by the + // TSynTable.Data method - but direct allocation of a TSynTableData on the + // stack is faster (due to the Variant overhead) + // - is defined either as an object either as a record, due to a bug + // in Delphi 2009/2010 compiler (at least): this structure is not initialized + // if defined as an object on the stack, but will be as a record :( + {$ifdef UNICODE}TSynTableData = record{$else}TSynTableData = object{$endif UNICODE} + {$ifdef UNICODE}private{$else}protected{$endif UNICODE} + VType: TVarType; + Filler: array[1..SizeOf(TVarData)-SizeOf(TVarType)-SizeOf(pointer)*2-4] of byte; + VID: integer; + VTable: TSynTable; + VValue: TSBFString; + {$ifndef NOVARIANTS} + function GetFieldValue(const FieldName: RawUTF8): Variant; overload; + procedure GetFieldVariant(const FieldName: RawUTF8; var result: Variant); + procedure SetFieldValue(const FieldName: RawUTF8; const Value: Variant); overload; + {$endif} + /// raise an exception if VTable=nil + procedure CheckVTableInitialized; + {$ifdef HASINLINE}inline;{$endif} + public + /// initialize a record data content for a specified table + // - a void content is set + procedure Init(aTable: TSynTable; aID: Integer=0); overload; {$ifdef HASINLINE}inline;{$endif} + /// initialize a record data content for a specified table + // - the specified SBF content is store inside this TSynTableData + procedure Init(aTable: TSynTable; aID: Integer; RecordBuffer: pointer; + RecordBufferLen: integer); overload; + /// the associated record ID + property ID: integer read VID write VID; + /// the associated TSynTable instance + property Table: TSynTable read VTable write VTable; + /// the record content, SBF compact binary format encoded + property SBF: TSBFString read VValue; + {$ifndef NOVARIANTS} + /// set or retrieve a field value from a variant data + property Field[const FieldName: RawUTF8]: Variant read GetFieldValue write SetFieldValue; + /// get a field value for a specified field + // - this method is faster than Field[], because it won't look for the field name + function GetFieldValue(aField: TSynTableFieldProperties): Variant; overload; + /// set a field value for a specified field + // - this method is faster than Field[], because it won't look for the field name + procedure SetFieldValue(aField: TSynTableFieldProperties; const Value: Variant); overload; + {$ifdef HASINLINE}inline;{$endif} + {$endif} + /// set a field value for a specified field, from SBF-encoded data + // - this method is faster than the other, because it won't look for the field + // name nor make any variant conversion + procedure SetFieldSBFValue(aField: TSynTableFieldProperties; const Value: TSBFString); + /// get a field value for a specified field, into SBF-encoded data + // - this method is faster than the other, because it won't look for the field + // name nor make any variant conversion + function GetFieldSBFValue(aField: TSynTableFieldProperties): TSBFString; + /// filter the SBF buffer record content with all registered filters + // - all field values are filtered in-place, following our SBF compact + // binary format encoding for this record + procedure FilterSBFValue; {$ifdef HASINLINE}inline;{$endif} + /// check the registered constraints according to a record SBF buffer + // - returns '' on success + // - returns an error message e.g. if a tftUnique constraint failed + // - RecordIndex=-1 in case of adding, or the physical index of the updated record + function ValidateSBFValue(RecordIndex: integer): string; + end; + {$A+} { packet object not allowed since Delphi 2009 :( } +{$endif DELPHI5OROLDER} + + PUpdateFieldEvent = ^TUpdateFieldEvent; + + /// an opaque structure used for TSynTable.UpdateFieldEvent method + TUpdateFieldEvent = record + /// the number of record added + Count: integer; + /// the list of IDs added + // - this list is already in increasing order, because GetIterating was + // called with the ioID order + IDs: TIntegerDynArray; + /// the offset of every record added + // - follows the IDs[] order + Offsets64: TInt64DynArray; + /// previous indexes: NewIndexs[oldIndex] := newIndex + NewIndexs: TIntegerDynArray; + /// the list of existing field in the previous data + AvailableFields: TSQLFieldBits; + /// where to write the updated data + WR: TFileBufferWriter; + end; + + /// will define a validation to be applied to a TSynTableFieldProperties field + // - a typical usage is to validate a value to be unique in the table + // (implemented in the TSynValidateTableUniqueField class) + // - the optional associated parameters are to be supplied JSON-encoded + // - ProcessField and ProcessRecordIndex properties will be filled before + // Process method call by TSynTableFieldProperties.Validate() + TSynValidateTable = class(TSynValidate) + protected + fProcessField: TSynTableFieldProperties; + fProcessRecordIndex: integer; + public + /// the associated TSQLRest instance + // - this value is filled by TSynTableFieldProperties.Validate with its + // self value to be used for the validation + // - it can be used in the overridden Process method + property ProcessField: TSynTableFieldProperties read fProcessField write fProcessField; + /// the associated record index (in case of update) + // - is set to -1 in case of adding, or the physical index of the updated record + // - this value is filled by TSynTableFieldProperties.Validate + // - it can be used in the overridden Process method + property ProcessRecordIndex: integer read fProcessRecordIndex write fProcessRecordIndex; + end; + + /// will define a validation for a TSynTableFieldProperties Unique field + // - implement constraints check e.g. if tfoUnique is set in Options + // - it will check that the field value is not void + // - it will check that the field value is not a duplicate + TSynValidateTableUniqueField = class(TSynValidateTable) + public + /// perform the unique field validation action to the specified value + // - duplication value check will use the ProcessField and + // ProcessRecordIndex properties, which will be filled before call by + // TSynTableFieldProperties.Validate() + // - aFieldIndex parameter is not used here, since we have already the + // ProcessField property set + // - here the Value is expected to be UTF-8 text, as converted from our SBF + // compact binary format via e.g. TSynTableFieldProperties.GetValue / + // GetRawUTF8: this is mandatory to have the validation rule fit with other + // TSynValidateTable classes + function Process(aFieldIndex: integer; const Value: RawUTF8; var ErrorMsg: string): boolean; override; + end; + + /// store the description of a table with records, to implement a Database + // - can be used with several storage engines, for instance TSynBigTableRecord + // - each record can have up to 64 fields + // - a mandatory ID field must be handled by the storage engine itself + // - will handle the storage of records into our SBF compact binary format, in + // which fixed-length fields are stored leftmost side, then variable-length + // fields follow + TSynTable = class + protected + fTableName: RawUTF8; + /// list of TSynTableFieldProperties instances + fField: TObjectList; + /// offset of the first variable length value field + fFieldVariableOffset: PtrUInt; + /// index of the first variable length value field + // - equals -1 if no variable length field exists + fFieldVariableIndex: integer; + /// bit is set for a tftWinAnsi, tftUTF8 or tftBlobInternal kind of field + // - these kind of field are encoded as a VarInt length, then the data + fFieldIsVarString: TSynTableFieldBits; + /// bit is set for a tftBlobExternal kind of field e.g. + fFieldIsExternal: TSynTableFieldBits; + /// event used for proper data retrieval of a given record buffer + fGetRecordData: TSynTableGetRecordData; + /// the global size of a default value, as encoded in our SBF compact binary format + fDefaultRecordLength: integer; + /// a default record data, as encoded in our SBF compact binary format + fDefaultRecordData: TSBFString; + /// list of TSynTableFieldProperties added via all AddField() call + fAddedField: TList; + /// true if any field has a tfoUnique option set + fFieldHasUniqueIndexes: boolean; + function GetFieldType(Index: integer): TSynTableFieldProperties; + function GetFieldCount: integer; + function GetFieldFromName(const aName: RawUTF8): TSynTableFieldProperties; + function GetFieldIndexFromName(const aName: RawUTF8): integer; + /// this method matchs the TSynTableFieldIndex event type + function GetFieldIndexFromShortName(const aName: ShortString): integer; + /// refresh Offset,FieldNumber,FieldSize and fFieldVariableIndex,fFieldVariableOffset + procedure AfterFieldModif; + public + /// create a table definition instance + constructor Create(const aTableName: RawUTF8); + /// create a table definition instance from a specified file reader + procedure LoadFrom(var RD: TFileBufferReader); + /// release used memory + destructor Destroy; override; + /// save field properties to a specified file writer + procedure SaveTo(WR: TFileBufferWriter); + + /// retrieve to the corresponding data address of a given field + function GetData(RecordBuffer: PUTF8Char; Field: TSynTableFieldProperties): pointer; + /// add a field description to the table + // - warning: the class responsible of the storage itself must process the + // data already stored when a field is created, e.g. in + // TSynBigTableRecord.AddFieldUpdate method + // - physical order does not necessary follow the AddField() call order: + // for better performance, it will try to store fixed-sized record first, + // multiple of 4 bytes first (access is faster if dat is 4 byte aligned), + // then variable-length after fixed-sized fields; in all case, a field + // indexed will be put first + function AddField(const aName: RawUTF8; aType: TSynTableFieldType; + aOptions: TSynTableFieldOptions=[]): TSynTableFieldProperties; + /// update a record content + // - return the updated record data, in our SBF compact binary format + // - if NewFieldData is not specified, a default 0 or '' value is appended + // - if NewFieldData is set, it must match the field value kind + // - warning: this method will update result in-place, so RecordBuffer MUST + // be <> pointer(result) or data corruption may occur + procedure UpdateFieldData(RecordBuffer: PUTF8Char; RecordBufferLen, + FieldIndex: integer; var result: TSBFString; const NewFieldData: TSBFString=''); + /// update a record content after any AddfieldUpdate, to refresh the data + // - AvailableFields must contain the list of existing fields in the previous data + function UpdateFieldRecord(RecordBuffer: PUTF8Char; var AvailableFields: TSQLFieldBits): TSBFString; + /// this Event is to be called for all data records (via a GetIterating method) + // after any AddfieldUpdate, to refresh the data + // - Opaque is in fact a pointer to a TUpdateFieldEvent record, and will contain + // all parameters set by TSynBigTableRecord.AddFieldUpdate, including a + // TFileBufferWriter instance to use to write the recreated data + // - it will work with either any newly added field, handly also field data + // order change in SBF record (e.g. when a fixed-sized field has been added + // on a record containing variable-length fields) + function UpdateFieldEvent(Sender: TObject; Opaque: pointer; ID, Index: integer; + Data: pointer; DataLen: integer): boolean; + /// event which must be called by the storage engine when some values are modified + // - if aOldIndex and aNewIndex are both >= 0, the corresponding aOldIndex + // will be replaced by aNewIndex value (i.e. called in case of a data Update) + // - if aOldIndex is -1 and aNewIndex is >= 0, aNewIndex refers to a just + // created item (i.e. called in case of a data Add) + // - if aOldIndex is >= 0 and aNewIndex is -1, aNewIndex refers to a just + // deleted item (i.e. called in case of a data Delete) + // - will update then sort all existing TSynTableFieldProperties.OrderedIndex + // values + // - the GetDataBuffer protected virtual method must have been overridden to + // properly return the record data for a given "physical/stored" index + // - aOldRecordData and aNewRecordData can be specified in order to guess + // if the field data has really been modified (speed up the update a lot + // to only sort indexed fields if its content has been really modified) + procedure FieldIndexModify(aOldIndex, aNewIndex: integer; + aOldRecordData, aNewRecordData: pointer); + /// return the total length of the given record buffer, encoded in our SBF + // compact binary format + function DataLength(RecordBuffer: pointer): integer; + {$ifndef NOVARIANTS} + /// create a Variant able to access any field content via late binding + // - i.e. you can use Var.Name to access the 'Name' field of record Var + // - if you leave ID and RecordBuffer void, a void record is created + function Data(aID: integer=0; RecordBuffer: pointer=nil; + RecordBufferLen: Integer=0): Variant; overload; + {$endif NOVARIANTS} + /// return a default content for ALL record fields + // - uses our SBF compact binary format + property DefaultRecordData: TSBFString read fDefaultRecordData; + /// list of TSynTableFieldProperties added via all AddField() call + // - this list will allow TSynBigTableRecord.AddFieldUpdate to refresh + // the data on disk according to the new field configuration + property AddedField: TList read fAddedField write fAddedField; + /// offset of the first variable length value field + property FieldVariableOffset: PtrUInt read fFieldVariableOffset; + public + {$ifndef DELPHI5OROLDER} + /// create a TJSONWriter, ready to be filled with GetJSONValues(W) below + // - will initialize all TJSONWriter.ColNames[] values according to the + // specified Fields index list, and initialize the JSON content + function CreateJSONWriter(JSON: TStream; Expand, withID: boolean; + const Fields: TSQLFieldIndexDynArray): TJSONWriter; overload; + /// create a TJSONWriter, ready to be filled with GetJSONValues(W) below + // - will initialize all TJSONWriter.ColNames[] values according to the + // specified Fields bit set, and initialize the JSON content + function CreateJSONWriter(JSON: TStream; Expand, withID: boolean; + const Fields: TSQLFieldBits): TJSONWriter; overload; + (** return the UTF-8 encoded JSON objects for the values contained + in the specified RecordBuffer encoded in our SBF compact binary format, + according to the Expand/WithID/Fields parameters of W + - if W.Expand is true, JSON data is an object, for direct use with any Ajax or .NET client: + ! {"col1":val11,"col2":"val12"} + - if W.Expand is false, JSON data is serialized (as used in TSQLTableJSON) + ! { "fieldCount":1,"values":["col1","col2",val11,"val12",val21,..] } + - only fields with a bit set in W.Fields will be appended + - if W.WithID is true, then the first ID field value is included *) + procedure GetJSONValues(aID: integer; RecordBuffer: PUTF8Char; W: TJSONWriter); + /// can be used to retrieve all values matching a preparated TSynTableStatement + // - this method matchs the TSynBigTableIterateEvent callback definition + // - Sender will be the TSynBigTable instance, and Opaque will point to a + // TSynTableStatement instance (with all fields initialized, including Writer) + function IterateJSONValues(Sender: TObject; Opaque: pointer; ID: integer; + Data: pointer; DataLen: integer): boolean; + {$endif DELPHI5OROLDER} + /// check the registered constraints according to a record SBF buffer + // - returns '' on success + // - returns an error message e.g. if a tftUnique constraint failed + // - RecordIndex=-1 in case of adding, or the physical index of the updated record + function Validate(RecordBuffer: pointer; RecordIndex: integer): string; + /// filter the SBF buffer record content with all registered filters + // - all field values are filtered in-place, following our SBF compact + // binary format encoding for this record + procedure Filter(var RecordBuffer: TSBFString); + + /// event used for proper data retrieval of a given record buffer, according + // to the physical/storage index value (not per-ID index) + // - if not set, field indexes won't work + // - will be mapped e.g. to TSynBigTable.GetPointerFromPhysicalIndex + property GetRecordData: TSynTableGetRecordData read fGetRecordData write fGetRecordData; + public + /// the internal Table name used to identify it (e.g. from JSON or SQL) + // - similar to the SQL Table name + property TableName: RawUTF8 read fTableName write fTableName; + /// number of fields in this table + property FieldCount: integer read GetFieldCount; + /// retrieve the properties of a given field + // - returns nil if the specified Index is out of range + property Field[Index: integer]: TSynTableFieldProperties read GetFieldType; + /// retrieve the properties of a given field + // - returns nil if the specified Index is out of range + property FieldFromName[const aName: RawUTF8]: TSynTableFieldProperties read GetFieldFromName; default; + /// retrieve the index of a given field + // - returns -1 if the specified Index is out of range + property FieldIndexFromName[const aName: RawUTF8]: integer read GetFieldIndexFromName; + /// read-only access to the Field list + property FieldList: TObjectList read fField; + /// true if any field has a tfoUnique option set + property HasUniqueIndexes: boolean read fFieldHasUniqueIndexes; + end; + +{$ifndef NOVARIANTS} + /// a custom variant type used to have direct access to a record content + // - use TSynTable.Data method to retrieve such a Variant + // - this variant will store internaly a SBF compact binary format + // representation of the record content + // - uses internally a TSynTableData object + TSynTableVariantType = class(TSynInvokeableVariantType) + protected + procedure IntGet(var Dest: TVarData; const V: TVarData; Name: PAnsiChar); override; + procedure IntSet(const V, Value: TVarData; Name: PAnsiChar); override; + public + /// retrieve the SBF compact binary format representation of a record content + class function ToSBF(const V: Variant): TSBFString; + /// retrieve the ID value associated to a record content + class function ToID(const V: Variant): integer; + /// retrieve the TSynTable instance associated to a record content + class function ToTable(const V: Variant): TSynTable; + /// clear the content + procedure Clear(var V: TVarData); override; + /// copy two record content + procedure Copy(var Dest: TVarData; const Source: TVarData; + const Indirect: Boolean); override; + end; +{$endif NOVARIANTS} + +const + /// used by TSynTableStatement.WhereField for "SELECT .. FROM TableName WHERE ID=?" + SYNTABLESTATEMENTWHEREID = 0; + + +/// low-level integer comparison according to a specified operator +// - SBF must point to the values encoded in our SBF compact binary format +// - Value must contain the plain integer value +// - Value can be a Currency accessed via a PInt64 +// - will work only for tftBoolean, tftUInt8, tftUInt16, tftUInt24, +// tftInt32, tftInt64 and tftCurrency field types +// - will handle only soEqualTo...soGreaterThanOrEqualTo operators +// - if SBFEnd is not nil, it will test for all values until SBF>=SBFEnd +// (can be used for tftArray) +// - returns true if both values match, or false otherwise +function CompareOperator(FieldType: TSynTableFieldType; SBF, SBFEnd: PUTF8Char; + Value: Int64; Oper: TCompareOperator): boolean; overload; + +/// low-level floating-point comparison according to a specified operator +// - SBF must point to the values encoded in our SBF compact binary format +// - Value must contain the plain floating-point value +// - will work only for tftDouble field type +// - will handle only soEqualTo...soGreaterThanOrEqualTo operators +// - if SBFEnd is not nil, it will test for all values until SBF>=SBFEnd +// (can be used for tftArray) +// - returns true if both values match, or false otherwise +function CompareOperator(SBF, SBFEnd: PUTF8Char; + Value: double; Oper: TCompareOperator): boolean; overload; + +/// low-level text comparison according to a specified operator +// - SBF must point to the values encoded in our SBF compact binary format +// - Value must contain the plain text value, in the same encoding (either +// WinAnsi either UTF-8, as FieldType defined for the SBF value) +// - will work only for tftWinAnsi and tftUTF8 field types +// - will handle all kind of operators (including soBeginWith, soContains and +// soSoundsLike*) but soSoundsLike* won't make use of the CaseSensitive parameter +// - for soSoundsLikeEnglish, soSoundsLikeFrench and soSoundsLikeSpanish +// operators, Value is not a real PUTF8Char but a prepared PSynSoundEx +// - if SBFEnd is not nil, it will test for all values until SBF>=SBFEnd +// (can be used for tftArray) +// - returns true if both values match, or false otherwise +function CompareOperator(FieldType: TSynTableFieldType; SBF, SBFEnd: PUTF8Char; + Value: PUTF8Char; ValueLen: integer; Oper: TCompareOperator; + CaseSensitive: boolean): boolean; overload; + +/// convert any AnsiString content into our SBF compact binary format storage +procedure ToSBFStr(const Value: RawByteString; out Result: TSBFString); + + +{ ************ low-level buffer processing functions ************************* } + +type + /// implements a thread-safe Bloom Filter storage + // - a "Bloom Filter" is a space-efficient probabilistic data structure, + // that is used to test whether an element is a member of a set. False positive + // matches are possible, but false negatives are not. Elements can be added to + // the set, but not removed. Typical use cases are to avoid unecessary + // slow disk or network access if possible, when a lot of items are involved. + // - memory use is very low, when compared to storage of all values: fewer + // than 10 bits per element are required for a 1% false positive probability, + // independent of the size or number of elements in the set - for instance, + // storing 10,000,000 items presence with 1% of false positive ratio + // would consume only 11.5 MB of memory, using 7 hash functions + // - use Insert() methods to add an item to the internal bits array, and + // Reset() to clear all bits array, if needed + // - MayExist() function would check if the supplied item was probably set + // - SaveTo() and LoadFrom() methods allow transmission of the bits array, + // for a disk/database storage or transmission over a network + // - internally, several (hardware-accelerated) crc32c hash functions will be + // used, with some random seed values, to simulate several hashing functions + // - Insert/MayExist/Reset methods are thread-safe + TSynBloomFilter = class(TSynPersistentLock) + private + fSize: cardinal; + fFalsePositivePercent: double; + fBits: cardinal; + fHashFunctions: cardinal; + fInserted: cardinal; + fStore: RawByteString; + function GetInserted: cardinal; + public + /// initialize the internal bits storage for a given number of items + // - by default, internal bits array size will be guess from a 1 % false + // positive rate - but you may specify another value, to reduce memory use + // - this constructor would compute and initialize Bits and HashFunctions + // corresponding to the expected false positive ratio + constructor Create(aSize: integer; aFalsePositivePercent: double = 1); reintroduce; overload; + /// initialize the internal bits storage from a SaveTo() binary buffer + // - this constructor will initialize the internal bits array calling LoadFrom() + constructor Create(const aSaved: RawByteString; aMagic: cardinal=$B1003F11); reintroduce; overload; + /// add an item in the internal bits array storage + // - this method is thread-safe + procedure Insert(const aValue: RawByteString); overload; + /// add an item in the internal bits array storage + // - this method is thread-safe + procedure Insert(aValue: pointer; aValueLen: integer); overload; virtual; + /// clear the internal bits array storage + // - you may call this method after some time, if some items may have + // been removed, to reduce false positives + // - this method is thread-safe + procedure Reset; virtual; + /// returns TRUE if the supplied items was probably set via Insert() + // - some false positive may occur, but not much than FalsePositivePercent + // - this method is thread-safe + function MayExist(const aValue: RawByteString): boolean; overload; + /// returns TRUE if the supplied items was probably set via Insert() + // - some false positive may occur, but not much than FalsePositivePercent + // - this method is thread-safe + function MayExist(aValue: pointer; aValueLen: integer): boolean; overload; + /// store the internal bits array into a binary buffer + // - may be used to transmit or store the state of a dataset, avoiding + // to recompute all Insert() at program startup, or to synchronize + // networks nodes information and reduce the number of remote requests + function SaveTo(aMagic: cardinal=$B1003F11): RawByteString; overload; + /// store the internal bits array into a binary buffer + // - may be used to transmit or store the state of a dataset, avoiding + // to recompute all Insert() at program startup, or to synchronize + // networks nodes information and reduce the number of remote requests + procedure SaveTo(aDest: TFileBufferWriter; aMagic: cardinal=$B1003F11); overload; + /// read the internal bits array from a binary buffer + // - as previously serialized by the SaveTo method + // - may be used to transmit or store the state of a dataset + function LoadFrom(const aSaved: RawByteString; aMagic: cardinal=$B1003F11): boolean; overload; + /// read the internal bits array from a binary buffer + // - as previously serialized by the SaveTo method + // - may be used to transmit or store the state of a dataset + function LoadFrom(P: PByte; PLen: integer; aMagic: cardinal=$B1003F11): boolean; overload; virtual; + published + /// maximum number of items which are expected to be inserted + property Size: cardinal read fSize; + /// expected percentage (1..100) of false positive results for MayExists() + property FalsePositivePercent: double read fFalsePositivePercent; + /// number of bits stored in the internal bits array + property Bits: cardinal read fBits; + /// how many hash functions would be applied for each Insert() + property HashFunctions: cardinal read fHashFunctions; + /// how many times the Insert() method has been called + property Inserted: cardinal read GetInserted; + end; + + /// implements a thread-safe differential Bloom Filter storage + // - this inherited class is able to compute incremental serialization of + // its internal bits array, to reduce network use + // - an obfuscated revision counter is used to identify storage history + TSynBloomFilterDiff = class(TSynBloomFilter) + protected + fRevision: Int64; + fSnapShotAfterMinutes: cardinal; + fSnapshotAfterInsertCount: cardinal; + fSnapshotTimestamp: Int64; + fSnapshotInsertCount: cardinal; + fKnownRevision: Int64; + fKnownStore: RawByteString; + public + /// add an item in the internal bits array storage + // - this overloaded thread-safe method would compute fRevision + procedure Insert(aValue: pointer; aValueLen: integer); override; + /// clear the internal bits array storage + // - this overloaded thread-safe method would reset fRevision + procedure Reset; override; + /// store the internal bits array into an incremental binary buffer + // - here the difference from a previous SaveToDiff revision will be computed + // - if aKnownRevision is outdated (e.g. if equals 0), the whole bits array + // would be returned, and around 10 bits per item would be transmitted + // (for 1% false positive ratio) + // - incremental retrieval would then return around 10 bytes per newly added + // item since the last snapshot reference state (with 1% ratio, i.e. 7 hash + // functions) + function SaveToDiff(const aKnownRevision: Int64): RawByteString; + /// use the current internal bits array state as known revision + // - is done the first time SaveToDiff() is called, then after 1/32th of + // the filter size has been inserted (see SnapshotAfterInsertCount property), + // or after SnapShotAfterMinutes property timeout period + procedure DiffSnapshot; + /// retrieve the revision number from an incremental binary buffer + // - returns 0 if the supplied binary buffer does not match this bloom filter + function DiffKnownRevision(const aDiff: RawByteString): Int64; + /// read the internal bits array from an incremental binary buffer + // - as previously serialized by the SaveToDiff() method + // - may be used to transmit or store the state of a dataset + // - returns false if the supplied content is incorrect, e.g. if the known + // revision is deprecated + function LoadFromDiff(const aDiff: RawByteString): boolean; + /// the opaque revision number of this internal storage + // - is in fact the Unix timestamp shifted by 31 bits, and an incremental + // counter: this pattern will allow consistent IDs over several ServPanels + property Revision: Int64 read fRevision; + /// after how many Insert() the internal bits array storage should be + // promoted as known revision + // - equals Size div 32 by default + property SnapshotAfterInsertCount: cardinal read fSnapshotAfterInsertCount + write fSnapshotAfterInsertCount; + /// after how many time the internal bits array storage should be + // promoted as known revision + // - equals 30 minutes by default + property SnapShotAfterMinutes: cardinal read fSnapShotAfterMinutes + write fSnapShotAfterMinutes; + end; + + +/// RLE compression of a memory buffer containing mostly zeros +// - will store the number of consecutive zeros instead of plain zero bytes +// - used for spare bit sets, e.g. TSynBloomFilter serialization +// - will also compute the crc32c of the supplied content +// - use ZeroDecompress() to expand the compressed result +// - resulting content would be at most 14 bytes bigger than the input +// - you may use this function before SynLZ compression +procedure ZeroCompress(P: PAnsiChar; Len: integer; Dest: TFileBufferWriter); + +/// RLE uncompression of a memory buffer containing mostly zeros +// - returns Dest='' if P^ is not a valid ZeroCompress() function result +// - used for spare bit sets, e.g. TSynBloomFilter serialization +// - will also check the crc32c of the supplied content +procedure ZeroDecompress(P: PByte; Len: integer; {$ifdef FPC}var{$else}out{$endif} Dest: RawByteString); + +/// RLE compression of XORed memory buffers resulting in mostly zeros +// - will perform ZeroCompress(Dest^ := New^ xor Old^) without any temporary +// memory allocation +// - is used e.g. by TSynBloomFilterDiff.SaveToDiff() in incremental mode +// - will also compute the crc32c of the supplied content +procedure ZeroCompressXor(New,Old: PAnsiChar; Len: cardinal; Dest: TFileBufferWriter); + +/// RLE uncompression and ORing of a memory buffer containing mostly zeros +// - will perform Dest^ := Dest^ or ZeroDecompress(P^) without any temporary +// memory allocation +// - is used e.g. by TSynBloomFilterDiff.LoadFromDiff() in incremental mode +// - returns false if P^ is not a valid ZeroCompress/ZeroCompressXor() result +// - will also check the crc32c of the supplied content +function ZeroDecompressOr(P,Dest: PAnsiChar; Len,DestLen: integer): boolean; + + +const + /// normal pattern search depth for DeltaCompress() + // - gives good results on most content + DELTA_LEVEL_FAST = 100; + /// brutal pattern search depth for DeltaCompress() + // - may become very slow, with minor benefit, on huge content + DELTA_LEVEL_BEST = 500; + /// 2MB as internal chunks/window default size for DeltaCompress() + // - will use up to 9 MB of RAM during DeltaCompress() - none in DeltaExtract() + DELTA_BUF_DEFAULT = 2 shl 20; + +/// compute difference of two binary buffers +// - returns '=' for equal buffers, or an optimized binary delta +// - DeltaExtract() could be used later on to compute New from Old + Delta +function DeltaCompress(const New, Old: RawByteString; + Level: integer=DELTA_LEVEL_FAST; BufSize: integer=DELTA_BUF_DEFAULT): RawByteString; overload; + +/// compute difference of two binary buffers +// - returns '=' for equal buffers, or an optimized binary delta +// - DeltaExtract() could be used later on to compute New from Old +function DeltaCompress(New, Old: PAnsiChar; NewSize, OldSize: integer; + Level: integer=DELTA_LEVEL_FAST; BufSize: integer=DELTA_BUF_DEFAULT): RawByteString; overload; + +/// compute difference of two binary buffers +// - returns '=' for equal buffers, or an optimized binary delta +// - DeltaExtract() could be used later on to compute New from Old + Delta +// - caller should call Freemem(Delta) once finished with the output buffer +function DeltaCompress(New, Old: PAnsiChar; NewSize, OldSize: integer; + out Delta: PAnsiChar; Level: integer=DELTA_LEVEL_FAST; BufSize: integer=DELTA_BUF_DEFAULT): integer; overload; + +type + /// result of function DeltaExtract() + TDeltaError = ( + dsSuccess, dsCrcCopy, dsCrcComp, dsCrcBegin, dsCrcEnd, dsCrcExtract, dsFlag, dsLen); + +/// returns how many bytes a DeltaCompress() result will expand to +function DeltaExtractSize(const Delta: RawByteString): integer; overload; + +/// returns how many bytes a DeltaCompress() result will expand to +function DeltaExtractSize(Delta: PAnsiChar): integer; overload; + +/// apply the delta binary as computed by DeltaCompress() +// - decompression don't use any RAM, will perform crc32c check, and is very fast +// - return dsSuccess if was uncompressed to aOutUpd as expected +function DeltaExtract(const Delta,Old: RawByteString; out New: RawByteString): TDeltaError; overload; + +/// low-level apply the delta binary as computed by DeltaCompress() +// - New should already be allocated with DeltaExtractSize(Delta) bytes +// - as such, expect Delta, Old and New to be <> nil, and Delta <> '=' +// - return dsSuccess if was uncompressed to aOutUpd as expected +function DeltaExtract(Delta,Old,New: PAnsiChar): TDeltaError; overload; + +function ToText(err: TDeltaError): PShortString; overload; + + +type + /// safe decoding of a TFileBufferWriter content + // - similar to TFileBufferReader, but faster and only for in-memory buffer + // - is also safer, since will check for reaching end of buffer + // - raise a EFastReader exception on decoding error (e.g. if a buffer + // overflow may occur) or call OnErrorOverflow/OnErrorData event handlers + {$ifdef FPC_OR_UNICODE}TFastReader = record{$else}TFastReader = object{$endif} + public + /// the current position in the memory + P: PAnsiChar; + /// the last position in the buffer + Last: PAnsiChar; + /// use this event to customize the ErrorOverflow process + OnErrorOverflow: procedure of object; + /// use this event to customize the ErrorData process + OnErrorData: procedure(const fmt: RawUTF8; const args: array of const) of object; + /// some opaque value, which may be a version number to define the binary layout + Tag: PtrInt; + /// initialize the reader from a memory block + procedure Init(Buffer: pointer; Len: integer); overload; + /// initialize the reader from a RawByteString content + procedure Init(const Buffer: RawByteString); overload; + /// raise a EFastReader with an "overflow" error message + procedure ErrorOverflow; + /// raise a EFastReader with an "incorrect data" error message + procedure ErrorData(const fmt: RawUTF8; const args: array of const); + /// read the next 32-bit signed value from the buffer + function VarInt32: integer; {$ifdef HASINLINE}inline;{$endif} + /// read the next 32-bit unsigned value from the buffer + function VarUInt32: cardinal; + /// try to read the next 32-bit signed value from the buffer + // - don't change the current position + function PeekVarInt32(out value: PtrInt): boolean; {$ifdef HASINLINE}inline;{$endif} + /// try to read the next 32-bit unsigned value from the buffer + // - don't change the current position + function PeekVarUInt32(out value: PtrUInt): boolean; + /// read the next 32-bit unsigned value from the buffer + // - this version won't call ErrorOverflow, but return false on error + // - returns true on read success + function VarUInt32Safe(out Value: cardinal): boolean; + /// read the next 64-bit signed value from the buffer + function VarInt64: Int64; {$ifdef HASINLINE}inline;{$endif} + /// read the next 64-bit unsigned value from the buffer + function VarUInt64: QWord; + /// read the next RawUTF8 value from the buffer + function VarUTF8: RawUTF8; overload; + /// read the next RawUTF8 value from the buffer + procedure VarUTF8(out result: RawUTF8); overload; + /// read the next RawUTF8 value from the buffer + // - this version won't call ErrorOverflow, but return false on error + // - returns true on read success + function VarUTF8Safe(out Value: RawUTF8): boolean; + /// read the next RawByteString value from the buffer + function VarString: RawByteString; {$ifdef HASINLINE}inline;{$endif} + /// read the next pointer and length value from the buffer + procedure VarBlob(out result: TValueResult); overload; {$ifdef HASINLINE}inline;{$endif} + /// read the next pointer and length value from the buffer + function VarBlob: TValueResult; overload; {$ifdef HASINLINE}inline;{$endif} + /// read the next ShortString value from the buffer + function VarShortString: shortstring; {$ifdef HASINLINE}inline;{$endif} + /// fast ignore the next VarUInt32/VarInt32/VarUInt64/VarInt64 value + // - don't raise any exception, so caller could check explicitly for any EOF + procedure VarNextInt; overload; {$ifdef HASINLINE}inline;{$endif} + /// fast ignore the next count VarUInt32/VarInt32/VarUInt64/VarInt64 values + // - don't raise any exception, so caller could check explicitly for any EOF + procedure VarNextInt(count: integer); overload; + /// read the next byte from the buffer + function NextByte: byte; {$ifdef HASINLINE}inline;{$endif} + /// read the next byte from the buffer, checking + function NextByteSafe(dest: pointer): boolean; {$ifdef HASINLINE}inline;{$endif} + /// read the next 4 bytes from the buffer as a 32-bit unsigned value + function Next4: cardinal; {$ifdef HASINLINE}inline;{$endif} + /// read the next 8 bytes from the buffer as a 64-bit unsigned value + function Next8: Qword; {$ifdef HASINLINE}inline;{$endif} + /// consumes the next byte from the buffer, if matches a given value + function NextByteEquals(Value: byte): boolean; {$ifdef HASINLINE}inline;{$endif} + /// returns the current position, and move ahead the specified bytes + function Next(DataLen: PtrInt): pointer; {$ifdef HASINLINE}inline;{$endif} + /// returns the current position, and move ahead the specified bytes + function NextSafe(out Data: Pointer; DataLen: PtrInt): boolean; {$ifdef HASINLINE}inline;{$endif} + {$ifndef NOVARIANTS} + /// read the next variant from the buffer + // - is a wrapper around VariantLoad(), so may suffer from buffer overflow + procedure NextVariant(var Value: variant; CustomVariantOptions: pointer); + /// read the JSON-serialized TDocVariant from the buffer + // - matches TFileBufferWriter.WriteDocVariantData format + procedure NextDocVariantData(out Value: variant; CustomVariantOptions: pointer); + {$endif NOVARIANTS} + /// copy data from the current position, and move ahead the specified bytes + procedure Copy(out Dest; DataLen: PtrInt); {$ifdef HASINLINE}inline;{$endif} + /// copy data from the current position, and move ahead the specified bytes + // - this version won't call ErrorOverflow, but return false on error + // - returns true on read success + function CopySafe(out Dest; DataLen: PtrInt): boolean; + /// apply TDynArray.LoadFrom on the buffer + // - will unserialize a previously appended dynamic array, e.g. as + // ! aWriter.WriteDynArray(DA); + procedure Read(var DA: TDynArray; NoCheckHash: boolean=false); + /// retrieved cardinal values encoded with TFileBufferWriter.WriteVarUInt32Array + // - only supports wkUInt32, wkVarInt32, wkVarUInt32 kind of encoding + function ReadVarUInt32Array(var Values: TIntegerDynArray): PtrInt; + /// retrieve some TAlgoCompress buffer, appended via Write() + // - BufferOffset could be set to reserve some bytes before the uncompressed buffer + function ReadCompressed(Load: TAlgoCompressLoad=aclNormal; BufferOffset: integer=0): RawByteString; + /// returns TRUE if the current position is the end of the input stream + function EOF: boolean; {$ifdef HASINLINE}inline;{$endif} + /// returns remaining length (difference between Last and P) + function RemainingLength: PtrUInt; {$ifdef HASINLINE}inline;{$endif} + end; + + /// abstract high-level handling of (SynLZ-)compressed persisted storage + // - LoadFromReader/SaveToWriter abstract methods should be overriden + // with proper binary persistence implementation + TSynPersistentStore = class(TSynPersistentLock) + protected + fName: RawUTF8; + fReader: TFastReader; + fReaderTemp: PRawByteString; + fLoadFromLastUncompressed, fSaveToLastUncompressed: integer; + fLoadFromLastAlgo: TAlgoCompress; + /// low-level virtual methods implementing the persistence reading + procedure LoadFromReader; virtual; + procedure SaveToWriter(aWriter: TFileBufferWriter); virtual; + public + /// initialize a void storage with the supplied name + constructor Create(const aName: RawUTF8); reintroduce; overload; virtual; + /// initialize a storage from a SaveTo persisted buffer + // - raise a EFastReader exception on decoding error + constructor CreateFrom(const aBuffer: RawByteString; + aLoad: TAlgoCompressLoad = aclNormal); + /// initialize a storage from a SaveTo persisted buffer + // - raise a EFastReader exception on decoding error + constructor CreateFromBuffer(aBuffer: pointer; aBufferLen: integer; + aLoad: TAlgoCompressLoad = aclNormal); + /// initialize a storage from a SaveTo persisted buffer + // - raise a EFastReader exception on decoding error + constructor CreateFromFile(const aFileName: TFileName; + aLoad: TAlgoCompressLoad = aclNormal); + /// fill the storage from a SaveTo persisted buffer + // - actually call the LoadFromReader() virtual method for persistence + // - raise a EFastReader exception on decoding error + procedure LoadFrom(const aBuffer: RawByteString; + aLoad: TAlgoCompressLoad = aclNormal); overload; + /// initialize the storage from a SaveTo persisted buffer + // - actually call the LoadFromReader() virtual method for persistence + // - raise a EFastReader exception on decoding error + procedure LoadFrom(aBuffer: pointer; aBufferLen: integer; + aLoad: TAlgoCompressLoad = aclNormal); overload; virtual; + /// initialize the storage from a SaveToFile content + // - actually call the LoadFromReader() virtual method for persistence + // - returns false if the file is not found, true if the file was loaded + // without any problem, or raise a EFastReader exception on decoding error + function LoadFromFile(const aFileName: TFileName; + aLoad: TAlgoCompressLoad = aclNormal): boolean; + /// persist the content as a SynLZ-compressed binary blob + // - to be retrieved later on via LoadFrom method + // - actually call the SaveToWriter() protected virtual method for persistence + // - you can specify ForcedAlgo if you want to override the default AlgoSynLZ + // - BufferOffset could be set to reserve some bytes before the compressed buffer + procedure SaveTo(out aBuffer: RawByteString; nocompression: boolean=false; + BufLen: integer=65536; ForcedAlgo: TAlgoCompress=nil; BufferOffset: integer=0); overload; virtual; + /// persist the content as a SynLZ-compressed binary blob + // - just an overloaded wrapper + function SaveTo(nocompression: boolean=false; BufLen: integer=65536; + ForcedAlgo: TAlgoCompress=nil; BufferOffset: integer=0): RawByteString; overload; + {$ifdef HASINLINE}inline;{$endif} + /// persist the content as a SynLZ-compressed binary file + // - to be retrieved later on via LoadFromFile method + // - returns the number of bytes of the resulting file + // - actually call the SaveTo method for persistence + function SaveToFile(const aFileName: TFileName; nocompression: boolean=false; + BufLen: integer=65536; ForcedAlgo: TAlgoCompress=nil): PtrUInt; + /// one optional text associated with this storage + // - you can define this field as published to serialize its value in log/JSON + property Name: RawUTF8 read fName; + /// after a LoadFrom(), contains the uncompressed data size read + property LoadFromLastUncompressed: integer read fLoadFromLastUncompressed; + /// after a SaveTo(), contains the uncompressed data size written + property SaveToLastUncompressed: integer read fSaveToLastUncompressed; + end; + + /// implement binary persistence and JSON serialization (not deserialization) + TSynPersistentStoreJson = class(TSynPersistentStore) + protected + // append "name" -> inherited should add properties to the JSON object + procedure AddJSON(W: TTextWriter); virtual; + public + /// serialize this instance as a JSON object + function SaveToJSON(reformat: TTextWriterJSONFormat = jsonCompact): RawUTF8; + end; + + +type + /// item as stored in a TRawByteStringGroup instance + TRawByteStringGroupValue = record + Position: integer; + Value: RawByteString; + end; + PRawByteStringGroupValue = ^TRawByteStringGroupValue; + /// items as stored in a TRawByteStringGroup instance + TRawByteStringGroupValueDynArray = array of TRawByteStringGroupValue; + + /// store several RawByteString content with optional concatenation + {$ifdef UNICODE}TRawByteStringGroup = record{$else}TRawByteStringGroup = object{$endif} + public + /// actual list storing the data + Values: TRawByteStringGroupValueDynArray; + /// how many items are currently stored in Values[] + Count: integer; + /// the current size of data stored in Values[] + Position: integer; + /// naive but efficient cache for Find() + LastFind: integer; + /// add a new item to Values[] + procedure Add(const aItem: RawByteString); overload; + /// add a new item to Values[] + procedure Add(aItem: pointer; aItemLen: integer); overload; + {$ifndef DELPHI5OROLDER} + /// add another TRawByteStringGroup to Values[] + procedure Add(const aAnother: TRawByteStringGroup); overload; + /// low-level method to abort the latest Add() call + // - warning: will work only once, if an Add() has actually been just called: + // otherwise, the behavior is unexpected, and may wrongly truncate data + procedure RemoveLastAdd; + /// compare two TRawByteStringGroup instance stored text + function Equals(const aAnother: TRawByteStringGroup): boolean; + {$endif DELPHI5OROLDER} + /// clear any stored information + procedure Clear; + /// append stored information into another RawByteString, and clear content + procedure AppendTextAndClear(var aDest: RawByteString); + // compact the Values[] array into a single item + // - is also used by AsText to compute a single RawByteString + procedure Compact; + /// return all content as a single RawByteString + // - will also compact the Values[] array into a single item (which is returned) + function AsText: RawByteString; + /// return all content as a single TByteDynArray + function AsBytes: TByteDynArray; + /// save all content into a TTextWriter instance + procedure Write(W: TTextWriter; Escape: TTextWriterKind=twJSONEscape); overload; + /// save all content into a TFileBufferWriter instance + procedure WriteBinary(W: TFileBufferWriter); overload; + /// save all content as a string into a TFileBufferWriter instance + // - storing the length as WriteVarUInt32() prefix + procedure WriteString(W: TFileBufferWriter); + /// add another TRawByteStringGroup previously serialized via WriteString() + procedure AddFromReader(var aReader: TFastReader); + /// returns a pointer to Values[] containing a given position + // - returns nil if not found + function Find(aPosition: integer): PRawByteStringGroupValue; overload; + /// returns a pointer to Values[].Value containing a given position and length + // - returns nil if not found + function Find(aPosition, aLength: integer): pointer; overload; + /// returns the text at a given position in Values[] + // - text should be in a single Values[] entry + procedure FindAsText(aPosition, aLength: integer; out aText: RawByteString); overload; + {$ifdef HASINLINE}inline;{$endif} + /// returns the text at a given position in Values[] + // - text should be in a single Values[] entry + function FindAsText(aPosition, aLength: integer): RawByteString; overload; + {$ifdef HASINLINE}inline;{$endif} + {$ifndef NOVARIANTS} + /// returns the text at a given position in Values[] + // - text should be in a single Values[] entry + // - explicitly returns null if the supplied text was not found + procedure FindAsVariant(aPosition, aLength: integer; out aDest: variant); + {$ifdef HASINLINE}inline;{$endif} + {$endif} + /// append the text at a given position in Values[], JSON escaped by default + // - text should be in a single Values[] entry + procedure FindWrite(aPosition, aLength: integer; W: TTextWriter; + Escape: TTextWriterKind=twJSONEscape; TrailingCharsToIgnore: integer=0); + {$ifdef HASINLINE}inline;{$endif} + /// append the blob at a given position in Values[], base-64 encoded + // - text should be in a single Values[] entry + procedure FindWriteBase64(aPosition, aLength: integer; W: TTextWriter; + withMagic: boolean); + {$ifdef HASINLINE}inline;{$endif} + /// copy the text at a given position in Values[] + // - text should be in a single Values[] entry + procedure FindMove(aPosition, aLength: integer; aDest: pointer); + end; + /// pointer reference to a TRawByteStringGroup + PRawByteStringGroup = ^TRawByteStringGroup; + + +{ ************ Security and Identifier classes ************************** } + +type + /// 64-bit integer unique identifier, as computed by TSynUniqueIdentifierGenerator + // - they are increasing over time (so are much easier to store/shard/balance + // than UUID/GUID), and contain generation time and a 16-bit process ID + // - mapped by TSynUniqueIdentifierBits memory structure + // - may be used on client side for something similar to a MongoDB ObjectID, + // but compatible with TSQLRecord.ID: TID properties + TSynUniqueIdentifier = type Int64; + + /// 16-bit unique process identifier, used to compute TSynUniqueIdentifier + // - each TSynUniqueIdentifierGenerator instance is expected to have + // its own unique process identifier, stored as a 16 bit integer 1..65535 value + TSynUniqueIdentifierProcess = type word; + + {$A-} + /// map 64-bit integer unique identifier internal memory structure + // - as stored in TSynUniqueIdentifier = Int64 values, and computed by + // TSynUniqueIdentifierGenerator + // - bits 0..14 map a 15-bit increasing counter (collision-free) + // - bits 15..30 map a 16-bit process identifier + // - bits 31..63 map a 33-bit UTC time, encoded as seconds since Unix epoch + {$ifdef FPC_OR_UNICODE}TSynUniqueIdentifierBits = record{$else}TSynUniqueIdentifierBits = object{$endif} + public + /// the actual 64-bit storage value + // - in practice, only first 63 bits are used + Value: TSynUniqueIdentifier; + /// 15-bit counter (0..32767), starting with a random value + function Counter: word; + {$ifdef HASINLINE}inline;{$endif} + /// 16-bit unique process identifier + // - as specified to TSynUniqueIdentifierGenerator constructor + function ProcessID: TSynUniqueIdentifierProcess; + {$ifdef HASINLINE}inline;{$endif} + /// low-endian 4-byte value representing the seconds since the Unix epoch + // - time is expressed in Coordinated Universal Time (UTC), not local time + // - it uses in fact a 33-bit resolution, so is "Year 2038" bug-free + function CreateTimeUnix: TUnixTime; + {$ifdef HASINLINE}inline;{$endif} + /// fill this unique identifier structure from its TSynUniqueIdentifier value + // - is just a wrapper around PInt64(@self)^ + procedure From(const AID: TSynUniqueIdentifier); + {$ifdef HASINLINE}inline;{$endif} + {$ifndef NOVARIANTS} + /// convert this identifier as an explicit TDocVariant JSON object + // - returns e.g. + // ! {"Created":"2016-04-19T15:27:58","Identifier":1,"Counter":1, + // ! "Value":3137644716930138113,"Hex":"2B8B273F00008001"} + function AsVariant: variant; {$ifdef HASINLINE}inline;{$endif} + /// convert this identifier to an explicit TDocVariant JSON object + // - returns e.g. + // ! {"Created":"2016-04-19T15:27:58","Identifier":1,"Counter":1, + // ! "Value":3137644716930138113,"Hex":"2B8B273F00008001"} + procedure ToVariant(out result: variant); + {$endif NOVARIANTS} + /// extract the UTC generation timestamp from the identifier as TDateTime + // - time is expressed in Coordinated Universal Time (UTC), not local time + function CreateDateTime: TDateTime; + {$ifdef HASINLINE}inline;{$endif} + /// extract the UTC generation timestamp from the identifier + // - time is expressed in Coordinated Universal Time (UTC), not local time + function CreateTimeLog: TTimeLog; + {$ifndef DELPHI5OROLDER} + /// compare two Identifiers + function Equal(const Another: TSynUniqueIdentifierBits): boolean; + {$ifdef HASINLINE}inline;{$endif} + {$endif DELPHI5OROLDER} + /// convert the identifier into a 16 chars hexadecimal string + function ToHexa: RawUTF8; + {$ifdef HASINLINE}inline;{$endif} + /// fill this unique identifier back from a 16 chars hexadecimal string + // - returns TRUE if the supplied hexadecimal is on the expected format + // - returns FALSE if the supplied text is invalid + function FromHexa(const hexa: RawUTF8): boolean; + /// fill this unique identifier with a fake value corresponding to a given + // timestamp + // - may be used e.g. to limit database queries on a particular time range + // - bits 0..30 would be 0, i.e. would set Counter = 0 and ProcessID = 0 + procedure FromDateTime(const aDateTime: TDateTime); + /// fill this unique identifier with a fake value corresponding to a given + // timestamp + // - may be used e.g. to limit database queries on a particular time range + // - bits 0..30 would be 0, i.e. would set Counter = 0 and ProcessID = 0 + procedure FromUnixTime(const aUnixTime: TUnixTime); + end; + {$A+} + + /// points to a 64-bit integer identifier, as computed by TSynUniqueIdentifierGenerator + // - may be used to access the identifier internals, from its stored + // Int64 or TSynUniqueIdentifier value + PSynUniqueIdentifierBits = ^TSynUniqueIdentifierBits; + + /// a 24 chars cyphered hexadecimal string, mapping a TSynUniqueIdentifier + // - has handled by TSynUniqueIdentifierGenerator.ToObfuscated/FromObfuscated + TSynUniqueIdentifierObfuscated = type RawUTF8; + + /// thread-safe 64-bit integer unique identifier computation + // - may be used on client side for something similar to a MongoDB ObjectID, + // but compatible with TSQLRecord.ID: TID properties, since it will contain + // a 63-bit unsigned integer, following our ORM expectations + // - each identifier would contain a 16-bit process identifier, which is + // supplied by the application, and should be unique for this process at a + // given time + // - identifiers may be obfuscated as hexadecimal text, using both encryption + // and digital signature + TSynUniqueIdentifierGenerator = class(TSynPersistent) + protected + fUnixCreateTime: cardinal; + fLatestCounterOverflowUnixCreateTime: cardinal; + fIdentifier: TSynUniqueIdentifierProcess; + fIdentifierShifted: cardinal; + fLastCounter: cardinal; + fCrypto: array[0..7] of cardinal; // only fCrypto[6..7] are used in practice + fCryptoCRC: cardinal; + fSafe: TSynLocker; + function GetComputedCount: Int64; + public + /// initialize the generator for the given 16-bit process identifier + // - you can supply an obfuscation key, which should be shared for the + // whole system, so that you may use FromObfuscated/ToObfuscated methods + constructor Create(aIdentifier: TSynUniqueIdentifierProcess; + const aSharedObfuscationKey: RawUTF8=''); reintroduce; + /// finalize the generator structure + destructor Destroy; override; + /// return a new unique ID + // - this method is very optimized, and would use very little CPU + procedure ComputeNew(out result: TSynUniqueIdentifierBits); overload; + /// return a new unique ID, type-casted to an Int64 + function ComputeNew: Int64; overload; + {$ifdef HASINLINE}inline;{$endif} + /// return an unique ID matching this generator pattern, at a given timestamp + // - may be used e.g. to limit database queries on a particular time range + procedure ComputeFromDateTime(const aDateTime: TDateTime; out result: TSynUniqueIdentifierBits); + /// return an unique ID matching this generator pattern, at a given timestamp + // - may be used e.g. to limit database queries on a particular time range + procedure ComputeFromUnixTime(const aUnixTime: TUnixTime; out result: TSynUniqueIdentifierBits); + /// map a TSynUniqueIdentifier as 24 chars cyphered hexadecimal text + // - cyphering includes simple key-based encryption and a CRC-32 digital signature + function ToObfuscated(const aIdentifier: TSynUniqueIdentifier): TSynUniqueIdentifierObfuscated; + /// retrieve a TSynUniqueIdentifier from 24 chars cyphered hexadecimal text + // - any file extension (e.g. '.jpeg') would be first deleted from the + // supplied obfuscated text + // - returns true if the supplied obfuscated text has the expected layout + // and a valid digital signature + // - returns false if the supplied obfuscated text is invalid + function FromObfuscated(const aObfuscated: TSynUniqueIdentifierObfuscated; + out aIdentifier: TSynUniqueIdentifier): boolean; + /// some 32-bit value, derivated from aSharedObfuscationKey as supplied + // to the class constructor + // - FromObfuscated and ToObfuscated methods will validate their hexadecimal + // content with this value to secure the associated CRC + // - may be used e.g. as system-depending salt + property CryptoCRC: cardinal read fCryptoCRC; + /// direct access to the associated mutex + property Safe: TSynLocker read fSafe; + published + /// the process identifier, associated with this generator + property Identifier: TSynUniqueIdentifierProcess read fIdentifier; + /// how many times ComputeNew method has been called + property ComputedCount: Int64 read GetComputedCount; + end; + +type + /// abstract TSynPersistent class allowing safe storage of a password + // - the associated Password, e.g. for storage or transmission encryption + // will be persisted encrypted with a private key (which can be customized) + // - if default simple symmetric encryption is not enough, you may define + // a custom TSynPersistentWithPasswordUserCrypt callback, e.g. to + // SynCrypto's CryptDataForCurrentUser, for hardened password storage + // - a published property should be defined as such in inherited class: + // ! property PasswordPropertyName: RawUTF8 read fPassword write fPassword; + // - use the PassWordPlain property to access to its uncyphered value + TSynPersistentWithPassword = class(TSynPersistent) + protected + fPassWord: RawUTF8; + fKey: cardinal; + function GetKey: cardinal; + {$ifdef HASINLINE}inline;{$endif} + function GetPassWordPlain: RawUTF8; + function GetPassWordPlainInternal(AppSecret: RawUTF8): RawUTF8; + procedure SetPassWordPlain(const Value: RawUTF8); + public + /// finalize the instance + destructor Destroy; override; + /// this class method could be used to compute the encrypted password, + // ready to be stored as JSON, according to a given private key + class function ComputePassword(const PlainPassword: RawUTF8; + CustomKey: cardinal=0): RawUTF8; overload; + /// this class method could be used to compute the encrypted password from + // a binary digest, ready to be stored as JSON, according to a given private key + // - just a wrapper around ComputePassword(BinToBase64URI()) + class function ComputePassword(PlainPassword: pointer; PlainPasswordLen: integer; + CustomKey: cardinal=0): RawUTF8; overload; + /// this class method could be used to decrypt a password, stored as JSON, + // according to a given private key + // - may trigger a ESynException if the password was stored using a custom + // TSynPersistentWithPasswordUserCrypt callback, and the current user + // doesn't match the expected user stored in the field + class function ComputePlainPassword(const CypheredPassword: RawUTF8; + CustomKey: cardinal=0; const AppSecret: RawUTF8=''): RawUTF8; + /// low-level function used to identify if a given field is a Password + // - this method is used e.g. by TJSONSerializer.WriteObject to identify the + // password field, since its published name is set by the inherited classes + function GetPasswordFieldAddress: pointer; + {$ifdef HASINLINE}inline;{$endif} + /// the private key used to cypher the password storage on serialization + // - application can override the default 0 value at runtime + property Key: cardinal read GetKey write fKey; + /// access to the associated unencrypted Password value + // - read may trigger a ESynException if the password was stored using a + // custom TSynPersistentWithPasswordUserCrypt callback, and the current user + // doesn't match the expected user stored in the field + property PasswordPlain: RawUTF8 read GetPassWordPlain write SetPassWordPlain; + end; + +var + /// function prototype to customize TSynPersistent class password storage + // - is called when 'user1:base64pass1,user2:base64pass2' layout is found, + // and the current user logged on the system is user1 or user2 + // - you should not call this low-level method, but assign e.g. from SynCrypto: + // $ TSynPersistentWithPasswordUserCrypt := CryptDataForCurrentUser; + TSynPersistentWithPasswordUserCrypt: + function(const Data,AppServer: RawByteString; Encrypt: boolean): RawByteString; + +type + /// could be used to store a credential pair, as user name and password + // - password will be stored with TSynPersistentWithPassword encryption + TSynUserPassword = class(TSynPersistentWithPassword) + protected + fUserName: RawUTF8; + published + /// the associated user name + property UserName: RawUTF8 read FUserName write FUserName; + /// the associated encrypted password + // - use the PasswordPlain public property to access to the uncrypted password + property Password: RawUTF8 read FPassword write FPassword; + end; + + /// handle safe storage of any connection properties + // - would be used by SynDB.pas to serialize TSQLDBConnectionProperties, or + // by mORMot.pas to serialize TSQLRest instances + // - the password will be stored as Base64, after a simple encryption as + // defined by TSynPersistentWithPassword + // - typical content could be: + // $ { + // $ "Kind": "TSQLDBSQLite3ConnectionProperties", + // $ "ServerName": "server", + // $ "DatabaseName": "", + // $ "User": "", + // $ "Password": "PtvlPA==" + // $ } + // - the "Kind" value will be used to let the corresponding TSQLRest or + // TSQLDBConnectionProperties NewInstance*() class methods create the + // actual instance, from its class name + TSynConnectionDefinition = class(TSynPersistentWithPassword) + protected + fKind: string; + fServerName: RawUTF8; + fDatabaseName: RawUTF8; + fUser: RawUTF8; + public + /// unserialize the database definition from JSON + // - as previously serialized with the SaveToJSON method + // - you can specify a custom Key used for password encryption, if the + // default value is not safe enough for you + // - this method won't use JSONToObject() so avoid any dependency to mORMot.pas + constructor CreateFromJSON(const JSON: RawUTF8; Key: cardinal=0); virtual; + /// serialize the database definition as JSON + // - this method won't use ObjectToJSON() so avoid any dependency to mORMot.pas + function SaveToJSON: RawUTF8; virtual; + published + /// the class name implementing the connection or TSQLRest instance + // - will be used to instantiate the expected class type + property Kind: string read fKind write fKind; + /// the associated server name (or file, for SQLite3) to be connected to + property ServerName: RawUTF8 read fServerName write fServerName; + /// the associated database name (if any), or additional options + property DatabaseName: RawUTF8 read fDatabaseName write fDatabaseName; + /// the associated User Identifier (if any) + property User: RawUTF8 read fUser write fUser; + /// the associated Password, e.g. for storage or transmission encryption + // - will be persisted encrypted with a private key + // - use the PassWordPlain property to access to its uncyphered value + property Password: RawUTF8 read fPassword write fPassword; + end; + + +type + /// class-reference type (metaclass) of an authentication class + TSynAuthenticationClass = class of TSynAuthenticationAbstract; + + /// abstract authentication class, implementing safe token/challenge security + // and a list of active sessions + // - do not use this class, but plain TSynAuthentication + TSynAuthenticationAbstract = class + protected + fSessions: TIntegerDynArray; + fSessionsCount: Integer; + fSessionGenerator: integer; + fTokenSeed: Int64; + fSafe: TSynLocker; + function ComputeCredential(previous: boolean; const UserName,PassWord: RawUTF8): cardinal; virtual; + function GetPassword(const UserName: RawUTF8; out Password: RawUTF8): boolean; virtual; abstract; + function GetUsersCount: integer; virtual; abstract; + public + /// initialize the authentication scheme + constructor Create; + /// finalize the authentation + destructor Destroy; override; + /// register one credential for a given user + // - this abstract method will raise an exception: inherited classes should + // implement them as expected + procedure AuthenticateUser(const aName, aPassword: RawUTF8); virtual; + /// unregister one credential for a given user + // - this abstract method will raise an exception: inherited classes should + // implement them as expected + procedure DisauthenticateUser(const aName: RawUTF8); virtual; + /// create a new session + // - should return 0 on authentication error, or an integer session ID + // - this method will check the User name and password, and create a new session + function CreateSession(const User: RawUTF8; Hash: cardinal): integer; virtual; + /// check if the session exists in the internal list + function SessionExists(aID: integer): boolean; + /// delete a session + procedure RemoveSession(aID: integer); + /// returns the current identification token + // - to be sent to the client for its authentication challenge + function CurrentToken: Int64; + /// the number of current opened sessions + property SessionsCount: integer read fSessionsCount; + /// the number of registered users + property UsersCount: integer read GetUsersCount; + /// to be used to compute a Hash on the client sude, for a given Token + // - the token should have been retrieved from the server, and the client + // should compute and return this hash value, to perform the authentication + // challenge and create the session + // - internal algorithm is not cryptographic secure, but fast and safe + class function ComputeHash(Token: Int64; const UserName,PassWord: RawUTF8): cardinal; virtual; + end; + + /// simple authentication class, implementing safe token/challenge security + // - maintain a list of user / name credential pairs, and a list of sessions + // - is not meant to handle authorization, just plain user access validation + // - used e.g. by TSQLDBConnection.RemoteProcessMessage (on server side) and + // TSQLDBProxyConnectionPropertiesAbstract (on client side) in SynDB.pas + TSynAuthentication = class(TSynAuthenticationAbstract) + protected + fCredentials: TSynNameValue; // store user/password pairs + function GetPassword(const UserName: RawUTF8; out Password: RawUTF8): boolean; override; + function GetUsersCount: integer; override; + public + /// initialize the authentication scheme + // - you can optionally register one user credential + constructor Create(const aUserName: RawUTF8=''; const aPassword: RawUTF8=''); reintroduce; + /// register one credential for a given user + procedure AuthenticateUser(const aName, aPassword: RawUTF8); override; + /// unregister one credential for a given user + procedure DisauthenticateUser(const aName: RawUTF8); override; + end; + + +{ ************ Expression Search Engine ************************** } + +type + /// exception type used by TExprParser + EExprParser = class(ESynException); + + /// identify an expression search engine node type, as used by TExprParser + TExprNodeType = (entWord, entNot, entOr, entAnd); + + /// results returned by TExprParserAbstract.Parse method + TExprParserResult = ( + eprSuccess, eprNoExpression, + eprMissingParenthesis, eprTooManyParenthesis, eprMissingFinalWord, + eprInvalidExpression, eprUnknownVariable, eprUnsupportedOperator, + eprInvalidConstantOrVariable); + + TParserAbstract = class; + + /// stores an expression search engine node, as used by TExprParser + TExprNode = class(TSynPersistent) + protected + fNext: TExprNode; + fNodeType: TExprNodeType; + function Append(node: TExprNode): boolean; + public + /// initialize a node for the search engine + constructor Create(nodeType: TExprNodeType); reintroduce; + /// recursively destroys the linked list of nodes (i.e. Next) + destructor Destroy; override; + /// browse all nodes until Next = nil + function Last: TExprNode; + /// points to the next node in the parsed tree + property Next: TExprNode read fNext; + /// what is actually stored in this node + property NodeType: TExprNodeType read fNodeType; + end; + + /// abstract class to handle word search, as used by TExprParser + TExprNodeWordAbstract = class(TExprNode) + protected + fOwner: TParserAbstract; + fWord: RawUTF8; + /// should be set from actual data before TExprParser.Found is called + fFound: boolean; + function ParseWord: TExprParserResult; virtual; abstract; + public + /// you should override this virtual constructor for proper initialization + constructor Create(aOwner: TParserAbstract; const aWord: RawUTF8); reintroduce; virtual; + end; + + /// class-reference type (metaclass) for a TExprNode + // - allow to customize the actual searching process for entWord + TExprNodeWordClass = class of TExprNodeWordAbstract; + + /// parent class of TExprParserAbstract + TParserAbstract = class(TSynPersistent) + protected + fExpression, fCurrentWord, fAndWord, fOrWord, fNotWord: RawUTF8; + fCurrent: PUTF8Char; + fCurrentError: TExprParserResult; + fFirstNode: TExprNode; + fWordClass: TExprNodeWordClass; + fWords: array of TExprNodeWordAbstract; + fWordCount: integer; + fNoWordIsAnd: boolean; + fFoundStack: array[byte] of boolean; // simple stack-based virtual machine + procedure ParseNextCurrentWord; virtual; abstract; + function ParseExpr: TExprNode; + function ParseFactor: TExprNode; + function ParseTerm: TExprNode; + procedure Clear; virtual; + // override this method to initialize fWordClass and fAnd/Or/NotWord + procedure Initialize; virtual; abstract; + /// perform the expression search over TExprNodeWord.fFound flags + // - warning: caller should check that fFirstNode<>nil (e.g. WordCount>0) + function Execute: boolean; {$ifdef HASINLINE}inline;{$endif} + public + /// initialize an expression parser + constructor Create; override; + /// finalize the expression parser + destructor Destroy; override; + /// initialize the parser from a given text expression + function Parse(const aExpression: RawUTF8): TExprParserResult; + /// try this parser class on a given text expression + // - returns '' on success, or an explicit error message (e.g. + // 'Missing parenthesis') + class function ParseError(const aExpression: RawUTF8): RawUTF8; + /// the associated text expression used to define the search + property Expression: RawUTF8 read fExpression; + /// how many words did appear in the search expression + property WordCount: integer read fWordCount; + end; + + /// abstract class to parse a text expression into nodes + // - you should inherit this class to provide actual text search + // - searched expressions can use parenthesis and &=AND -=WITHOUT +=OR operators, + // e.g. '((w1 & w2) - w3) + w4' means ((w1 and w2) without w3) or w4 + // - no operator is handled like a AND, e.g. 'w1 w2' = 'w1 & w2' + TExprParserAbstract = class(TParserAbstract) + protected + procedure ParseNextCurrentWord; override; + // may be overriden to provide custom words escaping (e.g. handle quotes) + procedure ParseNextWord; virtual; + procedure Initialize; override; + end; + + /// search expression engine using TMatch for the actual word searches + TExprParserMatch = class(TExprParserAbstract) + protected + fCaseSensitive: boolean; + fMatchedLastSet: integer; + procedure Initialize; override; + public + /// initialize the search engine + constructor Create(aCaseSensitive: boolean = true); reintroduce; + /// returns TRUE if the expression is within the text buffer + function Search(aText: PUTF8Char; aTextLen: PtrInt): boolean; overload; + /// returns TRUE if the expression is within the text buffer + function Search(const aText: RawUTF8): boolean; overload; {$ifdef HASINLINE}inline;{$endif} + end; + +const + /// may be used when overriding TExprParserAbstract.ParseNextWord method + PARSER_STOPCHAR = ['&', '+', '-', '(', ')']; + +function ToText(r: TExprParserResult): PShortString; overload; +function ToUTF8(r: TExprParserResult): RawUTF8; overload; + + +{ ************ Multi-Threading classes ************************** } + +type + /// internal item definition, used by TPendingTaskList storage + TPendingTaskListItem = packed record + /// the task should be executed when TPendingTaskList.GetTimestamp reaches + // this value + Timestamp: Int64; + /// the associated task, stored by representation as raw binary + Task: RawByteString; + end; + /// internal list definition, used by TPendingTaskList storage + TPendingTaskListItemDynArray = array of TPendingTaskListItem; + + /// handle a list of tasks, stored as RawByteString, with a time stamp + // - internal time stamps would be GetTickCount64 by default, so have a + // resolution of about 16 ms under Windows + // - you can add tasks to the internal list, to be executed after a given + // delay, using a post/peek like algorithm + // - execution delays are not expected to be accurate, but are best guess, + // according to NextTask call + // - this implementation is thread-safe, thanks to the Safe internal locker + TPendingTaskList = class(TSynPersistentLock) + protected + fCount: Integer; + fTask: TPendingTaskListItemDynArray; + fTasks: TDynArray; + function GetCount: integer; + function GetTimestamp: Int64; virtual; + public + /// initialize the list memory and resources + constructor Create; override; + /// append a task, specifying a delay in milliseconds from current time + procedure AddTask(aMilliSecondsDelayFromNow: integer; const aTask: RawByteString); virtual; + /// append several tasks, specifying a delay in milliseconds between tasks + // - first supplied delay would be computed from the current time, then + // it would specify how much time to wait between the next supplied task + procedure AddTasks(const aMilliSecondsDelays: array of integer; + const aTasks: array of RawByteString); + /// retrieve the next pending task + // - returns '' if there is no scheduled task available at the current time + // - returns the next stack as defined corresponding to its specified delay + function NextPendingTask: RawByteString; virtual; + /// flush all pending tasks + procedure Clear; virtual; + /// access to the locking methods of this instance + // - use Safe.Lock/TryLock with a try ... finally Safe.Unlock block + property Safe: PSynlocker read fSafe; + /// access to the internal TPendingTaskListItem.Timestamp stored value + // - corresponding to the current time + // - default implementation is to return GetTickCount64, with a 16 ms + // typical resolution under Windows + property Timestamp: Int64 read GetTimestamp; + /// how many pending tasks are currently defined + property Count: integer read GetCount; + /// direct low-level access to the internal task list + // - warning: this dynamic array length is the list capacity: use Count + // property to retrieve the exact number of stored items + // - use Safe.Lock/TryLock with a try ... finally Safe.Unlock block for + // thread-safe access to this array + // - items are stored in increasing Timestamp, i.e. the first item is + // the next one which would be returned by the NextPendingTask method + property Task: TPendingTaskListItemDynArray read fTask; + end; + +{$ifndef LVCL} // LVCL does not implement TEvent + +type + {$M+} + TSynBackgroundThreadAbstract = class; + TSynBackgroundThreadEvent = class; + {$M-} + + /// idle method called by TSynBackgroundThreadAbstract in the caller thread + // during remote blocking process in a background thread + // - typical use is to run Application.ProcessMessages, e.g. for + // TSQLRestClientURI.URI() to provide a responsive UI even in case of slow + // blocking remote access + // - provide the time elapsed (in milliseconds) from the request start (can be + // used e.g. to popup a temporary message to wait) + // - is call once with ElapsedMS=0 at request start + // - is call once with ElapsedMS=-1 at request ending + // - see TLoginForm.OnIdleProcess and OnIdleProcessForm in mORMotUILogin.pas + TOnIdleSynBackgroundThread = procedure(Sender: TSynBackgroundThreadAbstract; + ElapsedMS: Integer) of object; + + /// event prototype used e.g. by TSynBackgroundThreadAbstract callbacks + // - a similar signature is defined in SynCrtSock and LVCL.Classes + TNotifyThreadEvent = procedure(Sender: TThread) of object; + + /// abstract TThread with its own execution content + // - you should not use this class directly, but use either + // TSynBackgroundThreadMethodAbstract / TSynBackgroundThreadEvent / + // TSynBackgroundThreadMethod and provide a much more convenient callback + TSynBackgroundThreadAbstract = class(TThread) + protected + fProcessEvent: TEvent; + fOnBeforeExecute: TNotifyThreadEvent; + fOnAfterExecute: TNotifyThreadEvent; + fThreadName: RawUTF8; + fExecute: (exCreated,exRun,exFinished); + fExecuteLoopPause: boolean; + procedure SetExecuteLoopPause(dopause: boolean); + /// where the main process takes place + procedure Execute; override; + procedure ExecuteLoop; virtual; abstract; + public + /// initialize the thread + // - you could define some callbacks to nest the thread execution, e.g. + // assigned to TSQLRestServer.BeginCurrentThread/EndCurrentThread, or + // at least set OnAfterExecute to TSynLogFamily.OnThreadEnded + constructor Create(const aThreadName: RawUTF8; OnBeforeExecute: TNotifyThreadEvent=nil; + OnAfterExecute: TNotifyThreadEvent=nil; CreateSuspended: boolean=false); reintroduce; + /// release used resources + destructor Destroy; override; + {$ifndef HASTTHREADSTART} + /// method to be called to start the thread + // - Resume is deprecated in the newest RTL, since some OS - e.g. Linux - + // do not implement this pause/resume feature; we define here this method + // for older versions of Delphi + procedure Start; + {$endif} + {$ifdef HASTTHREADTERMINATESET} + /// properly terminate the thread + // - called by TThread.Terminate + procedure TerminatedSet; override; + {$else} + /// properly terminate the thread + // - called by reintroduced Terminate + procedure TerminatedSet; virtual; + /// reintroduced to call TeminatedSet + procedure Terminate; reintroduce; + {$endif} + /// wait for Execute/ExecuteLoop to be ended (i.e. fExecute<>exRun) + procedure WaitForNotExecuting(maxMS: integer=500); + /// temporary stop the execution of ExecuteLoop, until set back to false + // - may be used e.g. by TSynBackgroundTimer to delay the process of + // background tasks + property Pause: boolean read fExecuteLoopPause write SetExecuteLoopPause; + /// access to the low-level associated event used to notify task execution + // to the background thread + // - you may call ProcessEvent.SetEvent to trigger the internal process loop + property ProcessEvent: TEvent read fProcessEvent; + /// defined as public since may be used to terminate the processing methods + property Terminated; + end; + + /// state machine status of the TSynBackgroundThreadAbstract process + TSynBackgroundThreadProcessStep = ( + flagIdle, flagStarted, flagFinished, flagDestroying); + + /// state machine statuses of the TSynBackgroundThreadAbstract process + TSynBackgroundThreadProcessSteps = set of TSynBackgroundThreadProcessStep; + + /// abstract TThread able to run a method in its own execution content + // - typical use is a background thread for processing data or remote access, + // while the UI will be still responsive by running OnIdle event in loop: see + // e.g. how TSQLRestClientURI.OnIdle handle this in mORMot.pas unit + // - you should not use this class directly, but inherit from it and override + // the Process method, or use either TSynBackgroundThreadEvent / + // TSynBackgroundThreadMethod and provide a much more convenient callback + TSynBackgroundThreadMethodAbstract = class(TSynBackgroundThreadAbstract) + protected + fCallerEvent: TEvent; + fParam: pointer; + fCallerThreadID: TThreadID; + fBackgroundException: Exception; + fOnIdle: TOnIdleSynBackgroundThread; + fOnBeforeProcess: TNotifyThreadEvent; + fOnAfterProcess: TNotifyThreadEvent; + fPendingProcessFlag: TSynBackgroundThreadProcessStep; + fPendingProcessLock: TSynLocker; + procedure ExecuteLoop; override; + function OnIdleProcessNotify(start: Int64): integer; + function GetOnIdleBackgroundThreadActive: boolean; + function GetPendingProcess: TSynBackgroundThreadProcessStep; + procedure SetPendingProcess(State: TSynBackgroundThreadProcessStep); + // returns flagIdle if acquired, flagDestroying if terminated + function AcquireThread: TSynBackgroundThreadProcessStep; + procedure WaitForFinished(start: Int64; const onmainthreadidle: TNotifyEvent); + /// called by Execute method when fProcessParams<>nil and fEvent is notified + procedure Process; virtual; abstract; + public + /// initialize the thread + // - if aOnIdle is not set (i.e. equals nil), it will simply wait for + // the background process to finish until RunAndWait() will return + // - you could define some callbacks to nest the thread execution, e.g. + // assigned to TSQLRestServer.BeginCurrentThread/EndCurrentThread + constructor Create(aOnIdle: TOnIdleSynBackgroundThread; + const aThreadName: RawUTF8; OnBeforeExecute: TNotifyThreadEvent=nil; + OnAfterExecute: TNotifyThreadEvent=nil); reintroduce; + /// finalize the thread + destructor Destroy; override; + /// launch Process abstract method asynchronously in the background thread + // - wait until process is finished, calling OnIdle() callback in + // the meanwhile + // - any exception raised in background thread will be translated in the + // caller thread + // - returns false if self is not set, or if called from the same thread + // as it is currently processing (to avoid race condition from OnIdle() + // callback) + // - returns true when the background process is finished + // - OpaqueParam will be used to specify a thread-safe content for the + // background process + // - this method is thread-safe, that is it will wait for any started process + // already launch by another thread: you may call this method from any + // thread, even if its main purpose is to be called from the main UI thread + function RunAndWait(OpaqueParam: pointer): boolean; + /// set a callback event to be executed in loop during remote blocking + // process, e.g. to refresh the UI during a somewhat long request + // - you can assign a callback to this property, calling for instance + // Application.ProcessMessages, to execute the remote request in a + // background thread, but let the UI still be reactive: the + // TLoginForm.OnIdleProcess and OnIdleProcessForm methods of + // mORMotUILogin.pas will match this property expectations + // - if OnIdle is not set (i.e. equals nil), it will simply wait for + // the background process to finish until RunAndWait() will return + property OnIdle: TOnIdleSynBackgroundThread read fOnIdle write fOnIdle; + /// TRUE if the background thread is active, and OnIdle event is called + // during process + // - to be used e.g. to ensure no re-entrance from User Interface messages + property OnIdleBackgroundThreadActive: Boolean read GetOnIdleBackgroundThreadActive; + /// optional callback event triggered in Execute before each Process + property OnBeforeProcess: TNotifyThreadEvent read fOnBeforeProcess write fOnBeforeProcess; + /// optional callback event triggered in Execute after each Process + property OnAfterProcess: TNotifyThreadEvent read fOnAfterProcess write fOnAfterProcess; + end; + + /// background process method called by TSynBackgroundThreadEvent + // - will supply the OpaqueParam parameter as provided to RunAndWait() + // method when the Process virtual method will be executed + TOnProcessSynBackgroundThread = procedure(Sender: TSynBackgroundThreadEvent; + ProcessOpaqueParam: pointer) of object; + + /// allow background thread process of a method callback + TSynBackgroundThreadEvent = class(TSynBackgroundThreadMethodAbstract) + protected + fOnProcess: TOnProcessSynBackgroundThread; + /// just call the OnProcess handler + procedure Process; override; + public + /// initialize the thread + // - if aOnIdle is not set (i.e. equals nil), it will simply wait for + // the background process to finish until RunAndWait() will return + constructor Create(aOnProcess: TOnProcessSynBackgroundThread; + aOnIdle: TOnIdleSynBackgroundThread; const aThreadName: RawUTF8); reintroduce; + /// provide a method handler to be execute in the background thread + // - triggered by RunAndWait() method - which will wait until finished + // - the OpaqueParam as specified to RunAndWait() will be supplied here + property OnProcess: TOnProcessSynBackgroundThread read fOnProcess write fOnProcess; + end; + + /// allow background thread process of a variable TThreadMethod callback + TSynBackgroundThreadMethod = class(TSynBackgroundThreadMethodAbstract) + protected + /// just call the TThreadMethod, as supplied to RunAndWait() + procedure Process; override; + public + /// run once the supplied TThreadMethod callback + // - use this method, and not the inherited RunAndWait() + procedure RunAndWait(Method: TThreadMethod); reintroduce; + end; + + /// background process procedure called by TSynBackgroundThreadProcedure + // - will supply the OpaqueParam parameter as provided to RunAndWait() + // method when the Process virtual method will be executed + TOnProcessSynBackgroundThreadProc = procedure(ProcessOpaqueParam: pointer); + + /// allow background thread process of a procedure callback + TSynBackgroundThreadProcedure = class(TSynBackgroundThreadMethodAbstract) + protected + fOnProcess: TOnProcessSynBackgroundThreadProc; + /// just call the OnProcess handler + procedure Process; override; + public + /// initialize the thread + // - if aOnIdle is not set (i.e. equals nil), it will simply wait for + // the background process to finish until RunAndWait() will return + constructor Create(aOnProcess: TOnProcessSynBackgroundThreadProc; + aOnIdle: TOnIdleSynBackgroundThread; const aThreadName: RawUTF8); reintroduce; + /// provide a procedure handler to be execute in the background thread + // - triggered by RunAndWait() method - which will wait until finished + // - the OpaqueParam as specified to RunAndWait() will be supplied here + property OnProcess: TOnProcessSynBackgroundThreadProc read fOnProcess write fOnProcess; + end; + + /// an exception which would be raised by TSynParallelProcess + ESynParallelProcess = class(ESynException); + + /// callback implementing some parallelized process for TSynParallelProcess + // - if 0<=IndexStart<=IndexStop, it should execute some process + TSynParallelProcessMethod = procedure(IndexStart, IndexStop: integer) of object; + + /// thread executing process for TSynParallelProcess + TSynParallelProcessThread = class(TSynBackgroundThreadMethodAbstract) + protected + fMethod: TSynParallelProcessMethod; + fIndexStart, fIndexStop: integer; + procedure Start(Method: TSynParallelProcessMethod; IndexStart,IndexStop: integer); + /// executes fMethod(fIndexStart,fIndexStop) + procedure Process; override; + public + end; + + /// allow parallel execution of an index-based process in a thread pool + // - will create its own thread pool, then execute any method by spliting the + // work into each thread + TSynParallelProcess = class(TSynPersistentLock) + protected + fThreadName: RawUTF8; + fPool: array of TSynParallelProcessThread; + fThreadPoolCount: integer; + fParallelRunCount: integer; + public + /// initialize the thread pool + // - you could define some callbacks to nest the thread execution, e.g. + // assigned to TSQLRestServer.BeginCurrentThread/EndCurrentThread + // - up to MaxThreadPoolCount=32 threads could be setup (you may allow a + // bigger value, but interrest of this thread pool is to have its process + // saturating each CPU core) + // - if ThreadPoolCount is 0, no thread would be created, and process + // would take place in the current thread + constructor Create(ThreadPoolCount: integer; const ThreadName: RawUTF8; + OnBeforeExecute: TNotifyThreadEvent=nil; OnAfterExecute: TNotifyThreadEvent=nil; + MaxThreadPoolCount: integer = 32); reintroduce; virtual; + /// finalize the thread pool + destructor Destroy; override; + /// run a method in parallel, and wait for the execution to finish + // - will split Method[0..MethodCount-1] execution over the threads + // - in case of any exception during process, an ESynParallelProcess + // exception would be raised by this method + // - if OnMainThreadIdle is set, the current thread (which is expected to be + // e.g. the main UI thread) won't process anything, but call this event + // during waiting for the background threads + procedure ParallelRunAndWait(const Method: TSynParallelProcessMethod; + MethodCount: integer; const OnMainThreadIdle: TNotifyEvent = nil); + published + /// how many threads have been activated + property ParallelRunCount: integer read fParallelRunCount; + /// how many threads are currently in this instance thread pool + property ThreadPoolCount: integer read fThreadPoolCount; + /// some text identifier, used to distinguish each owned thread + property ThreadName: RawUTF8 read fThreadName; + end; + + TSynBackgroundThreadProcess = class; + + /// event callback executed periodically by TSynBackgroundThreadProcess + // - Event is wrTimeout after the OnProcessMS waiting period + // - Event is wrSignaled if ProcessEvent.SetEvent has been called + TOnSynBackgroundThreadProcess = procedure(Sender: TSynBackgroundThreadProcess; + Event: TWaitResult) of object; + + /// TThread able to run a method at a given periodic pace + TSynBackgroundThreadProcess = class(TSynBackgroundThreadAbstract) + protected + fOnProcess: TOnSynBackgroundThreadProcess; + fOnException: TNotifyEvent; + fOnProcessMS: cardinal; + fStats: TSynMonitor; + procedure ExecuteLoop; override; + public + /// initialize the thread for a periodic task processing + // - aOnProcess would be called when ProcessEvent.SetEvent is called or + // aOnProcessMS milliseconds period was elapse since last process + // - if aOnProcessMS is 0, will wait until ProcessEvent.SetEvent is called + // - you could define some callbacks to nest the thread execution, e.g. + // assigned to TSQLRestServer.BeginCurrentThread/EndCurrentThread + constructor Create(const aThreadName: RawUTF8; + aOnProcess: TOnSynBackgroundThreadProcess; aOnProcessMS: cardinal; + aOnBeforeExecute: TNotifyThreadEvent=nil; + aOnAfterExecute: TNotifyThreadEvent=nil; + aStats: TSynMonitorClass=nil; CreateSuspended: boolean=false); reintroduce; virtual; + /// finalize the thread + destructor Destroy; override; + /// access to the implementation event of the periodic task + property OnProcess: TOnSynBackgroundThreadProcess read fOnProcess; + /// event callback executed when OnProcess did raise an exception + // - supplied Sender parameter is the raised Exception instance + property OnException: TNotifyEvent read fOnException write fOnException; + published + /// access to the delay, in milliseconds, of the periodic task processing + property OnProcessMS: cardinal read fOnProcessMS write fOnProcessMS; + /// processing statistics + // - may be nil if aStats was nil in the class constructor + property Stats: TSynMonitor read fStats; + end; + + TSynBackgroundTimer = class; + + /// event callback executed periodically by TSynBackgroundThreadProcess + // - Event is wrTimeout after the OnProcessMS waiting period + // - Event is wrSignaled if ProcessEvent.SetEvent has been called + // - Msg is '' if there is no pending message in this task FIFO + // - Msg is set for each pending message in this task FIFO + TOnSynBackgroundTimerProcess = procedure(Sender: TSynBackgroundTimer; + Event: TWaitResult; const Msg: RawUTF8) of object; + + /// used by TSynBackgroundTimer internal registration list + TSynBackgroundTimerTask = record + OnProcess: TOnSynBackgroundTimerProcess; + Secs: cardinal; + NextTix: Int64; + FIFO: TRawUTF8DynArray; + end; + /// stores TSynBackgroundTimer internal registration list + TSynBackgroundTimerTaskDynArray = array of TSynBackgroundTimerTask; + + /// TThread able to run one or several tasks at a periodic pace in a + // background thread + // - as used e.g. by TSQLRest.TimerEnable/TimerDisable methods, via the + // inherited TSQLRestBackgroundTimer + // - each process can have its own FIFO of text messages + // - if you expect to update some GUI, you should rather use a TTimer + // component (with a period of e.g. 200ms), since TSynBackgroundTimer will + // use its own separated thread + TSynBackgroundTimer = class(TSynBackgroundThreadProcess) + protected + fTask: TSynBackgroundTimerTaskDynArray; + fTasks: TDynArray; + fTaskLock: TSynLocker; + procedure EverySecond(Sender: TSynBackgroundThreadProcess; Event: TWaitResult); + function Find(const aProcess: TMethod): integer; + function Add(aOnProcess: TOnSynBackgroundTimerProcess; + const aMsg: RawUTF8; aExecuteNow: boolean): boolean; + public + /// initialize the thread for a periodic task processing + // - you could define some callbacks to nest the thread execution, e.g. + // assigned to TSQLRestServer.BeginCurrentThread/EndCurrentThread, as + // made by TSQLRestBackgroundTimer.Create + constructor Create(const aThreadName: RawUTF8; + aOnBeforeExecute: TNotifyThreadEvent=nil; aOnAfterExecute: TNotifyThreadEvent=nil; + aStats: TSynMonitorClass=nil); reintroduce; virtual; + /// finalize the thread + destructor Destroy; override; + /// define a process method for a task running on a periodic number of seconds + // - for background process on a mORMot service, consider using TSQLRest + // TimerEnable/TimerDisable methods, and its associated BackgroundTimer thread + procedure Enable(aOnProcess: TOnSynBackgroundTimerProcess; aOnProcessSecs: cardinal); + /// undefine a task running on a periodic number of seconds + // - aOnProcess should have been registered by a previous call to Enable() method + // - returns true on success, false if the supplied task was not registered + // - for background process on a mORMot service, consider using TSQLRestServer + // TimerEnable/TimerDisable methods, and their TSynBackgroundTimer thread + function Disable(aOnProcess: TOnSynBackgroundTimerProcess): boolean; + /// add a message to be processed during the next execution of a task + // - supplied message will be added to the internal FIFO list associated + // with aOnProcess, then supplied to as aMsg parameter for each call + // - if aExecuteNow is true, won't wait for the next aOnProcessSecs occurence + // - aOnProcess should have been registered by a previous call to Enable() method + // - returns true on success, false if the supplied task was not registered + function EnQueue(aOnProcess: TOnSynBackgroundTimerProcess; + const aMsg: RawUTF8; aExecuteNow: boolean=false): boolean; overload; + /// add a message to be processed during the next execution of a task + // - supplied message will be added to the internal FIFO list associated + // with aOnProcess, then supplied to as aMsg parameter for each call + // - if aExecuteNow is true, won't wait for the next aOnProcessSecs occurence + // - aOnProcess should have been registered by a previous call to Enable() method + // - returns true on success, false if the supplied task was not registered + function EnQueue(aOnProcess: TOnSynBackgroundTimerProcess; + const aMsgFmt: RawUTF8; const Args: array of const; aExecuteNow: boolean=false): boolean; overload; + /// remove a message from the processing list + // - supplied message will be searched in the internal FIFO list associated + // with aOnProcess, then removed from the list if found + // - aOnProcess should have been registered by a previous call to Enable() method + // - returns true on success, false if the supplied message was not registered + function DeQueue(aOnProcess: TOnSynBackgroundTimerProcess; const aMsg: RawUTF8): boolean; + /// execute a task without waiting for the next aOnProcessSecs occurence + // - aOnProcess should have been registered by a previous call to Enable() method + // - returns true on success, false if the supplied task was not registered + function ExecuteNow(aOnProcess: TOnSynBackgroundTimerProcess): boolean; + /// returns true if there is currenly one task processed + function Processing: boolean; + /// wait until no background task is processed + procedure WaitUntilNotProcessing(timeoutsecs: integer=10); + /// low-level access to the internal task list + property Task: TSynBackgroundTimerTaskDynArray read fTask; + /// low-level access to the internal task mutex + property TaskLock: TSynLocker read fTaskLock; + end; + + /// the current state of a TBlockingProcess instance + TBlockingEvent = (evNone,evWaiting,evTimeOut,evRaised); + + {$M+} + /// a semaphore used to wait for some process to be finished + // - used e.g. by TBlockingCallback in mORMot.pas + // - once created, process would block via a WaitFor call, which would be + // released when NotifyFinished is called by the process background thread + TBlockingProcess = class(TEvent) + protected + fTimeOutMs: integer; + fEvent: TBlockingEvent; + fSafe: PSynLocker; + fOwnedSafe: boolean; + procedure ResetInternal; virtual; // override to reset associated params + public + /// initialize the semaphore instance + // - specify a time out millliseconds period after which blocking execution + // should be handled as failure (if 0 is set, default 3000 would be used) + // - an associated mutex shall be supplied + constructor Create(aTimeOutMs: integer; aSafe: PSynLocker); reintroduce; overload; virtual; + /// initialize the semaphore instance + // - specify a time out millliseconds period after which blocking execution + // should be handled as failure (if 0 is set, default 3000 would be used) + // - an associated mutex would be created and owned by this instance + constructor Create(aTimeOutMs: integer); reintroduce; overload; virtual; + /// finalize the instance + destructor Destroy; override; + /// called to wait for NotifyFinished() to be called, or trigger timeout + // - returns the final state of the process, i.e. evRaised or evTimeOut + function WaitFor: TBlockingEvent; reintroduce; overload; virtual; + /// called to wait for NotifyFinished() to be called, or trigger timeout + // - returns the final state of the process, i.e. evRaised or evTimeOut + function WaitFor(TimeOutMS: integer): TBlockingEvent; reintroduce; overload; + /// should be called by the background process when it is finished + // - the caller would then let its WaitFor method return + // - returns TRUE on success (i.e. status was not evRaised or evTimeout) + // - if the instance is already locked (e.g. when retrieved from + // TBlockingProcessPool.FromCallLocked), you may set alreadyLocked=TRUE + function NotifyFinished(alreadyLocked: boolean=false): boolean; virtual; + /// just a wrapper to reset the internal Event state to evNone + // - may be used to re-use the same TBlockingProcess instance, after + // a successfull WaitFor/NotifyFinished process + // - returns TRUE on success (i.e. status was not evWaiting), setting + // the current state to evNone, and the Call property to 0 + // - if there is a WaitFor currently in progress, returns FALSE + function Reset: boolean; virtual; + /// just a wrapper around fSafe^.Lock + procedure Lock; + /// just a wrapper around fSafe^.Unlock + procedure Unlock; + published + /// the current state of process + // - use Reset method to re-use this instance after a WaitFor process + property Event: TBlockingEvent read fEvent; + /// the time out period, in ms, as defined at constructor level + property TimeOutMs: integer read fTimeOutMS; + end; + {$M-} + + /// used to identify each TBlockingProcessPool call + // - allow to match a given TBlockingProcessPoolItem semaphore + TBlockingProcessPoolCall = type integer; + + /// a semaphore used in the TBlockingProcessPool + // - such semaphore have a Call field to identify each execution + TBlockingProcessPoolItem = class(TBlockingProcess) + protected + fCall: TBlockingProcessPoolCall; + procedure ResetInternal; override; + published + /// an unique identifier, when owned by a TBlockingProcessPool + // - Reset would restore this field to its 0 default value + property Call: TBlockingProcessPoolCall read fCall; + end; + + /// class-reference type (metaclass) of a TBlockingProcess + TBlockingProcessPoolItemClass = class of TBlockingProcessPoolItem; + + /// manage a pool of TBlockingProcessPoolItem instances + // - each call will be identified via a TBlockingProcessPoolCall unique value + // - to be used to emulate e.g. blocking execution from an asynchronous + // event-driven DDD process + // - it would also allow to re-use TEvent system resources + TBlockingProcessPool = class(TSynPersistent) + protected + fClass: TBlockingProcessPoolItemClass; + fPool: TObjectListLocked; + fCallCounter: TBlockingProcessPoolCall; // set TBlockingProcessPoolItem.Call + public + /// initialize the pool, for a given implementation class + constructor Create(aClass: TBlockingProcessPoolItemClass=nil); reintroduce; + /// finalize the pool + // - would also force all pending WaitFor to trigger a evTimeOut + destructor Destroy; override; + /// book a TBlockingProcess from the internal pool + // - returns nil on error (e.g. the instance is destroying) + // - or returns the blocking process instance corresponding to this call; + // its Call property would identify the call for the asynchronous callback, + // then after WaitFor, the Reset method should be run to release the mutex + // for the pool + function NewProcess(aTimeOutMs: integer): TBlockingProcessPoolItem; virtual; + /// retrieve a TBlockingProcess from its call identifier + // - may be used e.g. from the callback of the asynchronous process + // to set some additional parameters to the inherited TBlockingProcess, + // then call NotifyFinished to release the caller WaitFor + // - if leavelocked is TRUE, the returned instance would be locked: caller + // should execute result.Unlock or NotifyFinished(true) after use + function FromCall(call: TBlockingProcessPoolCall; + locked: boolean=false): TBlockingProcessPoolItem; virtual; + end; + +/// allow to fix TEvent.WaitFor() method for Kylix +// - under Windows or with FPC, will call original TEvent.WaitFor() method +function FixedWaitFor(Event: TEvent; Timeout: LongWord): TWaitResult; + +/// allow to fix TEvent.WaitFor(Event,INFINITE) method for Kylix +// - under Windows or with FPC, will call original TEvent.WaitFor() method +procedure FixedWaitForever(Event: TEvent); + +{$endif LVCL} // LVCL does not implement TEvent + + +{ ************ System Analysis types and classes ************************** } + +type + /// store CPU and RAM usage for a given process + // - as used by TSystemUse class + TSystemUseData = packed record + /// when the data has been sampled + Timestamp: TDateTime; + /// percent of current Kernel-space CPU usage for this process + Kernel: single; + /// percent of current User-space CPU usage for this process + User: single; + /// how many KB of working memory are used by this process + WorkKB: cardinal; + /// how many KB of virtual memory are used by this process + VirtualKB: cardinal; + end; + /// store CPU and RAM usage history for a given process + // - as returned by TSystemUse.History + TSystemUseDataDynArray = array of TSystemUseData; + + /// low-level structure used to compute process memory and CPU usage + {$ifdef FPC_OR_UNICODE}TProcessInfo = record private + {$else}TProcessInfo = object protected{$endif} + {$ifdef MSWINDOWS} + fSysPrevIdle, fSysPrevKernel, fSysPrevUser, + fDiffIdle, fDiffKernel, fDiffUser, fDiffTotal: Int64; + {$endif} + public + /// initialize the system/process resource tracking + function Init: boolean; + /// to be called before PerSystem() or PerProcess() iteration + function Start: boolean; + /// percent of current Idle/Kernel/User CPU usage for all processes + function PerSystem(out Idle,Kernel,User: currency): boolean; + /// retrieve CPU and RAM usage for a given process + function PerProcess(PID: cardinal; Now: PDateTime; out Data: TSystemUseData; + var PrevKernel, PrevUser: Int64): boolean; + end; + + /// event handler which may be executed by TSystemUse.BackgroundExecute + // - called just after the measurement of each process CPU and RAM consumption + // - run from the background thread, so should not directly make VCL calls, + // unless BackgroundExecute is run from a VCL timer + TOnSystemUseMeasured = procedure(ProcessID: integer; const Data: TSystemUseData) of object; + + /// internal storage of CPU and RAM usage for one process + TSystemUseProcess = record + ID: integer; + Data: TSystemUseDataDynArray; + PrevKernel: Int64; + PrevUser: Int64; + end; + /// internal storage of CPU and RAM usage for a set of processes + TSystemUseProcessDynArray = array of TSystemUseProcess; + + /// monitor CPU and RAM usage of one or several processes + // - you should execute BackgroundExecute on a regular pace (e.g. every second) + // to gather low-level CPU and RAM information for the given set of processes + // - is able to keep an history of latest sample values + // - use Current class function to access a process-wide instance + TSystemUse = class(TSynPersistentLock) + protected + fProcess: TSystemUseProcessDynArray; + fProcesses: TDynArray; + fDataIndex: integer; + fProcessInfo: TProcessInfo; + fHistoryDepth: integer; + fOnMeasured: TOnSystemUseMeasured; + fTimer: TSynBackgroundTimer; + fUnsubscribeProcessOnAccessError: boolean; + function ProcessIndex(aProcessID: integer): integer; + public + /// a TSynBackgroundThreadProcess compatible event + // - matches TOnSynBackgroundTimerProcess callback signature + // - to be supplied e.g. to a TSynBackgroundTimer.Enable method so that it + // will run every few seconds and retrieve the CPU and RAM use + procedure BackgroundExecute(Sender: TSynBackgroundTimer; + Event: TWaitResult; const Msg: RawUTF8); + /// a VCL's TTimer.OnTimer compatible event + // - to be run every few seconds and retrieve the CPU and RAM use: + // ! tmrSystemUse.Interval := 10000; // every 10 seconds + // ! tmrSystemUse.OnTimer := TSystemUse.Current.OnTimerExecute; + procedure OnTimerExecute(Sender: TObject); + /// track the CPU and RAM usage of the supplied set of Process ID + // - any aProcessID[]=0 will be replaced by the current process ID + // - you can specify the number of sample values for the History() method + // - you should then execute the BackgroundExecute method of this instance + // in a VCL timer or from a TSynBackgroundTimer.Enable() registration + constructor Create(const aProcessID: array of integer; + aHistoryDepth: integer=60); reintroduce; overload; virtual; + /// track the CPU and RAM usage of the current process + // - you can specify the number of sample values for the History() method + // - you should then execute the BackgroundExecute method of this instance + // in a VCL timer or from a TSynBackgroundTimer.Enable() registration + constructor Create(aHistoryDepth: integer=60); reintroduce; overload; virtual; + /// add a Process ID to the internal tracking list + procedure Subscribe(aProcessID: integer); + /// remove a Process ID from the internal tracking list + function Unsubscribe(aProcessID: integer): boolean; + /// returns the total (Kernel+User) CPU usage percent of the supplied process + // - aProcessID=0 will return information from the current process + // - returns -1 if the Process ID was not registered via Create/Subscribe + function Percent(aProcessID: integer=0): single; overload; + /// returns the Kernel-space CPU usage percent of the supplied process + // - aProcessID=0 will return information from the current process + // - returns -1 if the Process ID was not registered via Create/Subscribe + function PercentKernel(aProcessID: integer=0): single; overload; + /// returns the User-space CPU usage percent of the supplied process + // - aProcessID=0 will return information from the current process + // - returns -1 if the Process ID was not registered via Create/Subscribe + function PercentUser(aProcessID: integer=0): single; overload; + /// returns the total (Work+Paged) RAM use of the supplied process, in KB + // - aProcessID=0 will return information from the current process + // - returns 0 if the Process ID was not registered via Create/Subscribe + function KB(aProcessID: integer=0): cardinal; overload; + /// percent of current Idle/Kernel/User CPU usage for all processes + function PercentSystem(out Idle,Kernel,User: currency): boolean; + /// returns the detailed CPU and RAM usage percent of the supplied process + // - aProcessID=0 will return information from the current process + // - returns -1 if the Process ID was not registered via Create/Subscribe + function Data(out aData: TSystemUseData; aProcessID: integer=0): boolean; overload; + /// returns the detailed CPU and RAM usage percent of the supplied process + // - aProcessID=0 will return information from the current process + // - returns Timestamp=0 if the Process ID was not registered via Create/Subscribe + function Data(aProcessID: integer=0): TSystemUseData; overload; + /// returns total (Kernel+User) CPU usage percent history of the supplied process + // - aProcessID=0 will return information from the current process + // - returns nil if the Process ID was not registered via Create/Subscribe + // - returns the sample values as an array, starting from the last to the oldest + // - you can customize the maximum depth, with aDepth < HistoryDepth + function History(aProcessID: integer=0; aDepth: integer=0): TSingleDynArray; overload; + /// returns total (Kernel+User) CPU usage percent history of the supplied + // process, as a string of two digits values + // - aProcessID=0 will return information from the current process + // - returns '' if the Process ID was not registered via Create/Subscribe + // - you can customize the maximum depth, with aDepth < HistoryDepth + // - the memory history (in MB) can be optionally returned in aDestMemoryMB + function HistoryText(aProcessID: integer=0; aDepth: integer=0; + aDestMemoryMB: PRawUTF8=nil): RawUTF8; + {$ifndef NOVARIANTS} + /// returns total (Kernel+User) CPU usage percent history of the supplied process + // - aProcessID=0 will return information from the current process + // - returns null if the Process ID was not registered via Create/Subscribe + // - returns the sample values as a TDocVariant array, starting from the + // last to the oldest, with two digits precision (as currency values) + // - you can customize the maximum depth, with aDepth < HistoryDepth + function HistoryVariant(aProcessID: integer=0; aDepth: integer=0): variant; + {$endif} + /// access to a global instance, corresponding to the current process + // - its HistoryDepth will be of 60 items + class function Current(aCreateIfNone: boolean=true): TSystemUse; + /// returns detailed CPU and RAM usage history of the supplied process + // - aProcessID=0 will return information from the current process + // - returns nil if the Process ID was not registered via Create/Subscribe + // - returns the sample values as an array, starting from the last to the oldest + // - you can customize the maximum depth, with aDepth < HistoryDepth + function HistoryData(aProcessID: integer=0; aDepth: integer=0): TSystemUseDataDynArray; overload; + /// if any unexisting (e.g. closed/killed) process should be unregistered + // - e.g. if OpenProcess() API call fails + property UnsubscribeProcessOnAccessError: boolean + read fUnsubscribeProcessOnAccessError write fUnsubscribeProcessOnAccessError; + /// how many items are stored internally, and returned by the History() method + property HistoryDepth: integer read fHistoryDepth; + /// executed when TSystemUse.BackgroundExecute finished its measurement + property OnMeasured: TOnSystemUseMeasured read fOnMeasured write fOnMeasured; + /// low-level access to the associated timer running BackgroundExecute + // - equals nil if has been associated to no timer + property Timer: TSynBackgroundTimer read fTimer write fTimer; + end; + + /// stores information about a disk partition + TDiskPartition = packed record + /// the name of this partition + // - is the Volume name under Windows, or the Device name under POSIX + name: RawUTF8; + /// where this partition has been mounted + // - e.g. 'C:' or '/home' + // - you can use GetDiskInfo(mounted) to retrieve current space information + mounted: TFileName; + /// total size (in bytes) of this partition + size: QWord; + end; + /// stores information about several disk partitions + TDiskPartitions = array of TDiskPartition; + + /// value object able to gather information about the current system memory + TSynMonitorMemory = class(TSynPersistent) + protected + FAllocatedUsed: TSynMonitorOneSize; + FAllocatedReserved: TSynMonitorOneSize; + FMemoryLoadPercent: integer; + FPhysicalMemoryFree: TSynMonitorOneSize; + FVirtualMemoryFree: TSynMonitorOneSize; + FPagingFileTotal: TSynMonitorOneSize; + FPhysicalMemoryTotal: TSynMonitorOneSize; + FVirtualMemoryTotal: TSynMonitorOneSize; + FPagingFileFree: TSynMonitorOneSize; + fLastMemoryInfoRetrievedTix: cardinal; + procedure RetrieveMemoryInfo; virtual; + function GetAllocatedUsed: TSynMonitorOneSize; + function GetAllocatedReserved: TSynMonitorOneSize; + function GetMemoryLoadPercent: integer; + function GetPagingFileFree: TSynMonitorOneSize; + function GetPagingFileTotal: TSynMonitorOneSize; + function GetPhysicalMemoryFree: TSynMonitorOneSize; + function GetPhysicalMemoryTotal: TSynMonitorOneSize; + function GetVirtualMemoryFree: TSynMonitorOneSize; + function GetVirtualMemoryTotal: TSynMonitorOneSize; + public + /// initialize the class, and its nested TSynMonitorOneSize instances + constructor Create(aTextNoSpace: boolean); reintroduce; + /// finalize the class, and its nested TSynMonitorOneSize instances + destructor Destroy; override; + /// some text corresponding to current 'free/total' memory information + // - returns e.g. '10.3 GB / 15.6 GB' + class function FreeAsText(nospace: boolean=false): ShortString; + /// how many physical memory is currently installed, as text (e.g. '32 GB'); + class function PhysicalAsText(nospace: boolean=false): TShort16; + /// returns a JSON object with the current system memory information + // - numbers would be given in KB (Bytes shl 10) + class function ToJSON: RawUTF8; + {$ifndef NOVARIANTS} + /// fill a TDocVariant with the current system memory information + // - numbers would be given in KB (Bytes shl 10) + class function ToVariant: variant; + {$endif} + published + /// Total of allocated memory used by the program + property AllocatedUsed: TSynMonitorOneSize read GetAllocatedUsed; + /// Total of allocated memory reserved by the program + property AllocatedReserved: TSynMonitorOneSize read GetAllocatedReserved; + /// Percent of memory in use for the system + property MemoryLoadPercent: integer read GetMemoryLoadPercent; + /// Total of physical memory for the system + property PhysicalMemoryTotal: TSynMonitorOneSize read GetPhysicalMemoryTotal; + /// Free of physical memory for the system + property PhysicalMemoryFree: TSynMonitorOneSize read GetPhysicalMemoryFree; + /// Total of paging file for the system + property PagingFileTotal: TSynMonitorOneSize read GetPagingFileTotal; + /// Free of paging file for the system + property PagingFileFree: TSynMonitorOneSize read GetPagingFileFree; + {$ifdef MSWINDOWS} + /// Total of virtual memory for the system + // - property not defined under Linux, since not applying to this OS + property VirtualMemoryTotal: TSynMonitorOneSize read GetVirtualMemoryTotal; + /// Free of virtual memory for the system + // - property not defined under Linux, since not applying to this OS + property VirtualMemoryFree: TSynMonitorOneSize read GetVirtualMemoryFree; + {$endif} + end; + + /// value object able to gather information about a system drive + TSynMonitorDisk = class(TSynPersistent) + protected + fName: TFileName; + {$ifdef MSWINDOWS} + fVolumeName: TFileName; + {$endif} + fAvailableSize: TSynMonitorOneSize; + fFreeSize: TSynMonitorOneSize; + fTotalSize: TSynMonitorOneSize; + fLastDiskInfoRetrievedTix: cardinal; + procedure RetrieveDiskInfo; virtual; + function GetName: TFileName; + function GetAvailable: TSynMonitorOneSize; + function GetFree: TSynMonitorOneSize; + function GetTotal: TSynMonitorOneSize; + public + /// initialize the class, and its nested TSynMonitorOneSize instances + constructor Create; override; + /// finalize the class, and its nested TSynMonitorOneSize instances + destructor Destroy; override; + /// some text corresponding to current 'free/total' disk information + // - could return e.g. 'D: 64.4 GB / 213.4 GB' + class function FreeAsText: RawUTF8; + published + /// the disk name + property Name: TFileName read GetName; + {$ifdef MSWINDOWS} + /// the volume name (only available on Windows) + property VolumeName: TFileName read fVolumeName write fVolumeName; + /// space currently available on this disk for the current user + // - may be less then FreeSize, if user quotas are specified (only taken + // into account under Windows) + property AvailableSize: TSynMonitorOneSize read GetAvailable; + {$endif MSWINDOWS} + /// free space currently available on this disk + property FreeSize: TSynMonitorOneSize read GetFree; + /// total space + property TotalSize: TSynMonitorOneSize read GetTotal; + end; + + /// hold low-level information about current memory usage + // - as filled by GetMemoryInfo() + TMemoryInfo = record + memtotal, memfree, filetotal, filefree, vmtotal, vmfree, + allocreserved, allocused: QWord; + percent: integer; + end; + + +/// retrieve low-level information about all mounted disk partitions of the system +// - returned partitions array is sorted by "mounted" ascending order +function GetDiskPartitions: TDiskPartitions; + +/// retrieve low-level information about all mounted disk partitions as text +// - returns e.g. under Linux +// '/ /dev/sda3 (19 GB), /boot /dev/sda2 (486.8 MB), /home /dev/sda4 (0.9 TB)' +// or under Windows 'C:\ System (115 GB), D:\ Data (99.3 GB)' +// - uses internally a cache unless nocache is true +// - includes the free space if withfreespace is true - e.g. '(80 GB / 115 GB)' +function GetDiskPartitionsText(nocache: boolean=false; + withfreespace: boolean=false; nospace: boolean=false): RawUTF8; + +/// returns a JSON object containing basic information about the computer +// - including Host, User, CPU, OS, freemem, freedisk... +function SystemInfoJson: RawUTF8; + +{$ifdef MSWINDOWS} + +/// a wrapper around EnumProcesses() PsAPI call +function EnumAllProcesses(out Count: Cardinal): TCardinalDynArray; + +/// a wrapper around QueryFullProcessImageNameW/GetModuleFileNameEx PsAPI call +function EnumProcessName(PID: Cardinal): RawUTF8; + +{$endif MSWINDOWS} + + +/// retrieve low-level information about current memory usage +// - as used by TSynMonitorMemory +// - under BSD, only memtotal/memfree/percent are properly returned +// - allocreserved and allocused are set only if withalloc is TRUE +function GetMemoryInfo(out info: TMemoryInfo; withalloc: boolean): boolean; + +/// retrieve low-level information about a given disk partition +// - as used by TSynMonitorDisk and GetDiskPartitionsText() +// - only under Windows the Quotas are applied separately to aAvailableBytes +// in respect to global aFreeBytes +function GetDiskInfo(var aDriveFolderOrFile: TFileName; + out aAvailableBytes, aFreeBytes, aTotalBytes: QWord + {$ifdef MSWINDOWS}; aVolumeName: PFileName = nil{$endif}): boolean; + + +implementation + +{$ifdef FPCLINUX} +uses + {$ifdef BSD} + ctypes, + sysctl, + {$else} + Linux, + {$endif BSD} + SynFPCLinux; +{$endif FPCLINUX} + + +{ ************ TSynTable generic types and classes ************************** } + +{$ifndef NOVARIANTS} + +{ TSynTableVariantType } + +var + SynTableVariantType: TCustomVariantType = nil; + +procedure TSynTableVariantType.Clear(var V: TVarData); +begin + //Assert(V.VType=SynTableVariantType.VarType); + TSynTableData(V).VValue := ''; // clean memory release + PPtrUInt(@V)^ := 0; // will set V.VType := varEmpty +end; + +procedure TSynTableVariantType.Copy(var Dest: TVarData; + const Source: TVarData; const Indirect: Boolean); +begin + //Assert(Source.VType=SynTableVariantType.VarType); + inherited Copy(Dest,Source,Indirect); // copy VType+VID+VTable + if not Indirect then + with TSynTableData(Dest) do begin + PtrInt(VValue) := 0; // avoid GPF + VValue := TSynTableData(Source).VValue; // copy by reference + end; +end; + +procedure TSynTableVariantType.IntGet(var Dest: TVarData; + const V: TVarData; Name: PAnsiChar); +begin + TSynTableData(V).GetFieldVariant(RawByteString(Name),variant(Dest)); +end; + +procedure TSynTableVariantType.IntSet(const V, Value: TVarData; + Name: PAnsiChar); +begin + TSynTableData(V).SetFieldValue(RawByteString(Name),Variant(Value)); +end; + +class function TSynTableVariantType.ToID(const V: Variant): integer; +var Data: TSynTableData absolute V; +begin + if Data.VType<>SynTableVariantType.VarType then + result := 0 else + result := Data.VID; +end; + +class function TSynTableVariantType.ToSBF(const V: Variant): TSBFString; +var Data: TSynTableData absolute V; +begin + if Data.VType<>SynTableVariantType.VarType then + result := '' else + result := Data.VValue; +end; + +class function TSynTableVariantType.ToTable(const V: Variant): TSynTable; +var Data: TSynTableData absolute V; +begin + if Data.VType<>SynTableVariantType.VarType then + result := nil else + result := Data.VTable; +end; + +{$endif NOVARIANTS} + + +{ TSynTable } + +{$ifdef CPUX86} +function SortQWord(const A,B: QWord): integer; +asm // Delphi x86 compiler is not efficient, and oldest even incorrect + mov ecx, [eax] + mov eax, [eax + 4] + cmp eax, [edx + 4] + jnz @nz + cmp ecx, [edx] + jz @0 +@nz: jnb @p + or eax, -1 + ret +@0: xor eax, eax + ret +@p: mov eax, 1 +end; + +function SortInt64(const A,B: Int64): integer; +asm // Delphi x86 compiler is not efficient at compiling below code + mov ecx, [eax] + mov eax, [eax + 4] + cmp eax, [edx + 4] + jnz @nz + cmp ecx, [edx] + jz @0 + jnb @p +@n: or eax, -1 + ret +@0: xor eax, eax + ret +@nz: jl @n +@p: mov eax, 1 +end; +{$endif} + +{$ifndef SORTCOMPAREMETHOD} + +function SortU8(P1,P2: PUTF8Char): PtrInt; +begin + if P1<>P2 then + if P1<>nil then + if P2<>nil then begin + result := PByte(P1)^-PByte(P2)^; + exit; + end else + result := 1 else // P2=nil + result := -1 else // P1=nil + result := 0; // P1=P2 +end; + +function SortU16(P1,P2: PUTF8Char): PtrInt; +begin + if P1<>P2 then + if P1<>nil then + if P2<>nil then begin + result := PWord(P1)^-PWord(P2)^; + exit; + end else + result := 1 else // P2=nil + result := -1 else // P1=nil + result := 0; // P1=P2 +end; + +function SortI32(P1,P2: PUTF8Char): PtrInt; +begin + if P1<>P2 then + if P1<>nil then + if P2<>nil then begin + result := PInteger(P1)^-PInteger(P2)^; + exit; + end else + result := 1 else // P2=nil + result := -1 else // P1=nil + result := 0; // P1=P2 +end; + +function SortDouble(P1,P2: PUTF8Char): PtrInt; +var V: Double; +begin + if P1<>P2 then + if P1<>nil then + if P2<>nil then begin + V := PDouble(P1)^-PDouble(P2)^; + if V<0 then + result := -1 else + if V=0 then + result := 0 else + result := 1; + end else + result := 1 else // P2=nil + result := -1 else // P1=nil + result := 0; // P1=P2 +end; + +function SortU24(P1,P2: PUTF8Char): PtrInt; +begin + if P1<>P2 then + if P1<>nil then + if P2<>nil then begin + result := PtrInt(PWord(P1)^)+PtrInt(P1[2])shl 16 + -PtrInt(PWord(P2)^)-PtrInt(P2[2]) shl 16; + exit; + end else + result := 1 else // P2=nil + result := -1 else // P1=nil + result := 0; // P1=P2 +end; + +function SortVarUInt32(P1,P2: PUTF8Char): PtrInt; +begin + if P1<>P2 then + if P1<>nil then + if P2<>nil then begin + result := FromVarUInt32(PByte(P1))-FromVarUInt32(PByte(P2)); + exit; + end else + result := 1 else // P2=nil + result := -1 else // P1=nil + result := 0; // P1=P2 +end; + +function SortVarInt32(P1,P2: PUTF8Char): PtrInt; +begin + if P1<>P2 then + if P1<>nil then + if P2<>nil then begin + result := FromVarInt32(PByte(P1))-FromVarInt32(PByte(P2)); + exit; + end else + result := 1 else // P2=nil + result := -1 else // P1=nil + result := 0; // P1=P2 +end; + +{$ifdef CPU64} // PtrInt = Int64 -> so direct substraction works + +function SortI64(P1,P2: PUTF8Char): PtrInt; +begin + if P1<>P2 then + if P1<>nil then + if P2<>nil then + result := PInt64(P1)^-PInt64(P2)^ else + result := 1 else // P2=nil + result := -1 else // P1=nil + result := 0; // P1=P2 +end; + +function SortVarUInt64(P1,P2: PUTF8Char): PtrInt; +begin + if P1<>P2 then + if P1<>nil then + if P2<>nil then + result := FromVarUInt64(PByte(P1))-FromVarUInt64(PByte(P2)) else + result := 1 else // P2=nil + result := -1 else // P1=nil + result := 0; // P1=P2 +end; + +function SortVarInt64(P1,P2: PUTF8Char): PtrInt; +begin + if P1<>P2 then + if P1<>nil then + if P2<>nil then + result := FromVarInt64(PByte(P1))-FromVarInt64(PByte(P2)) else + result := 1 else // P2=nil + result := -1 else // P1=nil + result := 0; // P1=P2 +end; + +{$else} + +{$ifdef CPUX86} // circumvent comparison slowness (and QWord bug) + +function SortI64(P1,P2: PUTF8Char): PtrInt; +begin + if P1<>P2 then + if P1<>nil then + if P2<>nil then + result := SortInt64(PInt64(P1)^,PInt64(P2)^) else + result := 1 else // P2=nil + result := -1 else // P1=nil + result := 0; // P1=P2 +end; + +function SortVarUInt64(P1,P2: PUTF8Char): PtrInt; +begin + if P1<>P2 then + if P1<>nil then + if P2<>nil then + result := SortQWord(FromVarUInt64(PByte(P1)),FromVarUInt64(PByte(P2))) else + result := 1 else // P2=nil + result := -1 else // P1=nil + result := 0; // P1=P2 +end; + +function SortVarInt64(P1,P2: PUTF8Char): PtrInt; +begin + if P1<>P2 then + if P1<>nil then + if P2<>nil then + result := SortInt64(FromVarInt64(PByte(P1)),FromVarInt64(PByte(P2))) else + result := 1 else // P2=nil + result := -1 else // P1=nil + result := 0; // P1=P2 +end; + +{$else} + +function SortI64(P1,P2: PUTF8Char): PtrInt; +var V: Int64; +begin + if P1<>P2 then + if P1<>nil then + if P2<>nil then begin + V := PInt64(P1)^-PInt64(P2)^; + if V<0 then + result := -1 else + if V>0 then + result := 1 else + result := 0; + end else + result := 1 else // P2=nil + result := -1 else // P1=nil + result := 0; // P1=P2 +end; + +function SortVarUInt64(P1,P2: PUTF8Char): PtrInt; +var V1,V2: QWord; +begin + if P1<>P2 then + if P1<>nil then + if P2<>nil then begin + V1 := FromVarUInt64(PByte(P1)); + V2 := FromVarUInt64(PByte(P2)); + if V1>V2 then + result := 1 else + if V1=V2 then + result := 0 else + result := -1; + end else + result := 1 else // P2=nil + result := -1 else // P1=nil + result := 0; // P1=P2 +end; + +function SortVarInt64(P1,P2: PUTF8Char): PtrInt; +var V1,V2: Int64; +begin + if P1<>P2 then + if P1<>nil then + if P2<>nil then begin + V1 := FromVarInt64(PByte(P1)); + V2 := FromVarInt64(PByte(P2)); + if V1>V2 then + result := 1 else + if V1=V2 then + result := 0 else + result := -1; + end else + result := 1 else // P2=nil + result := -1 else // P1=nil + result := 0; // P1=P2 +end; + +{$endif CPUX86} + +{$endif CPU64} + +function SortStr(P1,P2: PUTF8Char): PtrInt; +var L1, L2, L, i: PtrInt; + PB1, PB2: PByte; +begin + if P1<>P2 then + if P1<>nil then + if P2<>nil then begin + if PtrInt(P1^)<=$7F then begin + L1 := PtrInt(P1^); + inc(P1); + end else begin + PB1 := pointer(P1); + L1 := FromVarUInt32High(PB1); + P1 := pointer(PB1); + end; + if PtrInt(P2^)<=$7F then begin + L2 := PtrInt(P2^); + inc(P2); + end else begin + PB2 := pointer(P2); + L2 := FromVarUInt32High(PB2); + P2 := pointer(PB2); + end; + L := L1; + if L2>L then + L := L2; + for i := 0 to L-1 do begin + result := PtrInt(P1[i])-PtrInt(P2[i]); + if Result<>0 then + exit; + end; + result := L1-L2; + end else + result := 1 else // P2=nil + result := -1 else // P1=nil + result := 0; // P1=P2 +end; + +function SortIStr(P1,P2: PUTF8Char): PtrInt; +var L1, L2, L, i: PtrInt; + PB1, PB2: PByte; +begin + if P1<>P2 then + if P1<>nil then + if P2<>nil then begin + if PtrInt(P1^)<=$7F then begin + L1 := PtrInt(P1^); + inc(P1); + end else begin + PB1 := pointer(P1); + L1 := FromVarUInt32High(PB1); + P1 := pointer(PB1); + end; + if PtrInt(P2^)<=$7F then begin + L2 := PtrInt(P2^); + inc(P2); + end else begin + PB2 := pointer(P2); + L2 := FromVarUInt32High(PB2); + P2 := pointer(PB2); + end; + if L2>L1 then + L := L2 else + L := L1; + for i := 0 to L-1 do // NormToUpperAnsi7 works for both WinAnsi & UTF-8 + if NormToUpperAnsi7[P1[i]]<>NormToUpperAnsi7[P2[i]] then begin + result := PtrInt(P1[i])-PtrInt(P2[i]); + exit; + end; + result := L1-L2; + end else + result := 1 else // P2=nil + result := -1 else // P1=nil + result := 0; // P1=P2 +end; + +const + FIELD_SORT: array[TSynTableFieldType] of TUTF8Compare = ( + nil, // tftUnknown, + SortU8, SortU8, SortU16, SortU24, SortI32, SortI64, + // tftBoolean,tftUInt8,tftUInt16,tftUInt24,tftInt32,tftInt64, + SortI64, SortDouble, SortVarUInt32,SortVarInt32,SortVarUInt64, + // tftCurrency,tftDouble, tftVarUInt32, tftVarInt32,tftVarUInt64, + SortStr, SortStr, SortStr, nil, SortVarInt64); + // tftWinAnsi,tftUTF8, tftBlobInternal,tftBlobExternal,tftVarInt64); + +{$endif SORTCOMPAREMETHOD} + +const + FIELD_FIXEDSIZE: array[TSynTableFieldType] of Integer = ( + 0, // tftUnknown, + 1, 1, 2, 3, 4, 8, 8, 8, + // tftBoolean, tftUInt8, tftUInt16, tftUInt24, tftInt32, tftInt64, tftCurrency, tftDouble + -1, -1, -1, // tftVarUInt32, tftVarInt32, tftVarUInt64 have -1 as size + -2, -2, -2, // tftWinAnsi, tftUTF8, tftBlobInternal have -2 as size + -3, // tftBlobExternal has -3 as size + -1); //tftVarInt64 + + // note: boolean is not in this set, because it can be 'true' or 'false' + FIELD_INTEGER: TSynTableFieldTypes = [ + tftUInt8, tftUInt16, tftUInt24, tftInt32, tftInt64, + tftVarUInt32, tftVarInt32, tftVarUInt64, tftVarInt64]; + +function TSynTable.AddField(const aName: RawUTF8; + aType: TSynTableFieldType; aOptions: TSynTableFieldOptions): TSynTableFieldProperties; +var aSize: Integer; +begin + result := nil; + aSize := FIELD_FIXEDSIZE[aType]; + if (self=nil) or (aSize=0) or IsRowID(pointer(aName)) or + not PropNameValid(pointer(aName)) or (GetFieldFromName(aName)<>nil) then + exit; + result := TSynTableFieldProperties.Create; + if fAddedField=nil then + fAddedField := TList.Create; + fAddedField.Add(result); + result.Name := aName; + result.FieldType := aType; + if tfoUnique in aOptions then + Include(aOptions,tfoIndex); // create an index for faster Unique field + if aSize=-3 then // external field has no index available + aOptions := aOptions-[tfoIndex,tfoUnique]; + result.Options := aOptions; + if aSize>0 then begin + // fixed-size field should be inserted left-side of the stream + if (tfoIndex in aOptions) or (aSize and 3=0) then begin + // indexed field or size is alignment friendly: put left side + if not ((tfoIndex in aOptions) and (aSize and 3=0)) then + // indexed+aligned field -> set first, otherwise at variable or not indexed + while result.FieldNumber=0 then + result.FieldNumber := fFieldVariableIndex else + result.FieldNumber := fField.Count; + fField.Insert(result.FieldNumber,result); + end else begin + if (tfoIndex in aOptions) and (fFieldVariableIndex>=0) then begin + // indexed field should be added left side (faster access for sort) + result.FieldNumber := fFieldVariableIndex; + while result.FieldNumbernil) and ((RecordBuffer=nil) or (RecordBufferLen=0)) then begin + // no data yet -> use default + RecordBuffer := pointer(fDefaultRecordData); + RecordBufferLen := fDefaultRecordLength; + end; + if RecordBuffer=pointer(result) then + // update content code below will fail -> please correct calling code + raise ETableDataException.CreateUTF8('In-place call of %.UpdateFieldData',[self]); + if (self=nil) or (cardinal(FieldIndex)>=cardinal(fField.Count)) then begin + SetString(result,PAnsiChar(RecordBuffer),RecordBufferLen); + exit; + end; + F := TSynTableFieldProperties(fField.List[FieldIndex]); + NewSize := length(NewFieldData); + if NewSize=0 then begin + // no NewFieldData specified -> use default field data to be inserted + NewData := pointer(F.fDefaultFieldData); + NewSize := F.fDefaultFieldLength; + end else + NewData := pointer(NewFieldData); + Dest := GetData(RecordBuffer,F); + DestOffset := Dest-RecordBuffer; + // update content + OldSize := F.GetLength(Dest); + dec(RecordBufferLen,OldSize); + SetLength(Result,RecordBufferLen+NewSize); + MoveFast(RecordBuffer^,PByteArray(result)[0],DestOffset); + MoveFast(NewData^,PByteArray(result)[DestOffset],NewSize); + MoveFast(Dest[OldSize],PByteArray(result)[DestOffset+NewSize],RecordBufferLen-DestOffset); +end; + +constructor TSynTable.Create(const aTableName: RawUTF8); +begin + if not PropNameValid(pointer(aTableName)) then + raise ETableDataException.CreateUTF8('Invalid %.Create(%)',[self,aTableName]); + fTableName := aTableName; + fField := TObjectList.Create; + fFieldVariableIndex := -1; +end; + +procedure TSynTable.LoadFrom(var RD: TFileBufferReader); +var n, i: integer; + aTableName: RawUTF8; +begin + fField.Clear; + RD.Read(aTableName); + if not PropNameValid(pointer(aTableName)) then + RD.ErrorInvalidContent; + fTableName := aTableName; + n := RD.ReadVarUInt32; + if cardinal(n)>=MAX_SQLFIELDS then + RD.ErrorInvalidContent; + for i := 0 to n-1 do + fField.Add(TSynTableFieldProperties.CreateFrom(RD)); + AfterFieldModif; +end; + +destructor TSynTable.Destroy; +begin + fField.Free; + fAddedField.Free; + inherited; +end; + +function TSynTable.GetFieldCount: integer; +begin + if self=nil then + result := 0 else + result := fField.Count; +end; + +function TSynTable.GetFieldFromName(const aName: RawUTF8): TSynTableFieldProperties; +var i: integer; +begin + if self<>nil then + for i := 0 to fField.Count-1 do begin + result := TSynTableFieldProperties(fField.List[i]); + if IdemPropNameU(result.Name,aName) then + exit; + end; + result := nil; +end; + +function TSynTable.GetFieldIndexFromName(const aName: RawUTF8): integer; +begin + if self<>nil then + for result := 0 to fField.Count-1 do + if IdemPropNameU(TSynTableFieldProperties(fField.List[result]).Name,aName) then + exit; + result := -1; +end; + +function TSynTable.GetFieldIndexFromShortName(const aName: ShortString): integer; +begin + if self<>nil then + for result := 0 to fField.Count-1 do + with TSynTableFieldProperties(fField.List[result]) do + if IdemPropName(aName,pointer(Name),length(Name)) then + exit; + result := -1; +end; + +function TSynTable.GetFieldType(Index: integer): TSynTableFieldProperties; +begin + if (self=nil) or (cardinal(Index)>=cardinal(fField.Count)) then + result := nil else // avoid GPF + result := fField.List[Index]; +end; + +{$ifndef DELPHI5OROLDER} + +function TSynTable.CreateJSONWriter(JSON: TStream; Expand, withID: boolean; + const Fields: TSQLFieldBits): TJSONWriter; +begin + result := CreateJSONWriter(JSON,Expand,withID,FieldBitsToIndex(Fields,fField.Count)); +end; + +function TSynTable.CreateJSONWriter(JSON: TStream; Expand, withID: boolean; + const Fields: TSQLFieldIndexDynArray): TJSONWriter; +var i,nf,n: integer; +begin + if (self=nil) or ((Fields=nil) and not withID) then begin + result := nil; // no data to retrieve + exit; + end; + result := TJSONWriter.Create(JSON,Expand,withID,Fields); + // set col names + if withID then + n := 1 else + n := 0; + nf := length(Fields); + SetLength(result.ColNames,nf+n); + if withID then + result.ColNames[0] := 'ID'; + for i := 0 to nf-1 do + result.ColNames[i+n] := TSynTableFieldProperties(fField.List[Fields[i]]).Name; + result.AddColumns; // write or init field names for appropriate JSON Expand +end; + +procedure TSynTable.GetJSONValues(aID: integer; RecordBuffer: PUTF8Char; + W: TJSONWriter); +var i,n: integer; + buf: array[0..MAX_SQLFIELDS-1] of PUTF8Char; +begin + if (self=nil) or (RecordBuffer=nil) or (W=nil) then + exit; // avoid GPF + if W.Expand then begin + W.Add('{'); + if W.WithID then + W.AddString(W.ColNames[0]); + end; + if W.WithID then begin + W.Add(aID); + W.Add(','); + n := 1; + end else + n := 0; + for i := 0 to fField.Count-1 do begin + buf[i] := RecordBuffer; + inc(RecordBuffer,TSynTableFieldProperties(fField.List[i]).GetLength(RecordBuffer)); + end; + for i := 0 to length(W.Fields)-1 do begin + if W.Expand then begin + W.AddString(W.ColNames[n]); // '"'+ColNames[]+'":' + inc(n); + end; + TSynTableFieldProperties(fField.List[W.Fields[i]]).GetJSON(buf[i],W); + W.Add(','); + end; + W.CancelLastComma; // cancel last ',' + if W.Expand then + W.Add('}'); +end; + +function TSynTable.IterateJSONValues(Sender: TObject; Opaque: pointer; + ID: integer; Data: pointer; DataLen: integer): boolean; +var Statement: TSynTableStatement absolute Opaque; + F: TSynTableFieldProperties; + nWhere,fIndex: cardinal; +begin // note: we should have handled -2 (=COUNT) case already + nWhere := length(Statement.Where); + if (self=nil) or (Statement=nil) or (Data=nil) or + (Statement.Select=nil) or (nWhere>1) or + ((nWhere=1)and(Statement.Where[0].ValueSBF='')) then begin + result := false; + exit; + end; + result := true; + if nWhere=1 then begin // Where=nil -> all rows + fIndex := Statement.Where[0].Field; + if fIndex=SYNTABLESTATEMENTWHEREID then begin + if ID<>Statement.Where[0].ValueInteger then + exit; + end else begin + dec(fIndex); // 0 is ID, 1 for field # 0, 2 for field #1, and so on... + if fIndex0 then + exit; + end; + end; + end; + GetJSONValues(ID,Data,Statement.Writer); +end; + +{$endif DELPHI5OROLDER} + +function TSynTable.GetData(RecordBuffer: PUTF8Char; Field: TSynTableFieldProperties): pointer; +var i: integer; + PB: PByte; +begin + if Field.Offset>=0 then + result := RecordBuffer+Field.Offset else begin + result := RecordBuffer+fFieldVariableOffset; + for i := fFieldVariableIndex to Field.FieldNumber-1 do + if i in fFieldIsVarString then begin + // inlined result := GotoNextVarString(result); + if PByte(result)^<=$7f then + inc(PtrUInt(result),PByte(result)^+1) else begin + PB := result; + inc(PtrUInt(result),FromVarUInt32High(PB)+PtrUInt(PB)-PtrUInt(result)); + end; + end else + if not (i in fFieldIsExternal) then begin + // inlined result := GotoNextVarInt(result) + while PByte(result)^>$7f do inc(PtrUInt(result)); + inc(PtrUInt(result)); + end; + end; +end; + +procedure TSynTable.SaveTo(WR: TFileBufferWriter); +var i: Integer; +begin + WR.Write(fTableName); + WR.WriteVarUInt32(fField.Count); + for i := 0 to fField.Count-1 do + TSynTableFieldProperties(fField.List[i]).SaveTo(WR); +end; + +procedure TSynTable.AfterFieldModif; +var i, Offs: integer; +begin + PInt64(@fFieldIsVarString)^ := 0; + PInt64(@fFieldIsExternal)^ := 0; + fFieldVariableIndex := -1; + fDefaultRecordLength := 0; + fFieldHasUniqueIndexes := false; + Offs := 0; + for i := 0 to fField.Count-1 do + with TSynTableFieldProperties(fField.List[i]) do begin + FieldNumber := i; + {$ifndef SORTCOMPAREMETHOD} + SortCompare := FIELD_SORT[FieldType]; + {$endif} + Owner := self; + FieldSize := FIELD_FIXEDSIZE[FieldType]; + if FieldSize>=0 then begin + //assert(Offs>=0); + Offset := Offs; + inc(Offs,FieldSize); + inc(fDefaultRecordLength,FieldSize); + fDefaultFieldLength := FieldSize; + end else begin + if FieldSize=-3 then + Include(fFieldIsExternal,i) else begin + fDefaultFieldLength := 1; + inc(fDefaultRecordLength); + if FieldSize=-2 then + Include(fFieldIsVarString,i); + {$ifndef SORTCOMPAREMETHOD} + if (FieldType in [tftWinAnsi,tftUTF8]) and + (tfoCaseInsensitive in Options) then + SortCompare := SortIStr; // works for both WinAnsi and UTF-8 encodings + {$endif} + end; + // we need the Offset even for tftBlobExternal (FieldSize=-3) + if fFieldVariableIndex<0 then begin + fFieldVariableIndex := i; + fFieldVariableOffset := Offs; + Offs := -1; + end; + Offset := Offs; + dec(Offs); + end; + SetLength(fDefaultFieldData,fDefaultFieldLength); + FillcharFast(pointer(fDefaultFieldData)^,fDefaultFieldLength,0); + end; + SetLength(fDefaultRecordData,fDefaultRecordLength); + FillcharFast(pointer(fDefaultRecordData)^,fDefaultRecordLength,0); +end; + +procedure TSynTable.FieldIndexModify(aOldIndex, aNewIndex: integer; + aOldRecordData, aNewRecordData: pointer); +var F: integer; +begin + for F := 0 to fField.Count-1 do + with TSynTableFieldProperties(fField.List[F]) do + if tfoIndex in Options then + OrderedIndexUpdate(aOldIndex,aNewIndex,aOldRecordData,aNewRecordData); +end; + +procedure TSynTable.Filter(var RecordBuffer: TSBFString); +var Old, New: RawUTF8; + NewRecord: TSBFString; // UpdateFieldData update result in-place + F, i: integer; +begin + for F := 0 to fField.Count-1 do + with TSynTableFieldProperties(fField.List[F]) do + if Filters<>nil then begin + Old := GetRawUTF8(pointer(RecordBuffer)); + New := Old; + for i := 0 to Filters.Count-1 do + TSynFilter(Filters.List[i]).Process(F,New); + if Old<>New then begin + // value was changed -> store modified + UpdateFieldData(pointer(RecordBuffer),length(RecordBuffer),F, + NewRecord,SBFFromRawUTF8(New)); + RecordBuffer := NewRecord; + end; + end; +end; + +{$ifndef NOVARIANTS} +function TSynTable.Data(aID: integer; RecordBuffer: pointer; RecordBufferLen: Integer): Variant; +var data: TSynTableData absolute result; +begin + if SynTableVariantType=nil then + SynTableVariantType := SynRegisterCustomVariantType(TSynTableVariantType); + {$ifndef FPC} + if data.VType and VTYPE_STATIC<>0 then + {$endif} + VarClear(result); + data.VType := SynTableVariantType.VarType; + data.VID := aID; + data.VTable := self; + pointer(data.VValue) := nil; // avoid GPF + if RecordBuffer=nil then + data.VValue := DefaultRecordData else begin + if RecordBufferLen=0 then + RecordBufferLen := DataLength(RecordBuffer); + SetString(data.VValue,PAnsiChar(RecordBuffer),RecordBufferLen); + end; +end; +{$endif NOVARIANTS} + +function TSynTable.DataLength(RecordBuffer: pointer): integer; +var F: Integer; + PC: PUTF8Char; +begin + if (Self<>nil) and (RecordBuffer<>nil) then begin + PC := RecordBuffer; + for F := 0 to fField.Count-1 do + inc(PC,TSynTableFieldProperties(fField.List[F]).GetLength(PC)); + result := PC-RecordBuffer; + end else + result := 0; +end; + +function TSynTable.UpdateFieldEvent(Sender: TObject; Opaque: pointer; + ID, Index: integer; Data: pointer; DataLen: integer): boolean; +var Added: PUpdateFieldEvent absolute Opaque; + F, aSize: integer; +begin // in practice, this data processing is very fast (thanks to WR speed) + with Added^ do begin + result := Count1 shl 30 then + raise ETableDataException.CreateUTF8('%: File size too big (>1GB)',[self]) else + Offsets64[Count] := WR.TotalWritten; + IDs[Count] := ID; + NewIndexs[Index] := Count; + inc(Count); + end; +end; + +function TSynTable.UpdateFieldRecord(RecordBuffer: PUTF8Char; + var AvailableFields: TSQLFieldBits): TSBFString; +var Lens: array[0..MAX_SQLFIELDS-1] of Integer; + F, Len, TotalLen: integer; + P: PUTF8Char; + Dest: PByte; +begin + // retrieve all field buffer lengths, to speed up record content creation + TotalLen := 0; + P := RecordBuffer; + for F := 0 to fField.Count-1 do + with TSynTableFieldProperties(fField.List[F]) do + if F in AvailableFields then begin + Len := GetLength(P); + inc(P,Len); + inc(TotalLen,Len); + Lens[F] := Len; + end else + inc(TotalLen,fDefaultFieldLength); + // create new record content + P := RecordBuffer; + SetString(Result,nil,TotalLen); + Dest := pointer(Result); + for F := 0 to fField.Count-1 do + with TSynTableFieldProperties(fField.List[F]) do + if F in AvailableFields then begin + Len := Lens[F]; + {$ifdef FPC}Move{$else}MoveFast{$endif}(P^,Dest^,Len); + inc(P,Len); + inc(Dest,Len); + end else begin + FillcharFast(Dest^,fDefaultFieldLength,0); + inc(Dest,fDefaultFieldLength); + end; + //Assert(PtrUInt(Dest)-PtrUInt(result)=PtrUInt(TotalLen)); +end; + +function TSynTable.Validate(RecordBuffer: pointer; RecordIndex: integer): string; +var F: integer; +begin + result := ''; + for F := 0 to fField.Count-1 do + with TSynTableFieldProperties(fField.List[F]) do + if Validates<>nil then begin + result := Validate(RecordBuffer,RecordIndex); + if result<>'' then + exit; + end; +end; + + +{ TSynTableFieldProperties } + +constructor TSynTableFieldProperties.CreateFrom(var RD: TFileBufferReader); +begin + fOrderedIndexFindAdd := -1; + RD.Read(Name); + if not PropNameValid(pointer(Name)) then + RD.ErrorInvalidContent; + RD.Read(@FieldType,SizeOf(FieldType)); + RD.Read(@Options,SizeOf(Options)); + if (FieldType>high(FieldType)) then + RD.ErrorInvalidContent; + OrderedIndexCount := RD.ReadVarUInt32Array(OrderedIndex); + if OrderedIndexCount>0 then begin + if tfoIndex in Options then begin + //assert(OrderedIndexReverse=nil); + OrderedIndexReverseSet(-1); // compute whole OrderedIndexReverse[] array + end else + RD.ErrorInvalidContent; + end; + // we allow a void OrderedIndex[] array from disk +end; + +destructor TSynTableFieldProperties.Destroy; +begin + Filters.Free; + Validates.Free; + inherited; +end; + +function TSynTableFieldProperties.GetJSON(FieldBuffer: pointer; + W: TTextWriter): pointer; +var len: integer; + tmp: RawUTF8; +begin + case FieldType of + // fixed-sized field value + tftBoolean: + W.Add(PBoolean(FieldBuffer)^); + tftUInt8: + W.Add(PByte(FieldBuffer)^); + tftUInt16: + W.Add(PWord(FieldBuffer)^); + tftUInt24: + // PInteger()^ and $ffffff -> possible GPF on Memory Mapped file + W.Add(PWord(FieldBuffer)^+integer(PByteArray(FieldBuffer)^[2])shl 16); + tftInt32: + W.Add(PInteger(FieldBuffer)^); + tftInt64: + W.Add(PInt64(FieldBuffer)^); + tftCurrency: + W.AddCurr64(PInt64(FieldBuffer)^); + tftDouble: + W.AddDouble(PDouble(FieldBuffer)^); + // some variable-size field value + tftVarUInt32: + W.Add(FromVarUInt32(PByte(FieldBuffer))); + tftVarInt32: + W.Add(FromVarInt32(PByte(FieldBuffer))); + tftVarUInt64: + W.AddQ(FromVarUInt64(PByte(FieldBuffer))); + tftVarInt64: + W.Add(FromVarInt64(PByte(FieldBuffer))); + // text storage - WinAnsi could use less space than UTF-8 + tftWinAnsi, tftUTF8: begin + W.Add('"'); + len := FromVarUInt32(PByte(FieldBuffer)); + if len>0 then + if FieldType=tftUTF8 then + W.AddJSONEscape(PAnsiChar(FieldBuffer),len) else begin + SetLength(tmp,len*3); // in-place decoding and appending + W.AddJSONEscape(pointer(tmp),WinAnsiBufferToUtf8(pointer(tmp),PAnsiChar(FieldBuffer),len)-pointer(tmp)); + end; + W.Add('"'); + result := PAnsiChar(FieldBuffer)+len; + exit; + end; + tftBlobInternal: begin + W.AddShort('"X'''); + len := FromVarUInt32(PByte(FieldBuffer)); + W.AddBinToHex(PByte(FieldBuffer),len); + W.Add('''','"'); + end; + tftBlobExternal: + ; // BLOB fields are not handled here, but must be directly accessed + end; + result := PAnsiChar(FieldBuffer)+FieldSize; // // tftWinAnsi,tftUTF8 already done +end; + +function TSynTableFieldProperties.GetLength(FieldBuffer: pointer): Integer; +var PB: PByte; +begin + if FieldSize>=0 then + result := FieldSize else + case FieldSize of + -1: begin // variable-length data + result := 0; + while PByteArray(FieldBuffer)^[result]>$7f do inc(result); + inc(result); + end; + -2: begin // tftWinAnsi, tftUTF8, tftBlobInternal records + result := PByte(FieldBuffer)^; + if result<=$7F then + inc(Result) else begin + PB := FieldBuffer; + result := FromVarUInt32High(PB)+PtrUInt(PB)-PtrUInt(FieldBuffer); + end; + end; + else + result := 0; // tftBlobExternal is not stored in FieldBuffer + end; +end; + +{$ifndef NOVARIANTS} +function TSynTableFieldProperties.GetVariant(FieldBuffer: pointer): Variant; +begin + GetVariant(FieldBuffer,result); +end; + +procedure TSynTableFieldProperties.GetVariant(FieldBuffer: pointer; var result: Variant); +var len: integer; + PB: PByte absolute FieldBuffer; + PA: PAnsiChar absolute FieldBuffer; + PU: PUTF8Char absolute FieldBuffer; + tmp: RawByteString; + {$ifndef UNICODE} + WS: WideString; + {$endif} +begin + case FieldType of + // fixed-sized field value + tftBoolean: + result := PBoolean(FieldBuffer)^; + tftUInt8: + result := PB^; + tftUInt16: + result := PWord(FieldBuffer)^; + tftUInt24: + // PInteger()^ and $ffffff -> possible GPF on Memory Mapped file + result := PWord(FieldBuffer)^+integer(PByteArray(FieldBuffer)^[2])shl 16; + tftInt32: + result := PInteger(FieldBuffer)^; + tftInt64: + result := PInt64(FieldBuffer)^; + tftCurrency: + result := PCurrency(FieldBuffer)^; + tftDouble: + result := PDouble(FieldBuffer)^; + // some variable-size field value + tftVarUInt32: + result := FromVarUInt32(PB); + tftVarInt32: + result := FromVarInt32(PB); + tftVarUInt64: + result := FromVarUInt64(PB); + tftVarInt64: + result := FromVarInt64(PB); + // text storage - WinAnsi could use less space than UTF-8 + tftWinAnsi: begin + len := FromVarUInt32(PB); + if len>0 then + {$ifdef UNICODE} + result := WinAnsiToUnicodeString(PA,len) + {$else} + result := CurrentAnsiConvert.AnsiToAnsi(WinAnsiConvert,PA,len) + {$endif} else + result := ''; + end; + tftUTF8: begin + len := FromVarUInt32(PB); + if len>0 then + {$ifdef UNICODE} + result := UTF8DecodeToUnicodeString(PU,len) + {$else} begin + UTF8ToSynUnicode(PU,len,WS); + result := WS; + end + {$endif} else + result := ''; + end; + tftBlobInternal: begin + len := FromVarUInt32(PB); + SetString(tmp,PA,len); + result := tmp; // return internal BLOB content as string + end + else + result := ''; // tftBlobExternal fields e.g. must be directly accessed + end; +end; +{$endif} + +{$ifdef ISDELPHI20062007} + {$WARNINGS OFF} // circument Delphi 2007 false positive warning +{$endif} + +function TSynTableFieldProperties.GetValue(FieldBuffer: pointer): RawUTF8; +var len: integer; + PB: PByte absolute FieldBuffer; + PC: PAnsiChar absolute FieldBuffer; +begin + result := ''; + case FieldType of + // fixed-sized field value + tftBoolean: + JSONBoolean(PBoolean(FieldBuffer)^,result); + tftUInt8: + UInt32ToUtf8(PB^,result); + tftUInt16: + UInt32ToUtf8(PWord(FieldBuffer)^,result); + tftUInt24: + // PInteger()^ and $ffffff -> possible GPF on Memory Mapped file + UInt32ToUtf8(PWord(FieldBuffer)^+integer(PByteArray(FieldBuffer)^[2])shl 16,result); + tftInt32: + Int32ToUtf8(PInteger(FieldBuffer)^,result); + tftInt64: + Int64ToUtf8(PInt64(FieldBuffer)^,result); + tftCurrency: + Curr64ToStr(PInt64(FieldBuffer)^,result); + tftDouble: + ExtendedToStr(PDouble(FieldBuffer)^,DOUBLE_PRECISION,result); + // some variable-size field value + tftVarUInt32: + UInt32ToUtf8(FromVarUInt32(PB),result); + tftVarInt32: + Int32ToUtf8(FromVarInt32(PB),result); + tftVarUInt64: + UInt64ToUtf8(FromVarUInt64(PB),result); + tftVarInt64: + Int64ToUtf8(FromVarInt64(PB),result); + // text storage - WinAnsi could use less space than UTF-8 + tftWinAnsi, tftUTF8, tftBlobInternal: begin + len := FromVarUInt32(PB); + if len>0 then + if FieldType<>tftWinAnsi then + SetString(result,PC,len) else + result := WinAnsiConvert.AnsiBufferToRawUTF8(PC,len); + end; + // tftBlobExternal fields e.g. must be directly accessed + end; +end; + +{$ifdef ISDELPHI20062007} + {$WARNINGS ON} // circument Delphi 2007 false positive warning +{$endif} + +procedure TSynTableFieldProperties.OrderedIndexReverseSet(aOrderedIndex: integer); +var nrev, ndx, n: PtrInt; +begin + n := length(OrderedIndex); + nrev := length(OrderedIndexReverse); + if nrev=0 then + if n=0 then + exit else begin + // void OrderedIndexReverse[] + nrev := MaxInteger(OrderedIndex,OrderedIndexCount,n)+1; + SetLength(OrderedIndexReverse,nrev); + FillcharFast(OrderedIndexReverse[0],nrev*4,255); // all to -1 + Reverse(OrderedIndex,OrderedIndexCount,pointer(OrderedIndexReverse)); + end; + if PtrUInt(aOrderedIndex)>=PtrUInt(OrderedIndexCount) then + exit; // e.g. CreateFrom() will call OrderedIndexReverseSet(-1) + if nrev=nrev then + SetLength(OrderedIndexReverse,ndx+256) else + OrderedIndexReverse[ndx] := aOrderedIndex; +end; + +procedure TSynTableFieldProperties.OrderedIndexSort(L, R: PtrInt); +var I, J, P: PtrInt; + TmpI, TmpJ: integer; +begin + if (L0 do dec(J); + end; + if I <= J then begin + if I < J then begin + TmpJ := OrderedIndex[J]; + TmpI := OrderedIndex[I]; + OrderedIndex[J] := TmpI; + OrderedIndex[I] := TmpJ; + // keep OrderedIndexReverse[OrderedIndex[i]]=i + OrderedIndexReverse[TmpJ] := I; + OrderedIndexReverse[TmpI] := J; + end; + if P = I then P := J else if P = J then P := I; + inc(I); dec(J); + end; + until I > J; + if J - L < R - I then begin // use recursion only for smaller range + if L < J then + OrderedIndexSort(L, J); + L := I; + end else begin + if I < R then + OrderedIndexSort(I, R); + R := J; + end; + until L >= R; +end; + +procedure TSynTableFieldProperties.OrderedIndexRefresh; +begin + if (self=nil) or not OrderedIndexNotSorted then + exit; // already sorted + OrderedIndexSort(0,OrderedIndexCount-1); + OrderedIndexNotSorted := false; +end; + +function TSynTableFieldProperties.OrderedIndexFind(Value: pointer): PtrInt; +var L,R: PtrInt; + cmp: PtrInt; +begin + if OrderedIndexNotSorted then + OrderedIndexRefresh; + L := 0; + R := OrderedIndexCount-1; + with Owner do + if (R>=0) and Assigned(GetRecordData) then + repeat + result := (L + R) shr 1; + cmp := SortCompare(GetData(GetRecordData(OrderedIndex[result],DataTemp1),self),Value); + if cmp=0 then + exit; + if cmp<0 then + L := result + 1 else + R := result - 1; + until (L > R); + result := -1 +end; + +function TSynTableFieldProperties.OrderedIndexFindAdd(Value: pointer): PtrInt; +var L,R,i: PtrInt; + cmp: PtrInt; +begin + if OrderedIndexNotSorted then + OrderedIndexRefresh; + R := OrderedIndexCount-1; + if R<0 then + result := 0 else + with Owner do begin + fOrderedIndexFindAdd := -1; + L := 0; + result := -1; // return -1 if found + repeat + i := (L + R) shr 1; + cmp := SortCompare(GetData(GetRecordData(OrderedIndex[i],DataTemp1),self),Value); + if cmp=0 then + exit; + if cmp<0 then + L := i + 1 else + R := i - 1; + until (L > R); + while (i>=0) and + (SortCompare(GetData(GetRecordData(OrderedIndex[i],DataTemp1),self),Value)>=0) do + dec(i); + result := i+1; // return the index where to insert + end; + fOrderedIndexFindAdd := result; // store inserting index for OrderedIndexUpdate +end; + +function TSynTableFieldProperties.OrderedIndexMatch(WhereSBFValue: pointer; + var MatchIndex: TIntegerDynArray; var MatchIndexCount: integer; Limit: Integer=0): Boolean; +var i, L,R: PtrInt; +begin + result := false; + if (self=nil) or (WhereSBFValue=nil) or not Assigned(Owner.GetRecordData) or + (OrderedIndex=nil) or not (tfoIndex in Options) then + exit; + i := OrderedIndexFind(WhereSBFValue); + if i<0 then + exit; // WHERE value not found + if (tfoUnique in Options) or (Limit=1) then begin + // unique index: direct fastest O(log(n)) binary search + AddSortedInteger(MatchIndex,MatchIndexCount,OrderedIndex[i]); + // AddSortedInteger() will fail if OrderedIndex[i] already exists + end else + with Owner do begin + // multiple index matches possible: add matching range + L := i; + repeat + dec(L); + until (L<0) or (SortCompare(GetData(GetRecordData( + OrderedIndex[L],DataTemp1),self),WhereSBFValue)<>0); + R := i; + repeat + inc(R); + until (R>=OrderedIndexCount) or + (SortCompare(GetData(GetRecordData(OrderedIndex[R],DataTemp1),self),WhereSBFValue)<>0); + if Limit=0 then + Limit := MaxInt; // no LIMIT set -> retrieve all rows + for i := L+1 to R-1 do begin + AddSortedInteger(MatchIndex,MatchIndexCount,OrderedIndex[i]); + dec(Limit); + if Limit=0 then + Break; // reach LIMIT upperbound result count + end; + end; + result := true; +end; + +function TSynTableFieldProperties.OrderedIndexUpdate(aOldIndex, aNewIndex: integer; + aOldRecordData, aNewRecordData: pointer): boolean; +var aOldIndexIndex: integer; +begin + result := false; + if (self=nil) or not Assigned(Owner.GetRecordData) then + exit; // avoid GPF + // update content + if aOldIndex<0 then + if aNewIndex<0 then begin + // both indexes equal -1 -> force sort + OrderedIndexSort(0,OrderedIndexCount-1); + OrderedIndexNotSorted := false; + end else begin + // added record + if tfoUnique in Options then begin + if fOrderedIndexFindAdd<0 then + raise ETableDataException.CreateUTF8( + '%.CheckConstraint call needed before %.OrderedIndexUpdate',[self,Name]); + OrderedIndexReverseSet(InsertInteger(OrderedIndex,OrderedIndexCount, + aNewIndex,fOrderedIndexFindAdd)); + end else begin + AddInteger(OrderedIndex,OrderedIndexCount,aNewIndex); + OrderedIndexReverseSet(OrderedIndexCount-1); + OrderedIndexNotSorted := true; // -> OrderedIndexSort() call on purpose + end; + end else begin + // aOldIndex>=0: update a value + // retrieve position in OrderedIndex[] to be deleted/updated + if OrderedIndexReverse=nil then + OrderedIndexReverseSet(0) else // do OrderedIndexReverse[OrderedIndex[i]] := i + {assert(aOldIndexnil) or (aOldIndex<>aNewIndex) then // not in-place update + with Owner do begin + if aOldRecordData=nil then + aOldRecordData := GetRecordData(aOldIndex,DataTemp1); + if aNewRecordData=nil then + aNewRecordData := GetRecordData(aNewIndex,DataTemp2); + if SortCompare(GetData(aOldRecordData,self),GetData(aNewRecordData,self))=0 then begin + // only sort if field content was modified -> MUCH faster in most case + result := true; + exit; + end; + end; + if tfoUnique in Options then begin + if fOrderedIndexFindAdd>=0 then begin + // we know which OrderedIndex[] has to be changed -> manual update + // - this is still a bottleneck in the current implementation, but + // I was not able to find out how to make it faster, and still + // being able to check unique field constraints without changing the + // OrderedIndex[] content from a simple list into e.g. a red-black + // tree: such a structure performs better, but uses much more memory + // and is to be implemented + // - it's still fast, faster than any DB AFAIK, around 500 updates + // per second with 1,000,000 records on a Core i7 + // - it's still faster to refresh OrderedIndex[] than iterating + // through all items to validate the unique constraint + DeleteInteger(OrderedIndex,OrderedIndexCount,aOldIndexIndex); + if fOrderedIndexFindAdd>aOldIndexIndex then + dec(fOrderedIndexFindAdd); + InsertInteger(OrderedIndex,OrderedIndexCount,aNewIndex,fOrderedIndexFindAdd); + Reverse(OrderedIndex,OrderedIndexCount,pointer(OrderedIndexReverse)); + end else + // slow full sort - with 1,000,000 items it's about 100 times slower + // (never called with common usage in SynBigTable unit) + OrderedIndexSort(0,OrderedIndexCount-1); + end else + OrderedIndexNotSorted := true; // will call OrderedIndexSort() on purpose + end; + end; + fOrderedIndexFindAdd := -1; // consume this value + result := true; +end; + +procedure TSynTableFieldProperties.SaveTo(WR: TFileBufferWriter); +begin + WR.Write(Name); + WR.Write(@FieldType,SizeOf(FieldType)); + WR.Write(@Options,SizeOf(Options)); + WR.WriteVarUInt32Array(OrderedIndex,OrderedIndexCount,wkVarUInt32); +end; + +function TSynTableFieldProperties.SBF(const Value: Int64): TSBFString; +var tmp: array[0..15] of AnsiChar; +begin + case FieldType of + tftInt32: begin // special version for handling negative values + PInteger(@tmp)^ := Value; + SetString(Result,tmp,SizeOf(Integer)); + end; + tftUInt8, tftUInt16, tftUInt24, tftInt64: + SetString(Result,PAnsiChar(@Value),FieldSize); + tftVarUInt32: + SetString(Result,tmp,PAnsiChar(ToVarUInt32(Value,@tmp))-tmp); + tftVarInt32: + SetString(Result,tmp,PAnsiChar(ToVarInt32(Value,@tmp))-tmp); + tftVarUInt64: + SetString(Result,tmp,PAnsiChar(ToVarUInt64(Value,@tmp))-tmp); + tftVarInt64: + SetString(Result,tmp,PAnsiChar(ToVarInt64(Value,@tmp))-tmp); + else + result := ''; + end; +end; + +function TSynTableFieldProperties.SBF(const Value: Integer): TSBFString; +var tmp: array[0..15] of AnsiChar; +begin + case FieldType of + tftUInt8, tftUInt16, tftUInt24, tftInt32: + SetString(Result,PAnsiChar(@Value),FieldSize); + tftInt64: begin // special version for handling negative values + PInt64(@tmp)^ := Value; + SetString(Result,tmp,SizeOf(Int64)); + end; + tftVarUInt32: + if Value<0 then // expect an unsigned integer + result := '' else + SetString(Result,tmp,PAnsiChar(ToVarUInt32(Value,@tmp))-tmp); + tftVarInt32: + SetString(Result,tmp,PAnsiChar(ToVarInt32(Value,@tmp))-tmp); + tftVarUInt64: + if cardinal(Value)>cardinal(maxInt) then + result := '' else // expect a 32 bit integer + SetString(Result,tmp,PAnsiChar(ToVarUInt64(Value,@tmp))-tmp); + tftVarInt64: + SetString(Result,tmp,PAnsiChar(ToVarInt64(Value,@tmp))-tmp); + else + result := ''; + end; +end; + +const + SBF_BOOL: array[boolean] of TSBFString = + (#0,#1); + +{$ifndef NOVARIANTS} +function TSynTableFieldProperties.SBF(const Value: Variant): TSBFString; +var V64: Int64; + VC: Currency absolute V64; + VD: Double absolute V64; +begin // VarIsOrdinal/VarIsFloat/VarIsStr are buggy -> use field type + case FieldType of + tftBoolean: + result := SBF_BOOL[boolean(Value)]; + tftUInt8, tftUInt16, tftUInt24, tftInt32, tftInt64, + tftVarUInt32, tftVarInt32, tftVarUInt64, tftVarInt64: begin + if not VariantToInt64(Value,V64) then + V64 := 0; + result := SBF(V64); + end; + tftCurrency: begin + VC := Value; + SetString(result,PAnsiChar(@VC),SizeOf(VC)); + end; + tftDouble: begin + VD := Value; + SetString(result,PAnsiChar(@VD),SizeOf(VD)); + end; + tftWinAnsi: + ToSBFStr(WinAnsiConvert.UTF8ToAnsi(VariantToUTF8(Value)),result); + tftUTF8: + ToSBFStr(VariantToUTF8(Value),result); + else + result := ''; + end; + if result='' then + result := SBFDefault; +end; +{$endif} + +function TSynTableFieldProperties.SBF(const Value: Boolean): TSBFString; +begin + if FieldType<>tftBoolean then + result := '' else + result := SBF_BOOL[Value]; +end; + +function TSynTableFieldProperties.SBFCurr(const Value: Currency): TSBFString; +begin + if FieldType<>tftCurrency then + result := '' else + SetString(Result,PAnsiChar(@Value),SizeOf(Value)); +end; + +procedure ToSBFStr(const Value: RawByteString; out Result: TSBFString); +var tmp: array[0..15] of AnsiChar; + Len, Head: integer; +begin + if PtrUInt(Value)=0 then + Result := #0 else begin + Len := {$ifdef FPC}length(Value){$else}PInteger(PtrUInt(Value)-SizeOf(integer))^{$endif}; + Head := PAnsiChar(ToVarUInt32(Len,@tmp))-tmp; + SetLength(Result,Len+Head); + {$ifdef FPC}Move{$else}MoveFast{$endif}(tmp,PByteArray(Result)[0],Head); + {$ifdef FPC}Move{$else}MoveFast{$endif}(pointer(Value)^,PByteArray(Result)[Head],Len); + end; +end; + +function TSynTableFieldProperties.SBF(const Value: RawUTF8): TSBFString; +begin + case FieldType of + tftUTF8: + ToSBFStr(Value,Result); + tftWinAnsi: + ToSBFStr(Utf8ToWinAnsi(Value),Result); + else + result := ''; + end; +end; + +function TSynTableFieldProperties.SBF(Value: pointer; ValueLen: integer): TSBFString; +var tmp: array[0..15] of AnsiChar; + Head: integer; +begin + if FieldType<>tftBlobInternal then + result := '' else + if (Value=nil) or (ValueLen=0) then + result := #0 else begin // inlined ToSBFStr() code + Head := PAnsiChar(ToVarUInt32(ValueLen,@tmp))-tmp; + SetString(Result,nil,ValueLen+Head); + {$ifdef FPC}Move{$else}MoveFast{$endif}(tmp,PByteArray(Result)[0],Head); + {$ifdef FPC}Move{$else}MoveFast{$endif}(Value^,PByteArray(Result)[Head],ValueLen); + end; +end; + +function TSynTableFieldProperties.SBFFloat(const Value: Double): TSBFString; +begin + if FieldType<>tftDouble then + result := '' else + SetString(Result,PAnsiChar(@Value),SizeOf(Value)); +end; + +function TSynTableFieldProperties.SBFFromRawUTF8(const aValue: RawUTF8): TSBFString; +var Curr: Currency; +begin + case FieldType of + tftBoolean: + if (SynCommons.GetInteger(pointer(aValue))<>0) or IdemPropNameU(aValue,'true') then + result := #1 else + result := #0; // store false by default + tftUInt8, tftUInt16, tftUInt24, tftInt32, tftVarInt32: + result := SBF(SynCommons.GetInteger(pointer(aValue))); + tftVarUInt32, tftInt64, tftVarUInt64, tftVarInt64: + result := SBF(SynCommons.GetInt64(pointer(aValue))); + tftCurrency: begin + PInt64(@Curr)^ := StrToCurr64(pointer(aValue)); + result := SBFCurr(Curr); + end; + tftDouble: + result := SBFFloat(GetExtended(pointer(aValue))); + // text storage - WinAnsi could use less space than UTF-8 + tftUTF8, tftWinAnsi: + result := SBF(aValue); + else + result := ''; // tftBlob* fields e.g. must be handled directly + end; +end; + +function TSynTableFieldProperties.GetInteger(RecordBuffer: pointer): Integer; +begin + if (self=nil) or (RecordBuffer=nil) or (Owner=nil) then + result := 0 else begin + RecordBuffer := Owner.GetData(RecordBuffer,self); + case FieldType of + tftBoolean, tftUInt8: + result := PByte(RecordBuffer)^; + tftUInt16: + result := PWord(RecordBuffer)^; + tftUInt24: + // PInteger()^ and $ffffff -> possible GPF on Memory Mapped file + result := PWord(RecordBuffer)^+integer(PByteArray(RecordBuffer)^[2])shl 16; + tftInt32: + result := PInteger(RecordBuffer)^; + tftInt64: + result := PInt64(RecordBuffer)^; + // some variable-size field value + tftVarUInt32: + result := FromVarUInt32(PByte(RecordBuffer)); + tftVarInt32: + result := FromVarInt32(PByte(RecordBuffer)); + tftVarUInt64: + result := FromVarUInt64(PByte(RecordBuffer)); + tftVarInt64: + result := FromVarInt64(PByte(RecordBuffer)); + else + result := 0; + end; + end; +end; + +function TSynTableFieldProperties.GetInt64(RecordBuffer: pointer): Int64; +var PB: PByte; +begin + if (self=nil) or (RecordBuffer=nil) or (Owner=nil) then + result := 0 else begin + PB := Owner.GetData(RecordBuffer,self); + case FieldType of + tftInt64: + result := PInt64(PB)^; + tftVarUInt64: + result := FromVarUInt64(PB); + tftVarInt64: + result := FromVarInt64(PB); + else + result := GetInteger(RecordBuffer); + end; + end; +end; + +function TSynTableFieldProperties.GetBoolean(RecordBuffer: pointer): Boolean; +begin + result := boolean(GetInteger(RecordBuffer)); +end; + +function TSynTableFieldProperties.GetCurrency(RecordBuffer: pointer): Currency; +begin + if (self=nil) or (RecordBuffer=nil) or (Owner=nil) then + result := 0 else + case FieldType of + tftCurrency: + result := PCurrency(Owner.GetData(RecordBuffer,self))^; + else + result := GetInt64(RecordBuffer); + end; +end; + +function TSynTableFieldProperties.GetDouble(RecordBuffer: pointer): Double; +begin + if (self=nil) or (RecordBuffer=nil) or (Owner=nil) then + result := 0 else + case FieldType of + tftDouble: + result := PDouble(Owner.GetData(RecordBuffer,self))^; + else + result := GetInt64(RecordBuffer); + end; +end; + +function TSynTableFieldProperties.GetRawUTF8(RecordBuffer: pointer): RawUTF8; +begin + if (self=nil) or (RecordBuffer=nil) or (Owner=nil) then + result := '' else begin + RecordBuffer := Owner.GetData(RecordBuffer,self); + if RecordBuffer<>nil then + result := GetValue(RecordBuffer) else // will do conversion to text + result := ''; + end; +end; + +function TSynTableFieldProperties.AddFilterOrValidate(aFilter: TSynFilterOrValidate): TSynFilterOrValidate; +procedure Add(var List: TObjectList); +begin + if List=nil then + List := TObjectList.Create; + List.Add(result); +end; +begin + result := aFilter; + if (self=nil) or (result=nil) then + result := nil else + if aFilter.InheritsFrom(TSynFilter) then + Add(Filters) else + if aFilter.InheritsFrom(TSynValidate) then + Add(Validates) else + result := nil; +end; + +function TSynTableFieldProperties.Validate(RecordBuffer: pointer; + RecordIndex: integer): string; +var i: integer; + Value: RawUTF8; + aValidate: TSynValidate; + aValidateTable: TSynValidateTable absolute aValidate; +begin + result := ''; + if (self=nil) or (Validates=nil) then + exit; + Value := GetRawUTF8(RecordBuffer); // TSynTableValidate needs RawUTF8 text + for i := 0 to Validates.Count-1 do begin + aValidate := Validates.List[i]; + if aValidate.InheritsFrom(TSynValidateTable) then begin + aValidateTable.ProcessField := self; + aValidateTable.ProcessRecordIndex := RecordIndex; + end; + if not aValidate.Process(FieldNumber,Value,result) then begin + if result='' then + // no custom message -> show a default message + result := format(sValidationFailed,[ + GetCaptionFromClass(aValidate.ClassType)]); + break; + end; + end; +end; + +{$ifdef SORTCOMPAREMETHOD} +function TSynTableFieldProperties.SortCompare(P1, P2: PUTF8Char): PtrInt; +var i, L: integer; +label minus,plus,zer; +begin + if P1<>P2 then + if P1<>nil then + if P2<>nil then + case FieldType of + tftBoolean, tftUInt8: + result := PByte(P1)^-PByte(P2)^; + tftUInt16: + result := PWord(P1)^-PWord(P2)^; + tftUInt24: + result := PtrInt(PWord(P1)^)+PtrInt(P1[2])shl 16 + -PtrInt(PWord(P2)^)-PtrInt(P2[2]) shl 16; + tftInt32: + result := PInteger(P1)^-PInteger(P2)^; + tftDouble: begin + PDouble(@SortCompareTmp)^ := PDouble(P1)^-PDouble(P2)^; + if PDouble(@SortCompareTmp)^<0 then + goto minus else + if PDouble(@SortCompareTmp)^>0 then + goto plus else + goto zer; + end; + tftVarUInt32: + with SortCompareTmp do begin + PB1 := Pointer(P1); + PB2 := Pointer(P2); + result := FromVarUInt32(PB1)-FromVarUInt32(PB2); + end; + tftVarInt32: + with SortCompareTmp do begin + PB1 := Pointer(P1); + PB2 := Pointer(P2); + result := FromVarInt32(PB1)-FromVarInt32(PB2); + end; + {$ifdef CPUX86} // circumvent comparison slowness (and QWord bug) + tftInt64, tftCurrency: + result := SortInt64(PInt64(P1)^,PInt64(P2)^); + tftVarUInt64: + with SortCompareTmp do begin + PB1 := Pointer(P1); + PB2 := Pointer(P2); + result := SortQWord(FromVarUInt64(PB1),FromVarUInt64(PB2)); + end; + tftVarInt64: + with SortCompareTmp do begin + PB1 := Pointer(P1); + PB2 := Pointer(P2); + result := SortInt64(FromVarInt64(PB1),FromVarInt64(PB2)); + end; + {$else} + {$ifdef CPU64} // PtrInt = Int64 + tftInt64, tftCurrency: + result := PInt64(P1)^-PInt64(P2)^; + tftVarUInt64: + with SortCompareTmp do begin + PB1 := Pointer(P1); + PB2 := Pointer(P2); + result := FromVarUInt64(PB1)-FromVarUInt64(PB2); + end; + tftVarInt64: + with SortCompareTmp do begin + PB1 := Pointer(P1); + PB2 := Pointer(P2); + result := FromVarInt64(PB1)-FromVarInt64(PB2); + end; + {$else} + tftInt64, tftCurrency: begin + PInt64(@SortCompareTmp)^ := PInt64(P1)^-PInt64(P2)^; + if PInt64(@SortCompareTmp)^<0 then + goto minus else + if PInt64(@SortCompareTmp)^>0 then + goto plus else + goto zer; + end; + tftVarUInt64: + with SortCompareTmp do begin + PB1 := Pointer(P1); + PB2 := Pointer(P2); + PInt64(@SortCompareTmp)^ := FromVarUInt64(PB1)-FromVarUInt64(PB2); + if PInt64(@SortCompareTmp)^<0 then + goto minus else + if PInt64(@SortCompareTmp)^>0 then + goto plus else + goto zer; + end; + tftVarInt64: + with SortCompareTmp do begin + PB1 := Pointer(P1); + PB2 := Pointer(P2); + PInt64(@SortCompareTmp)^ := FromVarInt64(PB1)-FromVarInt64(PB2); + if PInt64(@SortCompareTmp)^<0 then + goto minus else + if PInt64(@SortCompareTmp)^>0 then + goto plus else + goto zer; + end; + {$endif} + {$endif} + tftWinAnsi, tftUTF8, tftBlobInternal: + begin + with SortCompareTmp do begin + if PtrInt(P1^)<=$7F then begin + L1 := PtrInt(P1^); + inc(P1); + end else begin + PB1 := pointer(P1); + L1 := FromVarUInt32High(PB1); + P1 := pointer(PB1); + end; + if PtrInt(P2^)<=$7F then begin + L2 := PtrInt(P2^); + inc(P2); + end else begin + PB2 := pointer(P2); + L2 := FromVarUInt32High(PB2); + P2 := pointer(PB2); + end; + end; + with SortCompareTmp do begin + L := L1; + if L2>L then + L := L2; + end; + if tfoCaseInsensitive in Options then begin + i := 0; + while i0 then + exit else + inc(i); + end; + end else begin + i := 0; + while i0 then + exit else + inc(i); + end; + end; + with SortCompareTmp do + result := L1-L2; + end; + else + goto zer; + end else +plus: result := 1 else // P2=nil +minus:result := -1 else // P1=nil +zer:result := 0; // P1=P2 +end; +{$endif} + +function CompareOperator(FieldType: TSynTableFieldType; SBF, SBFEnd: PUTF8Char; + Value: Int64; Oper: TCompareOperator): boolean; +var V: Int64; + PB: PByte absolute SBF; +begin + result := true; + if PB<>nil then + repeat + case FieldType of + tftBoolean, tftUInt8: + V := PB^; + tftUInt16: + V := PWord(PB)^; + tftUInt24: + // PInteger()^ and $ffffff -> possible GPF on Memory Mapped file + V := PWord(PB)^+integer(PByteArray(PB)^[2])shl 16; + tftInt32: + V := PInteger(PB)^; + tftInt64: + V := PInt64(PB)^; + // some variable-size field value + tftVarUInt32: + V := FromVarUInt32(PB); + tftVarInt32: + V := FromVarInt32(PB); + tftVarUInt64: + V := FromVarUInt64(PB); + tftVarInt64: + V := FromVarInt64(PB); + else V := 0; // makes compiler happy + end; + case Oper of + soEqualTo: if V=Value then exit; + soNotEqualTo: if V<>Value then exit; + soLessThan: if VValue then exit; + soGreaterThanOrEqualTo: if V>=Value then exit; + else break; + end; + // not found: go to next value + if SBFEnd=nil then + break; // only one value to be checked + if FIELD_FIXEDSIZE[FieldType]>0 then + inc(SBF,FIELD_FIXEDSIZE[FieldType]); // FromVar*() already updated PB/SBF + until SBF>=SBFEnd; + result := false; // not found +end; + +function CompareOperator(SBF, SBFEnd: PUTF8Char; + Value: double; Oper: TCompareOperator): boolean; +begin + result := true; + if SBF<>nil then + repeat + case Oper of + soEqualTo: if PDouble(SBF)^=Value then exit; + soNotEqualTo: if PDouble(SBF)^<>Value then exit; + soLessThan: if PDouble(SBF)^Value then exit; + soGreaterThanOrEqualTo: if PDouble(SBF)^>=Value then exit; + else break; + end; + // not found: go to next value + if SBFEnd=nil then + break; // only one value to be checked + inc(SBF,SizeOf(Value)); + until SBF>=SBFEnd; + result := false; // not found +end; + +function CompareOperator(FieldType: TSynTableFieldType; SBF, SBFEnd: PUTF8Char; + Value: PUTF8Char; ValueLen: integer; Oper: TCompareOperator; + CaseSensitive: boolean): boolean; overload; +var L, Cmp: PtrInt; + PB: PByte; + tmp: array[byte] of AnsiChar; +begin + result := true; + if SBF<>nil then + repeat + // get length of text in the SBF encoded buffer + if integer(SBF^)<=$7f then begin + L := integer(SBF^); + inc(SBF); + end else begin + PB := Pointer(SBF); + L := FromVarUInt32(PB); + SBF := pointer(PB); + end; + // perform comparison: returns nil on match + case Oper of + soEqualTo..soGreaterThanOrEqualTo: begin + Cmp := L-ValueLen; + if Cmp<0 then + L := ValueLen; + if CaseSensitive then + Cmp := StrCompL(SBF,Value,L,Cmp) else + Cmp := StrCompIL(SBF,Value,L,Cmp); + case Oper of + soEqualTo: if Cmp=0 then exit; + soNotEqualTo: if Cmp<>0 then exit; + soLessThan: if Cmp<0 then exit; + soLessThanOrEqualTo: if Cmp<=0 then exit; + soGreaterThan: if Cmp>0 then exit; + soGreaterThanOrEqualTo: if Cmp>=0 then exit; + end; + end; + soBeginWith: + if ValueLen>=L then + if CaseSensitive then begin + if StrCompL(SBF,Value,ValueLen,0)=0 then + exit; + end else + if StrCompIL(SBF,Value,ValueLen,0)=0 then + exit; + soContains: begin + dec(L,ValueLen); + while L>=0 do begin + while (L>=0) and not(byte(SBF^) in IsWord) do begin + dec(L); + inc(SBF); + end; // begin of next word reached + if L<0 then + Break; // not enough chars to contain the Value + if CaseSensitive then begin + if StrCompL(SBF,Value,ValueLen,0)=0 then + exit; + end else + if StrCompIL(SBF,Value,ValueLen,0)=0 then + exit; + while (L>=0) and (byte(SBF^) in IsWord) do begin + dec(L); + inc(SBF); + end; // end of word reached + end; + if SBFEnd=nil then + break; // only one value to be checked + inc(SBF,ValueLen); // custom inc(SBF,L); + if SBFhigh(tmp) then + Cmp := high(tmp) else + Cmp := L; + tmp[Cmp] := #0; // TSynSoundEx expect the buffer to be #0 terminated + {$ifdef FPC}Move{$else}MoveFast{$endif}(SBF^,tmp,Cmp); + case FieldType of + tftWinAnsi: + if PSynSoundEx(Value)^.Ansi(tmp) then + exit; + tftUTF8: + if PSynSoundEx(Value)^.UTF8(tmp) then + exit; + else break; + end; + end; + else break; + end; + // no match -> go to the end of the SBF buffer + if SBFEnd=nil then + exit; // only one value to be checked + inc(SBF,L); + if SBF>=SBFEnd then + break; + until false; +end; + + +{ TSynValidateTableUniqueField } + +function TSynValidateTableUniqueField.Process(aFieldIndex: integer; + const Value: RawUTF8; var ErrorMsg: string): boolean; +var S: TSBFString; +begin + result := false; + if (self=nil) or (Value='') or (ProcessField=nil) then + exit; // void field can't be unique + if not (tfoIndex in ProcessField.Options) then + exit; // index should be always created by TSynTable.AfterFieldModif + S := ProcessField.SBFFromRawUTF8(Value); + if S='' then + exit; // void field can't be unique + if ProcessField.OrderedIndexFindAdd(Pointer(S))>=0 then + // there is some place to insert the Value -> not existing yet -> OK + result := true else begin + // RecordIndex=-1 in case of adding, or the physical index of the updated record + if (ProcessRecordIndex>=0) and + (ProcessField.OrderedIndex[ProcessField.OrderedIndexFind(Pointer(S))]= + ProcessRecordIndex) then + // allow update of the record + result := true else + // found a dupplicated value + ErrorMsg := sValidationFieldDuplicate; + end; +end; + + +{ TSynTableStatement } + +const + NULL_UPP = ord('N')+ord('U')shl 8+ord('L')shl 16+ord('L')shl 24; + +constructor TSynTableStatement.Create(const SQL: RawUTF8; + GetFieldIndex: TSynTableFieldIndex; SimpleFieldsBits: TSQLFieldBits; + FieldProp: TSynTableFieldProperties); +var Prop, whereBefore: RawUTF8; + P, B: PUTF8Char; + ndx,err,len,selectCount,whereCount: integer; + whereWithOR,whereNotClause: boolean; + +function GetPropIndex: integer; +begin + if not GetNextFieldProp(P,Prop) then + result := -1 else + if IsRowID(pointer(Prop)) then + result := 0 else begin // 0 = ID field + result := GetFieldIndex(Prop); + if result>=0 then // -1 = no valid field name + inc(result); // otherwise: PropertyIndex+1 + end; +end; +function SetFields: boolean; +var select: TSynTableStatementSelect; +begin + result := false; + FillcharFast(select,SizeOf(select),0); + select.Field := GetPropIndex; // 0 = ID, otherwise PropertyIndex+1 + if select.Field<0 then begin + if P^<>'(' then // Field not found -> try function(field) + exit; + P := GotoNextNotSpace(P+1); + select.FunctionName := Prop; + inc(fSelectFunctionCount); + if IdemPropNameU(Prop,'COUNT') and (P^='*') then begin + select.Field := 0; // count(*) -> count(ID) + select.FunctionKnown := funcCountStar; + P := GotoNextNotSpace(P+1); + end else begin + if IdemPropNameU(Prop,'DISTINCT') then + select.FunctionKnown := funcDistinct else + if IdemPropNameU(Prop,'MAX') then + select.FunctionKnown := funcMax; + select.Field := GetPropIndex; + if select.Field<0 then + exit; + end; + if P^<>')' then + exit; + P := GotoNextNotSpace(P+1); + end; + if P^ in ['+','-'] then begin + select.ToBeAdded := GetNextItemInteger(P,' '); + if select.ToBeAdded=0 then + exit; + P := GotoNextNotSpace(P); + end; + if IdemPChar(P,'AS ') then begin + inc(P,3); + if not GetNextFieldProp(P,select.Alias) then + exit; + end; + SetLength(fSelect,selectCount+1); + fSelect[selectCount] := select; + inc(selectCount); + result := true; +end; +function GetWhereValue(var Where: TSynTableStatementWhere): boolean; +var B: PUTF8Char; +begin + result := false; + P := GotoNextNotSpace(P); + Where.ValueSQL := P; + if PWord(P)^=ord(':')+ord('(') shl 8 then + inc(P,2); // ignore :(...): parameter (no prepared statements here) + if P^ in ['''','"'] then begin + // SQL String statement + P := UnQuoteSQLStringVar(P,Where.Value); + if P=nil then + exit; // end of string before end quote -> incorrect + {$ifndef NOVARIANTS} + RawUTF8ToVariant(Where.Value,Where.ValueVariant); + {$endif} + if FieldProp<>nil then + // create a SBF formatted version of the WHERE value + Where.ValueSBF := FieldProp.SBFFromRawUTF8(Where.Value); + end else + if (PInteger(P)^ and $DFDFDFDF=NULL_UPP) and (P[4] in [#0..' ',';']) then begin + // NULL statement + Where.Value := NULL_STR_VAR; // not void + {$ifndef NOVARIANTS} + SetVariantNull(Where.ValueVariant); + {$endif} + end else begin + // numeric statement or 'true' or 'false' (OK for NormalizeValue) + B := P; + repeat + inc(P); + until P^ in [#0..' ',';',')',',']; + SetString(Where.Value,B,P-B); + {$ifndef NOVARIANTS} + Where.ValueVariant := VariantLoadJSON(Where.Value); + {$endif} + Where.ValueInteger := GetInteger(pointer(Where.Value),err); + if FieldProp<>nil then + if Where.Value<>'?' then + if (FieldProp.FieldType in FIELD_INTEGER) and (err<>0) then + // we expect a true INTEGER value here + Where.Value := '' else + // create a SBF formatted version of the WHERE value + Where.ValueSBF := FieldProp.SBFFromRawUTF8(Where.Value); + end; + if PWord(P)^=ord(')')+ord(':')shl 8 then + inc(P,2); // ignore :(...): parameter + Where.ValueSQLLen := P-Where.ValueSQL; + P := GotoNextNotSpace(P); + if (P^=')') and (Where.FunctionName='') then begin + B := P; + repeat + inc(P); + until not (P^ in [#1..' ',')']); + while P[-1]=' ' do dec(P); // trim right space + SetString(Where.ParenthesisAfter,B,P-B); + P := GotoNextNotSpace(P); + end; + result := true; +end; +{$ifndef NOVARIANTS} +function GetWhereValues(var Where: TSynTableStatementWhere): boolean; +var v: TSynTableStatementWhereDynArray; + n, w: integer; + tmp: RawUTF8; +begin + result := false; + if Where.ValueSQLLen<=2 then + exit; + SetString(tmp,PAnsiChar(Where.ValueSQL)+1,Where.ValueSQLLen-2); + P := pointer(tmp); // parse again the IN (...,...,... ) expression + n := 0; + try + repeat + if n=length(v) then + SetLength(v,NextGrow(n)); + if not GetWhereValue(v[n]) then + exit; + inc(n); + if P^=#0 then + break; + if P^<>',' then + exit; + inc(P); + until false; + finally + P := Where.ValueSQL+Where.ValueSQLLen; // continue parsing as usual + end; + with TDocVariantData(Where.ValueVariant) do begin + InitFast(n,dvArray); + for w := 0 to n-1 do + AddItem(v[w].ValueVariant); + Where.Value := ToJSON; + end; + result := true; +end; +{$endif} +function GetWhereExpression(FieldIndex: integer; var Where: TSynTableStatementWhere): boolean; +begin + result := false; + Where.ParenthesisBefore := whereBefore; + Where.JoinedOR := whereWithOR; + Where.NotClause := whereNotClause; + Where.Field := FieldIndex; // 0 = ID, otherwise PropertyIndex+1 + case P^ of + '=': Where.Operator := opEqualTo; + '>': if P[1]='=' then begin + inc(P); + Where.Operator := opGreaterThanOrEqualTo; + end else + Where.Operator := opGreaterThan; + '<': case P[1] of + '=': begin + inc(P); + Where.Operator := opLessThanOrEqualTo; + end; + '>': begin + inc(P); + Where.Operator := opNotEqualTo; + end; + else + Where.Operator := opLessThan; + end; + 'i','I': + case P[1] of + 's','S': begin + P := GotoNextNotSpace(P+2); + if IdemPChar(P,'NULL') then begin + Where.Value := NULL_STR_VAR; + Where.Operator := opIsNull; + Where.ValueSQL := P; + Where.ValueSQLLen := 4; + {$ifndef NOVARIANTS} + TVarData(Where.ValueVariant).VType := varNull; + {$endif} + inc(P,4); + result := true; + end else + if IdemPChar(P,'NOT NULL') then begin + Where.Value := 'not null'; + Where.Operator := opIsNotNull; + Where.ValueSQL := P; + Where.ValueSQLLen := 8; + {$ifndef NOVARIANTS} + TVarData(Where.ValueVariant).VType := varNull; + {$endif} + inc(P,8); + result := true; // leave ValueVariant=unassigned + end; + exit; + end; + {$ifndef NOVARIANTS} + 'n','N': begin + Where.Operator := opIn; + P := GotoNextNotSpace(P+2); + if P^<>'(' then + exit; // incorrect SQL statement + B := P; // get the IN() clause as JSON + inc(P); + while (P^<>')') or (P[1]=':') do // handle :(...): within the clause + if P^=#0 then + exit else + inc(P); + inc(P); + SetString(Where.Value,PAnsiChar(B),P-B); + Where.ValueSQL := B; + Where.ValueSQLLen := P-B; + result := GetWhereValues(Where); + exit; + end; + {$endif} + end; // 'i','I': + 'l','L': + if IdemPChar(P+1,'IKE') then begin + inc(P,3); + Where.Operator := opLike; + end else + exit; + else exit; // unknown operator + end; + // we got 'WHERE FieldName operator ' -> handle value + inc(P); + result := GetWhereValue(Where); +end; + +label lim,lim2; +begin + P := pointer(SQL); + if (P=nil) or (self=nil) then + exit; // avoid GPF + P := GotoNextNotSpace(P); // trim left + if not IdemPChar(P,'SELECT ') then + exit else // handle only SELECT statement + inc(P,7); + // 1. get SELECT clause: set bits in Fields from CSV field IDs in SQL + selectCount := 0; + P := GotoNextNotSpace(P); // trim left + if P^=#0 then + exit; // no SQL statement + if P^='*' then begin // all simple (not TSQLRawBlob/TSQLRecordMany) fields + inc(P); + len := GetBitsCount(SimpleFieldsBits,MAX_SQLFIELDS)+1; + SetLength(fSelect,len); + selectCount := 1; // Select[0].Field := 0 -> ID + for ndx := 0 to MAX_SQLFIELDS-1 do + if ndx in SimpleFieldsBits then begin + fSelect[selectCount].Field := ndx+1; + inc(selectCount); + if selectCount=len then + break; + end; + GetNextFieldProp(P,Prop); + end else + if not SetFields then + exit else // we need at least one field name + if P^<>',' then + GetNextFieldProp(P,Prop) else + repeat + while P^ in [',',#1..' '] do inc(P); // trim left + until not SetFields; // add other CSV field names + // 2. get FROM clause + if not IdemPropNameU(Prop,'FROM') then exit; // incorrect SQL statement + GetNextFieldProp(P,Prop); + fTableName := Prop; + // 3. get WHERE clause + whereCount := 0; + whereWithOR := false; + whereNotClause := false; + whereBefore := ''; + GetNextFieldProp(P,Prop); + if IdemPropNameU(Prop,'WHERE') then begin + repeat + B := P; + if P^='(' then begin + fWhereHasParenthesis := true; + repeat + inc(P); + until not (P^ in [#1..' ','(']); + while P[-1]=' ' do dec(P); // trim right space + SetString(whereBefore,B,P-B); + B := P; + end; + ndx := GetPropIndex; + if ndx<0 then begin + if IdemPropNameU(Prop,'NOT') then begin + whereNotClause := true; + continue; + end; + if P^='(' then begin + inc(P); + SetLength(fWhere,whereCount+1); + with fWhere[whereCount] do begin + ParenthesisBefore := whereBefore; + JoinedOR := whereWithOR; + NotClause := whereNotClause; + FunctionName := UpperCase(Prop); + // Byte/Word/Integer/Cardinal/Int64/CurrencyDynArrayContains(BlobField,I64) + len := length(Prop); + if (len>16) and + IdemPropName('DynArrayContains',PUTF8Char(@PByteArray(Prop)[len-16]),16) then + Operator := opContains else + Operator := opFunction; + B := P; + Field := GetPropIndex; + if Field<0 then + P := B else + if P^<>',' then + break else + P := GotoNextNotSpace(P+1); + if (P^=')') or + (GetWhereValue(fWhere[whereCount]) and (P^=')')) then begin + inc(P); + break; + end; + end; + end; + P := B; + break; + end; + SetLength(fWhere,whereCount+1); + if not GetWhereExpression(ndx,fWhere[whereCount]) then + exit; // invalid SQL statement + inc(whereCount); + GetNextFieldProp(P,Prop); + if IdemPropNameU(Prop,'OR') then + whereWithOR := true else + if IdemPropNameU(Prop,'AND') then + whereWithOR := false else + goto lim2; + whereNotClause := false; + whereBefore := ''; + until false; + // 4. get optional LIMIT/OFFSET/ORDER clause +lim:P := GotoNextNotSpace(P); + while (P<>nil) and not(P^ in [#0,';']) do begin + GetNextFieldProp(P,Prop); +lim2: if IdemPropNameU(Prop,'LIMIT') then + fLimit := GetNextItemCardinal(P,' ') else + if IdemPropNameU(Prop,'OFFSET') then + fOffset := GetNextItemCardinal(P,' ') else + if IdemPropNameU(Prop,'ORDER') then begin + GetNextFieldProp(P,Prop); + if IdemPropNameU(Prop,'BY') then begin + repeat + ndx := GetPropIndex; // 0 = ID, otherwise PropertyIndex+1 + if ndx<0 then + exit; // incorrect SQL statement + AddFieldIndex(fOrderByField,ndx); + if P^<>',' then begin // check ORDER BY ... ASC/DESC + B := P; + if GetNextFieldProp(P,Prop) then + if IdemPropNameU(Prop,'DESC') then + fOrderByDesc := true else + if not IdemPropNameU(Prop,'ASC') then + P := B; + break; + end; + P := GotoNextNotSpace(P+1); + until P^ in [#0,';']; + end else + exit; // incorrect SQL statement + end else + if IdemPropNameU(Prop,'GROUP') then begin + GetNextFieldProp(P,Prop); + if IdemPropNameU(Prop,'BY') then begin + repeat + ndx := GetPropIndex; // 0 = ID, otherwise PropertyIndex+1 + if ndx<0 then + exit; // incorrect SQL statement + AddFieldIndex(fGroupByField,ndx); + if P^<>',' then + break; + P := GotoNextNotSpace(P+1); + until P^ in [#0,';']; + end else + exit; // incorrect SQL statement + end else + if Prop<>'' then + exit else // incorrect SQL statement + break; // reached the end of the statement + end; + end else + if Prop<>'' then + goto lim2; // handle LIMIT OFFSET ORDER + fSQLStatement := SQL; // make a private copy e.g. for Where[].ValueSQL +end; + +procedure TSynTableStatement.SelectFieldBits(var Fields: TSQLFieldBits; var withID: boolean); +var i: integer; +begin + FillcharFast(Fields,SizeOf(Fields),0); + withID := false; + for i := 0 to Length(Select)-1 do + if Select[i].Field=0 then + withID := true else + include(Fields,Select[i].Field-1); +end; + + +{$ifndef DELPHI5OROLDER} + +{ TSynTableData } + +procedure TSynTableData.CheckVTableInitialized; +begin + if VTable=nil then + raise ETableDataException.Create('TSynTableData non initialized'); +end; + +{$ifndef NOVARIANTS} + +function TSynTableData.GetFieldValue(const FieldName: RawUTF8): Variant; +begin + GetFieldVariant(FieldName,result); +end; + +procedure TSynTableData.GetFieldVariant(const FieldName: RawUTF8; var result: Variant); +var aField: TSynTableFieldProperties; +begin + if IsRowID(Pointer(FieldName)) then + result := VID else begin + CheckVTableInitialized; + aField := VTable.FieldFromName[FieldName]; + if aField=nil then + raise ETableDataException.CreateUTF8('Unknown % property',[FieldName]) else + aField.GetVariant(VTable.GetData(pointer(VValue),aField),result); + end; +end; + +function TSynTableData.GetFieldValue(aField: TSynTableFieldProperties): Variant; +begin + CheckVTableInitialized; + aField.GetVariant(VTable.GetData(pointer(VValue),aField),result); +end; + +{$endif NOVARIANTS} + +procedure TSynTableData.FilterSBFValue; +begin + CheckVTableInitialized; + VTable.Filter(VValue); +end; + +function TSynTableData.GetFieldSBFValue(aField: TSynTableFieldProperties): TSBFString; +var FieldBuffer: PAnsiChar; +begin + CheckVTableInitialized; + FieldBuffer := VTable.GetData(pointer(VValue),aField); + SetString(Result,FieldBuffer,aField.GetLength(FieldBuffer)); +end; + +procedure TSynTableData.Init(aTable: TSynTable; aID: Integer); +begin + VTable := aTable; + VID := aID; + VValue := VTable.DefaultRecordData; + {$ifdef UNICODE}FillcharFast(Filler,SizeOf(Filler),0);{$endif} +end; + +procedure TSynTableData.Init(aTable: TSynTable; aID: Integer; + RecordBuffer: pointer; RecordBufferLen: integer); +begin + VTable := aTable; + if (RecordBufferLen=0) or (RecordBuffer=nil) then begin + VID := 0; + VValue := VTable.DefaultRecordData; + end else begin + VID := aID; + SetString(VValue,PAnsiChar(RecordBuffer),RecordBufferLen); + end; +end; + +{$ifndef NOVARIANTS} +procedure TSynTableData.SetFieldValue(const FieldName: RawUTF8; + const Value: Variant); +var F: TSynTableFieldProperties; +begin + CheckVTableInitialized; + if IsRowID(Pointer(FieldName)) then + VID := Value else begin + F := VTable.FieldFromName[FieldName]; + if F=nil then + raise ETableDataException.CreateUTF8('Unknown % property',[FieldName]) else + SetFieldValue(F,Value); + end; +end; + +procedure TSynTableData.SetFieldValue(aField: TSynTableFieldProperties; const Value: Variant); +begin + SetFieldSBFValue(aField,aField.SBF(Value)); +end; +{$endif} + +procedure TSynTableData.SetFieldSBFValue(aField: TSynTableFieldProperties; + const Value: TSBFString); +var NewValue: TSBFString; +begin + CheckVTableInitialized; + if (aField.FieldSize>0) and (VValue<>'') then begin + // fixed size content: fast in-place update + {$ifdef FPC}Move{$else}MoveFast{$endif}(pointer(Value)^,VValue[aField.Offset+1],aField.FieldSize) + // VValue[F.Offset+1] above will call UniqueString(VValue), even under FPC + end else begin + // variable-length update + VTable.UpdateFieldData(pointer(VValue),length(VValue), + aField.FieldNumber,NewValue,Value); + VValue := NewValue; + end; +end; + +function TSynTableData.ValidateSBFValue(RecordIndex: integer): string; +begin + CheckVTableInitialized; + Result := VTable.Validate(Pointer(VValue),RecordIndex); +end; + +{$endif DELPHI5OROLDER} + + +{ ************ filtering and validation classes and functions *************** } + +function IsValidIP4Address(P: PUTF8Char): boolean; +var ndot: PtrInt; + V: PtrUInt; +begin + result := false; + if (P=nil) or not (P^ in ['0'..'9']) then + exit; + V := 0; + ndot := 0; + repeat + case P^ of + #0: break; + '.': if (P[-1]='.') or (V>255) then + exit else begin + inc(ndot); + V := 0; + end; + '0'..'9': V := (V*10)+ord(P^)-48; + else exit; + end; + inc(P); + until false; + if (ndot=3) and (V<=255) and (P[-1]<>'.') then + result := true; +end; + +function IsValidEmail(P: PUTF8Char): boolean; +// Initial Author: Ernesto D'Spirito - UTF-8 version by AB +// http://www.howtodothings.com/computers/a1169-validating-email-addresses-in-delphi.html +const + // Valid characters in an "atom" + atom_chars: TSynAnsicharSet = [#33..#255] - + ['(', ')', '<', '>', '@', ',', ';', ':', '\', '/', '"', '.', '[', ']', #127]; + // Valid characters in a "quoted-string" + quoted_string_chars: TSynAnsicharSet = [#0..#255] - ['"', #13, '\']; + // Valid characters in a subdomain + letters_digits: TSynAnsicharSet = ['0'..'9', 'A'..'Z', 'a'..'z']; +type + States = (STATE_BEGIN, STATE_ATOM, STATE_QTEXT, STATE_QCHAR, + STATE_QUOTE, STATE_LOCAL_PERIOD, STATE_EXPECTING_SUBDOMAIN, + STATE_SUBDOMAIN, STATE_HYPHEN); +var + State: States; + subdomains: integer; + c: AnsiChar; + ch: PtrInt; +begin + State := STATE_BEGIN; + subdomains := 1; + if P<>nil then + repeat + ch := ord(P^); + if ch and $80=0 then + inc(P) else + ch := GetHighUTF8UCS4(P); + if (ch<=255) and (WinAnsiConvert.AnsiToWide[ch]<=255) then + // convert into WinAnsi char + c := AnsiChar(ch) else + // invalid char + c := #127; + case State of + STATE_BEGIN: + if c in atom_chars then + State := STATE_ATOM else + if c='"' then + State := STATE_QTEXT else + break; + STATE_ATOM: + if c='@' then + State := STATE_EXPECTING_SUBDOMAIN else + if c='.' then + State := STATE_LOCAL_PERIOD else + if not (c in atom_chars) then + break; + STATE_QTEXT: + if c='\' then + State := STATE_QCHAR else + if c='"' then + State := STATE_QUOTE else + if not (c in quoted_string_chars) then + break; + STATE_QCHAR: + State := STATE_QTEXT; + STATE_QUOTE: + if c='@' then + State := STATE_EXPECTING_SUBDOMAIN else + if c='.' then + State := STATE_LOCAL_PERIOD else + break; + STATE_LOCAL_PERIOD: + if c in atom_chars then + State := STATE_ATOM else + if c='"' then + State := STATE_QTEXT else + break; + STATE_EXPECTING_SUBDOMAIN: + if c in letters_digits then + State := STATE_SUBDOMAIN else + break; + STATE_SUBDOMAIN: + if c='.' then begin + inc(subdomains); + State := STATE_EXPECTING_SUBDOMAIN + end else + if c='-' then + State := STATE_HYPHEN else + if not (c in letters_digits) then + break; + STATE_HYPHEN: + if c in letters_digits then + State := STATE_SUBDOMAIN else + if c<>'-' then + break; + end; + if P^=#0 then begin + P := nil; + break; + end; + until false; + result := (State = STATE_SUBDOMAIN) and (subdomains >= 2); +end; + +// code below adapted from ZMatchPattern.pas - http://www.zeoslib.sourceforge.net + +procedure TMatch.MatchMain; +var RangeStart, RangeEnd: PtrInt; + c: AnsiChar; + flags: set of(Invert, MemberMatch); +begin + while ((State = sNONE) and (P <= PMax)) do begin + c := Upper[Pattern[P]]; + if T > TMax then begin + if (c = '*') and (P + 1 > PMax) then + State := sVALID else + State := sABORT; + exit; + end else + case c of + '?': ; + '*': + MatchAfterStar; + '[': begin + inc(P); + byte(flags) := 0; + if Pattern[P] in ['!','^'] then begin + include(flags, Invert); + inc(P); + end; + if (Pattern[P] = ']') then begin + State := sPATTERN; + exit; + end; + c := Upper[Text[T]]; + while Pattern[P] <> ']' do begin + RangeStart := P; + RangeEnd := P; + inc(P); + if P > PMax then begin + State := sPATTERN; + exit; + end; + if Pattern[P] = '-' then begin + inc(P); + RangeEnd := P; + if (P > PMax) or (Pattern[RangeEnd] = ']') then begin + State := sPATTERN; + exit; + end; + inc(P); + end; + if P > PMax then begin + State := sPATTERN; + exit; + end; + if RangeStart < RangeEnd then begin + if (c >= Upper[Pattern[RangeStart]]) and (c <= Upper[Pattern[RangeEnd]]) then begin + include(flags, MemberMatch); + break; + end; + end + else + if (c >= Upper[Pattern[RangeEnd]]) and (c <= Upper[Pattern[RangeStart]]) then begin + include(flags, MemberMatch); + break; + end; + end; + if ((Invert in flags) and (MemberMatch in flags)) or + not ((Invert in flags) or (MemberMatch in flags)) then begin + State := sRANGE; + exit; + end; + if MemberMatch in flags then + while (P <= PMax) and (Pattern[P] <> ']') do + inc(P); + if P > PMax then begin + State := sPATTERN; + exit; + end; + end; + else + if c <> Upper[Text[T]] then + State := sLITERAL; + end; + inc(P); + inc(T); + end; + if State = sNONE then + if T <= TMax then + State := sEND else + State := sVALID; +end; + +procedure TMatch.MatchAfterStar; +var retryT, retryP: PtrInt; +begin + if (TMax = 1) or (P = PMax) then begin + State := sVALID; + exit; + end else + if (PMax = 0) or (TMax = 0) then begin + State := sABORT; + exit; + end; + while ((T <= TMax) and (P < PMax)) and (Pattern[P] in ['?', '*']) do begin + if Pattern[P] = '?' then + inc(T); + inc(P); + end; + if T >= TMax then begin + State := sABORT; + exit; + end else + if P >= PMax then begin + State := sVALID; + exit; + end; + repeat + if (Upper[Pattern[P]] = Upper[Text[T]]) or (Pattern[P] = '[') then begin + retryT := T; + retryP := P; + MatchMain; + if State = sVALID then + break; + State := sNONE; // retry until end of Text, (check below) or State valid + T := retryT; + P := retryP; + end; + inc(T); + if (T > TMax) or (P > PMax) then begin + State := sABORT; + exit; + end; + until State <> sNONE; +end; + +function SearchAny(aMatch: PMatch; aText: PUTF8Char; aTextLen: PtrInt): boolean; +begin + aMatch.State := sNONE; + aMatch.P := 0; + aMatch.T := 0; + aMatch.Text := aText; + aMatch.TMax := aTextLen - 1; + aMatch.MatchMain; + result := aMatch.State = sVALID; +end; + +// faster alternative (without recursion) for only * ? (but not [...]) + +function SearchNoRange(aMatch: PMatch; aText: PUTF8Char; aTextLen: PtrInt): boolean; +{$ifdef CPUX86} +var + c: AnsiChar; + pat, txt: PtrInt; // use local registers +begin + aMatch.T := 0; // aMatch.P/T are used for retry positions after * + aMatch.Text := aText; + aMatch.TMax := aTextLen - 1; + pat := 0; + txt := 0; + repeat + if pat <= aMatch.PMax then begin + c := aMatch.Pattern[pat]; + case c of + '?': + if txt <= aMatch.TMax then begin + inc(pat); + inc(txt); + continue; + end; + '*': begin + aMatch.P := pat; + aMatch.T := txt + 1; + inc(pat); + continue; + end; + else if (txt <= aMatch.TMax) and (c = aMatch.Text[txt]) then begin + inc(pat); + inc(txt); + continue; + end; + end; + end + else if txt > aMatch.TMax then + break; + txt := aMatch.T; + if (txt > 0) and (txt <= aMatch.TMax + 1) then begin + inc(aMatch.T); + pat := aMatch.P+1; + continue; + end; + result := false; + exit; + until false; + result := true; +end; +{$else} // optimized for x86_64/ARM with more registers +var + c: AnsiChar; + pat, patend, txtend, txtretry, patretry: PUTF8Char; +label + fin; +begin + pat := pointer(aMatch.Pattern); + if pat = nil then + goto fin; + patend := pat + aMatch.PMax; + patretry := nil; + txtend := aText + aTextLen - 1; + txtretry := nil; + repeat + if pat <= patend then begin + c := pat^; + if c <> '*' then + if c <> '?' then begin + if (aText <= txtend) and (c = aText^) then begin + inc(pat); + inc(aText); + continue; + end; + end + else begin // '?' + if aText <= txtend then begin + inc(pat); + inc(aText); + continue; + end; + end + else begin // '*' + inc(pat); + txtretry := aText + 1; + patretry := pat; + continue; + end; + end + else if aText > txtend then + break; + if (PtrInt(txtretry)> 0) and (txtretry <= txtend + 1) then begin + aText := txtretry; + inc(txtretry); + pat := patretry; + continue; + end; +fin:result := false; + exit; + until false; + result := true; +end; +{$endif CPUX86} + +function SearchNoRangeU(aMatch: PMatch; aText: PUTF8Char; aTextLen: PtrInt): boolean; +var + c: AnsiChar; + pat, txt: PtrInt; +begin + aMatch.T := 0; + aMatch.Text := aText; + aMatch.TMax := aTextLen - 1; + pat := 0; + txt := 0; + repeat + if pat <= aMatch.PMax then begin + c := aMatch.Pattern[pat]; + case c of + '?': + if txt <= aMatch.TMax then begin + inc(pat); + inc(txt); + continue; + end; + '*': begin + aMatch.P := pat; + aMatch.T := txt + 1; + inc(pat); + continue; + end; + else if (txt <= aMatch.TMax) and + (aMatch.Upper[c] = aMatch.Upper[aMatch.Text[txt]]) then begin + inc(pat); + inc(txt); + continue; + end; + end; + end + else if txt > aMatch.TMax then + break; + txt := aMatch.T; + if (txt > 0) and (txt <= aMatch.TMax + 1) then begin + inc(aMatch.T); + pat := aMatch.P+1; + continue; + end; + result := false; + exit; + until false; + result := true; +end; + +function SimpleContainsU(t, tend, p: PUTF8Char; pmax: PtrInt; up: PNormTable): boolean; + {$ifdef HASINLINE}inline;{$endif} +// brute force case-insensitive search p[0..pmax] in t..tend-1 +var first: AnsiChar; + i: PtrInt; +label next; +begin + first := up[p^]; + repeat + if up[t^] <> first then begin +next: inc(t); + if t < tend then + continue else + break; + end; + for i := 1 to pmax do + if up[t[i]] <> up[p[i]] then + goto next; + result := true; + exit; + until false; + result := false; +end; + +{$ifdef CPU64} // naive but very efficient code generation on FPC x86-64 +function SimpleContains8(t, tend, p: PUTF8Char; pmax: PtrInt): boolean; inline; +label next; +var i, first: PtrInt; +begin + first := PPtrInt(p)^; + repeat + if PPtrInt(t)^ <> first then begin +next: inc(t); + if t < tend then + continue else + break; + end; + for i := 8 to pmax do + if t[i] <> p[i] then + goto next; + result := true; + exit; + until false; + result := false; +end; +{$endif CPU64} + +function SimpleContains4(t, tend, p: PUTF8Char; pmax: PtrInt): boolean; + {$ifdef HASINLINE}inline;{$endif} +label next; +var i: PtrInt; +{$ifdef CPUX86} // circumvent lack of registers for this CPU +begin + repeat + if PCardinal(t)^ <> PCardinal(p)^ then begin +{$else} + first: cardinal; +begin + first := PCardinal(p)^; + repeat + if PCardinal(t)^ <> first then begin +{$endif} +next: inc(t); + if t < tend then + continue else + break; + end; + for i := 4 to pmax do + if t[i] <> p[i] then + goto next; + result := true; + exit; + until false; + result := false; +end; + +function SimpleContains1(t, tend, p: PUTF8Char; pmax: PtrInt): boolean; + {$ifdef HASINLINE}inline;{$endif} +label next; +var i: PtrInt; +{$ifdef CPUX86} +begin + repeat + if t^ <> p^ then begin +{$else} + first: AnsiChar; +begin + first := p^; + repeat + if t^ <> first then begin +{$endif} +next: inc(t); + if t < tend then + continue else + break; + end; + for i := 1 to pmax do + if t[i] <> p[i] then + goto next; + result := true; + exit; + until false; + result := false; +end; + +function CompareMemU(P1, P2: PUTF8Char; len: PtrInt; U: PNormTable): Boolean; + {$ifdef FPC}inline;{$endif} +begin // here we know that len>0 + result := false; + repeat + dec(len); + if U[P1[len]] <> U[P2[len]] then + exit; + until len = 0; + result := true; +end; + +function SearchVoid(aMatch: PMatch; aText: PUTF8Char; aTextLen: PtrInt): boolean; +begin + result := aTextLen = 0; +end; + +function SearchNoPattern(aMatch: PMatch; aText: PUTF8Char; aTextLen: PtrInt): boolean; +begin + result := (aMatch.PMax + 1 = aTextlen) and CompareMem(aText, aMatch.Pattern, aTextLen); +end; + +function SearchNoPatternU(aMatch: PMatch; aText: PUTF8Char; aTextLen: PtrInt): boolean; +begin + result := (aMatch.PMax + 1 = aTextlen) and CompareMemU(aText, aMatch.Pattern, aTextLen, aMatch.Upper); +end; + +function SearchContainsValid(aMatch: PMatch; aText: PUTF8Char; aTextLen: PtrInt): boolean; +begin + result := true; +end; + +function SearchContainsU(aMatch: PMatch; aText: PUTF8Char; aTextLen: PtrInt): boolean; +begin + dec(aTextLen, aMatch.PMax); + if aTextLen > 0 then + result := SimpleContainsU(aText, aText + aTextLen, aMatch.Pattern, aMatch.PMax, aMatch.Upper) + else + result := false; +end; + +function SearchContains1(aMatch: PMatch; aText: PUTF8Char; aTextLen: PtrInt): boolean; +begin + dec(aTextLen, aMatch.PMax); + if aTextLen > 0 then + result := SimpleContains1(aText, aText + aTextLen, aMatch.Pattern, aMatch.PMax) + else + result := false; +end; + +function SearchContains4(aMatch: PMatch; aText: PUTF8Char; aTextLen: PtrInt): boolean; +begin + dec(aTextLen, aMatch.PMax); + if aTextLen > 0 then + result := SimpleContains4(aText, aText + aTextLen, aMatch.Pattern, aMatch.PMax) + else + result := false; +end; + +{$ifdef CPU64} +function SearchContains8(aMatch: PMatch; aText: PUTF8Char; aTextLen: PtrInt): boolean; +begin // optimized e.g. to search an IP address as '*12.34.56.78*' in logs + dec(aTextLen, aMatch.PMax); + if aTextLen > 0 then + result := SimpleContains8(aText, aText + aTextLen, aMatch.Pattern, aMatch.PMax) + else + result := false; +end; +{$endif CPU64} + +function SearchStartWith(aMatch: PMatch; aText: PUTF8Char; aTextLen: PtrInt): boolean; +begin + result := (aMatch.PMax < aTextlen) and CompareMem(aText, aMatch.Pattern, aMatch.PMax + 1); +end; + +function SearchStartWithU(aMatch: PMatch; aText: PUTF8Char; aTextLen: PtrInt): boolean; +begin + result := (aMatch.PMax < aTextlen) and CompareMemU(aText, aMatch.Pattern, aMatch.PMax + 1, aMatch.Upper); +end; + +function SearchEndWith(aMatch: PMatch; aText: PUTF8Char; aTextLen: PtrInt): boolean; +begin + dec(aTextLen, aMatch.PMax); + result := (aTextlen >= 0) and CompareMem(aText + aTextLen, aMatch.Pattern, aMatch.PMax); +end; + +function SearchEndWithU(aMatch: PMatch; aText: PUTF8Char; aTextLen: PtrInt): boolean; +begin + dec(aTextLen, aMatch.PMax); + result := (aTextlen >= 0) and CompareMemU(aText + aTextLen, aMatch.Pattern, aMatch.PMax, aMatch.Upper); +end; + +procedure TMatch.Prepare(const aPattern: RawUTF8; aCaseInsensitive, aReuse: boolean); +begin + Prepare(pointer(aPattern), length(aPattern), aCaseInsensitive, aReuse); +end; + +procedure TMatch.Prepare(aPattern: PUTF8Char; aPatternLen: integer; + aCaseInsensitive, aReuse: boolean); +const SPECIALS: PUTF8Char = '*?['; +begin + Pattern := aPattern; + PMax := aPatternLen - 1; // search in Pattern[0..PMax] + if Pattern = nil then begin + Search := SearchVoid; + exit; + end; + if aCaseInsensitive and not IsCaseSensitive(aPattern,aPatternLen) then + aCaseInsensitive := false; // don't slow down e.g. number or IP search + if aCaseInsensitive then + Upper := @NormToUpperAnsi7 else + Upper := @NormToNorm; + Search := nil; + if aReuse then + if strcspn(Pattern, SPECIALS) > PMax then + if aCaseInsensitive then + Search := SearchNoPatternU + else + Search := SearchNoPattern + else if PMax > 0 then begin + if Pattern[PMax] = '*' then begin + if strcspn(Pattern + 1, SPECIALS) = PMax - 1 then + case Pattern[0] of + '*': begin // *something* + inc(Pattern); + dec(PMax, 2); // trim trailing and ending * + if PMax < 0 then + Search := SearchContainsValid + else if aCaseInsensitive then + Search := SearchContainsU + {$ifdef CPU64} + else if PMax >= 7 then + Search := SearchContains8 + {$endif} + else if PMax >= 3 then + Search := SearchContains4 + else + Search := SearchContains1; + end; + '?': + if aCaseInsensitive then + Search := SearchNoRangeU + else + Search := SearchNoRange; + '[': + Search := SearchAny; + else begin + dec(PMax); // trim trailing * + if aCaseInsensitive then + Search := SearchStartWithU + else + Search := SearchStartWith; + end; + end; + end + else if (Pattern[0] = '*') and (strcspn(Pattern + 1, SPECIALS) >= PMax) then begin + inc(Pattern); // jump leading * + if aCaseInsensitive then + Search := SearchEndWithU + else + Search := SearchEndWith; + end; + end else + if Pattern[0] in ['*','?'] then + Search := SearchContainsValid; + if not Assigned(Search) then begin + aPattern := PosChar(Pattern, '['); + if (aPattern = nil) or (aPattern - Pattern > PMax) then + if aCaseInsensitive then + Search := SearchNoRangeU + else + Search := SearchNoRange + else + Search := SearchAny; + end; +end; + +type // Holub and Durian (2005) SBNDM2 algorithm + // see http://www.cri.haifa.ac.il/events/2005/string/presentations/Holub.pdf + TSBNDMQ2Mask = array[AnsiChar] of cardinal; + PSBNDMQ2Mask = ^TSBNDMQ2Mask; + +function SearchSBNDMQ2ComputeMask(const Pattern: RawUTF8; u: PNormTable): RawByteString; +var + i: PtrInt; + p: PAnsiChar absolute Pattern; + m: PSBNDMQ2Mask absolute result; + c: PCardinal; +begin + SetLength(result, SizeOf(m^)); + {$ifdef FPC}FillChar{$else}FillCharFast{$endif}(m^, SizeOf(m^), 0); + for i := 0 to length(Pattern) - 1 do begin + c := @m^[u[p[i]]]; // for FPC code generation + c^ := c^ or (1 shl i); + end; +end; + +function SearchSBNDMQ2(aMatch: PMatch; aText: PUTF8Char; aTextLen: PtrInt): boolean; +var + mask: PSBNDMQ2Mask; + max, i, j: PtrInt; + state: cardinal; +begin + mask := pointer(aMatch^.Pattern); // was filled by SearchSBNDMQ2ComputeMask() + max := aMatch^.PMax; + i := max - 1; + dec(aTextLen); + if i < aTextLen then begin + repeat + state := mask[aText[i+1]] shr 1; // in two steps for better FPC codegen + state := state and mask[aText[i]]; + if state = 0 then begin + inc(i, max); // fast skip + if i >= aTextLen then + break; + continue; + end; + j := i - max; + repeat + dec(i); + if i < 0 then + break; + state := (state shr 1) and mask[aText[i]]; + until state = 0; + if i = j then begin + result := true; + exit; + end; + inc(i, max); + if i >= aTextLen then + break; + until false; + end; + result := false; +end; + +function SearchSBNDMQ2U(aMatch: PMatch; aText: PUTF8Char; aTextLen: PtrInt): boolean; +var + u: PNormTable; + mask: PSBNDMQ2Mask; + max, i, j: PtrInt; + state: cardinal; +begin + mask := pointer(aMatch^.Pattern); + max := aMatch^.PMax; + u := aMatch^.Upper; + i := max - 1; + dec(aTextLen); + if i < aTextLen then begin + repeat + state := mask[u[aText[i+1]]] shr 1; + state := state and mask[u[aText[i]]]; + if state = 0 then begin + inc(i, max); + if i >= aTextLen then + break; + continue; + end; + j := i - max; + repeat + dec(i); + if i < 0 then + break; + state := (state shr 1) and mask[u[aText[i]]]; + until state = 0; + if i = j then begin + result := true; + exit; + end; + inc(i, max); + if i >= aTextLen then + break; + until false; + end; + result := false; +end; + +procedure TMatch.PrepareContains(var aPattern: RawUTF8; + aCaseInsensitive: boolean); +begin + PMax := length(aPattern) - 1; + if aCaseInsensitive and not IsCaseSensitive(pointer(aPattern), PMax + 1) then + aCaseInsensitive := false; + if aCaseInsensitive then + Upper := @NormToUpperAnsi7 + else + Upper := @NormToNorm; + if PMax < 0 then + Search := SearchContainsValid + else if PMax > 30 then + if aCaseInsensitive then + Search := SearchContainsU + else + Search := {$ifdef CPU64}SearchContains8{$else}SearchContains4{$endif} + else if PMax >= 1 then begin // len in [2..31] = PMax in [1..30] + aPattern := SearchSBNDMQ2ComputeMask(aPattern, Upper); // lookup table + if aCaseInsensitive then + Search := SearchSBNDMQ2U + else + Search := SearchSBNDMQ2; + end + else if aCaseInsensitive then + Search := SearchContainsU + else + Search := SearchContains1; // todo: use IndexByte() on FPC? + Pattern := pointer(aPattern); +end; + +procedure TMatch.PrepareRaw(aPattern: PUTF8Char; aPatternLen: integer; + aSearch: TMatchSearchFunction); +begin + Pattern := aPattern; + PMax := aPatternLen - 1; // search in Pattern[0..PMax] + Search := aSearch; +end; + +function TMatch.Match(const aText: RawUTF8): boolean; +begin + if aText <> '' then + result := Search(@self, pointer(aText), length(aText)) + else + result := PMax < 0; +end; + +function TMatch.Match(aText: PUTF8Char; aTextLen: PtrInt): boolean; +begin + if (aText <> nil) and (aTextLen > 0) then + result := Search(@self, aText, aTextLen) + else + result := PMax < 0; +end; + +function TMatch.MatchThreadSafe(const aText: RawUTF8): boolean; +var local: TMatch; // thread-safe with no lock! +begin + local := self; + if aText <> '' then + result := local.Search(@local, pointer(aText), length(aText)) + else + result := local.PMax < 0; +end; + +function TMatch.MatchString(const aText: string): boolean; +var + local: TMatch; // thread-safe with no lock! + temp: TSynTempBuffer; + len: integer; +begin + if aText = '' then begin + result := PMax < 0; + exit; + end; + len := length(aText); + temp.Init(len * 3); + {$ifdef UNICODE} + len := RawUnicodeToUtf8(temp.buf, temp.len + 1, pointer(aText), len, [ccfNoTrailingZero]); + {$else} + len := CurrentAnsiConvert.AnsiBufferToUTF8(temp.buf, pointer(aText), len) - temp.buf; + {$endif} + local := self; + result := local.Search(@local, temp.buf, len); + temp.Done; +end; + +function TMatch.Equals(const aAnother{$ifndef DELPHI5OROLDER}: TMatch{$endif}): boolean; +begin + result := (PMax = TMatch(aAnother).PMax) and (Upper = TMatch(aAnother).Upper) and + CompareMem(Pattern, TMatch(aAnother).Pattern, PMax + 1); +end; + +function TMatch.PatternLength: integer; +begin + result := PMax + 1; +end; + +function TMatch.PatternText: PUTF8Char; +begin + result := Pattern; +end; + +function IsMatch(const Pattern, Text: RawUTF8; CaseInsensitive: boolean): boolean; +var match: TMatch; +begin + match.Prepare(Pattern, CaseInsensitive, {reuse=}false); + result := match.Match(Text); +end; + +function IsMatchString(const Pattern, Text: string; CaseInsensitive: boolean): boolean; +var match: TMatch; + pat, txt: RawUTF8; +begin + StringToUTF8(Pattern, pat); // local variable is mandatory for FPC + StringToUTF8(Text, txt); + match.Prepare(pat, CaseInsensitive, {reuse=}false); + result := match.Match(txt); +end; + +function SetMatchs(const CSVPattern: RawUTF8; CaseInsensitive: boolean; + out Match: TMatchDynArray): integer; +var P, S: PUTF8Char; +begin + result := 0; + P := pointer(CSVPattern); + if P <> nil then + repeat + S := P; + while not (P^ in [#0, ',']) do + inc(P); + if P <> S then begin + SetLength(Match, result + 1); + Match[result].Prepare(S, P - S, CaseInsensitive, {reuse=}true); + inc(result); + end; + if P^ = #0 then + break; + inc(P); + until false; +end; + +function SetMatchs(CSVPattern: PUTF8Char; CaseInsensitive: boolean; + Match: PMatch; MatchMax: integer): integer; +var S: PUTF8Char; +begin + result := 0; + if (CSVPattern <> nil) and (MatchMax >= 0) then + repeat + S := CSVPattern; + while not (CSVPattern^ in [#0, ',']) do + inc(CSVPattern); + if CSVPattern <> S then begin + Match^.Prepare(S, CSVPattern - S, CaseInsensitive, {reuse=}true); + inc(result); + if result > MatchMax then + break; + inc(Match); + end; + if CSVPattern^ = #0 then + break; + inc(CSVPattern); + until false; +end; + +function MatchExists(const One: TMatch; const Several: TMatchDynArray): boolean; +var + i: integer; +begin + result := true; + for i := 0 to high(Several) do + if Several[i].Equals(One) then + exit; + result := false; +end; + +function MatchAdd(const One: TMatch; var Several: TMatchDynArray): boolean; +var + n: integer; +begin + result := not MatchExists(One, Several); + if result then begin + n := length(Several); + SetLength(Several, n + 1); + Several[n] := One; + end; +end; + +function MatchAny(const Match: TMatchDynArray; const Text: RawUTF8): boolean; +var + m: PMatch; + i: integer; +begin + result := true; + if Match = nil then + exit; + m := pointer(Match); + for i := 1 to length(Match) do + if m^.Match(Text) then + exit + else + inc(m); + result := false; +end; + +procedure FilterMatchs(const CSVPattern: RawUTF8; CaseInsensitive: boolean; + var Values: TRawUTF8DynArray); +var + match: TMatchDynArray; + m, n, i: integer; +begin + if SetMatchs(CSVPattern, CaseInsensitive, match) = 0 then + exit; + n := 0; + for i := 0 to high(Values) do + for m := 0 to high(match) do + if match[m].Match(Values[i]) then begin + if i <> n then + Values[n] := Values[i]; + inc(n); + break; + end; + if n <> length(Values) then + SetLength(Values, n); +end; + + +{ TMatchs } + +constructor TMatchs.Create(const aPatterns: TRawUTF8DynArray; CaseInsensitive: Boolean); +begin + inherited Create; + Subscribe(aPatterns, CaseInsensitive); +end; + +function TMatchs.Match(const aText: RawUTF8): integer; +begin + result := Match(pointer(aText), length(aText)); +end; + +function TMatchs.Match(aText: PUTF8Char; aLen: integer): integer; +var + one: ^TMatchStore; + local: TMatch; // thread-safe with no lock! +begin + if (self = nil) or (fMatch = nil) then + result := -1 // no filter by name -> allow e.g. to process everything + else begin + one := pointer(fMatch); + if aLen <> 0 then begin + for result := 0 to fMatchCount - 1 do begin + local := one^.Pattern; + if local.Search(@local, aText, aLen) then + exit; + inc(one); + end; + end + else + for result := 0 to fMatchCount - 1 do + if one^.Pattern.PMax < 0 then + exit + else + inc(one); + result := -2; + end; +end; + +function TMatchs.MatchString(const aText: string): integer; +var + temp: TSynTempBuffer; + len: integer; +begin + len := length(aText); + temp.Init(len * 3); + {$ifdef UNICODE} + len := RawUnicodeToUtf8(temp.buf, temp.len + 1, pointer(aText), len, [ccfNoTrailingZero]); + {$else} + len := CurrentAnsiConvert.AnsiBufferToUTF8(temp.buf, pointer(aText), len, true) - temp.buf; + {$endif} + result := Match(temp.buf, len); + temp.Done; +end; + +procedure TMatchs.Subscribe(const aPatternsCSV: RawUTF8; CaseInsensitive: Boolean); +var + patterns: TRawUTF8DynArray; +begin + CSVToRawUTF8DynArray(pointer(aPatternsCSV), patterns); + Subscribe(patterns, CaseInsensitive); +end; + +procedure TMatchs.Subscribe(const aPatterns: TRawUTF8DynArray; CaseInsensitive: Boolean); +var + i, j, m, n: integer; + found: ^TMatchStore; + pat: PRawUTF8; +begin + m := length(aPatterns); + if m = 0 then + exit; + n := fMatchCount; + SetLength(fMatch, n + m); + pat := pointer(aPatterns); + for i := 1 to m do begin + found := pointer(fMatch); + for j := 1 to n do + if StrComp(pointer(found^.PatternInstance), pointer(pat^)) = 0 then begin + found := nil; + break; + end + else + inc(found); + if found <> nil then + with fMatch[n] do begin + PatternInstance := pat^; // avoid GPF if aPatterns[] is released + Pattern.Prepare(PatternInstance, CaseInsensitive, {reuse=}true); + inc(n); + end; + inc(pat); + end; + fMatchCount := n; + if n <> length(fMatch) then + SetLength(fMatch, n); +end; + + +{ TSynFilterOrValidate } + +constructor TSynFilterOrValidate.Create(const aParameters: RawUTF8); +begin + inherited Create; + SetParameters(aParameters); // should parse the JSON-encoded parameters +end; + +constructor TSynFilterOrValidate.CreateUTF8(const Format: RawUTF8; + const Args, Params: array of const); +begin + Create(FormatUTF8(Format,Args,Params,true)); +end; + +procedure TSynFilterOrValidate.SetParameters(const value: RawUTF8); +begin + fParameters := value; +end; + +function TSynFilterOrValidate.AddOnce(var aObjArray: TSynFilterOrValidateObjArray; + aFreeIfAlreadyThere: boolean): TSynFilterOrValidate; +var i: integer; +begin + if self<>nil then begin + for i := 0 to length(aObjArray)-1 do + if (PPointer(aObjArray[i])^=PPointer(self)^) and + (aObjArray[i].fParameters=fParameters) then begin + if aFreeIfAlreadyThere then + Free; + result := aObjArray[i]; + exit; + end; + ObjArrayAdd(aObjArray,self); + end; + result := self; +end; + + +{ TSynFilterUpperCase } + +procedure TSynFilterUpperCase.Process(aFieldIndex: integer; var value: RawUTF8); +begin + value := SynCommons.UpperCase(value); +end; + + +{ TSynFilterUpperCaseU } + +procedure TSynFilterUpperCaseU.Process(aFieldIndex: integer; var value: RawUTF8); +begin + value := UpperCaseU(value); +end; + + +{ TSynFilterLowerCase } + +procedure TSynFilterLowerCase.Process(aFieldIndex: integer; var value: RawUTF8); +begin + value := LowerCase(value); +end; + + +{ TSynFilterLowerCaseU } + +procedure TSynFilterLowerCaseU.Process(aFieldIndex: integer; var value: RawUTF8); +begin + value := LowerCaseU(value); +end; + + +{ TSynFilterTrim } + +procedure TSynFilterTrim.Process(aFieldIndex: integer; var value: RawUTF8); +begin + value := Trim(value); +end; + + +{ TSynFilterTruncate} + +procedure TSynFilterTruncate.SetParameters(const value: RawUTF8); +var V: array[0..1] of TValuePUTF8Char; + tmp: TSynTempBuffer; +begin + tmp.Init(value); + JSONDecode(tmp.buf,['MaxLength','UTF8Length'],@V); + fMaxLength := GetCardinalDef(V[0].Value,0); + fUTF8Length := V[1].Idem('1') or V[1].Idem('true'); + tmp.Done; +end; + +procedure TSynFilterTruncate.Process(aFieldIndex: integer; var value: RawUTF8); +begin + if fMaxLength-163 then + break; // exceeded 63-character limit of a DNS name + if (ForbiddenDomains<>'') and (FindCSVIndex(pointer(ForbiddenDomains),DOM)>=0) then + break; + i := length(value); + while (i>0) and (value[i]<>'.') do dec(i); + TLD := lowercase(copy(value,i+1,100)); + if (AllowedTLD<>'') and (FindCSVIndex(pointer(AllowedTLD),TLD)<0) then + break; + if (ForbiddenTLD<>'') and (FindCSVIndex(pointer(ForbiddenTLD),TLD)>=0) then + break; + if not fAnyTLD then + if FastFindPUTF8CharSorted(@TopLevelTLD,high(TopLevelTLD),pointer(TLD))<0 then + if length(TLD)<>2 then + break; // assume a two chars string is a ISO 3166-1 alpha-2 code + result := true; + exit; + until true; + ErrorMsg := Format(sInvalidEmailAddress,[UTF8ToString(value)]); + result := false; +end; + +procedure TSynValidateEmail.SetParameters(const value: RawUTF8); +var V: array[0..3] of TValuePUTF8Char; + tmp: TSynTempBuffer; +begin + inherited; + tmp.Init(value); + JSONDecode(tmp.buf,['AllowedTLD','ForbiddenTLD','ForbiddenDomains','AnyTLD'],@V); + LowerCaseCopy(V[0].Value,V[0].ValueLen,fAllowedTLD); + LowerCaseCopy(V[1].Value,V[1].ValueLen,fForbiddenTLD); + LowerCaseCopy(V[2].Value,V[2].ValueLen,fForbiddenDomains); + AnyTLD := V[3].Idem('1') or V[3].Idem('true'); + tmp.Done; +end; + + +{ TSynValidatePattern } + +procedure TSynValidatePattern.SetParameters(const Value: RawUTF8); +begin + inherited SetParameters(Value); + fMatch.Prepare(Value, ClassType=TSynValidatePatternI, {reuse=}true); +end; + +function TSynValidatePattern.Process(aFieldIndex: integer; const value: RawUTF8; + var ErrorMsg: string): boolean; + procedure SetErrorMsg; + begin + ErrorMsg := Format(sInvalidPattern,[UTF8ToString(value)]); + end; +begin + result := fMatch.Match(value); + if not result then + SetErrorMsg; +end; + + +{ TSynValidateNonVoidText } + +function Character01n(n: integer): string; +begin + if n<0 then + n := 0 else + if n>1 then + n := 2; + result := GetCSVItemString(pointer(string(sCharacter01n)),n); +end; + +procedure InvalidTextLengthMin(min: integer; var result: string); +begin + result := Format(sInvalidTextLengthMin,[min,Character01n(min)]); +end; + +function TSynValidateNonVoidText.Process(aFieldIndex: integer; const value: RawUTF8; + var ErrorMsg: string): boolean; +begin + if value='' then begin + InvalidTextLengthMin(1,ErrorMsg); + result := false; + end else + result := true; +end; + + +{ TSynValidateText } + +procedure TSynValidateText.SetErrorMsg(fPropsIndex, InvalidTextIndex, + MainIndex: integer; var result: string); +var P: PChar; +begin + P := pointer(string(sInvalidTextChar)); + result := GetCSVItemString(P,MainIndex); + if fPropsIndex>0 then + result := Format(result, + [fProps[fPropsIndex],GetCSVItemString(P,InvalidTextIndex), + Character01n(fProps[fPropsIndex])]); +end; + +function TSynValidateText.Process(aFieldIndex: integer; const value: RawUTF8; + var ErrorMsg: string): boolean; +var i, L: cardinal; + Min: array[2..7] of cardinal; +begin + result := false; + if fUTF8Length then + L := length(value) else + L := Utf8ToUnicodeLength(pointer(value)); + if LMaxLength then + ErrorMsg := Format(sInvalidTextLengthMax,[MaxLength,Character01n(MaxLength)]) else begin + {$ifdef FPC}FillChar{$else}FillCharFast{$endif}(Min,SizeOf(Min),0); + L := length(value); + for i := 1 to L do + case value[i] of + ' ': + inc(Min[7]); + 'a'..'z': begin + inc(Min[2]); + inc(Min[5]); + end; + 'A'..'Z': begin + inc(Min[2]); + inc(Min[6]); + end; + '0'..'9': + inc(Min[3]); + '_','!',';','.',',','/',':','?','%','$','=','"','#','@','(',')','{','}', + '+','''','-','*': + inc(Min[4]); + end; + for i := 2 to 7 do + if Min[i]fProps[i+8] then begin + SetErrorMsg(i+8,i,1,ErrorMsg); + exit; + end; + if value<>'' then begin + if MaxLeftTrimCountMaxLeftTrimCount then begin + SetErrorMsg(0,0,8,ErrorMsg); + exit; + end; + end; + if MaxRightTrimCountMaxRightTrimCount then begin + SetErrorMsg(0,0,9,ErrorMsg); + exit; + end; + end; + end; + result := true; + end; +end; + +procedure TSynValidateText.SetParameters(const value: RawUTF8); +var V: array[0..high(TSynValidateTextProps)+1] of TValuePUTF8Char; + i: integer; + tmp: TSynTempBuffer; +const DEFAULT: TSynValidateTextProps = ( + 1,maxInt,0,0,0,0,0,0,maxInt,maxInt,maxInt,maxInt,maxInt,maxInt,maxInt,maxInt); +begin + if (MinLength=0) and (MaxLength=0) then // if not previously set + fProps := DEFAULT; + inherited SetParameters(value); + if value='' then + exit; + tmp.Init(value); + try + JSONDecode(tmp.buf,['MinLength','MaxLength', + 'MinAlphaCount','MinDigitCount','MinPunctCount', + 'MinLowerCount','MinUpperCount','MinSpaceCount', + 'MaxLeftTrimCount','MaxRightTrimCount', + 'MaxAlphaCount','MaxDigitCount','MaxPunctCount', + 'MaxLowerCount','MaxUpperCount','MaxSpaceCount', + 'UTF8Length'],@V); + for i := 0 to high(fProps) do + fProps[i] := GetCardinalDef(V[i].Value,fProps[i]); + with V[high(V)] do + fUTF8Length := Idem('1') or Idem('true'); + finally + tmp.Done; + end; +end; + + +{ TSynValidatePassWord } + +procedure TSynValidatePassWord.SetParameters(const value: RawUTF8); +const DEFAULT: TSynValidateTextProps = ( + 5,20,1,1,1,1,1,0,maxInt,maxInt,maxInt,maxInt,maxInt,maxInt,maxInt,0); +begin + // set default values for validating a strong password + fProps := DEFAULT; + // read custom parameters + inherited; +end; + + +{ ************ low-level buffer processing functions************************* } + +{ TSynBloomFilter } + +const + BLOOM_VERSION = 0; + BLOOM_MAXHASH = 32; // only 7 is needed for 1% false positive ratio + +constructor TSynBloomFilter.Create(aSize: integer; aFalsePositivePercent: double); +const LN2 = 0.69314718056; +begin + inherited Create; + if aSize < 0 then + fSize := 1000 else + fSize := aSize; + if aFalsePositivePercent<=0 then + fFalsePositivePercent := 1 else + if aFalsePositivePercent>100 then + fFalsePositivePercent := 100 else + fFalsePositivePercent := aFalsePositivePercent; + // see http://stackoverflow.com/a/22467497 + fBits := Round(-ln(fFalsePositivePercent/100)*aSize/(LN2*LN2)); + fHashFunctions := Round(fBits/fSize*LN2); + if fHashFunctions=0 then + fHashFunctions := 1 else + if fHashFunctions>BLOOM_MAXHASH then + fHashFunctions := BLOOM_MAXHASH; + Reset; +end; + +constructor TSynBloomFilter.Create(const aSaved: RawByteString; aMagic: cardinal); +begin + inherited Create; + if not LoadFrom(aSaved,aMagic) then + raise ESynException.CreateUTF8('%.Create with invalid aSaved content',[self]); +end; + +procedure TSynBloomFilter.Insert(const aValue: RawByteString); +begin + Insert(pointer(aValue),length(aValue)); +end; + +procedure TSynBloomFilter.Insert(aValue: pointer; aValueLen: integer); +var h: integer; + h1,h2: cardinal; // https://goo.gl/Pls5wi +begin + if (self=nil) or (aValueLen<=0) or (fBits=0) then + exit; + h1 := crc32c(0,aValue,aValueLen); + if fHashFunctions=1 then + h2 := 0 else + h2 := crc32c(h1,aValue,aValueLen); + Safe.Lock; + try + for h := 0 to fHashFunctions-1 do begin + SetBitPtr(pointer(fStore),h1 mod fBits); + inc(h1,h2); + end; + inc(fInserted); + finally + Safe.UnLock; + end; +end; + +function TSynBloomFilter.GetInserted: cardinal; +begin + Safe.Lock; + try + result := fInserted; // Delphi 5 does not support LockedInt64[] + finally + Safe.UnLock; + end; +end; + +function TSynBloomFilter.MayExist(const aValue: RawByteString): boolean; +begin + result := MayExist(pointer(aValue),length(aValue)); +end; + +function TSynBloomFilter.MayExist(aValue: pointer; aValueLen: integer): boolean; +var h: integer; + h1,h2: cardinal; // https://goo.gl/Pls5wi +begin + result := false; + if (self=nil) or (aValueLen<=0) or (fBits=0) then + exit; + h1 := crc32c(0,aValue,aValueLen); + if fHashFunctions=1 then + h2 := 0 else + h2 := crc32c(h1,aValue,aValueLen); + Safe.Lock; + try + for h := 0 to fHashFunctions-1 do + if GetBitPtr(pointer(fStore),h1 mod fBits) then + inc(h1,h2) else + exit; + finally + Safe.UnLock; + end; + result := true; +end; + +procedure TSynBloomFilter.Reset; +begin + Safe.Lock; + try + if fStore='' then + SetLength(fStore,(fBits shr 3)+1); + FillcharFast(pointer(fStore)^,length(fStore),0); + fInserted := 0; + finally + Safe.UnLock; + end; +end; + +function TSynBloomFilter.SaveTo(aMagic: cardinal): RawByteString; +var W: TFileBufferWriter; + BufLen: integer; + temp: array[word] of byte; +begin + BufLen := length(fStore)+100; + if BufLen<=SizeOf(temp) then + W := TFileBufferWriter.Create(TRawByteStringStream,@temp,SizeOf(temp)) else + W := TFileBufferWriter.Create(TRawByteStringStream,BufLen); + try + SaveTo(W,aMagic); + W.Flush; + result := TRawByteStringStream(W.Stream).DataString; + finally + W.Free; + end; +end; + +procedure TSynBloomFilter.SaveTo(aDest: TFileBufferWriter; aMagic: cardinal=$B1003F11); +begin + aDest.Write4(aMagic); + aDest.Write1(BLOOM_VERSION); + Safe.Lock; + try + aDest.Write8(fFalsePositivePercent); + aDest.Write4(fSize); + aDest.Write4(fBits); + aDest.Write1(fHashFunctions); + aDest.Write4(fInserted); + ZeroCompress(pointer(fStore),Length(fStore),aDest); + finally + Safe.UnLock; + end; +end; + +function TSynBloomFilter.LoadFrom(const aSaved: RawByteString; aMagic: cardinal): boolean; +begin + result := LoadFrom(pointer(aSaved),length(aSaved)); +end; + +function TSynBloomFilter.LoadFrom(P: PByte; PLen: integer; aMagic: cardinal): boolean; +var start: PByte; + version: integer; +begin + result := false; + start := P; + if (P=nil) or (PLen<32) or (PCardinal(P)^<>aMagic) then + exit; + inc(P,4); + version := P^; inc(P); + if version>BLOOM_VERSION then + exit; + Safe.Lock; + try + fFalsePositivePercent := PDouble(P)^; inc(P,8); + if (fFalsePositivePercent<=0) or (fFalsePositivePercent>100) then + exit; + fSize := PCardinal(P)^; inc(P,4); + fBits := PCardinal(P)^; inc(P,4); + if fBits=BLOOM_MAXHASH then + exit; + Reset; + fInserted := PCardinal(P)^; inc(P,4); + ZeroDecompress(P,PLen-(PAnsiChar(P)-PAnsiChar(start)),fStore); + result := length(fStore)=integer(fBits shr 3)+1; + finally + Safe.UnLock; + end; +end; + + +{ TSynBloomFilterDiff } + +type + TBloomDiffHeader = packed record + kind: (bdDiff,bdFull,bdUpToDate); + size: cardinal; + inserted: cardinal; + revision: Int64; + crc: cardinal; + end; + +procedure TSynBloomFilterDiff.Insert(aValue: pointer; aValueLen: integer); +begin + Safe.Lock; + try + inherited Insert(aValue,aValueLen); + inc(fRevision); + inc(fSnapshotInsertCount); + finally + Safe.UnLock; + end; +end; + +procedure TSynBloomFilterDiff.Reset; +begin + Safe.Lock; + try + inherited Reset; + fSnapshotAfterInsertCount := fSize shr 5; + fSnapShotAfterMinutes := 30; + fSnapshotTimestamp := 0; + fSnapshotInsertCount := 0; + fRevision := UnixTimeUTC shl 31; + fKnownRevision := 0; + fKnownStore := ''; + finally + Safe.UnLock; + end; +end; + +procedure TSynBloomFilterDiff.DiffSnapshot; +begin + Safe.Lock; + try + fKnownRevision := fRevision; + fSnapshotInsertCount := 0; + SetString(fKnownStore,PAnsiChar(pointer(fStore)),length(fStore)); + if fSnapShotAfterMinutes=0 then + fSnapshotTimestamp := 0 else + fSnapshotTimestamp := GetTickCount64+fSnapShotAfterMinutes*60000; + finally + Safe.UnLock; + end; +end; + +function TSynBloomFilterDiff.SaveToDiff(const aKnownRevision: Int64): RawByteString; +var head: TBloomDiffHeader; + W: TFileBufferWriter; + temp: array[word] of byte; +begin + Safe.Lock; + try + if aKnownRevision=fRevision then + head.kind := bdUpToDate else + if (fKnownRevision=0) or + (fSnapshotInsertCount>fSnapshotAfterInsertCount) or + ((fSnapshotInsertCount>0) and (fSnapshotTimestamp<>0) and + (GetTickCount64>fSnapshotTimestamp)) then begin + DiffSnapshot; + head.kind := bdFull; + end else + if (aKnownRevisionfRevision) then + head.kind := bdFull else + head.kind := bdDiff; + head.size := length(fStore); + head.inserted := fInserted; + head.revision := fRevision; + head.crc := crc32c(0,@head,SizeOf(head)-SizeOf(head.crc)); + if head.kind=bdUpToDate then begin + SetString(result,PAnsiChar(@head),SizeOf(head)); + exit; + end; + if head.size+100<=SizeOf(temp) then + W := TFileBufferWriter.Create(TRawByteStringStream,@temp,SizeOf(temp)) else + W := TFileBufferWriter.Create(TRawByteStringStream,head.size+100); + try + W.Write(@head,SizeOf(head)); + case head.kind of + bdFull: + SaveTo(W); + bdDiff: + ZeroCompressXor(pointer(fStore),pointer(fKnownStore),head.size,W); + end; + W.Flush; + result := TRawByteStringStream(W.Stream).DataString; + finally + W.Free; + end; + finally + Safe.UnLock; + end; +end; + +function TSynBloomFilterDiff.DiffKnownRevision(const aDiff: RawByteString): Int64; +var head: ^TBloomDiffHeader absolute aDiff; +begin + if (length(aDiff)high(head.kind)) or + (head.size<>cardinal(length(fStore))) or + (head.crc<>crc32c(0,pointer(head),SizeOf(head^)-SizeOf(head.crc))) then + result := 0 else + result := head.Revision; +end; + +function TSynBloomFilterDiff.LoadFromDiff(const aDiff: RawByteString): boolean; +var head: ^TBloomDiffHeader absolute aDiff; + P: PByte; + PLen: integer; +begin + result := false; + P := pointer(aDiff); + PLen := length(aDiff); + if (PLenhigh(head.kind)) or + (head.crc<>crc32c(0,pointer(head),SizeOf(head^)-SizeOf(head.crc))) then + exit; + if (fStore<>'') and (head.size<>cardinal(length(fStore))) then + exit; + inc(P,SizeOf(head^)); + dec(PLen,SizeOf(head^)); + Safe.Lock; + try + case head.kind of + bdFull: + result := LoadFrom(P,PLen); + bdDiff: + if fStore<>'' then + result := ZeroDecompressOr(pointer(P),Pointer(fStore),PLen,head.size); + bdUpToDate: + result := true; + end; + if result then begin + fRevision := head.revision; + fInserted := head.inserted; + end; + finally + Safe.UnLock; + end; +end; + +procedure ZeroCompress(P: PAnsiChar; Len: integer; Dest: TFileBufferWriter); +var PEnd,beg,zero: PAnsiChar; + crc: cardinal; +begin + Dest.WriteVarUInt32(Len); + PEnd := P+Len; + beg := P; + crc := 0; + while P#0) and (P3 then begin + Len := zero-beg; + crc := crc32c(crc,beg,Len); + Dest.WriteVarUInt32(Len); + Dest.Write(beg,Len); + Len := P-zero; + crc := crc32c(crc,@Len,SizeOf(Len)); + Dest.WriteVarUInt32(Len-3); + beg := P; + end; + end; + Len := P-beg; + if Len>0 then begin + crc := crc32c(crc,beg,Len); + Dest.WriteVarUInt32(Len); + Dest.Write(beg,Len); + end; + Dest.Write4(crc); +end; + +procedure ZeroCompressXor(New,Old: PAnsiChar; Len: cardinal; Dest: TFileBufferWriter); +var beg,same,index,crc,L: cardinal; +begin + Dest.WriteVarUInt32(Len); + beg := 0; + index := 0; + crc := 0; + while indexOld[index]) and (index3 then begin + Dest.WriteVarUInt32(same-beg); + Dest.WriteXor(New+beg,Old+beg,same-beg,@crc); + crc := crc32c(crc,@L,SizeOf(L)); + Dest.WriteVarUInt32(L-3); + beg := index; + end; + end; + L := index-beg; + if L>0 then begin + Dest.WriteVarUInt32(L); + Dest.WriteXor(New+beg,Old+beg,L,@crc); + end; + Dest.Write4(crc); +end; + +procedure ZeroDecompress(P: PByte; Len: integer; {$ifdef FPC}var{$else}out{$endif} Dest: RawByteString); +var PEnd,D,DEnd: PAnsiChar; + DestLen,crc: cardinal; +begin + PEnd := PAnsiChar(P)+Len-4; + DestLen := FromVarUInt32(P); + SetString(Dest,nil,DestLen); // FPC uses var + D := pointer(Dest); + DEnd := D+DestLen; + crc := 0; + while PAnsiChar(P)DEnd then + break; + {$ifdef FPC}Move{$else}MoveFast{$endif}(P^,D^,Len); + crc := crc32c(crc,D,Len); + inc(P,Len); + inc(D,Len); + if PAnsiChar(P)>=PEnd then + break; + Len := FromVarUInt32(P)+3; + if D+Len>DEnd then + break; + {$ifdef FPC}FillChar{$else}FillCharFast{$endif}(D^,Len,0); + crc := crc32c(crc,@Len,SizeOf(Len)); + inc(D,Len); + end; + if crc<>PCardinal(P)^ then + Dest := ''; +end; + +function ZeroDecompressOr(P,Dest: PAnsiChar; Len,DestLen: integer): boolean; +var PEnd,DEnd: PAnsiChar; + crc: cardinal; +begin + PEnd := P+Len-4; + if cardinal(DestLen)<>FromVarUInt32(PByte(P)) then begin + result := false; + exit; + end; + DEnd := Dest+DestLen; + crc := 0; + while (PDEnd then + break; + crc := crc32c(crc,P,Len); + OrMemory(pointer(Dest),pointer(P),Len); + inc(P,Len); + inc(Dest,Len); + if P>=PEnd then + break; + Len := FromVarUInt32(PByte(P))+3; + crc := crc32c(crc,@Len,SizeOf(Len)); + inc(Dest,Len); + end; + result := crc=PCardinal(P)^; +end; + + +function Max(a,b: PtrInt): PtrInt; {$ifdef HASINLINE}inline;{$endif} +begin + if a > b then + result := a else + result := b; +end; + +function Min(a,b: PtrInt): PtrInt; {$ifdef HASINLINE}inline;{$endif} +begin + if a < b then + result := a else + result := b; +end; + +function Comp(a,b: PAnsiChar; len: PtrInt): PtrInt; +{$ifdef HASINLINE} inline; +var lenptr: PtrInt; +begin + result := 0; + lenptr := len-SizeOf(PtrInt); + if lenptr>=0 then + repeat + if PPtrInt(a+result)^<>PPtrInt(b+result)^ then + break; + inc(result,SizeOf(PtrInt)); + until result>lenptr; + if resultb[result] then + exit; + inc(result); + until result=len; +end; +{$else} // eax = a, edx = b, ecx = len +asm // the 'rep cmpsb' version is slower on Intel Core CPU (not AMD) + or ecx,ecx + push ebx + push ecx + jz @ok +@1: mov bx,[eax] + lea eax,[eax+2] + cmp bl,[edx] + jne @ok + dec ecx + jz @ok + cmp bh,[edx+1] + lea edx,[edx+2] + jne @ok + dec ecx + jnz @1 +@ok: pop eax + sub eax,ecx + pop ebx +end; +{$endif} + +function CompReverse(a,b: pointer; len: PtrInt): PtrInt; +begin + result := 0; + if len>0 then + repeat + if PByteArray(a)[-result]<>PByteArray(b)[-result] then + exit; + inc(result); + until result=len; +end; + +procedure movechars(s,d: PAnsiChar; t: PtrUInt); + {$ifdef HASINLINE}inline;{$endif} +// this code is sometimes used rather than MoveFast() for overlapping copy +begin + dec(PtrUInt(s), PtrUInt(d)); + inc(t, PtrUInt(d)); + repeat + d^ := s[PtrUInt(d)]; + inc(d); + until PtrUInt(d)=t; +end; + +function WriteCurOfs(curofs,curlen,curofssize: integer; sp: PAnsiChar): PAnsiChar; +begin + if curlen=0 then begin + sp^ := #0; + inc(sp); + end else begin + sp := Pointer(ToVarUInt32(curlen,PByte(sp))); + PInteger(sp)^ := curofs; + inc(sp,curofssize); + end; + result := sp; +end; + +{$ifdef CPUINTEL} // crc32c SSE4.2 hardware accellerated dword hash +function crc32csse42(buf: pointer): cardinal; +{$ifdef CPUX86} +asm + mov edx, eax + xor eax, eax + {$ifdef ISDELPHI2010} + crc32 eax, dword ptr[edx] + {$else} + db $F2, $0F, $38, $F1, $02 + {$endif} +end; +{$else} {$ifdef FPC}nostackframe; assembler; asm {$else} +asm // ecx=buf (Linux: edi=buf) + .noframe +{$endif FPC} + xor eax, eax + crc32 eax, dword ptr[buf] +end; +{$endif CPUX86} +{$endif CPUINTEL} + +function hash32prime(buf: pointer): cardinal; +begin // xxhash32-inspired - and won't pollute L1 cache with lookup tables + result := PCardinal(buf)^; + result := result xor (result shr 15); + result := result * 2246822519; + result := result xor (result shr 13); + result := result * 3266489917; + result := result xor (result shr 16); +end; + +const + HTabBits = 18; // fits well with DeltaCompress(..,BufSize=2MB) + HTabMask = (1 shl HTabBits)-1; // =$3ffff + HListMask = $ffffff; // HTab[]=($ff,$ff,$ff) + +type + PHTab = ^THTab; // HTabBits=18 -> SizeOf=767KB + THTab = packed array[0..HTabMask] of array[0..2] of byte; + +function DeltaCompute(NewBuf, OldBuf, OutBuf, WorkBuf: PAnsiChar; + NewBufSize, OldBufSize, MaxLevel: PtrInt; HList, HTab: PHTab): PAnsiChar; +var i, curofs, curlen, curlevel, match, curofssize, h, oldh: PtrInt; + sp, pInBuf, pOut: PAnsiChar; + ofs: cardinal; + spb: PByte absolute sp; + hash: function(buf: pointer): cardinal; +begin + // 1. fill HTab[] with hashes for all old data + {$ifdef CPUINTEL} + if cfSSE42 in CpuFeatures then + hash := @crc32csse42 else + {$endif} + hash := @hash32prime; + FillCharFast(HTab^,SizeOf(HTab^),$ff); // HTab[]=HListMask by default + pInBuf := OldBuf; + oldh := -1; // force calculate first hash + sp := pointer(HList); + for i := 0 to OldBufSize-3 do begin + h := hash(pInBuf) and HTabMask; + inc(pInBuf); + if h=oldh then + continue; + oldh := h; + h := PtrInt(@HTab^[h]); // fast 24-bit data process + PCardinal(sp)^ := PCardinal(h)^; + PCardinal(h)^ := cardinal(i) or (PCardinal(h)^ and $ff000000); + inc(sp,3); + end; + // 2. compression init + if OldBufSize<=$ffff then + curofssize := 2 else + curofssize := 3; + curlen := -1; + curofs := 0; + pOut := OutBuf+7; + sp := WorkBuf; + // 3. handle identical leading bytes + match := Comp(OldBuf,NewBuf,Min(OldBufSize,NewBufSize)); + if match>2 then begin + sp := WriteCurOfs(0,match,curofssize,sp); + sp^ := #0; inc(sp); + inc(NewBuf,match); + dec(NewBufSize,match); + end; + pInBuf := NewBuf; + // 4. main loop: identify longest sequences using hash, and store reference + if NewBufSize>=8 then + repeat + // hash 4 next bytes from NewBuf, and find longest match in OldBuf + ofs := PCardinal(@HTab^[hash(NewBuf) and HTabMask])^ and HListMask; + if ofs<>HListMask then begin // brute force search loop of best hash match + curlevel := MaxLevel; + repeat + with PHash128Rec(OldBuf+ofs)^ do + {$ifdef CPU64} // test 8 bytes + if PHash128Rec(NewBuf)^.Lo=Lo then begin + {$else} + if (PHash128Rec(NewBuf)^.c0=c0) and (PHash128Rec(NewBuf)^.c1=c1) then begin + {$endif} + match := Comp(@PHash128Rec(NewBuf)^.c2,@c2,Min(PtrUInt(OldBufSize)-ofs,NewBufSize)-8); + if match>curlen then begin // found a longer sequence + curlen := match; + curofs := ofs; + end; + end; + dec(curlevel); + ofs := PCardinal(@HList^[ofs])^ and HListMask; + until (ofs=HListMask) or (curlevel=0); + end; + // curlen = longest sequence length + if curlen<0 then begin // no sequence found -> copy one byte + dec(NewBufSize); + pOut^ := NewBuf^; + inc(NewBuf); + inc(pOut); + if NewBufSize>8 then // >=8 may overflow + continue else + break; + end; + inc(curlen,8); + sp := WriteCurOfs(curofs,curlen,curofssize,sp); + spb := ToVarUInt32(NewBuf-pInBuf,spb); + inc(NewBuf,curlen); // continue to search after the sequence + dec(NewBufSize,curlen); + curlen := -1; + pInBuf := NewBuf; + if NewBufSize>8 then // >=8 may overflow + continue else + break; + until false; + // 5. write remaining bytes + if NewBufSize>0 then begin + {$ifdef FPC}Move{$else}MoveFast{$endif}(NewBuf^,pOut^,NewBufSize); + inc(pOut,NewBufSize); + inc(newBuf,NewBufSize); + end; + sp^ := #0; inc(sp); + spb := ToVarUInt32(NewBuf-pInBuf,spb); + // 6. write header + PInteger(OutBuf)^ := pOut-OutBuf-7; + h := sp-WorkBuf; + PInteger(OutBuf+3)^ := h; + OutBuf[6] := AnsiChar(curofssize); + // 7. copy commands + {$ifdef FPC}Move{$else}MoveFast{$endif}(WorkBuf^,pOut^,h); + result := pOut+h; +end; + +function ExtractBuf(GoodCRC: cardinal; p: PAnsiChar; var aUpd, Delta: PAnsiChar; + Old: PAnsiChar): TDeltaError; +var pEnd, buf, upd, src: PAnsiChar; + bufsize, datasize, leading, srclen: PtrUInt; + curofssize: byte; +begin + // 1. decompression init + upd := aUpd; + bufsize := PCardinal(p)^ and $00ffffff; inc(p,3); + datasize := PCardinal(p)^ and $00ffffff; inc(p,3); + curofssize := ord(p^); inc(p); + buf := p; inc(p,bufsize); + pEnd := p+datasize; + src := nil; + // 2. main loop + while p0 then + if curofssize=2 then begin + src := Old+PWord(p)^; + inc(p,2); + end else begin + src := Old+PCardinal(p)^ and $00ffffff; + inc(p,3); + end; + // copy leading bytes + leading := FromVarUInt32(PByte(P)); + if leading<>0 then begin + {$ifdef FPC}Move{$else}MoveFast{$endif}(buf^,upd^,leading); + inc(buf,leading); + inc(upd,leading); + end; + // copy sequence + if srclen<>0 then begin + if PtrUInt(upd-src) direct copy of whole block + CreateCopied; + exit; + end; + // 2. compression init + bigfile := OldSize>BufSize; + if BufSize>NewSize then + BufSize := NewSize; + if BufSize>$ffffff then + BufSize := $ffffff; // we store offsets with 2..3 bytes -> max 16MB chunk + Trailing := 0; + Getmem(workbuf,BufSize); // compression temporary buffers + Getmem(HList,BufSize*SizeOf(HList[0])); + Getmem(HTab,SizeOf(HTab^)); + Getmem(Delta,Max(NewSize,OldSize)+4096); // Delta size max evalulation + try + d := Delta; + db := ToVarUInt32(NewSize,db); // Destination Size + // 3. handle leading and trailing identical bytes (for biggest files) + if bigfile then begin + BufRead := Comp(New,Old,Min(NewSize,OldSize)); // test 1st same chars + if BufRead>9 then begin // it happens very often + db := ToVarUInt32(BufRead,db); // blockSize = Size BufIdem + WriteByte(d,FLAG_BEGIN); + WriteInt(d,crc32c(0,New,BufRead)); + inc(New,BufRead); + dec(NewSize,BufRead); + inc(Old,BufRead); + dec(OldSize,BufRead); + end; // test last same chars + BufRead := CompReverse(New+NewSize-1,Old+OldSize-1,Min(NewSize,OldSize)); + if BufRead>5 then begin + if NewSize=BufRead then + dec(BufRead); // avoid block overflow + dec(OldSize,BufRead); + dec(NewSize,BufRead); + Trailing := BufRead; + end; + end; + // 4. main loop + repeat + BufRead := Min(BufSize,NewSize); + dec(NewSize,BufRead); + if (BufRead=0) and (Trailing>0) then begin + db := ToVarUInt32(Trailing,db); + WriteByte(d,FLAG_END); // block idem end flag -> BufRead := 0 not necessary + WriteInt(d,crc32c(0,New,Trailing)); + break; + end; + OldRead := Min(BufSize,OldSize); + dec(OldSize,OldRead); + db := ToVarUInt32(OldRead,db); + If (BufRead<4) or (OldRead<4) or (BufRead div 4>OldRead) then begin + WriteByte(d,FLAG_COPIED); // block copied flag + db := ToVarUInt32(BufRead,db); + if BufRead=0 then + break; + WriteInt(d,crc32c(0,New,BufRead)); + {$ifdef FPC}Move{$else}MoveFast{$endif}(New^,d^,BufRead); + inc(New,BufRead); + inc(d,BufRead); + end else begin + WriteByte(d,FLAG_COMPRESS); // block compressed flag + WriteInt(d,crc32c(0,New,BufRead)); + WriteInt(d,crc32c(0,Old,OldRead)); + d := DeltaCompute(New,Old,d,workbuf,BufRead,OldRead,Level,HList,HTab); + inc(New,BufRead); + inc(Old,OldRead); + end; + until false; + // 5. release temp memory + finally + result := d-Delta; + Freemem(HTab); + Freemem(HList); + Freemem(workbuf); + end; + if result>=NewSizeSave+17 then begin + // Delta didn't compress well -> store it (with 17 bytes overhead) + Freemem(Delta); + CreateCopied; + end; +end; + +function DeltaCompress(const New, Old: RawByteString; + Level, BufSize: integer): RawByteString; +begin + result := DeltaCompress(pointer(New),pointer(Old), + length(New),length(Old),Level,BufSize); +end; + +function DeltaCompress(New, Old: PAnsiChar; NewSize, OldSize: integer; + Level, BufSize: integer): RawByteString; +var Delta: PAnsiChar; + DeltaLen: integer; +begin + DeltaLen := DeltaCompress(New,Old,Newsize,OldSize,Delta,Level,BufSize); + SetString(result,Delta,DeltaLen); + Freemem(Delta); +end; + +function DeltaExtract(Delta,Old,New: PAnsiChar): TDeltaError; +var BufCRC: Cardinal; + Code: Byte; + Len, BufRead, OldRead: PtrInt; + db: PByte absolute Delta; + Upd: PAnsiChar; +begin + Result := dsSuccess; + Len := FromVarUInt32(db); + Upd := New; + repeat + OldRead := FromVarUInt32(db); + Code := db^; inc(db); + Case Code of + FLAG_COPIED: begin // block copied flag - copy new from Delta + BufRead := FromVarUInt32(db); + If BufRead=0 then + break; + If crc32c(0,Delta+4,BufRead)<>PCardinal(Delta)^ then begin + result := dsCrcCopy; + exit; + end; + inc(Delta,4); + {$ifdef FPC}Move{$else}MoveFast{$endif}(Delta^,New^,BufRead); + if BufRead>=Len then + exit; // if Old=nil -> only copy new + inc(Delta,BufRead); + inc(New,BufRead); + end; + FLAG_COMPRESS: begin // block compressed flag - extract Delta from Old + BufCRC := PCardinal(Delta)^; inc(Delta,4); + if crc32c(0,Old,OldRead)<>PCardinal(Delta)^ then begin + result := dsCrcComp; + exit; + end; + inc(Delta,4); + result := ExtractBuf(BufCRC,Delta,New,Delta,Old); + if result<>dsSuccess then + exit; + end; + FLAG_BEGIN: begin // block idem begin flag + if crc32c(0,Old,OldRead)<>PCardinal(Delta)^ then begin + result := dsCrcBegin; + exit; + end; + inc(Delta,4); + {$ifdef FPC}Move{$else}MoveFast{$endif}(Old^,New^,OldRead); + inc(New,OldRead); + end; + FLAG_END: begin // block idem end flag + if crc32c(0,Old,OldRead)<>PCardinal(Delta)^ then + Result := dsCrcEnd; + {$ifdef FPC}Move{$else}MoveFast{$endif}(Old^,New^,OldRead); + inc(New,OldRead); + break; + end; + else begin + result := dsFlag; + exit; + end; + end; // Case Code of + inc(Old,OldRead); + until false; + if New-Upd<>Len then + result := dsLen; +end; + +function DeltaExtract(const Delta,Old: RawByteString; out New: RawByteString): TDeltaError; +begin + if (Delta='') or (Delta='=') then begin + New := Old; + result := dsSuccess; + end else begin + SetLength(New,DeltaExtractSize(pointer(Delta))); + result := DeltaExtract(pointer(Delta),pointer(Old),pointer(New)); + end; +end; + +function DeltaExtractSize(const Delta: RawByteString): integer; +begin + result := DeltaExtractSize(pointer(Delta)); +end; + +function DeltaExtractSize(Delta: PAnsiChar): integer; +begin + if Delta=nil then + result := 0 else + result := FromVarUInt32(PByte(Delta)); +end; + +function ToText(err: TDeltaError): PShortString; +begin + result := GetEnumName(TypeInfo(TDeltaError),ord(err)); +end; + + +{ TFastReader } + +procedure TFastReader.Init(Buffer: pointer; Len: integer); +begin + P := Buffer; + Last := P+Len; + OnErrorOverflow := nil; + OnErrorData := nil; + Tag := 0; +end; + +procedure TFastReader.Init(const Buffer: RawByteString); +begin + Init(pointer(Buffer),length(Buffer)); +end; + +procedure TFastReader.ErrorOverflow; +begin + if Assigned(OnErrorOverflow) then + OnErrorOverflow else + raise EFastReader.Create('Reached End of Input'); +end; + +procedure TFastReader.ErrorData(const fmt: RawUTF8; const args: array of const); +begin + if Assigned(OnErrorData) then + OnErrorData(fmt,args) else + raise EFastReader.CreateUTF8('Incorrect Data: '+fmt,args); +end; + +function TFastReader.EOF: boolean; +begin + result := P>=Last; +end; + +function TFastReader.RemainingLength: PtrUInt; +begin + result := PtrUInt(Last)-PtrUInt(P); +end; + +function TFastReader.NextByte: byte; +begin + if P>=Last then + ErrorOverflow; + result := ord(P^); + inc(P); +end; + +function TFastReader.NextByteSafe(dest: pointer): boolean; +begin + if P>=Last then + result := false + else begin + PAnsiChar(dest)^ := P^; + inc(P); + result := true; + end; +end; + +function TFastReader.Next4: cardinal; +begin + if P+3>=Last then + ErrorOverflow; + result := PCardinal(P)^; + inc(P,4); +end; + +function TFastReader.Next8: QWord; +begin + if P+7>=Last then + ErrorOverflow; + result := PQWord(P)^; + inc(P,8); +end; + +function TFastReader.NextByteEquals(Value: byte): boolean; +begin + if P>=Last then + ErrorOverflow; + if ord(P^) = Value then begin + inc(P); + result := true; + end else + result := false; +end; + +function TFastReader.Next(DataLen: PtrInt): pointer; +begin + if P+DataLen>Last then + ErrorOverflow; + result := P; + inc(P,DataLen); +end; + +function TFastReader.NextSafe(out Data: Pointer; DataLen: PtrInt): boolean; +begin + if P+DataLen>Last then + result := false else begin + Data := P; + inc(P,DataLen); + result := true; + end; +end; + +procedure TFastReader.Copy(out Dest; DataLen: PtrInt); +begin + if P+DataLen>Last then + ErrorOverflow; + {$ifdef FPC}Move{$else}MoveFast{$endif}(P^,Dest,DataLen); + inc(P,DataLen); +end; + +function TFastReader.CopySafe(out Dest; DataLen: PtrInt): boolean; +begin + if P+DataLen>Last then + result := false else begin + {$ifdef FPC}Move{$else}MoveFast{$endif}(P^,Dest,DataLen); + inc(P,DataLen); + result := true; + end; +end; + +procedure TFastReader.VarBlob(out result: TValueResult); +begin + result.Len := VarUInt32; + if P+result.Len>Last then + ErrorOverflow; + result.Ptr := P; + inc(P,result.Len); +end; + +function TFastReader.VarBlob: TValueResult; +begin + result.Len := VarUInt32; + if P+result.Len>Last then + ErrorOverflow; + result.Ptr := P; + inc(P,result.Len); +end; + +{$ifndef NOVARIANTS} +procedure TFastReader.NextVariant(var Value: variant; CustomVariantOptions: pointer); +begin + P := VariantLoad(Value,P,CustomVariantOptions); + if P=nil then + ErrorData('VariantLoad=nil',[]) else + if P>Last then + ErrorOverFlow; +end; + +procedure TFastReader.NextDocVariantData(out Value: variant; CustomVariantOptions: pointer); +var json: TValueResult; + temp: TSynTempBuffer; +begin + VarBlob(json); + if json.Len<=0 then + exit; + temp.Init(json.Ptr,json.Len); // parsing will modify input buffer in-place + try + if CustomVariantOptions=nil then + CustomVariantOptions := @JSON_OPTIONS[true]; + TDocVariantData(Value).InitJSONInPlace(temp.buf,PDocVariantOptions(CustomVariantOptions)^); + finally + temp.Done; + end; +end; +{$endif NOVARIANTS} + +function TFastReader.VarInt32: integer; +begin + result := VarUInt32; + if result and 1<>0 then + // 1->1, 3->2.. + result := result shr 1+1 else + // 0->0, 2->-1, 4->-2.. + result := -(result shr 1); +end; + +function TFastReader.VarInt64: Int64; +begin + result := VarUInt64; + if result and 1<>0 then + // 1->1, 3->2.. + result := result shr 1+1 else + // 0->0, 2->-1, 4->-2.. + result := -(result shr 1); +end; + +function TFastReader.VarUInt32: cardinal; +var c: cardinal; +{$ifdef CPUX86} // not enough CPU registers +label err; +begin + result := ord(P^); + if P>=Last then + goto err; + inc(P); + if result<=$7f then + exit; + if P>=Last then + goto err; + c := ord(P^) shl 7; + inc(P); + result := result and $7F or c; + if c<=$7f shl 7 then + exit; // Values between 128 and 16256 + if P>=Last then + goto err; + c := ord(P^) shl 14; + inc(P); + result := result and $3FFF or c; + if c<=$7f shl 14 then + exit; // Values between 16257 and 2080768 + if P>=Last then + goto err; + c := ord(P^) shl 21; + inc(P); + result := result and $1FFFFF or c; + if c<=$7f shl 21 then + exit; // Values between 2080769 and 266338304 + if P>=Last then +err:ErrorOverflow; + c := ord(P^) shl 28; + inc(P); + result := result and $FFFFFFF or c; +{$else} + s,l: PByte; +label err,fin; +begin + s := pointer(P); + l := pointer(Last); + result := s^; + if PAnsiChar(s)>=PAnsiChar(l) then + goto err; + inc(s); + if result<=$7f then + goto fin; + if PAnsiChar(s)>=PAnsiChar(l) then + goto err; + c := s^ shl 7; + inc(s); + result := result and $7F or c; + if c<=$7f shl 7 then + goto fin; // Values between 128 and 16256 + if PAnsiChar(s)>=PAnsiChar(l) then + goto err; + c := s^ shl 14; + inc(s); + result := result and $3FFF or c; + if c<=$7f shl 14 then + goto fin; // Values between 16257 and 2080768 + if PAnsiChar(s)>=PAnsiChar(l) then + goto err; + c := s^ shl 21; + inc(s); + result := result and $1FFFFF or c; + if c<=$7f shl 21 then + goto fin; // Values between 2080769 and 266338304 + if PAnsiChar(s)>=PAnsiChar(l) then +err:ErrorOverflow; + c := s^ shl 28; + inc(s); + result := result and $FFFFFFF or c; +fin: + P := pointer(s); +{$endif} +end; + +procedure TFastReader.VarNextInt; +{$ifdef CPUX86} // not enough CPU registers +begin + repeat + if P>=Last then + break; // reached end of input + if P^<=#$7f then + break; // reached end of VarUInt32/VarUInt64 + inc(P); + until false; + inc(P); +{$else} +var s: PAnsiChar; +begin + s := P; + repeat + if s>=Last then + break; // reached end of input + if s^<=#$7f then + break; // reached end of VarUInt32/VarUInt64 + inc(s); + until false; + P := s+1; +{$endif CPUX86} +end; + +procedure TFastReader.VarNextInt(count: integer); +{$ifdef CPUX86} // not enough CPU registers +begin + if count=0 then + exit; + repeat + if P>=Last then + break; // reached end of input + if P^>#$7f then begin + inc(P); + continue; // didn't reach end of VarUInt32/VarUInt64 + end; + inc(P); + dec(count); + if count=0 then + break; + until false; +{$else} +var s, max: PAnsiChar; +begin + if count=0 then + exit; + s := P; + max := Last; + repeat + if s>=max then + break; // reached end of input + if s^>#$7f then begin + inc(s); + continue; // didn't reach end of VarUInt32/VarUInt64 + end; + inc(s); + dec(count); + if count=0 then + break; + until false; + P := s; +{$endif CPUX86} +end; + +function TFastReader.PeekVarInt32(out value: PtrInt): boolean; +begin + result := PeekVarUInt32(PtrUInt(value)); + if result then + if value and 1<>0 then + // 1->1, 3->2.. + value := value shr 1+1 else + // 0->0, 2->-1, 4->-2.. + value := -(value shr 1); +end; + +function TFastReader.PeekVarUInt32(out value: PtrUInt): boolean; +var s: PAnsiChar; +begin + result := false; + s := P; + repeat + if s>=Last then + exit; // reached end of input -> returns false + if s^<=#$7f then + break; // reached end of VarUInt32 + inc(s); + until false; + s := P; + value := VarUInt32; // fast value decode + P := s; // rewind + result := true; +end; + +function TFastReader.VarUInt32Safe(out Value: cardinal): boolean; +var c, n, v: cardinal; +begin + result := false; + if P>=Last then + exit; + v := ord(P^); + inc(P); + if v>$7f then begin + n := 0; + v := v and $7F; + repeat + if P>=Last then + exit; + c := ord(P^); + inc(P); + inc(n,7); + if c<=$7f then break; + v := v or ((c and $7f) shl n); + until false; + v := v or (c shl n); + end; + Value := v; + result := true; // success +end; + +function TFastReader.VarUInt64: QWord; +label err; +var c, n: PtrUInt; +begin + if P>=Last then +err: ErrorOverflow; + c := ord(P^); + inc(P); + if c>$7f then begin + result := c and $7F; + n := 0; + repeat + if P>=Last then + goto err; + c := ord(P^); + inc(P); + inc(n,7); + if c<=$7f then break; + result := result or (QWord(c and $7f) shl n); + until false; + result := result or (QWord(c) shl n); + end else + result := c; +end; + +function TFastReader.VarString: RawByteString; +begin + with VarBlob do + SetString(result,Ptr,Len); +end; + +procedure TFastReader.VarUTF8(out result: RawUTF8); +begin + with VarBlob do + FastSetString(result,Ptr,Len); +end; + +function TFastReader.VarUTF8: RawUTF8; +begin + with VarBlob do + FastSetString(result,Ptr,Len); +end; + +function TFastReader.VarShortString: shortstring; +begin + with VarBlob do + SetString(result,Ptr,Len); +end; + +function TFastReader.VarUTF8Safe(out Value: RawUTF8): boolean; +var len: cardinal; +begin + if VarUInt32Safe(len) then + if len=0 then + result := true else + if P+len<=Last then begin + FastSetString(Value,P,len); + inc(P,len); + result := true; + end else + result := false else + result := false; +end; + +procedure TFastReader.Read(var DA: TDynArray; NoCheckHash: boolean); +begin + P := DA.LoadFrom(P,nil,NoCheckHash); + if P=nil then + ErrorData('TDynArray.LoadFrom %',[DA.ArrayTypeShort^]); +end; + +function TFastReader.ReadVarUInt32Array(var Values: TIntegerDynArray): PtrInt; +var i: integer; + k: TFileBufferWriterKind; +begin + result := VarUInt32; + SetLength(Values,result); + Copy(k,1); + if k=wkUInt32 then begin + Copy(Values[0],result*4); + exit; + end; + Next(4); // format: Isize+varUInt32s + case k of + wkVarInt32: for i := 0 to result-1 do Values[i] := VarInt32; + wkVarUInt32: for i := 0 to result-1 do Values[i] := VarUInt32; + else ErrorData('ReadVarUInt32Array: unhandled kind=%', [ord(k)]); + end; +end; + +function TFastReader.ReadCompressed(Load: TAlgoCompressLoad; BufferOffset: integer): RawByteString; +var comp: PAnsiChar; + complen: PtrUInt; +begin + complen := VarUInt32; + comp := Next(complen); + TAlgoCompress.Algo(comp,complen).Decompress(comp,complen,result,Load,BufferOffset); +end; + + +{ TSynPersistentStore } + +constructor TSynPersistentStore.Create(const aName: RawUTF8); +begin + Create; + fName := aName; +end; + +constructor TSynPersistentStore.CreateFrom(const aBuffer: RawByteString; + aLoad: TAlgoCompressLoad); +begin + CreateFromBuffer(pointer(aBuffer),length(aBuffer),aLoad); +end; + +constructor TSynPersistentStore.CreateFromBuffer( + aBuffer: pointer; aBufferLen: integer; aLoad: TAlgoCompressLoad); +begin + Create(''); + LoadFrom(aBuffer,aBufferLen,aLoad); +end; + +constructor TSynPersistentStore.CreateFromFile(const aFileName: TFileName; + aLoad: TAlgoCompressLoad); +begin + Create(''); + LoadFromFile(aFileName,aLoad); +end; + +procedure TSynPersistentStore.LoadFromReader; +begin + fReader.VarUTF8(fName); +end; + +procedure TSynPersistentStore.SaveToWriter(aWriter: TFileBufferWriter); +begin + aWriter.Write(fName); +end; + +procedure TSynPersistentStore.LoadFrom(const aBuffer: RawByteString; + aLoad: TAlgoCompressLoad); +begin + if aBuffer <> '' then + LoadFrom(pointer(aBuffer),length(aBuffer),aLoad); +end; + +procedure TSynPersistentStore.LoadFrom(aBuffer: pointer; aBufferLen: integer; + aLoad: TAlgoCompressLoad); +var localtemp: RawByteString; + p: pointer; + temp: PRawByteString; +begin + if (aBuffer=nil) or (aBufferLen<=0) then + exit; // nothing to load + fLoadFromLastAlgo := TAlgoCompress.Algo(aBuffer,aBufferLen); + if fLoadFromLastAlgo = nil then + fReader.ErrorData('%.LoadFrom unknown TAlgoCompress AlgoID=%', + [self,PByteArray(aBuffer)[4]]); + temp := fReaderTemp; + if temp=nil then + temp := @localtemp; + p := fLoadFromLastAlgo.Decompress(aBuffer,aBufferLen,fLoadFromLastUncompressed,temp^,aLoad); + if p=nil then + fReader.ErrorData('%.LoadFrom %.Decompress failed',[self,fLoadFromLastAlgo]); + fReader.Init(p,fLoadFromLastUncompressed); + LoadFromReader; +end; + +function TSynPersistentStore.LoadFromFile(const aFileName: TFileName; + aLoad: TAlgoCompressLoad): boolean; +var temp: RawByteString; +begin + temp := StringFromFile(aFileName); + result := temp<>''; + if result then + LoadFrom(temp,aLoad); +end; + +procedure TSynPersistentStore.SaveTo(out aBuffer: RawByteString; nocompression: boolean; + BufLen: integer; ForcedAlgo: TAlgoCompress; BufferOffset: integer); +var writer: TFileBufferWriter; + temp: array[word] of byte; +begin + if BufLen<=SizeOf(temp) then + writer := TFileBufferWriter.Create(TRawByteStringStream,@temp,SizeOf(temp)) else + writer := TFileBufferWriter.Create(TRawByteStringStream,BufLen); + try + SaveToWriter(writer); + fSaveToLastUncompressed := writer.TotalWritten; + aBuffer := writer.FlushAndCompress(nocompression,ForcedAlgo,BufferOffset); + finally + writer.Free; + end; +end; + +function TSynPersistentStore.SaveTo(nocompression: boolean; BufLen: integer; + ForcedAlgo: TAlgoCompress; BufferOffset: integer): RawByteString; +begin + SaveTo(result,nocompression,BufLen,ForcedAlgo,BufferOffset); +end; + +function TSynPersistentStore.SaveToFile(const aFileName: TFileName; + nocompression: boolean; BufLen: integer; ForcedAlgo: TAlgoCompress): PtrUInt; +var temp: RawByteString; +begin + SaveTo(temp,nocompression,BufLen,ForcedAlgo); + if FileFromString(temp,aFileName) then + result := length(temp) else + result := 0; +end; + + +{ TSynPersistentStoreJson } + +procedure TSynPersistentStoreJson.AddJSON(W: TTextWriter); +begin + W.AddPropJSONString('name', fName); +end; + +function TSynPersistentStoreJson.SaveToJSON(reformat: TTextWriterJSONFormat): RawUTF8; +var + W: TTextWriter; +begin + W := TTextWriter.CreateOwnedStream(65536); + try + W.Add('{'); + AddJSON(W); + W.CancelLastComma; + W.Add('}'); + W.SetText(result, reformat); + finally + W.Free; + end; +end; + + +{ TRawByteStringGroup } + +procedure TRawByteStringGroup.Add(const aItem: RawByteString); +begin + if Values=nil then + Clear; // ensure all fields are initialized, even if on stack + if Count=Length(Values) then + SetLength(Values,NextGrow(Count)); + with Values[Count] do begin + Position := self.Position; + Value := aItem; + end; + LastFind := Count; + inc(Count); + inc(Position,Length(aItem)); +end; + +procedure TRawByteStringGroup.Add(aItem: pointer; aItemLen: integer); +var tmp: RawByteString; +begin + SetString(tmp,PAnsiChar(aItem),aItemLen); + Add(tmp); +end; + +{$ifndef DELPHI5OROLDER} // circumvent Delphi 5 compiler bug + +procedure TRawByteStringGroup.Add(const aAnother: TRawByteStringGroup); +var i: integer; + s,d: PRawByteStringGroupValue; +begin + if aAnother.Values=nil then + exit; + if Values=nil then + Clear; // ensure all fields are initialized, even if on stack + if Count+aAnother.Count>Length(Values) then + SetLength(Values,Count+aAnother.Count); + s := pointer(aAnother.Values); + d := @Values[Count]; + for i := 1 to aAnother.Count do begin + d^.Position := Position; + d^.Value := s^.Value; + inc(Position,length(s^.Value)); + inc(s); + inc(d); + end; + inc(Count,aAnother.Count); + LastFind := Count-1; +end; + +procedure TRawByteStringGroup.RemoveLastAdd; +begin + if Count>0 then begin + dec(Count); + dec(Position,Length(Values[Count].Value)); + Values[Count].Value := ''; // release memory + LastFind := Count-1; + end; +end; + +function TRawByteStringGroup.Equals(const aAnother: TRawByteStringGroup): boolean; +begin + if ((Values=nil) and (aAnother.Values<>nil)) or ((Values<>nil) and (aAnother.Values=nil)) or + (Position<>aAnother.Position) then + result := false else + if (Count<>1) or (aAnother.Count<>1) or (Values[0].Value<>aAnother.Values[0].Value) then + result := AsText=aAnother.AsText else + result := true; +end; + +{$endif DELPHI5OROLDER} + +procedure TRawByteStringGroup.Clear; +begin + Values := nil; + Position := 0; + Count := 0; + LastFind := 0; +end; + +procedure TRawByteStringGroup.AppendTextAndClear(var aDest: RawByteString); +var d,i: integer; + v: PRawByteStringGroupValue; +begin + d := length(aDest); + SetLength(aDest,d+Position); + v := pointer(Values); + for i := 1 to Count do begin + {$ifdef FPC}Move{$else}MoveFast{$endif}( + pointer(v^.Value)^,PByteArray(aDest)[d+v^.Position],length(v^.Value)); + inc(v); + end; + Clear; +end; + +function TRawByteStringGroup.AsText: RawByteString; +begin + if Values=nil then + result := '' else begin + if Count>1 then + Compact; + result := Values[0].Value; + end; +end; + +procedure TRawByteStringGroup.Compact; +var i: integer; + v: PRawByteStringGroupValue; + tmp: RawByteString; +begin + if (Values<>nil) and (Count>1) then begin + SetString(tmp,nil,Position); + v := pointer(Values); + for i := 1 to Count do begin + {$ifdef FPC}Move{$else}MoveFast{$endif}( + pointer(v^.Value)^,PByteArray(tmp)[v^.Position],length(v^.Value)); + {$ifdef FPC}Finalize(v^.Value){$else}v^.Value := ''{$endif}; // free chunks + inc(v); + end; + Values[0].Value := tmp; // use result for absolute compaction ;) + if Count>128 then + SetLength(Values,128); + Count := 1; + LastFind := 0; + end; +end; + +function TRawByteStringGroup.AsBytes: TByteDynArray; +var i: integer; +begin + result := nil; + if Values=nil then + exit; + SetLength(result,Position); + for i := 0 to Count-1 do + with Values[i] do + {$ifdef FPC}Move{$else}MoveFast{$endif}( + pointer(Value)^,PByteArray(result)[Position],length(Value)); +end; + +procedure TRawByteStringGroup.Write(W: TTextWriter; Escape: TTextWriterKind); +var i: integer; +begin + if Values<>nil then + for i := 0 to Count-1 do + with Values[i] do + W.Add(PUTF8Char(pointer(Value)),length(Value),Escape); +end; + +procedure TRawByteStringGroup.WriteBinary(W: TFileBufferWriter); +var i: integer; +begin + if Values<>nil then + for i := 0 to Count-1 do + W.WriteBinary(Values[i].Value); +end; + +procedure TRawByteStringGroup.WriteString(W: TFileBufferWriter); +begin + if Values=nil then begin + W.Write1(0); + exit; + end; + W.WriteVarUInt32(Position); + WriteBinary(W); +end; + +procedure TRawByteStringGroup.AddFromReader(var aReader: TFastReader); +var complexsize: integer; +begin + complexsize := aReader.VarUInt32; + if complexsize>0 then // directly create a RawByteString from aReader buffer + Add(aReader.Next(complexsize),complexsize); +end; + +function TRawByteStringGroup.Find(aPosition: integer): PRawByteStringGroupValue; +var i: integer; +begin + if (pointer(Values)<>nil) and (cardinal(aPosition)=result^.Position) and (aPositionaPosition then begin + dec(result); + LastFind := i; + exit; + end else + inc(result); + dec(result); + LastFind := Count-1; + end + else + result := nil; +end; + +function TRawByteStringGroup.Find(aPosition, aLength: integer): pointer; +var P: PRawByteStringGroupValue; + i: integer; +label found; +begin + if (pointer(Values)<>nil) and (cardinal(aPosition)=0) and (i+aLengthaPosition then begin + LastFind := i; +found: dec(P); + dec(aPosition,P^.Position); + if aLength-aPosition<=length(P^.Value) then + result := @PByteArray(P^.Value)[aPosition] else + result := nil; + exit; + end else + inc(P); + LastFind := Count-1; + goto found; + end + else + result := nil; +end; + +procedure TRawByteStringGroup.FindAsText(aPosition, aLength: integer; out aText: RawByteString); +var P: PRawByteStringGroupValue; +begin + P := Find(aPosition); + if P=nil then + exit; + dec(aPosition,P^.Position); + if (aPosition=0) and (length(P^.Value)=aLength) then + aText := P^.Value else // direct return if not yet compacted + if aLength-aPosition<=length(P^.Value) then + SetString(aText,PAnsiChar(@PByteArray(P^.Value)[aPosition]),aLength); +end; + +function TRawByteStringGroup.FindAsText(aPosition, aLength: integer): RawByteString; +begin + FindAsText(aPosition,aLength,result); +end; + +{$ifndef NOVARIANTS} +procedure TRawByteStringGroup.FindAsVariant(aPosition, aLength: integer; out aDest: variant); +var tmp: RawByteString; +begin + tmp := FindAsText(aPosition,aLength); + if tmp <> '' then + RawUTF8ToVariant(tmp,aDest); +end; +{$endif NOVARIANTS} + +procedure TRawByteStringGroup.FindWrite(aPosition, aLength: integer; + W: TTextWriter; Escape: TTextWriterKind; TrailingCharsToIgnore: integer); +var P: pointer; +begin + P := Find(aPosition,aLength); + if P<>nil then + W.Add(PUTF8Char(P)+TrailingCharsToIgnore,aLength-TrailingCharsToIgnore,Escape); +end; + +procedure TRawByteStringGroup.FindWriteBase64(aPosition, aLength: integer; + W: TTextWriter; withMagic: boolean); +var P: pointer; +begin + P := Find(aPosition,aLength); + if P<>nil then + W.WrBase64(P,aLength,withMagic); +end; + +procedure TRawByteStringGroup.FindMove(aPosition, aLength: integer; aDest: pointer); +var P: pointer; +begin + P := Find(aPosition,aLength); + if P<>nil then + {$ifdef FPC}Move{$else}MoveFast{$endif}(P^,aDest^,aLength); +end; + + +{ ************ Security and Identifiers classes ************************** } + +{ TSynUniqueIdentifierBits } + +function TSynUniqueIdentifierBits.Counter: word; +begin + result := PWord(@Value)^ and $7fff; +end; + +function TSynUniqueIdentifierBits.ProcessID: TSynUniqueIdentifierProcess; +begin + result := (PCardinal(@Value)^ shr 15) and $ffff; +end; + +function TSynUniqueIdentifierBits.CreateTimeUnix: TUnixTime; +begin + result := Value shr 31; +end; + +{$ifndef NOVARIANTS} +function TSynUniqueIdentifierBits.AsVariant: variant; +begin + ToVariant(result); +end; + +procedure TSynUniqueIdentifierBits.ToVariant(out result: variant); +begin + TDocVariantData(result).InitObject(['Created',DateTimeToIso8601Text(CreateDateTime), + 'Identifier',ProcessID,'Counter',Counter,'Value',Value, + 'Hex',Int64ToHex(Value)],JSON_OPTIONS_FAST); +end; +{$endif NOVARIANTS} + +{$ifndef DELPHI5OROLDER} +function TSynUniqueIdentifierBits.Equal(const Another: TSynUniqueIdentifierBits): boolean; +begin + result := Value=Another.Value; +end; +{$endif} + +procedure TSynUniqueIdentifierBits.From(const AID: TSynUniqueIdentifier); +begin + Value := AID; +end; + +function TSynUniqueIdentifierBits.CreateTimeLog: TTimeLog; +begin + PTimeLogBits(@result)^.From(UnixTimeToDateTime(Value shr 31)); +end; + +function TSynUniqueIdentifierBits.CreateDateTime: TDateTime; +begin + result := UnixTimeToDateTime(Value shr 31); +end; + +function TSynUniqueIdentifierBits.ToHexa: RawUTF8; +begin + Int64ToHex(Value,result); +end; + +function TSynUniqueIdentifierBits.FromHexa(const hexa: RawUTF8): boolean; +begin + result := (Length(hexa)=16) and HexDisplayToBin(pointer(hexa),@Value,SizeOf(Value)); +end; + +procedure TSynUniqueIdentifierBits.FromDateTime(const aDateTime: TDateTime); +begin + Value := DateTimeToUnixTime(aDateTime) shl 31; +end; + +procedure TSynUniqueIdentifierBits.FromUnixTime(const aUnixTime: TUnixTime); +begin + Value := aUnixTime shl 31; +end; + + +{ TSynUniqueIdentifierGenerator } + +const // fSafe.Padding[] slots + SYNUNIQUEGEN_COMPUTECOUNT = 0; + +procedure TSynUniqueIdentifierGenerator.ComputeNew( + out result: TSynUniqueIdentifierBits); +var currentTime: cardinal; +begin + currentTime := UnixTimeUTC; // fast API (under Windows, faster than GetTickCount64) + fSafe.Lock; + try + if currentTime>fUnixCreateTime then begin + fUnixCreateTime := currentTime; + fLastCounter := 0; // reset + end; + if fLastCounter=$7fff then begin // collision (unlikely) -> cheat on timestamp + inc(fUnixCreateTime); + fLastCounter := 0; + end else + inc(fLastCounter); + result.Value := Int64(fLastCounter or fIdentifierShifted) or + (Int64(fUnixCreateTime) shl 31); + inc(fSafe.Padding[SYNUNIQUEGEN_COMPUTECOUNT].VInt64); + finally + fSafe.UnLock; + end; +end; + +function TSynUniqueIdentifierGenerator.ComputeNew: Int64; +begin + ComputeNew(PSynUniqueIdentifierBits(@result)^); +end; + +function TSynUniqueIdentifierGenerator.GetComputedCount: Int64; +begin + {$ifdef NOVARIANTS} + fSafe.Lock; + result := fSafe.Padding[SYNUNIQUEGEN_COMPUTECOUNT].VInt64; + fSafe.Unlock; + {$else} + result := fSafe.LockedInt64[SYNUNIQUEGEN_COMPUTECOUNT]; + {$endif} +end; + +procedure TSynUniqueIdentifierGenerator.ComputeFromDateTime(const aDateTime: TDateTime; + out result: TSynUniqueIdentifierBits); +begin // assume fLastCounter=0 + ComputeFromUnixTime(DateTimeToUnixTime(aDateTime),result); +end; + +procedure TSynUniqueIdentifierGenerator.ComputeFromUnixTime(const aUnixTime: TUnixTime; + out result: TSynUniqueIdentifierBits); +begin // assume fLastCounter=0 + result.Value := aUnixTime shl 31; + if self<>nil then + result.Value := result.Value or fIdentifierShifted; +end; + +constructor TSynUniqueIdentifierGenerator.Create(aIdentifier: TSynUniqueIdentifierProcess; + const aSharedObfuscationKey: RawUTF8); +var i, len: integer; + crc: cardinal; +begin + fIdentifier := aIdentifier; + fIdentifierShifted := aIdentifier shl 15; + fSafe.Init; + {$ifdef NOVARIANTS} + variant(fSafe.Padding[SYNUNIQUEGEN_COMPUTECOUNT]) := 0; + {$else} + fSafe.LockedInt64[SYNUNIQUEGEN_COMPUTECOUNT] := 0; + {$endif} + // compute obfuscation key using hash diffusion of the supplied text + len := length(aSharedObfuscationKey); + crc := crc32ctab[0,len and 1023]; + for i := 0 to high(fCrypto)+1 do begin + crc := crc32ctab[0,crc and 1023] xor crc32ctab[3,i] xor + kr32(crc,pointer(aSharedObfuscationKey),len) xor + crc32c(crc,pointer(aSharedObfuscationKey),len) xor + fnv32(crc,pointer(aSharedObfuscationKey),len); + // do not modify those hashes above or you will break obfuscation pattern! + if i<=high(fCrypto) then + fCrypto[i] := crc else + fCryptoCRC := crc; + end; + // due to the weakness of the hash algorithms used, this approach is a bit + // naive and would be broken easily with brute force - but point here is to + // hide/obfuscate public values at end-user level (e.g. when publishing URIs), + // not implement strong security, so it sounds good enough for our purpose +end; + +destructor TSynUniqueIdentifierGenerator.Destroy; +begin + fSafe.Done; + {$ifdef FPC}FillChar{$else}FillCharFast{$endif}(fCrypto,SizeOf(fCrypto),0); + fCryptoCRC := 0; + inherited Destroy; +end; + +type // compute a 24 hexadecimal chars (96 bits) obfuscated pseudo file name + TSynUniqueIdentifierObfuscatedBits = packed record + crc: cardinal; + id: TSynUniqueIdentifierBits; + end; + +function TSynUniqueIdentifierGenerator.ToObfuscated( + const aIdentifier: TSynUniqueIdentifier): TSynUniqueIdentifierObfuscated; +var bits: TSynUniqueIdentifierObfuscatedBits; + key: cardinal; +begin + result := ''; + if aIdentifier=0 then + exit; + bits.id.Value := aIdentifier; + if self=nil then + key := 0 else + key := crc32ctab[0,bits.id.ProcessID and 1023] xor fCryptoCRC; + bits.crc := crc32c(bits.id.ProcessID,@bits.id,SizeOf(bits.id)) xor key; + if self<>nil then + bits.id.Value := bits.id.Value xor PInt64(@fCrypto[high(fCrypto)-1])^; + result := BinToHex(@bits,SizeOf(bits)); +end; + +function TSynUniqueIdentifierGenerator.FromObfuscated( + const aObfuscated: TSynUniqueIdentifierObfuscated; + out aIdentifier: TSynUniqueIdentifier): boolean; +var bits: TSynUniqueIdentifierObfuscatedBits; + len: integer; + key: cardinal; +begin + result := false; + len := PosExChar('.',aObfuscated); + if len=0 then + len := Length(aObfuscated) else + dec(len); // trim right '.jpg' + if (len<>SizeOf(bits)*2) or + not SynCommons.HexToBin(pointer(aObfuscated),@bits,SizeOf(bits)) then + exit; + if self=nil then + key := 0 else begin + bits.id.Value := bits.id.Value xor PInt64(@fCrypto[high(fCrypto)-1])^; + key := crc32ctab[0,bits.id.ProcessID and 1023] xor fCryptoCRC; + end; + if crc32c(bits.id.ProcessID,@bits.id,SizeOf(bits.id)) xor key=bits.crc then begin + aIdentifier := bits.id.Value; + result := true; + end; +end; + + +{ TSynPersistentWithPassword } + +destructor TSynPersistentWithPassword.Destroy; +begin + UniqueRawUTF8(fPassword); + FillZero(fPassword); + inherited Destroy; +end; + +class function TSynPersistentWithPassword.ComputePassword(const PlainPassword: RawUTF8; + CustomKey: cardinal): RawUTF8; +var instance: TSynPersistentWithPassword; +begin + instance := TSynPersistentWithPassword.Create; + try + instance.Key := CustomKey; + instance.SetPassWordPlain(PlainPassword); + result := instance.fPassWord; + finally + instance.Free; + end; +end; + +class function TSynPersistentWithPassword.ComputePassword(PlainPassword: pointer; + PlainPasswordLen: integer; CustomKey: cardinal): RawUTF8; +begin + result := ComputePassword(BinToBase64uri(PlainPassword,PlainPasswordLen)); +end; + +class function TSynPersistentWithPassword.ComputePlainPassword(const CypheredPassword: RawUTF8; + CustomKey: cardinal; const AppSecret: RawUTF8): RawUTF8; +var instance: TSynPersistentWithPassword; +begin + instance := TSynPersistentWithPassword.Create; + try + instance.Key := CustomKey; + instance.fPassWord := CypheredPassword; + result := instance.GetPassWordPlainInternal(AppSecret); + finally + instance.Free; + end; +end; + +function TSynPersistentWithPassword.GetPasswordFieldAddress: pointer; +begin + result := @fPassword; +end; + +function TSynPersistentWithPassword.GetKey: cardinal; +begin + if self=nil then + result := 0 else + result := fKey xor $A5abba5A; +end; + +function TSynPersistentWithPassword.GetPassWordPlain: RawUTF8; +begin + result := GetPassWordPlainInternal(''); +end; + +function TSynPersistentWithPassword.GetPassWordPlainInternal(AppSecret: RawUTF8): RawUTF8; +var value,pass: RawByteString; + usr: RawUTF8; + i,j: integer; +begin + result := ''; + if (self=nil) or (fPassWord='') then + exit; + if Assigned(TSynPersistentWithPasswordUserCrypt) then begin + if AppSecret='' then + ToText(ClassType,AppSecret); + usr := ExeVersion.User+':'; + i := PosEx(usr,fPassword); + if (i=1) or ((i>0) and (fPassword[i-1]=',')) then begin + inc(i,length(usr)); + j := PosEx(',',fPassword,i); + if j=0 then + j := length(fPassword)+1; + Base64ToBin(@fPassword[i],j-i,pass); + if pass<>'' then + result := TSynPersistentWithPasswordUserCrypt(pass,AppSecret,false); + end else begin + i := PosExChar(':',fPassword); + if i>0 then + raise ESynException.CreateUTF8('%.GetPassWordPlain unable to retrieve the '+ + 'stored value: current user is "%", but password in % was encoded for "%"', + [self,ExeVersion.User,AppSecret,copy(fPassword,1,i-1)]); + end; + end; + if result='' then begin + value := Base64ToBin(fPassWord); + SymmetricEncrypt(GetKey,value); + result := value; + end; +end; + +procedure TSynPersistentWithPassword.SetPassWordPlain(const value: RawUTF8); +var tmp: RawByteString; +begin + if self=nil then + exit; + if value='' then begin + fPassWord := ''; + exit; + end; + SetString(tmp,PAnsiChar(value),Length(value)); // private copy + SymmetricEncrypt(GetKey,tmp); + fPassWord := BinToBase64(tmp); +end; + + +{ TSynConnectionDefinition } + +constructor TSynConnectionDefinition.CreateFromJSON(const JSON: RawUTF8; + Key: cardinal); +var privateCopy: RawUTF8; + values: array[0..4] of TValuePUTF8Char; +begin + fKey := Key; + privateCopy := JSON; + JSONDecode(privateCopy,['Kind','ServerName','DatabaseName','User','Password'],@values); + fKind := values[0].ToString; + values[1].ToUTF8(fServerName); + values[2].ToUTF8(fDatabaseName); + values[3].ToUTF8(fUser); + values[4].ToUTF8(fPassWord); +end; + +function TSynConnectionDefinition.SaveToJSON: RawUTF8; +begin + result := JSONEncode(['Kind',fKind,'ServerName',fServerName, + 'DatabaseName',fDatabaseName,'User',fUser,'Password',fPassword]); +end; + + +{ TSynAuthenticationAbstract } + +constructor TSynAuthenticationAbstract.Create; +begin + fSafe.Init; + fTokenSeed := Random32; + fSessionGenerator := abs(fTokenSeed*PPtrInt(self)^); + fTokenSeed := abs(fTokenSeed*Random32); +end; + +destructor TSynAuthenticationAbstract.Destroy; +begin + fSafe.Done; + inherited; +end; + +class function TSynAuthenticationAbstract.ComputeHash(Token: Int64; + const UserName,PassWord: RawUTF8): cardinal; +begin // rough authentication - xxHash32 is less reversible than crc32c + result := xxHash32(xxHash32(xxHash32(Token,@Token,SizeOf(Token)), + pointer(UserName),length(UserName)),pointer(Password),length(PassWord)); +end; + +function TSynAuthenticationAbstract.ComputeCredential(previous: boolean; + const UserName,PassWord: RawUTF8): cardinal; +var tok: Int64; +begin + tok := GetTickCount64 div 10000; + if previous then + dec(tok); + result := ComputeHash(tok xor fTokenSeed,UserName,PassWord); +end; + +function TSynAuthenticationAbstract.CurrentToken: Int64; +begin + result := (GetTickCount64 div 10000) xor fTokenSeed; +end; + +procedure TSynAuthenticationAbstract.AuthenticateUser(const aName, aPassword: RawUTF8); +begin + raise ESynException.CreateFmt('%.AuthenticateUser() is not implemented',[self]); +end; + +procedure TSynAuthenticationAbstract.DisauthenticateUser(const aName: RawUTF8); +begin + raise ESynException.CreateFmt('%.DisauthenticateUser() is not implemented',[self]); +end; + +function TSynAuthenticationAbstract.CreateSession(const User: RawUTF8; Hash: cardinal): integer; +var password: RawUTF8; +begin + result := 0; + fSafe.Lock; + try + // check the given Hash challenge, against stored credentials + if not GetPassword(User,password) then + exit; + if (ComputeCredential(false,User,password)<>Hash) and + (ComputeCredential(true,User,password)<>Hash) then + exit; + // create the new session + repeat + result := fSessionGenerator; + inc(fSessionGenerator); + until result<>0; + AddSortedInteger(fSessions,fSessionsCount,result); + finally + fSafe.UnLock; + end; +end; + +function TSynAuthenticationAbstract.SessionExists(aID: integer): boolean; +begin + fSafe.Lock; + try + result := FastFindIntegerSorted(pointer(fSessions),fSessionsCount-1,aID)>=0; + finally + fSafe.UnLock; + end; +end; + +procedure TSynAuthenticationAbstract.RemoveSession(aID: integer); +var i: integer; +begin + fSafe.Lock; + try + i := FastFindIntegerSorted(pointer(fSessions),fSessionsCount-1,aID); + if i>=0 then + DeleteInteger(fSessions,fSessionsCount,i); + finally + fSafe.UnLock; + end; +end; + + +{ TSynAuthentication } + +constructor TSynAuthentication.Create(const aUserName,aPassword: RawUTF8); +begin + inherited Create; + fCredentials.Init(true); + if aUserName<>'' then + AuthenticateUser(aUserName,aPassword); +end; + +function TSynAuthentication.GetPassword(const UserName: RawUTF8; + out Password: RawUTF8): boolean; +var i: integer; +begin // caller did protect this method via fSafe.Lock + i := fCredentials.Find(UserName); + if i<0 then begin + result := false; + exit; + end; + password := fCredentials.List[i].Value; + result := true; +end; + +function TSynAuthentication.GetUsersCount: integer; +begin + fSafe.Lock; + try + result := fCredentials.Count; + finally + fSafe.UnLock; + end; +end; + +procedure TSynAuthentication.AuthenticateUser(const aName, aPassword: RawUTF8); +begin + fSafe.Lock; + try + fCredentials.Add(aName,aPassword); + finally + fSafe.UnLock; + end; +end; + +procedure TSynAuthentication.DisauthenticateUser(const aName: RawUTF8); +begin + fSafe.Lock; + try + fCredentials.Delete(aName); + finally + fSafe.UnLock; + end; +end; + + +{ ************ Database types and classes ************************** } + +{$ifdef FPC}{$push}{$endif} +{$WARNINGS OFF} // yes, we know there will be dead code below: we rely on it ;) + +function IsZero(const Fields: TSQLFieldBits): boolean; +var f: TPtrIntArray absolute Fields; +begin + {$ifdef CPU64} + if MAX_SQLFIELDS=64 then + result := (f[0]=0) else + if MAX_SQLFields=128 then + result := (f[0]=0) and (f[1]=0) else + if MAX_SQLFields=192 then + result := (f[0]=0) and (f[1]=0) and (f[2]=0) else + if MAX_SQLFields=256 then + result := (f[0]=0) and (f[1]=0) and (f[2]=0) and (f[3]=0) else + {$else} + if MAX_SQLFIELDS=64 then + result := (f[0]=0) and (f[1]=0) else + if MAX_SQLFields=128 then + result := (f[0]=0) and (f[1]=0) and (f[2]=0) and (f[3]=0) else + if MAX_SQLFields=192 then + result := (f[0]=0) and (f[1]=0) and (f[2]=0) and (f[3]=0) + and (f[4]=0) and (f[5]=0) else + if MAX_SQLFields=256 then + result := (f[0]=0) and (f[1]=0) and (f[2]=0) and (f[3]=0) + and (f[4]=0) and (f[5]=0) and (f[6]=0) and (f[7]=0) else + {$endif} + result := IsZero(@Fields,SizeOf(Fields)) +end; + +function IsEqual(const A,B: TSQLFieldBits): boolean; +var a_: TPtrIntArray absolute A; + b_: TPtrIntArray absolute B; +begin + {$ifdef CPU64} + if MAX_SQLFIELDS=64 then + result := (a_[0]=b_[0]) else + if MAX_SQLFields=128 then + result := (a_[0]=b_[0]) and (a_[1]=b_[1]) else + if MAX_SQLFields=192 then + result := (a_[0]=b_[0]) and (a_[1]=b_[1]) and (a_[2]=b_[2]) else + if MAX_SQLFields=256 then + result := (a_[0]=b_[0]) and (a_[1]=b_[1]) and (a_[2]=b_[2]) and (a_[3]=b_[3]) else + {$else} + if MAX_SQLFIELDS=64 then + result := (a_[0]=b_[0]) and (a_[1]=b_[1]) else + if MAX_SQLFields=128 then + result := (a_[0]=b_[0]) and (a_[1]=b_[1]) and (a_[2]=b_[2]) and (a_[3]=b_[3]) else + if MAX_SQLFields=192 then + result := (a_[0]=b_[0]) and (a_[1]=b_[1]) and (a_[2]=b_[2]) and (a_[3]=b_[3]) + and (a_[4]=b_[4]) and (a_[5]=b_[5]) else + if MAX_SQLFields=256 then + result := (a_[0]=b_[0]) and (a_[1]=b_[1]) and (a_[2]=b_[2]) and (a_[3]=b_[3]) + and (a_[4]=b_[4]) and (a_[5]=b_[5]) and (a_[6]=b_[6]) and (a_[7]=b_[7]) else + {$endif} + result := CompareMemFixed(@A,@B,SizeOf(TSQLFieldBits)) +end; + +procedure FillZero(var Fields: TSQLFieldBits); +begin + if MAX_SQLFIELDS=64 then + PInt64(@Fields)^ := 0 else + if MAX_SQLFields=128 then begin + PInt64Array(@Fields)^[0] := 0; + PInt64Array(@Fields)^[1] := 0; + end else + if MAX_SQLFields=192 then begin + PInt64Array(@Fields)^[0] := 0; + PInt64Array(@Fields)^[1] := 0; + PInt64Array(@Fields)^[2] := 0; + end else + if MAX_SQLFields=256 then begin + PInt64Array(@Fields)^[0] := 0; + PInt64Array(@Fields)^[1] := 0; + PInt64Array(@Fields)^[2] := 0; + PInt64Array(@Fields)^[3] := 0; + end else + {$ifdef FPC}FillChar{$else}FillCharFast{$endif}(Fields,SizeOf(Fields),0); +end; + +{$ifdef FPC}{$pop}{$else}{$WARNINGS ON}{$endif} + +procedure FieldBitsToIndex(const Fields: TSQLFieldBits; var Index: TSQLFieldIndexDynArray; + MaxLength,IndexStart: integer); +var i,n: integer; + sets: array[0..MAX_SQLFIELDS-1] of TSQLFieldIndex; // to avoid memory reallocation +begin + n := 0; + for i := 0 to MaxLength-1 do + if i in Fields then begin + sets[n] := i; + inc(n); + end; + SetLength(Index,IndexStart+n); + for i := 0 to n-1 do + Index[IndexStart+i] := sets[i]; +end; + +function FieldBitsToIndex(const Fields: TSQLFieldBits; + MaxLength: integer): TSQLFieldIndexDynArray; +begin + FieldBitsToIndex(Fields,result,MaxLength); +end; + +function AddFieldIndex(var Indexes: TSQLFieldIndexDynArray; Field: integer): integer; +begin + result := length(Indexes); + SetLength(Indexes,result+1); + Indexes[result] := Field; +end; + +function SearchFieldIndex(var Indexes: TSQLFieldIndexDynArray; Field: integer): integer; +begin + for result := 0 to length(Indexes)-1 do + if Indexes[result]=Field then + exit; + result := -1; +end; + +procedure FieldIndexToBits(const Index: TSQLFieldIndexDynArray; var Fields: TSQLFieldBits); +var i: integer; +begin + {$ifdef FPC}FillChar{$else}FillCharFast{$endif}(Fields,SizeOf(Fields),0); + for i := 0 to Length(Index)-1 do + if Index[i]>=0 then + include(Fields,Index[i]); +end; + +function FieldIndexToBits(const Index: TSQLFieldIndexDynArray): TSQLFieldBits; +begin + FieldIndexToBits(Index,result); +end; + +function DateToSQL(Date: TDateTime): RawUTF8; +begin + if Date<=0 then + result := '' else begin + SetLength(result,13); + PCardinal(pointer(result))^ := JSON_SQLDATE_MAGIC; + DateToIso8601PChar(Date,PUTF8Char(pointer(result))+3,True); + end; +end; + +function DateToSQL(Year,Month,Day: Cardinal): RawUTF8; +begin + if (Year=0) or (Month-1>11) or (Day-1>30) then + result := '' else begin + SetLength(result,13); + PCardinal(pointer(result))^ := JSON_SQLDATE_MAGIC; + DateToIso8601PChar(PUTF8Char(pointer(result))+3,True,Year,Month,Day); + end; +end; + +var + JSON_SQLDATE_MAGIC_TEXT: RawUTF8; + +function DateTimeToSQL(DT: TDateTime; WithMS: boolean): RawUTF8; +begin + if DT<=0 then + result := '' else begin + if frac(DT)=0 then + result := JSON_SQLDATE_MAGIC_TEXT+DateToIso8601(DT,true) else + if trunc(DT)=0 then + result := JSON_SQLDATE_MAGIC_TEXT+TimeToIso8601(DT,true,'T',WithMS) else + result := JSON_SQLDATE_MAGIC_TEXT+DateTimeToIso8601(DT,true,'T',WithMS); + end; +end; + +function TimeLogToSQL(const Timestamp: TTimeLog): RawUTF8; +begin + if Timestamp=0 then + result := '' else + result := JSON_SQLDATE_MAGIC_TEXT+PTimeLogBits(@Timestamp)^.Text(true); +end; + +function Iso8601ToSQL(const S: RawByteString): RawUTF8; +begin + if IsIso8601(pointer(S),length(S)) then + result := JSON_SQLDATE_MAGIC_TEXT+S else + result := ''; +end; + +function SQLToDateTime(const ParamValueWithMagic: RawUTF8): TDateTime; +begin + result := Iso8601ToDateTimePUTF8Char(PUTF8Char(pointer(ParamValueWithMagic))+3, + length(ParamValueWithMagic)-3); +end; + +const + NULL_LOW = ord('n')+ord('u')shl 8+ord('l')shl 16+ord('l')shl 24; + +function SQLParamContent(P: PUTF8Char; out ParamType: TSQLParamType; out ParamValue: RawUTF8; + out wasNull: boolean): PUTF8Char; +var PBeg: PAnsiChar; + L: integer; + c: cardinal; +begin + ParamType := sptUnknown; + wasNull := false; + result := nil; + if P=nil then + exit; + while (P^<=' ') and (P^<>#0) do inc(P); + case P^ of + '''','"': begin + P := UnQuoteSQLStringVar(P,ParamValue); + if P=nil then + exit; // not a valid quoted string (e.g. unexpected end in middle of it) + ParamType := sptText; + L := length(ParamValue)-3; + if L>0 then begin + c := PInteger(ParamValue)^ and $00ffffff; + if c=JSON_BASE64_MAGIC then begin + // ':("\uFFF0base64encodedbinary"):' format -> decode + Base64MagicDecode(ParamValue); // wrapper function to avoid temp. string + ParamType := sptBlob; + end else + if (c=JSON_SQLDATE_MAGIC) and // handle ':("\uFFF112012-05-04"):' format + IsIso8601(PUTF8Char(pointer(ParamValue))+3,L) then begin + Delete(ParamValue,1,3); // return only ISO-8601 text + ParamType := sptDateTime; // identified as Date/Time + end; + end; + end; + '-','+','0'..'9': begin // allow 0 or + in SQL + // check if P^ is a true numerical value + PBeg := pointer(P); + ParamType := sptInteger; + repeat inc(P) until not (P^ in ['0'..'9']); // check digits + if P^='.' then begin + inc(P); + if P^ in ['0'..'9'] then begin + ParamType := sptFloat; + repeat inc(P) until not (P^ in ['0'..'9']); // check fractional digits + end else begin + ParamType := sptUnknown; // invalid '23023.' value + exit; + end; + end; + if byte(P^) and $DF=ord('E') then begin + ParamType := sptFloat; + inc(P); + if P^='+' then inc(P) else + if P^='-' then inc(P); + while P^ in ['0'..'9'] do inc(P); + end; + FastSetString(ParamValue,PBeg,P-PBeg); + end; + 'n': + if PInteger(P)^=NULL_LOW then begin + inc(P,4); + wasNull := true; + end else + exit; // invalid content (only :(null): expected) + else + exit; // invalid content + end; + while (P^<=' ') and (P^<>#0) do inc(P); + if PWord(P)^<>Ord(')')+Ord(':')shl 8 then + // we expect finishing with P^ pointing at '):' + ParamType := sptUnknown else + // result<>nil only if value content in P^ + result := P+2; +end; + +function ExtractInlineParameters(const SQL: RawUTF8; + var Types: TSQLParamTypeDynArray; var Values: TRawUTF8DynArray; + var maxParam: integer; var Nulls: TSQLFieldBits): RawUTF8; +var ppBeg: integer; + P, Gen: PUTF8Char; + wasNull: boolean; +begin + maxParam := 0; + {$ifdef FPC}FillChar{$else}FillCharFast{$endif}(Nulls,SizeOf(Nulls),0); + ppBeg := PosEx(RawUTF8(':('),SQL,1); + if (ppBeg=0) or (PosEx(RawUTF8('):'),SQL,ppBeg+2)=0) then begin + // SQL code with no valid :(...): internal parameters -> leave maxParam=0 + result := SQL; + exit; + end; + // compute GenericSQL from SQL, converting :(...): into ? + FastSetString(result,pointer(SQL),length(SQL)); // private copy for unescape + P := pointer(result); // in-place string unescape (keep SQL untouched) + Gen := P+ppBeg-1; // Gen^ just before :( + inc(P,ppBeg+1); // P^ just after :( + repeat + Gen^ := '?'; // replace :(...): by ? + inc(Gen); + if length(Values)<=maxParam then + SetLength(Values,maxParam+16); + if length(Types)<=maxParam then + SetLength(Types,maxParam+64); + P := SQLParamContent(P,Types[maxParam],Values[maxParam],wasNull); + if P=nil then begin + maxParam := 0; + result := SQL; + exit; // any invalid parameter -> try direct SQL + end; + if wasNull then + include(Nulls,maxParam); + while (P^<>#0) and (PWord(P)^<>Ord(':')+Ord('(')shl 8) do begin + Gen^ := P^; + inc(Gen); + inc(P); + end; + if P^=#0 then + Break; + inc(P,2); + inc(maxParam); + until false; + // return generic SQL statement, with ? place-holders and params in Values[] + SetLength(result,Gen-pointer(result)); + inc(maxParam); +end; + +function InlineParameter(ID: Int64): shortstring; +begin + FormatShort(':(%):',[ID],result); +end; + +function InlineParameter(const value: RawUTF8): RawUTF8; +begin + QuotedStrJSON(value,result,':(','):'); +end; + +function SQLVarLength(const Value: TSQLVar): integer; +begin + case Value.VType of + ftBlob: + result := Value.VBlobLen; + ftUTF8: + result := StrLen(Value.VText); // fast enough for our purpose + else + result := 0; // simple/ordinal values, or ftNull + end; +end; + +{$ifndef NOVARIANTS} + +procedure VariantToSQLVar(const Input: variant; var temp: RawByteString; + var Output: TSQLVar); +var wasString: boolean; +begin + Output.Options := []; + with TVarData(Input) do + if VType=varVariant or varByRef then + VariantToSQLVar(PVariant(VPointer)^,temp,Output) else + case VType of + varEmpty, varNull: + Output.VType := ftNull; + varByte: begin + Output.VType := ftInt64; + Output.VInt64 := VByte; + end; + varInteger: begin + Output.VType := ftInt64; + Output.VInt64 := VInteger; + end; + {$ifndef DELPHI5OROLDER} + varLongWord: begin + Output.VType := ftInt64; + Output.VInt64 := VLongWord; + end; + {$endif} + varWord64, varInt64: begin + Output.VType := ftInt64; + Output.VInt64 := VInt64; + end; + varSingle: begin + Output.VType := ftDouble; + Output.VDouble := VSingle; + end; + varDouble: begin // varDate would be converted into ISO8601 by VariantToUTF8() + Output.VType := ftDouble; + Output.VDouble := VDouble; + end; + varCurrency: begin + Output.VType := ftCurrency; + Output.VInt64 := VInt64; + end; + varString: begin // assume RawUTF8 + Output.VType := ftUTF8; + Output.VText := VPointer; + end; + else // handle less current cases + if VariantToInt64(Input,Output.VInt64) then + Output.VType := ftInt64 else begin + VariantToUTF8(Input,RawUTF8(temp),wasString); + if wasString then begin + Output.VType := ftUTF8; + Output.VText := pointer(temp); + end else + Output.VType := ftNull; + end; + end; +end; + +function VariantTypeToSQLDBFieldType(const V: Variant): TSQLDBFieldType; +var tmp: TVarData; +begin + with TVarData(V) do + case VType of + varEmpty: + result := ftUnknown; + varNull: + result := ftNull; + {$ifndef DELPHI5OROLDER}varShortInt, varWord, varLongWord,{$endif} + varSmallInt, varByte, varBoolean, varInteger, varInt64, varWord64: + result := ftInt64; + varSingle,varDouble: + result := ftDouble; + varDate: + result := ftDate; + varCurrency: + result := ftCurrency; + varString: + if (VString<>nil) and (PCardinal(VString)^ and $ffffff=JSON_BASE64_MAGIC) then + result := ftBlob else + result := ftUTF8; + else + if SetVariantUnRefSimpleValue(V,tmp) then + result := VariantTypeToSQLDBFieldType(variant(tmp)) else + result := ftUTF8; + end; +end; + +{$endif NOVARIANTS} + +{ TJSONWriter } + +procedure TJSONWriter.CancelAllVoid; +const VOIDARRAY: PAnsiChar = '[]'#10; + VOIDFIELD: PAnsiChar = '{"FieldCount":0}'; +begin + CancelAll; // rewind JSON + if fExpand then // same as sqlite3_get_table() + inc(fTotalFileSize,fStream.Write(VOIDARRAY^,3)) else + inc(fTotalFileSize,fStream.Write(VOIDFIELD^,16)); +end; + +constructor TJSONWriter.Create(aStream: TStream; Expand, withID: boolean; + const Fields: TSQLFieldBits; aBufSize: integer); +begin + Create(aStream,Expand,withID,FieldBitsToIndex(Fields),aBufSize); +end; + +constructor TJSONWriter.Create(aStream: TStream; Expand, withID: boolean; + const Fields: TSQLFieldIndexDynArray; aBufSize: integer); +begin + if aStream=nil then + CreateOwnedStream else + inherited Create(aStream,aBufSize); + fExpand := Expand; + fWithID := withID; + fFields := Fields; +end; + +procedure TJSONWriter.AddColumns(aKnownRowsCount: integer); +var i: integer; +begin + if fExpand then begin + if twoForceJSONExtended in CustomOptions then + for i := 0 to High(ColNames) do + ColNames[i] := ColNames[i]+':' else + for i := 0 to High(ColNames) do + ColNames[i] := '"'+ColNames[i]+'":'; + end else begin + AddShort('{"fieldCount":'); + Add(length(ColNames)); + if aKnownRowsCount>0 then begin + AddShort(',"rowCount":'); + Add(aKnownRowsCount); + end; + AddShort(',"values":["'); + // first row is FieldNames + for i := 0 to High(ColNames) do begin + AddString(ColNames[i]); + AddNoJSONEscape(PAnsiChar('","'),3); + end; + CancelLastChar('"'); + fStartDataPosition := fStream.Position+(B-fTempBuf); + // B := buf-1 at startup -> need ',val11' position in + // "values":["col1","col2",val11,' i.e. current pos without the ',' + end; +end; + +procedure TJSONWriter.ChangeExpandedFields(aWithID: boolean; + const aFields: TSQLFieldIndexDynArray); +begin + if not Expand then + raise ESynException.CreateUTF8( + '%.ChangeExpandedFields() called with Expanded=false',[self]); + fWithID := aWithID; + fFields := aFields; +end; + +procedure TJSONWriter.EndJSONObject(aKnownRowsCount,aRowsCount: integer; + aFlushFinal: boolean); +begin + CancelLastComma; // cancel last ',' + Add(']'); + if not fExpand then begin + if aKnownRowsCount=0 then begin + AddShort(',"rowCount":'); + Add(aRowsCount); + end; + Add('}'); + end; + Add(#10); + if aFlushFinal then + FlushFinal; +end; + +procedure TJSONWriter.TrimFirstRow; +var P, PBegin, PEnd: PUTF8Char; +begin + if (self=nil) or not fStream.InheritsFrom(TMemoryStream) or + fExpand or (fStartDataPosition=0) then + exit; + // go to begin of first row + FlushToStream; // we need the data to be in fStream memory + // PBegin^=val11 in { "fieldCount":1,"values":["col1","col2",val11,"val12",val21,..] } + PBegin := TMemoryStream(fStream).Memory; + PEnd := PBegin+fStream.Position; + PEnd^ := #0; // mark end of current values + inc(PBegin,fStartDataPosition+1); // +1 to include ',' of ',val11' + // jump to end of first row + P := GotoNextJSONItem(PBegin,length(ColNames)); + if P=nil then exit; // unexpected end + // trim first row data + if P^<>#0 then + {$ifdef FPC}Move{$else}MoveFast{$endif}(P^,PBegin^,PEnd-P); // erase content + fStream.Seek(PBegin-P,soCurrent); // adjust current stream position +end; + + +{ ************ Expression Search Engine ************************** } + +function ToText(r: TExprParserResult): PShortString; +begin + result := GetEnumName(TypeInfo(TExprParserResult), ord(r)); +end; + +function ToUTF8(r: TExprParserResult): RawUTF8; +begin + result := UnCamelCase(TrimLeftLowerCaseShort(ToText(r))); +end; + + +{ TExprNode } + +function TExprNode.Append(node: TExprNode): boolean; +begin + result := node <> nil; + if result then + Last.fNext := node; +end; + +constructor TExprNode.Create(nodeType: TExprNodeType); +begin + inherited Create; + fNodeType := nodeType; +end; + +destructor TExprNode.Destroy; +begin + fNext.Free; + inherited Destroy; +end; + +function TExprNode.Last: TExprNode; +begin + result := self; + while result.Next <> nil do + result := result.Next; +end; + + +{ TParserAbstract } + +constructor TParserAbstract.Create; +begin + inherited Create; + Initialize; +end; + +destructor TParserAbstract.Destroy; +begin + Clear; + inherited Destroy; +end; + +procedure TParserAbstract.Clear; +begin + fWordCount := 0; + fWords := nil; + fExpression := ''; + FreeAndNil(fFirstNode); +end; + +function TParserAbstract.ParseExpr: TExprNode; +begin + result := ParseFactor; + ParseNextCurrentWord; + if (fCurrentWord = '') or (fCurrentWord = ')') then + exit; + if IdemPropNameU(fCurrentWord, fAndWord) then begin // w1 & w2 = w1 AND w2 + ParseNextCurrentWord; + if result.Append(ParseExpr) then + result.Append(TExprNode.Create(entAnd)); + exit; + end + else if IdemPropNameU(fCurrentWord, fOrWord) then begin // w1 + w2 = w1 OR w2 + ParseNextCurrentWord; + if result.Append(ParseExpr) then + result.Append(TExprNode.Create(entOr)); + exit; + end + else if fNoWordIsAnd and result.Append(ParseExpr) then // 'w1 w2' = 'w1 & w2' + result.Append(TExprNode.Create(entAnd)); +end; + +function TParserAbstract.ParseFactor: TExprNode; +begin + if fCurrentError <> eprSuccess then + result := nil + else if IdemPropNameU(fCurrentWord, fNotWord) then begin + ParseNextCurrentWord; + result := ParseFactor; + if fCurrentError <> eprSuccess then + exit; + result.Append(TExprNode.Create(entNot)); + end + else + result := ParseTerm; +end; + +function TParserAbstract.ParseTerm: TExprNode; +begin + result := nil; + if fCurrentError <> eprSuccess then + exit; + if fCurrentWord = '(' then begin + ParseNextCurrentWord; + result := ParseExpr; + if fCurrentError <> eprSuccess then + exit; + if fCurrentWord <> ')' then begin + FreeAndNil(result); + fCurrentError := eprMissingParenthesis; + end; + end + else if fCurrentWord = '' then begin + result := nil; + fCurrentError := eprMissingFinalWord; + end + else + try // calls meta-class overriden constructor + result := fWordClass.Create(self, fCurrentWord); + fCurrentError := TExprNodeWordAbstract(result).ParseWord; + if fCurrentError <> eprSuccess then begin + FreeAndNil(result); + exit; + end; + SetLength(fWords, fWordCount + 1); + fWords[fWordCount] := TExprNodeWordAbstract(result); + inc(fWordCount); + except + FreeAndNil(result); + fCurrentError := eprInvalidExpression; + end; +end; + +function TParserAbstract.Parse(const aExpression: RawUTF8): TExprParserResult; +var + depth: integer; + n: TExprNode; +begin + Clear; + fCurrentError := eprSuccess; + fCurrent := pointer(aExpression); + ParseNextCurrentWord; + if fCurrentWord = '' then begin + result := eprNoExpression; + exit; + end; + fFirstNode := ParseExpr; + result := fCurrentError; + if result = eprSuccess then begin + depth := 0; + n := fFirstNode; + while n <> nil do begin + case n.NodeType of + entWord: begin + inc(depth); + if depth > high(fFoundStack) then begin + result := eprTooManyParenthesis; + break; + end; + end; + entOr, entAnd: + dec(depth); + end; + n := n.Next; + end; + end; + if result = eprSuccess then + fExpression := aExpression + else + Clear; + fCurrent := nil; +end; + +class function TParserAbstract.ParseError(const aExpression: RawUTF8): RawUTF8; +var + parser: TParserAbstract; + res: TExprParserResult; +begin + parser := Create; + try + res := parser.Parse(aExpression); + if res = eprSuccess then + result := '' + else + result := ToUTF8(res); + finally + parser.Free; + end; +end; + +function TParserAbstract.Execute: boolean; +var + n: TExprNode; + st: PBoolean; +begin // code below compiles very efficiently on FPC/x86-64 + st := @fFoundStack; + n := fFirstNode; + repeat + case n.NodeType of + entWord: begin + st^ := TExprNodeWordAbstract(n).fFound; + inc(st); // see eprTooManyParenthesis above to avoid buffer overflow + end; + entNot: + PAnsiChar(st)[-1] := AnsiChar(ord(PAnsiChar(st)[-1]) xor 1); + entOr: begin + dec(st); + PAnsiChar(st)[-1] := AnsiChar(st^ or boolean(PAnsiChar(st)[-1])); + end; { TODO : optimize TExprParser OR when left member is already TRUE } + entAnd: begin + dec(st); + PAnsiChar(st)[-1] := AnsiChar(st^ and boolean(PAnsiChar(st)[-1])); + end; + end; + n := n.Next; + until n = nil; + result := boolean(PAnsiChar(st)[-1]); +end; + + +{ TExprParserAbstract } + +procedure TExprParserAbstract.Initialize; +begin + fAndWord := '&'; + fOrWord := '+'; + fNotWord := '-'; + fNoWordIsAnd := true; +end; + +procedure TExprParserAbstract.ParseNextCurrentWord; +var + P: PUTF8Char; +begin + fCurrentWord := ''; + P := fCurrent; + if P = nil then + exit; + while P^ in [#1..' '] do + inc(P); + if P^ = #0 then + exit; + if P^ in PARSER_STOPCHAR then begin + FastSetString(fCurrentWord, P, 1); + fCurrent := P + 1; + end + else begin + fCurrent := P; + ParseNextWord; + end; +end; + +procedure TExprParserAbstract.ParseNextWord; +const + STOPCHAR = PARSER_STOPCHAR + [#0, ' ']; +var + P: PUTF8Char; +begin + P := fCurrent; + while not(P^ in STOPCHAR) do + inc(P); + FastSetString(fCurrentWord, fCurrent, P - fCurrent); + fCurrent := P; +end; + + +{ TExprNodeWordAbstract } + +constructor TExprNodeWordAbstract.Create(aOwner: TParserAbstract; const aWord: RawUTF8); +begin + inherited Create(entWord); + fWord := aWord; + fOwner := aOwner; +end; + + +{ TExprParserMatchNode } + +type + TExprParserMatchNode = class(TExprNodeWordAbstract) + protected + fMatch: TMatch; + function ParseWord: TExprParserResult; override; + end; + PExprParserMatchNode = ^TExprParserMatchNode; + +function TExprParserMatchNode.ParseWord: TExprParserResult; +begin + fMatch.Prepare(fWord, (fOwner as TExprParserMatch).fCaseSensitive, {reuse=}true); + result := eprSuccess; +end; + + +{ TExprParserMatch } + +constructor TExprParserMatch.Create(aCaseSensitive: boolean); +begin + inherited Create; + fCaseSensitive := aCaseSensitive; +end; + +procedure TExprParserMatch.Initialize; +begin + inherited Initialize; + fWordClass := TExprParserMatchNode; +end; + +function TExprParserMatch.Search(const aText: RawUTF8): boolean; +begin + result := Search(pointer(aText), length(aText)); +end; + +function TExprParserMatch.Search(aText: PUTF8Char; aTextLen: PtrInt): boolean; +const // rough estimation of UTF-8 characters + IS_UTF8_WORD = ['0' .. '9', 'A' .. 'Z', 'a' .. 'z', #$80 ..#$ff]; +var + P, PEnd: PUTF8Char; + n: PtrInt; +begin + P := aText; + if (P = nil) or (fWords = nil) then begin + result := false; + exit; + end; + if fMatchedLastSet > 0 then begin + n := fWordCount; + repeat + dec(n); + fWords[n].fFound := false; + until n = 0; + fMatchedLastSet := 0; + end; + PEnd := P + aTextLen; + while (P < PEnd) and (fMatchedLastSet < fWordCount) do begin + while not(P^ in IS_UTF8_WORD) do begin + inc(P); + if P = PEnd then + break; + end; + if P = PEnd then + break; + aText := P; + repeat + inc(P); + until (P = PEnd) or not(P^ in IS_UTF8_WORD); + aTextLen := P - aText; + n := fWordCount; + repeat + dec(n); + with TExprParserMatchNode(fWords[n]) do + if not fFound and fMatch.Match(aText, aTextLen) then begin + fFound := true; + inc(fMatchedLastSet); + end; + until n = 0; + end; + result := Execute; +end; + + +{ ************ Multi-Threading classes ************************** } + +{ TPendingTaskList } + +constructor TPendingTaskList.Create; +begin + inherited Create; + fTasks.InitSpecific(TypeInfo(TPendingTaskListItemDynArray),fTask,djInt64,@fCount); +end; + +function TPendingTaskList.GetTimestamp: Int64; +begin + result := {$ifdef FPCLINUX}SynFPCLinux.{$endif}GetTickCount64; +end; + +procedure TPendingTaskList.AddTask(aMilliSecondsDelayFromNow: integer; + const aTask: RawByteString); +var item: TPendingTaskListItem; + ndx: integer; +begin + item.Timestamp := GetTimestamp+aMilliSecondsDelayFromNow; + item.Task := aTask; + fSafe.Lock; + try + if fTasks.FastLocateSorted(item,ndx) then + inc(ndx); // always insert just after any existing timestamp + fTasks.FastAddSorted(ndx,item); + finally + fSafe.UnLock; + end; +end; + +procedure TPendingTaskList.AddTasks( + const aMilliSecondsDelays: array of integer; + const aTasks: array of RawByteString); +var item: TPendingTaskListItem; + i,ndx: integer; +begin + if length(aTasks)<>length(aMilliSecondsDelays) then + exit; + item.Timestamp := GetTimestamp; + fSafe.Lock; + try + for i := 0 to High(aTasks) do begin + inc(item.Timestamp,aMilliSecondsDelays[i]); + item.Task := aTasks[i]; + if fTasks.FastLocateSorted(item,ndx) then + inc(ndx); // always insert just after any existing timestamp + fTasks.FastAddSorted(ndx,item); + end; + finally + fSafe.UnLock; + end; +end; + +function TPendingTaskList.GetCount: integer; +begin + if self=nil then + result := 0 else begin + fSafe.Lock; + try + result := fCount; + finally + fSafe.UnLock; + end; + end; +end; + +function TPendingTaskList.NextPendingTask: RawByteString; +begin + result := ''; + if (self=nil) or (fCount=0) then + exit; + fSafe.Lock; + try + if fCount>0 then + if GetTimestamp>=fTask[0].Timestamp then begin + result := fTask[0].Task; + fTasks.FastDeleteSorted(0); + end; + finally + fSafe.UnLock; + end; +end; + +procedure TPendingTaskList.Clear; +begin + if (self=nil) or (fCount=0) then + exit; + fSafe.Lock; + try + fTasks.Clear; + finally + fSafe.UnLock; + end; +end; + + +{$ifndef LVCL} // LVCL does not implement TEvent + +{ TSynBackgroundThreadAbstract } + +constructor TSynBackgroundThreadAbstract.Create(const aThreadName: RawUTF8; + OnBeforeExecute,OnAfterExecute: TNotifyThreadEvent; CreateSuspended: boolean); +begin + fProcessEvent := TEvent.Create(nil,false,false,''); + fThreadName := aThreadName; + fOnBeforeExecute := OnBeforeExecute; + fOnAfterExecute := OnAfterExecute; + inherited Create(CreateSuspended{$ifdef FPC},512*1024{$endif}); // DefaultStackSize=512KB +end; + +{$ifndef HASTTHREADSTART} +procedure TSynBackgroundThreadAbstract.Start; +begin + Resume; +end; +{$endif} + +{$ifndef HASTTHREADTERMINATESET} +procedure TSynBackgroundThreadAbstract.Terminate; +begin + inherited Terminate; // FTerminated := True + TerminatedSet; +end; +{$endif} + +procedure TSynBackgroundThreadAbstract.TerminatedSet; +begin + fProcessEvent.SetEvent; // ExecuteLoop should handle Terminated flag +end; + +procedure TSynBackgroundThreadAbstract.WaitForNotExecuting(maxMS: integer); +var endtix: Int64; +begin + if fExecute = exRun then begin + endtix := SynCommons.GetTickCount64+maxMS; + repeat + Sleep(1); // wait for Execute to finish + until (fExecute <> exRun) or (SynCommons.GetTickCount64>=endtix); + end; +end; + +destructor TSynBackgroundThreadAbstract.Destroy; +begin + if fExecute = exRun then begin + Terminate; + WaitForNotExecuting(100); + end; + inherited Destroy; + FreeAndNil(fProcessEvent); +end; + +procedure TSynBackgroundThreadAbstract.SetExecuteLoopPause(dopause: boolean); +begin + if Terminated or (dopause=fExecuteLoopPause) or (fExecute=exFinished) then + exit; + fExecuteLoopPause := dopause; + fProcessEvent.SetEvent; // notify Execute main loop +end; + +procedure TSynBackgroundThreadAbstract.Execute; +begin + try + if fThreadName='' then + SetCurrentThreadName('%(%)',[self,pointer(self)]) else + SetCurrentThreadName('%',[fThreadName]); + if Assigned(fOnBeforeExecute) then + fOnBeforeExecute(self); + try + fExecute := exRun; + while not Terminated do + if fExecuteLoopPause then + FixedWaitFor(fProcessEvent,100) else + ExecuteLoop; + finally + if Assigned(fOnAfterExecute) then + fOnAfterExecute(self); + end; + finally + fExecute := exFinished; + end; +end; + +{ TSynBackgroundThreadMethodAbstract } + +constructor TSynBackgroundThreadMethodAbstract.Create(aOnIdle: TOnIdleSynBackgroundThread; + const aThreadName: RawUTF8; OnBeforeExecute,OnAfterExecute: TNotifyThreadEvent); +begin + fOnIdle := aOnIdle; // cross-platform may run Execute as soon as Create is called + fCallerEvent := TEvent.Create(nil,false,false,''); + fPendingProcessLock.Init; + inherited Create(aThreadName,OnBeforeExecute,OnAfterExecute); +end; + +destructor TSynBackgroundThreadMethodAbstract.Destroy; +begin + SetPendingProcess(flagDestroying); + fProcessEvent.SetEvent; // notify terminated + FixedWaitForever(fCallerEvent); // wait for actual termination + FreeAndNil(fCallerEvent); + inherited Destroy; + fPendingProcessLock.Done; +end; + +function TSynBackgroundThreadMethodAbstract.GetPendingProcess: TSynBackgroundThreadProcessStep; +begin + fPendingProcessLock.Lock; + result := fPendingProcessFlag; + fPendingProcessLock.UnLock; +end; + +procedure TSynBackgroundThreadMethodAbstract.SetPendingProcess(State: TSynBackgroundThreadProcessStep); +begin + fPendingProcessLock.Lock; + fPendingProcessFlag := State; + fPendingProcessLock.UnLock; +end; + +procedure TSynBackgroundThreadMethodAbstract.ExecuteLoop; +{$ifndef DELPHI5OROLDER} +var E: TObject; +{$endif} +begin + case FixedWaitFor(fProcessEvent,INFINITE) of + wrSignaled: + case GetPendingProcess of + flagDestroying: begin + fCallerEvent.SetEvent; // abort caller thread process + Terminate; // forces Execute loop ending + exit; + end; + flagStarted: + if not Terminated then + if fExecuteLoopPause then // pause -> try again later + fProcessEvent.SetEvent else + try + fBackgroundException := nil; + try + if Assigned(fOnBeforeProcess) then + fOnBeforeProcess(self); + try + Process; + finally + if Assigned(fOnAfterProcess) then + fOnAfterProcess(self); + end; + except + {$ifdef DELPHI5OROLDER} + on E: Exception do + fBackgroundException := ESynException.CreateUTF8( + 'Redirected %: "%"',[E,E.Message]); + {$else} + E := AcquireExceptionObject; + if E.InheritsFrom(Exception) then + fBackgroundException := Exception(E); + {$endif} + end; + finally + SetPendingProcess(flagFinished); + fCallerEvent.SetEvent; + end; + end; + end; +end; + +function TSynBackgroundThreadMethodAbstract.AcquireThread: TSynBackgroundThreadProcessStep; +begin + fPendingProcessLock.Lock; + try + result := fPendingProcessFlag; + if result=flagIdle then begin // we just acquired the thread! congrats! + fPendingProcessFlag := flagStarted; // atomic set "started" flag + fCallerThreadID := ThreadID; + end; + finally + fPendingProcessLock.UnLock; + end; +end; + +function TSynBackgroundThreadMethodAbstract.OnIdleProcessNotify(start: Int64): integer; +begin + result := {$ifdef FPCLINUX}SynFPCLinux.{$else}SynCommons.{$endif}GetTickCount64-start; + if result<0 then + result := MaxInt; // should happen only under XP -> ignore + if Assigned(fOnIdle) then + fOnIdle(self,result) ; +end; + +procedure TSynBackgroundThreadMethodAbstract.WaitForFinished(start: Int64; + const onmainthreadidle: TNotifyEvent); +var E: Exception; +begin + if (self=nil) or not(fPendingProcessFlag in [flagStarted, flagFinished]) then + exit; // nothing to wait for + try + if Assigned(onmainthreadidle) then begin + while FixedWaitFor(fCallerEvent,100)=wrTimeout do + onmainthreadidle(self); + end else + {$ifdef MSWINDOWS} // do process the OnIdle only if UI + if Assigned(fOnIdle) then begin + while FixedWaitFor(fCallerEvent,100)=wrTimeout do + OnIdleProcessNotify(start); + end else + {$endif} + FixedWaitForever(fCallerEvent); + if fPendingProcessFlag<>flagFinished then + ESynException.CreateUTF8('%.WaitForFinished: flagFinished?',[self]); + if fBackgroundException<>nil then begin + E := fBackgroundException; + fBackgroundException := nil; + raise E; // raise background exception in the calling scope + end; + finally + fParam := nil; + fCallerThreadID := 0; + FreeAndNil(fBackgroundException); + SetPendingProcess(flagIdle); + if Assigned(fOnIdle) then + fOnIdle(self,-1); // notify finished + end; +end; + +function TSynBackgroundThreadMethodAbstract.RunAndWait(OpaqueParam: pointer): boolean; +var start: Int64; + ThreadID: TThreadID; +begin + result := false; + ThreadID := GetCurrentThreadId; + if (self=nil) or (ThreadID=fCallerThreadID) then + // avoid endless loop when waiting in same thread (e.g. UI + OnIdle) + exit; + // 1. wait for any previous request to be finished (should not happen often) + if Assigned(fOnIdle) then + fOnIdle(self,0); // notify started + start := {$ifdef FPCLINUX}SynFPCLinux.{$else}SynCommons.{$endif}GetTickCount64; + repeat + case AcquireThread of + flagDestroying: + exit; + flagIdle: + break; // we acquired the background thread + end; + case OnIdleProcessNotify(start) of // Windows.GetTickCount64 res is 10-16 ms + 0..20: SleepHiRes(0); + 21..100: SleepHiRes(1); + 101..900: SleepHiRes(5); + else SleepHiRes(50); + end; + until false; + // 2. process execution in the background thread + fParam := OpaqueParam; + fProcessEvent.SetEvent; // notify background thread for Call pending process + WaitForFinished(start,nil); // wait for flagFinished, then set flagIdle + result := true; +end; + +function TSynBackgroundThreadMethodAbstract.GetOnIdleBackgroundThreadActive: boolean; +begin + result := (self<>nil) and Assigned(fOnIdle) and (GetPendingProcess<>flagIdle); +end; + + +{ TSynBackgroundThreadEvent } + +constructor TSynBackgroundThreadEvent.Create(aOnProcess: TOnProcessSynBackgroundThread; + aOnIdle: TOnIdleSynBackgroundThread; const aThreadName: RawUTF8); +begin + inherited Create(aOnIdle,aThreadName); + fOnProcess := aOnProcess; +end; + +procedure TSynBackgroundThreadEvent.Process; +begin + if not Assigned(fOnProcess) then + raise ESynException.CreateUTF8('Invalid %.RunAndWait() call',[self]); + fOnProcess(self,fParam); +end; + + +{ TSynBackgroundThreadMethod } + +procedure TSynBackgroundThreadMethod.Process; +var Method: ^TThreadMethod; +begin + if fParam=nil then + raise ESynException.CreateUTF8('Invalid %.RunAndWait() call',[self]); + Method := fParam; + Method^(); +end; + +procedure TSynBackgroundThreadMethod.RunAndWait(Method: TThreadMethod); +var Met: TMethod absolute Method; +begin + inherited RunAndWait(@Met); +end; + + +{ TSynBackgroundThreadProcedure } + +constructor TSynBackgroundThreadProcedure.Create(aOnProcess: TOnProcessSynBackgroundThreadProc; + aOnIdle: TOnIdleSynBackgroundThread; const aThreadName: RawUTF8); +begin + inherited Create(aOnIdle,aThreadName); + fOnProcess := aOnProcess; +end; + +procedure TSynBackgroundThreadProcedure.Process; +begin + if not Assigned(fOnProcess) then + raise ESynException.CreateUTF8('Invalid %.RunAndWait() call',[self]); + fOnProcess(fParam); +end; + + +{ TSynParallelProcessThread } + +procedure TSynParallelProcessThread.Process; +begin + if not Assigned(fMethod) then + exit; + fMethod(fIndexStart,fIndexStop); + fMethod := nil; +end; + +procedure TSynParallelProcessThread.Start( + Method: TSynParallelProcessMethod; IndexStart, IndexStop: integer); +begin + fMethod := Method; + fIndexStart := IndexStart; + fIndexStop := IndexStop; + fProcessEvent.SetEvent; // notify execution +end; + + +{ TSynBackgroundThreadProcess } + +constructor TSynBackgroundThreadProcess.Create(const aThreadName: RawUTF8; + aOnProcess: TOnSynBackgroundThreadProcess; aOnProcessMS: cardinal; + aOnBeforeExecute, aOnAfterExecute: TNotifyThreadEvent; + aStats: TSynMonitorClass; CreateSuspended: boolean); +begin + if not Assigned(aOnProcess) then + raise ESynException.CreateUTF8('%.Create(aOnProcess=nil)',[self]); + if aStats<>nil then + fStats := aStats.Create(aThreadName); + fOnProcess := aOnProcess; + fOnProcessMS := aOnProcessMS; + if fOnProcessMS=0 then + fOnProcessMS := INFINITE; // wait until ProcessEvent.SetEvent or Terminated + inherited Create(aThreadName,aOnBeforeExecute,aOnAfterExecute,CreateSuspended); +end; + +destructor TSynBackgroundThreadProcess.Destroy; +begin + if fExecute=exRun then begin + Terminate; + WaitForNotExecuting(10000); // expect the background task to be finished + end; + inherited Destroy; + fStats.Free; +end; + +procedure TSynBackgroundThreadProcess.ExecuteLoop; +var wait: TWaitResult; +begin + wait := FixedWaitFor(fProcessEvent,fOnProcessMS); + if not Terminated and (wait in [wrSignaled,wrTimeout]) then + if fExecuteLoopPause then // pause -> try again later + fProcessEvent.SetEvent else + try + if fStats<>nil then + fStats.ProcessStartTask; + try + fOnProcess(self,wait); + finally + if fStats<>nil then + fStats.ProcessEnd; + end; + except + on E: Exception do begin + if fStats<>nil then + fStats.ProcessErrorRaised(E); + if Assigned(fOnException) then + fOnException(E); + end; + end; +end; + + +{ TSynBackgroundTimer } + +var + ProcessSystemUse: TSystemUse; + +constructor TSynBackgroundTimer.Create(const aThreadName: RawUTF8; + aOnBeforeExecute, aOnAfterExecute: TNotifyThreadEvent; aStats: TSynMonitorClass); +begin + fTasks.Init(TypeInfo(TSynBackgroundTimerTaskDynArray),fTask); + fTaskLock.Init; + {$ifndef NOVARIANTS} + fTaskLock.LockedBool[0] := false; + {$endif} + inherited Create(aThreadName,EverySecond,1000,aOnBeforeExecute,aOnAfterExecute,aStats); +end; + +destructor TSynBackgroundTimer.Destroy; +begin + if (ProcessSystemUse<>nil) and (ProcessSystemUse.fTimer=self) then + ProcessSystemUse.fTimer := nil; // allows processing by another background timer + inherited Destroy; + fTaskLock.Done; +end; + +const + TIXPRECISION = 32; // GetTickCount64 resolution (for aOnProcessSecs=1) + +procedure TSynBackgroundTimer.EverySecond( + Sender: TSynBackgroundThreadProcess; Event: TWaitResult); +var tix: Int64; + i,f,n: integer; + t: ^TSynBackgroundTimerTask; + todo: TSynBackgroundTimerTaskDynArray; // avoid lock contention +begin + if (fTask=nil) or Terminated then + exit; + tix := {$ifdef FPCLINUX}SynFPCLinux.{$else}SynCommons.{$endif}GetTickCount64; + n := 0; + fTaskLock.Lock; + try + variant(fTaskLock.Padding[0]) := true; + try + for i := 0 to length(fTask)-1 do begin + t := @fTask[i]; + if tix>=t^.NextTix then begin + SetLength(todo,n+1); + todo[n] := t^; + inc(n); + t^.FIFO := nil; // now owned by todo[n].FIFO + t^.NextTix := tix+((t^.Secs*1000)-TIXPRECISION); + end; + end; + finally + fTaskLock.UnLock; + end; + for i := 0 to n-1 do + with todo[i] do + if FIFO<>nil then + for f := 0 to length(FIFO)-1 do + try + OnProcess(self,Event,FIFO[f]); + except + end + else + try + OnProcess(self,Event,''); + except + end; + finally + {$ifdef NOVARIANTS} + fTaskLock.Lock; + variant(fTaskLock.Padding[0]) := false; + fTaskLock.UnLock; + {$else} + fTaskLock.LockedBool[0] := false; + {$endif} + end; +end; + +function TSynBackgroundTimer.Find(const aProcess: TMethod): integer; +begin // caller should have made fTaskLock.Lock; + for result := length(fTask)-1 downto 0 do + with TMethod(fTask[result].OnProcess) do + if (Code=aProcess.Code) and (Data=aProcess.Data) then + exit; + result := -1; +end; + +procedure TSynBackgroundTimer.Enable( + aOnProcess: TOnSynBackgroundTimerProcess; aOnProcessSecs: cardinal); +var task: TSynBackgroundTimerTask; + found: integer; +begin + if (self=nil) or Terminated or not Assigned(aOnProcess) then + exit; + if aOnProcessSecs=0 then begin + Disable(aOnProcess); + exit; + end; + task.OnProcess := aOnProcess; + task.Secs := aOnProcessSecs; + task.NextTix := {$ifdef FPCLINUX}SynFPCLinux.{$else}SynCommons.{$endif}GetTickCount64+ + (aOnProcessSecs*1000-TIXPRECISION); + fTaskLock.Lock; + try + found := Find(TMethod(aOnProcess)); + if found>=0 then + fTask[found] := task else + fTasks.Add(task); + finally + fTaskLock.UnLock; + end; +end; + +function TSynBackgroundTimer.Processing: boolean; +begin + {$ifdef NOVARIANTS} + with fTaskLock.Padding[0] do + result := (VType=varBoolean) and VBoolean; + {$else} + result := fTaskLock.LockedBool[0]; + {$endif} +end; + +procedure TSynBackgroundTimer.WaitUntilNotProcessing(timeoutsecs: integer); +var timeout: Int64; +begin + if not Processing then + exit; + timeout := {$ifdef FPCLINUX}SynFPCLinux.{$else}SynCommons.{$endif}GetTickCount64+timeoutsecs*1000; + repeat + SleepHiRes(1); + until not Processing or + ({$ifdef FPCLINUX}SynFPCLinux.{$else}SynCommons.{$endif}GetTickcount64>timeout); +end; + +function TSynBackgroundTimer.ExecuteNow(aOnProcess: TOnSynBackgroundTimerProcess): boolean; +begin + result := Add(aOnProcess,#0,true); +end; + +function TSynBackgroundTimer.EnQueue(aOnProcess: TOnSynBackgroundTimerProcess; + const aMsg: RawUTF8; aExecuteNow: boolean): boolean; +begin + result := Add(aOnProcess,aMsg,aExecuteNow); +end; + +function TSynBackgroundTimer.EnQueue(aOnProcess: TOnSynBackgroundTimerProcess; + const aMsgFmt: RawUTF8; const Args: array of const; aExecuteNow: boolean): boolean; +var msg: RawUTF8; +begin + FormatUTF8(aMsgFmt,Args,msg); + result := Add(aOnProcess,msg,aExecuteNow); +end; + +function TSynBackgroundTimer.Add(aOnProcess: TOnSynBackgroundTimerProcess; + const aMsg: RawUTF8; aExecuteNow: boolean): boolean; +var found: integer; +begin + result := false; + if (self=nil) or Terminated or not Assigned(aOnProcess) then + exit; + fTaskLock.Lock; + try + found := Find(TMethod(aOnProcess)); + if found>=0 then begin + with fTask[found] do begin + if aExecuteNow then + NextTix := 0; + if aMsg<>#0 then + AddRawUTF8(FIFO,aMsg); + end; + if aExecuteNow then + ProcessEvent.SetEvent; + result := true; + end; + finally + fTaskLock.UnLock; + end; +end; + +function TSynBackgroundTimer.DeQueue(aOnProcess: TOnSynBackgroundTimerProcess; + const aMsg: RawUTF8): boolean; +var found: integer; +begin + result := false; + if (self=nil) or Terminated or not Assigned(aOnProcess) then + exit; + fTaskLock.Lock; + try + found := Find(TMethod(aOnProcess)); + if found>=0 then + with fTask[found] do + result := DeleteRawUTF8(FIFO,FindRawUTF8(FIFO,aMsg)); + finally + fTaskLock.UnLock; + end; +end; + +function TSynBackgroundTimer.Disable(aOnProcess: TOnSynBackgroundTimerProcess): boolean; +var found: integer; +begin + result := false; + if (self=nil) or Terminated or not Assigned(aOnProcess) then + exit; + fTaskLock.Lock; + try + found := Find(TMethod(aOnProcess)); + if found>=0 then begin + fTasks.Delete(found); + result := true; + end; + finally + fTaskLock.UnLock; + end; +end; + +{ TSynParallelProcess } + +constructor TSynParallelProcess.Create(ThreadPoolCount: integer; const ThreadName: RawUTF8; + OnBeforeExecute, OnAfterExecute: TNotifyThreadEvent; + MaxThreadPoolCount: integer); +var i: integer; +begin + inherited Create; + if ThreadPoolCount<0 then + raise ESynParallelProcess.CreateUTF8('%.Create(%,%)',[Self,ThreadPoolCount,ThreadName]); + if ThreadPoolCount>MaxThreadPoolCount then + ThreadPoolCount := MaxThreadPoolCount; + fThreadPoolCount := ThreadPoolCount; + fThreadName := ThreadName; + SetLength(fPool,fThreadPoolCount); + for i := 0 to fThreadPoolCount-1 do + fPool[i] := TSynParallelProcessThread.Create(nil,FormatUTF8('%#%/%', + [fThreadName,i+1,fThreadPoolCount]),OnBeforeExecute,OnAfterExecute); +end; + +destructor TSynParallelProcess.Destroy; +begin + ObjArrayClear(fPool); + inherited; +end; + +procedure TSynParallelProcess.ParallelRunAndWait(const Method: TSynParallelProcessMethod; + MethodCount: integer; const OnMainThreadIdle: TNotifyEvent); +var use,t,n,perthread: integer; + error: RawUTF8; +begin + if (MethodCount<=0) or not Assigned(Method) then + exit; + if not Assigned(OnMainThreadIdle) then + if (self=nil) or (MethodCount=1) or (fThreadPoolCount=0) then begin + Method(0,MethodCount-1); // no need (or impossible) to use background thread + exit; + end; + use := MethodCount; + t := fThreadPoolCount; + if not Assigned(OnMainThreadIdle) then + inc(t); // include current thread + if use>t then + use := t; + try + // start secondary threads + perthread := MethodCount div use; + if perthread=0 then + use := 1; + n := 0; + for t := 0 to use-2 do begin + repeat + case fPool[t].AcquireThread of + flagDestroying: // should not happen + raise ESynParallelProcess.CreateUTF8( + '%.ParallelRunAndWait [%] destroying',[self,fPool[t].fThreadName]); + flagIdle: + break; // acquired (should always be the case) + end; + Sleep(1); + if Assigned(OnMainThreadIdle) then + OnMainThreadIdle(self); + until false; + fPool[t].Start(Method,n,n+perthread-1); + inc(n,perthread); + inc(fParallelRunCount); + end; + // run remaining items in the current/last thread + if n'' then + raise ESynParallelProcess.CreateUTF8('%.ParallelRunAndWait: %',[self,error]); + end; +end; + + +{ TBlockingProcess } + +constructor TBlockingProcess.Create(aTimeOutMs: integer; aSafe: PSynLocker); +begin + inherited Create(nil,false,false,''); + if aTimeOutMs<=0 then + fTimeOutMs := 3000 else // never wait for ever + fTimeOutMs := aTimeOutMs; + fSafe := aSafe; +end; + +constructor TBlockingProcess.Create(aTimeOutMs: integer); +begin + fOwnedSafe := true; + Create(aTimeOutMS,NewSynLocker); +end; + +destructor TBlockingProcess.Destroy; +begin + if fOwnedSafe then + fSafe^.DoneAndFreeMem; + inherited Destroy; +end; + +function TBlockingProcess.WaitFor: TBlockingEvent; +begin + fSafe^.Lock; + try + result := fEvent; + if fEvent in [evRaised,evTimeOut] then + exit; + fEvent := evWaiting; + finally + fSafe^.UnLock; + end; + FixedWaitFor(self,fTimeOutMs); + fSafe^.Lock; + try + if fEvent<>evRaised then + fEvent := evTimeOut; + result := fEvent; + finally + fSafe^.UnLock; + end; +end; + +function TBlockingProcess.WaitFor(TimeOutMS: integer): TBlockingEvent; +begin + if TimeOutMS <= 0 then + fTimeOutMs := 3000 // never wait for ever + else + fTimeOutMs := TimeOutMS; + result := WaitFor; +end; + +function TBlockingProcess.NotifyFinished(alreadyLocked: boolean): boolean; +begin + result := false; + if not alreadyLocked then + fSafe^.Lock; + try + if fEvent in [evRaised,evTimeOut] then + exit; // ignore if already notified + fEvent := evRaised; + SetEvent; // notify caller to unlock "WaitFor" method + result := true; + finally + fSafe^.UnLock; + end; +end; + +procedure TBlockingProcess.ResetInternal; +begin + ResetEvent; + fEvent := evNone; +end; + +function TBlockingProcess.Reset: boolean; +begin + fSafe^.Lock; + try + result := fEvent<>evWaiting; + if result then + ResetInternal; + finally + fSafe^.UnLock; + end; +end; + +procedure TBlockingProcess.Lock; +begin + fSafe^.Lock; +end; + +procedure TBlockingProcess.Unlock; +begin + fSafe^.Unlock; +end; + + +{ TBlockingProcessPoolItem } + +procedure TBlockingProcessPoolItem.ResetInternal; +begin + inherited ResetInternal; // set fEvent := evNone + fCall := 0; +end; + + +{ TBlockingProcessPool } + +constructor TBlockingProcessPool.Create(aClass: TBlockingProcessPoolItemClass); +begin + inherited Create; + if aClass=nil then + fClass := TBlockingProcessPoolItem else + fClass := aClass; + fPool := TObjectListLocked.Create(true); +end; + +const + CALL_DESTROYING = -1; + +destructor TBlockingProcessPool.Destroy; +var i: integer; + someWaiting: boolean; +begin + fCallCounter := CALL_DESTROYING; + someWaiting := false; + for i := 0 to fPool.Count-1 do + with TBlockingProcessPoolItem(fPool.List[i]) do + if Event=evWaiting then begin + SetEvent; // release WaitFor (with evTimeOut) + someWaiting := true; + end; + if someWaiting then + sleep(10); // propagate the pending evTimeOut to the WaitFor threads + fPool.Free; + inherited; +end; + +function TBlockingProcessPool.NewProcess(aTimeOutMs: integer): TBlockingProcessPoolItem; +var i: integer; + p: ^TBlockingProcessPoolItem; +begin + result := nil; + if fCallCounter=CALL_DESTROYING then + exit; + if aTimeOutMs<=0 then + aTimeOutMs := 3000; // never wait for ever + fPool.Safe.Lock; + try + p := pointer(fPool.List); + for i := 1 to fPool.Count do + if p^.Call=0 then begin + result := p^; // found a non-used entry + result.fTimeOutMs := aTimeOutMS; + break; + end else + inc(p); + if result=nil then begin + result := fClass.Create(aTimeOutMS); + fPool.Add(result); + end; + inc(fCallCounter); // 1,2,3,... + result.fCall := fCallCounter; + finally + fPool.Safe.UnLock; + end; +end; + +function TBlockingProcessPool.FromCall(call: TBlockingProcessPoolCall; + locked: boolean): TBlockingProcessPoolItem; +var i: integer; + p: ^TBlockingProcessPoolItem; +begin + result := nil; + if (fCallCounter=CALL_DESTROYING) or (call<=0) then + exit; + fPool.Safe.Lock; + try + p := pointer(fPool.List); + for i := 1 to fPool.Count do + if p^.Call=call then begin + result := p^; + if locked then + result.Lock; + exit; + end else + inc(p); + finally + fPool.Safe.UnLock; + end; +end; + +{$ifdef KYLIX3} +type + // see http://stackoverflow.com/a/3085509 about this known Kylix bug + TEventHack = class(THandleObject) // should match EXACTLY SyncObjs.pas source! + private + FEvent: TSemaphore; + FManualReset: Boolean; + end; + +function FixedWaitFor(Event: TEvent; Timeout: LongWord): TWaitResult; +var E: TEventHack absolute Event; + procedure SetResult(res: integer); + begin + if res=0 then + result := wrSignaled else + if errno in [EAGAIN,ETIMEDOUT] then + result := wrTimeOut else begin + write(TimeOut,':',errno,' '); + result := wrError; + end; + end; +{.$define USESEMTRYWAIT} +// sem_timedwait() is slower than sem_trywait(), but consuming much less CPU +{$ifdef USESEMTRYWAIT} +var time: timespec; +{$else} +var start,current: Int64; + elapsed: LongWord; +{$endif} +begin + if Timeout=INFINITE then begin + SetResult(sem_wait(E.FEvent)); + exit; + end; + if TimeOut=0 then begin + SetResult(sem_trywait(E.FEvent)); + exit; + end; + {$ifdef USESEMTRYWAIT} + clock_gettime(CLOCK_REALTIME,time); + inc(time.tv_sec,TimeOut div 1000); + inc(time.tv_nsec,(TimeOut mod 1000)*1000000); + while time.tv_nsec>1000000000 do begin + inc(time.tv_sec); + dec(time.tv_nsec,1000000000); + end; + SetResult(sem_timedwait(E.FEvent,time)); + {$else} + start := GetTickCount64; + repeat + if sem_trywait(E.FEvent)=0 then begin + result := wrSignaled; + break; + end; + current := GetTickCount64; + elapsed := current-start; + if elapsed=0 then + sched_yield else + if elapsed>TimeOut then begin + result := wrTimeOut; + break; + end else + if elapsed<5 then + usleep(50) else + usleep(1000); + until false; + {$endif} + if E.FManualReset then begin + repeat until sem_trywait(E.FEvent)<>0; // reset semaphore state + sem_post(E.FEvent); + end; +end; + +{$else KYLIX3} // original FPC or Windows implementation is OK + +function FixedWaitFor(Event: TEvent; Timeout: LongWord): TWaitResult; +begin + result := Event.WaitFor(TimeOut); +end; + +{$endif KYLIX3} + +procedure FixedWaitForever(Event: TEvent); +begin + FixedWaitFor(Event,INFINITE); +end; + +{$endif LVCL} // LVCL does not implement TEvent + + +{ ************ System Analysis types and classes ************************** } + + +function SystemInfoJson: RawUTF8; +var cpu,mem: RawUTF8; +begin + cpu := TSystemUse.Current(false).HistoryText(0,15,@mem); + with SystemInfo do + result := JSONEncode([ + 'host',ExeVersion.Host,'user',ExeVersion.User,'os',OSVersionText, + 'cpu',CpuInfoText,'bios',BiosInfoText, + {$ifdef MSWINDOWS}{$ifndef CPU64}'wow64',IsWow64,{$endif}{$endif MSWINDOWS} + {$ifdef CPUINTEL}'cpufeatures', LowerCase(ToText(CpuFeatures, ' ')),{$endif} + 'processcpu',cpu,'processmem',mem, + 'freemem',TSynMonitorMemory.FreeAsText, + 'disk',GetDiskPartitionsText(false,true)]); +end; + + +{ TProcessInfo } + +{$ifdef MSWINDOWS} +type + TProcessMemoryCounters = record + cb: DWORD; + PageFaultCount: DWORD; + PeakWorkingSetSize: PtrUInt; + WorkingSetSize: PtrUInt; + QuotaPeakPagedPoolUsage: PtrUInt; + QuotaPagedPoolUsage: PtrUInt; + QuotaPeakNonPagedPoolUsage: PtrUInt; + QuotaNonPagedPoolUsage: PtrUInt; + PagefileUsage: PtrUInt; + PeakPagefileUsage: PtrUInt; + end; +const + PROCESS_QUERY_LIMITED_INFORMATION = $1000; +var + // PROCESS_QUERY_INFORMATION or PROCESS_QUERY_LIMITED_INFORMATION + OpenProcessAccess: DWORD; + // late-binding of Windows version specific API entries + GetSystemTimes: function(var lpIdleTime, lpKernelTime, lpUserTime: TFileTime): BOOL; stdcall; + GetProcessTimes: function(hProcess: THandle; + var lpCreationTime, lpExitTime, lpKernelTime, lpUserTime: TFileTime): BOOL; stdcall; + GetProcessMemoryInfo: function(Process: THandle; + var ppsmemCounters: TProcessMemoryCounters; cb: DWORD): BOOL; stdcall; + EnumProcessModules: function (hProcess: THandle; var lphModule: HMODULE; cb: DWORD; + var lpcbNeeded: DWORD): BOOL; stdcall; + EnumProcesses: function(lpidProcess: PDWORD; cb: DWORD; var cbNeeded: DWORD): BOOL; stdcall; + GetModuleFileNameExW: function(hProcess: THandle; hModule: HMODULE; + lpBaseName: PWideChar; nSize: DWORD): DWORD; stdcall; + // Vista+/WS2008+ (use GetModuleFileNameEx on XP) + QueryFullProcessImageNameW: function(hProcess: THandle; dwFlags: DWORD; + lpExeName: PWideChar; lpdwSize: PDWORD): BOOL; stdcall; + +procedure InitWindowsAPI; +var Kernel, Psapi: THandle; +begin + if OSVersion>=wVista then + OpenProcessAccess := PROCESS_QUERY_LIMITED_INFORMATION else + OpenProcessAccess := PROCESS_QUERY_INFORMATION or PROCESS_VM_READ; + Kernel := GetModuleHandle(kernel32); + @GetSystemTimes := GetProcAddress(Kernel,'GetSystemTimes'); + @GetProcessTimes := GetProcAddress(Kernel,'GetProcessTimes'); + @QueryFullProcessImageNameW := GetProcAddress(Kernel,'QueryFullProcessImageNameW'); + Psapi := LoadLibrary('Psapi.dll'); + if Psapi>=32 then begin + @EnumProcesses := GetProcAddress(Psapi,'EnumProcesses'); + @GetModuleFileNameExW := GetProcAddress(Psapi,'GetModuleFileNameExW'); + @EnumProcessModules := GetProcAddress(Psapi, 'EnumProcessModules'); + @GetProcessMemoryInfo := GetProcAddress(Psapi,'GetProcessMemoryInfo'); + end; +end; + +function EnumAllProcesses(out Count: Cardinal): TCardinalDynArray; +var n: cardinal; +begin + n := 2048; + repeat + SetLength(result, n); + if EnumProcesses(pointer(result), n * 4, Count) then + Count := Count shr 2 else + Count := 0; + if Count < n then begin + if Count = 0 then + result := nil; + exit; + end; + inc(n, 1024); // (very unlikely) too small buffer + until n>8192; +end; + +function EnumProcessName(PID: Cardinal): RawUTF8; +var h: THandle; + len: DWORD; + name: array[0..4095] of WideChar; +begin + result := ''; + if PID = 0 then + exit; + h := OpenProcess(OpenProcessAccess, false, PID); + if h <> 0 then + try + if Assigned(QueryFullProcessImageNameW) then begin + len := high(name); + if QueryFullProcessImageNameW(h, 0, name, @len) then + RawUnicodeToUtf8(name, len, result); + end else + if GetModuleFileNameExW(h,0,name,high(name))<>0 then + RawUnicodeToUtf8(name, StrLenW(name), result); + finally + CloseHandle(h); + end; +end; + +function TProcessInfo.Init: boolean; +begin + {$ifdef FPC}FillChar{$else}FillCharFast{$endif}(self,SizeOf(self),0); + result := Assigned(GetSystemTimes) and Assigned(GetProcessTimes) and + Assigned(GetProcessMemoryInfo); // no monitoring API under oldest Windows +end; + +function TProcessInfo.Start: boolean; +var ftidl,ftkrn,ftusr: TFileTime; + sidl,skrn,susr: Int64; +begin + result := Assigned(GetSystemTimes) and GetSystemTimes(ftidl,ftkrn,ftusr); + if not result then + exit; + FileTimeToInt64(ftidl,sidl); + FileTimeToInt64(ftkrn,skrn); + FileTimeToInt64(ftusr,susr); + fDiffIdle := sidl-fSysPrevIdle; + fDiffKernel := skrn-fSysPrevKernel; + fDiffUser := susr-fSysPrevUser; + fDiffTotal := fDiffKernel+fDiffUser; // kernel time also includes idle time + dec(fDiffKernel, fDiffIdle); + fSysPrevIdle := sidl; + fSysPrevKernel := skrn; + fSysPrevUser := susr; +end; + +function TProcessInfo.PerProcess(PID: cardinal; Now: PDateTime; + out Data: TSystemUseData; var PrevKernel, PrevUser: Int64): boolean; +var + h: THandle; + ftkrn,ftusr,ftp,fte: TFileTime; + pkrn,pusr: Int64; + mem: TProcessMemoryCounters; +begin + result := false; + {$ifdef FPC}FillChar{$else}FillCharFast{$endif}(Data,SizeOf(Data),0); + h := OpenProcess(OpenProcessAccess,false,PID); + if h<>0 then + try + if GetProcessTimes(h,ftp,fte,ftkrn,ftusr) then begin + if Now<>nil then + Data.Timestamp := Now^; + FileTimeToInt64(ftkrn,pkrn); + FileTimeToInt64(ftusr,pusr); + if (PrevKernel<>0) and (fDiffTotal>0) then begin + Data.Kernel := ((pkrn-PrevKernel)*100)/fDiffTotal; + Data.User := ((pusr-PrevUser)*100)/fDiffTotal; + end; + PrevKernel := pkrn; + PrevUser := pusr; + {$ifdef FPC}FillChar{$else}FillCharFast{$endif}(mem,SizeOf(mem),0); + mem.cb := SizeOf(mem); + if GetProcessMemoryInfo(h,mem,SizeOf(mem)) then begin + Data.WorkKB := mem.WorkingSetSize shr 10; + Data.VirtualKB := mem.PagefileUsage shr 10; + end; + result := true; + end; + finally + CloseHandle(h); + end; +end; + +function TProcessInfo.PerSystem(out Idle,Kernel,User: currency): boolean; +begin + if fDiffTotal<=0 then begin + Idle := 0; + Kernel := 0; + User := 0; + result := false; + end else begin + Kernel := SimpleRoundTo2Digits((fDiffKernel*100)/fDiffTotal); + User := SimpleRoundTo2Digits((fDiffUser*100)/fDiffTotal); + Idle := 100-Kernel-User; // ensure sum is always 100% + result := true; + end; +end; +{$else} // not implemented yet (use /proc ?) +function TProcessInfo.Init: boolean; +begin + {$ifdef FPC}FillChar{$else}FillCharFast{$endif}(self,SizeOf(self),0); + result := false; +end; + +function TProcessInfo.Start: boolean; +begin + result := false; +end; + +function TProcessInfo.PerProcess(PID: cardinal; Now: PDateTime; + out Data: TSystemUseData; var PrevKernel, PrevUser: Int64): boolean; +begin + result := false; +end; + +function TProcessInfo.PerSystem(out Idle,Kernel,User: currency): boolean; +var P: PUTF8Char; + U, K, I, S: cardinal; +begin // see http://www.linuxhowtos.org/System/procstat.htm + result := false; + P := pointer(StringFromFile('/proc/stat', {nosize=}true)); + if P=nil then + exit; + U := GetNextItemCardinal(P,' '){=user}+GetNextItemCardinal(P,' '){=nice}; + K := GetNextItemCardinal(P,' '){=system}; + I := GetNextItemCardinal(P,' '){=idle}; + S := U+K+I; + Kernel := SimpleRoundTo2Digits((K*100)/S); + User := SimpleRoundTo2Digits((U*100)/S); + Idle := 100-Kernel-User; // ensure sum is always 100% + result := S<>0; +end; { TODO : use a diff approach for TProcessInfo.PerSystem on Linux } +{$endif MSWINDOWS} + + +{ TSystemUse } + +procedure TSystemUse.BackgroundExecute(Sender: TSynBackgroundTimer; + Event: TWaitResult; const Msg: RawUTF8); +var i: integer; + now: TDateTime; +begin + if (fProcess=nil) or (fHistoryDepth=0) or not fProcessInfo.Start then + exit; + fTimer := Sender; + now := NowUTC; + fSafe.Lock; + try + inc(fDataIndex); + if fDataIndex>=fHistoryDepth then + fDataIndex := 0; + for i := high(fProcess) downto 0 do // backwards for fProcesses.Delete(i) + with fProcess[i] do + if fProcessInfo.PerProcess(ID,@now,Data[fDataIndex],PrevKernel,PrevUser) then begin + if Assigned(fOnMeasured) then + fOnMeasured(ID,Data[fDataIndex]); + end else + if UnsubscribeProcessOnAccessError then + // if GetLastError=ERROR_INVALID_PARAMETER then + fProcesses.Delete(i); + finally + fSafe.UnLock; + end; +end; + +procedure TSystemUse.OnTimerExecute(Sender: TObject); +begin + BackgroundExecute(nil,wrSignaled,''); +end; + +constructor TSystemUse.Create(const aProcessID: array of integer; + aHistoryDepth: integer); +var i: integer; +begin + inherited Create; + fProcesses.Init(TypeInfo(TSystemUseProcessDynArray),fProcess); + {$ifdef MSWINDOWS} + if not Assigned(GetSystemTimes) or not Assigned(GetProcessTimes) or + not Assigned(GetProcessMemoryInfo) then + exit; // no system monitoring API under oldest Windows + {$else} + exit; // not implemented yet + {$endif} + if aHistoryDepth<=0 then + aHistoryDepth := 1; + fHistoryDepth := aHistoryDepth; + SetLength(fProcess,length(aProcessID)); + for i := 0 to high(aProcessID) do begin + {$ifdef MSWINDOWS} + if aProcessID[i]=0 then + fProcess[i].ID := GetCurrentProcessID else + {$endif} + fProcess[i].ID := aProcessID[i]; + SetLength(fProcess[i].Data,fHistoryDepth); + end; +end; + +constructor TSystemUse.Create(aHistoryDepth: integer); +begin + Create([0],aHistoryDepth); +end; + +procedure TSystemUse.Subscribe(aProcessID: integer); +var i,n: integer; +begin + if self=nil then + exit; + {$ifdef MSWINDOWS} + if aProcessID=0 then + aProcessID := GetCurrentProcessID; + {$endif} + fSafe.Lock; + try + n := length(fProcess); + for i := 0 to n-1 do + if fProcess[i].ID=aProcessID then + exit; // already subscribed + SetLength(fProcess,n+1); + fProcess[n].ID := aProcessID; + SetLength(fProcess[n].Data,fHistoryDepth); + finally + fSafe.UnLock; + end; +end; + +function TSystemUse.Unsubscribe(aProcessID: integer): boolean; +var i: integer; +begin + result := false; + if self=nil then + exit; + fSafe.Lock; + try + i := ProcessIndex(aProcessID); + if i>=0 then begin + fProcesses.Delete(i); + result := true; + end; + finally + fSafe.UnLock; + end; +end; + +function TSystemUse.ProcessIndex(aProcessID: integer): integer; +begin // caller should have made fSafe.Enter + {$ifdef MSWINDOWS} + if aProcessID=0 then + aProcessID := GetCurrentProcessID; + {$endif} + if self<>nil then + for result := 0 to high(fProcess) do + if fProcess[result].ID=aProcessID then + exit; + result := -1; +end; + +function TSystemUse.Data(out aData: TSystemUseData; aProcessID: integer=0): boolean; +var i: integer; +begin + result := false; + if self<>nil then begin + fSafe.Lock; + try + i := ProcessIndex(aProcessID); + if i>=0 then begin + with fProcess[i] do + aData := Data[fDataIndex]; + result := aData.Timestamp<>0; + if result then + exit; + end; + finally + fSafe.UnLock; + end; + end; + {$ifdef FPC}FillChar{$else}FillCharFast{$endif}(aData,SizeOf(aData),0); +end; + +function TSystemUse.Data(aProcessID: integer): TSystemUseData; +begin + Data(result,aProcessID); +end; + +function TSystemUse.KB(aProcessID: integer=0): cardinal; +begin + with Data(aProcessID) do + result := WorkKB+VirtualKB; +end; + +function TSystemUse.Percent(aProcessID: integer): single; +begin + with Data(aProcessID) do + result := Kernel+User; +end; + +function TSystemUse.PercentKernel(aProcessID: integer): single; +begin + result := Data(aProcessID).Kernel; +end; + +function TSystemUse.PercentUser(aProcessID: integer): single; +begin + result := Data(aProcessID).User; +end; + +function TSystemUse.PercentSystem(out Idle,Kernel,User: currency): boolean; +begin + result := fProcessInfo.PerSystem(Idle,Kernel,User); +end; + +function TSystemUse.HistoryData(aProcessID,aDepth: integer): TSystemUseDataDynArray; +var i,n,last: integer; +begin + result := nil; + if self=nil then + exit; + fSafe.Lock; + try + i := ProcessIndex(aProcessID); + if i>=0 then + with fProcess[i] do begin + n := length(Data); + last := n-1; + if (aDepth>0) and (n>aDepth) then + n := aDepth; + SetLength(result,n); // make ordered copy + for i := 0 to n-1 do begin + if i<=fDataIndex then + result[i] := Data[fDataIndex-i] else begin + result[i] := Data[last]; + dec(last); + end; + if PInt64(@result[i].Timestamp)^=0 then begin + SetLength(result,i); // truncate to latest available sample + break; + end; + end; + end; + finally + fSafe.UnLock; + end; +end; + +function TSystemUse.History(aProcessID,aDepth: integer): TSingleDynArray; +var i,n: integer; + data: TSystemUseDataDynArray; +begin + data := HistoryData(aProcessID,aDepth); + n := length(data); + SetLength(result,n); + for i := 0 to n-1 do + result[i] := data[i].Kernel+data[i].User; +end; + +class function TSystemUse.Current(aCreateIfNone: boolean): TSystemUse; +begin + if (ProcessSystemUse=nil) and aCreateIfNone then + GarbageCollectorFreeAndNil(ProcessSystemUse,TSystemUse.Create(60)); + result := ProcessSystemUse; +end; + +function TSystemUse.HistoryText(aProcessID,aDepth: integer; + aDestMemoryMB: PRawUTF8): RawUTF8; +var data: TSystemUseDataDynArray; + mem: RawUTF8; + i: integer; +begin + result := ''; + data := HistoryData(aProcessID,aDepth); + {$ifdef LINUXNOTBSD} // bsd: see VM_LOADAVG + // https://www.retro11.de/ouxr/211bsd/usr/src/lib/libc/gen/getloadavg.c.html + if data = nil then + result := StringFromFile('/proc/loadavg',{HasNoSize=}true) else + {$endif LINUXNOTBSD} + for i := 0 to high(data) do + with data[i] do begin + result := FormatUTF8('%% ',[result,TruncTo2Digits(Kernel+User)]); + if aDestMemoryMB<>nil then + mem := FormatUTF8('%% ',[mem,TruncTo2Digits(WorkKB/1024)]); + end; + result := trim(result); + if aDestMemoryMB<>nil then + aDestMemoryMB^ := trim(mem); +end; + +{$ifndef NOVARIANTS} + +function TSystemUse.HistoryVariant(aProcessID,aDepth: integer): variant; +var res: TDocVariantData absolute result; + data: TSystemUseDataDynArray; + i: integer; +begin + VarClear(result); + data := HistoryData(aProcessID,aDepth); + res.InitFast(length(data),dvArray); + for i := 0 to high(data) do + res.AddItem(TruncTo2Digits(data[i].Kernel+data[i].User)); +end; + +{$endif NOVARIANTS} + +function SortDynArrayDiskPartitions(const A,B): integer; +begin + result := SortDynArrayString(TDiskPartition(A).mounted,TDiskPartition(B).mounted); +end; + +function GetDiskPartitions: TDiskPartitions; +{$ifdef MSWINDOWS} // DeviceIoControl(IOCTL_DISK_GET_PARTITION_INFO) requires root +var drives, drive, m, n: integer; + fn, volume: TFileName; +{$else} +var mounts, fs, mnt, typ: RawUTF8; + p: PUTF8Char; + fn: TFileName; + n: integer; +{$endif} + av, fr, tot: QWord; +begin + result := nil; + n := 0; +{$ifdef MSWINDOWS} + fn := '#:\'; + drives := GetLogicalDrives; + m := 1 shl 2; + for drive := 3 to 26 do begin // retrieve partitions mounted as C..Z drives + if drives and m <> 0 then begin + fn[1] := char(64+drive); + if GetDiskInfo(fn,av,fr,tot,@volume) then begin + SetLength(result,n+1); + StringToUTF8(volume,result[n].name); + volume := ''; + result[n].mounted := fn; + result[n].size := tot; + inc(n); + end; + end; + m := m shl 1; + end; +{$else} // see https://github.com/gagern/gnulib/blob/master/lib/mountlist.c + mounts := StringFromFile({$ifdef BSD}'/etc/mtab'{$else}'/proc/self/mounts'{$endif}, + {hasnosize=}true); + p := pointer(mounts); + repeat + fs := ''; + mnt := ''; + typ := ''; + ScanUTF8(GetNextLine(p,p),'%S %S %S',[@fs,@mnt,@typ]); + if (fs<>'') and (fs<>'rootfs') and (IdemPCharArray(pointer(fs),['/DEV/LOOP'])<0) and + (mnt<>'') and (mnt<>'/mnt') and (typ<>'') and + (IdemPCharArray(pointer(mnt),['/PROC/','/SYS/','/RUN/'])<0) and + (FindPropName(['autofs','proc','subfs','debugfs','devpts','fusectl','mqueue', + 'rpc-pipefs','sysfs','devfs','kernfs','ignore','none','tmpfs','securityfs', + 'ramfs','rootfs','devtmpfs','hugetlbfs','iso9660'],typ)<0) then begin + fn := UTF8ToString(mnt); + if GetDiskInfo(fn,av,fr,tot) and (tot>1 shl 20) then begin + //writeln('fs=',fs,' mnt=',mnt,' typ=',typ, ' av=',KB(av),' fr=',KB(fr),' tot=',KB(tot)); + SetLength(result,n+1); + result[n].name := fs; + result[n].mounted := fn; + result[n].size := tot; + inc(n); + end; + end; + until p=nil; + DynArray(TypeInfo(TDiskPartitions),result).Sort(SortDynArrayDiskPartitions); + {$endif} +end; + +var + _DiskPartitions: TDiskPartitions; + +function GetDiskPartitionsText(nocache, withfreespace, nospace: boolean): RawUTF8; +const F: array[boolean] of RawUTF8 = ({$ifdef MSWINDOWS}'%: % (% / %)', '%: % (%/%)' + {$else}'% % (% / %)', '% % (%/%)'{$endif}); +var i: integer; + parts: TDiskPartitions; + function GetInfo(var p: TDiskPartition): shortstring; + var av, fr, tot: QWord; + begin + if not withfreespace or not GetDiskInfo(p.mounted,av,fr,tot) then + {$ifdef MSWINDOWS} + FormatShort('%: % (%)',[p.mounted[1],p.name,KB(p.size,nospace)],result) else + FormatShort(F[nospace],[p.mounted[1],p.name,KB(p.size,nospace)],result); + {$else} + FormatShort('% % (%)',[p.mounted,p.name,KB(p.size,nospace)],result) else + FormatShort(F[nospace],[p.mounted,p.name,KB(fr,nospace),KB(tot,nospace)],result); + {$endif} + end; +begin + if (_DiskPartitions=nil) or nocache then + _DiskPartitions := GetDiskPartitions; + parts := _DiskPartitions; + if parts=nil then + result := '' else + ShortStringToAnsi7String(GetInfo(parts[0]),result); + for i := 1 to high(parts) do + result := FormatUTF8('%, %',[result,GetInfo(parts[i])]); +end; + +{ TSynMonitorMemory } + +constructor TSynMonitorMemory.Create(aTextNoSpace: boolean); +begin + FAllocatedUsed := TSynMonitorOneSize.Create(aTextNoSpace); + FAllocatedReserved := TSynMonitorOneSize.Create(aTextNoSpace); + FPhysicalMemoryFree := TSynMonitorOneSize.Create(aTextNoSpace); + FVirtualMemoryFree := TSynMonitorOneSize.Create(aTextNoSpace); + FPagingFileTotal := TSynMonitorOneSize.Create(aTextNoSpace); + FPhysicalMemoryTotal := TSynMonitorOneSize.Create(aTextNoSpace); + FVirtualMemoryTotal := TSynMonitorOneSize.Create(aTextNoSpace); + FPagingFileFree := TSynMonitorOneSize.Create(aTextNoSpace); +end; + +destructor TSynMonitorMemory.Destroy; +begin + FAllocatedReserved.Free; + FAllocatedUsed.Free; + FPhysicalMemoryFree.Free; + FVirtualMemoryFree.Free; + FPagingFileTotal.Free; + FPhysicalMemoryTotal.Free; + FVirtualMemoryTotal.Free; + FPagingFileFree.Free; + inherited Destroy; +end; + +class function TSynMonitorMemory.FreeAsText(nospace: boolean): ShortString; +const F: array[boolean] of RawUTF8 = ('% / %', '%/%'); +begin + with TSynMonitorMemory.Create(nospace) do + try + RetrieveMemoryInfo; + FormatShort(F[nospace],[fPhysicalMemoryFree.Text,fPhysicalMemoryTotal.Text],result); + finally + Free; + end; +end; + +var + PhysicalAsTextCache: TShort16; // this value doesn't change usually + +class function TSynMonitorMemory.PhysicalAsText(nospace: boolean): TShort16; +begin + if PhysicalAsTextCache='' then + with TSynMonitorMemory.Create(nospace) do + try + PhysicalAsTextCache := PhysicalMemoryTotal.Text; + finally + Free; + end; + result := PhysicalAsTextCache; +end; + +class function TSynMonitorMemory.ToJSON: RawUTF8; +begin + with TSynMonitorMemory.Create(false) do + try + RetrieveMemoryInfo; + FormatUTF8('{Allocated:{reserved:%,used:%},Physical:{total:%,free:%,percent:%},'+ + {$ifdef MSWINDOWS}'Virtual:{total:%,free:%},'+{$endif}'Paged:{total:%,free:%}}', + [fAllocatedReserved.Bytes shr 10,fAllocatedUsed.Bytes shr 10, + fPhysicalMemoryTotal.Bytes shr 10,fPhysicalMemoryFree.Bytes shr 10, fMemoryLoadPercent, + {$ifdef MSWINDOWS}fVirtualMemoryTotal.Bytes shr 10,fVirtualMemoryFree.Bytes shr 10,{$endif} + fPagingFileTotal.Bytes shr 10,fPagingFileFree.Bytes shr 10],result); + finally + Free; + end; +end; + +{$ifndef NOVARIANTS} +class function TSynMonitorMemory.ToVariant: variant; +begin + result := _JsonFast(ToJSON); +end; +{$endif} + +function TSynMonitorMemory.GetAllocatedUsed: TSynMonitorOneSize; +begin + RetrieveMemoryInfo; + result := FAllocatedUsed; +end; + +function TSynMonitorMemory.GetAllocatedReserved: TSynMonitorOneSize; +begin + RetrieveMemoryInfo; + result := FAllocatedReserved; +end; + +function TSynMonitorMemory.GetMemoryLoadPercent: integer; +begin + RetrieveMemoryInfo; + result := FMemoryLoadPercent; +end; + +function TSynMonitorMemory.GetPagingFileFree: TSynMonitorOneSize; +begin + RetrieveMemoryInfo; + result := FPagingFileFree; +end; + +function TSynMonitorMemory.GetPagingFileTotal: TSynMonitorOneSize; +begin + RetrieveMemoryInfo; + result := FPagingFileTotal; +end; + +function TSynMonitorMemory.GetPhysicalMemoryFree: TSynMonitorOneSize; +begin + RetrieveMemoryInfo; + result := FPhysicalMemoryFree; +end; + +function TSynMonitorMemory.GetPhysicalMemoryTotal: TSynMonitorOneSize; +begin + RetrieveMemoryInfo; + result := FPhysicalMemoryTotal; +end; + +function TSynMonitorMemory.GetVirtualMemoryFree: TSynMonitorOneSize; +begin + RetrieveMemoryInfo; + result := FVirtualMemoryFree; +end; + +function TSynMonitorMemory.GetVirtualMemoryTotal: TSynMonitorOneSize; +begin + RetrieveMemoryInfo; + result := FVirtualMemoryTotal; +end; + +{$ifdef MSWINDOWS} +{$ifndef UNICODE} // missing API for oldest Delphi +type + DWORDLONG = Int64; + TMemoryStatusEx = record + dwLength: DWORD; + dwMemoryLoad: DWORD; + ullTotalPhys: DWORDLONG; + ullAvailPhys: DWORDLONG; + ullTotalPageFile: DWORDLONG; + ullAvailPageFile: DWORDLONG; + ullTotalVirtual: DWORDLONG; + ullAvailVirtual: DWORDLONG; + ullAvailExtendedVirtual: DWORDLONG; + end; + +// information about the system's current usage of both physical and virtual memory +function GlobalMemoryStatusEx(var lpBuffer: TMemoryStatusEx): BOOL; + stdcall; external kernel32; +{$endif} +{$endif} + +function GetMemoryInfo(out info: TMemoryInfo; withalloc: boolean): boolean; +{$ifdef WITH_FASTMM4STATS} +var Heap: TMemoryManagerState; + sb: integer; +{$endif} +{$ifdef MSWINDOWS} +var global: TMemoryStatusEx; + {$ifdef FPC}mem: TProcessMemoryCounters;{$endif} +begin + FillCharFast(global,SizeOf(global),0); + global.dwLength := SizeOf(global); + result := GlobalMemoryStatusEx(global); + info.percent := global.dwMemoryLoad; + info.memtotal := global.ullTotalPhys; + info.memfree := global.ullAvailPhys; + info.filetotal := global.ullTotalPageFile; + info.filefree := global.ullAvailPageFile; + info.vmtotal := global.ullTotalVirtual; + info.vmfree := global.ullAvailVirtual; + {$ifdef FPC} // GetHeapStatus is only about current thread -> use WinAPI + if withalloc and Assigned(GetProcessMemoryInfo) then begin + FillcharFast(mem,SizeOf(mem),0); + mem.cb := SizeOf(mem); + GetProcessMemoryInfo(GetCurrentProcess,mem,SizeOf(mem)); + info.allocreserved := mem.PeakWorkingSetSize; + info.allocused := mem.WorkingSetSize; + end; + {$endif FPC} +{$else} +{$ifdef BSD} +begin + {$ifdef FPC}FillChar{$else}FillCharFast{$endif}(info,SizeOf(info),0); + info.memtotal := fpsysctlhwint({$ifdef DARWIN}HW_MEMSIZE{$else}HW_PHYSMEM{$endif}); + info.memfree := info.memtotal-fpsysctlhwint(HW_USERMEM); + if info.memtotal<>0 then // avoid div per 0 exception + info.percent := ((info.memtotal-info.memfree)*100)div info.memtotal; +{$else} +var si: TSysInfo; // Linuxism + P: PUTF8Char; + {$ifdef FPC}mu: cardinal{$else}const mu=1{$endif}; +begin + FillCharFast(info,SizeOf(info),0); + {$ifdef FPC} + result := SysInfo(@si)=0; + mu := si.mem_unit; + {$else} + result := SysInfo(si)=0; // some missing fields in Kylix' Libc + {$endif} + if si.totalram<>0 then // avoid div per 0 exception + info.percent := ((si.totalram-si.freeram)*100)div si.totalram; + info.memtotal := si.totalram*mu; + info.memfree := si.freeram*mu; + info.filetotal := si.totalswap*mu; + info.filefree := si.freeswap*mu; + if withalloc then begin + // virtual memory information is not available under Linux + P := pointer(StringFromFile('/proc/self/statm',{hasnosize=}true)); + info.allocreserved := GetNextItemCardinal(P,' ')*SystemInfo.dwPageSize; // VmSize + info.allocused := GetNextItemCardinal(P,' ')*SystemInfo.dwPageSize; // VmRSS + end; + // GetHeapStatus is only about current thread -> use /proc/[pid]/statm +{$endif BSD} +{$endif MSWINDOWS} +{$ifdef WITH_FASTMM4STATS} // override OS information by actual FastMM4 + if withalloc then begin + GetMemoryManagerState(Heap); // direct raw FastMM4 access + info.allocused := Heap.TotalAllocatedMediumBlockSize+Heap.TotalAllocatedLargeBlockSize; + info.allocreserved := Heap.ReservedMediumBlockAddressSpace+Heap.ReservedLargeBlockAddressSpace; + for sb := 0 to high(Heap.SmallBlockTypeStates) do + with Heap.SmallBlockTypeStates[sb] do begin + inc(info.allocused,UseableBlockSize*AllocatedBlockCount); + inc(info.allocreserved,ReservedAddressSpace); + end; + end; +{$endif WITH_FASTMM4STATS} +end; + +procedure TSynMonitorMemory.RetrieveMemoryInfo; +var tix: cardinal; + info: TMemoryInfo; +begin + tix := GetTickCount64 shr 7; // allow 128 ms resolution for updates + if fLastMemoryInfoRetrievedTix<>tix then begin + fLastMemoryInfoRetrievedTix := tix; + if not GetMemoryInfo(info,{withalloc=}true) then + exit; + FMemoryLoadPercent := info.percent; + FPhysicalMemoryTotal.Bytes := info.memtotal; + FPhysicalMemoryFree.Bytes := info.memfree; + FPagingFileTotal.Bytes := info.filetotal; + FPagingFileFree.Bytes := info.filefree; + FVirtualMemoryTotal.Bytes := info.vmtotal; + FVirtualMemoryFree.Bytes := info.vmfree; + FAllocatedReserved.Bytes := info.allocreserved; + FAllocatedUsed.Bytes := info.allocused; + end; +end; + + +{ TSynMonitorDisk } + +constructor TSynMonitorDisk.Create; +begin + fAvailableSize := TSynMonitorOneSize.Create({nospace=}false); + fFreeSize := TSynMonitorOneSize.Create({nospace=}false); + fTotalSize := TSynMonitorOneSize.Create({nospace=}false); +end; + +destructor TSynMonitorDisk.Destroy; +begin + fAvailableSize.Free; + fFreeSize.Free; + fTotalSize.Free; + inherited; +end; + +function TSynMonitorDisk.GetName: TFileName; +begin + RetrieveDiskInfo; + result := fName; +end; + +function TSynMonitorDisk.GetAvailable: TSynMonitorOneSize; +begin + RetrieveDiskInfo; + result := fAvailableSize; +end; + +function TSynMonitorDisk.GetFree: TSynMonitorOneSize; +begin + RetrieveDiskInfo; + result := fFreeSize; +end; + +function TSynMonitorDisk.GetTotal: TSynMonitorOneSize; +begin + RetrieveDiskInfo; + result := fTotalSize; +end; + +class function TSynMonitorDisk.FreeAsText: RawUTF8; +var name: TFileName; + avail,free,total: QWord; +begin + GetDiskInfo(name,avail,free,total); + FormatUTF8('% % / %',[name, KB(free),KB(total)],result); +end; + +{$ifdef MSWINDOWS} +function GetDiskFreeSpaceExA(lpDirectoryName: PAnsiChar; + var lpFreeBytesAvailableToCaller, lpTotalNumberOfBytes, + lpTotalNumberOfFreeBytes: QWord): LongBool; stdcall; external kernel32; +function GetDiskFreeSpaceExW(lpDirectoryName: PWideChar; + var lpFreeBytesAvailableToCaller, lpTotalNumberOfBytes, + lpTotalNumberOfFreeBytes: QWord): LongBool; stdcall; external kernel32; +function DeviceIoControl(hDevice: THandle; dwIoControlCode: DWORD; lpInBuffer: Pointer; + nInBufferSize: DWORD; lpOutBuffer: Pointer; nOutBufferSize: DWORD; + var lpBytesReturned: DWORD; lpOverlapped: POverlapped): BOOL; stdcall; external kernel32; +{$endif} + +function GetDiskInfo(var aDriveFolderOrFile: TFileName; + out aAvailableBytes, aFreeBytes, aTotalBytes: QWord + {$ifdef MSWINDOWS}; aVolumeName: PFileName = nil{$endif}): boolean; +{$ifdef MSWINDOWS} +var tmp: array[0..MAX_PATH-1] of Char; + dummy,flags: DWORD; + dn: TFileName; +begin + if aDriveFolderOrFile='' then + aDriveFolderOrFile := SysUtils.UpperCase(ExtractFileDrive(ExeVersion.ProgramFilePath)); + dn := aDriveFolderOrFile; + if (dn<>'') and (dn[2]=':') and (dn[3]=#0) then + dn := dn+'\'; + if (aVolumeName<>nil) and (aVolumeName^='') then begin + tmp[0] := #0; + GetVolumeInformation(pointer(dn),tmp,MAX_PATH,nil,dummy,flags,nil,0); + aVolumeName^ := tmp; + end; + result := {$ifdef UNICODE}GetDiskFreeSpaceExW{$else}GetDiskFreeSpaceExA{$endif}( + pointer(dn),aAvailableBytes,aTotalBytes,aFreeBytes); +{$else} +{$ifdef KYLIX3} +var fs: TStatFs64; + h: THandle; +begin + if aDriveFolderOrFile='' then + aDriveFolderOrFile := '.'; + h := FileOpen(aDriveFolderOrFile,fmShareDenyNone); + result := fstatfs64(h,fs)=0; + FileClose(h); + aAvailableBytes := fs.f_bavail*fs.f_bsize; + aFreeBytes := aAvailableBytes; + aTotalBytes := fs.f_blocks*fs.f_bsize; +{$endif} +{$ifdef FPC} +var fs: tstatfs; +begin + if aDriveFolderOrFile='' then + aDriveFolderOrFile := '.'; + result := fpStatFS(aDriveFolderOrFile,@fs)=0; + aAvailableBytes := QWord(fs.bavail)*QWord(fs.bsize); + aFreeBytes := aAvailableBytes; // no user Quota involved here + aTotalBytes := QWord(fs.blocks)*QWord(fs.bsize); +{$endif FPC} +{$endif MSWINDOWS} +end; + +procedure TSynMonitorDisk.RetrieveDiskInfo; +var tix: cardinal; +begin + tix := GetTickCount64 shr 7; // allow 128 ms resolution for updates + if fLastDiskInfoRetrievedTix<>tix then begin + fLastDiskInfoRetrievedTix := tix; + GetDiskInfo(fName,PQWord(@fAvailableSize.Bytes)^,PQWord(@fFreeSize.Bytes)^, + PQWord(@fTotalSize.Bytes)^{$ifdef MSWINDOWS},@fVolumeName{$endif}); + end; +end; + + +initialization + Assert(SizeOf(TSynTableFieldType)=1); // as expected by TSynTableFieldProperties + Assert(SizeOf(TSynTableFieldOptions)=1); + {$ifndef NOVARIANTS} + Assert(SizeOf(TSynTableData)=SizeOf(TVarData)); + {$endif NOVARIANTS} + Assert(SizeOf(THTab)=$40000*3); // 786,432 bytes + Assert(SizeOf(TSynUniqueIdentifierBits)=SizeOf(TSynUniqueIdentifier)); + SetLength(JSON_SQLDATE_MAGIC_TEXT,3); + PCardinal(pointer(JSON_SQLDATE_MAGIC_TEXT))^ := JSON_SQLDATE_MAGIC; + {$ifdef MSWINDOWS} + InitWindowsAPI; + {$endif MSWINDOWS} + TTextWriter.RegisterCustomJSONSerializerFromText([ + TypeInfo(TDiskPartitions), + 'name:RawUTF8 mounted:string size:QWord', + TypeInfo(TSystemUseDataDynArray), + 'Timestamp:TDateTime Kernel,User:single WorkDB,VirtualKB:cardinal']); +end. + diff --git a/ThirdParty/mORMot/Source/Synopse.inc b/ThirdParty/mORMot/Source/Synopse.inc index f390fe69..47dd30a1 100644 --- a/ThirdParty/mORMot/Source/Synopse.inc +++ b/ThirdParty/mORMot/Source/Synopse.inc @@ -20,7 +20,7 @@ The Initial Developer of the Original Code is Arnaud Bouchez. - Portions created by the Initial Developer are Copyright (C) 2018 + Portions created by the Initial Developer are Copyright (C) 2019 the Initial Developer. All Rights Reserved. Contributor(s): @@ -109,13 +109,6 @@ // to access 'VarCopyProc' from unit 'SynCommons'" // - shall be set at the package options level, and left untouched by default -{$define WITHLOG} -// if defined, logging will be supported via the TSQLLog family -// - should be left defined: TSQLog.Family.Level default setting won't log -// anything, so there won't be any noticeable performance penalty to have -// this WITHLOG conditional defined, which is expected by high-level units -// of the framework, like DDD or UI - {.$define DOPATCHTRTL} // if defined, the low-level patches made to RecordCopy() low-level function // as defined in SynCommons.pas will be applied (if applicable to your Delphi @@ -147,18 +140,33 @@ // which will use BT[mem] opcodes, which are slow on Intel, but fast on AMD // (with the Delphi x86 compiler, may not be the case for LLVM or FPC) +{.$define FORCE_STRSSE42} +// sse4.2 string instructions may read up to 16 bytes after the actual end buffer +// -> define this if you want StrLen/StrComp/strspn/strcspn to use SSE4.2 opcodes +// but you would eventually experiment weird random GPF in your project, raising +// unexpected SIGABRT/SIGSEGV under POSIX system: so is disabled below for our +// LINUX conditional - and use at your own risk under Windows! + +{.$define DISABLE_SSE42} +// if defined, no SSE4.2 nor AES-NI instruction will be used, i.e. disable +// FORCE_STRSSE42 and all crc32c opcodes - is set for FPC DARWIN target + {.$define WITH_ERMS} // you may define this to enable REP MOVSB/STOSB for Fillchar/Move if cfERMS // flag is available in Intel's CpuFeatures -// -> disabled by default, since in practice it is much slower for small blocks +// -> disabled by default, since in practice it is (much) slower for small blocks {.$define NOXPOWEREDNAME} // define this to avoid sending "X-Powered-By: Synopse mORMot" HTTP header {.$define SQLVIRTUALLOGS} // enable low-level logging of SQlite3 virtual table query planner costs -// -> to be used only for internal debugging +// -> to be defined only for internal debugging +{.$define DDDNOSYNDB} +// SynDB / external SQL DB won't be linked to the executable by dddInfraSettings +{.$define DDDNOMONGODB} +// Mongo DB client won't be linked to the executable by dddInfraSettings {$ifdef FPC} @@ -200,7 +208,7 @@ {$define HASUINT64} {$define HASINLINENOTX86} {$define NODELPHIASM} // ignore low-level System.@LStrFromPCharLen calls - {$define HASAESNI} + {$define HASAESNI} // should be commented to test project with valgrind {$define HASTTHREADSTART} {$define HASINTERFACEASTOBJECT} {$define EXTENDEDTOSTRING_USESTR} // FloatToText uses str() in FPC @@ -274,7 +282,11 @@ {$define LINUX} // not true, but a POSIX/BSD system {$ifdef DARWIN} {$define FPCSQLITE3STATIC} // we supply Darwin static libs - {$define FPC_PIC} // may have not be defined by the compiler options + {$ifdef CPUX86} + {$define FPC_PIC} // may have not be defined by the compiler options + {$endif} + {$undef FORCE_STRSSE42} // fails otherwise for sure + {$define ABSOLUTEPASCAL} // NO asm nor redirection (until stabilized) {$else} {$define BSDNOTDARWIN} {$endif} @@ -284,6 +296,10 @@ {$endif} {$endif} + {$ifdef LINUX} + {$undef FORCE_STRSSE42} // avoid fatal SIGABRT/SIGSEGV on POSIX systems + {$define FPCLINUX} + {$endif} {$ifdef FPC_PIC} {$define PUREPASCAL} // most asm code is not PIC-safe with global constants {$endif} @@ -305,6 +321,9 @@ {$define CPUINTEL} {$define FPC_CPUINTEL} {$ASMMODE INTEL} // as Delphi expects + {$ifndef FPC_PIC} + {$define CPUX86NOTPIC} // use "absolute" instead of local register + {$endif FPC_PIC} {$endif CPUX86} {$endif CPU64} @@ -394,6 +413,7 @@ {$define CPU32DELPHI} {$undef CPU64} {$define CPUX86} // for compatibility with older versions of Delphi + {$define CPUX86NOTPIC} // use "absolute" instead of local register {$endif CPUX64} {$IFDEF CONDITIONALEXPRESSIONS} // Delphi 6 or newer @@ -442,6 +462,9 @@ {$define HASINLINENOTX86} {$define HASREGION} {$define HASFASTMM4} + // you can define this so that GetMemoryInfo/TSynMonitorMemory returns + // low-level FastMM4 information + {.$define WITH_FASTMM4STATS} {$ifend} {$ifdef VER180} {$define ISDELPHI20062007} // to circumvent some specific bugs @@ -582,10 +605,29 @@ {$endif} {$endif} {$endif} + {$ifdef DISABLE_SSE42} + {$undef FORCE_STRSSE42} + {$endif DISABLE_SSE42} {$else} {$undef HASAESNI} // AES-NI is an Intel-specific feature + {$define ABSOLUTEPASCALORNOTINTEL} {$endif CPUINTEL} +{$ifdef ABSOLUTEPASCAL} + {$define ABSOLUTEORPUREPASCAL} + {$define ABSOLUTEPASCALORNOTINTEL} +{$endif ABSOLUTEPASCAL} +{$ifdef PUREPASCAL} + {$define ABSOLUTEORPUREPASCAL} +{$endif PUREPASCAL} + +{$define WITHLOG} +// if defined, logging will be supported via the TSQLLog family +// - should be left defined: TSQLog.Family.Level default setting won't log +// anything, so there won't be any noticeable performance penalty to have +// this WITHLOG conditional defined, which is expected by high-level part +// of the framework, like DDD or UI units + {$ifdef FPC} {$ifndef FPCSQLITE3STATIC} // see above about this FPC-specific conditional {$define NOSQLITE3STATIC} @@ -595,12 +637,12 @@ {$ifdef CPU64} {$define NOSQLITE3STATIC} {$endif} -{$endif} +{$endif FPC} {$ifdef NOSQLITE3STATIC} // our proprietary crypto expects a statically linked custom sqlite3.c {$define NOSQLITE3ENCRYPT} -{$endif} +{$endif NOSQLITE3STATIC} {$ifdef MSWINDOWS} {$define USEWININET} // publish TWinINet/TWinHttp/TWinHttpAPI classes @@ -613,6 +655,11 @@ {$define USELIBCURL} // for Android, consider using https://github.com/gcesarmza/curl-android-ios // static libraries and force USELIBCURL in the project conditionals - {$endif} -{$endif} + {$endif ANDROID} +{$endif MSWINDOWS} + +{$ifdef USELIBCURL} + {.$define LIBCURLMULTI} + // enable https://curl.haxx.se/libcurl/c/libcurl-multi.html interface +{$endif USELIBCURL} diff --git a/ThirdParty/mORMot/Source/SynopseCommit.inc b/ThirdParty/mORMot/Source/SynopseCommit.inc index b0169bbb..db0c327e 100644 --- a/ThirdParty/mORMot/Source/SynopseCommit.inc +++ b/ThirdParty/mORMot/Source/SynopseCommit.inc @@ -1 +1 @@ -'1.18.4886' +'1.18.5231' diff --git a/Utils/Source/MARScmd/MARScmd_VCL.dproj b/Utils/Source/MARScmd/MARScmd_VCL.dproj index 08d82c1d..e7859151 100644 --- a/Utils/Source/MARScmd/MARScmd_VCL.dproj +++ b/Utils/Source/MARScmd/MARScmd_VCL.dproj @@ -1,7 +1,7 @@  {5AC1AE2B-2715-4744-81F3-0E0B30BD6CAF} - 18.5 + 19.0 VCL MARScmd_VCL.dpr True @@ -157,12 +157,20 @@ classes 1
+ + classes + 1 +
res\xml 1 + + res\xml + 1 + @@ -175,96 +183,242 @@ library\lib\armeabi 1 + + library\lib\armeabi + 1 + + + + + library\lib\armeabi-v7a + 1 + library\lib\mips 1 + + library\lib\mips + 1 + library\lib\armeabi-v7a 1 + + library\lib\arm64-v8a + 1 + + + + + library\lib\armeabi-v7a + 1 + res\drawable 1 + + res\drawable + 1 + res\values 1 + + res\values + 1 + res\values-v21 1 + + res\values-v21 + 1 + + + + + res\values + 1 + + + res\values + 1 + res\drawable 1 + + res\drawable + 1 + res\drawable-xxhdpi 1 + + res\drawable-xxhdpi + 1 + res\drawable-ldpi 1 + + res\drawable-ldpi + 1 + res\drawable-mdpi 1 + + res\drawable-mdpi + 1 + res\drawable-hdpi 1 + + res\drawable-hdpi + 1 + res\drawable-xhdpi 1 + + res\drawable-xhdpi + 1 + + + + + res\drawable-mdpi + 1 + + + res\drawable-mdpi + 1 + + + + + res\drawable-hdpi + 1 + + + res\drawable-hdpi + 1 + + + + + res\drawable-xhdpi + 1 + + + res\drawable-xhdpi + 1 + + + + + res\drawable-xxhdpi + 1 + + + res\drawable-xxhdpi + 1 + + + + + res\drawable-xxxhdpi + 1 + + + res\drawable-xxxhdpi + 1 + res\drawable-small 1 + + res\drawable-small + 1 + res\drawable-normal 1 + + res\drawable-normal + 1 + res\drawable-large 1 + + res\drawable-large + 1 + res\drawable-xlarge 1 + + res\drawable-xlarge + 1 + + + + + res\values + 1 + + + res\values + 1 + @@ -353,6 +507,9 @@ 0 + + 0 + 0 @@ -385,6 +542,17 @@ 1 + + + 1 + + + 1 + + + 1 + + 1 @@ -396,6 +564,39 @@ 1 + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + 1 @@ -407,6 +608,71 @@ 1 + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + ..\$(PROJECTNAME).launchscreen\Assets\LaunchScreenImage.imageset + 1 + + + ..\$(PROJECTNAME).launchscreen\Assets\LaunchScreenImage.imageset + 1 + + 1 @@ -418,6 +684,136 @@ 1 + + + 1 + + + 1 + + + 1 + + + + + ..\$(PROJECTNAME).launchscreen\Assets\LaunchScreenImage.imageset + 1 + + + ..\$(PROJECTNAME).launchscreen\Assets\LaunchScreenImage.imageset + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + ..\$(PROJECTNAME).launchscreen\Assets\LaunchScreenImage.imageset + 1 + + + ..\$(PROJECTNAME).launchscreen\Assets\LaunchScreenImage.imageset + 1 + + 1 @@ -429,6 +825,16 @@ 1 + + + ..\$(PROJECTNAME).launchscreen\Assets\LaunchScreenImage.imageset + 1 + + + ..\$(PROJECTNAME).launchscreen\Assets\LaunchScreenImage.imageset + 1 + + 1 @@ -451,10 +857,55 @@ 1 + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + ..\$(PROJECTNAME).launchscreen\Assets\LaunchScreenImage.imageset + 1 + + + ..\$(PROJECTNAME).launchscreen\Assets\LaunchScreenImage.imageset + 1 + + + + + ..\$(PROJECTNAME).launchscreen\Assets\LaunchScreenImage.imageset + 1 + + + ..\$(PROJECTNAME).launchscreen\Assets\LaunchScreenImage.imageset + 1 + + 1 + + 1 + @@ -495,6 +946,16 @@ 1 + + + ..\$(PROJECTNAME).launchscreen + 64 + + + ..\$(PROJECTNAME).launchscreen + 64 + + 1 @@ -547,6 +1008,10 @@ library\lib\armeabi-v7a 1 + + library\lib\arm64-v8a + 1 + 1 @@ -571,6 +1036,12 @@ 0 + + + library\lib\armeabi-v7a + 1 + + 1 @@ -608,6 +1079,7 @@ + True diff --git a/Utils/Source/MARScmd/MARScmd_VCL.res b/Utils/Source/MARScmd/MARScmd_VCL.res index 68a6af8f..0e5c8edc 100644 Binary files a/Utils/Source/MARScmd/MARScmd_VCL.res and b/Utils/Source/MARScmd/MARScmd_VCL.res differ diff --git a/media/GitHubSocialTemplate.png b/media/GitHubSocialTemplate.png new file mode 100644 index 00000000..f2bd361c Binary files /dev/null and b/media/GitHubSocialTemplate.png differ diff --git a/media/GitHubSocialTemplate.xcf b/media/GitHubSocialTemplate.xcf new file mode 100644 index 00000000..8f34db37 Binary files /dev/null and b/media/GitHubSocialTemplate.xcf differ diff --git a/tests/Tests.JWT.pas b/tests/Tests.JWT.pas index 84463163..973d1538 100644 --- a/tests/Tests.JWT.pas +++ b/tests/Tests.JWT.pas @@ -16,7 +16,7 @@ TMARSJWT = class(TObject) procedure Duration(const ASeconds: Int64); function GetTokenForVerifyOne: string; virtual; public - const DUMMY_SECRET = 'dummy_secret'; + const DUMMY_SECRET = '12345678901234567890123456789012'; [Test] procedure BuildOne; [Test] procedure VerifyOne; @@ -142,9 +142,10 @@ procedure TMARSJWT.Duration5secs; function TMARSJWT.GetTokenForVerifyOne: string; begin // beware: will expire one million days after Nov 15th, 2017 that is somewhere around Thu, 13 Oct 4755 :-D - Result := 'eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.' - + 'eyJkdXJhdGlvbiI6MTAwMDAwMCwiUm9sZXMiOiJzdGFuZGFyZCIsImlhdCI6MTUxMDczOTg0OCwiZXhwIjo4NzkxMDczNjI0OCwiQ2xhaW0yIjoxMjMsIlVzZXJOYW1lIjoiQW5kcmVhMSIsIkxBTkdVQUdFX0lEIjoxLCJpc3MiOiJNQVJTLUN1cmlvc2l0eSIsIkNsYWltMSI6IlByaW1vIn0.' - + '2DAhF5DWfTCPK13EYSkdlT2LRUA9kmHJcO9v-Gs0x6E'; + Result := + 'eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9' + +'.eyJkdXJhdGlvbiI6MTAwMDAwMCwiUm9sZXMiOiJzdGFuZGFyZCIsImlhdCI6MTUxMDczOTg0OCwiZXhwIjo4NzkxMDczNjI0OCwiQ2xhaW0yIjoxMjMsIlVzZXJOYW1lIjoiQW5kcmVhMSIsIkxBTkdVQUdFX0lEIjoxLCJpc3MiOiJNQVJTLUN1cmlvc2l0eSIsIkNsYWltMSI6IlByaW1vIn0' + +'.HpiUlC0d-a-oA4rZRFOpQsHxML55B0vXL5BbtEQNnLI'; end; procedure TMARSJWT.VerifyOne;