Jump to content

Leaderboard


Popular Content

Showing content with the highest reputation on 08/06/24 in Posts

  1. Hi All I created a Delphi implementation of ULID - A Universally Unique Lexicographically Sortable Identifier. ULID's are a better option than GUIDs where sorting is needed 128-bit compatibility with UUID 1.21e+24 unique ULIDs per millisecond Lexicographically sortable! Canonically encoded as a 26 character string, as opposed to the 36 character UUID Uses Crockford's base32 for better efficiency and readability (5 bits per character) Case insensitive No special characters (URL safe) Monotonic sort order (correctly detects and handles the same millisecond) https://github.com/VSoftTechnologies/VSoft.Ulid
  2. Vincent Parrett

    VSoft.Ulid - A Delphi Implementation of ULID for Delphi XE2 or later.

    @Stefan Glienke is way ahead of you already sent me a few suggestions. Using a variant record case Boolean of True: ( FRandomness0 : byte; FRandomness1 : byte; FRandomness2 : byte; FRandomness3 : byte; FRandomness4 : byte; FRandomness5 : byte; FRandomness6 : byte; FRandomness7 : byte; FRandomness8 : byte; FRandomness9 : byte); False: ( FRandomness0_1 : Word; FRandomness2_9 : UInt64); Did squeeze a out few ns better performance. I'll test your idea though - because micro optimising is more fun the boring stuff I was working on... 😉
  3. Vincent Parrett

    VSoft.Ulid - A Delphi Implementation of ULID for Delphi XE2 or later.

    This made no discernable difference. Your version of avoiding the move was slightly faster than the variant array idea. Thanks to a bunch of suggestions from Stefan & Kas it is now a lot faster than the c# version I was comparing it to (64bit create - 63ns) - I did also add some validation to the parse method which slowed it down again! The big speedup in the Parse method was avoiding TEncoding.GetBytes - which uses a dynamic array (slow). D 11.3 - 32bit ------------------------------------------------------------------- Benchmark Time CPU Iterations ------------------------------------------------------------------- TUlid.Create 52.1 ns 51.6 ns 10000000 TUlid.Parse - AnsiString 28.4 ns 28.5 ns 23578947 TUlid.Parse 33.7 ns 33.0 ns 20363636 TUlid.ToString 32.0 ns 32.5 ns 23578947 D 11.3 64bit ------------------------------------------------------------------- Benchmark Time CPU Iterations ------------------------------------------------------------------- TUlid.Create 10.9 ns 11.0 ns 64000000 TUlid.Parse - AnsiString 28.1 ns 28.5 ns 23578947 TUlid.Parse 29.2 ns 29.5 ns 24888889 TUlid.ToString 29.4 ns 29.3 ns 22400000 D12.1 32bit ------------------------------------------------------------------- Benchmark Time CPU Iterations ------------------------------------------------------------------- TUlid.Create 51.2 ns 51.6 ns 10000000 TUlid.Parse - AnsiString 28.5 ns 28.3 ns 24888889 TUlid.Parse 30.2 ns 30.5 ns 23578947 TUlid.ToString 30.2 ns 29.3 ns 22400000 D 12.1 64bit ------------------------------------------------------------------- Benchmark Time CPU Iterations ------------------------------------------------------------------- TUlid.Create 8.94 ns 8.79 ns 74666667 TUlid.Parse - AnsiString 28.1 ns 28.5 ns 26352941 TUlid.Parse 32.8 ns 32.2 ns 21333333 TUlid.ToString 33.0 ns 33.0 ns 20363636 Anyway, that's more than fast enough - given that this is more than likely used to generate id's for use with databases or in messaging - it's unlikely you would ever hit a performance issue with this! https://github.com/VSoftTechnologies/VSoft.Ulid/tree/f-optimise I'll merge into main shortly. Edit : forgot to mention, the reason 32bit is still slower than 64bit is due to way the compiler does Int64 division (calls a helper function).
  4. Vincent Parrett

    Upgrading to new RAD Studio vs. project Lib names

    This is a pet peeve of mine, library authors and package naming - not using LibSuffix in their packages and adding the version to the package names. If you are not using runtime packages (most people don't) - then the IDE doesn't really track what is used by the project - it just uses the Library path and project Search path to make those libraires available. I have been working on a solution for a while - a package manager - still a work in progress but getting there - see delphi.dev - it will need library author/vendors to create packages to make it work though. DPM does store the package references in the dproj file - when you load up the project in a new version of RAD Studio (and the ide plugin is installed) - it will automatically download and install the dpm packages (assuming there are versions available for the new delphi version).
  5. vfbb

    Rounded polygon

    Here's an example: uses Skia; function MakeCubicSplineInterpolation(const APoints: TArray<TPointF>): ISkPath; var LPathBuilder: ISkPathBuilder; LSegments: Integer; I: Integer; mx: Single; my: Single; LScratches: array of record a, b, c, r, p: TPointF; end; begin LPathBuilder := TSkPathBuilder.Create; if Length(APoints) < 2 then Exit(LPathBuilder.Detach); if Length(APoints) = 2 then begin LPathBuilder.MoveTo(APoints[0]); LPathBuilder.LineTo(APoints[1]); Exit(LPathBuilder.Detach); end; LSegments := Length(APoints) - 1; SetLength(LScratches, LSegments); LScratches[0].a := PointF(0, 0); LScratches[0].b := PointF(2, 2); LScratches[0].c := PointF(1, 1); LScratches[0].r := PointF(APoints[0].X + 2 * APoints[1].X, APoints[0].Y + 2 * APoints[1].Y); for I := 1 to LSegments - 2 do begin LScratches[I].a := PointF(1, 1); LScratches[I].b := PointF(4, 4); LScratches[I].c := PointF(1, 1); LScratches[I].r := PointF(4 * APoints[i].X + 2 * APoints[I + 1].X, 4 * APoints[I].Y + 2 * APoints[I + 1].Y); end; LScratches[LSegments - 1].a := PointF(2, 2); LScratches[LSegments - 1].b := PointF(7, 7); LScratches[LSegments - 1].c := PointF(0, 0); LScratches[LSegments - 1].r := PointF(8 * APoints[LSegments - 1].X + APoints[LSegments].X, 8 * APoints[LSegments - 1].Y + APoints[LSegments].Y); for I := 1 to LSegments - 1 do begin mx := LScratches[I].a.X / LScratches[I - 1].b.X; my := LScratches[I].a.Y / LScratches[I - 1].b.Y; LScratches[I].b := LScratches[I].b - PointF(mx * LScratches[I - 1].c.X, my * LScratches[I - 1].c.Y); LScratches[I].r := LScratches[I].r - PointF(mx * LScratches[I - 1].r.X, my * LScratches[I - 1].r.Y); end; LScratches[LSegments - 1].p := PointF(LScratches[LSegments - 1].r.X / LScratches[LSegments - 1].b.X, LScratches[LSegments - 1].r.Y / LScratches[LSegments - 1].b.Y); for I := Length(APoints) - 3 downto 0 do begin LScratches[I].p := PointF((LScratches[I].r.X - LScratches[I].c.X * LScratches[I + 1].p.X) / LScratches[I].b.X, (LScratches[I].r.Y - LScratches[I].c.Y * LScratches[I + 1].p.Y) / LScratches[I].b.Y); end; LPathBuilder.MoveTo(APoints[0]); for I := 0 to LSegments - 2 do begin LPathBuilder.CubicTo(LScratches[I].p, PointF(2 * APoints[I + 1].X - LScratches[I + 1].p.X, 2 * APoints[I + 1].Y - LScratches[I + 1].p.Y), APoints[I + 1]); end; LPathBuilder.CubicTo(LScratches[LSegments - 1].p, PointF(0.5 * (APoints[LSegments].X + LScratches[LSegments - 1].p.X), 0.5 * (APoints[LSegments].Y + LScratches[LSegments - 1].p.Y)), APoints[LSegments]); Result := LPathBuilder.Detach; end; procedure TForm1.SkPaintBox1Draw(ASender: TObject; const ACanvas: ISkCanvas; const ADest: TRectF; const AOpacity: Single); var LPaint: ISkPaint; LMyPoints: TArray<TPointF>; begin LMyPoints := [PointF(62, 511), PointF(162, 605), PointF(262, 610), PointF(362, 402), PointF(462, 959), PointF(562, 58), PointF(662, 272), PointF(762, 99), PointF(862, 759), PointF(962, 945)]; LPaint := TSkPaint.Create(TSkPaintStyle.Stroke); LPaint.Color := TAlphaColors.Red; LPaint.AntiAlias := True; LPaint.StrokeWidth := 3; LPaint.StrokeCap := TSkStrokeCap.Round; ACanvas.DrawPath(MakeCubicSplineInterpolation(LMyPoints), LPaint); LPaint.StrokeWidth := 10; LPaint.Color := TAlphaColors.Black; ACanvas.DrawPoints(TSkDrawPointsMode.Points, LMyPoints, LPaint); end; Result: Note: You don't need to use Skia, it was just a facilitator for the example.
  6. Clément

    Synedit Help

    Hi! I'm using Synedit to built a small SQL editor, but I need to change some default behavior. 1) I need to trigger Auto-completion when a '.' is typed, but I don't want to display the Completion Form base on a timer interval. Since the data is obtained asynchronously, I want to fill and display the completion form after receiving a specific custom message (WM_GETCOMPLETIONDATA). When handling the message, I will fill the list and display the form. Is there a way to call the completion form on demand? (the filling and displaying follows...) 2) I need to fill the autocompletion list based on the previous token value. The user can type a SQL select as select c.| from categories c (The '|' is the current cursor position). As the user types the '.', I would like to fill the completion list with all the columns from categories table. As '.' is typed, I can get the previous token ( which is "c" in this case), and do some parsing to discover "c" is in fact an alias for "Categories", send a custom message (WM_GETCOMPLETIONDATA) and when receiving the data, I can fill the completion form with all fields from categories table. It seems to me that SynEditProposal.OnExecute is the correct event to handle all the parsing. Is there a better one? TIA, Clément
  7. Alexander Sviridenkov

    Synedit Help

    Maybe to use preloaded database schema? In this case completion can be filled synchronously with no significant delay.
×