Files
moslab-code/src/l4/pkg/l4re-core/cxx/lib/ipc/include/ipc_stream
2025-09-12 15:55:45 +02:00

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