perlhacktut
СОДЕРЖАНИЕ
ИМЯ
perlhacktut - Пошаговое руководство по созданию простой исправления кода на C
ОПИСАНИЕ
В этом документе рассматривается пример простой правки.
Если вы еще не читали perlhack, сделайте это сначала! Возможно, вам также стоит ознакомиться с perlsource.
После этого ознакомьтесь со статьей perlhacktips.
ПРИМЕР ПРОСТОЙ ПОПРАВКИ
Давайте рассмотрим простую правку от начала до конца.
Вот что предложил Ларри: если U является первым активным форматом во время pack, (например, pack "U3C8", @stuff) то результирующая строка должна обрабатываться как закодированная в UTF-8.
Если вы работаете с клоном репозитория Perl с помощью git, вам следует создать ветку для внесенных изменений. Это значительно упростит создание правильной правки. Подробности о том, как это сделать, см. в perlgit.
Создание исправления
Как подготовиться к исправлению? Сначала найдите нужный код — pack происходит во время выполнения, поэтому он будет в одном из файлов pp. Действительно, pp_pack находится в pp.c. Поскольку мы собираемся изменить этот файл, скопируем его в pp.c~.
[Когда этот учебник был написан, он находился в файле pp.c. Сейчас он был разделен на pp_unpack в свой собственный файл pp_pack.c]
Теперь давайте посмотрим на pp_pack: мы применяем шаблон к pat, а затем перебираем шаблон, беря каждый символ формата по очереди в datum_type. Затем для каждого возможного символа формата мы поглощаем другие аргументы в шаблоне (ширина поля, звездочка и т. д.) и преобразуем следующий фрагмент входных данных в указанный формат, добавив его к выходному SV cat.
Как узнать, является ли U первым форматом в pat? Если у нас есть указатель на начало pat, то если мы видим U, мы можем проверить, все ли ещё в начале строки. Вот где настраивается pat:
STRLEN fromlen;
char *pat = SvPVx(*++MARK, fromlen);
char *patend = pat + fromlen;
I32 len;
I32 datumtype;
SV *fromstr; У нас будет ещё один указатель на строку:
STRLEN fromlen;
char *pat = SvPVx(*++MARK, fromlen);
char *patend = pat + fromlen;
+ char *patcopy;
I32 len;
I32 datumtype;
SV *fromstr; И сразу перед началом цикла мы установим patcopy в начало pat:
items = SP - MARK;
MARK++;
SvPVCLEAR(cat);
+ patcopy = pat;
while (pat < patend) { Теперь, если мы увидим U, который находился в начале строки, мы включим флаг UTF8 для выходного SV, cat:
+ if (datumtype == 'U' && pat==patcopy+1)
+ SvUTF8_on(cat);
if (datumtype == '#') {
while (pat < patend && *pat != '\n')
pat++; Помните, что он должен быть patcopy+1, потому что первый символ строки — это U, который был поглощен в datumtype!
Ого, мы забыли кое-что: а что если в начале шаблона есть пробелы? pack(" U*", @stuff) будет иметь U как первый активный символ, даже если это не первое в шаблоне. В этом случае нам нужно продвинуть patcopy вместе с pat при обнаружении пробелов:
if (isSPACE(datumtype))
continue; должно стать
if (isSPACE(datumtype)) {
patcopy++;
continue;
} Хорошо. Часть с C завершена. Теперь нам нужно сделать две дополнительные вещи, прежде чем эта правка будет готова к работе: мы изменили поведение Perl, поэтому мы должны задокументировать это изменение. Также нам нужно предоставить дополнительные регрессионные тесты, чтобы убедиться, что наша правка работает и не создает ошибки где-то в другом месте.
Тестирование исправления
Регрессионные тесты для каждого оператора находятся в t/op/, поэтому скопируем t/op/pack.t в t/op/pack.t~. Теперь мы можем добавить наши тесты в конец. Сначала мы проверим, что U действительно создает строки Юникода.
В файле t/op/pack.t есть разумная функция ok(), но если её нет, мы можем использовать функцию из t/test.pl.
require './test.pl';
plan( tests => 159 ); Итак, вместо этого:
print 'not ' unless "1.20.300.4000" eq sprintf "%vd",
pack("U*",1,20,300,4000);
print "ok $test\n"; $test++; мы можем написать более разумный код (см. Test::More для полного объяснения функций is() и других функций тестирования).
is( "1.20.300.4000", sprintf "%vd", pack("U*",1,20,300,4000),
"U* produces Unicode" ); Теперь проверим, что мы правильно обработали пробелы в начале:
is( "1.20.300.4000", sprintf "%vd", pack(" U*",1,20,300,4000),
" with spaces at the beginning" ); И, наконец, проверим, что мы не создаем строки Юникода, если U не является первым активным форматом:
isnt( v1.20.300.4000, sprintf "%vd", pack("C0U*",1,20,300,4000),
"U* not first isn't Unicode" ); Нельзя забыть изменить количество тестов в верхней части, иначе автоматизированный тестер запутается. Это будет выглядеть так:
print "1..156\n"; или так:
plan( tests => 156 ); Теперь мы собираем Perl и запускаем его через тестовый набор. Наши новые тесты пройдены, ура!
Документирование исправления
Наконец, документация. Работа никогда не заканчивается, пока не закончены все формальности, поэтому давайте опишем внесенное изменение. Соответствующее место — pod/perlfunc.pod; опять же, мы создаем копию, а затем вставим этот текст в описание pack:
=item *
If the pattern begins with a C<U>, the resulting string will be treated
as UTF-8-encoded Unicode. You can force UTF-8 encoding on in a string
with an initial C<U0>, and the bytes that follow will be interpreted as
Unicode characters. If you don't want this to happen, you can begin
your pattern with C<C0> (or anything else) to force Perl not to UTF-8
encode your string, and then follow this with a C<U*> somewhere in your
pattern. Отправка
Подробности о том, как отправить это исправление, см. в perlhack.
АВТОР
Этот документ первоначально написал Нейтан Торкингтон, и его поддерживает почтовая рассылка perl5-porters.
© 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.34.0/perlhacktut