Spec-Zone.ru › Perl 5.36

perliol

СОДЕРЖАНИЕ

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

ИМЯ

perliol - C API для реализации Perl's IO в слоях.

СИНОПСИС

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

ОПИСАНИЕ

В этом документе описано поведение и реализация абстракции PerlIO, описанной в perlapio, когда USE_PERLIO определена.

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

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

Цель реализации — предоставить API PerlIO гибким и независимым от платформы способом. Это также опыт «объектно-ориентированного C с vtable», который может быть применён к Raku.

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

PerlIO — это стек слоёв.

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

Слой содержит «vtable», таблицу операций ввода/вывода (на уровне C — таблицу указателей функций), и флаги состояния. Функции в vtable реализуют такие операции, как «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 или подобных системах 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. Данные на экземпляр для конкретной дескриптор.

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

К функциям и атрибутам можно получить доступ через член «tab» (для таблицы) 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

Слой выполняет преобразование «\n» в CR,LF для вывода и CR,LF в «\n» для ввода, подобно Win32. Обычно слой «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 * для аргумента, переданного слою.

Если слой открывает или получает права доступа к файловому дескриптору, этот слой отвечает за приведение флага закрытия при выполнении в правильное состояние. Флаг должен быть сброшен для файлового дескриптора с номером меньше или равным 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: Требуется более подробная документация.

Используется в рамках процесса "клонирования" при запуске потока (в этом случае param будет не 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);

Надстройка над stdio's ungetc(). Должно обеспечить, чтобы последующие чтения видели байты в 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
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, см. "Опрос слоёв файловых дескрипторов" в 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 системы не позволяет perl'овскому доступу «быстрых чтений», и которые не различают O_TEXT и O_BINARY.

"stdio"

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

"crlf"

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

"mmap"

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

"pending"

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

"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 для получения дополнительных сведений.

Задачи

Вещи, которые необходимо сделать для улучшения этого документа.

  • Объяснить, как создать действительный дескриптор файла без прохождения через 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, который является API, но содержит примеры там, где что-то неясно, для человека, который ещё не является гуру PerlIO (ещё).

© 1993–2021 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.36.0/perliol

Spec-Zone.ru

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