Jump to content

Leaderboard


Popular Content

Showing content with the highest reputation on 06/20/20 in all areas

  1. Hi, when you read the title, you probably already had the standard answer in your head: "Does not work with Indy, supports only OpenSSL 1.0.2 at most and thus no TLS 1.3". I can reassure you, that is not my point. Or actually even more precisely: That is exactly my point 😉 I've spent "a little" time writing the Indy support for OpenSSL 1.1.1 and TLS 1.3, and added a push request to Indy: #299. At the same time I have fixed a few issues that have been posted to GitHub (see PR). I wrote 2 new IO handlers ( one for server and one for client), the old ones are unchanged to avoid conflicts. Everything was written and tested in Delphi Berlin 10.1.2 on Win32 and Win64. I have neither macOS nor iOS nor Linux nor Android, nor FreePascal, nor older (or newer) Delphi versions. I have tried to keep older Delphi versions in mind to ensure that it will run on them, but there have been no tests on my part. I have tested it extensively in small test applications with other servers/clients. In addition, I built it into a large Real World program with TCP server/client, SMTP/IMAP/POP clients, FTP client, HTTP client, and it also ran there without problems. Unfortunately the nice man, who has provided new binary files of OpenSSL under indy.fulgan.com has said that he does not offer versions > 1.0.2 anymore. So I used the versions of slWebPro in the beginning (they even still work on WinXP), later I used the versions of Overbyte, because they do not have any external dependencies (and are digitally signed, but no XP anymore^^). But both worked without problems. All files are located in the subfolder "Lib/Protocols/OpenSSL". There are also subfolders "static" and "dynamic" which offers quite extensive imports of the OpenSSL API, once for static linking, once for dynamic loading/unloading. For dynamic loading there are also possibilities in the "IdOpenSSLLoader.pas" to trigger the loading/unloading itself, if you need the API outside of the IO handler (e.g. for your own x509 generation). To save me the double writing of the imports, I wrote a kind of intermediate code in the folder "Intermediate", which I let generate with "GenerateCode" to the two variants. The tool "GenerateCode" is only a simple string customization and actually only designed for Berlin, so I didn't bother about downward compatibility. As a normal user of the IO handlers you don't need them, only if you make changes to the API implementation. So and now comes your. It would be nice if one or the other would test this, so that it is not only WOMM certified, but can withstand more real situations. For me it also works with the Indy, which comes with Delphi Berlin, when I create another unit, which provides some new Indy types and functions. Of course some units have to be adapted locally to use the new unit.
  2. function TMemoryStream.Write(const Buffer; Count: Longint): Longint; var Pos: Int64; begin if (FPosition >= 0) and (Count >= 0) then begin Pos := FPosition + Count; if Pos > 0 then begin if Pos > FSize then begin if Pos > FCapacity then SetCapacity(Pos); FSize := Pos; end; System.Move(Buffer, (PByte(FMemory) + FPosition)^, Count); FPosition := Pos; Result := Count; Exit; end; end; Result := 0; end; This is TMemoryStream.Write from System.Classes (which apart from using Int64 now seems to be unchanged since at least Delphi 2007). It looks rather inefficient to me: It checks for Count >= 0 while in my opinion it should check for Count > 0 because otherwise nothing gets written anyway and Result gets set to 0 (Remember Count is 0 in that case) which is done after the if statement anyway. If it did check for Count > 0 it could simply omit the Pos > 0 check because we already know that it must be because FPosition >= 0 and Count > 0, so FPosition + Count must also be > 0. So, my implementation would look like this: function TMemoryStream.Write(const Buffer; Count: Integer): Longint; var Pos: Int64; begin if (FPosition < 0) or (Count <= 0) then begin Result := 0; Exit; //==> end; Pos := FPosition + Count; if Pos > FSize then begin if Pos > FCapacity then SetCapacity(Pos); FSize := Pos; end; System.Move(Buffer, Pointer(Longint(FMemory) + FPosition)^, Count); FPosition := Pos; Result := Count; end; Am I missing something? Another oddity is that the GetSize method is not overridden to simply return FSize rather than calling Seek three times. Given that there are quite a few places in the RTL / VCL (and also JCL / JVCL, Indy and of course my own code) which use TMemoryStream as a convenient way to move around some bytes, it's odd that it still has these inefficiencies.
  3. Alexander Sviridenkov

    Fixing memory leaks with FastMM4

    Report is quite simple. First part is call stack, Usually top items are from RTL and gives you little info, so skip items from top until you'll find your own unit, this will be class/method where leaked block was created, Second section is (possible) class that uses this block.
  4. Mahdi Safsafi

    TMemoryStream.Write

    I see 🙂 For the first time when a CPU sees a branch, CPU uses static predictor : Mostly all CPU assumes that a backward branch is going to be taken for the reason I explained with the loop example. For forward branch, many CPU assumes (but not all) that a forward branch is not going to be taken. Some of them do a random prediction like core2. UPDATE: @Attila KovacsI forgot to answer your if/else question. remember what I said, for first time seen, CPU assumes "if" is taken because we intend to execute if-statement. So it's else section that is not going to be taken.
  5. Mahdi Safsafi

    TMemoryStream.Write

    They're considered as first seen when there is no previous information available. When CPU has no prior information about a branch, it assumes that the jump is taken (because most of the time we intend to execute the code inside "if statement"). BP uses complex algorithms for the prediction (85-90% of the prediction are correct !). Modern CPU achieves up to 95% ! Those algorithms kept improved since the first time. As I said before, for recent CPU, there is a full specialized CPU-unit for BP. While your program is running, CPU records all executed branches. When calling a logic for the second, third time, ... CPU uses previous available information to predicates whether a branch is going to be taken or not. Note that this technology is widely used by many architectures (not only for x86). However, implementation varies (some of them have a dedicated BP unit, others not, some of them are more able of OoOE, others have limited support, ... ).
  6. John Kouraklis

    Your RAD Studio 10.4 Sydney issues

    LSP form works...when it works What annoys me is that you first need to save a file before LSP is able to locate the changes--and this may not be what you always want. In all the videos EMBA presented and demonstrated LSP, they used units from the libraries. They never played live in the sense to add variables or procedures and then fire up LSP. In other versions I had CodeInsightPlus by DevJet which is an amazing product. Maybe they should have considered to buy it.
  7. Mahdi Safsafi

    TMemoryStream.Write

    OoOE is an old technology designed to improve CPU idle. That means CPU is free to rearrange the order of execution of a logic but must yield the same expectation. Branch prediction (BP) was needed to make that feature works smoothly. So in short, there was two important time-frame here. an era before P4, where developers/compilers were forced to cooperate with CPU in order to benefit from this feature. CPU provided special opcode prefix for branch called hint_prediction ( 0x2E: Branch Not Taken; 0x3E: Branch Taken). Compiler such gcc provided built in function to support that (__builtin_expect ). Developers were forced to use the asm version or the builtin function to benefit from BP. Obviously, this wasn't good because only high qualified developers(that clearly understood the technology) were able to use it. Besides they couldn't even cover all their logic (it was just impossible to cover all your if statements). CPU maker realized that and replaced the ugly prefix 0x2E, 0x3E by an automatic solution that does not require developer cooperation (>P4). Today, CPU maker are working heavy to improve OoOE/BP because it was clear that on a multiple run (not a single run) this can give a high performance (They dedicated special CPU unit for just BP). Now life is more easy but yet you still need to understand the concept in order to make a high performance version of your logic. For example, the second implementation of TMemoryStream.Write has two state and spins on Result:=0. Original implementation has mostly one state and highly optimized for Count > 0 on a multiple run. function TMemoryStream.Write(const Buffer; Count: Integer): Longint; var Pos: Int64; begin // there is a hight chance that the condition is false. if CPU predicates to true ... thats a waste of time. if (FPosition < 0) or (Count <= 0) then begin Result := 0; Exit; // cpu must wait to validate BP(idle). end else begin // state2: Pos := FPosition + Count; if Pos > FSize then begin if Pos > FCapacity then SetCapacity(Pos); FSize := Pos; end; System.Move(Buffer, Pointer(Longint(FMemory) + FPosition)^, Count); FPosition := Pos; Result := Count; // if CPU prediction was wrong, it must recover from state2(heavy). end; end; function TMemoryStream.Write(const Buffer; Count: Longint): Longint; var Pos: Int64; begin // there is a hight chance that the condition is true. If CPU predicates to true ... it saved time. if (FPosition >= 0) and (Count >= 0) then begin Pos := FPosition + Count; if Pos > 0 then begin if Pos > FSize then begin if Pos > FCapacity then SetCapacity(Pos); FSize := Pos; end; System.Move(Buffer, (PByte(FMemory) + FPosition)^, Count); FPosition := Pos; Result := Count; Exit; // cpu may wait here to validate BP(s). end; end; // state2: Result := 0; // recovering from state2 is not heavy. end;
  8. Mahdi Safsafi

    Class Constructor in Delphi 10.4

    Class constructors are executed in the lexical order in which they implemented (not declared). Class destructor executed in the reverse order in which they implemented. NOTE: Many languages follow the same principle.
  9. Stefan Glienke

    Class Constructor in Delphi 10.4

    According to what Allen wrote in 2009 that is not true. If there is a bug with that please link to the QP entry and/or code to repro wrong behavior.
  10. David Heffernan

    Your RAD Studio 10.4 Sydney issues

    Beta testing is no substitute for having your own comprehensive set of automated tests. Beta testing is best used to help identify issues with design. It really shouldn't be used to find implementation bugs. I hope that Emba don't do that. When Emba tell us that they are going to focus on quality, it comes with talk of how many QP issues have been resolved. But for me that misses the point. Unless they are also fixing the development process that allows so many bugs, they will remain stuck on the treadmill, running to stay still.
×