1293 lines
36 KiB
C++
1293 lines
36 KiB
C++
// vi:set ft=cpp: -*- Mode: C++ -*-
|
|
/**
|
|
* \file
|
|
* IPC stream
|
|
*/
|
|
/*
|
|
* (c) 2008-2009 Adam Lackorzynski <adam@os.inf.tu-dresden.de>,
|
|
* Alexander Warg <warg@os.inf.tu-dresden.de>,
|
|
* Torsten Frenzel <frenzel@os.inf.tu-dresden.de>
|
|
* economic rights: Technische Universität Dresden (Germany)
|
|
*
|
|
* License: see LICENSE.spdx (in this directory or the directories above)
|
|
*/
|
|
#pragma once
|
|
|
|
#include <l4/sys/ipc.h>
|
|
#include <l4/sys/capability>
|
|
#include <l4/sys/cxx/ipc_types>
|
|
#include <l4/sys/cxx/ipc_varg>
|
|
#include <l4/cxx/type_traits>
|
|
#include <l4/cxx/minmax>
|
|
|
|
namespace L4 {
|
|
namespace Ipc {
|
|
|
|
class Ostream;
|
|
class Istream;
|
|
|
|
namespace Internal {
|
|
/**
|
|
* Abstraction for inserting an array into an Ipc::Ostream.
|
|
* \internal
|
|
*
|
|
* An object of Buf_cp_out can be used to insert an array of arbitrary values,
|
|
* that can be inserted into an Ipc::Ostream individually.
|
|
* The array is therefore copied to the message buffer, in contrast to
|
|
* data handled with Msg_out_buffer or Msg_io_buffer.
|
|
*
|
|
* On insertion into the Ipc::Ostream exactly the given number of elements
|
|
* of type T are copied to the message buffer, this means the source buffer
|
|
* is no longer referenced after insertion into the stream.
|
|
*
|
|
* The method buf_cp_out() should be used to create instances of Buf_cp_out.
|
|
*
|
|
* The counterpart is either Buf_cp_in (buf_cp_in()) or Buf_in (buf_in()).
|
|
*/
|
|
template< typename T >
|
|
class Buf_cp_out
|
|
{
|
|
public:
|
|
/**
|
|
* Create a buffer object for the given array.
|
|
*
|
|
* \param v The pointer to the array with size elements of type T.
|
|
* \param size The number of elements in the array.
|
|
*/
|
|
Buf_cp_out(T const *v, unsigned long size) : _v(v), _s(size) {}
|
|
|
|
/**
|
|
* Get the number of elements in the array.
|
|
*
|
|
* \return The number of elements in the array.
|
|
*
|
|
* \note This function is usually used by the Ipc::Ostream itself.
|
|
*/
|
|
unsigned long size() const { return _s; }
|
|
|
|
/**
|
|
* Get the pointer to the array.
|
|
*
|
|
* \return Pointer to the array.
|
|
*
|
|
* \note This function is usually used by the Ipc::Ostream itself.
|
|
*/
|
|
T const *buf() const { return _v; }
|
|
|
|
private:
|
|
friend class Ostream;
|
|
T const *_v;
|
|
unsigned long _s;
|
|
};
|
|
}
|
|
|
|
/**
|
|
* Insert an array into an Ipc::Ostream.
|
|
*
|
|
* \param v Pointer to the array that shall be inserted into an
|
|
* Ipc::Ostream.
|
|
* \param size Number of elements in the array.
|
|
*
|
|
* This function inserts an array (e.g. a string) into an Ipc::Ostream.
|
|
* The data is copied to the stream. On insertion into the Ipc::Ostream
|
|
* exactly the given number of elements of type T are copied to the message
|
|
* buffer, this means the source buffer is no longer referenced after
|
|
* insertion into the stream.
|
|
*
|
|
* \see The counterpart is either buf_cp_in() or buf_in().
|
|
*/
|
|
template< typename T >
|
|
Internal::Buf_cp_out<T> buf_cp_out(T const *v, unsigned long size)
|
|
{ return Internal::Buf_cp_out<T>(v, size); }
|
|
|
|
|
|
namespace Internal {
|
|
/**
|
|
* Abstraction for extracting array from an Ipc::Istream.
|
|
* \internal
|
|
*
|
|
* An instance of Buf_cp_in can be used to extract an array from
|
|
* an Ipc::Istream. This is the counterpart to the Buf_cp_out abstraction.
|
|
* The data from the received message is thereby copied to the given buffer
|
|
* and size is set to the number of elements found in the stream.
|
|
* To avoid the copy operation Buf_in may be used instead.
|
|
*
|
|
* \see buf_cp_in(), Buf_in, buf_in(), Buf_cp_out, and buf_cp_out().
|
|
*/
|
|
template< typename T >
|
|
class Buf_cp_in
|
|
{
|
|
public:
|
|
/**
|
|
* Create a buffer for extracting an array from an Ipc::Istream.
|
|
*
|
|
* \param v The buffer for array (copy in).
|
|
* \param[in,out] size Input: the number of elements the array can take at
|
|
* most <br>
|
|
* Output: the number of elements found in the stream.
|
|
*/
|
|
Buf_cp_in(T *v, unsigned long &size) : _v(v), _s(&size) {}
|
|
|
|
unsigned long &size() const { return *_s; }
|
|
T *buf() const { return _v; }
|
|
|
|
private:
|
|
friend class Istream;
|
|
T *_v;
|
|
unsigned long *_s;
|
|
};
|
|
}
|
|
|
|
/**
|
|
* Extract an array from an Ipc::Istream.
|
|
*
|
|
* \param v Pointer to the array that shall receive the values from
|
|
* the Ipc::Istream.
|
|
* \param[in,out] size Input: the number of elements the array can take at
|
|
* most <br>
|
|
* Output: the number of elements found in the stream.
|
|
*
|
|
* buf_cp_in() can be used to extract an array from an Ipc::Istream. This is
|
|
* the counterpart buf_cp_out(). The data from the received message is
|
|
* thereby copied to the given buffer and size is set to the number of
|
|
* elements found in the stream. To avoid the copy operation buf_in() may be
|
|
* used instead.
|
|
*
|
|
* \see buf_in() and buf_cp_out().
|
|
*/
|
|
template< typename T >
|
|
Internal::Buf_cp_in<T> buf_cp_in(T *v, unsigned long &size)
|
|
{ return Internal::Buf_cp_in<T>(v, size); }
|
|
|
|
/**
|
|
* Abstraction for extracting a zero-terminated string from an Ipc::Istream.
|
|
*
|
|
* An instance of Str_cp_in can be used to extract a zero-terminated string
|
|
* an Ipc::Istream. The data from the received message is thereby copied to the
|
|
* given buffer and size is set to the number of characters found in the
|
|
* stream. The string is zero terminated in any circumstances. When the given
|
|
* buffer is smaller than the received string the last byte in the buffer will
|
|
* be the zero terminator. In the case the received string is shorter than the
|
|
* given buffer the zero termination will be placed behind the received data.
|
|
* This provides a zero-terminated result even in cases where the sender did
|
|
* not provide proper termination or in cases of too small receiver buffers.
|
|
*
|
|
* \see str_cp_in().
|
|
*/
|
|
template< typename T >
|
|
class Str_cp_in
|
|
{
|
|
public:
|
|
/**
|
|
* Create a buffer for extracting an array from an Ipc::Istream.
|
|
*
|
|
* \param v The buffer for string.
|
|
* \param[in,out] size Input: The number of bytes available in `v` <br>
|
|
* Output: The number of bytes received (including the
|
|
* terminator).
|
|
*/
|
|
Str_cp_in(T *v, unsigned long &size) : _v(v), _s(&size) {}
|
|
|
|
unsigned long &size() const { return *_s; }
|
|
T *buf() const { return _v; }
|
|
|
|
private:
|
|
friend class Istream;
|
|
T *_v;
|
|
unsigned long *_s;
|
|
};
|
|
|
|
/**
|
|
* Create a Str_cp_in for the given values.
|
|
*
|
|
* \param v Pointer to the array that shall receive the values from
|
|
* the Ipc::Istream.
|
|
* \param[in,out] size Input: the number of elements the array can take at
|
|
* most <br>
|
|
* Output: the number of elements found in the stream.
|
|
*
|
|
* This function makes it more convenient to extract arrays from an
|
|
* Ipc::Istream (\see Str_cp_in.)
|
|
*/
|
|
template< typename T >
|
|
Str_cp_in<T> str_cp_in(T *v, unsigned long &size)
|
|
{ return Str_cp_in<T>(v, size); }
|
|
|
|
/**
|
|
* Pointer to an element of type T in an Ipc::Istream.
|
|
*
|
|
* This wrapper can be used to extract an element of type T from an
|
|
* Ipc::Istream, whereas the data is not copied out, but a pointer into
|
|
* the message buffer itself is returned. With is mechanism it is possible
|
|
* to avoid an extra copy of large data structures from a received IPC
|
|
* message, instead the returned pointer gives direct access to the data
|
|
* in the message.
|
|
*
|
|
* See msg_ptr().
|
|
*/
|
|
template< typename T >
|
|
class Msg_ptr
|
|
{
|
|
private:
|
|
T **_p;
|
|
public:
|
|
/**
|
|
* Create a Msg_ptr object that set pointer p to point into the message
|
|
* buffer.
|
|
*
|
|
* \param p The pointer that is adjusted to point into the message buffer.
|
|
*/
|
|
explicit Msg_ptr(T *&p) : _p(&p) {}
|
|
void set(T *p) const { *_p = p; }
|
|
};
|
|
|
|
/**
|
|
* Create an Msg_ptr to adjust the given pointer.
|
|
*
|
|
* This function makes it more convenient to extract pointers to data in the
|
|
* message buffer itself from an Ipc::Istream. This may be used to avoid copy
|
|
* out of large data structures. (See Msg_ptr.)
|
|
*/
|
|
template< typename T >
|
|
Msg_ptr<T> msg_ptr(T *&p)
|
|
{ return Msg_ptr<T>(p); }
|
|
|
|
|
|
namespace Internal {
|
|
/**
|
|
* Abstraction to extract an array from an Ipc::Istream.
|
|
* \internal
|
|
*
|
|
* This wrapper provides a possibility to extract an array from an
|
|
* Ipc::Istream, without extra copy overhead. In contrast to Buf_cp_in
|
|
* the data is not copied to a buffer, but a pointer to the array is returned.
|
|
*
|
|
* The mechanism is comparable to that of Msg_ptr, however it handles arrays
|
|
* inserted with Buf_cp_out.
|
|
*
|
|
* See buf_in(), Buf_cp_out, buf_cp_out(), Buf_cp_in, and buf_cp_in().
|
|
*/
|
|
template< typename T >
|
|
class Buf_in
|
|
{
|
|
public:
|
|
/**
|
|
* Create a Buf_in to adjust a pointer to the array and the size of the array.
|
|
*
|
|
* \param v The pointer to adjust to the first element of the array.
|
|
* \param[out] size The number of elements found in the stream.
|
|
*/
|
|
Buf_in(T *&v, unsigned long &size) : _v(&v), _s(&size) {}
|
|
|
|
void set_size(unsigned long s) const { *_s = s; }
|
|
T *&buf() const { return *_v; }
|
|
|
|
private:
|
|
friend class Istream;
|
|
T **_v;
|
|
unsigned long *_s;
|
|
};
|
|
}
|
|
|
|
/**
|
|
* Return a pointer to stream array data.
|
|
*
|
|
* \param[out] v Pointer to the array within the Ipc::Istream.
|
|
* \param[out] size The number of elements found in the stream.
|
|
*
|
|
* This routine provides a possibility to extract an array from an
|
|
* Ipc::Istream, without extra copy overhead. In contrast to buf_cp_in()
|
|
* the data is not copied to a buffer, but a pointer to the array is returned.
|
|
* The user must make sure the UTCB is not used for other purposes while the
|
|
* returned pointer is still in use.
|
|
*
|
|
* The mechanism is comparable to that of Msg_ptr, however it handles arrays
|
|
* inserted with buf_cp_out().
|
|
*
|
|
* \see buf_cp_in() and buf_cp_out().
|
|
*/
|
|
template< typename T >
|
|
Internal::Buf_in<T> buf_in(T *&v, unsigned long &size)
|
|
{ return Internal::Buf_in<T>(v, size); }
|
|
|
|
namespace Utcb_stream_check
|
|
{
|
|
static bool check_utcb_data_offset(unsigned sz)
|
|
{ return sz > sizeof(l4_umword_t) * L4_UTCB_GENERIC_DATA_SIZE; }
|
|
}
|
|
|
|
|
|
/**
|
|
* Input stream for IPC unmarshalling.
|
|
*
|
|
* Ipc::Istream is part of the dynamic IPC marshalling infrastructure, as well
|
|
* as Ipc::Ostream and Ipc::Iostream.
|
|
*
|
|
* Ipc::Istream is an input stream supporting extraction of values from an
|
|
* IPC message buffer. A received IPC message can be unmarshalled using the
|
|
* usual extraction operator (>>).
|
|
*
|
|
* There exist some special wrapper classes to extract arrays (see
|
|
* Ipc_buf_cp_in and Ipc_buf_in) and indirect strings (see Msg_in_buffer and
|
|
* Msg_io_buffer).
|
|
*/
|
|
class Istream
|
|
{
|
|
public:
|
|
/**
|
|
* Create an input stream for the given message buffer.
|
|
*
|
|
* The given message buffer is used for IPC operations wait()/receive()
|
|
* and received data can be extracted using the >> operator afterwards.
|
|
* In the case of indirect message parts a buffer of type Msg_in_buffer
|
|
* must be inserted into the stream before the IPC operation and contains
|
|
* received data afterwards.
|
|
*
|
|
* \param utcb The message buffer to receive IPC messages.
|
|
*/
|
|
Istream(l4_utcb_t *utcb)
|
|
: _tag(), _utcb(utcb),
|
|
_current_msg(reinterpret_cast<char*>(l4_utcb_mr_u(utcb)->mr)),
|
|
_pos(0), _current_buf(0)
|
|
{}
|
|
|
|
/**
|
|
* Reset the stream to empty, and ready for receive()/wait().
|
|
* The stream is reset to the same state as on its creation.
|
|
*/
|
|
void reset()
|
|
{
|
|
_pos = 0;
|
|
_current_buf = 0;
|
|
_current_msg = reinterpret_cast<char*>(l4_utcb_mr_u(_utcb)->mr);
|
|
}
|
|
|
|
/**
|
|
* Check whether a value of type T can be obtained from the stream.
|
|
*/
|
|
template< typename T >
|
|
bool has_more(unsigned long count = 1)
|
|
{
|
|
auto const max_bytes = L4_UTCB_GENERIC_DATA_SIZE * sizeof(l4_umword_t);
|
|
unsigned apos = cxx::Type_traits<T>::align(_pos);
|
|
return (count <= max_bytes / sizeof(T))
|
|
&& (apos + (sizeof(T) * count)
|
|
<= _tag.words() * sizeof(l4_umword_t));
|
|
}
|
|
|
|
/**
|
|
* \name Get/Put Functions.
|
|
*/
|
|
///@{
|
|
|
|
/**
|
|
* Copy out an array of type `T` with `size` elements.
|
|
*
|
|
* \param buf Pointer to a buffer for size elements of type T.
|
|
* \param elems Number of elements of type T to copy out.
|
|
*
|
|
* \return The number of elements copied out.
|
|
*
|
|
* See \ref operator>>()
|
|
*/
|
|
template< typename T >
|
|
unsigned long get(T *buf, unsigned long elems)
|
|
{
|
|
if (L4_UNLIKELY(!has_more<T>(elems)))
|
|
return 0;
|
|
|
|
unsigned long size = elems * sizeof(T);
|
|
_pos = cxx::Type_traits<T>::align(_pos);
|
|
|
|
__builtin_memcpy(buf, _current_msg + _pos, size);
|
|
_pos += size;
|
|
return elems;
|
|
}
|
|
|
|
|
|
/**
|
|
* Skip size elements of type T in the stream.
|
|
*
|
|
* \param elems Number of elements to skip.
|
|
*/
|
|
template< typename T >
|
|
void skip(unsigned long elems)
|
|
{
|
|
if (L4_UNLIKELY(!has_more<T>(elems)))
|
|
return;
|
|
|
|
unsigned long size = elems * sizeof(T);
|
|
_pos = cxx::Type_traits<T>::align(_pos);
|
|
_pos += size;
|
|
}
|
|
|
|
/**
|
|
* Read one size elements of type T from the stream and return a pointer.
|
|
*
|
|
* \param buf A Msg_ptr that is actually set to point to the element in the
|
|
* stream.
|
|
* \param elems Number of elements to extract (default is 1).
|
|
*
|
|
* \return The number of elements extracted.
|
|
*
|
|
* In contrast to a normal get, this version does actually not copy the data
|
|
* but returns a pointer to the data.
|
|
*
|
|
* See \ref operator>>()
|
|
*/
|
|
template< typename T >
|
|
unsigned long get(Msg_ptr<T> const &buf, unsigned long elems = 1)
|
|
{
|
|
if (L4_UNLIKELY(!has_more<T>(elems)))
|
|
return 0;
|
|
|
|
unsigned long size = elems * sizeof(T);
|
|
_pos = cxx::Type_traits<T>::align(_pos);
|
|
|
|
buf.set(reinterpret_cast<T*>(_current_msg + _pos));
|
|
_pos += size;
|
|
return elems;
|
|
}
|
|
|
|
|
|
/**
|
|
* Extract a single element of type T from the stream.
|
|
*
|
|
* \param[out] v The element.
|
|
*
|
|
* \retval true An element was successfully extracted.
|
|
* \retval false An element could not be extracted.
|
|
*
|
|
* See \ref operator>>()
|
|
*/
|
|
template< typename T >
|
|
bool get(T &v)
|
|
{
|
|
if (L4_UNLIKELY(!has_more<T>()))
|
|
{
|
|
v = T();
|
|
return false;
|
|
}
|
|
|
|
_pos = cxx::Type_traits<T>::align(_pos);
|
|
v = *(reinterpret_cast<T*>(_current_msg + _pos));
|
|
_pos += sizeof(T);
|
|
return true;
|
|
}
|
|
|
|
|
|
bool get(Ipc::Varg *va)
|
|
{
|
|
Ipc::Varg::Tag t;
|
|
if (!has_more<Ipc::Varg::Tag>())
|
|
{
|
|
va->tag(0);
|
|
return 0;
|
|
}
|
|
get(t);
|
|
va->tag(t);
|
|
char const *d;
|
|
get(msg_ptr(d), va->length());
|
|
va->data(d);
|
|
|
|
return 1;
|
|
}
|
|
|
|
/**
|
|
* Get the message tag of a received IPC.
|
|
*
|
|
* \return The L4 message tag for the received IPC.
|
|
*
|
|
* This is in particular useful for handling page faults or exceptions.
|
|
*
|
|
* See \ref operator>>()
|
|
*/
|
|
l4_msgtag_t tag() const { return _tag; }
|
|
|
|
|
|
/**
|
|
* Get the message tag of a received IPC.
|
|
*
|
|
* \return A reference to the L4 message tag for the received IPC.
|
|
*
|
|
* This is in particular useful for handling page faults or exceptions.
|
|
*
|
|
* See \ref operator>>()
|
|
*/
|
|
l4_msgtag_t &tag() { return _tag; }
|
|
|
|
///@}
|
|
|
|
/**
|
|
* \internal
|
|
* Put a receive item into the stream's buffer registers.
|
|
*/
|
|
inline bool put(Rcv_fpage const &);
|
|
|
|
/**
|
|
* \internal
|
|
* Put a small receive item into the stream's buffer registers.
|
|
*/
|
|
inline bool put(Small_buf const &);
|
|
|
|
|
|
/**
|
|
* \name IPC operations.
|
|
*/
|
|
///@{
|
|
|
|
/**
|
|
* Wait for an incoming message from any sender.
|
|
*
|
|
* \param[out] src Contains the sender after a successful IPC operation.
|
|
*
|
|
* \return Syscall return tag.
|
|
*
|
|
* This wait is actually known as 'open wait'.
|
|
*/
|
|
inline l4_msgtag_t wait(l4_umword_t *src)
|
|
{ return wait(src, L4_IPC_NEVER); }
|
|
|
|
/**
|
|
* Wait for an incoming message from any sender.
|
|
*
|
|
* \param[out] src Contains the sender after a successful IPC operation.
|
|
* \param timeout Timeout used for IPC.
|
|
*
|
|
* \return The IPC result tag (l4_msgtag_t).
|
|
*
|
|
* This wait is actually known as 'open wait'.
|
|
*/
|
|
inline l4_msgtag_t wait(l4_umword_t *src, l4_timeout_t timeout);
|
|
|
|
/**
|
|
* Wait for a message from the specified sender.
|
|
*
|
|
* \param src The sender id to receive from.
|
|
*
|
|
* \return The IPC result tag (l4_msgtag_t).
|
|
*
|
|
* This is commonly known as 'closed wait'.
|
|
*/
|
|
inline l4_msgtag_t receive(l4_cap_idx_t src)
|
|
{ return receive(src, L4_IPC_NEVER); }
|
|
inline l4_msgtag_t receive(l4_cap_idx_t src, l4_timeout_t timeout);
|
|
|
|
///@}
|
|
|
|
/**
|
|
* Return utcb pointer.
|
|
*/
|
|
inline l4_utcb_t *utcb() const { return _utcb; }
|
|
|
|
protected:
|
|
l4_msgtag_t _tag;
|
|
l4_utcb_t *_utcb;
|
|
char *_current_msg;
|
|
unsigned _pos;
|
|
unsigned char _current_buf;
|
|
};
|
|
|
|
class Istream_copy : public Istream
|
|
{
|
|
private:
|
|
l4_msg_regs_t _mrs;
|
|
|
|
public:
|
|
Istream_copy(Istream const &o) : Istream(o), _mrs(*l4_utcb_mr_u(o.utcb()))
|
|
{
|
|
// do some reverse mr to utcb trickery
|
|
_utcb = reinterpret_cast<l4_utcb_t *>
|
|
(reinterpret_cast<l4_addr_t>(&_mrs)
|
|
- reinterpret_cast<l4_addr_t>(l4_utcb_mr_u(nullptr)));
|
|
_current_msg = reinterpret_cast<char*>(l4_utcb_mr_u(_utcb)->mr);
|
|
}
|
|
|
|
};
|
|
|
|
/**
|
|
* Output stream for IPC marshalling.
|
|
*
|
|
* Ipc::Ostream is part of the dynamic IPC marshalling infrastructure, as well
|
|
* as Ipc::Istream and Ipc::Iostream.
|
|
*
|
|
* Ipc::Ostream is an output stream supporting insertion of values into an
|
|
* IPC message buffer. A IPC message can be marshalled using the
|
|
* usual insertion operator <<, see \link ipc_stream IPC stream operators
|
|
* \endlink.
|
|
*
|
|
* There exist some special wrapper classes to insert arrays (see
|
|
* Ipc::Buf_cp_out) and indirect strings (see Msg_out_buffer and
|
|
* Msg_io_buffer).
|
|
*/
|
|
class Ostream
|
|
{
|
|
public:
|
|
/**
|
|
* Create an IPC output stream using the given message buffer `utcb`.
|
|
*/
|
|
Ostream(l4_utcb_t *utcb)
|
|
: _tag(), _utcb(utcb),
|
|
_current_msg(reinterpret_cast<char *>(l4_utcb_mr_u(_utcb)->mr)),
|
|
_pos(0), _current_item(0)
|
|
{}
|
|
|
|
/**
|
|
* Reset the stream to empty, same state as a newly created stream.
|
|
*/
|
|
void reset()
|
|
{
|
|
_pos = 0;
|
|
_current_item = 0;
|
|
_current_msg = reinterpret_cast<char*>(l4_utcb_mr_u(_utcb)->mr);
|
|
}
|
|
|
|
/**
|
|
* \name Get/Put functions.
|
|
*
|
|
* These functions are basically used to implement the insertion operators
|
|
* (<<) and should not be called directly.
|
|
*/
|
|
///@{
|
|
|
|
/**
|
|
* Put an array with `size` elements of type `T` into the stream.
|
|
*
|
|
* \param buf A pointer to the array to insert into the buffer.
|
|
* \param size The number of elements in the array.
|
|
*/
|
|
template< typename T >
|
|
bool put(T *buf, unsigned long size)
|
|
{
|
|
size *= sizeof(T);
|
|
_pos = cxx::Type_traits<T>::align(_pos);
|
|
if (Utcb_stream_check::check_utcb_data_offset(_pos + size))
|
|
return false;
|
|
|
|
__builtin_memcpy(_current_msg + _pos, buf, size);
|
|
_pos += size;
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* Insert an element of type `T` into the stream.
|
|
*
|
|
* \param v The element to insert.
|
|
*/
|
|
template< typename T >
|
|
bool put(T const &v)
|
|
{
|
|
_pos = cxx::Type_traits<T>::align(_pos);
|
|
if (Utcb_stream_check::check_utcb_data_offset(_pos + sizeof(T)))
|
|
return false;
|
|
|
|
*(reinterpret_cast<T*>(_current_msg + _pos)) = v;
|
|
_pos += sizeof(T);
|
|
return true;
|
|
}
|
|
|
|
int put(Varg const &va)
|
|
{
|
|
put(va.tag());
|
|
put(va.data(), va.length());
|
|
|
|
return 0;
|
|
}
|
|
|
|
template< typename T >
|
|
int put(Varg_t<T> const &va)
|
|
{ return put(static_cast<Varg const &>(va)); }
|
|
|
|
/**
|
|
* Extract the L4 message tag from the stream.
|
|
*
|
|
* \return The extracted L4 message tag.
|
|
*/
|
|
l4_msgtag_t tag() const { return _tag; }
|
|
|
|
/**
|
|
* Extract a reference to the L4 message tag from the stream.
|
|
*
|
|
* \return A reference to the L4 message tag.
|
|
*/
|
|
l4_msgtag_t &tag() { return _tag; }
|
|
|
|
///@}
|
|
|
|
/**
|
|
* \internal
|
|
* Put a send item into the stream's message buffer.
|
|
*/
|
|
inline bool put_snd_item(Snd_fpage const &);
|
|
|
|
|
|
/**
|
|
* \name IPC operations.
|
|
*/
|
|
///@{
|
|
|
|
/**
|
|
* Send the message via IPC to the given receiver.
|
|
*
|
|
* \param dst The destination for the message.
|
|
* \param proto Protocol to use.
|
|
* \param flags Flags to use.
|
|
*
|
|
* \return The syscall return tag.
|
|
*/
|
|
inline l4_msgtag_t send(l4_cap_idx_t dst, long proto = 0, unsigned flags = 0);
|
|
|
|
///@}
|
|
|
|
/**
|
|
* Return utcb pointer.
|
|
*/
|
|
inline l4_utcb_t *utcb() const { return _utcb; }
|
|
#if 0
|
|
/**
|
|
* Get the currently used bytes in the stream.
|
|
*/
|
|
unsigned long tell() const
|
|
{
|
|
unsigned w = l4_bytes_to_mwords(_pos) - _current_item * 2;
|
|
_tag = l4_msgtag(0, w, _current_item, 0);
|
|
}
|
|
#endif
|
|
public:
|
|
l4_msgtag_t prepare_ipc(long proto = 0, unsigned flags = 0)
|
|
{
|
|
unsigned w = l4_bytes_to_mwords(_pos) - _current_item * 2;
|
|
return l4_msgtag(proto, w, _current_item, flags);
|
|
}
|
|
|
|
// XXX: this is a hack for <l4/sys/cxx/ipc_server> adaption
|
|
void set_ipc_params(l4_msgtag_t tag)
|
|
{
|
|
_pos = (tag.words() + tag.items() * 2) * sizeof(l4_umword_t);
|
|
_current_item = tag.items();
|
|
}
|
|
protected:
|
|
l4_msgtag_t _tag;
|
|
l4_utcb_t *_utcb;
|
|
char *_current_msg;
|
|
unsigned _pos;
|
|
unsigned char _current_item;
|
|
};
|
|
|
|
|
|
/**
|
|
* Input/Output stream for IPC [un]marshalling.
|
|
*
|
|
* The Ipc::Iostream is part of the AW Env IPC framework as well as
|
|
* Ipc::Istream and Ipc::Ostream.
|
|
* In particular an Ipc::Iostream is a combination of an Ipc::Istream and an
|
|
* Ipc::Ostream. It can use either a single message buffer for receiving and
|
|
* sending messages or a pair of a receive and a send buffer. The stream also
|
|
* supports combined IPC operations such as call() and reply_and_wait(), which
|
|
* can be used to implement RPC functionality.
|
|
*/
|
|
class Iostream : public Istream, public Ostream
|
|
{
|
|
public:
|
|
|
|
/**
|
|
* Create an IPC IO stream with a single message buffer.
|
|
*
|
|
* \param utcb The message buffer used as backing store.
|
|
*
|
|
* The created IO stream uses the same message buffer for sending and
|
|
* receiving IPC messages.
|
|
*/
|
|
explicit Iostream(l4_utcb_t *utcb)
|
|
: Istream(utcb), Ostream(utcb)
|
|
{}
|
|
|
|
// disambiguate those functions
|
|
l4_msgtag_t tag() const { return Istream::tag(); }
|
|
l4_msgtag_t &tag() { return Istream::tag(); }
|
|
l4_utcb_t *utcb() const { return Istream::utcb(); }
|
|
|
|
/**
|
|
* Reset the stream to its initial state.
|
|
*
|
|
* Input as well as the output stream are reset.
|
|
*/
|
|
void reset()
|
|
{
|
|
Istream::reset();
|
|
Ostream::reset();
|
|
}
|
|
|
|
|
|
/**
|
|
* \name Get/Put functions.
|
|
*
|
|
* These functions are basically used to implement the insertion operators
|
|
* (<<) and should not be called directly.
|
|
*/
|
|
///@{
|
|
|
|
using Istream::get;
|
|
using Istream::put;
|
|
using Ostream::put;
|
|
|
|
///@}
|
|
|
|
/**
|
|
* \name IPC operations.
|
|
*/
|
|
///@{
|
|
|
|
/**
|
|
* Do an IPC call using the message in the output stream and receive the
|
|
* reply in the input stream.
|
|
*
|
|
* \param dst The destination to call.
|
|
* \param timeout The IPC timeout for the call.
|
|
* \param proto The protocol value to use in the message tag.
|
|
*
|
|
* \return The result tag of the IPC operation.
|
|
*
|
|
* This is a combined IPC operation consisting of a send and a receive
|
|
* to/from the given destination `dst`.
|
|
*
|
|
* A call is usually used by clients for RPCs to a server.
|
|
*/
|
|
inline l4_msgtag_t call(l4_cap_idx_t dst, l4_timeout_t timeout, long proto = 0);
|
|
inline l4_msgtag_t call(l4_cap_idx_t dst, long proto = 0);
|
|
|
|
/**
|
|
* Do an IPC reply and wait.
|
|
*
|
|
* \param[in,out] src_dst Input: the destination for the send operation. <br>
|
|
* Output: the source of the received message.
|
|
* \param proto Protocol to use.
|
|
*
|
|
* \return The result tag of the IPC operation.
|
|
*
|
|
* This is a combined IPC operation consisting of a send operation and
|
|
* an open wait for any message.
|
|
*
|
|
* A reply and wait is usually used by servers that reply to a client
|
|
* and wait for the next request by any other client.
|
|
*/
|
|
inline l4_msgtag_t reply_and_wait(l4_umword_t *src_dst, long proto = 0)
|
|
{ return reply_and_wait(src_dst, L4_IPC_SEND_TIMEOUT_0, proto); }
|
|
|
|
inline l4_msgtag_t send_and_wait(l4_cap_idx_t dest, l4_umword_t *src,
|
|
long proto = 0)
|
|
{ return send_and_wait(dest, src, L4_IPC_SEND_TIMEOUT_0, proto); }
|
|
|
|
/**
|
|
* Do an IPC reply and wait.
|
|
*
|
|
* \param[in,out] src_dst Input: the destination for the send operation. <br>
|
|
* Output: the source of the received message.
|
|
* \param timeout Timeout used for IPC.
|
|
* \param proto Protocol to use.
|
|
*
|
|
* \return The result tag of the IPC operation.
|
|
*
|
|
* This is a combined IPC operation consisting of a send operation and
|
|
* an open wait for any message.
|
|
*
|
|
* A reply and wait is usually used by servers that reply to a client
|
|
* and wait for the next request by any other client.
|
|
*/
|
|
inline l4_msgtag_t reply_and_wait(l4_umword_t *src_dst,
|
|
l4_timeout_t timeout, long proto = 0);
|
|
inline l4_msgtag_t send_and_wait(l4_cap_idx_t dest, l4_umword_t *src,
|
|
l4_timeout_t timeout, long proto = 0);
|
|
inline l4_msgtag_t reply(l4_timeout_t timeout, long proto = 0);
|
|
inline l4_msgtag_t reply(long proto = 0)
|
|
{ return reply(L4_IPC_SEND_TIMEOUT_0, proto); }
|
|
|
|
///@}
|
|
};
|
|
|
|
|
|
inline bool
|
|
Ostream::put_snd_item(Snd_fpage const &v)
|
|
{
|
|
typedef Snd_fpage T;
|
|
_pos = cxx::Type_traits<Snd_fpage>::align(_pos);
|
|
if (Utcb_stream_check::check_utcb_data_offset(_pos + sizeof(T)))
|
|
return false;
|
|
|
|
*(reinterpret_cast<T*>(_current_msg + _pos)) = v;
|
|
_pos += sizeof(T);
|
|
++_current_item;
|
|
return true;
|
|
}
|
|
|
|
|
|
inline bool
|
|
Istream::put(Rcv_fpage const &item)
|
|
{
|
|
unsigned words = item.forward_mappings() ? 3 : 2;
|
|
if (_current_buf >= L4_UTCB_GENERIC_BUFFERS_SIZE - words - 1)
|
|
return false;
|
|
|
|
l4_utcb_br_u(_utcb)->bdr &= ~L4_BDR_OFFSET_MASK;
|
|
|
|
l4_umword_t *buf
|
|
= reinterpret_cast<l4_umword_t *>(&l4_utcb_br_u(_utcb)->br[_current_buf]);
|
|
*buf++ = item.base_x();
|
|
*buf++ = item.data();
|
|
if (item.forward_mappings())
|
|
*buf++ = item.rcv_task();
|
|
_current_buf += words;
|
|
return true;
|
|
}
|
|
|
|
|
|
inline bool
|
|
Istream::put(Small_buf const &item)
|
|
{
|
|
if (_current_buf >= L4_UTCB_GENERIC_BUFFERS_SIZE - 2)
|
|
return false;
|
|
|
|
l4_utcb_br_u(_utcb)->bdr &= ~L4_BDR_OFFSET_MASK;
|
|
|
|
reinterpret_cast<Small_buf&>(l4_utcb_br_u(_utcb)->br[_current_buf]) = item;
|
|
_current_buf += 1;
|
|
return true;
|
|
}
|
|
|
|
|
|
inline l4_msgtag_t
|
|
Ostream::send(l4_cap_idx_t dst, long proto, unsigned flags)
|
|
{
|
|
l4_msgtag_t tag = prepare_ipc(proto, L4_MSGTAG_FLAGS & flags);
|
|
return l4_ipc_send(dst, _utcb, tag, L4_IPC_NEVER);
|
|
}
|
|
|
|
inline l4_msgtag_t
|
|
Iostream::call(l4_cap_idx_t dst, l4_timeout_t timeout, long label)
|
|
{
|
|
l4_msgtag_t tag = prepare_ipc(label);
|
|
tag = l4_ipc_call(dst, Ostream::_utcb, tag, timeout);
|
|
Istream::tag() = tag;
|
|
Istream::_pos = 0;
|
|
return tag;
|
|
}
|
|
|
|
inline l4_msgtag_t
|
|
Iostream::call(l4_cap_idx_t dst, long label)
|
|
{ return call(dst, L4_IPC_NEVER, label); }
|
|
|
|
|
|
inline l4_msgtag_t
|
|
Iostream::reply_and_wait(l4_umword_t *src_dst, l4_timeout_t timeout, long proto)
|
|
{
|
|
l4_msgtag_t tag = prepare_ipc(proto);
|
|
tag = l4_ipc_reply_and_wait(Ostream::_utcb, tag, src_dst, timeout);
|
|
Istream::tag() = tag;
|
|
Istream::_pos = 0;
|
|
return tag;
|
|
}
|
|
|
|
|
|
inline l4_msgtag_t
|
|
Iostream::send_and_wait(l4_cap_idx_t dest, l4_umword_t *src,
|
|
l4_timeout_t timeout, long proto)
|
|
{
|
|
l4_msgtag_t tag = prepare_ipc(proto);
|
|
tag = l4_ipc_send_and_wait(dest, Ostream::_utcb, tag, src, timeout);
|
|
Istream::tag() = tag;
|
|
Istream::_pos = 0;
|
|
return tag;
|
|
}
|
|
|
|
inline l4_msgtag_t
|
|
Iostream::reply(l4_timeout_t timeout, long proto)
|
|
{
|
|
l4_msgtag_t tag = prepare_ipc(proto);
|
|
tag = l4_ipc_send(L4_INVALID_CAP | L4_SYSF_REPLY, Ostream::_utcb, tag, timeout);
|
|
Istream::tag() = tag;
|
|
Istream::_pos = 0;
|
|
return tag;
|
|
}
|
|
|
|
inline l4_msgtag_t
|
|
Istream::wait(l4_umword_t *src, l4_timeout_t timeout)
|
|
{
|
|
l4_msgtag_t res;
|
|
res = l4_ipc_wait(_utcb, src, timeout);
|
|
tag() = res;
|
|
_pos = 0;
|
|
return res;
|
|
}
|
|
|
|
|
|
inline l4_msgtag_t
|
|
Istream::receive(l4_cap_idx_t src, l4_timeout_t timeout)
|
|
{
|
|
l4_msgtag_t res;
|
|
res = l4_ipc_receive(src, _utcb, timeout);
|
|
tag() = res;
|
|
_pos = 0;
|
|
return res;
|
|
}
|
|
|
|
} // namespace Ipc
|
|
} // namespace L4
|
|
|
|
/**
|
|
* Extract one element of type `T` from the stream `s`.
|
|
*
|
|
* \param s The stream to extract from.
|
|
* \param[out] v Extracted value.
|
|
*
|
|
* \return The stream `s`.
|
|
*/
|
|
inline L4::Ipc::Istream &operator >> (L4::Ipc::Istream &s, bool &v) { s.get(v); return s; }
|
|
inline L4::Ipc::Istream &operator >> (L4::Ipc::Istream &s, int &v) { s.get(v); return s; }
|
|
inline L4::Ipc::Istream &operator >> (L4::Ipc::Istream &s, long int &v) { s.get(v); return s; }
|
|
inline L4::Ipc::Istream &operator >> (L4::Ipc::Istream &s, long long int &v) { s.get(v); return s; }
|
|
inline L4::Ipc::Istream &operator >> (L4::Ipc::Istream &s, unsigned int &v) { s.get(v); return s; }
|
|
inline L4::Ipc::Istream &operator >> (L4::Ipc::Istream &s, unsigned long int &v) { s.get(v); return s; }
|
|
inline L4::Ipc::Istream &operator >> (L4::Ipc::Istream &s, unsigned long long int &v) { s.get(v); return s; }
|
|
inline L4::Ipc::Istream &operator >> (L4::Ipc::Istream &s, short int &v) { s.get(v); return s; }
|
|
inline L4::Ipc::Istream &operator >> (L4::Ipc::Istream &s, unsigned short int &v) { s.get(v); return s; }
|
|
inline L4::Ipc::Istream &operator >> (L4::Ipc::Istream &s, char &v) { s.get(v); return s; }
|
|
inline L4::Ipc::Istream &operator >> (L4::Ipc::Istream &s, unsigned char &v) { s.get(v); return s; }
|
|
inline L4::Ipc::Istream &operator >> (L4::Ipc::Istream &s, signed char &v) { s.get(v); return s; }
|
|
inline L4::Ipc::Istream &operator << (L4::Ipc::Istream &s, L4::Ipc::Rcv_fpage const &v) { s.put(v); return s; }
|
|
inline L4::Ipc::Istream &operator << (L4::Ipc::Istream &s, L4::Ipc::Small_buf const &v) { s.put(v); return s; }
|
|
inline L4::Ipc::Istream &operator >> (L4::Ipc::Istream &s, L4::Ipc::Snd_fpage &v)
|
|
{
|
|
l4_umword_t b, d;
|
|
s >> b >> d;
|
|
v = L4::Ipc::Snd_fpage(b, d);
|
|
return s;
|
|
}
|
|
inline L4::Ipc::Istream &operator >> (L4::Ipc::Istream &s, L4::Ipc::Varg &v)
|
|
{ s.get(&v); return s; }
|
|
|
|
|
|
/**
|
|
* Extract the L4 message tag from the stream `s`.
|
|
*
|
|
* \param s The stream to extract from.
|
|
* \param[out] v The extracted tag.
|
|
*
|
|
* \return The stream `s`.
|
|
*/
|
|
inline
|
|
L4::Ipc::Istream &operator >> (L4::Ipc::Istream &s, l4_msgtag_t &v)
|
|
{
|
|
v = s.tag();
|
|
return s;
|
|
}
|
|
|
|
/**
|
|
* Extract an array of `T` elements from the stream `s`.
|
|
*
|
|
* \param s The stream to extract from.
|
|
* \param[out] v Pointer to the extracted array (ipc_buf_in()).
|
|
*
|
|
* \return The stream `s`.
|
|
*
|
|
* This operator actually does not copy out the data in the array, but
|
|
* returns a pointer into the message buffer itself. This means that the
|
|
* data is only valid as long as there is no new data inserted into the stream.
|
|
*
|
|
* \note If array does not fit into transmitted words size will be set to zero.
|
|
* Client has to implement check against zero.
|
|
*
|
|
* See Ipc::Buf_in, Ipc::Buf_cp_in, and Ipc::Buf_cp_out.
|
|
*/
|
|
template< typename T >
|
|
inline
|
|
L4::Ipc::Istream &operator >> (L4::Ipc::Istream &s,
|
|
L4::Ipc::Internal::Buf_in<T> const &v)
|
|
{
|
|
unsigned long si;
|
|
if (s.get(si) && s.has_more<T>(si))
|
|
v.set_size(s.get(L4::Ipc::Msg_ptr<T>(v.buf()), si));
|
|
else
|
|
v.set_size(0);
|
|
return s;
|
|
}
|
|
|
|
/**
|
|
* Extract an element of type `T` from the stream `s`.
|
|
*
|
|
* \param s The stream to extract from.
|
|
* \param[out] v Pointer to the extracted element.
|
|
*
|
|
* \return The stream `s`.
|
|
*
|
|
* This operator actually does not copy out the data, but
|
|
* returns a pointer into the message buffer itself. This means that the
|
|
* data is only valid as long as there is no new data inserted into the stream.
|
|
*
|
|
* See Msg_ptr.
|
|
*/
|
|
template< typename T >
|
|
inline
|
|
L4::Ipc::Istream &operator >> (L4::Ipc::Istream &s,
|
|
L4::Ipc::Msg_ptr<T> const &v)
|
|
{
|
|
s.get(v);
|
|
return s;
|
|
}
|
|
|
|
/**
|
|
* Extract an array of `T` elements from the stream `s`.
|
|
*
|
|
* \param s The stream to extract from.
|
|
* \param[out] v Buffer description to copy the array to (Ipc::Buf_cp_out()).
|
|
*
|
|
* \return The stream `s`.
|
|
*
|
|
* This operator does a copy out of the data into the given buffer.
|
|
*
|
|
* See Ipc::Buf_in, Ipc::Buf_cp_in, and Ipc::Buf_cp_out.
|
|
*/
|
|
template< typename T >
|
|
inline
|
|
L4::Ipc::Istream &operator >> (L4::Ipc::Istream &s,
|
|
L4::Ipc::Internal::Buf_cp_in<T> const &v)
|
|
{
|
|
unsigned long sz;
|
|
s.get(sz);
|
|
v.size() = s.get(v.buf(), cxx::min(v.size(), sz));
|
|
return s;
|
|
}
|
|
|
|
/**
|
|
* Extract a zero-terminated string from the stream.
|
|
*
|
|
* \param s The stream to extract from.
|
|
* \param[out] v Buffer description to copy the array to (Ipc::Str_cp_out()).
|
|
*
|
|
* \return The stream `s`.
|
|
*
|
|
* This operator does a copy out of the data into the given buffer.
|
|
*/
|
|
template< typename T >
|
|
inline
|
|
L4::Ipc::Istream &operator >> (L4::Ipc::Istream &s,
|
|
L4::Ipc::Str_cp_in<T> const &v)
|
|
{
|
|
unsigned long sz;
|
|
s.get(sz);
|
|
unsigned long rsz = s.get(v.buf(), cxx::min(v.size(), sz));
|
|
if (rsz < v.size() && v.buf()[rsz - 1])
|
|
++rsz; // add the zero termination behind the received data
|
|
|
|
if (rsz != 0)
|
|
v.buf()[rsz - 1] = 0;
|
|
|
|
v.size() = rsz;
|
|
return s;
|
|
}
|
|
|
|
|
|
/**
|
|
* Insert an element to type `T` into the stream `s`.
|
|
*
|
|
* \param s The stream to insert the element `v`.
|
|
* \param v The element to insert.
|
|
*
|
|
* \return The stream `s`.
|
|
*/
|
|
inline L4::Ipc::Ostream &operator << (L4::Ipc::Ostream &s, bool v) { s.put(v); return s; }
|
|
inline L4::Ipc::Ostream &operator << (L4::Ipc::Ostream &s, int v) { s.put(v); return s; }
|
|
inline L4::Ipc::Ostream &operator << (L4::Ipc::Ostream &s, long int v) { s.put(v); return s; }
|
|
inline L4::Ipc::Ostream &operator << (L4::Ipc::Ostream &s, long long int v) { s.put(v); return s; }
|
|
inline L4::Ipc::Ostream &operator << (L4::Ipc::Ostream &s, unsigned int v) { s.put(v); return s; }
|
|
inline L4::Ipc::Ostream &operator << (L4::Ipc::Ostream &s, unsigned long int v) { s.put(v); return s; }
|
|
inline L4::Ipc::Ostream &operator << (L4::Ipc::Ostream &s, unsigned long long int v) { s.put(v); return s; }
|
|
inline L4::Ipc::Ostream &operator << (L4::Ipc::Ostream &s, short int v) { s.put(v); return s; }
|
|
inline L4::Ipc::Ostream &operator << (L4::Ipc::Ostream &s, unsigned short int v) { s.put(v); return s; }
|
|
inline L4::Ipc::Ostream &operator << (L4::Ipc::Ostream &s, char v) { s.put(v); return s; }
|
|
inline L4::Ipc::Ostream &operator << (L4::Ipc::Ostream &s, unsigned char v) { s.put(v); return s; }
|
|
inline L4::Ipc::Ostream &operator << (L4::Ipc::Ostream &s, signed char v) { s.put(v); return s; }
|
|
inline L4::Ipc::Ostream &operator << (L4::Ipc::Ostream &s, L4::Ipc::Snd_fpage const &v) { s.put_snd_item(v); return s; }
|
|
template< typename T >
|
|
inline L4::Ipc::Ostream &operator << (L4::Ipc::Ostream &s, L4::Cap<T> const &v)
|
|
{ s << L4::Ipc::Snd_fpage(v.fpage()); return s; }
|
|
|
|
inline L4::Ipc::Ostream &operator << (L4::Ipc::Ostream &s, L4::Ipc::Varg const &v)
|
|
{ s.put(v); return s; }
|
|
template< typename T >
|
|
inline L4::Ipc::Ostream &operator << (L4::Ipc::Ostream &s, L4::Ipc::Varg_t<T> const &v)
|
|
{ s.put(v); return s; }
|
|
|
|
/**
|
|
* Insert the L4 message tag into the stream `s`.
|
|
*
|
|
* \param s The stream to insert the tag `v`.
|
|
* \param v The L4 message tag to insert.
|
|
*
|
|
* \return The stream `s`.
|
|
*
|
|
* \note Only one message tag can be inserted into a stream. Multiple
|
|
* insertions simply overwrite previous insertions.
|
|
*/
|
|
inline
|
|
L4::Ipc::Ostream &operator << (L4::Ipc::Ostream &s, l4_msgtag_t const &v)
|
|
{
|
|
s.tag() = v;
|
|
return s;
|
|
}
|
|
|
|
/**
|
|
* Insert an array with elements of type `T` into the stream `s`.
|
|
*
|
|
* \param s The stream to insert the array `v`.
|
|
* \param v The array to insert (see Ipc::Buf_cp_out()).
|
|
*
|
|
* \return The stream `s`.
|
|
*/
|
|
template< typename T >
|
|
inline
|
|
L4::Ipc::Ostream &operator << (L4::Ipc::Ostream &s,
|
|
L4::Ipc::Internal::Buf_cp_out<T> const &v)
|
|
{
|
|
s.put(v.size());
|
|
s.put(v.buf(), v.size());
|
|
return s;
|
|
}
|
|
|
|
/**
|
|
* Insert a zero terminated character string into the stream `s`.
|
|
*
|
|
* \param s The stream to insert the string `v`.
|
|
* \param v The string to insert.
|
|
*
|
|
* \return The stream `s`.
|
|
*
|
|
* This operator produces basically the same content as the array insertion,
|
|
* however the length of the array is calculated using `strlen(v) + 1`
|
|
* The string is copied into the message including the trailing zero.
|
|
*/
|
|
inline
|
|
L4::Ipc::Ostream &operator << (L4::Ipc::Ostream &s, char const *v)
|
|
{
|
|
unsigned long l = __builtin_strlen(v) + 1;
|
|
s.put(l);
|
|
s.put(v, l);
|
|
return s;
|
|
}
|
|
|
|
namespace L4 { namespace Ipc {
|
|
/**
|
|
* Read a value out of a stream.
|
|
*
|
|
* \param s An Istream.
|
|
*
|
|
* \return The value of type `T`.
|
|
*
|
|
* The stream position is progressed accordingly.
|
|
*/
|
|
template< typename T >
|
|
inline
|
|
T read(Istream &s) { T t; s >> t; return t; }
|
|
|
|
} // namespace Ipc
|
|
} // namespace L4
|