Alcinoe — це бібліотека компонентів з відкритим вихідним кодом для Delphi та C++Builder, яка підтримується Zeus64 на GitHub. Вона охоплює сферу, яку VCL та FireMonkey RTL залишають третім сторонам: відеопрогравач з GPU-прискоренням, обгортку WebRTC, нативні елементи керування редагуванням для iOS та Android, дворежимний парсер JSON/BSON, клієнт MongoDB з пулом з'єднань, обгортку ImageMagick та набір елементів керування FireMonkey, які повністю оминають стандартний конвеєр рендерингу. Бібліотека здобула репутацію на Rio (10.3.3) та Sydney (10.4.2), і відтоді слідує за кожним релізом Embarcadero. На момент написання статті вона повністю сумісна з Delphi 11.1 Alexandria та Delphi Athens 12.3
Встановлення Alcinoe у проект
Процес встановлення залежить від одного питання: чи потрібна вам підтримка візуальних компонентів Alcinoe під час проектування? Якщо ні, повністю пропустіть BPL. Додайте {alcinoe_rootdir}\source до шляхів пошуку бібліотек проекту, і ви закінчили. Кожен невізуальний компонент, включаючи парсери, клієнти баз даних та утиліти для роботи з рядками, компілюється з вихідного коду без реєстрації будь-чого
Якщо вам потрібна підтримка під час проектування, шлях трохи довший. Відкрийте Component > Install Packages в IDE Delphi, знайдіть BPL, який відповідає вашій версії (наприклад, {alcinoe_rootdir}\lib\bpl\alcinoe\Win32\alexandria\Alcinoe_alexandria.bpl), встановіть його, а потім все одно додайте {alcinoe_rootdir}\source до шляху пошуку. BPL реєструє компоненти; каталог з вихідним кодом — це те, що компілятор знаходить під час компіляції вашого проекту
Alcinoe постачає необов'язкові патчі до вихідних кодів Embarcadero RTL. Якщо вони вам потрібні, перейдіть до {alcinoe_rootdir}\embarcadero\, виберіть підкаталог для вашої версії та запустіть update.bat. Скрипт очікує GIT у PATH і припускає стандартне місце встановлення Embarcadero. Він завантажує оригінальний вихідний код RTL і застосовує патчі. Після завершення додайте цей виправлений каталог з вихідним кодом до шляху пошуку вашого проекту, щоб компілятор вибирав його замість копії лише для читання в дереві встановлення Embarcadero. Нічого з цього не потрібно для початку; це має значення лише тоді, коли ви стикаєтеся з помилками, які ці патчі вирішують
Android та проксі D8 для дешугарингу
Кілька компонентів Alcinoe (WebRTC, відео на базі ExoPlayer) залежать від Java-бібліотек, які використовують можливості мови Java 8. Інструментарій Android, який постачається зі старими версіями Delphi, використовує dx.bat для перетворення DEX, який не може обробляти ці байт-коди на рівнях API нижче 26. Рішенням є дешугаринг (desugaring), який D8 обробляє автоматично під час безпосереднього виклику. Alcinoe надає проксі-скрипт за адресою {alcinoe_rootdir}\tools\D8Proxy\dx.bat, який перенаправляє виклики від системи збірки Delphi до D8, роблячи дешугаринг прозорим. Замініть оригінальний dx.bat у каталозі build-tools вашого Android SDK (зазвичай C:\SDKs\android\build-tools\30.0.3\) на цей проксі. Embarcadero відстежував основну проблему в RSP-24155; пізніші версії інструментів SDK вирішили її безпосередньо, тому перевірте, чи все ще потрібен обхідний шлях для вашого поточного інструментарію
Проблема рендерингу FireMonkey та відповідь Alcinoe
Стандартний цикл малювання FireMonkey стає вузьким місцем у користувацьких інтерфейсах із інтенсивним прокручуванням. Один TRectangle із закругленими кутами може зайняти близько 3 мс на перемальовування, оскільки стандартна реалізація перераховує шлях на кожному кадрі. При 20 таких видимих елементах керування це додає до 60 мс на прохід кадру, що обмежує ефективну частоту кадрів значно нижче порогу плавного прокручування
Alcinoe вирішує це за допомогою буфера в GPU для кожного елемента керування. Перше малювання рендерить елемент керування у TTexture, що зберігається в пам'яті GPU. Наступні перемальовування блітять цю текстуру замість повторного виконання алгоритму малювання. Виміряний результат на тому ж прямокутнику із закругленими кутами падає з близько 3 мс до близько 0.1 мс. Крім буферизації, Alcinoe замінює малювання шляхів OpenGL для базових фігур нативними API малювання Android та iOS, уникаючи компромісу між якістю та продуктивністю, пов'язаного з Form.Quality. Відповідні елементи керування — це TALRectangle, TALCircle та набір покращених контейнерів макетів, включаючи ScrollBox та TabControl
TALJsonDocument: DOM та SAX в одному типі
TALJsonDocument — це парсер JSON та BSON від Alcinoe. Він підтримує два режими обходу. Режим DOM будує дерево об'єктів у пам'яті, надаючи довільний доступ до будь-якого вузла ціною пам'яті, пропорційної розміру документа. Режим SAX запускає події, коли парсер читає кожен токен, не зберігаючи жодного дерева, що є правильним вибором, коли вам потрібно відфільтрувати великий документ і зберегти лише кілька значень. DOM-парсери в Delphi (DBXJSON, SuperObject та інші) зазвичай у три-п'ять разів повільніші за підхід SAX для того ж вмісту, оскільки кожне виділення вузла несе накладні витрати на створення об'єкта поверх самої роботи парсингу
Цей тип слідує тому ж патерну навігації по вузлах, що й TALXMLDocument. Мінімальне читання DOM виглядає так:
MyJsonDoc.LoadFromJSON(AJsonStr, False {dom mode});
MyJsonDoc.ParseOptions := [poAllowComments];
// read scalar values
ShowMessage(MyJsonDoc.ChildNodes['name'].ChildNodes['first'].Text);
ShowMessage(IntToStr(MyJsonDoc.ChildNodes['_id'].Int32));
// iterate an array
for I := 0 to MyJsonDoc.ChildNodes['contribs'].ChildNodes.Count - 1 do
Writeln(MyJsonDoc.ChildNodes['contribs'].ChildNodes[I].Text);
Для режиму SAX призначте анонімну процедуру OnParseText перед викликом LoadFromJSON з другим аргументом, встановленим у True. Зворотний виклик отримує шлях до вузла, ім'я, значення та TALJSONNodeSubType, який ідентифікує тип JSON (рядок, ціле число, число з плаваючою комою, логічне значення тощо). Цей режим не створює виділень у купі для вузлів, тому він масштабується до документів довільного розміру, не вичерпуючи бюджет пам'яті
TALJsonDocument також читає та пише BSON нативно; передайте True як прапорець BSON до LoadFromFile або SaveToFile. Другий варіант, TALJsonDocumentU, використовує UnicodeString (UTF-16) внутрішньо замість AnsiString (UTF-8) для контекстів, де навколишній код працює в Unicode всюди
Клієнт MongoDB та пулінг з'єднань
Драйвер MongoDB від Alcinoe охоплює типові операції запитів і нативно обробляє пулінг з'єднань. Простий клієнт, TAlMongoDBClient, відкриває та закриває одне з'єднання на кожну операцію. Варіант з пулом, TAlMongoDBConnectionPoolClient, підтримує набір активних з'єднань і передає одне з них кожному викликаючому потоку з пулу, повертаючи його після завершення виклику. Ця модель не дозволяє кільком потокам блокувати один одного на етапі встановлення з'єднання, що має значення, коли фонові робочі процеси одночасно опитують одну й ту саму базу даних. Для курсорів, що підтримують постійне з'єднання (tailable cursors) на обмежених колекціях (capped collections), TAlMongoDBTailMonitoringThread спостерігає за новими документами та запускає зворотний виклик, коли вони надходять, що є стандартним патерном для потокової передачі журналів або сповіщень про зміни без регулярного опитування
Інші компоненти, про які варто знати
ALVideoPlayer рендерить відео в TTexture, а не в оверлейне вікно, тому інші елементи керування FireMonkey можуть розташовуватися над ним у Z-порядку. Бекенд Android використовує ExoPlayer, який додає підтримку DASH, HLS та SmoothStreaming поза межами того, що обробляє вбудований MediaPlayer Android. Бекенд iOS використовує AVPlayer з еквівалентною підтримкою HLS
TALWebRTC обгортає стек WebRTC для однорангового (peer-to-peer) аудіо та відео. Він не вимагає браузера або плагіна, а з'єднання проходить через NAT за допомогою стандартного узгодження ICE/STUN/TURN, яке обробляє базова бібліотека
TALStringList замінює сортування TStringList на базі AnsiCompareText на незалежне від локалі порядкове порівняння та швидке сортування (quicksort), яке працює до 10 разів швидше на великих списках. Хешований варіант, TALHashedStringList, додає внутрішню хеш-таблицю для пошуку за час O(1) ціною трохи вищих накладних витрат на невеликих списках. Зауважте, що TALStringList — це 8-бітний список AnsiString, а не Unicode; він добре підходить для серверного коду, де UTF-8 є робочим кодуванням, а чиста пропускна здатність має більше значення, ніж порівняння з урахуванням локалі
На 64-бітній Windows спадщина FastCode, яка дала багатьом рядковим підпрограмам Alcinoe їхню перевагу у швидкості (переважно написаний вручну асемблер x86), не переноситься. Збірки Win64 повертаються до реалізацій на Pascal, які працюють помітно повільніше на робочих навантаженнях з інтенсивним використанням рядків. Проект demo\ALStringBenchMark дозволяє вам виміряти розрив на вашому обладнанні, перш ніж переходити до 64-бітної збірки, де пропускна здатність рядків є вузьким місцем
Повний вихідний код знаходиться на github.com/Zeus64/alcinoe