1/**
2 * \file
3 * \brief XDR implementation using LWIP PBuf structures
4 *
5 * Uses standard XDR structure. Private fields in XDR are used as follows:
6 *  * x_private points to the first struct pbuf in a pbuf chain
7 *  * x_base points to the current struct pbuf in a pbuf chain
8 *  * x_handy is the position (offset) _within the current pbuf_
9 */
10
11/*
12 * Copyright (c) 2008, ETH Zurich.
13 * All rights reserved.
14 *
15 * This file is distributed under the terms in the attached LICENSE file.
16 * If you do not find this file, copies can be found by writing to:
17 * ETH Zurich D-INFK, Universitaetstrasse 6, CH-8092 Zurich. Attn: Systems Group.
18 */
19
20#include <assert.h>
21#include <nfs/xdr.h>
22#include "xdr_pbuf.h"
23#include <net_sockets/net_sockets.h>
24
25/* make space within the buffer, returns NULL if it won't fit */
26static inline int32_t *make_space(XDR *xdr, size_t size)
27{
28    if (xdr->x_handy + size > xdr->size) {
29        fprintf(stderr, "xdr_pbuf: make_space(%zu) failing (%zu available)\n",
30                size, xdr->size - (size_t)xdr->x_handy);
31        return NULL;
32    } else {
33        int32_t *ret = (int32_t *)((char *)xdr->x_base + xdr->x_handy);
34        xdr->x_handy += size;
35        return ret;
36    }
37}
38
39/* get a word from underlying stream */
40static bool xdr_pbuf_getint32(XDR *xdr, int32_t *ret)
41{
42    int32_t *buf = make_space(xdr, sizeof(int32_t));
43    if (buf) {
44        *ret = ntohl((uint32_t)*buf);
45        return true;
46    } else {
47        return false;
48    }
49}
50
51/* put a word to underlying stream */
52static bool xdr_pbuf_putint32(XDR *xdr, const int32_t *val)
53{
54    int32_t *buf = make_space(xdr, sizeof(int32_t));
55    if (buf) {
56        *buf = htonl((uint32_t)(*val));
57        return true;
58    } else {
59        return false;
60    }
61}
62
63/* common implementation of getbytes and putbytes */
64static bool movebytes(bool copyin, XDR *xdr, char *callerbuf, size_t nbytes)
65{
66    while (nbytes > 0) {
67        size_t space = xdr->size - xdr->x_handy;
68        if (space > nbytes) {
69            space = nbytes;
70        }
71        int32_t *buf = make_space(xdr, space);
72        assert(buf != NULL);
73        if (copyin) {
74            memcpy(buf, callerbuf, space);
75        } else {
76            memcpy(callerbuf, buf, space);
77        }
78        nbytes -= space;
79        callerbuf += space;
80    }
81    return true;
82}
83
84/* get some bytes from underlying stream */
85static bool xdr_pbuf_getbytes(XDR *xdr, char *retbuf, size_t nbytes)
86{
87    return movebytes(false, xdr, retbuf, nbytes);
88}
89
90/* put some bytes to underlying stream */
91static bool xdr_pbuf_putbytes(XDR *xdr, const char *inbuf, size_t nbytes)
92{
93    return movebytes(true, xdr, (char *)inbuf, nbytes);
94}
95
96/* returns bytes off from beginning */
97static size_t xdr_pbuf_getpostn(XDR *xdr)
98{
99    return xdr->x_handy;
100}
101
102/* lets you reposition the stream */
103static bool xdr_pbuf_setpostn(XDR *xdr, size_t pos)
104{
105    if (pos > xdr->size) {
106        return false;
107    } else {
108        xdr->x_base = xdr->x_private;
109        xdr->x_handy = pos;
110        return true;
111    }
112}
113
114/* buf quick ptr to buffered data */
115static int32_t *xdr_pbuf_inline(XDR *xdr, size_t nbytes)
116{
117    assert(nbytes % BYTES_PER_XDR_UNIT == 0);
118    return make_space(xdr, nbytes);
119}
120
121/* free privates of this xdr_stream */
122static void xdr_pbuf_destroy(XDR *xdr)
123{
124    net_free(xdr->x_private);
125}
126
127/// XDR operations table
128static struct xdr_ops xdr_pbuf_ops = {
129    .x_getint32 = xdr_pbuf_getint32,
130    .x_putint32 = xdr_pbuf_putint32,
131    .x_getbytes = xdr_pbuf_getbytes,
132    .x_putbytes = xdr_pbuf_putbytes,
133    .x_getpostn = xdr_pbuf_getpostn,
134    .x_setpostn = xdr_pbuf_setpostn,
135    .x_inline = xdr_pbuf_inline,
136    .x_destroy = xdr_pbuf_destroy,
137};
138
139/**
140 * \brief Create XDR and allocate PBUF for serialising data
141 *
142 * \param xdr Memory for XDR struct, to be initialised
143 * \param size Size of pbuf buffers to allocate
144 *
145 * \returns True on success, false on error
146 */
147bool xdr_create_send(XDR *xdr, size_t size)
148{
149    assert(xdr != NULL);
150    assert(size % BYTES_PER_XDR_UNIT == 0);
151    xdr->x_base = xdr->x_private = net_alloc(size);
152    xdr->size = size;
153    assert(xdr->x_private);
154    xdr->x_op = XDR_ENCODE;
155    xdr->x_ops = &xdr_pbuf_ops;
156    xdr->x_handy = 0;
157    return true;
158}
159
160/**
161 * \brief Create XDR for deserialising data in given PBUF
162 *
163 * \param xdr Memory for XDR struct, to be initialised
164 * \param pbuf LWIP packet buffer pointer
165 *
166 * \returns True on success, false on error
167 */
168void xdr_create_recv(XDR *xdr, void *data, size_t size)
169{
170    assert(xdr != NULL);
171    assert(size % BYTES_PER_XDR_UNIT == 0);
172    xdr->x_base = xdr->x_private = data;
173    xdr->size = size;
174    assert(xdr->x_private);
175    memcpy(xdr->x_private, data, size);
176    xdr->x_op = XDR_DECODE;
177    xdr->x_ops = &xdr_pbuf_ops;
178    xdr->x_handy = 0;
179}
180