You are not logged in.
Pages: 1
Hi dear Arnaud,
As PChar is compiled with {$POINTERMATH ON}, direct pointer arithmatic operations should be applied with the multiplication of the pointer's base type-size with the value specified by operation.
Thus in for eg:
function PosExStringPas(pSub, p: PChar; Offset: PtrUInt): PtrInt;
We calculate the length of the "p" and "pSub" in Delphi version as
PInteger(p - 4)^; //and so for "pSub"
This actually is computed as "PByet(p) - 8", Where Char is two-byte sized (in Unicode Versions of Delphi).
Seems it should be edited for both "p" and "pSub" as for eg:
PInteger(PByte(p) - 4)^;
For always correct access to string's length field.
Please let me know if I am wrong...
Thanks.
Hi all
Could any one witness any compiled-code performance enhancements in comparison with previous versions (10-10.1.x) !!??
At least in Win32 compiler, as for Win64 the compilation process always fails as noticed..
And dear Arnaud,
It would be very thankful if you let us know about the quality of generated code with the very new LLVM-based Linux compiler, as I remembered, you mentioned somewhere the final generated code produced using Delphi mobile compilers are not so optimized; may be due to unqualified settings applied to the LLVM-backend...
I'd like to know if this new server-support-release also suffers from the same performance issue !!??
Thanks in advance.
Yes,
I was just testing all different aspects of the mORMot framework for better understanding of myself from its operations, I ran the tests in both logging enabled and disabled states, and as always you're right, almost no performance difference !!! Great work by all means.
Thanks.
Hi dear Arnaud,
In the following code snippet:
destructor TSQLHTTPRestServer.Destroy;
begin
fLog.Enter(Self).Log... // the rest of the code line.
{the rest of the destructor routine}
end;
when this destructor is called with {$define WITHLOG}, everything goes right and it works like a charm, but if we do compile with "WITHLOG" directive undefined, an AV occurs when the destructor is called.
Seems the "fLog" field in above code snippet is not instanciated, as it may not being used by any other means of the framework. I think that code-line above should be surrounded with a {ifdef WITHLOG}{endif} pair - like many other parts of your code - to prevet this issue.
To reproduce this issue you can compile the Client/Server official example with "WITHLOG" undefined.
I think it is a simple and small regression which remained hidden as the most of times we all compile our code with {define WITHLOG}.
And please excuse me for that unclear ticket, I should have explained more. Actually I created the ticket in a situation that I was very in a hurry.
Thanks.
Don't be shy, and spread the word on mORMot around you:
You can be sure that I'll do my best !!!, I honestly believe mORMot deserves much more. But unfortunately Delphi (Pascal in general) and its related market is relatively small and heavily disposed to use visual-friendly and RAD components and tools which need the least possible coding efforts. Obviously with the current community concerns, it takes this great framework more time to be well-known and accepted !
I'd like to have a serous try to make it available for .NET ASAP (Using C++/CLI to minimize the overhead of managed/native code interaction), I also like to let services be introduced in JavaScript/TypeScript (something like Node.js) using the integrated SpiderMonkey engine, as well as in .NET in Visual Studio, but I really need to spend much more time with mORMot to get more and more familiar with its low level operations and of course your SOS !!!.
I've seen a job offer in France last week especially asking for mORMot skills.
Small, but nice move!
Very nice and very happy to hear that...
How do you want to make the connection check?
By sending a ping on the other side?
Hi,
"Ping" uses an ICMP echo, so it is expected to work with any active connection regardless of being run within a network-tunnel (if we assume there would be NO blocking firewall-roles or route-maps). As the problem is basically the web-socket itself, we should send the traffic directly through the currently upgraded web-socket and get the result(s) back; in order to validate its functionality.
You have introduced a method to check the server availability as "TSQLRestClientURI.ServerTimeStampSynchronize()" which performs fast and simple pulling. After the web-socket's handshake - which is HTTP-based -, the rest of connection semantics uses web-socket's standards; so if the "upgrade" is satisfied, any other traffic should be transferred in web-socket's way. Therefore "ServerTimeStampSynchronize()" should fulfill the need.
What is your idea ??
As you may know https://www.websocket.org/echo.html implements an echo-server using HTML5 web-sockets. Here you can use a VPN (using different tunneling protocols) to reveal the problem, as the problem is also noticed on the page.
To be honest I think implementing HTML5 Web-sockets was a very nice try, but unfortunately it is not so accepted by many active network-devices' configurations, simply because it is very young and not widely used, today.
This great technology works nice on a local-host, but may not work as expected in a real world use-case.
For example; consider xDSL users which are connected to their ISPs using a tunnel (very common), they simply may have problems with web-socket connections and also offices/organizations which use proxies and different network tunneling services for different purposes (Security, Segmentation and Net. Traffic management) and ... They all may have unsuccessful experiment with their web-sockets-powered apps.
As I mentioned in my earlier post, secure web-sockets (wss://) using TLS may fail in very rare situations and seem to be operational in real world projects. so if we want to have a safe and working web-socket service with today's common network settings, we'd better use secure web-sockets i.e: over TLS.
But as you yourself noticed, IMHO the best and always working solution to implement a bidirectional, asynchronous, and REST-friendly publish-subscribe collaboration mechanism, for today's mORMot; is hiring a message queue system, as the great, light, cross-platform, open-source and of course very fast "0MQ" - which you also liked.
...and as always, thanks for you great work.
Best.
Hi Dear Arnaud.
I took a look at "THttpClientWebSockets.WebSocketsUpgrade()" method's implementation, I think we need an additional connection check even if the WS-upgrade reported successfully. As you may know WS connections may fail in presence of many widely used tunneling-services, especially with regular websocket connections. WSS (TLS Enabled) websockets may pass this step with much much greater chance (very rare to fail). As the use of tunneling [for security reasons, logical network segmentation, traffic control (bandwidth management) and ...] seems very common, I think such a solution should be considered.
Thanks.
Amir wrote:Thank you very much for this and also for the great "TSQLRestServer.RecordVersionSynchronizeCallback()" feature.
This feature is still in implementation stage: not tested, and with some potential changes.
Thanks for your notice... But I was very excited about introducing another great feature that satisfies my current project's need in a very simple way. But I know as a convention, if a road-map listed feature or important-update is completed and well tested, most of time a dedicated blog post is born and "self-tests" group of projects involve new test-cases...
To be honest, I've been familiar with Synopse Tools and mORMot framework since 2010-2011, and I followed its progress periodically by checking the timeline and reading your great blog posts (introducing new features and/or challenging other tools/technologies AND sharing experiences and ideas) and also some forum discussions; And as many other things I decided to use mORMot multiple times and unfortunately this never achieved, but these days I'm feeling deeply integrated with your mORMots' life-cycle !!!, as I'm carefully studding mORMot's documentation (Very detail and intensive !!! Thanks.) as well as some most-visited forum topics in addition to reviewing/reading blog posts. (WOWWW !!! GREAT WORK... REALLY GREAT CROSS-PLATFORM OPEN SOURCE PROJECT IN PASCAL WORLD !!!).
THANKS FOR SUCH A GREAT WORK...
Thank you very much for this and also for the great "TSQLRestServer.RecordVersionSynchronizeCallback()" feature.
Hi dear Arnaud,
Thanks for implementing the great master/slave replication feature.
I've recently downloded the latest repository and I'd like to use it as main backend in my current project, with this I need to manage a list of products to report and highlight new and/or updated items to the end users (Clients), as appropriate. As far as I studied the correspondig ducumenation, I could not find out how to reflect the change-type applied to a record; for example, I need to know which items are new to my client(s) and which comes as updates.
It would be very helpful for me and also for same scenarios, if you provide such feature or if currently available let me know how to get it used.
As a simple proposal, it would be nice if you let an optional callback parameter in "TSQLRestClient.RecordVersionSynchronize" which receives the "new" (recently retreived from master) and "old" (available with the current processing slave) "TSQLRecordVersion"s, and for new records the old-version value could be set to TSQLRecord(-1) for example; and for deleted records, implementing a new method which lets the clients fetch deleted item-IDs for any TSQLRecord (which directly queries the dedicated deletion-track table; i.e: "TSQLRecordTableDelete", if necessary) seems enough.
Thanks for your great work.
Thanks for your quick answer; Of course I also prefer to use dedicated files; but here security matters...
I've to hide the files from direct view and access !!!
I'd be thankful, if you suggest better solutions.
Hi, I need to store large-sized blobs in a local SQlite database, I'd like to know if anyone has advices or tricks for tuning and better performance gain.
Thanks in advance.
Thanks now the query works as expected.
Hi,
When I post this query
"SELECT ROWID FROM UserData WHERE UserData MATCH 'UserID:2298691215 OR EMail:edc@edc.org';"
using ExecuteList(), the resulting TSQLTableJSON does not contain all matching records; but when I execute it (exactly the same statements) using the SQLite-Database-Editor ("SQLite Expert" in my case) everything is OK and always all records are retrieved as expected; This seems like an issue !!?.
I'm using the latest 1.18 version of mORMot.
Thanks.
Thanks alot. well working.
But is there any difference performance-wise between the use of dedicated client per thread and this new threadsafe implementation in general ?
I think there should be no (significant) difference with use of SQLite kernel but for other engines like MS SQL Server or Oracle, using a seperate client per thread may provide better scaling; Is it right ?
Thanks.
Hi ...
I tested the "04 - HTTP Client-Server" as modified like what you see in previous post in windows XP and 7 as well (and always the applications had been running with admin privileges), and they all behaved the same and the problem still persists.
Would you please help me find where I'm going wrong ?
Yes, I had run the server with admin privileges several times, not only the server but also the client app
I'm using win 8.1, I doubt if something within this new windows version causes that, I'll do test on win 7 and XP and post the feedback ASAP.
But one more thing that has messed up my mind is that the same problem occurs when using windows message-mode !! I think this mode should be well thread-safe and scalable...
I may be doing something wrong, would be so thankful if you help me solve this.
Thanks.
To show my exact problem I have changed sample "04 - HTTP Client-Server" a little - ofcourse only the "Project04Client" part is modified and the server-application will stay unchanged - to help with reraising the exception that I'm suffering from.
here is the modified version of unit1.dfm
object Form1: TForm1
Left = 604
Top = 370
BorderStyle = bsSingle
ClientHeight = 416
ClientWidth = 490
Color = clBtnFace
Font.Charset = DEFAULT_CHARSET
Font.Color = clWindowText
Font.Height = -13
Font.Name = 'Tahoma'
Font.Style = []
OldCreateOrder = False
OnCreate = FormCreate
OnDestroy = FormDestroy
PixelsPerInch = 96
TextHeight = 16
object Label1: TLabel
Left = 40
Top = 16
Width = 67
Height = 16
Caption = 'Your name:'
end
object Label2: TLabel
Left = 40
Top = 72
Width = 86
Height = 16
Caption = 'Your message:'
end
object Label3: TLabel
Left = 232
Top = 192
Width = 37
Height = 16
Caption = 'Label3'
end
object Label4: TLabel
Left = 48
Top = 267
Width = 393
Height = 48
AutoSize = False
Caption =
'PLEASE run this application in debugging-mode within the IDE, To' +
' reveal and catch the OS ERROR #12019 in SynCrtSock unit which I' +
#39'm suffering from.'
Font.Charset = DEFAULT_CHARSET
Font.Color = clRed
Font.Height = -13
Font.Name = 'Tahoma'
Font.Style = []
ParentFont = False
ShowAccelChar = False
WordWrap = True
end
object Label5: TLabel
Left = 48
Top = 326
Width = 393
Height = 54
AutoSize = False
Caption =
'I tried to show the exact system error number and its correspond' +
'ing message, but its not simply possible; as it may be overwrit' +
'ten with other consequent errors or even reset.'
Font.Charset = DEFAULT_CHARSET
Font.Color = clWindowText
Font.Height = -13
Font.Name = 'Tahoma'
Font.Style = []
ParentFont = False
ShowAccelChar = False
WordWrap = True
end
object QuestionMemo: TMemo
Left = 32
Top = 88
Width = 409
Height = 121
ScrollBars = ssVertical
TabOrder = 0
end
object NameEdit: TEdit
Left = 32
Top = 32
Width = 217
Height = 24
TabOrder = 1
end
object AddButton: TButton
Left = 48
Top = 232
Width = 145
Height = 25
Caption = 'Add the message'
TabOrder = 2
OnClick = AddButtonClick
end
object QuitButton: TButton
Left = 312
Top = 232
Width = 129
Height = 25
Caption = 'Quit'
TabOrder = 3
OnClick = QuitButtonClick
end
object FindButton: TButton
Left = 256
Top = 32
Width = 185
Height = 25
Caption = 'Find a previous message'
TabOrder = 4
OnClick = FindButtonClick
end
object Button1: TButton
Left = 48
Top = 384
Width = 393
Height = 25
Caption = 'Please add a message and check this'
TabOrder = 5
OnClick = Button1Click
end
end
and this is the modified version of unit1.pas
unit Unit1;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, StdCtrls,
SynCommons, mORMot,
SampleData, System.Contnrs;
type
TTestThread = class(TThread)
private
P: TSQLSampleRecord;
FOccured: Boolean;
protected
procedure Execute; override;
public
destructor Destroy; override;
end;
TForm1 = class(TForm)
QuestionMemo: TMemo;
NameEdit: TEdit;
Label1: TLabel;
Label2: TLabel;
AddButton: TButton;
QuitButton: TButton;
FindButton: TButton;
Button1: TButton;
Label3: TLabel;
Label4: TLabel;
Label5: TLabel;
procedure AddButtonClick(Sender: TObject);
procedure FormDestroy(Sender: TObject);
procedure QuitButtonClick(Sender: TObject);
procedure FindButtonClick(Sender: TObject);
procedure FormCreate(Sender: TObject);
procedure Button1Click(Sender: TObject);
private
FList: TObjectList;
public
Database: TSQLRest;
Model: TSQLModel;
end;
var
Form1: TForm1;
implementation
{$R *.dfm}
{ TTestThread }
destructor TTestThread.Destroy;
begin
inherited Destroy;
P.Free;
end;
procedure TTestThread.Execute;
var LError: Cardinal;
begin
if P = nil then
P := TSQLSampleRecord.Create;
while not (Terminated or FOccured) do begin
Form1.Database.Retrieve(1, P, False);
LError := GetLastError;
if LError <> ERROR_SUCCESS then begin
FOccured := True;
Synchronize(
procedure
begin
Form1.QuestionMemo.Lines.Add(Format('Error Code: %d'#13#10'Error Message: %s'#13#10'=========',
[LError, SysErrorMessage(LError)]));
end
);
end;
end;
end;
procedure TForm1.AddButtonClick(Sender: TObject);
var Rec: TSQLSampleRecord;
begin
Rec := TSQLSampleRecord.Create;
try
// we use explicit StringToUTF8() for conversion below
// a real application should use TLanguageFile.StringToUTF8() in mORMoti18n
Rec.Name := StringToUTF8(NameEdit.Text);
Rec.Question := StringToUTF8(QuestionMemo.Text);
if Database.Add(Rec,true)=0 then
ShowMessage('Error adding the data') else begin
NameEdit.Text := '';
QuestionMemo.Text := '';
NameEdit.SetFocus;
end;
finally
Rec.Free;
end;
end;
procedure TForm1.FormDestroy(Sender: TObject);
begin
FList.Free;
Database.Free;
Model.Free;
end;
procedure TForm1.QuitButtonClick(Sender: TObject);
begin
Close;
end;
procedure TForm1.Button1Click(Sender: TObject);
var I: NativeInt;
begin
for I := 0 to 9 do
FList.Add(TTestThread.Create);
end;
procedure TForm1.FindButtonClick(Sender: TObject);
var Rec: TSQLSampleRecord;
begin
Rec := TSQLSampleRecord.Create(Database,'Name=?',[StringToUTF8(NameEdit.Text)]);
try
if Rec.ID=0 then
QuestionMemo.Text := 'Not found' else
QuestionMemo.Text := UTF8ToString(Rec.Question);
finally
Rec.Free;
end;
end;
procedure TForm1.FormCreate(Sender: TObject);
begin
Model := CreateSampleModel; // from SampleData unit
FList := TObjectList.Create(True);
end;
end.
Please run the client application with debugging enabled within the IDE to let the debugger reveal the OS error #12019 as mORMot's SynCrtSock.pas seems fault-tolerant enough to hide that.
Please let me know if something is wrong here;
Thanks in advance.
"21 - HTTP Client-Server performance" Sample compiles with Delphi XE5 update 2; but when running, after clicking the "Start" button, an access violation halts the program execution.
The debugger points to "SynSelfTests.pas" at line #10207
{$ifdef LVCL}
if fDatabase.AquireWriteMode = amMainThread then ....
//..
//..
//..
{$endif
as LVCL is not defined in my test-case and well, the fDatabase has not been initialized; because the fDatabase is to be Created in the section
if fClientOnlyServerIP = '' then begin
// ...
fDatabase := TSQLRestServerDB.Create(.......);
// the rest of operations...
end;
and the "ClientOnlyServerIP" property is initialized in the "OnClick event of Start button" to be localhost ip-address, so the above-mentioned condition never matches and so the fDatabase is not instanciated with a TSQLRestServerDB.
it seems the first if-block should be embraced by the second one or some extra checks should be considered or ....
Thanks.
Hi dear Arnaud and THANKS FOR SUCH A GREAT FRAMEWORK...
I decide to use mORMot in my recent project and I've started to do some excercises to get fluent with mORMots' language !!
Now I've encountered a problem with a very simple test-app; this app uses TSQLRestClient.Retreive method with the definite signature of TSQLRestClient_Instance.Retreive(aID, TSQLRecord_instance, FALSE); to retreive data within background threads simultanously.
When I adapt the code to run within the main thread, everything sounds well; but when I switch to spread the retreival-tasks within threads, sometimes (NOT ALWAYS), it fails. I'd like to know if there are any notes or advices that I should consider or even this approach is a working-solution or I have to implement my retreival-task as a service and consume it at client-side using consumer-threads instead ?
(I'm using the latest version of mORMot framework (1.8.x) + its SQLite kernel as data-store, and I've tested my sample app in HTTP Client-Server and also Windows-Messages architectures and the problem still persists.).
Thanks.
The place to exchange ideas and experiences on Delphi x64 assembly programming.
Dear Arnaud,
As you know, Delphi visual forms are encoded as DFM files and included into the executables as resources (RT_RCDATA). This implementation causes that DFMs could be simply exposed to the public, and this resource leak might be harmful in some situations.
my main question, concerns about how we can prevent this... and I prefer to do it in a delphi way, not using third parties.
I searched the web and I found many utilities designed to provide secure executables and libraries, but all of them are going to protect and compress whole of executable (or libraries) that is earned with heavy overhead. then I asked this question to find a solution with the least overhead and speed penalty.
and I made a guss it could be possible if I use a compression library (something using LZO algorithms for online decompression with no memory overhead) like "SynLZO.pas" and compress only forms and data modules with password protection, and also decomress it while initializing the forms, this causes to have a smaller executable and also provides a good security.
And I agree with you about the previous topic. but I am a novice programmer and computer studet who encountered lots of problems and questions while challenging the programming languages and utilities, and I try to find the best answer to them. so I look up the answer in some related books in hand or search the web and also post my questions to good forums. and I am not producing a software so I need no protection mechanism, I just wanted to learn and practice.
Thanks.
Dear Yogiyang..
Thanks..
Yoda Protector is a good and fast resource packer and protector which could be operative for most exe, dll, ocx files. it also can pack and protect delphi executables and libraries.
Please consider this article
-- Note the newer version (1.03.3), included some bug fix and improvements which had covered some issues noted in the article.
I also recommend evaluating SoftwarePassport Armadillo
Hi All,
Delphi forms are accessible after compiling, through a resource viewer program (e.g. Resource Hacker, Resource Builder, Restorator, etc.). this may reveal the program logic. My question is how to protect delphi application from this risk ...
some components (e.g. Bellinium Effects Citadel) provide this facility or some applications like "WTM EXE Stealth Protector", "Logic Protect EXE Creator", "Private exe Protector", "Exe Protect" and many more, help us have much secure applications, but with a heavy overhead, ofcourse Citadel works more efficiently ...
Is there any solution with the least penalty ?
Thanks.
Hi Dear Arnaud.
And Thanks for every thing...
One more question, does delphi iteself align a for, while or repeat/until loop in an appropriate manner ???
Thanks...
I remember all your remarks. There are lots of things I should learn... I know nothing about pipelining & ..., beside documents you introduced, I've started to study "The Intel Microprocessors ... 7th Ed - Barry B. Brey". I decide to learn this completely... I love lowlevel
All thanks.
function NumToPersian(const str: string): string;
asm
test eax, eax
xchg eax, edx
jz System.@UStrClr
@@Continue:
push edx
mov edx, [edx - 4]
push edx
push eax
push edx
call System.@UStrClr
pop eax
call System.@NewUnicodeString
pop edx
mov [edx], eax
mov edx, eax
pop ecx
pop eax
shr ecx, 1
push ebx
jz @@One
nop
@@Loop:
dec ecx
mov ebx, dword ptr [eax]
lea ebx, ebx + $06C006C0
lea eax, eax + 4
mov dword ptr [edx], ebx
lea edx, edx + 4
jnz @@Loop
@@One:
mov ebx, dword ptr [eax]
test bx, bx
jz @@Quit
add ebx, $6C0
mov dword ptr [edx], ebx
@@Quit:
pop ebx
end;
By the wayyyyyy....
I tried to make just one call to "UStrClr" but because it affects all the world !!! and I had to push EDX, ECX registers, I found it much better to include two "UStrClr"s !!!
Please let me know your idea, which guides me through the rest of the work....
thanks.
Hi Dear Arnaud
Here's new code, using UStrClr + NewUnicodeString, which has @@Loop at 4-byte aligned address, I tried to make an aligned loop without adding NOPs...
And about "size"... I was about aligning both address of the loop and size of the loop, this achieved by adding one extra NOP instraction before loop start-point.
It was because when studying "Intel Optimization... PDF" which you introduced, I misunderstood a subject, that after progressing in study I found it.
Sorry.
function NumToPersian(const str: string): string;
asm
test eax, eax
xchg eax, edx
jnz @@Continue
call System.@UStrClr
ret
@@Continue:
push edx
mov edx, [edx - 4]
push edx
push eax
push edx
call System.@UStrClr
pop eax
call System.@NewUnicodeString
pop edx
mov [edx], eax
mov edx, [edx]
pop ecx
pop eax
test ecx, 1
jz @@Prepare
inc ecx
@@Prepare:
shr ecx, 1
push ebx
@@Loop:
dec ecx
mov ebx, dword ptr [eax]
lea ebx, ebx + $06C006C0
lea eax, eax + 4
mov dword ptr [edx], ebx
lea edx, edx + 4
jnz @@Loop
pop ebx
end;
best.
Thanks Dear Arnaud.
I put a breakpoint at the start point of the @@Loop and using the delphi dissembler stopping at that point, I could simply calculate the size of Loop which was 11 bytes, and needed only one NOP instruction, to be DWORD aligned.
.
.
.
nop
@@Loop:
dec ecx
mov ebx, dword ptr [eax]
lea ebx, ebx - $06C006C0
lea eax, eax + 4
mov dword ptr [edx], ebx
lea edx, edx + 4
jnz @@Loop
.
.
.
I'll also replace the UStrSetLength with combination of UStrClr and NewUnicodeString as you advised to optimize the function.
All Thanks for ur help.
Cheers.
I'm back ... I read something about data alignment also also I decided to study delphi string managment mechanism, so I read string management routines in "system.pas" unit, and now I am aware of what happens to a string in its lifecycle... this should have been accomplished soOoOoOoner SORRY ...
Then I found that, the problem you mentioned about "Clearing empty strings" still persists in my code. Where I'd used "NewUnicodeString" function, the string previously assigned (temp string), never cleared, if there was one. I found another function in "system.pas" which fits my needs, called "UStrSetLength". so I changed the code to provide a DWORD aligned function which manages strings using the function (UStrSetLength) correctly.
Now how can I align the loop that is the main part of the function ? --- You noticed by adding NOP at start point of the loop, but how I can have the size of my loop to define number of NOPs to apply. --- You highlited something in a comment of your code about ALT-F12 shortcut, I tried multiple times in different situations, but nothing occured, would you please help me again.
Thank You.
function NumStrToFa(const str: string): string;
asm
xchg eax, edx
test edx, edx
jz @@Null
push edx
mov edx, dword ptr [edx - 4]
push edx
call System.@UStrSetLength
mov edx, eax
pop ecx
pop eax
mov edx, dword ptr [edx]
push ebx
shr ecx, 1
jz @@one
@@Loop:
dec ecx
mov ebx, dword ptr [eax]
lea ebx, ebx + $06C006C0
lea eax, eax + 4
mov dword ptr [edx], ebx
lea edx, edx + 4
jnz @@Loop
@@one:
mov ebx, dword ptr [eax]
test bx, bx
jz @@Quit
add ebx, $6C0
mov dword ptr [edx], ebx
@@Quit:
pop ebx
ret
@@Null:
jmp System.@UStrClr
end;
Please let me know your idea.
Cheers ...
Hi ...
Where can I read about data alignment !!! I know nothing about what u say I really like to learn why it is so important.
Know only I can make a guss of what you said about alignment, that I'll change the code and post it again.
Please let me know a complete reference about it.
as you noticed the the dummy code removed from first start of the function:
function NumStrToFa(const str: string): string;
asm
test eax, eax
jz @@Null
push eax
mov eax, dword ptr [eax - 4]
push eax
.
.
.
The function you mentioned is "UStrClr" which shoulf be applied at the end of the function.
that should manage the emty string explicitly.
.
.
.
@@Null:
mov eax, edx // preparing the result parameter for "UStrClr" function
jmp System.@UStrClr
end; // end of the function declaration
Realllyyyy Thankssssss again.
Hi Dear Arnaud,
Thanx for ur beautiful code... I am learning from your codes vary much.
THANK YOU.
I had checked to prevent extra "pushing and popping" of registers, when I wrote the code; but unfortunately the "NewUnicodeString" function affects and manipulates all EAX, ECX, EDX registers' data !!!
so I had to save them to retrieve essential data needed to run the rest of code. and the couple of PUSH-POP which has surrounded the "NewUnicodeString" seems necessary, but ther is another "PUSH EDX-POP EDX" which can be ANIHILATED... as you've illustrated in your code .
I always prefer native pointers, even in pure pascal. like C/C++. but I wanted to test delphi-string manipulation using assembly, and I found it does not make me a favour !!!
I think I'd be back to pointers again, any where possible ... . But there is no other choice in VCL, we should challenge delphi string.
As you noticed I could have written such code by converting two wideChars at the same time. now I have completed this function using your code template, and I moved and replaced
and ecx, 1 ===> test ecx, 1
then there is no need to have another couple of [PUSH ECX - POP ECX], and also by merging two sections ("@@nomore", "@@one") in one, we have a smaller function.
function NumStrToFa(const str: string): string;
asm
test eax, eax
jz @@Null2
push eax
mov eax, [eax - 4]
test eax, eax
jz @@Null1
push eax
push edx
call System.@NewUnicodeString
pop edx
mov [edx], eax
pop ecx
pop eax
mov edx, [edx]
push ebx
test ecx, 1
jz @@Loop
shr ecx, 1
@@one:
mov bx, [eax]
add bx, $6C0
mov [edx], bx
test ecx, ecx
jz @@Quit
lea eax, eax + 2
lea edx, edx + 2
@@Loop:
dec ecx
mov ebx, [eax]
lea ebx, ebx + $06C006C0
lea eax, eax + 4
mov [edx], ebx
lea edx, edx + 4
jnz @@Loop
@@Quit:
pop ebx
ret
@@Null1:
add esp, 4
@@Null2:
end;
Please tell me about these changes... Thanks.
Hi all,
here I have written an assembly function, which converts a general numerical string to Persian - Arabic equvalent.
for example: '1234567890' ==> '۱۲۳۴۵۶۷۸۹۰'
NOTE ---- all characters of the string parameter assumed to be numeric, and no further checking seems necessary.
Your ideas are welcome to help a newcomer amateur programmer optimize this little piece of code.
thanx.
function NumStrToFa(const str: string): string;
asm
test eax, eax // check if the string is null
jz @@Null2
push eax // save the input string pointer to stack
mov eax, [eax - 4] // length of the input string
test eax, eax // checking for emty string
jz @@Null1
push eax // saving the length of input string to stack
push edx // saving the @Result
call System.@NewUnicodeString //creating a new string which forms the result
pop edx // retrieving the @Result
mov [edx], eax // assign the new string to @Result
pop ecx // retrieving the Length of string
pop eax // retrieving the input string pointer
push edx // saving the Result string
mov edx, [edx] // forming the edx register to be used in the loop.
push ebx
@@Loop:
mov bx, word ptr [eax + ecx * 2 - 2]
add bx, 6C0h //$C0 is the Persian-Arabic Zero character order ('۰') - '0'
mov word ptr [edx + ecx * 2 - 2], bx
Loop @@Loop
pop ebx
pop edx // having the Result back
ret
@@Null1:
add esp, 4
@@Null2:
end;
Pages: 1