1// vi:set ft=cpp: -*- Mode: C++ -*-
2/*
3 * (c) 2014 Alexander Warg <alexander.warg@kernkonzept.com>
4 *
5 * This file is part of TUD:OS and distributed under the terms of the
6 * GNU General Public License 2.
7 * Please see the COPYING-GPL-2 file for details.
8 *
9 * As a special exception, you may use this file as part of a free software
10 * library without restriction.  Specifically, if other files instantiate
11 * templates or use macros or inline functions from this file, or you compile
12 * this file and link it with other files to produce an executable, this
13 * file does not by itself cause the resulting executable to be covered by
14 * the GNU General Public License.  This exception does not however
15 * invalidate any other reasons why the executable file might be covered by
16 * the GNU General Public License.
17 */
18#pragma once
19
20#include "types"
21#include "ipc_basics"
22#include "ipc_types"
23
24namespace L4 { namespace Ipc L4_EXPORT {
25
26/// Default type for passing length of an array.
27typedef unsigned short Array_len_default;
28
29/**
30 * Array reference data type for arrays located in the message.
31 * \note Use Array for normal RPC interfaces, Array_ref is usually used
32 *       as server-side argument, see Array.
33 * \tparam ELEM_TYPE  The data type of an array element, should be 'const'
34 *                    when used as input.
35 * \tparam LEN_TYPE   Data type used to store the number of elements in
36 *                    the array.
37 */
38template< typename ELEM_TYPE, typename LEN_TYPE = Array_len_default >
39struct Array_ref
40{
41  typedef ELEM_TYPE *ptr_type;
42  typedef LEN_TYPE len_type;
43
44  len_type length;
45  ptr_type data;
46  Array_ref() = default;
47  Array_ref(len_type length, ptr_type data)
48  : length(length), data(data)
49  {}
50
51  template<typename X> struct Non_const
52  { typedef Array_ref<X, LEN_TYPE> type; };
53
54  template<typename X> struct Non_const<X const>
55  { typedef Array_ref<X, LEN_TYPE> type; };
56
57  Array_ref(typename Non_const<ELEM_TYPE>::type const &other)
58  : length(other.length), data(other.data)
59  {}
60};
61
62/**
63 * Array data type for dynamically sized arrays in RPCs.
64 * \tparam ELEM_TYPE  The data type of an array element, should be 'const'
65 *                    when used as input.
66 * \tparam LEN_TYPE   Data type used to store the number of elements in
67 *                    the array.
68 *
69 * An Array generally encapsulates a data pointer and a length (number of
70 * elements). Array does \em not provide any storage for the data itself.
71 * The storage is either provided by a client-side caller or in the case
72 * of Array_ref is the message itself.
73 *
74 * Arrays can be used as input or as output arguments, when used as input
75 * ELEM_TYPE should be qualified \a const, when used as output a reference
76 * to an array must be used and the ELEM_TYPE must \em not be qualified
77 * \a const. It is the caller's responsibility to provide an array buffer
78 * of sufficient length. If a message from the server is too large it will
79 * be silently truncated.
80 *
81 * If backward compatibility with Ipc::Stream is required, then LEN_TYPE must
82 * be `unsigned long`.
83 */
84template<typename ELEM_TYPE, typename LEN_TYPE = Array_len_default>
85struct Array : Array_ref<ELEM_TYPE , LEN_TYPE>
86{
87  /// Make array
88  Array() {}
89  /// Make array from length and data pointer.
90  Array(LEN_TYPE length, ELEM_TYPE *data)
91  : Array_ref<ELEM_TYPE, LEN_TYPE>(length, data)
92  {}
93
94  template<typename X> struct Non_const
95  { typedef Array<X, LEN_TYPE> type; };
96
97  template<typename X> struct Non_const<X const>
98  { typedef Array<X, LEN_TYPE> type; };
99
100  /// Make a const array from a non-const array
101  Array(typename Non_const<ELEM_TYPE>::type const &other)
102  : Array_ref<ELEM_TYPE, LEN_TYPE>(other.length, other.data)
103  {}
104};
105
106/**
107 * Server-side copy in buffer for Array.
108 * \tparam ELEM_TYPE  Data type of an array element.
109 * \tparam LEN_TYPE   Data type for the number of elements in the array.
110 * \tparam MAX        The maximum number of elements in the buffer.
111 *                    If the actual message is longer than the buffer, it
112 *                    will be silently truncated.
113 *
114 * This type is assignment compatible to Array_ref<ELEM_TYPE, LEN_TYPE> and
115 * provides a transparent server-side copy-in mechanism for array parameters.
116 * The Array_in_buf provides the storage for the array data and receives a
117 * copy of the data passed to the server-function.
118 */
119template< typename ELEM_TYPE,
120          typename LEN_TYPE = Array_len_default,
121          LEN_TYPE MAX      = (L4_UTCB_GENERIC_DATA_SIZE *
122                                sizeof(l4_umword_t)) / sizeof(ELEM_TYPE) >
123struct Array_in_buf
124{
125  typedef Array_ref<ELEM_TYPE, LEN_TYPE> array;
126  typedef Array_ref<ELEM_TYPE const, LEN_TYPE> const_array;
127
128  /// The data elements
129  ELEM_TYPE data[MAX];
130  /// The length of the array
131  LEN_TYPE length;
132
133  /// copy in data from a source array
134  void copy_in(const_array a)
135  {
136    length = a.length;
137    if (length > MAX)
138      length = MAX;
139
140    for (LEN_TYPE i = 0; i < length; ++i)
141      data[i] = a.data[i];
142  }
143
144  /// Make Array_in_buf from a const array
145  Array_in_buf(const_array a)  { copy_in(a); }
146  /// Make Array_in_buf from a non-const array
147  Array_in_buf(array a)  { copy_in(a); }
148};
149
150// implementation details for transmission
151namespace Msg {
152
153/// Array as input arguments
154template<typename A, typename LEN>
155struct Elem< Array<A, LEN> >
156{
157  /// Array<> as argument at the interface
158  typedef Array<A, LEN> arg_type;
159  /// Array_ref<> at the server side
160  typedef Array_ref<A, LEN> svr_type;
161  typedef svr_type svr_arg_type;
162  enum { Is_optional = false };
163};
164
165/// Array as output argument
166template<typename A, typename LEN>
167struct Elem< Array<A, LEN> & >
168{
169  /// Array<> & at the interface
170  typedef Array<A, LEN> &arg_type;
171  /// Array_ref<> as server storage type
172  typedef Array_ref<A, LEN> svr_type;
173  /// Array_ref<> & at the server side
174  typedef svr_type &svr_arg_type;
175  enum { Is_optional = false };
176};
177
178/// Array_ref as output argument
179template<typename A, typename LEN>
180struct Elem< Array_ref<A, LEN> & >
181{
182  /// Array_ref<> at the interface
183  typedef Array_ref<A, LEN> &arg_type;
184  /// Array_ref<> as server storage
185  typedef Array_ref<typename L4::Types::Remove_const<A>::type, LEN> svr_type;
186  /// Array_ref<> & as server argument
187  typedef svr_type &svr_arg_type;
188  enum { Is_optional = false };
189};
190
191template<typename A> struct Class<Array<A> > : Class<A>::type {};
192template<typename A> struct Class<Array_ref<A> > : Class<A>::type {};
193
194namespace Detail {
195
196template<typename A, typename LEN, typename ARRAY, bool REF>
197struct Clnt_val_ops_d_in : Clnt_noops<ARRAY>
198{
199  using Clnt_noops<ARRAY>::to_msg;
200  static int to_msg(char *msg, unsigned offset, unsigned limit,
201                    ARRAY a, Dir_in, Cls_data)
202  {
203    offset = align_to<LEN>(offset);
204    if (L4_UNLIKELY(!check_size<LEN>(offset, limit)))
205      return -L4_EMSGTOOLONG;
206    *reinterpret_cast<LEN *>(msg + offset) = a.length;
207    offset = align_to<A>(offset + sizeof(LEN));
208    if (L4_UNLIKELY(!check_size<A>(offset, limit, a.length)))
209      return -L4_EMSGTOOLONG;
210    typedef typename L4::Types::Remove_const<A>::type elem_type;
211    elem_type *data = reinterpret_cast<elem_type*>(msg + offset);
212
213    // we do not correctly handle overlaps
214    if (!REF || data != a.data)
215      for (LEN i = 0; i < a.length; ++i)
216        data[i] = a.data[i];
217
218    return offset + a.length * sizeof(A);
219  }
220};
221} // namespace Detail
222
223template<typename A, typename LEN>
224struct Clnt_val_ops<Array<A, LEN>, Dir_in, Cls_data> :
225  Detail::Clnt_val_ops_d_in<A, LEN, Array<A, LEN>, false> {};
226
227template<typename A, typename LEN>
228struct Clnt_val_ops<Array_ref<A, LEN>, Dir_in, Cls_data> :
229  Detail::Clnt_val_ops_d_in<A, LEN, Array_ref<A, LEN>, true> {};
230
231template<typename A, typename LEN, typename CLASS>
232struct Svr_val_ops< Array_ref<A, LEN>, Dir_in, CLASS >
233: Svr_noops< Array_ref<A, LEN> >
234{
235  typedef Array_ref<A, LEN> svr_type;
236
237  using Svr_noops<svr_type>::to_svr;
238  static int to_svr(char *msg, unsigned offset, unsigned limit,
239                    svr_type &a, Dir_in, Cls_data)
240  {
241    offset = align_to<LEN>(offset);
242    if (L4_UNLIKELY(!check_size<LEN>(offset, limit)))
243      return -L4_EMSGTOOSHORT;
244    a.length = *reinterpret_cast<LEN *>(msg + offset);
245    offset = align_to<A>(offset + sizeof(LEN));
246    if (L4_UNLIKELY(!check_size<A>(offset, limit, a.length)))
247      return -L4_EMSGTOOSHORT;
248    a.data = reinterpret_cast<A*>(msg + offset);
249    return offset + a.length * sizeof(A);
250  }
251};
252
253template<typename A, typename LEN>
254struct Svr_xmit< Array<A, LEN> > : Svr_xmit< Array_ref<A, LEN> > {};
255
256template<typename A, typename LEN>
257struct Clnt_val_ops<Array<A, LEN>, Dir_out, Cls_data> : Clnt_noops<Array<A, LEN> >
258{
259  typedef Array<A, LEN> type;
260
261  using Clnt_noops<type>::from_msg;
262  static int from_msg(char *msg, unsigned offset, unsigned limit, long,
263                      type &a, Dir_out, Cls_data)
264  {
265    offset = align_to<LEN>(offset);
266    if (L4_UNLIKELY(!check_size<LEN>(offset, limit)))
267      return -L4_EMSGTOOSHORT;
268
269    LEN l = *reinterpret_cast<LEN *>(msg + offset);
270
271    offset = align_to<A>(offset + sizeof(LEN));
272    if (L4_UNLIKELY(!check_size<A>(offset, limit, l)))
273      return -L4_EMSGTOOSHORT;
274
275    A *data = reinterpret_cast<A*>(msg + offset);
276
277    if (l > a.length)
278      l = a.length;
279    else
280      a.length = l;
281
282    for (unsigned i = 0; i < l; ++i)
283      a.data[i] = data[i];
284
285    return offset + l * sizeof(A);
286  };
287};
288
289template<typename A, typename LEN>
290struct Clnt_val_ops<Array_ref<A, LEN>, Dir_out, Cls_data> :
291  Clnt_noops<Array_ref<A, LEN> >
292{
293  typedef Array_ref<A, LEN> type;
294
295  using Clnt_noops<type>::from_msg;
296  static int from_msg(char *msg, unsigned offset, unsigned limit, long,
297                      type &a, Dir_out, Cls_data)
298  {
299    offset = align_to<LEN>(offset);
300    if (L4_UNLIKELY(!check_size<LEN>(offset, limit)))
301      return -L4_EMSGTOOSHORT;
302
303    LEN l = *reinterpret_cast<LEN *>(msg + offset);
304
305    offset = align_to<A>(offset + sizeof(LEN));
306    if (L4_UNLIKELY(!check_size<A>(offset, limit, l)))
307      return -L4_EMSGTOOSHORT;
308
309    a.data = reinterpret_cast<A*>(msg + offset);
310    a.length = l;
311    return offset + l * sizeof(A);
312  };
313};
314
315template<typename A, typename LEN, typename CLASS>
316struct Svr_val_ops<Array_ref<A, LEN>, Dir_out, CLASS> :
317  Svr_noops<Array_ref<typename L4::Types::Remove_const<A>::type, LEN> &>
318{
319  typedef typename L4::Types::Remove_const<A>::type elem_type;
320  typedef Array_ref<elem_type, LEN> &svr_type;
321
322  using Svr_noops<svr_type>::to_svr;
323  static int to_svr(char *msg, unsigned offset, unsigned limit,
324                    svr_type a, Dir_out, Cls_data)
325  {
326    offset = align_to<LEN>(offset);
327    if (L4_UNLIKELY(!check_size<LEN>(offset, limit)))
328      return -L4_EMSGTOOLONG;
329
330    offset = align_to<A>(offset + sizeof(LEN));
331    a.data = reinterpret_cast<elem_type *>(msg + offset);
332    a.length = (limit-offset) / sizeof(A);
333    return offset;
334  }
335
336  using Svr_noops<svr_type>::from_svr;
337  static int from_svr(char *msg, unsigned offset, unsigned limit, long,
338                      svr_type a, Dir_out, Cls_data)
339  {
340    offset = align_to<LEN>(offset);
341    if (L4_UNLIKELY(!check_size<LEN>(offset, limit)))
342      return -L4_EMSGTOOLONG;
343
344    *reinterpret_cast<LEN *>(msg + offset) = a.length;
345
346    offset = align_to<A>(offset + sizeof(LEN));
347    if (L4_UNLIKELY(!check_size<A>(offset, limit, a.length)))
348      return -L4_EMSGTOOLONG;
349
350    return offset + a.length * sizeof(A);
351  }
352};
353
354template<typename A, typename LEN>
355struct Svr_xmit<Array<A, LEN> &> : Svr_xmit<Array_ref<A, LEN> &> {};
356
357// Pointer to array is not implemented.
358template<typename A, typename LEN>
359struct Is_valid_rpc_type< Array_ref<A, LEN> *> : L4::Types::False {};
360
361// Optional input arrays are not implemented.
362template<typename A, typename LEN>
363struct Is_valid_rpc_type< Opt<Array_ref<A, LEN> > > : L4::Types::False {};
364
365} // namespace Msg
366
367}}
368