Архив метки: queue

Over-field in List-queue

Que     QUEUE
Name      STRING(40)
Key       STRING(10),OVER(Name)
Desc      STRING(100)
Price     DECIMAL(11,2)
        END

?List{PROP:Use} = Que

  1. List not «sees» OVER-field QUE.Key
  2. List considers, that the field Desc has number 2, and field Price has number 3.
  3. ?List{PROPLIST:FieldNo,2} return 2.    ! column for Desc field Who(Que,2) return «Key».
  4. Sort(Que,+Que:Desc); Sort(Que,-Que:Desc)
  5. We delete all records from the list. Sometimes we obtain GPF.
  6. End program or procedure — Always we obtain GPF.

Динамические очереди

bdr>  Меня интересуют твои наработки по динамическим очередям. Как я понимаю это реализовано у тебя в виде библиотеки(lib или dll).  Возможно ли получить от тебя dll, если да, то не кинеш ли на мыло baa@dionis.ru.  Заранее благодарен!

Я планирую на праздники (первые) отделить от данной библиотеки класс DynaFile по причине его «сырости» и тогда брошу все необходимое, например, на Клариошу. Иначе мой инет просто физически не потянет рассылать отдельно всем заинтересованным. Как и DynaView, это будут LIB и DLL модули для 32бит. Читать далее

Некоторые особенности при работе с QUEUE #2

Помнится, не так давно в наших эхах шли обсуждения на тему эмуляции Set для очередей. В особенности, когда очередь отсортирована, например, по двум полям, и надо встать на начало или конец какой-либо последовательности по первому полю.

Как известно, в C55 появился новый оператор, позволяющий это делать — POSITION().
Но! Есть подобный оператор, точнее функция, и в C5!

  Map
    Module('')
      RTL::Position(QUEUE _Que,LONG _F1=0,...),LONG,RAW,C,NAME('Cla$POSITIONqueuekey')
    .
  .

Использование данной функции несколько отличается от POSITION из C55:

  • POSITION(C55) работает только по текущей сортировке
  • POSITION(C5) работает по любой сортировке, т.к. принудительно, если необходимо, делает SORT.

В остальном логика работы и возвращаемый результат одинаковы для обеих функций.
Для POSITION(C55) не проверял, но POSITION(C5), кроме того, выставляет POINTER на (POSITION(C5)-1). Кроме случая, когда возвращается 0. Тогда POINTER так-же равен 0.

А теперь — о плохом 🙁
К сожалению, разработчики не экспортировали данную функцию в DLL-библиотеку! Таким образом, использование данной функции возможно ТОЛЬКО для LOCALE-варианта сборки приложения.
Вот такой вот облом! Главное не понятно — ПОЧЕМУ !!!

Некоторые особенности при работе с QUEUE

Все прекрасно знают, как работать с очередями используя ключевые поля.
Типа:

Add(Queue,+QUE:Field1,-QUE:Field2)
Put(Queue,+QUE:Field1,-QUE:Field2)
Get(Queue,+QUE:Field1,-QUE:Field2)
Sort(Queue,+QUE:Field1,-QUE:Field2)

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

Но! Довольно часто было бы неплохо задавать в качестве ключевых полей их номера в структуре записи очереди. Стандартные операторы такого не позволяют, но можно использовать для этого их внутренние представления, которые как раз и работают с номерами полей.

MAP
  MODULE('ClaRTL')
    QUE::Add(QUEUE _Que,LONG _F1=0,LONG _F2=0,LONG _F3=0,LONG _F4=0,LONG |
    _F5=0,LONG _F6=0,LONG _F7=0,LONG _F8=0,LONG _F9=0,LONG _F10=0,LONG=0) |
    ,RAW,C,NAME('Cla$ADDqueuekey')
    QUE::Put(QUEUE _Que,LONG _F1=0,LONG _F2=0,LONG _F3=0,LONG _F4=0,LONG |
    _F5=0,LONG _F6=0,LONG _F7=0,LONG _F8=0,LONG _F9=0,LONG _F10=0,LONG=0), |
    RAW,C,NAME('Cla$PUTqueuekey')
    QUE::Get(QUEUE _Que,LONG _F1=0,LONG _F2=0,LONG _F3=0,LONG _F4=0,LONG |
    _F5=0,LONG _F6=0,LONG _F7=0,LONG _F8=0,LONG _F9=0,LONG _F10=0,LONG=0), |
    RAW,C,NAME('Cla$GETqueuekey')
    QUE::Sort(QUEUE _Que,LONG _F1=0,LONG _F2=0,LONG _F3=0,LONG _F4=0,LONG |
    _F5=0,LONG _F6=0,LONG _F7=0,LONG _F8=0,LONG _F9=0,LONG _F10=0,LONG=0), |
    RAW,C,NAME('Cla$SORTqueuekey')
    QUE::Kill(QUEUE _Que),RAW,NAME('Cla$FREEqueue')
  END
END

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

QUE::Sort(Queue,1,-2) - Sort(Queue,+QUE:Field1,-QUE:Field2)

Последняя процедура QUE::Kill() может быть полезна при использовании большого кол-ва очередей. Как известно, при первом же обращении к очереди, для данной очереди создается блок служебных параметров размером ~1Kb. Данный блок освобождается ТОЛЬКО при завершении программы или при уничтожении очереди, если она была создана динамически через New(). Не существует других легальных способов освободить память, выделенную под этот служебный блок. Так вот данная процедура (QUE::Kill) позволяет как раз сделать это совершенно легально. Надо, также, иметь ввиду, что после применения данной процедуры ничего не мешает использовать эту очередь в дальнейшем. При следующем же обращении к этой очереди служебный блок будет создан заново. Другими словами, процедура QUE::Kill позволяет как бы эмулировать процесс создания-удаления динамических очередей операторами New()-Dispose().

Некоторые особенности работы QUEUE

Не так давно по эхам «пробегало» письмо, в котором автор сетовал на невозможность сортировки очереди по полям типа $STRING.

Это действительно так. Но! Это — не глюк Клариона! Это — особенность внутренней реализации структур, основанных на GROUP.

Дело в том, что структуры на основе GROUP имеют как бы двойной интерфейс доступа к полям.

Первый интерфейс — прямое обращение к полю структуры по его метке, которое обрабатывается компилятором на этапе создания программы. При этом, естественно, компилятору доступна ВСЯ информация по типам переменных. Соответственно этим типам ведется и работа с такими полями. Читать далее

Некоторые наблюдения по QUEUE #2

Не так давно Сергей Чушкин спрашивал насчет паковки записей очередей в C5-C55. Тогда я ответил, что подробно еще не разбирался с очередями.

В общем, подошла и очередь очередей :)(Каламбурчик, однако!) Теперь могу сказать точно — паковка в очередях есть и она работает! Более подробно сам механизм:

При общем размере записи (без учета OVER-полей) до 127 байт (включительно) запись не пакуется.
Начиная с размера 128 байт запись пакуется по простому алгоритму:
При упаковке вся запись рассматривается как массив байтов, без различения полей и их типов. При этом все повторяющиеся байты заменяются на последовательность <длина,байт-образец>. Перед последовательностью, которую не удалось сжать таким образом, ставиться байт с установленным старшим битом. Все остальные биты этого байта используются для задания длины несжатой последовательности. При необходимости (для задания большой длины) к данному байту добавляются еще байты. Аналогично и для задания кол-ва повторений байта-образца.
Только теперь старший бит сброшен.

В общем случае, для любой очереди, в качестве управляющей информации, распределяется около 1Kb памяти. Это без учета динамических ключей и накладных расходов на каждую запись.
Накладные расходы на каждую запись составляют 28 байт.
Кстати, из этих 28 байт полезной инфы — только 12 байт!
Остальные 16 байт — инфа менеджера памяти. На каждый динамический ключ еще по 4 байта.
Для упакованных записей — еще 4 байта. Да и еще каждая запись (упакованная или нет) выравнивается на границу 8 байт. Т.е., в худшем случае — еще +7байт.
Вот такая арифметика!

Кстати, реализовано все это достаточно нетривиально.
В заголовке каждой записи есть указатель, с помощью которого, через таблицу дискрипторов, вызывается процедура для обработки данной записи. Для неупакованных и упакованных записей она разная. В общем случае, для всех записей очереди вызывается одна и та-же процедура. Но, никто ведь не мешает повесить на каждую запись свою, уникальную, процедуру обработки данной записи! Какие, однако, перспективы!

И, вообще, согласен полностью с, кажется А.Ивановым, что очереди в Кларион — это сила!
Если-бы еще найти как подменить внутреннюю процедуру сортировки на свою, тогда вообще был бы полный кайф!

Некоторые наблюдения по QUEUE

Возможно кому будут интересны некоторые выводы по работе очередей, которые (выводы) сформировались в результате небольшого «копания» в них. Все выводы относятся ТОЛЬКО к реализации очередей в версии C5bee.

Динамические ключи действительно существуют и работают.

При первом поиске (Get) по неключевому полю (полям) выполняется стандартный SORT по этому полю (полям). Результат данной виртуальной сортировки сохраняется в так называемом «динамическом ключе», который представляет собой обычный массив из указателей на физические записи очереди. Под указателем имеется ввиду НЕ номер записи в очереди а именно указатель на область памяти, где физически размещена запись. Это дает возможность не перестраивать повторно уже созданные динамические ключи при изменениях очереди, таких как Add/Put/Delete/Sort. Читать далее

Я знаю как можно работать напрямую с файловыми и QUEUE структурами

Я знаю как можно работать напрямую с файловыми и QUEUE структурами. Т.е. можно использовать их динамическое формирование/изменение/удаление.

Очередь в памяти представлена практически так-же как и описание файла.
Например, тип FILE — это указатель на область памяти, которую назовем, например, «заголовок файла». Формат этого заголовка остался практически неизменным со времен CDD30. Несколько изменились лишь типы некоторых полей и в C5 добавились несколько полей, назначение которых Читать далее