//sunrpc/xdr1/example.c
1 #include "unpipc.h"
2 #include "example.h"
3 int
4 main(int argc, char **argv)
5 {
6 int size;
7 example foo;
8 size = RNDUP(sizeof(foo.a)) + RNDUP(sizeof(foo.b)) +
9 RNDUP(sizeof(foo.c[0])) * MAXC;
10 printf("size = %dn", size);
11 exit(0);
12 }
8-9 Макрос RNDUP определен в файле <rpc/xdr.h>. Он округляет аргумент к ближайшему кратному BYTES_PER_XDR_UNIT (4). Для массива фиксированного размера вычисляется размер каждого элемента, который затем умножается на количество элементов.
Проблема возникает в случае использования типов данных переменной длины. Если мы объявим stringd<10>, максимальный размер будет RNDUP(sizeof( int)) (для длины) плюс RNDUP(sizeof(char)*10) (для символов строки). Но мы не можем вычислить размер буфера, если максимальный размер не указан в объявлении переменной (например, float e<>). Лучше всего в этом случае выделять буфер с запасом, а потом проверять, не возвращают ли подпрограммы XDR ошибку (упражнение 16.5).
Пример: необязательные данные
Существуют три способа задания необязательных данных в файле XDR, примеры для всех приведены в листинге 16.17.
Листинг 16.17. Файл спецификации XDR, иллюстрирующий способы задания необязательных данных
//sunrpc/xdr1/opt1.x
1 union optlong switch (bool flag) {
2 case TRUE:
3 long val;
4 case FALSE:
5 void;
6 };
7 struct args {
8 optlong arg1; /* объединение с булевским дискриминантом */
9 long arg2<1>; /* массив переменной длины с одним элементом */
10 long *arg3; /* указатель */
11 };
Объявление объединения с булевским дискриминантом
1-8 Мы определяем объединение с ветвями FALSE и TRUE и структуру этого типа. Если флаг дискриминанта TRUE, за ним следует значение типа long; в противном случае за ним ничего не следует. После кодирования библиотекой XDR это объединение будет закодировано как:
■ 4 байта флага со значением 1 (TRUE) и 4 байта целочисленного значения либо
■ 4 байта флага со значением 0 (FALSE).
Объявление массива переменной длины
9 Если мы указываем массив переменной длины с одним возможным элементом, он будет передан как:
■ 4 байта со значением 1 и 4 байта значения либо
■ 4 байта со значением 0.
Объявление указателя XDR
10 Новый способ определения необязательных данных заключается в объявлении указателя. Он будет закодирован как:
■ 4 байта со значением 1 и 4 байта значения либо
■ 4 байта со значением 0
в зависимости от значения соответствующего указателя при кодировании данных. Если указатель ненулевой, используется первый вариант кодирования. Если указатель нулевой, получится второй вариант. Это удобный способ кодирования необязательных данных в случае, если в нашем коде имеется указатель на эти данные.
Важная деталь реализации, благодаря которой оба варианта дают одинаковый результат при кодировании, заключается в том, что значение TRUE равно 1, что совпадает с длиной массива переменной длины, когда в нем есть один элемент.
В листинге 16.18 приведен текст заголовочного файла, созданного программой rpcgen для данного файла спецификации.
Листинг 16.18. Заголовочный файл, получившийся в результате обработки листинга 16.17
//sunrpc/xdr1/opt1.h
7 struct optlong {
8 bool_t flag;
9 union {
10 long val;
11 } optlong_u;
12 };
13 typedef struct optlong optlong;
14 struct args {
15 optlong arg1;
16 struct {
17 u_int arg2_len;
18 long *arg2_val;
19 } arg2;
20 long *arg3;
21 };
22 typedef struct args args;
14-21 Хотя все три аргумента кодируются одинаково, способы присваивания и получения их значений в языке С различны.
В листинге 16.19 приведен текст простой пpoгрaммы, устанавливающей значения всех трех аргументов так, что ни одно из полей long не кодируется.
Листинг 16.19. Ни одно из значений не будет закодировано
//sunrpc/xdr1/opt1z.с
1 #include "unpipc.h"
2 #include "opt1.h"
3 int
4 main(int argc, char **argv)
5 {
6 int i;
7 XDR xhandle;
8 char *buff;
9 long *lptr;
10 args out;
11 size_t size;
12 out.arg1.flag = FALSE;
13 out.arg2.arg2_len = 0;
14 out.arg3 = NULL;
15 buff = Malloc(BUFFSIZE); /* Адрес должен быть кратен четырем */
16 xdrmem_create(&xhandle, buff, BUFFSIZE, XOR_ENCODE);
17 if (xdr_args(&xhandle, &out) != TRUE)
18 err_quit("xdr_args error");
19 size = xdr_getpos(&xhandle);
20 lptr = (long*)buff;
21 for (i = 0; i < size; i += 4)
22 printf("%ldn", (long) ntohl(*lptr++));
23 exit(0);
24 }
Присваивание значений
12-14 Дискриминанту объединения присваивается значение FALSE, длина массива переменной длины устанавливается в 0, а указатель делается нулевым (NULL).
Выделение буфера и кодирование
15-19 Мы выделяем буфер и кодируем структуру out в поток XDR.
Вывод содержимого буфера XDR
20-22 Мы выводим содержимое буфера XDR по 4 байта, используя функцию ntohl (host-to-network long integer) для преобразования из порядка XDR big-endian в байтовый порядок узла. В результате получается именно то, что должно было быть помещено в буфер библиотекой XDR времени выполнения:
solaris % opt1z
0
0
0
Как мы и предполагали, каждому аргументу отводится 4 байта со значением О, указывающим на то, что за ним не следует никаких данных.
В листинге 16.20 приведена измененная версия программы, которая присваивает значения всем трем аргументам, кодирует их в поток XDR и выводит его содержимое.
Листинг 16.20. Присваивание значений аргументам из листинга 16.17
//sunrpc/xdr1/opt1.c
1 #include "unpipc.h"
2 #include "opt1.h"
3 int
4 main(int argc, char **argv)
5 {
6 int i;
7 XOR xhandle;
8 char *buff;
9 long lval2, lval3, *lptr;
10 args out;
11 size_t size;
12 out.arg1.flag = TRUE;
13 out.arg1.optlong_u.val = 5;
14 lval2 = 9876;
15 out.arg2.arg2_len = 1;
16 out.arg2.arg2_val = &lval2;
17 lval3 = 123;
18 out.arg3 = &lval3;
19 buff = Malloc(BUFFSIZE); /* адрес должен быть кратен 4 */
20 xdrmem_create(&xhandle, buff, BUFFSIZE, XDR_ENCODE);
21 if (xdr_args(&xhandle, &out) != TRUE)
22 err_quit("xdr_args error");
23 size = xdr_getpos(&xhandle);
24 lptr = (long *) buff;
25 for (i = 0; i < size; i += 4)
26 printf("%ldn", (long) ntohl(*lptr++));
27 exit(0);
28 }
Присваивание значений
12-18 Для присваивания значения объединению мы устанавливаем дискриминант в TRUE, а затем присваиваем значение полю long. Длину массива мы также сначала устанавливаем в 1. Указатель мы устанавливаем на соответствующее значение в памяти.
При запуске этой программы мы получим ожидаемые шесть 4-байтовых значений:
solaris % opt1
1 значение дискриминанта TRUE
5
1 длина массива переменной длины
9876
1 флаг для ненулевого указателя
123
Пример: обработка связного списка
Если осуществима передача необязательных данных, мы можем расширить возможности указателей в XDR и использовать их для кодирования и декодирования связных списков, содержащих произвольное количество элементов. В нашем примере используется связный список пар имя-значение. Соответствующий файл спецификации XDR приведен в листинге 16.21.
Листинг 16.21. Спецификация XDR для связного списка пар имя-значение
//sunrpc/xdr1/opt2.x
1 struct mylist {
2 string name<>;
3 long value;
4 mylist *next;
5 };
6 struct args {
7 mylist *list;
8 };
1-5 Структура mylist содержит одну пару имя-значение и указатель на следующую структуру такого типа. Указатель в последней структуре списка будет нулевым.
В листинге 16.22 приведен текст заголовочного файла, созданного программой rpcgen из файла opt2.х.
Листинг 16.22. Заголовочный файл, созданный программой rpcgen
//sunrpc/xdr1/opt2.h
7 struct mylist {
8 char *name;
9 long value;
10 struct mylist *next;