Spec-Zone.ru › Perl 5.30

perliol

СОДЕРЖАНИЕ

  • НАЗВАНИЕ
  • СИНТАКСИС
  • ОПИСАНИЕ
    • История и предыстория
    • Основная структура
    • Слои против дисциплин
    • Структуры данных
    • Функции и атрибуты
    • Данные на экземпляр
    • Слои в действии.
    • Флаги состояния экземпляра
    • Подробное описание методов
    • Утилиты
    • Реализация слоёв PerlIO
    • Ядерные слои
    • Слои расширения
  • TODO

НАЗВАНИЕ

perliol — C-API для реализации PerlIO в слоях.

СИНТАКСИС

/* Defining a layer ... */
#include <perliol.h>

ОПИСАНИЕ

Этот документ описывает поведение и реализацию абстракции PerlIO, описанной в perlapio, когда USE_PERLIO определён.

История и предыстория

Абстракция PerlIO была введена в perl5.003_02, но оставалась просто абстракцией до perl5.7.0. Однако в это время ряд расширений perl переключился на её использование, поэтому API в основном фиксирован для поддержания совместимости (исходного кода).

Целью реализации является предоставление API PerlIO гибким и независимым от платформы способом. Это также попытка использования подхода «Объектно-ориентированный C с vтаблицами», который может быть применён к Perl 6.

Основная структура

PerlIO представляет собой стек слоёв.

Нижние уровни стека работают с низкоуровневыми системными вызовами (дескрипторы файлов в C), получая и выводя байты, более высокие уровни стека буферизуют, фильтруют и иначе обрабатывают ввод-вывод и возвращают символы (или байты) Perl. Термины выше и ниже используются для обозначения относительного расположения слоёв стека.

Слой содержит «vтаблицу», таблицу операций ввода-вывода (на уровне C — таблицу указателей функций) и флаги состояния. Функции в vтаблице реализуют такие операции, как «open», «read» и «write».

При запросе ввода-вывода, например, «read», запрос идёт от Perl сначала вниз по стеку с использованием функций «read» каждого слоя, затем внизу запрос на ввод поступает от системных сервисов, затем результат возвращается вверх по стеку, и, наконец, интерпретируется как данные Perl.

Запросы не обязательно всегда проходят до уровня операционной системы: именно здесь вступает в игру буферизация PerlIO.

Когда вы выполняете open() и указываете дополнительные слои PerlIO для развертывания, указанные вами слои «помещаются» поверх уже существующего стека по умолчанию. Один из способов понять это — «операционная система слева», а «Perl справа».

Какие именно слои находятся в этом стеке по умолчанию, зависит от многих факторов: вашей операционной системы, версии Perl, конфигурации Perl во время компиляции и конфигурации Perl во время выполнения. См. PerlIO, "PERLIO" в perlrun и open для получения дополнительной информации.

binmode() работает аналогично open(): по умолчанию указанные слои помещаются поверх существующего стека.

Однако обратите внимание, что даже когда указанные слои «помещаются поверх» для open() и binmode(), это не означает, что эффекты ограничены «верхом»: слои PerlIO могут быть очень «активными» и проверять и влиять на слои, расположенные и глубже в стеке. Например, есть слой под названием «raw», который многократно «выталкивает» слои, пока не достигнет первого слоя, который заявил о своей способности обрабатывать двоичные данные. «Помещённые» слои обрабатываются в порядке слева направо.

sysopen() работает (неудивительно) на более низком уровне стека, чем open(). Например, в системах Unix или подобных Unix sysopen() работает непосредственно на уровне дескрипторов файлов: в терминах слоёв PerlIO он использует только слой «unix», который является довольно тонким оболочкой поверх дескрипторов файлов Unix.

Слои против дисциплин

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

Надеюсь, это естественный термин, учитывая реализацию, и должен избежать коннотаций, которые присущи более ранним использованиям «дисциплины» для вещей, которые довольно сильно отличаются.

Структуры данных

Основной структурой данных является PerlIOl:

typedef struct _PerlIO PerlIOl;
typedef struct _PerlIO_funcs PerlIO_funcs;
typedef PerlIOl *PerlIO;

struct _PerlIO
{
 PerlIOl *      next;       /* Lower layer */
 PerlIO_funcs * tab;        /* Functions for this layer */
 U32            flags;      /* Various flags for state */
};

A PerlIOl * — указатель на структуру, а PerlIO * на уровне приложения — указатель на PerlIOl * — то есть указатель на указатель на структуру. Это позволяет PerlIO * на уровне приложения оставаться постоянным, в то время как фактический PerlIOl * внизу изменяется. (Сравните с SV * perl, который остаётся постоянным, в то время как его поле sv_any меняется по мере изменения типа скаляра.) Поток ввода-вывода в общем случае представляется как указатель на этот связанный список «слоёв».

Следует отметить, что из-за двойного косвенного обращения в PerlIO *, &(perlio->next) «есть» PerlIO *, и поэтому, по крайней мере, один слой до некоторой степени может использовать стандартный API следующего слоя.

«Слой» состоит из двух частей:

  1. Функции и атрибуты класса «слоя».

  2. Данные на экземпляр для конкретной дескриптора.

Функции и атрибуты

К функциям и атрибутам осуществляется доступ через член «таб» (для таблицы) в PerlIOl. Функции (методы класса слоя) фиксированы и определяются типом PerlIO_funcs. Они в основном такие же, как публичные функции PerlIO_xxxxx.

struct _PerlIO_funcs
{
 Size_t     fsize;
 char *     name;
 Size_t     size;
 IV         kind;
 IV         (*Pushed)(pTHX_ PerlIO *f,
                            const char *mode,
                            SV *arg,
                            PerlIO_funcs *tab);
 IV         (*Popped)(pTHX_ PerlIO *f);
 PerlIO *   (*Open)(pTHX_ PerlIO_funcs *tab,
                          PerlIO_list_t *layers, IV n,
                          const char *mode,
                          int fd, int imode, int perm,
                          PerlIO *old,
                          int narg, SV **args);
 IV         (*Binmode)(pTHX_ PerlIO *f);
 SV *       (*Getarg)(pTHX_ PerlIO *f, CLONE_PARAMS *param, int flags)
 IV         (*Fileno)(pTHX_ PerlIO *f);
 PerlIO *   (*Dup)(pTHX_ PerlIO *f,
                         PerlIO *o,
                         CLONE_PARAMS *param,
                         int flags)
 /* Unix-like functions - cf sfio line disciplines */
 SSize_t    (*Read)(pTHX_ PerlIO *f, void *vbuf, Size_t count);
 SSize_t    (*Unread)(pTHX_ PerlIO *f, const void *vbuf, Size_t count);
 SSize_t    (*Write)(pTHX_ PerlIO *f, const void *vbuf, Size_t count);
 IV         (*Seek)(pTHX_ PerlIO *f, Off_t offset, int whence);
 Off_t      (*Tell)(pTHX_ PerlIO *f);
 IV         (*Close)(pTHX_ PerlIO *f);
 /* Stdio-like buffered IO functions */
 IV         (*Flush)(pTHX_ PerlIO *f);
 IV         (*Fill)(pTHX_ PerlIO *f);
 IV         (*Eof)(pTHX_ PerlIO *f);
 IV         (*Error)(pTHX_ PerlIO *f);
 void       (*Clearerr)(pTHX_ PerlIO *f);
 void       (*Setlinebuf)(pTHX_ PerlIO *f);
 /* Perl's snooping functions */
 STDCHAR *  (*Get_base)(pTHX_ PerlIO *f);
 Size_t     (*Get_bufsiz)(pTHX_ PerlIO *f);
 STDCHAR *  (*Get_ptr)(pTHX_ PerlIO *f);
 SSize_t    (*Get_cnt)(pTHX_ PerlIO *f);
 void       (*Set_ptrcnt)(pTHX_ PerlIO *f,STDCHAR *ptr,SSize_t cnt);
};

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

  1. Функции открытия и настройки

  2. Основные операции ввода-вывода

  3. Опции буферизации класса stdio.

  4. Функции для поддержки традиционного «быстрого» доступа Perl к буферу.

Слой не обязан реализовывать все функции, но вся таблица должна присутствовать. Нереализованные ячейки могут быть NULL (что приведёт к ошибке при вызове) или заполнены «заглушками» для наследования поведения от «базового класса». Это «наследование» фиксировано для всех экземпляров слоя, но поскольку слой выбирает, какие заглушки заполнять в таблице, ограниченное «множественное наследование» возможно.

Данные на экземпляр

Данные на экземпляр хранятся в памяти за пределами основной структуры PerlIOl, делая PerlIOl первым членом структуры слоя следующим образом:

typedef struct
{
 struct _PerlIO base;       /* Base "class" info */
 STDCHAR *      buf;        /* Start of buffer */
 STDCHAR *      end;        /* End of valid part of buffer */
 STDCHAR *      ptr;        /* Current position in buffer */
 Off_t          posn;       /* Offset of buf into the file */
 Size_t         bufsiz;     /* Real size of buffer */
 IV             oneword;    /* Emergency buffer */
} PerlIOBuf;

Таким образом (как и для скаляров perl) указатель на PerlIOBuf может рассматриваться как указатель на PerlIOl.

Слои в действии.

             table           perlio          unix
         |           |
         +-----------+    +----------+    +--------+
PerlIO ->|           |--->|  next    |--->|  NULL  |
         +-----------+    +----------+    +--------+
         |           |    |  buffer  |    |   fd   |
         +-----------+    |          |    +--------+
         |           |    +----------+

Выше показано, как схема слоёв работает в простом случае. Указатель PerlIO * приложения указывает на запись в таблице(ах), представляющей открытые (выделенные) дескрипторы. Например, первые три ячейки в таблице соответствуют stdin, stdout и stderr. Таблица, в свою очередь, указывает на текущий «верхний» слой для дескриптора — в данном случае экземпляр общего буферизующего слоя «perlio». Этот слой, в свою очередь, указывает на следующий слой ниже — в этом случае низкоуровневый слой «unix».

Выше примерно эквивалентно буферизованному потоку «stdio», но с гораздо большей гибкостью:

  • Если низкоуровневый Unix-слой read/write/lseek не подходит (например), для сокетов, то слой «unix» можно заменить (во время открытия или даже динамически) слоем «socket».

  • Разные дескрипторы могут иметь разные схемы буферизации. «Верхний» слой может быть слоем «mmap», если чтение файлов на диске было бы быстрее с использованием mmap вместо read. Небуферизованный поток можно реализовать, просто не имея буферизующего слоя.

  • Можно вставлять дополнительные слои для обработки данных по мере их прохождения. В этом и заключалась основная необходимость включения схемы в perl 5.7.0+ — нам нужен был механизм, позволяющий преобразовывать данные между внутренней кодировкой perl (по крайней мере, концептуально, Unicode как UTF-8) и «родным» форматом, используемым системой. Это обеспечивается слоем ":encoding(xxxx)", который обычно располагается над буферизующим слоем.

  • Можно добавить слой, который выполняет перевод «\n» в CRLF. Этот слой можно использовать на любой платформе, а не только на тех, которые обычно это делают.

Флаги состояния экземпляра

Общие флаги состояния — это гибрид флагов типа O_XXXXX, выведенных из строки режима, переданной в PerlIO_open(), и биты состояния для типичных буферизующих слоёв.

PERLIO_F_EOF

Конец файла.

PERLIO_F_CANWRITE

Запись разрешена, т.е. открыт как "w" или "r+" или "a" и т.д.

PERLIO_F_CANREAD

Чтение разрешено, т.е. открыт как "r" или "w+" (или даже "a+" - уф).

PERLIO_F_ERROR

Произошла ошибка (для PerlIO_error()).

PERLIO_F_TRUNCATE

Усечение файла, как предложено режимом открытия.

PERLIO_F_APPEND

Все записи должны быть добавлениями.

PERLIO_F_CRLF

Уровень выполняет преобразование, подобное Win32, "\n" на CR,LF для вывода и CR,LF на "\n" для ввода. Обычно предоставленный уровень "crlf" - единственный, кто должен об этом заботиться. PerlIO_binmode() будет управлять этим флагом, а не добавлять/удалять уровни, если бит PERLIO_K_CANCRLF установлен для класса уровней.

PERLIO_F_UTF8

Данные, записанные в этот уровень, должны быть закодированы в UTF-8; данные, предоставленные этим уровнем, должны рассматриваться как закодированные в UTF-8. Может быть установлен на любом уровне с помощью фиктивного уровня ":utf8". Также установлен на уровне ":encoding".

PERLIO_F_UNBUF

Уровень не буферизован - т.е. запись в следующий уровень должна происходить для каждой записи в этот уровень.

PERLIO_F_WRBUF

Буфер этого уровня в настоящее время содержит данные, записанные в него, но не отправленные следующему уровню.

PERLIO_F_RDBUF

Буфер этого уровня в настоящее время содержит непрочитанные данные, прочитанные из нижнего уровня.

PERLIO_F_LINEBUF

Уровень использует буферизацию по строкам. Данные записи должны передаваться следующему уровню, когда встречается "\n". Любые данные за "\n" должны затем обрабатываться.

PERLIO_F_TEMP

Файл был unlink()н, или должен быть удалён при close().

PERLIO_F_OPEN

Дескриптор открыт.

PERLIO_F_FASTGETS

Этот экземпляр этого уровня поддерживает интерфейс "быстрый gets". Обычно устанавливается на основе PERLIO_K_FASTGETS для класса и наличием функции(й) в таблице. Однако классу, который обычно предоставляет этот интерфейс, может потребоваться отказаться от него в конкретном экземпляре. Уровень "pending" должен сделать это, когда он наложен поверх уровня, который не поддерживает этот интерфейс. (Perl's sv_gets() не ожидает изменения быстрого gets поведения потоков во время одного "get".)

Подробное описание методов

fsize
Size_t fsize;

Размер таблицы функций. Сравнивается со значением, известным коду PerlIO, для проверки совместимости. Будущие версии могут уметь работать с слоями, скомпилированными с использованием устаревшей версии заголовков.

name
char * name;

Имя слоя, метод open() которого Perl должен вызывать при открытии. Например, если слой называется APR, вы вызовете:

open $fh, ">:APR", ...

и Perl знает, что ему необходимо вызвать метод PerlIOAPR_open(), реализованный слоем APR.

size
Size_t size;

Размер структуры данных на экземпляр, например:

sizeof(PerlIOAPR)

Если это поле равно нулю, то PerlIO_pushed не выделяет память и предполагает, что функция Pushed слоя выполнит все необходимые манипуляции со стеком слоёв — используется для избежания накладных расходов malloc/free для фиктивных слоёв. Если поле отлично от нуля, оно должно быть по крайней мере размером с PerlIOl, PerlIO_pushed выделит память для структур данных слоя и добавит новый слой в стек потока. (Если метод Pushed слоя возвращает ошибку, слой извлекается из стека.)

kind
IV kind;
  • PERLIO_K_BUFFERED

    Слой буферизован.

  • PERLIO_K_RAW

    Слой допускается в стеке binmode(FH) — т. е. он не (или настроится так, чтобы не) преобразовывал байты, проходящие через него.

  • PERLIO_K_CANCRLF

    Слой может преобразовывать между "\n" и CRLF концами строк.

  • PERLIO_K_FASTGETS

    Слой допускает просмотр буфера.

  • PERLIO_K_MULTIARG

    Используется, когда метод open() слоя принимает больше аргументов, чем обычно. Дополнительные аргументы должны находиться не перед аргументом MODE. При использовании этого флага слой отвечает за проверку аргументов.

Pushed
IV     (*Pushed)(pTHX_ PerlIO *f,const char *mode, SV *arg);

Единственный абсолютно обязательный метод. Вызывается, когда слой помещается в стек. Аргумент mode может быть NULL, если это происходит после открытия. Аргумент arg будет не-NULL, если был передан строковый аргумент. В большинстве случаев это должно вызвать PerlIOBase_pushed(), чтобы преобразовать mode в соответствующие флаги PERLIO_F_XXXXX в дополнение к любым действиям, которые выполняет сам слой. Если слой не ожидает аргумент, ему не нужно ни сохранять переданный ему аргумент, ни предоставлять Getarg() (он, возможно, Perl_warn то, что аргумент был неожиданным).

Возвращает 0 при успехе. При ошибке возвращает -1 и должно установить errno.

Popped
IV      (*Popped)(pTHX_ PerlIO *f);

Вызывается, когда слой извлекается из стека. Слой обычно извлекается после вызова Close(). Но слой может быть извлечён без закрытия, если программа динамически управляет слоями в потоке. В таких случаях Popped() должен освободить все ресурсы (буферы, таблицы преобразования и т. д.), не хранящиеся непосредственно в структуре слоя. Он также должен Unread() любые неиспользованные данные, которые были прочитаны и буферизованы из слоя ниже, обратно в этот слой, чтобы их можно было повторно предоставить тому, что сейчас находится выше.

Возвращает 0 при успехе и ошибке. Если Popped() возвращает true, то perlio.c предполагает, что либо слой извлёк себя, либо слой является чем-то особенным и должен быть сохранён по другим причинам. В большинстве случаев он должен возвращать false.

Open
PerlIO *        (*Open)(...);

Метод Open() имеет много аргументов, потому что он объединяет функции perl's open, PerlIO_open, perl's sysopen, PerlIO_fdopen и PerlIO_reopen. Полный прототип следующий:

PerlIO *       (*Open)(pTHX_ PerlIO_funcs *tab,
                       PerlIO_list_t *layers, IV n,
                       const char *mode,
                       int fd, int imode, int perm,
                       PerlIO *old,
                       int narg, SV **args);

Open должен (возможно, косвенно) вызвать PerlIO_allocate() для выделения слота в таблице и связывания его с информацией о слоях для открытого файла, вызвав PerlIO_push. Массив layers содержит все слои, предназначенные для PerlIO *, и все аргументы, переданные им. n — это индекс в этом массиве слоя, который вызывается. Макрос PerlIOArg вернёт (возможно, NULL) SV* для аргумента, переданного слою.

Если слой открывает или берёт во владение дескриптор файла, этот слой отвечает за приведение флага close-on-exec дескриптора файла в нужное состояние. Флаг должен быть сброшен для дескриптора файла с номером меньше или равным PL_maxsysfd, и установлен для любого дескриптора файла с большим номером. Для обеспечения потоковой безопасности, когда слой открывает новый дескриптор файла, он должен, если возможно, открыть его с флагом close-on-exec, изначально установленным.

Строка mode — это строка типа "fopen()", которая соответствовала бы регулярному выражению /^[I#]?[rwa]\+?[bt]?$/.

Префикс 'I' используется при создании stdin..stderr с помощью специальных вызовов PerlIO_fdopen; префикс '#' означает, что это sysopen, и что imode и perm должны быть переданы в PerlLIO_open3; 'r' означает чтение, 'w' означает запись и 'a' означает добавление. Суффикс '+' означает, что разрешены как чтение, так и запись/добавление. Суффикс 'b' означает, что файл должен быть двоичным, а 't' означает, что он текстовый. (Практически все слои должны выполнять ввод-вывод в двоичном режиме и игнорировать биты b/t. Слой :crlf должен быть помещён в стек для обработки этого различия.)

Если old не NULL, то это PerlIO_reopen. Perl сам не использует это (ещё?) и семантика немного неясна.

Если fd не отрицательно, то это числовой дескриптор файла fd, который будет открыт способом, совместимым со строкой режима. Вызов эквивалентен PerlIO_fdopen. В этом случае nargs будет равен нулю. Флаг close-on-exec дескриптора файла может быть установлен или сброшен; слой, который берёт его под своё управление, отвечает за приведение флага в нужное состояние.

Если nargs больше нуля, то он указывает на количество аргументов, переданных в open, в противном случае он будет равен 1, если, например, был вызван PerlIO_open. В простых случаях SvPV_nolen(*args) — это путь к файлу для открытия.

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

Если была выполнена PerlIO_push, и открытие завершилось неудачно, он должен PerlIO_pop сам, так как в противном случае слой не будет удалён и может привести к проблемам.

Возвращает NULL при неудаче.

Binmode
IV        (*Binmode)(pTHX_ PerlIO *f);

Необязательно. Используется, когда слой :raw помещается в стек (явным образом или в результате binmode(FH)). Если он отсутствует, слой будет извлечён. Если он присутствует, он должен настроить слой как двоичный (или извлечь себя) и вернуть 0. Если он возвращает -1 в случае ошибки, binmode завершится неудачно, при этом слой по-прежнему находится в стеке.

Getarg
SV *      (*Getarg)(pTHX_ PerlIO *f,
                    CLONE_PARAMS *param, int flags);

Необязательно. Если присутствует, он должен возвратить SV*, представляющий строковый аргумент, переданный слою при его размещении в стеке. Например, ":encoding(ascii)" вернёт SvPV со значением "ascii". (Аргументы param и flags можно игнорировать в большинстве случаев)

Dup использует Getarg для получения аргумента, первоначально переданного в Pushed, поэтому вы должны реализовать эту функцию, если ваш слой имеет дополнительный аргумент для Pushed и будет когда-либо Dup.

Fileno
IV        (*Fileno)(pTHX_ PerlIO *f);

Возвращает числовой дескриптор Unix/Posix для обработчика. Обычно PerlIOBase_fileno() (который просто спрашивает у следующего слоя ниже) будет достаточным для этого.

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

Dup
PerlIO * (*Dup)(pTHX_ PerlIO *f, PerlIO *o,
                CLONE_PARAMS *param, int flags);

XXX: Требуется больше документации.

Используется в рамках процесса "клонирования", когда создаётся поток (в этом случае параметр будет не NULL) и когда поток дублируется с помощью '&' в open.

Аналогично Open, возвращает PerlIO* при успехе, NULL при неудаче.

Read
SSize_t (*Read)(pTHX_ PerlIO *f, void *vbuf, Size_t count);

Базовая операция чтения.

Обычно вызывает Fill и манипулирует указателями (возможно, через API). PerlIOBuf_read() может подойти для производных классов, которые предоставляют методы "быстрого получения".

Возвращает фактическое количество прочитанных байтов или -1 при ошибке.

Unread
SSize_t (*Unread)(pTHX_ PerlIO *f,
                  const void *vbuf, Size_t count);

Надмножество ungetc() stdio. Должно обеспечить, чтобы будущие чтения видели байты в vbuf. Если нет очевидно лучшей реализации, то PerlIOBase_unread() предоставляет функцию путём размещения "фиктивного" слоя "ожидания" над вызывающим слоем.

Возвращает количество непрочитанных символов.

Write
SSize_t (*Write)(PerlIO *f, const void *vbuf, Size_t count);

Базовая операция записи.

Возвращает количество записанных байтов или -1 при ошибке.

Seek
IV      (*Seek)(pTHX_ PerlIO *f, Off_t offset, int whence);

Устанавливает указатель файла. Обычно должен вызвать собственный метод Flush и затем метод Seek следующего слоя ниже.

Возвращает 0 при успехе, -1 при ошибке.

Tell
Off_t   (*Tell)(pTHX_ PerlIO *f);

Возвращает указатель файла. Может основываться на кэшированном положении слоёв, чтобы избежать накладных расходов.

Возвращает -1 при неудаче получения указателя файла.

Close
IV      (*Close)(pTHX_ PerlIO *f);

Закрывает поток. Обычно должен вызвать PerlIOBase_close() для сброса себя и закрытия слоёв ниже, а затем освободить любые структуры данных (буферы, таблицы преобразования и т. д.), не хранящиеся непосредственно в структуре данных.

Возвращает 0 при успехе, -1 при неудаче.

Flush
IV      (*Flush)(pTHX_ PerlIO *f);

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

Возвращает 0 при успехе, -1 при ошибке.

Fill
IV      (*Fill)(pTHX_ PerlIO *f);

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

Возвращает 0 при успехе, -1 при ошибке.

Eof
END_OF_DOCUMENT_MARKER
IV      (*Eof)(pTHX_ PerlIO *f);

Возвращает индикатор конца файла. PerlIOBase_eof() обычно достаточно.

Возвращает 0 в конце файла, 1, если это не конец файла, -1 при ошибке.

Error
IV      (*Error)(pTHX_ PerlIO *f);

Возвращает индикатор ошибки. PerlIOBase_error() обычно достаточно.

Возвращает 1, если произошла ошибка (обычно когда PERLIO_F_ERROR установлено), 0 в противном случае.

Clearerr
void    (*Clearerr)(pTHX_ PerlIO *f);

Очищает индикаторы конца файла и ошибки. Следует вызвать PerlIOBase_clearerr(), чтобы установить флаги PERLIO_F_XXXXX, что может быть достаточно.

Setlinebuf
void    (*Setlinebuf)(pTHX_ PerlIO *f);

Помечает поток как строковый буферизованный. PerlIOBase_setlinebuf() устанавливает флаг PERLIO_F_LINEBUF и обычно достаточно.

Get_base
STDCHAR *       (*Get_base)(pTHX_ PerlIO *f);

Выделяет (если это ещё не сделано) буфер чтения для этого уровня и возвращает указатель на него. Возвращает NULL при ошибке.

Get_bufsiz
Size_t  (*Get_bufsiz)(pTHX_ PerlIO *f);

Возвращает количество байтов, которые были последними Fill() помещены в буфер.

Get_ptr
STDCHAR *       (*Get_ptr)(pTHX_ PerlIO *f);

Возвращает текущую позицию указателя чтения относительно буфера этого уровня.

Get_cnt
SSize_t (*Get_cnt)(pTHX_ PerlIO *f);

Возвращает количество байтов, которые ещё предстоит прочитать в текущем буфере.

Set_ptrcnt
void    (*Set_ptrcnt)(pTHX_ PerlIO *f,
                      STDCHAR *ptr, SSize_t cnt);

Корректирует указатель чтения и количество байтов, чтобы соответствовать ptr и/или cnt. Приложение (или уровень выше) должно гарантировать их согласованность. (Проверка разрешена по соображениям надёжности.)

Служебные функции

Для запроса следующего уровня вниз используйте PerlIONext(PerlIO *f).

Для проверки того, что PerlIO* действителен, используйте PerlIOValid(PerlIO *f). (Всё это фактически проверяет, что указатель не равен NULL и что указатель за ним также не равен NULL.)

PerlIOBase(PerlIO *f) возвращает указатель "Base", или, другими словами, указатель PerlIOl*.

PerlIOSelf(PerlIO* f, type) возвращает PerlIOBase, приведенный к типу.

Perl_PerlIO_or_Base(PerlIO* f, callback, base, failure, args) либо вызывает callback из функций слоя f (только по имени функции ввода-вывода, например, "Read") с args, либо, если такого callback нет, вызывает base версию callback с теми же args, либо, если f недействителен, устанавливает errno в EBADF и возвращает failure.

Perl_PerlIO_or_fail(PerlIO* f, callback, failure, args) либо вызывает callback функций слоя f с args, либо, если такого callback нет, устанавливает errno в EINVAL. Либо, если f недействителен, устанавливает errno в EBADF и возвращает failure.

Perl_PerlIO_or_Base_void(PerlIO* f, callback, base, args) либо вызывает callback функций слоя f с args, либо, если такого callback нет, вызывает base версию callback с теми же args, либо, если f недействителен, устанавливает errno в EBADF.

Perl_PerlIO_or_fail_void(PerlIO* f, callback, args) либо вызывает callback функций слоя f с args, либо, если такого callback нет, устанавливает errno в EINVAL. Либо, если f недействителен, устанавливает errno в EBADF.

Реализация слоёв PerlIO

Если документация по реализации кажется неясной или недостаточной, ознакомьтесь с существующими реализациями слоёв PerlIO, которые включают:

  • Реализации на C

    Файлы perlio.c и perliol.h в ядре Perl реализуют слои "unix", "perlio", "stdio", "crlf", "utf8", "byte", "raw", "pending", а также слои "mmap" и "win32", если применимо. (Слои "win32" на данный момент не завершены и не используются, чтобы узнать, что используется вместо этого в Win32, см. "Querying the layers of filehandles" в PerlIO.)

    PerlIO::encoding, PerlIO::scalar, PerlIO::via в ядре Perl.

    PerlIO::gzip и APR::PerlIO (mod_perl 2.0) в CPAN.

  • Реализации на Perl

    PerlIO::via::QuotedPrint в ядре Perl и PerlIO::via::* в CPAN.

При создании слоя PerlIO вы можете быть ленивыми, другими словами, реализовать только те методы, которые вас интересуют. Другие методы можно либо заменить "пустыми" методами

PerlIOBase_noop_ok
PerlIOBase_noop_fail

(которые ничего не делают и возвращают соответственно ноль и -1) или для определённых методов можно предположить поведение по умолчанию, используя NULL-методы. Метод Open ищет помощь на уровне "родителя". Следующая таблица обобщает это поведение:

   method      behaviour with NULL

   Clearerr    PerlIOBase_clearerr
   Close       PerlIOBase_close
   Dup         PerlIOBase_dup
   Eof         PerlIOBase_eof
   Error       PerlIOBase_error
   Fileno      PerlIOBase_fileno
   Fill        FAILURE
   Flush       SUCCESS
   Getarg      SUCCESS
   Get_base    FAILURE
   Get_bufsiz  FAILURE
   Get_cnt     FAILURE
   Get_ptr     FAILURE
   Open        INHERITED
   Popped      SUCCESS
   Pushed      SUCCESS
   Read        PerlIOBase_read
   Seek        FAILURE
   Set_cnt     FAILURE
   Set_ptrcnt  FAILURE
   Setlinebuf  PerlIOBase_setlinebuf
   Tell        FAILURE
   Unread      PerlIOBase_unread
   Write       FAILURE

FAILURE        Set errno (to EINVAL in Unixish, to LIB$_INVARG in VMS)
               and return -1 (for numeric return values) or NULL (for
               pointers)
INHERITED      Inherited from the layer below
SUCCESS        Return 0 (for numeric return values) or a pointer 

Основные слои

Файл perlio.c предоставляет следующие слои:

"unix"

Базовый небуферизованный слой, который вызывает Unix/POSIX read(), write(), lseek(), close(). Без буферизации. Даже на платформах, которые различают O_TEXT и O_BINARY, этот слой всегда O_BINARY.

"perlio"

Очень полный универсальный буферизованный слой, предоставляющий весь API PerlIO. Он также предназначен для использования в качестве "базового класса" для других слоёв. (Например, его метод Read() реализован через методы Get_cnt()/Get_ptr()/Set_ptrcnt()).

"perlio" над "unix" обеспечивает полную замену stdio, как видно через API PerlIO. Это значение по умолчанию для USE_PERLIO, когда stdio системы не позволяет перлу доступ к "быстрым функциям получения", и которые не различают O_TEXT и O_BINARY.

"stdio"

Слой, который предоставляет API PerlIO через схему слоёв, но реализует его, вызывая системные функции stdio. Это (на данный момент) значение по умолчанию, если stdio системы обеспечивает достаточный доступ для функций перла "быстрое получение", и которые не различают O_TEXT и O_BINARY.

"crlf"

Слой, полученный с использованием "perlio" в качестве базового класса. Обеспечивает преобразование "\n" в CR,LF в стиле Win32. Может быть применён как над "perlio", так и служить самим буферизованным слоем. "crlf" над "unix" является значением по умолчанию, если система отличает O_TEXT и O_BINARY открытие. (В какой-то момент "unix" будет заменён на "родной" слой ввода-вывода Win32 для этой платформы, так как слой чтения/записи Win32 имеет различные недостатки.) Слой "crlf" представляет собой разумную модель для слоя, который каким-либо образом преобразует данные.

"mmap"

Если Configure обнаруживает функции mmap(), этот слой предоставляется (с "perlio" в качестве "базового"), который выполняет операции "чтения" путём mmap() файла. Улучшение производительности незначительно на современных системах, поэтому он в основном служит примером концепции. Вероятно, он будет исключён из ядра в какой-то момент. Слой "mmap" представляет собой разумную модель для минималистичного "производного" слоя.

"pending"

Внутреннее производное от "perlio", которое может использоваться для предоставления функции Unread() для слоёв, у которых нет буфера или которые не хотят этим заниматься. (По сути, Fill() извлекает себя из стека и таким образом возобновляет чтение со слоя ниже.)

"raw"

Заглушка, которая никогда не существует в стеке слоёв. Вместо этого, при "вставке" он фактически извлекает себя из стека, а затем вызывает функцию таблицы Binmode на всех слоях в стеке — обычно это (через PerlIOBase_binmode) удаляет любые слои, у которых нет бита PERLIO_K_RAW. Слои могут изменить это поведение, определив свою собственную запись Binmode.

"utf8"

Ещё одна заглушка. При вставке она извлекает себя и устанавливает флаг PERLIO_F_UTF8 на слое, который был (и снова является) вершиной стека.

Кроме того, perlio.c также предоставляет ряд PerlIOBase_xxxx() функций, которые предназначены для использования в слотах таблиц классов, которым не нужно ничего особенного для определённого метода.

Расширяемые слои

Слои могут быть доступны модулями расширения. При встрече неизвестного слоя код PerlIO выполнит следующее:

use PerlIO 'layer';

Где layer — неизвестный слой. PerlIO.pm затем попытается:

require PerlIO::layer;

Если после этого процесса слой всё ещё не определён, open завершится ошибкой.

Следующие расширяемые слои входят в состав perl:

":encoding"
use Encoding;

делает этот слой доступным, хотя PerlIO.pm "знает", где его найти. Это пример слоя, который принимает аргумент при вызове, таким образом:

open( $fh, "<:encoding(iso-8859-7)", $pathname );
":scalar"

Обеспечивает поддержку чтения данных из и записи данных в скаляр.

open( $fh, "+<:scalar", \$scalar );

При открытии такого дескриптора, чтение извлекает байты из строкового значения $scalar, а запись изменяет значение. В обоих случаях позиция в $scalar начинается с нуля, но может быть изменена через seek, и определена через tell.

Обратите внимание, что этот слой подразумевается при вызове open(), таким образом:

open( $fh, "+<", \$scalar );
":via"

Предназначен для реализации слоёв на языке Perl. Например:

use PerlIO::via::StripHTML;
open( my $fh, "<:via(StripHTML)", "index.html" );

См. PerlIO::via для подробностей.

TODO

Задачи, которые необходимо выполнить для улучшения данной документации.

  • Объяснить, как создать действительный дескриптор файла без прохождения через open() (т.е. применить слой). Например, если файл не открыт через Perl, но мы хотим получить дескриптор, как будто он был открыт Perl.

    Как PerlIO_apply_layera вписывается в это, где находится его документация, был ли он опубликован?

    В настоящее время пример может быть таким:

    PerlIO *foo_to_PerlIO(pTHX_ char *mode, ...)
    {
        char *mode; /* "w", "r", etc */
        const char *layers = ":APR"; /* the layer name */
        PerlIO *f = PerlIO_allocate(aTHX);
        if (!f) {
            return NULL;
        }
    
        PerlIO_apply_layers(aTHX_ f, mode, layers);
    
        if (f) {
            PerlIOAPR *st = PerlIOSelf(f, PerlIOAPR);
            /* fill in the st struct, as in _open() */
            st->file = file;
            PerlIOBase(f)->flags |= PERLIO_F_OPEN;
    
            return f;
        }
        return NULL;
    }
  • Исправить/добавить документацию в местах, помеченных как XXX.

  • Обработка ошибок слоями не указана. Например, когда $! должен быть явно установлен, когда обработка ошибок должна быть просто делегирована верхнему слою.

    Возможно, стоит дать некоторые подсказки по использованию SETERRNO() или указатели на то, где их можно найти.

  • Я думаю, что конкретные примеры помогли бы лучше понять API. Конечно, я согласен с тем, что API должен быть лаконичным, но так как нет второго документа, который был бы более руководством, я думаю, что начать с документа API, но с примерами там, где есть неясные моменты, будет легче для человека, который ещё не является экспертом в PerlIO.

© 1993–2020 Larry Wall and others
Licensed under the GNU General Public License version 1 or later, or the Artistic License.
The Perl logo is a trademark of the Perl Foundation.
https://perldoc.perl.org/5.30.3/perliol

Spec-Zone.ru

Настройки Оффлайн Что нового Помощь О нас
Spec-Zone .ru
спецификации, руководства, описания, API