Alcinoe este o bibliotecă de componente open-source pentru Delphi și C++Builder, întreținută pe GitHub de Zeus64. Acoperă exact teritoriul pe care VCL și RTL-ul FireMonkey îl lasă în seama terților: un player video accelerat pe GPU, un wrapper peste WebRTC, controale native de editare pentru iOS și Android, un parser JSON/BSON cu două moduri, un client MongoDB cu connection pooling, un wrapper peste ImageMagick și o colecție de controale FireMonkey care ocolesc complet pipeline-ul standard de randare. Biblioteca și-a construit reputația pe Rio (10.3.3) și Sydney (10.4.2), iar de atunci a ținut pasul cu fiecare release Embarcadero. La momentul scrierii, este pe deplin compatibilă cu Delphi 11.1 Alexandria și Delphi Athens 12.3
Cum aduceți Alcinoe într-un proiect
Instalarea se împarte după o singură întrebare: aveți nevoie de suport design-time pentru controalele vizuale din Alcinoe? Dacă nu, săriți complet peste BPL. Adăugați {alcinoe_rootdir}\source în library search path-ul proiectului și ați terminat. Toate componentele non-vizuale, inclusiv parserele, clienții de baze de date și utilitarele pentru șiruri, se compilează din sursă fără să înregistrați nimic
Când chiar aveți nevoie de suport design-time, drumul e puțin mai lung. Deschideți Component > Install Packages în IDE-ul Delphi, mergeți la BPL-ul care corespunde versiunii voastre (de exemplu {alcinoe_rootdir}\lib\bpl\alcinoe\Win32\alexandria\Alcinoe_alexandria.bpl), instalați-l și tot trebuie să adăugați apoi {alcinoe_rootdir}\source în search path. BPL-ul înregistrează componentele; directorul sursă este ceea ce găsește compilatorul atunci când compilează proiectul vostru
Alcinoe livrează și patch-uri opționale pentru sursele RTL de la Embarcadero. Dacă le vreți, mergeți în {alcinoe_rootdir}\embarcadero\, alegeți subdirectorul versiunii voastre și rulați update.bat. Scriptul se așteaptă să găsească GIT în PATH și presupune locația implicită de instalare Embarcadero. Descarcă sursa RTL originală și aplică patch-urile. După aceea, adăugați directorul cu surse patch-uite în search path-ul proiectului, ca să fie găsit de compilator înaintea copiei read-only din arborele de instalare Embarcadero. Nimic din toate acestea nu e necesar ca să porniți; contează doar dacă loviți bug-urile pe care le rezolvă patch-urile
Android și proxy-ul de desugaring D8
Câteva componente Alcinoe (WebRTC, video-ul bazat pe ExoPlayer) depind de biblioteci Java care folosesc funcționalități de limbaj din Java 8. Toolchain-ul Android livrat cu versiunile mai vechi de Delphi folosește dx.bat pentru conversia în DEX, iar acesta nu poate digera acele bytecode-uri pe API level sub 26. Rezolvarea este desugaring-ul, pe care D8 îl face automat când este invocat direct. Alcinoe pune la dispoziție un script proxy în {alcinoe_rootdir}\tools\D8Proxy\dx.bat, care redirecționează apelurile de la build system-ul Delphi către D8 și face desugaring-ul transparent. Înlocuiți dx.bat original din directorul build-tools al SDK-ului Android (de obicei C:\SDKs\android\build-tools\30.0.3\) cu acest proxy. Embarcadero a urmărit problema de fond la RSP-24155; versiunile mai noi de SDK tools au rezolvat-o direct, așa că verificați dacă toolchain-ul vostru actual mai are nevoie de workaround
Problema de randare din FireMonkey și răspunsul Alcinoe
Ciclul implicit de paint din FireMonkey devine un bottleneck în interfețele cu mult scroll. Un singur TRectangle cu colțuri rotunjite poate consuma în jur de 3 ms la redesenare, pentru că implementarea standard recalculează path-ul la fiecare cadru. Cu 20 de astfel de controale vizibile, se adună 60 ms per trecere de cadru, ceea ce plafonează frame rate-ul efectiv mult sub pragul unui scroll fluid
Alcinoe rezolvă asta cu un buffer rezident pe GPU pentru fiecare control. Primul paint randează controlul într-un TTexture ținut în memoria GPU. Redesenările ulterioare fac blit pe acea textură în loc să reruleze algoritmul de paint. Rezultatul măsurat pe același dreptunghi rotunjit scade de la circa 3 ms la circa 0,1 ms. Dincolo de buffering, Alcinoe înlocuiește desenarea de path-uri prin OpenGL pentru formele simple cu API-urile native de desenare din Android și iOS, ocolind compromisul calitate/performanță legat de Form.Quality. Controalele relevante sunt TALRectangle, TALCircle și un set de containere de layout îmbunătățite, printre care un ScrollBox și un TabControl
TALJsonDocument: DOM și SAX într-un singur tip
TALJsonDocument este parserul de JSON și BSON din Alcinoe. Suportă două moduri de parcurgere. Modul DOM construiește un arbore de obiecte în memorie și oferă acces aleatoriu la orice nod, cu prețul unui consum de memorie proporțional cu dimensiunea documentului. Modul SAX declanșează evenimente pe măsură ce parserul citește fiecare token, fără să rețină vreun arbore, și este alegerea corectă atunci când filtrați un document mare și păstrați doar câteva valori. Parserele DOM din Delphi (DBXJSON, SuperObject și celelalte) sunt de regulă de trei până la cinci ori mai lente decât o abordare SAX pe același conținut, pentru că fiecare alocare de nod adaugă overhead-ul de creare a obiectului peste munca de parsare propriu-zisă
Tipul urmează același model de navigare pe noduri ca TALXMLDocument. O citire DOM minimală arată așa:
MyJsonDoc.LoadFromJSON(AJsonStr, False {dom mode});
MyJsonDoc.ParseOptions := [poAllowComments];
// citiți valori scalare
ShowMessage(MyJsonDoc.ChildNodes['name'].ChildNodes['first'].Text);
ShowMessage(IntToStr(MyJsonDoc.ChildNodes['_id'].Int32));
// parcurgeți un tablou
for I := 0 to MyJsonDoc.ChildNodes['contribs'].ChildNodes.Count - 1 do
Writeln(MyJsonDoc.ChildNodes['contribs'].ChildNodes[I].Text);
Pentru modul SAX, atribuiți o procedură anonimă lui OnParseText înainte de a apela LoadFromJSON cu al doilea argument setat pe True. Callback-ul primește calea nodului, numele, valoarea și un TALJSONNodeSubType care identifică tipul JSON (string, integer, float, boolean și așa mai departe). Modul acela nu produce alocări pe heap pentru noduri, deci scalează la documente oricât de mari fără să spargă bugetul de memorie
TALJsonDocument citește și scrie și BSON nativ; treceți True ca flag BSON către LoadFromFile sau SaveToFile. O a doua variantă, TALJsonDocumentU, folosește intern UnicodeString (UTF-16) în loc de AnsiString (UTF-8), pentru contextele în care codul din jur lucrează integral în Unicode
Clientul MongoDB și connection pooling
Driverul de MongoDB din Alcinoe acoperă operațiile uzuale de interogare și se ocupă nativ de connection pooling. Clientul simplu, TAlMongoDBClient, deschide și închide câte o conexiune per operație. Varianta cu pool, TAlMongoDBConnectionPoolClient, menține un set de conexiuni vii și dă câte una fiecărui thread apelant din pool, primind-o înapoi când apelul se încheie. Modelul acesta împiedică thread-urile să se blocheze reciproc la stabilirea conexiunii, ceea ce contează ori de câte ori niște workeri de fundal interoghează simultan aceeași bază de date. Pentru cursoare tailable pe colecții capped, TAlMongoDBTailMonitoringThread urmărește documentele noi și declanșează un callback la sosirea lor, adică modelul standard pentru streaming de log-uri sau notificări de schimbare fără polling
Alte componente care merită știute
ALVideoPlayer randează video într-un TTexture, nu într-o fereastră overlay, așa că alte controale FireMonkey pot sta deasupra lui în Z-order. Backend-ul de Android folosește ExoPlayer, care aduce suport pentru DASH, HLS și SmoothStreaming, dincolo de ce acoperă MediaPlayer-ul integrat din Android. Backend-ul de iOS folosește AVPlayer, cu suport HLS echivalent
TALWebRTC împachetează stiva WebRTC pentru audio și video peer-to-peer. Nu are nevoie de browser sau de plugin, iar conexiunea traversează NAT-ul prin negocierea standard ICE/STUN/TURN de care se ocupă biblioteca de dedesubt
TALStringList înlocuiește sortarea bazată pe AnsiCompareText din TStringList cu o comparație ordinală independentă de locale și cu un quicksort de până la 10x mai rapid pe liste mari. Varianta cu hash, TALHashedStringList, adaugă o tabelă de hash internă pentru căutare O(1), cu prețul unui overhead ușor mai mare pe listele mici. Rețineți că TALStringList este o listă de AnsiString pe 8 biți, nu una Unicode; se potrivește bine în cod de server, unde UTF-8 este codificarea de lucru și unde throughput-ul brut contează mai mult decât comparația sensibilă la locale
Pe Windows pe 64 de biți, moștenirea FastCode care a dat avantajul de viteză multora dintre rutinele de șiruri din Alcinoe (în mare parte assembly x86 scris de mână) nu se transferă. Build-urile Win64 cad înapoi pe implementările Pascal, care rulează vizibil mai lent pe încărcări intensive cu șiruri. Proiectul demo\ALStringBenchMark vă lasă să măsurați diferența pe hardware-ul vostru înainte de a vă angaja într-un build pe 64 de biți acolo unde throughput-ul pe șiruri este un bottleneck
Sursa completă se află la github.com/Zeus64/alcinoe