Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions doc/antora.yml
Original file line number Diff line number Diff line change
Expand Up @@ -20,3 +20,9 @@ nav:
ext:
cpp-reference:
config: doc/mrdocs.yml
# cpp-tagfiles:
# files:
# - file: ./doc/tagfiles/boost-buffers-doxygen.tag.xml
# base_url: 'xref:reference:'
# using-namespaces:
# - boost::buffers
2 changes: 1 addition & 1 deletion doc/build_antora.sh
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,6 @@ npm ci
echo "Building docs in custom dir..."
PATH="$(pwd)/node_modules/.bin:${PATH}"
export PATH
npx antora --clean --fetch "$PLAYBOOK"
npx antora --clean --fetch "$PLAYBOOK" --stacktrace --log-level all
echo "Done"

4 changes: 4 additions & 0 deletions doc/modules/ROOT/nav.adoc
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
* xref:1.intro.adoc[]
* xref:2.algorithms.adoc[]
* xref:3.dynamic-buffers.adoc[]
* xref:4.custom-sequences.adoc[]
* Concepts
** xref:concepts/ConstBufferSequence.adoc[]
** xref:concepts/MutableBufferSequence.adoc[]
Expand Down
115 changes: 115 additions & 0 deletions doc/modules/ROOT/pages/1.intro.adoc
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
//
// Copyright (c) 2025 Vinnie Falco ([email protected])
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
// Official repository: https://github.com/cppalliance/buffers
//

= Introduction

The C++ language is strongly typed; programs express data structures and
algorithms in terms of types. However many categories of algorithms operate
instead on data represented as raw bytes, such as:

* Encryption and decryption
* Compression and decompression
* JSON parsing and serialization
* Network protocols, such as HTTP and WebSocket

Algorithms are often combined; a network application may need to remove the
framing from a WebSocket stream, apply decompression to the unframed payloads,
then parse the decompressed payloads as serialized JSON. We define common
vocabulary types and concepts to facilitate interoperability between libraries,
in order that such algorithms may be composed.

== Contiguous Buffers

The fundamental representation of unstructured bytes is the contiguous buffer,
characterized by a possibly `const` pointer to region of memory with a defined
number of valid bytes. These are represented as objects of type
cpp:const_buffer[] and cpp:mutable_buffer[].

[source,cpp]
----
struct const_buffer
{
void const* data() const noexcept;
std::size_t size() const noexcept;
};

struct mutable_buffer
{
void * data() const noexcept;
std::size_t size() const noexcept;
};
----

This representation is helpful yet insufficient for all use-cases. For example
an algorithm which must add framing data to caller-provided contiguous buffers
would need to perform costly reallocations and copies to represent it as a
single buffer before passing it on to the next algorithm. Operating-system
level facilities which transact raw bytes on file descriptors or sockets often
provide a
https://en.wikipedia.org/wiki/Vectored_I/O[_scatter/gather_]
interface: an ordered list of zero or more contiguous buffers. Linux provides
the type
https://man7.org/linux/man-pages/man3/iovec.3type.html[`iovec`]
this purpose. In C++ we may consider using a container to represent an
sequence of buffers:

[source,cpp]
----
std::list< const_buffer > bs1;

std::vector< mutable_buffer > bs2;

std::array< const_buffer, 3 > bs3;
----

There are some caveats with this approach:

* `list` is expensive to copy.
* `vector` can be resized at runtime yet allocates memory to do so.
* `array` is fixed in size. Inserting additional buffers requires metaprogramming
and a redeclaration of the container type.
* Choosing a single type as a vocabulary type precludes user-defined types.

== Sequence Requirements

The approach taken by this library is the same as the approach used in the popular
Boost.Asio network library. That is, to define the concepts
xref:concepts/ConstBufferSequence.adoc[_ConstBufferSequence_] and
xref:concepts/MutableBufferSequence.adoc[_MutableBufferSequence_] for
representing buffer sequences with these semantics:

* A buffer sequence is cheap to copy.

* A copy of a buffer sequence refers to the same underlying memory regions.

* Buffer sequences model bidirectional ranges whose value type is convertible
to cpp:const_buffer[] or cpp:mutable_buffer[] for sequences whose contents.
are modifiable.

* The types cpp:const_buffer[] and cpp:mutable_buffer[] are also considered
to be buffer sequences.

Algorithms which use buffer sequences have these additional requirements:

* The algorithm shall maintain a copy of every input buffer sequence for at
least as long as any of the underlying memory regions are accessed.

* Iterators to elements of a buffer sequence are obtained using
cpp:begin[] and cpp:end[].

== Buffer Pairs

Buffer sequences of length two; that is, buffer sequences consisting of up to
two separate contiguous memory regions, occur in design patterns with sufficient
frequency that the library provides a custom implementation for representing
them. Objects of type cpp:const_buffer_pair[] or cpp::mutable_buffer_pair[] are
buffer sequences of length two. These are the type of sequences returned by
the cpp:circular_buffer[] dynamic buffer, discussed later.


88 changes: 88 additions & 0 deletions doc/modules/ROOT/pages/2.algorithms.adoc
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
//
// Copyright (c) 2025 Vinnie Falco ([email protected])
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
// Official repository: https://github.com/cppalliance/buffers
//

= Algorithms

Algorithms which operate on buffer sequences are written as function templates,
using one or more template parameters to reflect the sequence types. The
metafunctions cpp:is_const_buffer_sequence[] and cpp:is_mutable_buffer_sequence[]
determine whether or not a specified type meets the syntactic requirements for
buffer sequences. They can be used for generating precise error messages at
compile time when parameters are misused, or they can be used to constrain a
function's overload set as shown:

[source,cpp]
----
template< class ConstBufferSequence >
requires is_const_buffer_sequence< ConstBufferSequence >
void print( ConstBufferSequence const& bs );
----

To iterate over a sequence the functions cpp::begin[] and
cpp:end[] are used to obtain the starting and ending iterators. This
example writes each individual buffer in the sequence to standard output,
separated by a newline:

[source,cpp]
----
template< class ConstBufferSequence >
requires is_const_buffer_sequenc<ConstBufferSequence>
void print( ConstBufferSequence const& bs )
{
auto const end_ = buffers::end( bs );
for( auto it = buffers::begin( bs ); it != end_; ++it )
{
buffers::const_buffer cb( *it );
std::cout << std::string_view( static_cast<char const*>cb.data(), cb.size() ) << "\n";
}
}
----

WARNING: Care must be exercised when a raw pointer is cast to other data
types such as `char` or to aggregate types. It is the caller's responsibility
to ensure that such casts do not violate the type safety features of the
language.

== Calculating Size

The function cpp:size[] returns the total number of bytes represented in
a sequence of buffers, with this equivalent signature:

[source,cpp]
----
template< class ConstBufferSequence >
std::size_t
size(
ConstBufferSequence const& bs);
----

The default implementation performs a straightforward summation of individual
elements visited through iteration. The size customization point, discussed
later, permits optimizations of the algorithm for types which can do better.

== Copying Bytes

To facilitate copying data between arbitrary buffer sequences, the function
cpp:copy[] is provided, with this equivalent signature:

[source,cpp]
----
template<
class MutableBufferSequence,
class ConstBufferSequence >
std::size_t
copy(
MutableBufferSequence const& dest,
ConstBufferSequence const& src,
std::size_t at_most = std::numeric_limits<std::size_t>::max );
----

This function copies up to `at_most` bytes from the source buffer sequence to
the destination sequence, or less depending on the size of the smaller of the
two sequences. The return value indicates the actual number of bytes copied.
30 changes: 30 additions & 0 deletions doc/modules/ROOT/pages/3.dynamic-buffers.adoc
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
//
// Copyright (c) 2025 Vinnie Falco ([email protected])
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
// Official repository: https://github.com/cppalliance/buffers
//

= Dynamic Buffers

While buffer sequences are useful for transporting raw bytes across function
boundaries, they can only represent sequences of fixed size. To allow the
creation of sequences whose size can change, we adopt the "dynamic buffer"
concept introduced in Boost.Asio. A dynamic buffer represents memory regions
which may be automatically sized as required.

* A dynamic buffer has an input sequence defined as a _ConstBufferSequence_

* A dynamic buffer has an output sequence defined as a _MutableBufferSequence_

* Callers acquire the output sequence by calling `prepare(n)` where `n` is the
desired size.

* Callers invoke `commit(n)` to move written bytes from the output sequence
to the input sequence.

* The input sequence is returned by calling `data()`

* The input sequence is released by calling `consume(n)`
86 changes: 86 additions & 0 deletions doc/modules/ROOT/pages/4.custom-sequences.adoc
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
//
// Copyright (c) 2025 Vinnie Falco ([email protected])
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
// Official repository: https://github.com/cppalliance/buffers
//

= Customization

The definitions for sequences allow for the possibility of user-defined types
to act as custom sequences. The library provides a set of customization points
so its algorithms can be optimized for these types. Customization points are
implementing as overloads of a function named `tag_invoke` found by argument
dependent lookup. For example, here is a custom sequence which implements the
`size` customization point:

[source,cpp]
----
struct custom_sequence
{
struct iterator;

iterator begin() const noexcept;
iterator end() const noexcept;

/** Return the total number of bytes in the sequence.

This function is intended to be found via
argument-dependent lookup only.
*/
friend
std::size_t
tag_invoke(
buffers::size_tag const&,
custom_sequence const& bs ) noexcept
{
return bs.size_impl();
}

private:
std::size_t size_impl() const noexcept;
};

----

NOTE: For maximum compile-time efficiency, overloads of `tag_invoke` for a
specific type should be declared as _hidden friends_ \[1\] of that type. That is,
the definition for the function should be in the class declaration, and no
out-of-class or namespace-scope declarations should exist.

These are the available customization points and tags:

[cols="1a,1a,3a"]
|===
|tag|Signature|Semantics, Pre/Post-conditions

|`size_tag`
|
[source,cpp]
----
std::size_t
tag_invoke(
size_tag const&,
T const& )
----
|Return the total number of bytes represented by a buffer sequence object.

|`slice_tag`
|
[source,cpp]
----
void
tag_invoke(
slice_tag const&,
T&,
slice_how how,
std::size_t n )
----
|Remove a number of bytes from a buffer sequence object, modifying the
object in place.

|===

\[1\] https://www.youtube.com/watch?v=POa_V15je8Y[Making New Friends (CppCon 2018)]
28 changes: 28 additions & 0 deletions doc/modules/ROOT/pages/5.asio-buffers.adoc
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
//
// Copyright (c) 2025 Vinnie Falco ([email protected])
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
// Official repository: https://github.com/cppalliance/buffers
//

= Asio Buffers

This library borrows heavily on the concepts and algorithms invented by
Christopher Kohlhoff in the Asio and Boost.Asio libraries. A conscious design
choice was made to avoid depending on Asio's concrete types, as libraries
which deal in buffers may not also need to perform asynchronous I/O. For
example, the Sans I/O philosophy of library design suggests that protocol
libraries be engineered to operate independently of any particular model of
asynchrony or I/O. It is desired that these libraries remain agnostic and not
incur compile-time dependencies on particular I/O implementations. This can be
seen in the
https://github.com/cppalliance/http_proto[Boost.Http.Proto] (proposed) and
https://github.com/cppalliance/http_proto[Boost.WS.Proto] (proposed) libraries.




*

Loading
Loading