Jump to content

Kas Ob.

Members
  • Content Count

    552
  • Joined

  • Last visited

  • Days Won

    9

Posts posted by Kas Ob.


  1. 21 minutes ago, rgdawson said:

    I think that the problem is that Default(T) is not finalizing the variable using the class operator, before the first Initialize.

    No, hold your horses here!

     

    Default is right with its own definition, there is no default value in Pascal/Delphi to begin with except nil and zero, asking Default to finalize what by might not be initialized is wrong and outside of its usage, Default can't every know what it is clearing, because this what should be called Clearing or Zeroing.

    Untill we have default value for declared fields and the compiler put these values when ever a field/variable used then Default should be used as zeroing only.


  2. That is one ugly big mess of a call stack, i can see these crucial point of events

    [794F1030]{rtl290.bpl  } System.TMonitor.TryEnter (Line 20340, "System.pas" + 10) + $0          // there is no Exit but also no rentry for this one
    [794F0B50]{rtl290.bpl  } System.TMonitor.Enter (Line 20001, "System.pas" + 4) + $2
    [028AED7C]{vcl290.bpl  } Vcl.Controls.TControl.Perform (Line 7698, "Vcl.Controls.pas" + 10) + $8
    [028AD786]{vcl290.bpl  } Vcl.Controls.TControl.SetVisible (Line 6724, "Vcl.Controls.pas" + 5) + $C
    [029F7D5D]{vcl290.bpl  } Vcl.Forms.TCustomForm.SetVisible (Line 6143, "Vcl.Forms.pas" + 9) + $4
    [029FE52E]{vcl290.bpl  } Vcl.Forms.TCustomForm.Hide (Line 9721, "Vcl.Forms.pas" + 0) + $2
    [029F67A3]{vcl290.bpl  } Vcl.Forms.TCustomForm.BeforeDestruction (Line 5411, "Vcl.Forms.pas" + 6) + $14
    [794F07B5]{rtl290.bpl  } System.@BeforeDestruction (Line 19744, "System.pas" + 10) + $0
    [029F67BA]{vcl290.bpl  } Vcl.Forms.TCustomForm.Destroy (Line 5421, "Vcl.Forms.pas" + 0) + $6
    [794F0084]{rtl290.bpl  } System.TObject.Free (Line 18403, "System.pas" + 1) + $4
    [7890B734]{coreide290.bpl} FileFind.TThreadSearch.KillSearchProgress (Line 399, "FileFind.pas" + 1) + $5         // TThreadSearch.KillSearchProgress ?!!!
    [79610026]{rtl290.bpl  } System.Classes.CheckSynchronize (Line 16334, "System.Classes.pas" + 32) + $9            // not so much of evil here <-- 
    [02A047A4]{vcl290.bpl  } Vcl.Forms.TApplication.DoActionIdle (Line 13828, "Vcl.Forms.pas" + 3) + $13
    [02A04992]{vcl290.bpl  } Vcl.Forms.TApplication.Idle (Line 13897, "Vcl.Forms.pas" + 30) + $11
    [02A03977]{vcl290.bpl  } Vcl.Forms.TApplication.HandleMessage (Line 13312, "Vcl.Forms.pas" + 1) + $11
    [02A03CA9]{vcl290.bpl  } Vcl.Forms.TApplication.Run (Line 13451, "Vcl.Forms.pas" + 27) + $3
    [005B8AD2]{bds.exe     } bds.bds + $DE

    That TThreadSearch.KillSearchProgress is closing what ?!! , is it trying to killing/closing objects that own a CustomForm, yet these form is calling SetVisible, SetVisible in turn is Performing a Message event like call and locking the whole damn thing,....

    And that is not it yet, here comes this part (gem)

    [7961477C]{rtl290.bpl  } System.Classes.StdWndProc (Line 19085, "System.Classes.pas" + 9) + $2
    [029FB6FD]{vcl290.bpl  } Vcl.Forms.TCustomForm.SetWindowFocus (Line 8182, "Vcl.Forms.pas" + 10) + $8
    [029FB828]{vcl290.bpl  } Vcl.Forms.TCustomForm.SetActive (Line 8219, "Vcl.Forms.pas" + 12) + $3
    [029FC712]{vcl290.bpl  } Vcl.Forms.TCustomForm.WMActivate (Line 8695, "Vcl.Forms.pas" + 6) + $A
    [7891F7EC]{coreide290.bpl} MainWorkFrm.TMainWorkForm.WMActivate (Line 140, "MainWorkFrm.pas" + 0) + $0
    [00578A30]{bds.exe     } AppMain.TAppBuilder.WMActivate + $C
    [028AF146]{vcl290.bpl  } Vcl.Controls.TControl.WndProc (Line 7920, "Vcl.Controls.pas" + 91) + $6
    [029BC5BF]{vcl290.bpl  } Vcl.Themes.TStyleManager.GetStyle (Line 5822, "Vcl.Themes.pas" + 7) + $D
    [794F1800]{rtl290.bpl  } System.@TryFinallyExit (Line 23793, "System.pas" + 8) + $0
    [029BC6AD]{vcl290.bpl  } Vcl.Themes.TStyleManager.GetStyle (Line 5833, "Vcl.Themes.pas" + 18) + $1E

    Starts with GetStyle then again try to Activate the Main IDE Form

    Then comes a lot of stuff and hot mess, with many messages and code, then comes this gem which is shown on the stack 3 times

    [79579F09]{rtl290.bpl  } System.SyncObjs.CoWaitForMultipleHandles (Line 716, "System.SyncObjs.pas" + 3) + $B
    [7957A1DD]{rtl290.bpl  } System.SyncObjs.THandleObject.WaitForMultiple (Line 855, "System.SyncObjs.pas" + 14) + $18
    [786C0D17]{coreide290.bpl} ParserThread.TParseThread.Lock (Line 398, "ParserThread.pas" + 12) + $15
    [79610891]{rtl290.bpl  } System.Classes.TThread.SetPriority (Line 16713, "System.Classes.pas" + 1) + $13
    [786C0E36]{coreide290.bpl} ParserThread.TParseThread.CancelAndLock (Line 441, "ParserThread.pas" + 4) + $2

    CancelAndLock is called, SetPriority then Lock then wait, why it is waiting ? and, on what is waiting ? and why it is recursive 3 times ? Who knows !

    And that is not it, comes this part after first locking 

    [794F0084]{rtl290.bpl  } System.TObject.Free (Line 18403, "System.pas" + 1) + $4
    [786DE822]{coreide290.bpl} ProjectGroup.TProjectGroupWrapper.Close (Line 2848, "ProjectGroup.pas" + 2) + $5
    [00568245]{bds.exe     } AppMain.TAppBuilder.DestroyProjectGroup + $1A9
    [0056C0C8]{bds.exe     } AppMain.TAppBuilder.WindowCloseQuery + $4CC
    ..............
    [786DE822]{coreide290.bpl} ProjectGroup.TProjectGroupWrapper.Close (Line 2848, "ProjectGroup.pas" + 2) + $5
    [00568245]{bds.exe     } AppMain.TAppBuilder.DestroyProjectGroup + $1A9
    [0056C0C8]{bds.exe     } AppMain.TAppBuilder.WindowCloseQuery + $4CC
    ..............
    [794F0084]{rtl290.bpl  } System.TObject.Free (Line 18403, "System.pas" + 1) + $4
    [7890D720]{coreide290.bpl} FileFind.TerminateFindInFiles (Line 1124, "FileFind.pas" + 2) + $5
    [00568339]{bds.exe     } AppMain.TAppBuilder.CanCloseProjectGroup + $59
    [0056BCFC]{bds.exe     } AppMain.TAppBuilder.WindowCloseQuery + $100

    Finally we reach the end part

    [787830FB]{coreide290.bpl} ProjectModule.TBaseProject.PromptForSave (Line 1668, "ProjectModule.pas" + 1) + $1B
    [78784795]{coreide290.bpl} ProjectModule.TCustomProject.Save (Line 2093, "ProjectModule.pas" + 5) + $F
    [786DF5B3]{coreide290.bpl} ProjectGroup.TProjectGroupWrapper.GetCanCloseMiscProjects (Line 3116, "ProjectGroup.pas" + 12) + $8
    [00568339]{bds.exe     } AppMain.TAppBuilder.CanCloseProjectGroup + $59
    [0056BD0E]{bds.exe     } AppMain.TAppBuilder.WindowCloseQuery + $112

    So yes the IDE is closed, and is trying to ask you by a prompt to save or not while checking of any lock, even after DestroyProjectGroup is called, it is trying to execute GetCanCloseMiscProjects...

     

    Simply put, it is a hot mess, yet it must me easy to find and fix, the looking for the culprit starts here TThreadSearch.KillSearchProgress and finding why is leading to close the IDE ?

     

    I can suggest few things to try : (for you of course)

    1) I don't see any 3rd-party code involvement, so it is IDE bug, unless a hooked code involved without being exist in the stack call, which would be strange.

    2) Theming and that change your IDE theme or disable it, i don't have any of these shiny new IDEs with shiny looking themes, so not sure what can be done here.

    3) Go to administrative tools -> services and disable the Windows "Themes" service, it could have something to do with all of this bug and/or buggy skinning part of the code.

    4) Where is the cursor when this happen, i mean what item in the IDE did have the focus when this happen, and does the text input cursor something to do with this ?

     

     

    This function is very strange here which called once :

    [02A09B70]{vcl290.bpl  } Vcl.Forms.TScrollingStyleHook.WndProc (Line 16714, "Vcl.Forms.pas" + 0) + $0

    For me it is like trying to scroll and update, i would double check why it is there to begin with, because after it the stack start to make no sense, thanks to ThemeEngine or what ever theming code called.


  3. 4 hours ago, Sebastian Ledesma said:

    Any hints?

    1) Use ClassName instead of WindowName in FindWindow, more reliable as your main form title most likely is not "myVCLApplication", unless you are using this as example here, in all cases, use the class name, which is the Delphi class name for your main form, like TForm1 or TMyMainForm...

    2) and yes find and send to the main window/form.

    3) Don't use ShowWindow and SetForegroundWindow here , these should be on the receiving part, so this part of detect and send/post message, should be only post PostMessage, nothing more.

     

    When in doubt and for testing this, PostMessage should work locally form the same app, so test it in-place, if it does work then will work from any other application/process, to test it add timer with something like 10 second interval, then find the window and post this message to your main form, in other words, use the same code that you will use from another instances, minimize your application and see if it does receive and behave as you want.


  4. 9 hours ago, Dmitry Onoshko said:

    (2) I start feeling the good old library is not that good for the task,

    You picked the one of the worst also outdated socket library, literally it is not installed by default in the IDE for many years, it is deprecated.

     

    9 hours ago, Dmitry Onoshko said:

    (1) saving a few messages seems to be a good idea,

    You can't hit that messages limit easily, in fact i doubt that even possible if you tried to do so, to be able to hit that limit with socket messages would be an accomplishment.

    9 hours ago, Dmitry Onoshko said:

    (3) especially since SOMAXCONN is 5 there,

    SOMAXCONN has nothing to do with max connection server can serve or connect, it is about backlog, how many sockets trying to connect at the same exact time (literally at the same millisecond or microsecond), the rest at this exact time will be dropped.

    9 hours ago, Dmitry Onoshko said:

    (4) why on earth there’s no other good socket library included with Delphi

    You have many to pick from 

    Indy, ICS, mORMot/mORMot2, RealThinClient, these are opensource 


  5. 6 minutes ago, omnibrain said:

    That one's initially very expensive... Do you know what renewals cost?

    The price you see after clicking buy is not for only for SIP, /nsoftware website only offer Red Carpet license, a license for everything in their products page https://www.nsoftware.com/subscriptions , for individual package you have to contact their sales, don't know now but they used to offer license with source code for individual library too.


  6. 2 minutes ago, Dmitry Onoshko said:

    I don’t really understand this part. My point was that to receive data the loop is not the way to go (well, not the only one) since that would generally lead to several posted messages, and when processing them — to a few useless recv calls if no data has arrived since then. But you seem to suggest using the loop anyway.

    Stupid me !, not confirming the negation, sorry for that, and you know.. what you can go with loop or without it, the difference is little and close to none, for any non very intensive socket usage like sending/receiving large data (like huge files)

     

    also i missed to point the need to remove ioctlsocket , even you are not asking for the available data, ReceiveBuf uselessly internally is doing it, this can be removed by using your own RecvData.


  7. 22 minutes ago, Dmitry Onoshko said:

    I also saw somewhere that receive notification message would be posted to the message queue whenever ReceiveBuf leaves some data unread, so no point making a loop for ReceiveBuf, right?

    This is long story !, in short the answer is yes, loop is way better and will yield better throughput.

     

    The little longer one with a suggestion ,

    There is very popular wrong practice of checking for the length of available data over socket then allocate a buffer then perform the receive (recv) on that buffer with the already (assumed) known length, this is waste of time !

     

    i suggest to pre-allocate buffers for TCP, it could be 64kb (exactly 1024*64), or 16kb (any size will work but it is better to have a multiple of 4kb size and not less than 8kb), but it depends on how you expect you traffic speed, anyway.. with that allocated buffer just perform recv with the largest size your buffer can handle/fit, no need to check for available data length.

    Will work as expected and will squeeze speed and relax the network (vertically cascade) drivers (and TCP stack and provider) from this extra useless operation, operation that go into the kernel with afd driver then return empty handed, while you can recv as much as you can and it will not behave any different, on the contrary will give you all what is there to receive in one shot.


  8. And one more thing as reminder, about socket send API https://learn.microsoft.com/en-us/windows/win32/api/winsock2/nf-winsock2-send

    You always need to check the result of sent bytes, compare with your own data length, this is standard usage, but keep in mind if send sent part of the packet, this doesn't not mean that the next send for the rest will return fail with WSAEWOULDBLOCK, 

    No, send can partially send for many other situations like busy/full locally or the network driver imposing interrupt moderation...  and none of these situations does/should concern you,

     

    Just keep in mind unless send return SOCKET_ERROR and WSAEWOULDBLOCK is the WSAGetLastError, then you can safely try to send more, i always use loops.

     

    I saw this bad design that caused sluggishness in sending out of wrong assumptions ! 


  9. Hi,

     

    Well, i have this feeling that you are hesitating to do the right out of fear of braking things, i suggest to stop looking at these part of code as fragile as glass vase.

     

    Remy brilliantly explained all what do you need to know, now step back to look to the big picture here, you need functionality out of these TClientSocket and TServerClient, they are nice but far from best or very practical design, so .. adjust them to your need and this is the whole point of OOP to begin with.

     

    Example, SendBuf do raise exceptions unless you suppress them by changing the ErrorCode in the event, do you need this functionality ? it is not a big deal, but why not add your own function like SendData which takes care of the buffer too, this will simplify the buffering, also if needed add your own events, add somting like buffering triggered to signal traffic congestion, slowness, not responding peer... utilize this in you GUI, nice to see colored stuff indicating connection status.

     

    If you see what is going inside SendBuf, all what is needed to extended or replace it with better version is two things

    1) Dont forget to Lock then Unlock, accessible "ClientSocket1.Socket.Lock" ..

    2) you have the SocketHandle to pass to "send" (the API), also accessible "ClientSocket1.Socket.SocketHandle" 

    Extend them from outside, or if you prefer OOP then things way simpler.

     

    For your question about handling errors for Listen in TServerSocket, these are shortcoming in the design, try follow DoActivate logic, and i think you have all what you need to overload these functions with better and more suitable functionality.

    You can override TCustomWinSocket.Listen with your own, that raise exception in case your (added) event OnServerError is not assigned.

     

    In other words, have faith in your self, you got it !

     

    55 minutes ago, Dmitry Onoshko said:

    SetErrorProc

    I really suggest to stay away from this, as it is utilizing a threadvar, unless you really know what are you doing, you must know exactly when and who is accessing it, as you described it, can go messy very quick.

     

    Good luck and happy coding !


  10. Hi,

     

    From what i see, and i am guessing here, that exception is not from this code you pasted, but from one or two different places not shown here:

    One raised EInvalidPointer : Invalid pointer operation
    The other raised EAccessViolation : Access violation at address 0047C27C in module 'VRMEditor.exe'. Read of address FFFFFFFx

    The second exception does look like managed type variable wrongly or unsafely accessed, in other words it was uninitialized yet there was something trying to access or may be just clean it up, or it could be overwritten on the stack and had corrupted value and the exception was triggered in the hidden part of "end;", (aka clean up) 

     

    Try to extended logging within  self.Log(... itself , there most likely your broken code, track memory corruption too.


  11. A small correction for this GPT answer:

    1 hour ago, bravesofts said:

    LObj itself is a local variable, so it is allocated on the stack.

    Not every local variable is allocated on the stack, so LObj could be on the stack, or due to optimization have a dedicated CPU register to use.

    In other words, LObj is either on the stack or in a register, but the result will be the same, the stack might hold data (Value) previously been written (or zero if the stack is never being reached before at this depth), and a register will have a Value from the last usage being from this procedure/function or a previous one.

     

    Delphi compiler is less optimized for this registers usage (optimization) for local variable then FPC and less than most other languages compilers, yet it does it sometimes.

    • Like 1

  12. 12 hours ago, Stefan Glienke said:

    That would be true if dynamic array allocation would be done in a way that ensures that element [0] would be at a 16 byte aligned address, which is not the case

    Alas, that is the case, and this is why when performance is the target i don't use any managed type, a small record with size, count and a pointer where the item[0] are reference to raw record, with a record helper to provide addition and deteletion.

     

    11 hours ago, David Heffernan said:

    It is if you use a proper memory manager

    All memory manager i know including system Heap ones, do 16 bytes aligning, the problem in outdated and inefficient RTL managed types design, these should be updated, no memory manager can help with unaligned structure to begin with.

     


  13. 46 minutes ago, Rollo62 said:

    Or do you mean there is the possibility in the Tools/Options or else, to set such event globally, one for all projects?

    Well that is possible, but it might be an unneeded extra work.

     

    What i meant is something like this

    image.thumb.png.4f2c0f70cae5b1f713c8f12204e2e93b.png

    Just include the batch file which can have relative path, also it can take a parameter for the output path, even the name of the output file.

    and from the dproj file

        <PropertyGroup Condition="'$(Base)'!=''">
            <PostBuildEvent><![CDATA[BuildConst.bat
    $(PostBuildEvent)]]></PostBuildEvent>

    Also i did always prefer using the inc files in place for naming and versioning, over batch files, thus i have their content in the project and in the EXE description ... 

    • Like 1

  14. 8 minutes ago, David Heffernan said:

    This was true once upon a time but is not true for modern x86-64 architectures. 

    True for almost all cases but not all, there still some few edge cases.

    Another point, it will works for old and new CPU.

    Another point, it will help any new optimized memory copy functions to use the SIMD version without going through the first and unaligned 16 bytes, but for this it should be aligned to 16bytes to get the most of juice, meaning that record should be 32bytes.


  15. 3 hours ago, Rollo62 said:

    as early as possible in the process,

    Something like this 

    set filename=MyAppConsts.pas
    @echo on
    echo building %filename%
    @echo off
    
    
    (echo unit Unit1;& echo.& echo interface& echo.& echo const) > %filename% 
    
    echo   APPLICATION_APP_NAME = 'MyApp';>> %filename% 
    echo   APPLICATION_COMPANY_NAME = 'MyCompany';>> %filename% 
    
    
    (echo.& echo implementation& echo.& echo end.) >> %filename% 
    
    @echo on
    @echo done building %filename%
    
    
    #exit 0
    

    the result a file "MyAppConsts.pas" 

    unit Unit1;
    
    interface
    
    const
      APPLICATION_APP_NAME = 'MyApp'; 
      APPLICATION_COMPANY_NAME = 'MyCompany'; 
    
    implementation
    
    end.

    there is no earlier than that !


  16. Another thing, having a record with 17 bytes is little strange, and means you are using packed record, if you are tying to save some performance and minimize time used in copying, then let me assure you are doing the opposite.

    Remove the packed and/or make sure it is padded to multiple of 4 bytes, meaning 20 bytes should be your minimum, you might see better a little performance right away, even if it is very small it is better, or remove the packed modifier from the record and let the compiler align it for you, unless you are using and exchanging the same records between 32bit and 64bit version, in that case you see how the compiler did it, and replicate the padding yourself with the packed one.


  17. 2 hours ago, A.M. Hoornweg said:

    My worry is, whenever I delete an element at the beginning, would the RTL just allocate a new block and copy the old array minus the first element to the new location? . 

    Even when that is the case, i mean allocate new and copy the content, FastMM has very nice algorithm to recycle the system allocated blocks, FastMM request and allocate chunks from OS, lets say we have 1 MB allocated, FastMM will slice it into smaller chunks and provide the application with requested sizes, when a requested size is not available, it will request another big check 1MB and repeat, the probability of you cause fragmentation is very low, as it only will happen with the 1MB(s) chunks, and to do this you will have to requested and not freed too many blocks (huge amount of small sizes) , then free have of them in specific order to cause, but when your lets say list will grow above certain size then FastMM will not utilize the sliced chunks, it will request new form the OS and free (return it to OS) it when done, hence no matter how you changed or resize your list or even tens/hundreds of them you will not be able to cause any memory fragmentation, nor memory access degradation, the performance will more or less stay the same.

     

    Relocating on single delete as Stefan pointed is a problem, so do as Dalija suggested or implement you own raw list which will perform relocate on delete, it is easy.

    One more thing, many thinks linked lists are good and fast, and they are but only when the items count reach something around 500k item.

     

    Another thing, if your list doesn't need to be ordered, then on delete move/copy the last item on top the deleted one and decrease the count, hence remove few overheads, in such case your list will need size (capacity) and count (actual items count), grow and resize only when count is equal to size, and never downsize !, the logic is simple here, this size needed once so it could be needed again.


  18. 21 minutes ago, A.M. Hoornweg said:

    I'm worried that there might be a risk of excessive memory fragmentation and I'd like to hear your opinions on this.

    Don't worry there, as long you are using FastMM (or the default memory manager, they are the same) , there is no memory fragmentation, in fact it is impossible to fragment the memory when using FastMM with one allocation, these lists/arrays... are one allocation from the point of view of MM, hence however you operate on them like delete and append, it always one continuous allocation. 


  19. You still can (and may be should) use the record variant as Stefan suggested, only extended it a little from case boolean to case integer with 0,1 and 2 where you can align the needed types, in the third (additional) case just put 

        case Integer of
            0: (
              FRandomness0 : byte;
              FRandomness1 : byte;
              FRandomness2 : byte;
              FRandomness3 : byte;
              FRandomness4 : byte;
              FRandomness5 : byte;
              FRandomness6 : byte;
              FRandomness7 : byte;
              FRandomness8 : byte;
              FRandomness9 : byte);
            1: (
              FRandomness0_1 : Word;
              FRandomness2_9 : UInt64)
            2: (
              FRandomness0_N : NativeUInt);

    There is no need for padding, the compiler should take care of that.


  20. 18 minutes ago, Vincent Parrett said:
    3 hours ago, Kas Ob. said:

    in other words add/use Random1 and Random2, use only one Next.

    This made no discernable difference.

    It is expected with your modern CPU, but the difference will be noticeable when the CPU under high usage, this is just for information and you don't need to do it, i liked to share.

     

    Anyway, on my XE8 i found one of these loch ness monsters, the one that i saw in real life and in dreams, the ones that very hard to catch red handed, when the compiler throw the towel and silently produce unoptimized ugly assembly instructions.

     

    This is reproducible on my XE8 and would love to know if this case is still in newer versions:

    1) create new and empty console applicaiton.

    2) add VSoft.Ulid

    3) put this line in the main procedure, 

    uses
      System.SysUtils,
      VSoft.Ulid;
    
    begin
      TUlid.Create;  // we don't care now for memory leak or whatevery
    end.

    4) add 64-bit platform and enable optimize, keep stack frame enabled.

    5) use this create, the same i used earlier

    class function TUlid.InternalNewUlid(timestamp: UInt64): TUlid;
    var
      random : UInt64;
      ts : Int64Rec absolute timestamp;
    begin
      result := default(TUlId);
      //reverse order!
      result.FTimeStamp0 := ts.Bytes[5];
      result.FTimeStamp1 := ts.Bytes[4];
      result.FTimeStamp2 := ts.Bytes[3];
      result.FTimeStamp3 := ts.Bytes[2];
      result.FTimeStamp4 := ts.Bytes[1];
      result.FTimeStamp5 := ts.Bytes[0];
      random := FXorShift64.Next;
    
      PNativeUInt(@result.FRandomness0)^ := NativeUInt(random);
      random := FXorShift64.Next;
      Move(random,result.FRandomness2, 8); //  comment and uncomment this line and see the difference in assembly code
      PUInt64(@result.FRandomness0)^ := random;
    end;

    The result is astonishing !

     

    Here the difference from my XE8

    image.thumb.png.993a0aa871eb5265dd21dd9372b311ca.pngimage.thumb.png.340d57f719102fb3dff2990736cc3973.png

     

    The compiler with move behaved differently and stupidly.

     

    One side note this is also really annoying, though i don't know if this still the same for the newer and enhanced versions

    image.png.f9f26e21e43c5d6f7f8d496156720064.png 

    it could be handled way better for these limited size like up to 128bit.


  21. One more thing that bothering me, and you might test and confirm as you or somebody have some insight on this:

     

    Do the borders scale ?

    I mean the border is there for reason and they are one pixel wide, for the window title, but when scaled to 200% do they become 2 pixel ? or they stayed as 1pixel? 

    See, windows shadow bothered me in the past and they changed few times over many Windows versions, their scaling too.


  22. 13 hours ago, JonRobertson said:

    Does anyone know why using a value of one less than captionHeight results in the entire caption bar being painted?

    I still thinking about this, and as no one added any input then i add my assumption.

     

    I think the logic behind this is very simple, Windows has its default drawing (themed or not) and if you are changing its parameters in whole or in some then you are on your own.

    In examples might be easier to explain, like, while you change the height of an item in ListBox or ListView, then the default drawing/rendering for item will perform according to its default no matter what you have set, if you set the item height to 3 pixel then you will see the item drawn by OS stayed as default like 8 pixel and the items will overlap, same if you set them to something like 50 pixel, the system will not check and adjust the font size neither the image or checkboxes...

     

    Its like an unspoken rule, if you are changing the height then you have to continue and override the default drawing, and draw your own title/item.. with your own drawing function and your own font, brush, pen....


  23. @Vincent Parrett I would suggest to benchmark the following

    class function TUlid.InternalNewUlid(timestamp: UInt64): TUlid;
    var
      random : UInt64;
      ts : Int64Rec absolute timestamp;
    begin
      result := default(TUlId);
      //reverse order!
      result.FTimeStamp0 := ts.Bytes[5];
      result.FTimeStamp1 := ts.Bytes[4];
      result.FTimeStamp2 := ts.Bytes[3];
      result.FTimeStamp3 := ts.Bytes[2];
      result.FTimeStamp4 := ts.Bytes[1];
      result.FTimeStamp5 := ts.Bytes[0];
      random := FXorShift64.Next;
      //Move(random,result.FRandomness0, 2); // randomness 0-1
      //PWORD(@result.FRandomness0)^ := UInt16(random);      // the exact size
      PNativeUInt(@result.FRandomness0)^ := NativeUInt(random);  // it is safe to overflow here, might yeild better and simpler asm instruction
      random := FXorShift64.Next;
      //Move(random,result.FRandomness2, 8); // randomness 2-9
      PUInt64(@result.FRandomness0)^ := random;
    end;

    Removing the overhead of Move should count for something, i guess !!

     

×