Spec-Zone.ru › Perl 5.38

perliol

СОДЕРЖАНИЕ

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

НАЗВАНИЕ

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

СИНТАКСИС

/* 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 реализуют такие операции, как «открытие», «чтение» и «запись».

Когда запрашивается ввод-вывод, например, «чтение», запрос идёт от Perl сначала вниз по стеку, используя функции «чтение» каждого слоя, затем внизу запрашивается ввод от системных служб, затем результат возвращается вверх по стеку, в конечном итоге интерпретируясь как данные 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. Данные для каждого экземпляра конкретной обработки.

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

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

  • Разные обработчики могут иметь разные схемы буферизации. «Верхний» слой может быть слоем «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 для класса и наличием функции(ей) в таблице. Однако классу, который обычно предоставляет этот интерфейс, может потребоваться отказаться от него в конкретном экземпляре. Слой «ожидания» должен это сделать, когда он размещён над слоем, который не поддерживает интерфейс. (Perl sv_gets() не ожидает изменения поведения потока быстрого gets во время одного «получения».)

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

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() имеет много аргументов, потому что он объединяет функции перловских open, PerlIO_open, перловских 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) и при дублировании потока с помощью '&' в %%%CODE_BLOCK_137%%.

Аналогично 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
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, отформатированный как type.

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 системы не позволяет перлю "быстрый доступ к get", и которые не различают O_TEXT и O_BINARY.

"stdio"

Слой, который предоставляет API PerlIO через схему слоёв, но реализует его, вызывая stdio системы. Это (в настоящее время) значение по умолчанию, если stdio системы обеспечивает достаточный доступ для перля к "быстрым get", и которые не различают 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" в качестве "базового"), который выполняет операции "read", mmap()ing файл. Улучшение производительности незначительно на современных системах, поэтому он в основном существует как демонстрация концепции. Вероятно, он будет отделён от ядра в какой-то момент. Слой "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 для получения подробной информации.

Задачи

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

  • Объясните, как создать действительный fh без прохождения через open() (т.е. применить слой). Например, если файл не открывается через perl, но мы хотим получить fh, как если бы он был открыт 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–2023 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.38.0/perliol

Spec-Zone.ru

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