Содержание:
- Предисловие
- Модуль AcedMemory
- Модуль AcedBinary
- Модуль AcedStreams
- Модуль AcedStrings
- Модуль AcedCommon
- Модуль AcedCompression
- Модуль AcedCrypto
- Модуль AcedLists
- Модуль AcedStorage
- Модуль AcedExcelReport
- Описание демонстрационного проекта
- Заключение
В данной статье описывается свободно распространяемая библиотека функций и классов AcedUtils, которая облегчает разработку приложений под Borland Delphi на платформе Win32. Исходный код библиотеки и дополнительные материалы прилагаются к статье.
Предисловие
При разработке функций и классов, составляющих библиотеку AcedUtils, упор делался на оптимизацию по быстродействию. Но разработчик прикладной программы должен понимать, что секрет достижения максимальной производительности заключается не столько в оптимизации машинного кода, ассемблерных вставках и т.п., сколько в использовании правильных алгоритмов. Например, если стоит задача поиска некоторого значения в массиве элементов, можно пойти несколькими путями. Самый простой путь – последовательный перебор элементов и сравнение их с искомым значением. Для массива, содержащего всего несколько элементов, этот метод может оказаться самым эффективным. Если массив содержит много элементов, лучше заранее отсортировать его, а затем использовать бинарный поиск, который несравнимо быстрее последовательного перебора элементов. Если число элементов массива измеряется десятками тысяч и поиск в нем выполняется часто, имеет смысл пожертвовать лишней памятью для организации хешированного списка. Время поиска в хеше не зависит от размера массива. Но и хешированный список имеет свои недостатки, как, например, невозможность последовательной индексации элементов.
Кратко рассмотрим назначение модулей в составе AcedUtils.
- AcedMemory – реализует быстродействующий менеджер памяти, оптимизированный для работы с большим числом мелких фрагментов данных. Особенностью является ведение пула блоков памяти одного размера. Таким образом решается проблема дефрагментации пространства памяти, занятого блоками размером до 64кБ. Кроме того, данный менеджер памяти отличается высоким быстродействием и оптимальным, с точки зрения кэширования памяти, выравниванием распределяемых блоков.
- AcedConsts – содержит константы и методы, используемые другими модулями в составе AcedUtils. Кроме того, содержит определение типов ссылок на массивы, таких как PIntegerItemList, PObjectItemList, PPointerItemList и других.
- AcedBinary – объединяет функции для работы с блоками памяти, массивами байт, чисел типа Integer, Word, LongWord и битовыми строками. Включает функции для эффективной работы с упорядоченными массивами одинарных и двойных слов, выполнения логических операций над множествами, представленными в виде таких массивов.
- AcedStreams – содержит классы TBinaryReader и TBinaryWriter, позволяющие считывать данные из бинарного потока и помещать данные в бинарный поток. Исходные и выходные данные бинарного потока могут представляться в виде массива байт, строки в кодировке Base64, файла на диске или потока типа TStream. Данные, помещенные в бинарный поток, защищаются контрольной суммой Адлера и, при необходимости, сжимаются методом LZ+Huffman. Кроме того, данные в потоке могут быть зашифрованы методом RC6 и защищены цифровой сигнатурой SHA-256. В модуле AcedStreams также находятся классы TReaderStream и TWriterStream, которые позволяют работать с экземплярами классов TBinaryReader и TBinaryWriter, как будто эти классы являются потомками стандартного класса TStream.
- AcedStrings – содержит разнообразные функции для работы со строками, в частности, для сравнения строк с учетом и без учета регистра символов, перевода символов и строк в верхний/нижний регистр, перекодировки строк из DOS в Windows и обратно, поиска, замены, удаления подстрок и т.д. Кроме того, в AcedStrings находится класс TStringBuilder, предназначенный для построения длинных строк из отдельных фрагментов, а также изменения таких строк "на месте". При использовании этого класса память не распределяется заново каждый раз при добавлении к строке следующего фрагмента. Построение длинной строки с помощью экземпляра класса TStringBuilder выполняется значительно быстрее простой конкатенации строк в цикле.
- AcedCommon – объединяет функции, не выделенные в отдельные категории, в частности, функции форматирования даты и времени, преобразования числа в строку и наоборот, функции для записи денежной суммы прописью, перевода строки или байтового массива в кодировку Base64 и восстановления из кодировки Base64, а также для расчета контрольной суммы Адлера и CRC32, для работы со счетчиками и таймерами при измерении временных интервалов. Кроме того, здесь находятся некоторые вспомогательные функции, в том числе относящиеся к пользовательскому интерфейсу.
- AcedCompression – содержит функции, реализующие сжатие бинарных данных методом LZ+Huffman, и последующую распаковку данных, сжатых таким способом. Используемый алгоритм упаковки представляет собой немного измененный вариант алгоритма, который применяется в архиваторе PKZIP. За счет этих изменений достигается большая степень сжатия в режимах, отличных от скоростного (dcmFastest).
- AcedCrypto – объединяет функции для шифрования данных методами RC4 и RC6 (в режимах ECB, CFB и OFB), для расчета значения односторонней хеш-функции SHA-256, используемой при верификации данных. Цифровая сигнатура SHA-256 может вычисляться как для целой строки или массива байт, так и для данных, представленных в виде нескольких фрагментов. В этом же модуле находятся функции, реализующие генератор псевдослучайных чисел Mersenne Twister с периодом 219937-1.
- AcedLists – содержит классы для работы со списками: TBitList – битовая строка, т.е. упакованный набор элементов типа Boolean; классы TIntegerList, TWordList представляют собой упорядоченные наборы значений типа Integer и Word, соответственно; классы TIntegerAssociatedList, TStringAssociatedList – это сортированные списки ключей типа Integer и String, соответственно, с каждым из которых связан объект типа TObject; классы TIntegerHashTable, TStringHashTable аналогичны двум предыдущим, но ключи в них хранятся в виде хешированного списка; TLinkedList – связанный список, в узлах которого помещаются значения типа TObject; классы TArrayList, TArrayReadOnlyList – это коллекции указателей на объекты или записи с возможностью сортировки, поиска и группирования элементов.
- AcedStorage – объединяет классы для организации объектного хранилища данных, которое представляет собой эффективный способ хранения данных на локальном компьютере или файловом сервере с возможностью многопользовательского доступа. При реализации объектного хранилища используется хеширование записей по первичным ключам, индексация с возможностью выделения диапазонов значений, уведомления об изменении данных, поддерживаются ограничения уникальности. Для обеспечения возможности одновременного изменения данных одного набора, т.е. коллекции, несколькими пользователями применяется механизм транзакций. Каждая таблица данных представляется коллекцией типа TSerializableCollection, в которой содержатся записи – объекты, производные от класса TSerializableObject.
- AcedNetWait – форма, которая используется модулем AcedStorage при возникновении коллизий на файловом сервере. На экран выводится сообщение о том, что в данный момент открываемый файл данных заблокирован, т.к. он записывается другим пользователем или другим приложением. В этом случае пользователь должен подождать, повторить попытку открытия файла или отменить операцию. Следует отметить, что файл данных блокируется только на время автоматического согласования изменений и непосредственного сохранения данных. Блокировка не выполняется в процессе интерактивной корректировки данных одной и той же таблицы несколькими пользователями.
- AcedExcelReport – содержит функции и классы, облегчающие подготовку и печать отчетов с помощью Microsoft Excel. Обычно при работе с Microsoft Excel из Delphi приходится обращаться к различным интерфейсам из модуля Excel97, сгенерированного на основе библиотеки типов Microsoft Excel. В модуле AcedExcelReport собраны функции, наиболее часто используемые при подготовке отчетов. Это позволяет сократить размер и уменьшить сложность кода для генерации отчетов, а также сократить число обращений к Microsoft Excel через COM-интерфейсы, что существенно повышает производительность.
- AcedGridFrame, AcedCheckFrame, AcedViewFrame – фреймы для отображения на экране данных в виде таблицы с возможностью навигации. Представляют собой аналог компонента TStringGrid с расширенными возможностями. AcedGridFrame отображает таблицу и строку поиска. Для столбцов задается режим выравнивания и заголовок. Каждая ячейка может отображаться своим цветом. Поддерживается многострочный текст в ячейках и режим отложенного выделения записей. AcedCheckFrame, кроме того, показывает окно пометки (Checkbox) рядом с каждой записью и предоставляет коллекцию помеченных записей и коллекцию недоступных записей, для которых нельзя изменить пометку. AcedViewFrame отображает информацию в виде таблицы без строки поиска и выделения цветом текущей записи. Этот фрейм игнорирует события от клавиатуры, но предоставляет методы для скроллинга таблицы из кода. Дополнительную информацию о grid-фреймах можно почерпнуть из комментариев в исходном коде соответствующих модулей.
- AcedGrids – содержит описание общих типов и событий, используемых фреймами: AcedGridFrame, AcedCheckFrame и AcedViewFrame.
Теперь подробнее рассмотрим функциональность основных модулей AcedUtils.
Модуль AcedMemory
Этот модуль предназначен для замены стандартного менеджера памяти Borland Delphi альтернативным механизмом распределения памяти. Целью разработки нового менеджера памяти было стремление оптимизировать работу с небольшими блоками памяти, которые возникают при организации хранения данных в виде объектной базы, когда каждая запись представляется экземпляром соответствующего класса. Под оптимизацией подразумевается, во-первых, повышение скорости при выделении/освобождении блоков памяти, во-вторых, решение проблемы фрагментации свободных блоков. Рассмотрим подробнее, за счет чего это достигается при использовании AcedMemory.
Память запрашивается у операционной системы вызовом VirtualAlloc блоками размером 2МБ, которые в данном контексте называются регионами. Каждый регион состоит из 32 блоков размером 64кБ. Каждый такой блок предназначен для фрагментов памяти определенного размера. Например, один блок может содержать 4 фрагмента по 16кБ, другой – 1024 фрагмента по 64 байта и т.д. Когда из прикладной программы вызывается функция GetMem, размер выделяемых фрагментов памяти округляется вверх до ближайшего числа, равного 2 в степени N, где N может быть от 3 до 16. Когда все фрагменты памяти нужного размера в составе блока заняты, в регионе выделяется следующий блок под фрагменты памяти нужного размера. Если все 32 блока в составе региона заняты, создается новый регион размером 2МБ. Адрес каждого фрагмента памяти (до 64кБ) кратен его размеру, что положительно влияет на производительность. В отличие от стандартного менеджера памяти, размер выделяемых фрагментов не хранится в памяти с отрицательным смещением, а вычисляется, при необходимости, по адресу фрагмента. Когда требуется выделить фрагмент памяти размером более 64кБ, этот размер округляется вверх до числа, кратного 4096 байтам, а затем вызывается функция VirtualAlloc для запроса соответствующего блока памяти у операционной системы.
Для подключения AcedMemory к проекту, ссылку на этот модуль необходимо добавить в раздел uses файла проекта, причем AcedMemory должен стоять первым в этом разделе. Стандартные модули Forms, Classes и прочие должны находиться в списке uses, после AcedMemory. Если этого не сделать, при завершении программы возникнет ошибка. Это происходит из-за того, что в разделе finalization модуля Classes косвенно вызывается функция FreeMem для освобождения некоторых блоков памяти, выделенных в процессе работы программы. Модули финализируются в порядке, обратном их перечислению в разделе uses файла проекта. Чтобы менеджер памяти AcedMemory был еще активен на момент вызова раздела finalization модуля Classes, ссылка на AcedMemory должна находиться в списке uses файла проекта перед модулем Classes и любыми другими модулями, использующим Classes, как, например, модуль Forms.
Кроме, собственно, механизма для выделения/освобождения памяти, в модуле AcedMemory имеется несколько вспомогательных функций: G_GetMemSize возвращает размер блока памяти, адрес которого передан как параметр; функция G_GetTotalMemAllocated возвращает общий объем памяти, выделенный прикладной программе. Вызов этой функции не приводит к длительному пересчету размеров всех выделенных блоков. Вместо этого просто возвращается значение внутреннего счетчика памяти. Функция G_GetTotalMemCommitted возвращает общий размер физической памяти, выделенной программе операционной системой. Это число не учитывает память, выделенную в "куче" для служебных структур модуля AcedMemory, а также память, распределенную средствами, отличными от менеджера памяти.
Модуль AcedBinary
В этом модуле собрано большое число функций для работы с блоками памяти, массивами байт, слов, двойных слов, битовыми строками. Подробное их описание можно найти в исходном коде модуля. Следует обратить внимание на то, что функции из модулей AcedBinary и AcedCrypto используют инструкции MMX для достижения большей производительности. Поэтому на компьютере пользователя должен быть установлен, как минимум, процессор Pentium MMX или Pentium II, поддерживающий данный набор инструкций. Рассмотрим основные группы функций в AcedBinary.
К первой группе относятся функции, работающие с массивами одно-, двух- или четырехбайтных элементов. Например, распределить блок памяти и сразу обнулить его можно вызовом функции G_AllocMem. Когда необходимо заполнить нулями уже распределенный массив, пригодится функция G_ZeroMem. Если нужно скопировать содержимое одного блока памяти в другой и заранее известно, что эти блоки памяти не перекрываются, вместо стандартной процедуры Move лучше воспользоваться функцией G_CopyMem из AcedBinary, которая выполнит то же действие значительно быстрее. При работе с массивами двойных слов можно использовать функцию G_FillLongs для заполнения массива определенным значением, функцию G_CopyLongs для копирования такого массива или его фрагмента в другой массив. Когда требуется найти число типа Word или Integer в несортированном массиве, вместо организации цикла с перебором элементов массива лучше вызвать функцию G_Scan_Word или G_Scan_Integer. Эти функции используют специальные машинные инструкции для быстрого поиска значения в массиве двух- или четырехбайтных элементов.
В AcedBinary есть группа функций для работы с отсортированными массивами элементов типа Integer, Word, LongWord, которая включает функции для сортировки массивов по возрастанию (G_Sort…), выполнения бинарного поиска (G_Search…), обращения порядка элементов (G_Reverse…). Кроме того, такие массивы можно рассматривать как множества и применять к ним соответствующие операции. Например, отсортированный массив уникальных элементов типа Integer – это одно из возможных представлений множества: [-2147483648..2147483647]. Функция G_AndSet_Integer формирует из двух таких массивов новый массив, состоящий только из тех элементов, которые присутствуют в обоих массивах; G_OrSet_Integer возвращает массив, состоящий из элементов, которые присутствуют хотя бы в одном из исходных массивов; G_XorSet_Integer формирует массив из элементов, которые присутствуют только в одном из исходных массивов; G_AndNotSet_Integer возвращает массив элементов, которые присутствуют в первом, но отсутствуют во втором массиве. Есть еще две информационные функции для работы со множествами: G_SetIntersectsWith_Integer возвращает True, если в двух массивах есть какие-либо общие элементы, т.е. пересечение двух множеств не является пустым множеством; G_SetContainedIn_Integer возвращает True, если все элементы первого массива присутствуют во втором массиве, т.е. первое множество является подмножеством второго. Аналогичные функции имеются для массивов элементов типа Word и LongWord. Эти функции могут использоваться совместно с классами AcedIntegerList и AcedWordList, определенными в модуле AcedStreams.
Следующая группа функций в AcedBinary предназначена для работы с битами в составе двойного слова, т.е. значения типа LongWord. Эта группа включает функции: G_CeilPowerOfTwo, G_FloorPowerOfTwo (округляют двойное слово до ближайшей степени числа 2 вверх или вниз, соответственно); G_ReverseBits (обращает порядок следования битов); G_BitTest32, G_BitSet32, G_BitReset32, G_BitToggle32 (используются для проверки, установки, сброса или инвертирования одного бита в составе двойного слова). Есть также функции для подсчета числа установленных и сброшенных битов, поиска первого или последнего бита с заданным состоянием и т.п.
Последняя группа функций аналогична только что рассмотренным, но эти функции работают с битами в составе битовой строки. Например, функция G_BitSet устанавливает в единицу бит с заданным индексом в битовой строке, адресуемой первым параметром этой функции; функция G_ToggleBits инвертирует все биты в заданном диапазоне битовой строки; G_CountOfFreeBits подсчитывает число нулевых битов в битовой строке; G_SetBitScanForward возвращает индекс первого установленного, т.е. единичного, бита в составе битовой строки. Эти и другие функции используются классом TBitList, определенным в модуле AcedLists, для эффективной работы с данными, представленными в виде битовой строки.
Модуль AcedStreams
Содержит классы, предназначенные для работы с бинарным потоком данных. Бинарный поток в данном контексте – это просто массив байт в памяти, размер которого динамически увеличивается по мере добавления новых данных. После того, как все данные помещены в поток, содержимое бинарного потока может быть преобразовано в массив, строку в кодировке Base64, сохранено в файле или в другом потоке типа TStream. При этом может быть произведена упаковка данных методом LZ+Huffman, шифрование методом RC6 и подпись цифровой сигнатурой SHA-256. Для проверки целостности данных вместе с ними в бинарный поток помещается значение контрольной суммы Адлера. Бинарный поток создается с помощью экземпляра класса TBinaryWriter. Cтандартные классы в составе VCL ориентированы на работу с потоком типа TStream. Чтобы решить эту проблему в модуле AcedStreams предусмотрен класс TStreamWriter, который является потомком стандартного класса TStream и, одновременно, представляет собой оболочку над TBinaryWriter. Таким образом, можно, например, сохранить изображение типа TGraphic в потоке TBinaryWriter. Для этого следует создать экземпляр класса TStreamWriter, передав в его конструктор ссылку на TBinaryWriter. Затем этот экземпляр TStreamWriter можно передать как параметр в метод TGraphic.SaveToStream. Кроме классов TBinaryWriter, TStreamWriter, в модуле AcedStreams имеются соответствующие им классы TBinaryReader, TStreamReader, предназначенные для загрузки данных из бинарного потока.
Когда нужно считать данные из потока, после создания экземпляра класса TBinaryReader конструктором Create, чтобы загрузить массив с данными в память, необходимо вызвать один из следующих методов: LoadFromArray (загружает данные, представленные в виде массива байт, ссылка на который передается в качестве параметра), LoadFromBase64 (загружает данные из строки в кодировке Base64), LoadFromFile (загружает данные из файла, который был предварительно открыт и установлен в позицию, соответствующую началу блока данных), LoadFromStream (загружает данные из потока типа TStream). Все методы LoadFrom… принимают в качестве параметра ссылку на 256-битный ключ EncryptionKey. Если в этом параметре передан указатель на ключ, отличный от nil, при загрузке данных выполняется их дешифрование с указанным ключом и проверка целостности данных по сохраненному в потоке значению цифровой сигнатуры SHA-256. В классе TBinaryReader имеются методы для чтения из потока значений различного типа, включая String, Integer, TDateTime и т.д., а также метод Read, копирующий заданное число байт из бинарного потока в указанную область памяти. Если данные бинарного потока должны быть считаны некоторым стандартным классом, работающим только с потоками типа TStream, таким как класс TBitmap, нужно создать экземпляр класса TStreamReader, передав в его конструктор ссылку на соответствующий экземпляр TBinaryReader. После этого данные могут быть загружены методом TBitmap.LoadFromStream с передачей в него ссылки на созданный экземпляр TStreamReader.
Класс TBinaryWriter используется для создания и заполнения динамического массива, представляющего собой бинарного поток. Данные записываются в поток методами WriteString, WriteInteger, WriteDateTime и т.п. Кроме того, метод Write позволяет скопировать в поток содержимое произвольной области памяти. Когда все необходимые данные помещены в бинарный поток, можно вызвать один из следующих методов: SaveToArray (представляет данные потока, возможно, сжатые и зашифрованные, в виде массива байт), SaveToBase64 (представляет данные в виде строки в кодировке Base64), SaveToFile (сохраняет данные в файле, дескриптор которого передан в качестве параметра), SaveToStream (сохраняет данные в потоке типа TStream). Все методы SaveTo… принимают параметр CompressionMode, который устанавливает режим сжатия бинарных данных перед помещением их в выходной массив (см. перечень констант, выбирающих режим сжатия, при описании модуля AcedCompression). Методы SaveTo… принимают также параметр EncryptionKey. Если он не равен значению nil, то должен содержать ссылку на 256-битный ключ, т.е. массив из 32 байт. В этом случае данные бинарного потока шифруются методом RC6 с указанным ключом. Кроме того, для них вычисляется значение цифровой сигнатуры SHA-256, которая также записывается в выходной массив и используется в дальнейшем при расшифровке данных для проверки их целостности. Ключ шифра передается параметром типа PSHA256Digest. Это является своего рода подсказкой того, что в качестве ключа при шифровании методом RC6 удобно использовать значение односторонней функции SHA-256, рассчитанное для некоторого массива байт или строки символов, вводимой пользователем в качестве пароля.
Модуль AcedStrings
AcedStrings содержит набор функций, предназначенных для работы со строками типа AnsiString. Кроме того, в этом модуле находится класс TStringBuilder, являющийся аналогом классов StringBuilder из .NET или Java. Рассмотрим подробнее, что полезного есть в этом модуле.
В AcedStrings находятся функции, реализующие быстрое сравнение строк с учетом или без учета регистра символов. Сравнение выполняется на больше-меньше или на равно-не равно. При сравнении могут учитываться все символы или только определенное число начальных символов строки. Следующая группа объединяет функции, используемые для преобразования строк и отдельных символов к верхнему или нижнему регистру. Далее следуют функции, которые облегчают работу со строками, сохраненными в кодировке MS-DOS. Они позволяют быстро менять кодировку строк с DOS на Windows и обратно. В процессе преобразования используются массивы перекодировки ToOemChars и ToAnsiChars, определенные в конце интерфейсного раздела модуля. Эти массивы по умолчанию соответствуют кодировке "Кириллица Win1251". Однако, используя небольшую программу, текст которой приведен в виде комментария в модуле AcedStrings, можно сформировать соответствующие таблицы для любой другой кодировки символов. Есть также возможность динамического формирования таблиц перекодировки в момент запуска программы на компьютере конечного пользователя в зависимости от текущих региональных настроек. Этот режим включается при определении символа USE_DYNAMIC_TABLES в коде модуля AcedStrings.
Следующая группа содержит функции поиска, замены, удаления подстрок и отдельных символов. Здесь находятся функции быстрого поиска подстрок с учетом и без учета регистра символов. Поиск выполняется с начала (функции G_PosStr/G_PosText) или с конца строки (функции G_LastPosStr/G_LastPosText). Для поиска отдельных символов служат функции G_CharPos и G_LastCharPos. Функция G_Paste заменяет фрагмент строки определенной длины, начиная с указанного символа, другим фрагментом. Функции: G_ReplaceStr, G_ReplaceText, G_ReplaceChar, G_ReplaceChars, G_ReplaceCharsWithOneChar используются для замены всех вхождений подстроки или некоторого символа в строке другой подстрокой или символом. Функции: G_Delete, G_CutLeft, G_CutRight, G_DeleteStr, G_DeleteText, G_DeleteChar, G_DeleteChars, G_KeepChars используются для удаления из строки фрагментов, подстрок или отдельных символов. Функции: G_Compact, G_Trim, G_TrimLeft, G_TrimRight удаляют из строки пробелы и управляющие символы.
Далее следуют функции для работы с маской. Под маской здесь понимается шаблон, подобный тому, который используется при поиске файлов на диске, как, например, "rpt??w?.xls" или "*.tmp". Функция G_ApplyMask форматирует строку символов с помощью маски, а G_ExtractWithMask, наоборот, извлекает данные из строки по маске; G_ValidateMask используется для проверки того, что строка удовлетворяет заданной маске; функции G_ValidateWildStr и G_ValidateWildText выполняют проверку соответствия строки заданному шаблону, который может содержать специальные символы, предполагающие замену их любым количеством произвольных символов, т.е. аналогичные символу '*' в шаблоне для поиска файлов.
Кроме перечисленных, в AcedStrings входят функции: G_CountOfChar, G_CountOfChars для подсчета числа вхождений одного или нескольких символов в строку; G_IsEmpty для проверки того, что строка является пустой или содержит только пробелы и управляющие символы; G_PadLeft, G_PadRight, G_Center для дополнения строки, соответственно, слева, справа или с обеих сторон указанным символом до требуемой длины; функция G_Duplicate формирует строку, состоящую из нескольких копий другой строки; G_StrReverse обращает порядок символов в строке.
Если для управления памятью в проекте используется менеджер памяти AcedMemory, и в тексте модуля AcedStrings определен символ USE_ACED_MEMORY, становятся доступны функции G_NewStr, G_Append, G_Insert, G_AppendNewLine. Они могут использоваться как альтернатива классу TStringBuilder для того, чтобы эффективно изменять и дополнять строки типа AnsiString на месте, без лишнего перераспределения памяти. Например, формирование длинной строки в цикле с помощью функции G_Append выполняется значительно быстрее простой конкатенации строк. Однако, этот способ все же менее эффективен, чем использование класса TStringBuilder, т.к. при каждом изменении строки тратится дополнительное время на определение размера области памяти, выделенной под строку. Функции типа G_Append, G_Insert лучше применять для внесения небольшого числа изменений в длинную строку, когда создание специального экземпляра класса TStringBuilder неоправданно.
Класс TStringBuilder
Этот класс предназначен для динамического создания строки из отдельных фрагментов и изменения строки произвольной длины. В принципе, можно изменять и дополнять обычную строку типа AnsiString. Однако при каждом изменении длины такой строки происходит выделение памяти под новую строку и копирование в нее данных. Перераспределение памяти – это довольно-таки медленная операция, независимо от используемого менеджера памяти. В экземпляре TStringBuilder строка, по возможности, изменяется на месте, без лишней перетасовки памяти. Например, если необходимо сформировать строку из элементов некоторого списка, то вместо того, чтобы по очереди добавлять строки к переменной типа String, лучше создать экземпляр класса TStringBuilder, добавить каждую строку из списка методом Append, а затем преобразовать результат к типу String вызовом метода ToString класса TStringBuilder.
В классе TStringBuilder реализованы основные методы для внесения изменений и дополнения строки. В частности, метод Append добавляет к данной строке другую строку, фрагмент строки, один или несколько повторяющихся символов, десятичное или шестнадцатеричное представление числа; метод Insert вставляет подстроку в указанную позицию; метод Delete удаляет фрагмент строки; метод Reverse обращает порядок символов во всей строке или в отдельном фрагменте; метод Clear очищает строку. Еще есть методы для создания копии экземпляра класса TStringBuilder (метод Clone), преобразования строки или ее фрагмента к типу AnsiString (метод ToString), заполнения фрагмента строки указанным символом (метод Fill), добавления в конец строки символов перевода строки (метод AppendNewLine) и другие. Свойство Chars класса TStringBuilder позволяет работать со строкой как с обычным массивом символов.
Модуль AcedCommon
В AcedCommon собраны разнообразные функции, в том числе, для форматирования даты и времени, преобразования числа в строку и строки в число, записи числовых и денежных сумм прописью, замены символов строки шестнадцатеричными кодами этих символов и, наоборот, замены кодов символами, кодирования байтовых массивов и строк в Base64 и обратного их преобразования. Кроме того, здесь находятся функции для вычисления контрольной суммы Адлера и CRC32, работы со счетчиками, таймером высокого разрешении, а также несколько вспомогательных функций.
Форматирование даты и времени
В начале модуля AcedCommon объявлены константные массивы, содержащие названия месяцев и дней недели, записанных по-русски и по-английски. Для форматирования даты используется функция G_FormatDate. Она возвращает дату представленную краткой числовой формой или полной формой, содержащей название месяца и слово "года". Строка-результат может включать также полное или краткое название дня недели или, вообще, состоять только из дня недели без даты. Функция G_SplitDate позволяет выделить из даты день месяца как числовое значение, а сам месяц и год представить в виде строки. Для форматирования времени используется функция G_FormatTime, которая преобразует время в строку по правилам русского языка с возможным указанием числа секунд и миллисекунд.
Операции с числами типа Double и Currency
Следующая группа объединяет функции для работы со значениями типа Double и Currency. В частности, процедуры G_Inc, G_Dec, G_Mult, G_Div принимают в качестве первого параметра ссылку на переменную типа Double или Currency и, соответственно, складывают, вычитают, умножают или делят ее значение на число, переданное вторым параметром. Эти процедуры призваны компенсировать собой отсутствие в языке Delphi операций "+=", "/=" и т.п. из C-подобных языков. Функцию G_CurrencyToDouble необходимо вызывать для приведения значения типа Currency к типу Double, т.к. обычный способ приведения: "Double(Currency)" не срабатывает из-за ошибки в компиляторе. Функция G_ThousandsOfCurrencyToDouble преобразует значение типа Currency к типу Double, одновременно делит его на 1000 и округляет до 3-х знаков после запятой.
Преобразование числа в строку и строки в число
В AcedCommon есть несколько функций для преобразования целого числа в строку, содержащую его десятичное или шестнадцатеричное представление (функции G_IntToStr, G_UIntToHex и др.). G_HexToUInt возвращает число типа LongWord, шестнадцатеричная запись которого передана параметром типа String. Функции G_Between… используются для проверки того, что число, записанное в виде строки, лежит в нужном диапазоне. Функции G_StrTo… выполняют преобразование строки в число, типа Integer, LongWord, Int64, Extended или Currency. В случае ошибки эти функции не генерируют исключение, а возвращают значение False. Еще есть функция G_ModDiv10, которая делит значение var-параметра на 10 и возвращает остаток от деления как результат функции. Она может быть полезна при преобразовании в строку длинных чисел.
Функции G_NumToStr и G_NumToRub используются для записи числовых и денежных сумм прописью по-русски. Различные особенности записи чисел прописью настраиваются соответствующими параметрами форматирования. Например, вызов G_NumToStr(54321, S, nfFemale) вернет в переменной S строку: "пятьдесят четыре тысячи триста двадцать одна", а вызов G_NumToRub(3.2, ruFull, ruNumShort) вернет строку: "Три рубля 20 коп.".
Работа с кодировкой Base64 и шестнадцатеричными кодами
Функция G_StrToCodes возвращает строку, в которой каждый символ исходной строки, переданной параметром, представлен его шестнадцатеричным кодом. Например, вызов G_StrToCodes('ABC') вернет строку: "414243". Функция G_CodesToStr используется для обратного преобразования: G_CodesToStr('414242') вернет строку "ABC". Здесь же находятся функции для работы с кодировкой Base64, позволяющей представить строку символов или массив байт в виде строки, не содержащей пробелов, управляющих символов и символов в национальной кодировке. Функция G_Base64Encode выполняет кодирование строки символов или массива байт в Base64. Результат возвращается в виде строки типа AnsiString. Обратное преобразование выполняется функцией G_Base64Decode, которая восстанавливает исходный массив байт или строку символов из строки в кодировке Base64.
Вычисление контрольной суммы Адлера и CRC32
Следующая группа функций предназначена для вычисления контрольных сумм, которые используются при проверке целостности данных. Контрольная сумма представляет собой число типа LongWord, которое зависит от всего исходного массива данных. При изменении хотя бы одного бита данных, значение контрольной суммы меняется. Это значение может, например, передаваться вместе с данными по сети, а затем пересчитываться на приемном конце и сравниваться с исходным значением контрольной суммы. Если оба значения равны, то с большой вероятностью можно утверждать, что данные переданы без ошибок. Здесь имеются в виду случайные ошибки при передаче, а не предумышленное искажение данных. Для надежной защиты данных от любого вида вмешательства вместо контрольной суммы нужно использовать цифровую сигнатуру, такую как SHA-256 (см. описание модуля AcedCrypto). Правда, значение односторонней функции SHA-256 рассчитывается намного медленнее, чем контрольная сумма типа CRC32.
Функция G_Adler32 вычисляет контрольную сумму Адлера массива байт в соответствии с RFC 1950. Эта контрольная сумма используется библиотекой zlib. Она получается быстрее, чем CRC32, но, по всей видимости, является чуть менее надежной. Функции G_CRC32 и G_NextCRC32 возвращают значение контрольной суммы CRC32 указанной области памяти. Вторая функция отличается от первой тем, что она используется для поэтапного расчета контрольной суммы массива, представленного в виде нескольких фрагментов. Например, если длинный массив передается по сети в виде нескольких пакетов, функция G_NextCRC32, вызываемая последовательно для каждого пакета данных, вычислит в результате контрольную сумму целого массива. Пара функций G_CRC32_Str и G_CRC32_Text может использоваться при хешировании строк. Первая функция возвращает значение типа LongWord, которое зависит от всех символов строки с учетом их регистра. Вторая функция возвращает значение, которое зависит от всех символов строки, но не зависит от их регистра, т.е. это значение будет одинаковым для строк, которые отличаются только регистром символов.
Функции для замера временных интервалов
Когда в программе нужно отмерить временные интервалы, можно воспользоваться стандартной функцией GetTickCount из модуля Windows, которая возвращает число миллисекунд, прошедшее с момента загрузки операционной системы. Поскольку это значение выражается числом типа LongWord, со временем оно может переполниться и пойти на следующий "круг". Функция G_TickCountSince из модуля AcedCommon учитывает эту особенность и возвращает число миллисекунд, прошедшее с того момента, как функция GetTickCount вернула значение Tick, передаваемое в качестве параметра в G_TickCountSince. Хотя GetTickCount возвращает число миллисекунд, точность этой функции не превышает 0.1 секунды. Когда нужно отмерить интервал с точностью до одной миллисекунды можно воспользоваться таймером высокого разрешения. Получить текущее значение таймера можно вызовом функции G_QueryPC, которая возвращает значение, полученное вызовом системной функции QueryPerformanceCounter. По разности значений таймера можно определить длительность временного интервала с помощью функции G_GetTimeInterval. Кроме того, с помощью функции G_GetPC_Delta можно, наоборот, вычислить разность значений таймера, соответствующую временному интервалу определенной длительности.
К замеру временных интервалов с помощью таймера высокого разрешения имеет смысл прибегать только когда требуется высокая точность. В остальных случаях лучше воспользоваться функциями GetTickCount и G_TickCountSince, т.к. они используют меньше ресурсов системы. В модуле AcedCommon имеется также функция G_RDTSC, считывающая значение 64-разрядного счетчика, который увеличивается на каждом такте процессора. В некоторых случаях эта функция может заменить собой таймер высокого разрешения. Кроме того, она может быть полезна при инициализации генератора случайных чисел.
Прочие функции
Функция G_SwitchToApplication переводит на передний план запущенный экземпляр приложения, к которому принадлежит окно с указанным именем класса. Обычно эта функция применяется для предотвращения повторного запуска приложения, чтобы вместо запуска нового экземпляра вывести на передний план запущенный ранее экземпляр приложения. Кроме того, ее можно использовать, например, для переключения на другую задачу при организации приложения в виде нескольких независимых процессов. Функция G_SelectMenu используется для имитации выбора пользователем заданного пункта или подпункта меню текущего окна. При этом в функцию окна посылаются события, имитирующие нажатие пользователем клавиши вызова меню, клавиш со стрелками вправо и вниз, клавиши Enter, в последовательности, необходимой для выбора нужного пункта меню. При этом глубина раскрываемого подпункта может быть любой. Функции G_ToggleKey и G_IsToggled управляют состоянием клавиш: Num Lock, Caps Lock, Scroll Lock. Первая функция позволяет изменить состояние любой из этих клавиш, вторая – считывает текущее состояние соответствующей клавиши. Последняя функция G_ODS помещает сообщение в Event Log (журнал, вызываемый из отладчика в среде Borland Delphi по Ctrl+Alt+V).
Модуль AcedCompression
В модуле AcedCompression находятся функции, предназначенные для сжатия бинарных данных методом Зива-Лемпела с последующим преобразованием в код Хаффмана. Упаковка данных осуществляется функцией G_Deflate, которая принимает в качестве параметра Mode одну из констант, выбирающих режим сжатия. Возможны следующие режимы:
- dcmNoCompression – данные не сжимаются, просто копируются в выходной массив. Функция G_Deflate переключается в этот режим автоматически, если сжимаемые данные не являются избыточными, например, если они зашифрованы или упакованы другим методом. Размер исходного массива увеличивается при этом на 4 байта, в которых сохраняется длина исходного массива. Таким образом, 4 байта – это максимальная величина, на которую увеличивается длина исходного массива в случае невозможности его сжатия.
- dcmFastest – используется, когда надо сжать данные максимально быстро. При этом качество сжатия во многом зависит от характера самих данных. Если они содержат большое число повторяющихся фрагментов, использование этого режима нежелательно, т.к. в нем используется "облегченный" вариант словарного метода сжатия (Зива-Лемпела). Сжатие Хаффмана, наоборот, реализовано в полной мере. Оно основано на разности частот появления отдельных кодов символов. Если к исходным данным применим такой способ упаковки и они содержат мало повторяющихся последовательностей, режим dcmFastest может оказаться даже более эффективным, чем режим dcmMaximumCompression, за счет большей дисперсии частот отдельных кодов. Это справедливо, например, при сжатии wav-файлов. Максимальное расстояние между повторяющимися последовательностями байт в этом режиме принимается равным 8191 байту.
- dcmFast – обычная степень сжатия, достигаемая за минимальное время. Вероятно, данный режим является оптимальным при сжатии бинарных данных. В этом случае использование режимов dcmNormal и dcmMaximumCompression приводит к уменьшению размера выходного массива на 1-2%, но время сжатия возрастает в несколько раз. В режиме dcmFast максимальное расстояние между повторяющимися фрагментами данных равно 65535 байтам.
- dcmNormal – обычно соответствует хорошей степени сжатия. Его можно использовать для данных с большим числом повторяющихся фрагментов, например, текстов или doc-файлов. При этом сжатие происходит в 1.5-2 раза быстрее, чем в режиме dcmMaximumCompression, но примерно во столько же раз медленнее, чем в режиме dcmFast. Максимальное расстояние между повторяющимися группами байт принимается равным 131071 байту.
- dcmMaximumCompression – в этом режиме достигается максимальная степень упаковки, доступная для реализованного в AcedCompression метода сжатия. Может применяться для сильно избыточных данных или для данных, которые редко сжимаются, а затем многократно используются. Максимальное расстояние между повторяющимися фрагментами данных принимается равным 262143 байтам.
В модуле AcedCompression есть два варианта функции G_Deflate. В первом варианте функция сама распределяет память под массив, достаточный для хранения упакованных данных. Во втором варианте массив должен быть создан заранее, а функция G_Deflate только заполняет его данными.
Имеются также функции для распаковки сжатого массива данных. Размер исходного массива можно получить вызовом функции G_GetInflatedLength, а сама распаковка данных выполняется функцией G_Inflate. Имеется два варианта этой функции. В первом варианте память под массив, содержащий выходные данные, распределяется функцией G_Inflate. Во втором варианте, массив для хранения распакованных данных должен быть создан заранее размером не менее, чем длина, которую вернул вызов функции G_GetInflatedLength для упакованного массива данных. Следует отметить, что используемый режим сжатия не влияет на скорость распаковки данных (естественно, кроме режима dcmNoCompression, при котором данные итак находятся в распакованном виде).
Продолжение