// vi:set ft=cpp: -*- Mode: C++ -*- /* * (c) 2014 Alexander Warg * * This file is part of TUD:OS and distributed under the terms of the * GNU General Public License 2. * Please see the COPYING-GPL-2 file for details. * * As a special exception, you may use this file as part of a free software * library without restriction. Specifically, if other files instantiate * templates or use macros or inline functions from this file, or you compile * this file and link it with other files to produce an executable, this * file does not by itself cause the resulting executable to be covered by * the GNU General Public License. This exception does not however * invalidate any other reasons why the executable file might be covered by * the GNU General Public License. */ #pragma once #include "types" #include "ipc_basics" #include "ipc_types" namespace L4 { namespace Ipc L4_EXPORT { /// Default type for passing length of an array. typedef unsigned short Array_len_default; /** * Array reference data type for arrays located in the message. * \note Use Array for normal RPC interfaces, Array_ref is usually used * as server-side argument, see Array. * \tparam ELEM_TYPE The data type of an array element, should be 'const' * when used as input. * \tparam LEN_TYPE Data type used to store the number of elements in * the array. */ template< typename ELEM_TYPE, typename LEN_TYPE = Array_len_default > struct Array_ref { typedef ELEM_TYPE *ptr_type; typedef LEN_TYPE len_type; len_type length; ptr_type data; Array_ref() = default; Array_ref(len_type length, ptr_type data) : length(length), data(data) {} template struct Non_const { typedef Array_ref type; }; template struct Non_const { typedef Array_ref type; }; Array_ref(typename Non_const::type const &other) : length(other.length), data(other.data) {} }; /** * Array data type for dynamically sized arrays in RPCs. * \tparam ELEM_TYPE The data type of an array element, should be 'const' * when used as input. * \tparam LEN_TYPE Data type used to store the number of elements in * the array. * * An Array generally encapsulates a data pointer and a length (number of * elements). Array does \em not provide any storage for the data itself. * The storage is either provided by a client-side caller or in the case * of Array_ref is the message itself. * * Arrays can be used as input or as output arguments, when used as input * ELEM_TYPE should be qualified \a const, when used as output a reference * to an array must be used and the ELEM_TYPE must \em not be qualified * \a const. It is the caller's responsibility to provide an array buffer * of sufficient length. If a message from the server is too large it will * be silently truncated. * * If backward compatibility with Ipc::Stream is required, then LEN_TYPE must * be `unsigned long`. */ template struct Array : Array_ref { /// Make array Array() {} /// Make array from length and data pointer. Array(LEN_TYPE length, ELEM_TYPE *data) : Array_ref(length, data) {} template struct Non_const { typedef Array type; }; template struct Non_const { typedef Array type; }; /// Make a const array from a non-const array Array(typename Non_const::type const &other) : Array_ref(other.length, other.data) {} }; /** * Server-side copy in buffer for Array. * \tparam ELEM_TYPE Data type of an array element. * \tparam LEN_TYPE Data type for the number of elements in the array. * \tparam MAX The maximum number of elements in the buffer. * If the actual message is longer than the buffer, it * will be silently truncated. * * This type is assignment compatible to Array_ref and * provides a transparent server-side copy-in mechanism for array parameters. * The Array_in_buf provides the storage for the array data and receives a * copy of the data passed to the server-function. */ template< typename ELEM_TYPE, typename LEN_TYPE = Array_len_default, LEN_TYPE MAX = (L4_UTCB_GENERIC_DATA_SIZE * sizeof(l4_umword_t)) / sizeof(ELEM_TYPE) > struct Array_in_buf { typedef Array_ref array; typedef Array_ref const_array; /// The data elements ELEM_TYPE data[MAX]; /// The length of the array LEN_TYPE length; /// copy in data from a source array void copy_in(const_array a) { length = a.length; if (length > MAX) length = MAX; for (LEN_TYPE i = 0; i < length; ++i) data[i] = a.data[i]; } /// Make Array_in_buf from a const array Array_in_buf(const_array a) { copy_in(a); } /// Make Array_in_buf from a non-const array Array_in_buf(array a) { copy_in(a); } }; // implementation details for transmission namespace Msg { /// Array as input arguments template struct Elem< Array > { /// Array<> as argument at the interface typedef Array arg_type; /// Array_ref<> at the server side typedef Array_ref svr_type; typedef svr_type svr_arg_type; enum { Is_optional = false }; }; /// Array as output argument template struct Elem< Array & > { /// Array<> & at the interface typedef Array &arg_type; /// Array_ref<> as server storage type typedef Array_ref svr_type; /// Array_ref<> & at the server side typedef svr_type &svr_arg_type; enum { Is_optional = false }; }; /// Array_ref as output argument template struct Elem< Array_ref & > { /// Array_ref<> at the interface typedef Array_ref &arg_type; /// Array_ref<> as server storage typedef Array_ref::type, LEN> svr_type; /// Array_ref<> & as server argument typedef svr_type &svr_arg_type; enum { Is_optional = false }; }; template struct Class > : Class::type {}; template struct Class > : Class::type {}; namespace Detail { template struct Clnt_val_ops_d_in : Clnt_noops { using Clnt_noops::to_msg; static int to_msg(char *msg, unsigned offset, unsigned limit, ARRAY a, Dir_in, Cls_data) { offset = align_to(offset); if (L4_UNLIKELY(!check_size(offset, limit))) return -L4_EMSGTOOLONG; *reinterpret_cast(msg + offset) = a.length; offset = align_to(offset + sizeof(LEN)); if (L4_UNLIKELY(!check_size(offset, limit, a.length))) return -L4_EMSGTOOLONG; typedef typename L4::Types::Remove_const::type elem_type; elem_type *data = reinterpret_cast(msg + offset); // we do not correctly handle overlaps if (!REF || data != a.data) for (LEN i = 0; i < a.length; ++i) data[i] = a.data[i]; return offset + a.length * sizeof(A); } }; } // namespace Detail template struct Clnt_val_ops, Dir_in, Cls_data> : Detail::Clnt_val_ops_d_in, false> {}; template struct Clnt_val_ops, Dir_in, Cls_data> : Detail::Clnt_val_ops_d_in, true> {}; template struct Svr_val_ops< Array_ref, Dir_in, CLASS > : Svr_noops< Array_ref > { typedef Array_ref svr_type; using Svr_noops::to_svr; static int to_svr(char *msg, unsigned offset, unsigned limit, svr_type &a, Dir_in, Cls_data) { offset = align_to(offset); if (L4_UNLIKELY(!check_size(offset, limit))) return -L4_EMSGTOOSHORT; a.length = *reinterpret_cast(msg + offset); offset = align_to(offset + sizeof(LEN)); if (L4_UNLIKELY(!check_size(offset, limit, a.length))) return -L4_EMSGTOOSHORT; a.data = reinterpret_cast(msg + offset); return offset + a.length * sizeof(A); } }; template struct Svr_xmit< Array > : Svr_xmit< Array_ref > {}; template struct Clnt_val_ops, Dir_out, Cls_data> : Clnt_noops > { typedef Array type; using Clnt_noops::from_msg; static int from_msg(char *msg, unsigned offset, unsigned limit, long, type &a, Dir_out, Cls_data) { offset = align_to(offset); if (L4_UNLIKELY(!check_size(offset, limit))) return -L4_EMSGTOOSHORT; LEN l = *reinterpret_cast(msg + offset); offset = align_to(offset + sizeof(LEN)); if (L4_UNLIKELY(!check_size(offset, limit, l))) return -L4_EMSGTOOSHORT; A *data = reinterpret_cast(msg + offset); if (l > a.length) l = a.length; else a.length = l; for (unsigned i = 0; i < l; ++i) a.data[i] = data[i]; return offset + l * sizeof(A); }; }; template struct Clnt_val_ops, Dir_out, Cls_data> : Clnt_noops > { typedef Array_ref type; using Clnt_noops::from_msg; static int from_msg(char *msg, unsigned offset, unsigned limit, long, type &a, Dir_out, Cls_data) { offset = align_to(offset); if (L4_UNLIKELY(!check_size(offset, limit))) return -L4_EMSGTOOSHORT; LEN l = *reinterpret_cast(msg + offset); offset = align_to(offset + sizeof(LEN)); if (L4_UNLIKELY(!check_size(offset, limit, l))) return -L4_EMSGTOOSHORT; a.data = reinterpret_cast(msg + offset); a.length = l; return offset + l * sizeof(A); }; }; template struct Svr_val_ops, Dir_out, CLASS> : Svr_noops::type, LEN> &> { typedef typename L4::Types::Remove_const::type elem_type; typedef Array_ref &svr_type; using Svr_noops::to_svr; static int to_svr(char *msg, unsigned offset, unsigned limit, svr_type a, Dir_out, Cls_data) { offset = align_to(offset); if (L4_UNLIKELY(!check_size(offset, limit))) return -L4_EMSGTOOLONG; offset = align_to(offset + sizeof(LEN)); a.data = reinterpret_cast(msg + offset); a.length = (limit-offset) / sizeof(A); return offset; } using Svr_noops::from_svr; static int from_svr(char *msg, unsigned offset, unsigned limit, long, svr_type a, Dir_out, Cls_data) { offset = align_to(offset); if (L4_UNLIKELY(!check_size(offset, limit))) return -L4_EMSGTOOLONG; *reinterpret_cast(msg + offset) = a.length; offset = align_to(offset + sizeof(LEN)); if (L4_UNLIKELY(!check_size(offset, limit, a.length))) return -L4_EMSGTOOLONG; return offset + a.length * sizeof(A); } }; template struct Svr_xmit &> : Svr_xmit &> {}; // Pointer to array is not implemented. template struct Is_valid_rpc_type< Array_ref *> : L4::Types::False {}; // Optional input arrays are not implemented. template struct Is_valid_rpc_type< Opt > > : L4::Types::False {}; } // namespace Msg }}