среда, декабря 01, 2010

Вы все еще верите докам?

Вот что говорит Platform SDK о реализации метода Invoke, в части о возвращаемых значениях, интерфейса IDispatch:

DISP_E_NONAMEDARGS

This implementation of IDispatch does not support named arguments.

А вот что говорит система получив DISP_E_NONAMEDARGS:

Eng:

---------------------------
test
---------------------------
No named arguments.
---------------------------
OK  
---------------------------

Rus:

---------------------------
test
---------------------------
Именованные аргументы отсутствуют.
---------------------------
ОК  
---------------------------

 

Совсем одно и то же, правда?

четверг, ноября 25, 2010

Условная компиляция: defines vs. const

Те кому приходится обеспечивать работоспособность кода на нескольких версиях Delphi не по наслышке знают о “прелестях” условной компиляции. Довольно сложно держать в голове набор фич каждой версии и особенности их работы. Ориентация на CompilerVersion, RTLVersion и VERXXX кажется простым делом в момент написания кода, но превращается в кошмар к моменту его рефакторинга т.к. все детали из головы уже выветрились и скучные выражения вроде {$IF CompilerVersion >= 18.5} ясности не добавляют. Обычно эта проблема решается с помощью включаемых файлов (те, что с расширением .inc), где, основываясь на значениях перечисленных выше сущностей (и возможно некоторых других), создается некий набор определений (defines) с читаемыми именами. Делается это, например, так:

{$IFNDEF FPC}
{$DEFINE DCC}
{$ENDIF}
...
// Anonymous methods
{$IF Defined(DCC) and (CompilerVersion >= 20)}
{$DEFINE HAS_ANON_METHODS}
{$IFEND}


Тут мы видим создаваемое определение HAS_ANON_METHODS, которое в дальнейшем позволит нам не вспоминать о версии компилятора, а просто и удобно писать код:



Type
TMyEvent = {$IFDEF HAS_ANON_METHODS}Reference To {$ENDIF}Procedure(Sender : TObject) Of Object;


Но так ли, на самом деле, это удобно? Судите сами. CodeInsight не обеспечивает подсказки для определений. Написав “HAS_” и нажав Ctrl+Space вы не получите весь список возможных определений, а значит снова должны все держать в голове. Это не удобно. Лично меня это просто убивало. Однако, решение есть (для версий Delphi начиная с 2005):



Unit Common.Features;

Interface

Type

//
Delphi = Record

Type

//
Platform = Record

Const

Windows = {$IF Defined(MSWINDOWS)}True{$ELSE}False{$IFEND};
Linux = {$IF Defined(LNUX)}True{$ELSE}False{$IFEND};
MacOS = {$IF Defined(MACOS)}True{$ELSE}False{$IFEND};

{$REGION ' Platform check '}

{$IF Ord(Windows) + Ord(Linux) + Ord(MacOS) <> 1}

{$MESSAGE FATAL 'Unknown platform'}

{$IFEND}

{$ENDREGION}

x32 = SizeOf(Pointer) = 4;
x64 = SizeOf(Pointer) = 8;

End;
//

//
Language = Record

Const

Unicode = {$IF Declared(UnicodeString)}True{$ELSE}False{$IFEND};
Generics = CompilerVersion > 18.5;
AnonymousMethods = CompilerVersion >= 20;
Attributes = {$IF Declared(TCustomAttribute)}True{$ELSE}False{$IFEND};

End;
//

//
RTL = Record

Type

//
TObject = Record

Const

ToString = RTLVersion >= 20;

End;
//

//
ObjectInvoke = Record

Const

MaxParams = {$IF RTLVersion < 20}10{$ELSE}32{$IFEND};

End;
//

End;
//

End;
//

Implementation

End.


Это не законченное решение, пока это только концепт. Надеюсь, основная идея понятна. Небольшой пример кода:



scode      := E_FAIL;
bstrSource := {$IF Not Delphi.Language.Unicode}UniUtf8Decode{$IFEND}(ClassName);

{$IF Delphi.RTL.TObject.ToString}

bstrDescription := ExceptObject.ToString

{$ELSE}

If ExceptObject Is Exception Then
bstrDescription := Exception(ExceptObject).Message
Else
bstrDescription := {$IF Not Delphi.Language.Unicode}UniUtf8Decode{$IFEND}(ExceptObject.ClassName);

{$IFEND}


 



Помимо того, что тут мы сразу видим иерархию (и это очень помогает), нам еще и CodeInsight будет помогать (только за пределами фигурных скобок).



26.11.2010



Забыл написать о самом серьезном преимуществе такого решения. Компилятор контролирует корректность идентификаторов (правда с некоторыми оговорками, но это не важно), и если где-то ошибиться в написании то такой код просто не скомпилируется.

суббота, июня 26, 2010

Generics vs. Inline

Директива Inline не действует на методы в дженериках. Кажется, благое дело обернуть работу с динамическими массивами структурой-дженериком дабы увеличить функциональность путем добавления простых методов Add, Remove, Insert и т.п. Но все благие намерения натыкаются на суровую реальность в которой существует компилятор Delphi :( Столкновение произошло на этапе добавления свойства Items с сеттером SetItem и геттером GetItem, которые, конечно-же, были объявлены как Inline ведь их тела состоят ровно из одной строки (делать проверки индексов в столь тонкой обертке не практично и даже вредно), а вызываться они могут очень и очень часто. После проверки выяснилось, что компилятор не разворачивает тела этих методов, а делает их вызовы :( Можно, конечно, было объявить свойство без сеттера и геттера, но такие свойства не могут быть дефолтными, а значит писать код вида arr[index] := x; уже не удастся.

“Что-же так все неуклюже-то?” © Особенности национальной охоты.

понедельник, января 25, 2010

Ужасы… Windows Platform SDK

Пользовался Windows Platform SDK for Windows Vista.  Желая иметь самую свежую документацию скачал новый Windows SDK for Windows 7 and .NET Framework 3.5 Service Pack 1 (Build date: 6/11/2009).  Ужас. Смотрим статью о WideCharToMultiByte:

Список кодовых страниц для которых флаги и некоторые параметры должны быть нулевыми.

Список из нового SDK:

  • 50220
  • 50221
  • 50222
  • 50225
  • 50227
  • 50229
  • 57002 through 57011
  • 65000 (UTF-7)
  • 42 (Symbol)

А вот страницы из прежнего SDK:

  • 50220
  • 50221
  • 50222
  • 50225
  • 50227
  • 50229
  • 52936
  • 54936
  • 57002 through 57011
  • 65000 (UTF7)
  • 42 (Symbol)

 

Как видим, из нового SDK исчезло упоминание страницы 52936 (54936 упоминается в документации дополнительно). Здравствуйте, грабли!

Теперь сравните описания параметров.

Новый SDK:

lpDefaultChar [in]

Optional. Pointer to the character to use if a character cannot be represented in the specified code page. The application sets this parameter to NULL if the function is to use a system default value. To obtain the system default character, the application can call the GetCPInfo or GetCPInfoEx function.

For the CP_UTF7 and CP_UTF8 settings for CodePage, this parameter must be set to NULL. Otherwise, the function fails with ERROR_INVALID_PARAMETER.

lpUsedDefaultChar [out]

Optional. Pointer to a flag that indicates if the function has used a default character in the conversion. The flag is set to TRUE if one or more characters in the source string cannot be represented in the specified code page. Otherwise, the flag is set to FALSE. This parameter can be set to NULL.

For the CP_UTF7 and CP_UTF8 settings for CodePage, this parameter must be set to NULL. Otherwise, the function fails with ERROR_INVALID_PARAMETER.

И прежний SDK:

lpDefaultChar

[in] Pointer to the character to use if a wide character cannot be represented in the specified code page. The application sets this parameter to a null pointer if the function is to use a system default value. To obtain the system default character, the application can call the GetCPInfo or GetCPInfoEx function.

For the code pages listed for dwFlags, this parameter must be set to a null pointer. Otherwise, the function fails with ERROR_INVALID_PARAMETER.

lpUsedDefaultChar
[in] Pointer to a flag that indicates if the function is to use a default character in the conversion. The flag is set to TRUE if a default character is necessary, and to FALSE otherwise. This parameter can be set to a null pointer.

For the code pages mentioned in dwFlags, lpUsedDefaultChar must be a null pointer. Otherwise, the function fails with ERROR_INVALID_PARAMETER.

Нужно ли говорить, что информация в новом SDK не соответствует действительности. Здравствуйте, еще одни грабли! Ужас… Как можно верить такой доке? Остается только проверять.

суббота, октября 31, 2009

Delphi 2010. Быдлокод

Юнит DBXJSON.pas. Я вообще в ауте...

class function TJSONString.Hex(const Digit: Integer): Byte;
var
HexData: TBytes;
begin
SetLength(HexData,16);
HexData[0] := Ord('0');
HexData[1] := Ord('1');
HexData[2] := Ord('2');
HexData[3] := Ord('3');
HexData[4] := Ord('4');
HexData[5] := Ord('5');
HexData[6] := Ord('6');
HexData[7] := Ord('7');
HexData[8] := Ord('8');
HexData[9] := Ord('9');
HexData[10] := Ord('A');
HexData[11] := Ord('B');
HexData[12] := Ord('C');
HexData[13] := Ord('D');
HexData[14] := Ord('E');
HexData[15] := Ord('F');
Result := HexData[Digit];
end;

вторник, октября 20, 2009

Delphi 2010. System.TObject.GetInterface

Полюбуйтесь на код обеспечивающий проверку идентификатора интерфейса на соответствие "magic GUID" ({CEDF24DE-80A4-447D-8C75-EB871DC121FD}), для безопасного кастинга (TMyObject(TMyIntf)) к объектому типу:

if (Int64(ObjCastGUID.D1) = Int64(IID.D1)) and
(Int64(ObjCastGUID.D4) = Int64(IID.D4)) then
Pointer(Obj) := Self;

И это в системном юните... #78808

вторник, мая 26, 2009

Ubuntu 9.04

Четыре дня назад получил по почте новую версию Ubuntu. Итог: оно все лучше и лучше. Несмотря на то, что чего-то не срослось с wubi общие впечатления самые положительные. Очень порадовала, сразу бросившаяся в глаза, качественная настройка саб-пиксельного сглаживания шрифтов. Им бы еще иконки отдизайнить в какой-нибудь конторе, чтоб в едином стиле, да в Гноме немного уменьшить контролы. В общем, мне понравилось :)

воскресенье, мая 17, 2009

#72213 Fixed

На фикс такой очевидной ошибки компилятора потребовалось два месяца... Страшно подумать сколько будут фиксить некорректную инициализацию/финализацию advanced-records, на которую налететь не так просто :( Вот и юзай новые языковые фичи...

вторник, апреля 28, 2009

Три класса секундомеров

Выложен модуль Common.Classes.pas содержащий три класса (advanced-records) секундомеров. TLowResStopwatch на основе GetTickCount, THighResStopwatch на основе QueryPerformanceCounter, и TStopwatch использующий один из предыдущих классов секундомеров в зависимости от аппаратной части компьютера.

вторник, апреля 21, 2009

Исправление в Interface Extender

Исправлена ошибка при работе с динамическим массивом. Проявлялась при попытке добавить несколько интерфейсов на один класс.

понедельник, апреля 20, 2009

Расширенная поддержка интерфейсов для Delphi

В блоге Алексея Тимохина озвучена хотелка касающаяся поддержки интерфейсов базовыми классами. На мой взгляд, идея весьма спорная, во всяком случае при нынешнем положении дел в языке. Однако, реализовать нечно подобное можно уже сейчас ;) Тут я набросал пример, как это может быть реализовано (intfExtender.pas реализует непосредственно механизм расширения, а unit1.pas содержит два примера). Если кому пригодится, буду только рад ;) Универсальность прикладного кода, о которой говорит Алексей, таким образом можно обеспечить.

p.s. Модуль сильно не тестировался, посему никаких гарантий если что ;)

среда, апреля 15, 2009

Очередные ошибки инициализации/финализации в Delphi 2006 - 2009

Отчет #73008 описывает несвоевременную финализцию энумератора являющегося интерфейсом при использовании в основном теле программы (в процедурах и функциях работает нормально), что в некоторых случаях может приводить к ошибкам доступа (Access violation).

Отчет #73009 описывает неадекватную инициализацию (а точнее ее отсутствие) интерфейсного поля advanced record в случае, когда последняя используется в качестве возвращаемого значения.

пятница, апреля 10, 2009

Утечки и неадекватное поведение Delphi 2006 - 2009 Update 1

При передаче advanced record имеющей интерфейсное поле, в качестве константного параметра, есть немалый шанс налететь на утечку и получить неадекватное поведение т.к. даже константный параметр имеющий размер поинтера передается по значению. Подробности и код в отчете #72878

понедельник, марта 23, 2009

суббота, марта 14, 2009

Ошибка компиляции на ровном месте

Если у класса или записи определить вложенный энумератор, который будет иметь поля "управляемых" типов (string, interface, dynamic array, variant), то при попытке использования в операторе for-in-do мы получим ошибку компиляции на несоответствие типов. Воспроизводится, и на Turbo Delphi Explorer, и на Delphi 2009 Update 1. Репорт #72213.

понедельник, февраля 02, 2009

Обновлен Common.Encodings.pas

На домашней страничке выложен обновленный Common.Encodings.pas

Добавлены классы кодировок UTF-32 и UTF-32BE (только Delphi 2009), добавлен метод определения кодировки по BOM. Добавлены функции-аналоги WideCharToMultiByte и MultiByteToWideChar для кодировок UTF-16, UTF-16BE, UTF-32, UTF-32BE. Функции приведены к единым сигнатурам. Определен процедурный тип с сигнатурой кодирующей и декодирующей функций. Добавлен воркараунд для корректного определения доступности кодировок UTF-7, UTF-8 под Windows NT 4.0

суббота, января 31, 2009

Утечки на интерфейсных параметрах

Теряются объекты передаваемые в качестве параметров интерфейсных типов в случае, когда не происходит кастинга с проверкой (as) к определенному интерфейсу. Для кода:

 Test(TMyObject.Create('1')); // will not be destroyed
Test(IInterface(TMyObject.Create('2'))); // will not be destroyed
Test(TMyObject.Create('3') As IInterface);

утечки будут в первых двух вызовах. Воспроизводится, и на Turbo Delphi, и на Delphi 2009. Отчет #71015.

пятница, января 16, 2009

QC, в чем магия?

Только вчера "ругался" на неторопливость правки багов в компиляторе, а сегодня глянул на свой  отчет №70529 и удивился -- он получил статус Internally Opened (как я понимаю, над ошибкой уже работают). Так вот, в чем магия? Неужели голос Алексея был решающим? ;)

четверг, января 15, 2009

Ошибка в Delphi 2009 Update 1

Передача односимвольной строки в диспетчеризируемый метод приводит к передаче кода символа, а не строки. Лечится приведением строки к строке :) т.е. вот так: String('1'). Репорт номер 70529, поддержите, кому не лень.

пятница, октября 31, 2008

Приемчик

Потребовалось символ юникод строки проверить на предмет вхождения в суррогатную пару (а конкретно, является ли символ младшим суррогатом). Все бы ничего, но индекс символа вычисляемый и вычислять его для каждого сравнения (коих собственно два) не кошерно. Есть варианты решения:

1. Сохранить индекс в переменной. Просто, но нужна переменная.

2. Сделать функцию и заинлайнить ее. Нужна функция.

3. Извернуться.

Собственно, я о третьем варианте:

     With WordRec(AContent[ACharIndex + CharCount]) Do
If (Word(Bytes) >= $DC00) And (Word(Bytes) <= $DFFF) Then;

воскресенье, октября 26, 2008

Оперативность QC

Все шесть моих репортов прошли во внутреннюю базу в среднем через 2 недели после подачи. Шустро. Правда с одним лажа вышла, отрепортил о баге пофикшеном в релизе :(

пятница, октября 10, 2008

Полный QCец...

Только я отрепортил о нескольких ошибках, как меня снова перестало пускать к сервису... Плавающие баги, они самые милые, ага :)

p.s.

Ой блин... Снял галку DN Authentification и все снова заработало. Сплошные воркараунды :))

среда, октября 08, 2008

понедельник, октября 06, 2008

Val в Delphi 2009

Знаете как работает Val с юникодом? Рассматриваются лишь младшие байты символов :( Для проверки прогоните цикл от lo(Char) до high(Char) и посмотрите, сколько символов будет "успешно" преобразовано в число. Грустно. Если кто-то это читает, заQCите плиз...

пятница, октября 03, 2008

Обновился Common.Encodings

В метод GetEncoding добавлен параметр со значением по умолчанию позволяющий указывать на возможность использования стандартных (определенных в классе TEncoding) кодировок. Скажем, код GetEncoding('utf-16') создаст новый объект класса TUnicodeEncoding, а код GetEncoding('utf-16', True) вернет существующий объект TEncoding.Unicode;

среда, сентября 24, 2008

Delphi 2009 aka Tiburon. TEncoding.

На домашней страничке выложен модуль Common.Encodings расширяющий возможности стандартного класса TEncoding. Модуль содержит class helper для TEncoding и несколько вспомогательных advanced records.

Примеры использования: 

1.) Перечисление доступных кодировок

Var

ei : TEncodingInfo;

Begin

For ei In TEncoding.Encodings Do
If ei.IsAvailable Then
WriteLn('codepage: ', ei.CodePage, ' name: ', ei.Name);

End;

2.) Создание кодировки по имени:

 TEncoding.Encodings.ByName('utf-16').GetEncoding;

или

 TEncoding.GetEncoding('utf-16');

3.) Создание кодировки по номеру кодовой страницы:

 TEncoding.Encodings.ByCodePage(CP_UTF16_BE).GetEncoding;

или

 TEncoding.GetEncoding(CP_UTF16_BE);

4.) Получение информации о существющей кодировке:

 With TEncoding.UTF8.GetInfo Do
WriteLn('codepage: ', CodePage, ' name: ', Name, ' display name: ', DisplayName);

пятница, сентября 19, 2008

Delphi 2009. Вкратце.

Юникод+дженерики+анонимные методы это все понятно.

Порадовала скорость среды! И особенно скорость CodeInsight (несмотря на то, что я его все равно всегда отключаю)

Наконец-то они научились различать Implicit и Explicit для записей в нативе (2007 не пробовал, может и там уже было). Но вот редактор сильно подкачал, разучившись (в 2006 он и не сильно-то умел) перемещаться по Ctrl+Shift+Up/Down между декларацией и реализацией перегруженных операторов записи (правда, я тестировал на 12.0.3155.16733, может в релизе и поправили), а также разучился (для перегруженных операторов опять же) по Ctrl+Shift+C создавать макет метода по описанию.

Появился UInt64 -- беззнаковое 64битное число.

BeginThread и TInterfaceList по-прежнему имеют потенциальные утечки.

Неработает передача Int64 в параметре диспетчеризуемого метода (вызов метода от вариантной переменной). Точнее, если передавать переменную типа Int64 все работает, а если значение Int64(1024) -- нет.

Exception обзавелся потенциальной поддержкой стек-трейсинга, но только потенциальной :)

Очень порадовал {$POINTERMATH}

В комментариях исходников встречается sysmac.h. Интересно, это то о чем я думаю...?

Порадовал хелпер для TRTLCriticalSection (syncobjs.pas)

Порадовал обновленный ZLib

Огорчает нелепость использования системных InterlockedCompareExchangePointer (и прочих Interlocked...) при наличии нативных в system.pas

Ribbon огорчил неаккуратными скинами (ждем редактора :) или сторонних, качественных скинов) и моргающей отрисовкой. DoubleBuffered Ribbon-контрола  не помогает, а буферизовать форму то еще извращение.

TLinkLabel неюзабельна совершенно, т.к. на системах младше XP, линки распознаваться не будут и пользователь будет видеть разметку.

понедельник, мая 12, 2008

Embarcadero + CodeGear

О покупке CodeGear компанией Embarcadero, стало известно почти неделю тому назад. А вот обращение к клиентам CodeGear, в котором, в частности, говорится о том, что дальнейшая разработка будет нацелена на гетерогенные платформы и различные СУБД. Ребята, идем на Linux и MacOS? :)

четверг, апреля 24, 2008

XML-RPC: Общие ошибки

Одна из самых часто встречающихся ошибок в реализациях XML-RPC -- экранирование лишних символов в строках. Лишних в том смысле, что спецификация XML-RPC их экранирования не требует. Для корректного парсинга пакета требуется экранировать символы: < (&lt;) и & (&amp;). Любые другие символы экранироваться не должны, но очень часто ошибочно экранируют еще и другие символы: ' (&apos;), " (&quot;), > (&gt;).

среда, апреля 23, 2008

Extended RTTI {$METHODINFO ON}

Пару дней назад, налетел на очень интересную граблю. В своем XML-RPC сервере я использую возможность Delphi создавать расширенную RTTI, для регистрации объектов на сервере. Описывается класс с некоторым API, публичная часть (та, что будет доступна XML-RPC клиентам) выносится в секцию Published (для которой и создается Extended RTTI). Затем создается объект данного класса  и регистрируется на сервере. В процессе регистрации сервер получает прототипы методов разбирая RTTI. Все работает прекрасно, кроме того, что помимо методов вынесенных в Published, компилятор создает RTTI еще и для некоторых (даже перегруженных!) методов из секции Public. Грабля, вылезла на довольно сложном классе имеющем nested types. На простых классах такого не обнаруживается.

пятница, апреля 18, 2008

Утечка в TInterfaceList

У данного списка (как впрочем и у TList, и его потомков) есть свойство Count позволяющее, как определять количество элементов списка, так и устанавливать его. Очень удобно использовать возможность назначения количества элементов, для многократной очистки списка (т.к. делается это без перераспределения памяти к которому приводит стандартный Clear). Однако, в классе TInterfaceList данная операция приводит к потере ссылок на хранящиеся интерфейсы, следовательно они никогда не будут освобождены.

воскресенье, апреля 13, 2008

XML-RPC. Снова :)

Сегодня получил 5046 вызовов в секунду (и это на отладочной версии, со всеми проверками и ассертами)!

вторник, апреля 08, 2008

Delphi хотелка

Уже который раз ловлю себя на мысли, что очень хочется иметь параметризованные циклы for-in-do. Тогда можно было бы писать примерно так: For Item[Param] In Items Do... Что приводило бы к выборке значений соответствующих параметрам (разумеется, при поддержке перечислителя).

И вторая хотелка касающаяся for-in-do это наличие двух методов вызываемых перед началом перечисления и после. Что помогло бы при реализации перечислителей в виде advanced records, когда им (перечислителям) требуется выполнять некоторые операции (например блокировка/разблокировка) над контейнером.

воскресенье, марта 30, 2008

А знаете ли вы, что...

Операция сравнения AString = '' выполняется в два раза быстрее чем Length(AString) = 0 для AnsiString, и медленнее (примерно в 1.3) для WideString при условии, что строка не пустая.

Плагиат

Какой то пельмень, по имени Max, тырит посты из моего блога (и видимо не только из моего) и публикует на своем (ссылку не дам, ибо нефиг)  от своего имени... Этот пост для него.

четверг, марта 20, 2008

Новые высоты :)

Сегодня, после небольшой переделки работы с SAX'ом, получил скорость в 4096 (число-то какое :)) вызовов XML-RPC в секунду (метод без параметров, возвращающий nil)

воскресенье, января 27, 2008

HTML Display Components

Кажется, сабжевая библиотека тихо умирает :( Автор отказывается делать какие-либо изменения/дополнения в будущем.

пятница, января 11, 2008

Ужос, нах...

Вчера, на одном форуме, увидел вопрос дельфиста:

"Раньше использовал инди смтп а как его запихать в поток не знаю. Формы же нет, куда его перетащить можно."

Это было бы смешно, если б не было так грустно :( Остается надеяться, что вот такие кадры, в скором времени, свалят под .Net (а именно такие, в основном, и валят, т.к. Delphi их донимает "акцесс виолатион'ами") и перестанут порочить доброе имя Дельфистов. В результате, свалившие будут писать тормозных и охотчих до памяти монстров (но главное (!), без "акцесс виолатион'ов" :D) на, и без того, тормозящей платформе, а лагерь .Net получит перебежавших чайников, в добавок к родным образованиям :) и постепенно будет ими опорочен :). Аминь.

пятница, января 04, 2008

Очередная бага TAlXmlDocument

Ошибка, иногда, появляется при парсинге документов имеющих комментарии. Воспроизвести ошибку довольно просто -- в цикле создавать и парсить документ, с комментарием переменной длинны, от одного символа до размера буфера. Дело в том, что в процедуре PosInXmlString не учитываются некоторые особенности при расчете смещения. Однако, решить проблему очень просто:

  1. В процедуре PosInXmlString изменить строку Offset := RawXmlStringLength - RawXmlStringPos{ + 2};
  2. В процедуре AnalyzeComment добавить условие на размер комментария, длина которого не может быть меньше нуля And (P1 - RawXmlStringPos - 4 >= 0)

четверг, декабря 27, 2007

Проблема скорости TALXmlDocument

В общем, прилетело откуда не ждали :( Пару дней назад решил попробовать отдачу статистики работы сервера XML-RPC в комментарие пакета (т.е. вместо, собственно, комментария отдается XML документ c различными данными). Все бы ничего, да только производительность сервера просела очень значительно... На два порядка, т.е. в 100 с лишним раз. Первым подозреваемым стал именно парсер т.к. непонятки с размером буфера у него уже были. Написал автору -- тишина. Решил сам покопаться в дебрях чужих исходников :). Профайлера нема -- ищем проблему читая код :) Нужно отдать автору парсера должное, он многое сделал для того, чтоб его создание, по скоростным характеристикам, укладывало на лопатки конкурентов :) Там и ассемблерный код, и адаптирующийся под процессор набор процедур/функций для работы со строками. В общем видно, что человек старался. Однако нашел я там и такой код, что просто волосы дыбом и нужного смайла не могу найти. Код приводить не стану т.к. вне контекста (читай метода использования) он абсолютно нормален, скажу лишь, что функция называется CharPosInString. После того, как я ее переписал тормоза сократились в четыре раза (т.е. стало тормозить не в 100 раз, а в 25 :)). Ищу проблему дальше. Меняю размер комментария и вижу, что чем больше комментарий, тем сильнее тормоза. Потом убираю комментарий и добавляю в пакет параметр типа Base64 размером в 64Kb -- снова тормоза. Начинаю грешить на внутренний буфер парсера. После некоторых манипуляций выясняется, что какой бы ни была константа BufferSize, парсер выделяет буфер под самый большой контент встретившийся ему при разборе и потом спокойно работает с этим буфером. Нашел функцию ExpandRawXmlString отвечающую за увеличение буфера при необходимости. В одно из условий добавил несколько строчек и проблема разрешилась (т.е. буффер увеличвается только на время, когда он нужен, а затем возвращается к прежним размерам). В общем и целом, тормоза пропали совсем, но сердцем чую -- нужен профайлер, чтоб найти корень зла тормозящий на любом буфере больше 64 байт :).

суббота, декабря 01, 2007

Странный ping. Yandex XML-RPC

Сегодня, от нечего делать, xml-rpc debugger'ом решил дернуть (а вдруг работает) метод system.getCapabilities у сервера Яндекса, который обрабатывает пинги блогов. В ответ меня поблагодарили за пинг и сообщили, что все прошло без ошибок :) Прикольно они там реализовали xml-rpc...

понедельник, ноября 19, 2007

Что-то, как-то...

Ну, совсем меня Ubuntu убила...  Вчерась решил заархивировать папку Install весом в 9Gb находящуюся на виндовом ntfs разделе. Не скажу, что в этом была большая необходимость, т.к. архив я предварительно сделал под Windows, просто хотелось посмотреть, как оно в Ubuntu :) Открываю контестное меню на папке Install, выбираю пункт Add to archive (или что-то вроде этого, точно не помню), выбираю тип архива tar.gzip. Отлично! Пошел процесс архивирования. Минут через пять, я решаю прервать это дело. Но не тут-то было... Окошечко, конечно закрылось, но вот индикатор диска продолжал указывать на сильную активность. Подождав еще минут пять, я запустил системный монитор и был несколько удивлен увиденным... В списке процессов преспокойненько болтался процесс tar и явно продолжал архивацию. Я в шоке.

суббота, ноября 17, 2007

Вторые впечатления...

Ох, неоднозначные... Все, вроде, в Ubuntu приятно, но вот словил таки две аномалии. Аномалия первая. Запустил поиск файлов по маске *.png. Файлов нашлось аж 16 тысяч с копейками. Затем, эти найденные файлы, перетаскиваю (drag&drop) в соседнее окошечно с открытой папкой. Все, ОС померла, в смысле впала в состояние анабиоза, в котором только дисковая система продолжала свою активность. Сразу зачах плейер (не помню название, но это тот, из поставки, что с подкастами), перестал двигаться курсор мышки. В общем через некоторое, весьма продолжительное, время диалог поиска файлов самопроизвольно закрылся безо всяких сообщений (так ничего и не скопировалось). Аномалия вторая. Слушая музычку через упомянутый уже плейер, я решил еще помсотреть видео из папки Examples. Запускаю на просмотр видео с Нельсоном Манделлой, и... Это все. Запускается другой плейер и ОС зависает намертво (плейер с подкастами перестает играть). Судя по тому, как зашуршал кулер на проце, чем то ОС его грузила, но прождав минут 10 и не получив ни какого результата я просто выключил буку. Вот такие дела.

среда, ноября 14, 2007

Первые впечатления

Попробовав Ubuntu с LiveCD, я остался очень приятно удивлен качеством дистрибьютива. Имея прежний опыт общения лишь с ASP-Linux (кажется 9.0), могу сказать, что Ubuntu это уже не просто шаг на десктопы пользователей, это полноценный (ну пусть, с некоторыми оговорками) конкурент Windows! Поразила отзывчивость интерфейса при сворачивании/восстановлении окон в момент пиковой загрузки процессора. Кстати, все оборудование моего ноутбука (Toshiba Satellite Pro M30) было определено корректно, и даже noname блютуз донгл определился и заработал. Единственная претензия, так это устойчивое желание Ubuntu ползти в инет, что за драйвером для видюхи (чтоб работали эффекты Compiz Fuzion), что за русским языком для локализации (посмотреть на русский фейс было просто интересно). Возможно, это из-за того, что я использовал LiveCD, а не устанавливал систему на винт. Очень порадовало качество сглаживания шрифтов Subpixel Smoothing, кажется более качественным нежели Windows ClearType. А вот сами шрифты изяществом не блещут. В общем и целом, Ubuntu зачет!

Убунта

Сегодня получил заказанные 09.10 диски Ubuntu! Доставили прямо в почтовый ящик. Теперь буду смотреть :)

среда, ноября 07, 2007

XML-RPC boxcarring 2

Позавчерашний результат в 4500 вызовов в секунду, что-то не очень меня порадовал... По моим прикидкам, должно было работать значительно быстрее. Сегодня начал искать узкое место. Им оказался используемый мною SAX парсер, который сильно тормозил при размере пакета в 2 мегабайта. Порылся в его исходниках (благо, он открытый и бесплатный), и попытался без профилирования определить, где могут быть затыки. Нашел константу BufferSize = 8192 байта. Отлично, подумал я :), сейчас увеличу буфер в два раза и посмотрю. Посмотрел :( Скорость упала вдвое. Я в полнейшем шоке. Решил буфер уменьшить... Скорость возросла :) В общем, я дошел до буфера в 16 байт :) Причем с таким буфером, скорость хорошая, и при парсинге больших строк, и при парсинге файловых стримов (85Мб).

P.S. Руки дойдут, посмотрю на него под профайлером, а пока результат меня удовлетворил -- 16667 вызовов в секунду!

понедельник, ноября 05, 2007

пятница, ноября 02, 2007

Delphi-грабельки 2

Ох уж эти грабельки... Сегодня наступил там, где  и не ожидалось :( Есть в ObjectPascal чудесная функция Slice, которая возвращает N первых элементов открытого массива и использовать которую можно только, как параметр процедуры или функции. Вот сегодня мне эта радость и понадобилась. Надобность обернулась руганью компилятора о том, что функция используется не по назначению т.е. не в качестве параметра функции или процедуры. Ну, это компилятор так думал... На самом деле, все оказалось проще. Функция, а точнее метод, в который я собирался передать кусочек массива, являлся перегруженным и имел сестру-процедуру принимавшую два параметра, первым из которых должен был быть открытый массив  (который, собственно, Slice и возвращает). Такие вот грабельки. Пришлось морщить лоб и выдумывать другое имя для сестры-процедуры :(

среда, октября 31, 2007

Delphi-грабельки

Оказывается, в Delphi нельзя объявить больше одного перегруженного метода в секции Published.

Сегодня я на эти грабельки наступил... Обидно :(

пятница, октября 19, 2007

Набросок XML-RPC сервера

Сегодня сделал таки набросок XML-RPC сервера чтоб оценить скорость работы. В общем скорость получилась потрясающая: 3750 вызовов в секунду! Правда ввиду того, что это лишь набросок, где отсутствует куча проверок, в окончательном варианте цифра будет поменьше. Но думается мне, что не меньше 3500 :)

вторник, октября 16, 2007

XML-RPC хотелка

Очень хочется официального апдейта спецификации XML-RPC с целью добавления 64-битных целых. Не очень радует перспектива передавать такие числа в виде строки/двуэлементного массива/записи с двумя членами. Куда бы написать об этом (?), xmlrpc.com совсем мертв, кажется :(

среда, октября 10, 2007

Семь нянек

Сегодня бегло полистал вот эту книгу:

Programming Web Services with XML-RPC
Simon St. Laurent
Joe Johnston
Edd Dumbill
Publisher: O'Reilly
First Edition June 2001
ISBN: 0-596-00119-3, 230 pages

Неоднозначные впечатления... Прям на 24 странице,  в пункте 2.3.1.1 Parameters присутствует фраза "Even if the method requires no parameters, the <params> element must still be present." противоречащая спецификации XML-RPC. Вроде и авторов аж трое, а такой косяк допустили. Немного ранее авторы сделали смелый вывод о том, что дубликаты членов структуры запрещены т.к. они (авторы), в противном случае, не могут понять, как может интерпретироваться подобная структура :)

В общем, книга мне не очень понравилась. Какая-то однобокость суждений ощущается.

вторник, октября 09, 2007

Убунта

Сегодня оформил пред-заказ на 2 (32+64 bit) CD Linux Ubuntu 7.10. Интересно, сколько диски будут добираться до меня и действительно ли все будет бесплатно... Решил FPC 2.2 под линуксом посмотреть :)

пятница, сентября 21, 2007

Рекорды скорости

Не то, чтобы я специально подтачивал библиотеку для достижения высоких результатов, просто в следствии совсем крошечной оптимизации удалось получить уже 2583 вызова в секунду :) без модификации серверного компонента.

понедельник, августа 13, 2007

Скорость передачи данных в XML-RPC

Сегодня продолжил тесты на локальной машине. На этот раз решил проверить чистую скорость передачи данных (как если бы передавался файл). В результате, скорость чуть больше 13Мб в секунду! Передавались блоки по 32Кб, в единственном параметре метода, тип Base64 (т.е. реальный блок получался ~43Кб. Не стоит забывать, что спецификация XML-RPC разрешает использовать и строки для передачи бинарных данных). Выходит, что файл размером в 130Мб был бы передан за 10 секунд. Если блоки передавать строкой, а не Base64, скорость получается 28.5Мб в секунду :)

Было желание попробовать этот тест с использованием gzip-сжатия, но мне стало лень прикручивать фильтр к IIS :)

суббота, августа 11, 2007

Скорость XML-RPC

Сегодня протестировал свою библиотеку XML-RPC на локальной машине (до этого тестировал только при нахождении сервера под виртуалкой). Сервер IIS 5.1 под которым работает мой xml-rpc ISAPI. Клиент -- Delphi-приложение. Клиент вызывает метод currentTime.GetCurrentTime возвращающий текущую дату и время. Многократные замеры показывают результат в 2560 вызовов в секунду (и это без использования boxcarring'а и на неспециализированном сервере)!!! Кто там говорил, что XML-RPC не эффективен ;)

воскресенье, августа 05, 2007

Цвет затемнения в Smoke-screen

Для себя выбрал оптимальный вариант подбора цветов затемнения. Изначально использовал не черный, а очень темно-серый (RGB: 32, 32, 32). Сейчас выбираю цвет в оттенок обоя рабочего стола, т.е. если на обое преобладает зеленый цвет, я немного усиливаю этот цветовой канал (скажем RGB: 32, 48, 16) и ослабляю другой. В результате затемнение ощущается этаким, продолжением обоя :)

четверг, августа 02, 2007

"Турба", ты меня удивляешь...

Сегодня у меня получилось создать объект абстрактного (!) класса... Глюк плавающий и воспроизведению, что-то не поддается.

воскресенье, июля 22, 2007

JSON

Вот тут обнаружил JSON Toolkit v0.3 (с поддержкой RPC). В закладки, однозначно! Пригодится, вдуг решу свой гейт JSON-RPC 2XML-RPC and vice versa сделать :)

среда, июля 18, 2007

Снова баг TurboDelphi/BDS2006

Не помню, может я уже писал об этом, просто позавчера снова напоролся... Имеем свойство интерфейсного типа. Имеем сеттер получающий константный параметр (новое значение св-ва ес-но). Внутри сеттера выполняем проверку поддержки необходимого нам интерфейса средствами Supports(Value, IMyInft); Все. После отработки сеттера, мы теряем интерфейс. Баг обходится либо, отказом от константного параметра либо, проверкой интерфейса расширенной версией Supports с дополнительным параметром. Интересно, в D2007 это воспроизводится?

воскресенье, июня 24, 2007

ControlHooks

На домашней страничке выложены модули (D2006/Turbo ~15Kb)  позволяющие создавать классы-ловушки элементов управления (потомки TControl), а также пример использования (класс-ловушка обеспечивающий принятое в Windows поведение элементов управления Edit и Combobox при статусах Disabled и ReadOnly).

воскресенье, июня 10, 2007

Synapse

В модуле blcksock.pas включена директива {M+}, соответственно все классы библиотеки получают RTTI т.к. имеют published-члены. Не знаю, с какой целью автор это сделал (может для Lazarus'а. Кажется, я что-то слышал об этом), но я, для win32, директиву отключаю.

пятница, июня 01, 2007

Решение с null-stream

Собственно, ничего нового и оригинального я не напишу, но вдруг, кому пригодится :). Есть у меня класс TXmlRpcPacketGenerator, который формирует XML-RPC-пакет в соответствии с параметрами и опциями форматирования (отступы, декларация, варианты компактности). Формирование пакета происходит только в поток т.е. в объект-наследник класса TStream. Встала задача, определения размера будущего пакета ДО его формирования. Было несколько вариантов решения, но остановился я на т.н. null-stream. Создается класс TNullStream(TStream), в котором перекрываются методы Read, Write и Seek. Что делают эти методы понятно из их названия и в контексте названия потока т.е. ничего не делают :), а просто манипулируют счетчиком. Потом в такой поток формируется пакет и задача решена :)

Замечания относительно скорости: самая тяжелая операция в формировании XML-RPC-пакета - кодирование Base64, однако на это можно не обращать внимания т.к. по моим замерам (и моим-же кодером) строка, общим объемом, в 18Mb кодируется всего за 0.25 секунды (PM-1.7Gh 512RAM) (но это время только чистого кодирования). Другая тяжелая операция - это большое количество данных примитивных типов (массивы чисел, дат, булевых и т.п.), однако и это не так страшно, например массив из 100.000 структур формируется за 0.2 секунды и имеет размер 2.4Mb.

вторник, мая 22, 2007

Надоело читать про iso8601

На просторах ненашего интернета читал статьи об XML-RPC. Я, честно говоря, только о русских авторах думал не очень хорошо, а оказалось - эпидемия :) В большинстве (если не во всех) статей авторы пишут, что XML-RPC использует для представления даты и времени стандарт "квази" iso8601 (т.е. это представление, якобы, не соответствует стандарту). Внимательно читая iso8601 я не увидел ни одного несоответствия рекомендациям стандарта. Они, что, друг-друга просто перепечатывают?

вторник, мая 15, 2007

Colocation заблаговременно...

Озадачился найти контору, которая предоставляет услугу colocation за приемлемые (2кило рубля в месяц максимум) деньги и без дурацких ограничений по трафику, вида 1/4 (а у меня задумка такая, что входящего будет больше нежели исходящего). Полазил по инету, полазил, да и опечалился совсем :( У всех ограничения :(( Нашел keyweb.ru, так у них дата-центр в Германии... Что и делать, где железо размещать...???

воскресенье, апреля 22, 2007

Маленький хинт :)

Часто в работе приходится использовать сторонние библиотеки (хотя это можно отнести и к своим) имеющие директивы условной компиляции и соответствующие определения. Это здорово, это позволяет гибко, под нужды приложения, компилировать библиотеку (например я, в UIB включаю UIB_NO_COMPONENT, чтоб предком всех классов стал TObject). Это хорошая практика, но она имеет недостатки: когда происходит обновление версии библиотеки, старые определения (в файлах .inc) могут быть утеряны, не всегда-же все держится в голове. А если таких библиотек несколько? В общем, я для себя нашел способ избавиться от этой головной боли :) В отдельный модуль, или файл проекта (а вообще, куда угодно) подключаются файлы определений (*.inc) библиотек и создаются директивы условной компиляции (пример для UIB):

{$IFDEF DLLREGISTRY}

{$MESSAGE FATAL 'Отключить $DEFINE DLLREGISTRY (jvuib.inc)'}

{$ENDIF}

{$IFNDEF FB20}

{$MESSAGE FATAL 'Включить $DEFINE FB20 (jvuib.inc)'}

{$ENDIF}

{$IFDEF UIBTHREADSAFE}

{$MESSAGE FATAL 'Отключить $DEFINE UIBTHREADSAFE (jvuib.inc)'}

{$ENDIF}

{$IFNDEF UIB_NO_COMPONENT}

{$MESSAGE FATAL 'Включить $DEFINE UIB_NO_COMPONENT (jvuib.inc)'}

{$ENDIF}

Теперь уж точно ничего не забудется при обновлении :)

пятница, марта 16, 2007

OLE automation

Никогда не любил эту штуку. Не, ну здорово, конечно, в Word'е страничку иль в Excel'е табличку/отчетик заделать, но чтоб постоянно использовать - боже упаси. Однако решил, заради эксперимента, прикрутить к своей реализации XML-RPC вызов методов сервера через механизмы диспетчеризации (IDispatch). Надумано - наделано :) Вчера закончил. Результаты, честно говоря, меня очень порадовали. Прямой вызов метода выигрывает у своего диспетчеризуемого аналога всего 0.2 секунды при 10.000 итераций (т.е. разница в пересчете на одну итерацию сопоставима с погрешностью замеров). Проигрывая при  этом, по простоте (правда не сильно) и читабельности кода:

Прямой вызов:
For SIndex := 1 To 50 Do
Memo1.Lines.Add(SysUtils.Format('%d - %s', [SIndex, Call('examples.getStateName', [SIndex])]));

Диспетчеризуемый вызов:
For SIndex := 1 To 50 Do
Memo1.Lines.Add(SysUtils.Format('%d - %s', [SIndex, Server.examples.getStateName(SIndex)]));

пятница, марта 09, 2007

Однако, Linux...

Вчера и сегодня в новостях "Компьютерры" мелькнули сообщения о том, что Dell и HP будут поставлять компьютеры с Linux... Интересное кино.

четверг, февраля 22, 2007

Идеальное хранилище

Вот так высокопарно :) Разумеется, ничего идеального нет, но то, о чем я сегодня пишу, в некотором смысле тянет на это определение ;) Речь пойдет о хранении настроек приложения, как частном случае. Как известно, для хранения настроек изначально (аж со времен DOS) использовались т.н. ini-файлы, текстовые файлы с незамысловатой структурой. Решение очень простое и даже примитивное. Примитивность заключалась в неструктурированности и непизированности хранимых значений, и именно из-за нетипизированности были основные проблемы (конечно, если разработчику не лень в каждом месте выполнять вручную конвертирование типов из строкового представления, для него это не проблема). Потом Майкрософт сказала, что ini-файлы - прошлый век, и всем пора пользоваться реестром (единым системным хранилищем). Там, и типизация, и возможность "структурного" хранения и вообще реестр - rulezzz!  И толпы программистов-леммингов кинулись претворять в жизнь призыв Большого Билла. Но, как оказалось, структурированность реестра не относилась к типизации данных (их по прежнему приходилось хранить в "плоском" виде), структурировать можно было только расположение (что, собственно, присутствовало и в ini-файлах).  Разработчики привыкшие к такому положению дел еще со времен ini-файлов, кажется, не особенно и растроились (запросто храня массивы и структуры в виде бинарных блоков). Но время шло и пользователи стали замечать, что файлы реестра пухнут и, как следствие, система уже не так резва, как после установки. Оказалось, что всему виной приложения оставляющие, даже после своего удаление, в реестре груды мусора. Эта проблема породила целый класс программ-чистильщиков реестра (авторы, наверное, благодарны Большому Биллу). [Кстати, с приходом .Net изменилось отношение Майкрософт к вопросу хранения настроек, теперь они не рекомендуют использовать реестр и снова предлагают хранить все в файлах (все возвращается на круги своя ;))] Потом появилась идея, хранить настройки в файлах формата XML. Идея совсем не плоха, т.к. структурирование заложено в саму суть формата, но беда (не формата, как такового, а идеи хранения в нем типизированных данных) в том, что типизация в формате не предусмотрена (еще раз скажу, что это не проблема самого формата), конечно существуют различные механизмы описания, типа DTD, но для задачи хранения настроек это слишком... Как вариант, можно пойти своим путем (так сделала сама Майкрософт для своего продукта Virtual PC 2004) и хранить в xml-файлах информацию о типах элементов (благо формат позволяет делать это весьма элегантно). Я же, хочу рассказать еще об одном варианте "своего пути", личном ;) Так случилось, что мне пришлось писать собственную реализацию протокола XML-RPC, и сейчас, софт, который пишется, пишется с прицелом на этот протокол. Так вот, для хранения настроек и некоторых файлов с данными, было решено использовать формат пакетов XML-RPC (A, что может быть лучше? Тут, и строгая типизация, и богатый набор самих типов (да еще и парсинг уже написан и отлажен)). Сам протокол имеет только два типа пакетов methodCall и methodResponse, настройки логичнее хранить во втором, но чтоб не смущать пользователей любящих подкрутить что-нибудь руками тэгом <methodResponse>, я добавил третий тип пакета value (это не потребовало сколь нибудь значительной переделки генератора пакетов т.к. элементы value в любом случае сериализуются), который аналогичен пакету methodResponse, но где корневым элементом является value.

воскресенье, февраля 18, 2007

Очередной баг

Сегодня наткнулся на интересную особенность работы конструкторов записей. Имеем запись с полем типа Variant. В конструкторе, который, как известно, может вызываться сколь угодно раз для объекта/записи, присваиваем полю интерфейс получаемый из вновь создаваемого объекта. По идее, должно произойти освобождение старого объекта (если вызов не первый) т.к. ссылка на его интерфейс теряется, а затем назначение полю нового значения. Но при входе в конструктор, благодаря компилятору, поле получает значение Unassigned, в результате чего, мы имеем утечку памяти.

четверг, февраля 08, 2007

POPFile 0.22.4. Осьминог жукастый

Где-то, на просторах инета наткнулся на классификатор почты, POPFile, использующий теорему Байеса (Bayes) для отделения мух от котлет :) Нашел несколько хвалебных отзывов от его пользователей (говорят фильтрует аж 99.9% процентов спама), и самое главное, увидел, что он имеет XML-RPC API для взаимодействия с внешним миром. Не смог пройти мимо и не попробовать "повзаимодействовать" :) Качнув и установив его на виртуальную машину,  активировал работу XML-RPC API для внешних подключений (по умолчанию XML-RPC отключен). Дело за клиентом! По первому попавшемуся примеру сваял маленький пробник для перечисления корзин классификатора. И тут меня ждал неприятный сюрприз... Ошибшись в имени метода (пропустил одну букву) я получил в ответе некорректную (не соответствующую спецификации XML-RPC) fault-структуру, где член faultCode имел строковый тип... Выходит, используемая им перловая реализация (XMLRPC::Lite если я не ошибаюсь) кривая, коли допускает подобные вольности. Вот такие дела, авторам отпишусь :)

P.S. Вот и делай людям добро ;) Попытался отрапортовать об ошибке, так оказалось, для этого еще и на SourceForge'е нужно регистрироваться. Ну уж нет :)

P.P.S. На вызов метода release_session_key отдается структура, также не соответствующая спецификации XML-RPC (тэг <params> не имеет обязательных дочерних элементов).

вторник, февраля 06, 2007

Неудавшийся поиск

Что-то я ни как не могу найти фришный (собственно и платных не видел) монитор для XML-RPC :( Не просто проксю, которая будет показывать тела пакетов, а именно полноценный монитор с парсингом сообщений и раскладыванием по полочкам передаваемых значений (в древовидную структуру например). Что-то вроде майкрософтовского SOAP listener'а (вроде так называется). А если у него окажется еще и поддержка boxcarring'а и introspection'а, то ваще будет рай :) Уж больно не хочется самому писать...

четверг, февраля 01, 2007

"Приведение" типов

Правда не совсем приведение... Встала задача получить из TMemoryStream строку. Самый простой путь, он же и самый не эффективный,  задать строке размер SetLength и потом просто скопировать память с помощью Move. Неэффективность налицо: двукратно увеличеное требование к памяти и время на копирование. Но есть вариант проще:

  Type

TOpenedMemoryStream = Class(TMemoryStream);

Var

Data : PChar;

...

With TOpenedMemoryStream(AStream) Do
Begin

// Если выделенной памяти ровно столько сколько требуется потоку
// (обычно больше т.к. память выделяется блоками по 8192 байт)
// увеличиваем количество выделенной памяти не изменяя размера потока
If Capacity = Size Then
Capacity := Capacity + 1;

// Сразу за блоком, занимаемым потоком, записываем признак конца строки
// и сохраняем указатель на блок памяти для для последующей обработки
PByte(Integer(Memory) + Size)^ := 0;
Data := Memory;

End;

Теперь переменную Data можно приводить к строковому типу :)

Исходники

На домашней страничке выложен архив (~8Kb) содержащий модули и шаблон для создания мультиформенного приложения (MS Office 200 like).

четверг, января 25, 2007

День под знаком "петли"

Сегодня у меня был ужасный день... На моей Тоше (Toshiba Satellite Pro M30) жутко расхлябалась левая петля :(. Такое случалось и раньше, но там я обходился малой кровью, а именно: снимал с петли накладку (снимается она легко) и под две скрепленные заклепкой пластины (между которыми и образовывался люфт) подкладывал всяческие железки. Это помогало, но на некоторое время. Я прекрасно понимал, что раз оно заболталось, значит с одной стороны у заклепки отваливается шляпка, но с какой было не ясно. Операцию подкладывания я проводил целых три раза, и вот сегодня свершилось... Люфт стал настолько большим, что при открывании крышки накладка петли сама-собой отщелкивалась :(. Ничего не оставалось делать, как разобрать бук и посмотреть на виновницу "торжества". Как я и предполагал, нижняя шляпка заклепки отвалилась наполовину. Пришлось вооружаться плоскогубцами и пилкой по металлу т.к. заклепка выниматься не желала. Получасовая битва наконец принесла свои плоды, заклепка сдалась! Я подыскал подходящий по размеру болтик с "гравером" и гаечкой и принялся восстанавливать работоспособность железного элемента. Наконец все закончено и бук можно собирать! Ура! Сборка прошла весьма успешно и я был уже готов включить его и проверить на "мало ли чего", как вдруг вспомнил, что не поставил накладку на петлю. Ну-да, дело плевое, не первый раз, решил я, и принялся накладку устанавливать. Но, не тут-то было... Гаечка оказалась слишком толстой, и накладка, ни как не хотела надеваться на петлю :(((( Щьёрт побьерьы!!! Снова разбирать бук... Это был сильный стресс... Но делать нечего, пришлось вновь разбирать до винтика (если кто разбирал эту модель, поймет, там снимать нужно все вплоть до HDD и CD). После разборки и поиска замены толстой гаечке, я обнаружил, что заменить ее нечем :((((( На помощь пришел напильник, коим я старательно обтачивал гайку, а заодно и головку болта. Покончив с этим делом, и примерив накладку (она наделась просто чудно) я решил, до кучи, еще и кулер смазать литолом! Надумано - наделано! Кулер смазан, накладка надета, осталась сборка. Сборка заняла минут пять, потом влажная уборка рабочего места и протирание матрицы и самого бука. У-ф-ф-ф... Зато теперь петля никуда от меня не денется ;)

вторник, января 23, 2007

Наследование записей

Ну не так, чтоб уж совсем наследование, но механизм расширения функционала рабочий. Итак: определяем запись TRecord с неким множеством методов. Затем (типа наследуемся) определяем запись TRecordDesc = Type TRecord. Теперь эти записи не совместимы (для чего это нужно - ниже). Описываем Record Helper For TRecordDesc с неким множеством методов/свойств. В итоге, мы получили функционально расширенную запись, ну а несовместимось нужна была, чтоб члены record helper'а не попали к предку. Однако копирование записи достигается простым приведением типа ;) Если бы хэлперы позволяли описывать перегрузку операторов, можно было обойтись и без приведения.

Однако есть еще способ ;). Можно использовать агрегацию, тем самым избавиться от хэлперов и приобрести возможность перегружать операторы :).

понедельник, января 22, 2007

Где, блин, революция!?

Еще, когда только планировалась к выходу Delphi 6, весь инет шумел о веб-сервисах, все говорили, что вот, грядет оно, светлое будущее. И собственно где? Ну блоги, понятное дело, ну еще г-н Орейли чего-то там сделал, ну да, г-н Виннер, а еще? Публичных веб-сервисов кот наплакал, да и те умеют лишь рапортовать о валюте, времени да погоде... Это я к чему, да просто г-н Балмер недавно снова предсказал революцию веб-сервисов в течении ближайших 10 лет :).

Что еще интересно, по задумке идеологов, веб-сервисы должна была объединить одна важная особенность - протокол взаимодействия (тогда на это точили SOAP, а Borland даже сделала RTTI для интерфейсов в шестой версии). И взгляните, что происходит сейчас: образовалась целая куча подходов к осуществлению взаимодействия (SOAP, XML-RPC, REST, JSON, PI-RPC, AJAX и.т.д), каждый считает себя самым умным и делает то, что считает нужным. Например контора на "Г" прибила бету поискового сервиса на SOAP, и перевела его на AJAX (чего уж не на GData).  Таже контора обещает отломать от Blogger'а XML-RPC API и перейти на что-то свое... Короче, разброд полнейший... Что-же касается SOAP, то он уже давно превратился в монстра с которым, никому, кроме породивших его Франкенштейнов, связываться не хочется.

воскресенье, января 14, 2007

Что делают в армии?

На этот вопрос каждый отвечает себе сам :) Я пошел в армию вполне осознанно, бросив учебу в университете. Пошел, потому что так было надо (ну воспитывали нас так), т.е. все нормальные косили, а остальные шли служить. До этого я учился в техническом лицее, где получил диплом программиста и к тому времени писал на Clipper'е, Turbo Pascal'е и Assembler'е. Имея первое место "на республике" по программированию, и соответствующий диплом, я наивно полагал, что в армии тоже буду программировать :) Но так в общем и вышло ;) Служил я в РВСН в/ч 44200, прикомандированным к центру психологической работы. Программировал в основном психологические тесты разных мастей по именитым методикам, но такая работа мне не очень нравилась, хотя и напрягала не особенно. Но кроме этого довелось мне писать две интересные программы: первая - расчет заработной платы военнослужащих, а вторая - мой дембельский аккорд (оптимальное распределение военнослужащих). О второй писать не буду т.к. писать придется слишком много :) Ну а первая, вот она:

 Написана на Borland Pascal 7.1 with objects. Работала под DOS, в режиме 640 x 480 x 4. В качестве базы данных использовался самописный движок для работы с .dbf (это была моя дипломная работа в лицее). Для программирования интерфейса импользовалась также самописная библиотека. Жаль, что в то время, я ни как не мог въехать в прелесть ООП и библиотека по сути состояла из объектов, но представленных весьма специфично ;) (т.е. каждый объект был записью которая манипулировалась неким множеством процедур и функций). Программа расчета  имела возможность создавать архивные копии данных при переходе на новый месяц, была предусмотрена система справочников, а для простоты изменения алгоритма расчета мне пришлось добавить поддержку формул, для чего был написан синтаксический анализатор (как же мне было трудно и интересно ;)) По интерфейсу было решено закосить под недавно вышедшую Windows 98, отсюда и название такое ;) Мне даже было не влом сделать поддержку цветовых схем аля Windows (см. следующий скриншот)

 

 

 

 

 

 

Программа получилась даже очень ничего ;) и была внедрена в одной из воинских частей, за что я был премирован новой шоколадной "афганкой" (кто служил тот поймет ;) Вот этим я занимался в армии :)

пятница, января 12, 2007

Ностальгия...

Сегодня наткнулся на скриншоты своей (ну писал-то я ее не один, нас было двое, но скрины только моего творчества ;)) первой и единственной корпоративной (как это модно сейчас называть) системы. Чего-то воспоминания нахлынули... В общем вот:

Это диалог регистрации пользователя в системе, где помимо обычных в этом деле  Диалог регистрацииполей можно было указать еще псевдоним базы данных к которой будет производится подключение. Это было очень удобно для разработки (а она непрерывно велась в процессе эксплуатации системы) т.к. все спорные моменты и приемы отлаживались на тестовой базе, а при опасных манипуляциях можно было подключиться и вовсе к резервному серверу :).

Далее опишу, как происходило обновление клиентских приложений.Диалог обновления Ввиду того, клиентские места были очень сильно разбросаны территориально (удаленные цеха, промыслы. самый дальний ~120км), проблема обновления ПО не решалась методом беготни с дискеткой по офисному зданию. Поэтому соответствующий функционал был заложен в серверную часть еще на стадии проектирования. Реализовано все было очень просто: сервер имел самые последние версии исполняемых модулей, разработчик делая билд (т.е. увеличивая версию) при подключении к серверу публиковал измененный модуль. Когда клиент подключался к серверу, тот проверял версии модулей клиента и имеющиеся у него новые и в случае необходимости, клиенту сливались обновленные версии.

Процесс публикации отражен на следующем скрине:

Пример UI сервераЭто не боевой сервер, скрин был сделан специально для демонстрации состояний клиентских подключений. В динамике все выглядело очень эффектно :), при каждом новом запросе, мониторчик клиента мигал, было сказачно красиво ;).

Моим первым приложением был "Электронный табель" позволявший на местах (в цехах, на промыслах) вести электронный учет рабочего времени, что позволяло отделу кадров оперативно отслеживать ситуацию на рабочих местах и вносить соответствующие коррективы. Но скрина этого приложения у меня не сохранилось, вместо него публикую скрин приложения "Отдел труда и зарплаты",ОТиЗП которое полностью повторяло функционал ведения табеля, но кроме этого делало еще массу полезных вещей :). На этом скрине видно большое меню, которое формировалось  (т.е. выполнялся весьма приличный запрос к серверу и по результатам строилось меню) в момент клика пользователем на соответствующем пункте. Работало очень даже шустро :)

Затем было написано приложение "Банковские операции", но его скрина у меня тоже не сохранилось. После отработки по банкам, я сел за приложение "Кассовые операции", скрин которого приведен ниже.Кассовые операции Это было достаточно простое приложение, но в нем был один интересный (в то время) для меня момент: его нужно было интегрировать с веб-сервером разработчиков (т.е. не корпоративным, а именно девелоперским, т.е. нашим) т.к. на сервере работал скрипт завязаный на сторонний софт, и тащить тот софт на клиента не было ни какого желания (это только добавило бы головной боли). Почему именно веб? А почему нет! :)

Затем мне пришлось возвращаться к старой теме (дело в том, что ранее я уже писал для этой конторы сию прилагу, но только под ДОС, и по договору т.е. я там еще не работал) и писать приложение "Подотчет".Подотчет Изюминкой этой работы для меня стала реализация "мультиформенного" приложения т.е. приложения без главной формы (office2000 like). Решилась проблема достаточно просто и эффективно, правда пришлось сильно попотеть изучая механизмы взаимодействия различных частей VCL, чтоб обеспечить корректность работы в таком режиме. Особенно это касалось модальности окна только для активной формы.

Ну и наконец скрин боевого сервераБоевой сервер. На скрине открыт диалог настройки с назначением прав пользователя. Пользовательская база была интегрирована с базой MS-SQL на котором и крутилась вся система. На SQL сервере было всего 25 лицензий, но за счет серверной части нашей системы (т.е. по сути ее второго уровня) на этих лицензиях работало ~70 целовек :) (я делал стресс-тестирование, сервер выдержал 150 подключений, но не выдержала моя рабочая машинка :))  Я тогда считал, мы экономили для конторы примерно $20К :) (сейчас вероятно цена уже не актуальна). Сервер вел подробнейшие логи работы пользователей, все запросы падали в лог, поэтому мы всегда могли отыскать любые концы :) (и это было важно ввиду некоей борьбы между двумя отделами). Т.к. система работала на BSS (Borland Socket Server) (знаю, что плохо, но это был единственный приемлемый вариант) у нас были частые проблемы с обрывами связи (т.к. сам BSS кривой, да и инфраструктура нашей сети была далека от идеала), поэтому в серверную и клиентскую части был добавлен функционал обеспечивающий механизм сессий и решавший таким образом проблемы с обрывами.

Кроме перечисленных, были написаны и другие приложения, скорее для разработчика нежели для пользователя, но из-за их незначительности упоминания они не удостаиваются.  Вот :) Писать замаялся ;)

p.s. Еще и армейскую работу вспомнить, чтоль...

четверг, января 11, 2007

Еще один Free

Обнаружил, что HTML Display Components стали бесплатными! Идеальное решение для HTML-интерфейса или отчетов в приложениях. К плюсам можно отнести независимость от сторонних движков и их версий, вследстие чего, однозначное отображение на любой системе. К минусам - не самое быстрое форматирование контента и скромные возможности управления внутренними объектами вследствие отсутствия поддержки DOM. Но мне, все равно, очень нравится ;)

понедельник, января 01, 2007

пятница, декабря 29, 2006

Инструменты, которые Free

Скорее речь даже не об инструментальных средствах разработчика, а о free-софте вообще. После выхода Turbo Delphi Explorer (которая free) у меня появилось желание если не отказаться от проприетарного софта совсем, то хотя бы свести его использование к минимуму. Как раз к этому моменту, вдоволь наэкспериментировавшись с Вистой (коею пользоваться не собираюсь, но в курсе быть нужно), решил начать с чистого листа, т.е. винта :) Вставил рекавери в свою Тошу и согласился на все, что мне предложили ;).

Итак:

  • ОС - лицензионная XP Pro SP2.
  • Браузер - Опера 9
  • Почта - Опера 9
  • Качалка - WinWGet (оболочка над консольной wget)
  • Аська - QIP
  • Блог-клиент - Windows Live Writer
  • Музычка - foobar 2000
  • Видео - Media Player Classic
  • Офис - OpenOffice (мне очень понравился)
  • CD писалка - с Тошей в комплекте идет Drag'n Drop CD+DVD (рядом поставил CDBurnerXP 3 Pro)
  • CD-виртуализатор - Алкоголь 120% (вечный триал ;) ибо мне хватает 3 дисков из 6 доступных)
  • Графика - GIMP, PhotoFiltre, XnView
  • 3D - Blender
  • Веб-дизайн - Nvu (мне показался удобнее дрима :)
  • Виртуалка - Microsoft Virtual PC 2004 (она же free!!!)
  • Архиватор - 7zip
  • Файл-менеджер - Far
  • Файрвол - Jetico Personal Firewall
  • Антивирус - Dr.Web CureIT!
  • Секурность - TrueCrypt
  • Программирование - Turbo Delphi Explorer, Ada GNAT
  • СУБД - FireBird (у него же есть embedded версия!!! ;) средство администрирования - IBExpert (для русских free!!!) (смотрел еще на Interbase Development Studio, но что-то она глючновата...)
  • Инсталь-мейкер - InnoSetup
  • Компоненты - множество бесплатных и разных ;) Условие только одно - наличие исходников. Очень нравятся: Synapse, UIB, ToolBar2000

Чему не нашел замены (может посоветует кто...):

  • Macromedia Fireworks (правда я его использовал не по назначению, а именно, как обычную рисовалку (т.е. его веб-ориентация меня мало интересовала). Может глянуть на Inscape или, как его там...)
  • AWIcons Pro - отличный редактор иконок
  • ReGet WebSynchronizer - отличный синхронизатор (я им бэкапы исходников делаю :)

вторник, декабря 19, 2006

И снова пакеты в Turbo Delphi Explorer

Ранее я уже писал о процессе подключения пакетов в Turbo Delphi Explorer, а теперь выкладываю модуль делающий это без необходимости саморегистрации подключаемых пакетов.

понедельник, ноября 13, 2006

Ловкость рук, и ни какого мошенства

Задача: дан класс имеющий приватный TThreadList (список с thread-safe доступом) и содержащий некие объекты. Нужно написать метод предоставляющий безопасный доступ к содержимому списка, без доступа пользователя к объекту-списку.

На первый взгляд задача совсем не простая т.к. для того чтоб обеспечить безопасный доступ необходимо вызывать методы блокировки/разблокировки объекта-списка, а условия нам этого не позволяют. На помощь приходят... интерфейсы (ну если быть точным, то лишь механизм подсчета их ссылок).

Решение: Определяем интерфейс IObjectProvider, который будет возвращать наш метод.

Type

IObjectProvider = Interface

Function GetObject : TObject;

Property Object : TObject Read GetObject;

End;

Затем описываем класс TObjectProvider, который реализует описанный ранее интефейс.

Type

//
TObjectProvider = Class(TInterfacedObject, IObjectProvider)

Private

FList : TThreadList;
FObject : TObject;

Public

Constructor Create(Const AList : TThreadList; AIndex : Integer);
Destructor Destroy; Override;

Function GetObject : TObject;

End;
//

// Реализация конструктора
Begin

FList := AList;
FObject := TObject(FList.LockList.Items[AIndex]);

End;
//

// Реализация деструктора
Begin

FList.UnlockList;
Inherited;

End;
//

// Реализация метода GetObject
Begin

Result := FObject;

End;
//


Теперь можно реализовать и наш метод:

Function SafeGetObject(AIndex : Integer) : IObjectProvider;
Begin

Result := TObjectProvider.Create(ThreadSafeList, AIndex);

End;

Как видно из кода, метод возвращает не сам объект, а интерфейс объекта-провайдера, для обеспечения безопасности. Пользователь вызвав метод и раскрыв его оператором with, получает в эксклюзивное пользование затребованный объект, а после завершения работы с объектом сработает механизм подсчета ссылок и будет вызван деструктор объекта-провайдера, который в свою очередь разблокирует список, после чего объект-провайдер будет разрушен.


Пример:

With SafeGetObject(5) Do
Begin

Object.Method;
...
Object.Method;

End;

воскресенье, ноября 12, 2006

Выравнивание вариантной части

Сегодня столкнулся с интересной штукой: вариантные поля (описанные с использованием case) записи могут быть выровнены по разному (ну компилятору ес-но виднее). В смысле не гарантируется что начало каждого вариантного поля будет сразу после последнего описанного не вариантного (или начала записи). Об этом, по большому счету, можно и не беспокоится до тех пор пока не придется обращаться к полям по смещению ;). Но об этом нужно помнить и быть аккуратным.

p.s. На packed записях не проверял.

понедельник, октября 30, 2006

Заменитель Indy

Не так давно открыл для себя сетевую библиотеку Synapse. Ощущения самые положительные. В отличии от монстра по имени Indy, Synapse являет собой легкую и элегантную оболочку над сетевой инфраструктурой сокетов. В настоящий момент, на ее основе, мною создаются транспортные уровни клиента и сервера XML-RPC (правда реализация на Indy, тоже предусматривается ;))

Глюкобага. Очередная...

Заколебало :( Сегодня снова столкнулся с очень неприятной багой компилятора (бага старая и ранее я ее описывал на iXBT).

Объявляем вложенный тип в записи. Пытаемся объявить поле данного типа и получаем от компилятора пламенный привет. А вот свойства объявленного вложенного типа определяются на ура. Есть workaround: объявить поле с типом допускающим приведение к объявленному, объявить свойство проблемного типа и в методах доступа (read/write) производить приведение типов (поле к свойству/свойство к полю). Дабы устранить оверхэд методов, объявить их, как  inline.

среда, октября 18, 2006

Маленькая радость

Америки я не отрою, если скажу, что с появлением динамических массивов и динамических строк жить стало легче, жить стало веселей :) Распределение памяти и управление временем жизни берет на себя компилятор, а мы можем избавится от выделения буферов средствами GetMem и ей подобным, т.к. такие массивы и строки являются по сути указателями на те самые буферы. И самое приятное, что такой буфер можно передать любой части программы без (даже временного) дублирования, т.к. компилятор использует механизм подсчета ссылок. Правда использовать такой буфер придется с приведением типа, но оно того стоит ;)

Пример:

s := 'http://www.borland.com/';


Stream.Write(Pointer(s)^, Length(s));

четверг, октября 12, 2006

Борис, ты не прав!

FAQ Apache, в части о сжатии запроса/ответа XML-RPC, говорит о том, что спецификация XML-RPC запрещает использование сжатия (и где они это нашли??). Считаю необходимым прояснить ситуацию. Спецификация XML-RPC требует: чтоб тип контента (Content-Type) запроса/ответа соответствовал text/xml, имелись некоторые обязательные заголовки (User-Agent, Content-Length) и не более того. Спецификация не запрещает применения сжатия к контенту (Content-Encoding), поэтому - Apache, ты не прав!

понедельник, октября 02, 2006

Enumerators (for-in-do)

Одним из самых заметных нововведений BDS 2006 является внесение в язык конструкции расширяющей оператор цикла For. For <container_item> In <container> Do; Переписывать сюда кусок документации не считаю нужным, и поэтому буду краток :) Лучше реализовывать перечислители в виде advanced-records т.к. при работе с ними не требуется динамического распределения памяти, а значит, код будет работать быстрее. Плюс удобнее работать с перечислителем вручную (не нужно создавать, не нужно финалить и разрушать), в случае необходимости. Правда сделать это (реализовать записью) не всегда удается, т.к. компилятор начинает ругаться на возвращаемый тип от GetEnumerator (видимо очередная глюкобага). Удачный пример перечислителя - перебор элементов XML документа, учитывая его структуру (каждый элемент может являться и родителем и ребенком) можно создать эффективный инструмент для перебора дочерних элементов любого родителя (хоть корня, хоть любого ребенка) с возможностью полного/близкого перебора. Сам перечислитель может обладать при этом указателем текущей глубины и номером элемента относительно начала перебора.