1) Intraweb rendering implementation
Lots of Intraweb controls use an utility function to render their HTML. This function called TextToHTML is implemented as a class function of TIWBaseHTMLControl class. This function is used because if you have an IWText component with a caption containing a special character, lets say, an ampersand (&) it must be properly encoded to be shown by the browser. An ampersand declares the beginning of an entity reference (a special character) in HTML, so to be shown in the page it must be replaced by "&" (without quotes). If you have IW sources you can search for TextToHTML and see that it is used in a lot of places, most inside RenderHTML and RenderAsync methods.2) TextToHTML implementation
Well, TextToHTML is not as fast as it could be. If you imagine that a lot of strings should be processed by TextToHTML before they get to the browser you may improve your application performance if you make that function faster, isn't it? Let's look at TextToHTML code:class function TIWBaseHTMLControl.TextToHTML(const AText: string; const AConvertEOLs: Boolean; const AConvertSpaces: Boolean): string; var f : integer; xIsCallBack: Boolean; begin Result := ''; xIsCallBack := GGetWebApplicationThreadVar.IsCallBack; for f := 1 to Length(AText) do begin case AText[f] of '<' : Result := Result + '<'; '>' : Result := Result + '>'; '"' : Result := Result + '"'; '''' : Result := Result + '''; '&' : Result := Result + '&'; else {$ifdef UNICODE} if (Char(AText[f]) in [#10, #13]) then begin {$else} if (AnsiChar(AText[f]) in [#10, #13]) then begin {$endif} if AConvertEOLs then begin case AText[f] of #10 : Result := Result + '< br >'; #13 : Result := Result + ''; end; end else begin Result := Result + AText[f]; end; end else begin if (AText[f] = #32) and AConvertSpaces then begin if xIsCallBack then begin Result := Result + ' '; end else begin Result := Result + ' ' end; end else begin Result := Result + AText[f]; end; end; end; end; end;We can see that there is a main loop interating throught all characters of the AText string, concatenating char by char to generate the result. This approach has two drawbacks: (1) Every string concatenation requires a new memory allocation and (2) it is SLOW!
3) A New TextToHTML implementation
We can implement TextToHTML using another approach, used by Delphi's own RTL in functions like HTMLEncode (unit HTTPApp.pas):class function TIWBaseHTMLControlHack.TextToHTML(const AText: string; const AConvertEOLs: Boolean; const AConvertSpaces: Boolean): string; var Sp, Rp: PChar; xIsCallBack: Boolean; begin xIsCallBack := GGetWebApplicationThreadVar.IsCallBack; SetLength(Result, Length(AText) * 10); Sp := PChar(AText); Rp := PChar(Result); while Sp^ <> #0 do begin case Sp^ of '&': begin FormatBuf(Rp^, 5, '&', 5, []); Inc(Rp, 4); end; '<', '>': begin if Sp^ = '<' then FormatBuf(Rp^, 4, '<', 4, []) else FormatBuf(Rp^, 4, '>', 4, []); Inc(Rp, 3); end; '"': begin FormatBuf(Rp^, 6, '"', 6, []); Inc(Rp, 5); end; '''': begin FormatBuf(Rp^, 5, ''', 5, []); Inc(Rp, 4); end; '\': begin FormatBuf(Rp^, 5, '\', 5, []); Inc(Rp, 4); end; #10: if AConvertEOLs then begin FormatBuf(Rp^, 4, '< br >', 4, []); Inc(Rp, 3); end else Rp^ := Sp^; #13: if AConvertEOLs then begin Dec(Rp); end else Rp^ := Sp^; #32: if AConvertSpaces then begin if xIsCallBack then begin FormatBuf(Rp^, 10, '&nbsp;', 10, []); Inc(Rp, 9); end else begin FormatBuf(Rp^, 6, ' ', 6, []); Inc(Rp, 5); end; end else Rp^ := Sp^; else Rp^ := Sp^ end; Inc(Rp); Inc(Sp); end; SetLength(Result, Rp - PChar(Result)); end;This new implemenation has two main advantages over the former: (1) There is less overhead due memory allocations, and (2) it is FASTER! :)
4) The problem
TextToHTML is a public method of TIWBaseHTMLControl but it is not virtual. Even if it were virtual, we would have to create descendant classes to override this method and it is not acceptable.5) The solution: Hack it!
Thanks to great code (RtlVclOptimize.pas) created and made available by Andreas Hausladen we can change TIWBaseHTMLControl implementation on the fly, patching the class. I will not enter in detail about Andreas code, but the hack is done replacing, in memory, the TIWBaseHTMLControl.TextToHTML method by another method from some other class. I needed to change RtlVclOptimize.pas a little, putting the declaration of the function "CodeRedirect" in the interface section, so it could be used by my code, outside that unit.Update: I'm not using Andreas RtlVclOptimize.pas anymore because it has a few incompatibilities with recent Delphi versions. I'm using another unit CodeRedirect.pas, included in the download file.
6) Speed comparison
The use of Andy's RtlVclOptimize unit alone can significantly improve IW applications performance. Moreover, in my tests, my TextToHTML implemenation is more than 4 times faster than the original. Original code: 14.7 seconds versus 3.4 seconds using my code (1,000,000 function calls). Another advantage is less memory allocations for strings (less memory fragmentation and less LOCK+multicore related issues - read more about it here).7) More Hacks!
Well, not satisfied hacking TextToHTML code, I did the same thing with another TIWBaseHTMLControl method, TextToJSStringLiteral . Finally I've packed all these little things inside a unit. To speed up your IWApplication a bit just declare RtlVclOptimize (from Andreas) and my unit, IntrawebPatch.pas inside your .DPR file and you are done!8) Finally, the downloads!
You can download my patch unit (IntrawebPatch.pas) and other required files here.Enjoy!