blob: 9bc184db8dcc0a97efcbadd4cad84d167de91234 [file] [log] [blame]
.. Licensed to the Apache Software Foundation (ASF) under one
or more contributor license agreements. See the NOTICE file distributed with this work for
additional information regarding copyright ownership. The ASF licenses this file to you under the
Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software distributed under the License
is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
or implied. See the License for the specific language governing permissions and limitations under
the License.
.. include:: ../common-defs.rst
.. highlight:: cpp
.. default-domain:: cpp
.. |MemSpan| replace:: :code:`MemSpan`
.. _swoc-mem-span:
********
MemSpan
********
Synopsis
********
:code:`#include "swoc/MemSpan.h"`
.. class:: template < typename T > MemSpan
:libswoc:`Reference documentation <MemSpan>`.
|MemSpan| is a view on *writable* memory. This distinguishes it from classes like :code:`TextView`.
The purpose of the class is to provide a compact description of a contiguous writeable block of
memory, carrying both location and size information to (hopefully) prevent these from becoming
separated. A |MemSpan| is a view because it never owns memory. It should be treated as a smart pointer that
carries size information. As text views are intended to replace passing a character pointer and size
separately, a memory span is intended to replace passing a pointer and a size as separate arguments.
|MemSpan| has two styles. The "void" style treats the memory as purely a block of memory without
internal structure. The "typed" style treats the memory as an array of a specific type. These
can be inter-converted, in the equivalent of type casting. The rules about
conversions between span types is modeled on pointer conversions. Therefore a typed memory span
implicitly converts to a void span, but a void span requires an explicit cast or rebinding to
convert to a typed memory span.
As with pointers, a constant span can refer to writable memory and
a non-constant span can refer to writable memory. Whether the memory referenced by the
span is writable depends on whether the type of the span is a constant type. E.g. :code:`MemSpan<char>` refers
to writable memory and :code:`MemSpan<char const>` does not.
Usage
*****
A primary use of |MemSpan| is to generalize the type of a block of memory. For instance a function parameter
could be :code:`std::vector<alpha>` for some type :code:`alpha`. Calling the function, however, requires
the creation of a vector. This can require an unneeded memory allocation.
The other primary use is to work with chunks of memory embedded in larger containers. For instance in the
previous example, not only is creating another vector needed, but it also makes working with
subspans very difficult. Either copying is required or it can be worked around with various
additional parameters, or by reverting to the
most primitve and passing in a raw pointer and a count. It is much easier to have a |MemSpan| parameter type
which handles all of these cases with minimal complexity. E.g. a function like ::
int f(MemSpan<char> source) { ... }
can be passed data from a vector, an array, a raw pointer and size, stack memory, etc., including types not
defined in the scope of the function.
Void Span
=========
A :code:`MemSpan<void>` describes an undifferentiated contiguous block of memory. It can be
constructed from either a pointer and length :libswoc:`MemSpan\< void >::MemSpan(value_arg *,
size_t)` or a half open range of two pointers :libswoc:`MemSpan\< void >::MemSpan(value_arg *, value_arg * )`.
A default constructed instance, or one constructed from :code:`nullptr` has no
memory and zero size.
The implementation differentiates between :code:`void` and :code:`void const` in order to be const correct.
A :code:`void` span implicitly converts to a :code:`void const` span but not the other way as is the case for
:code:`void*` and :code:`void const*` pointers. A :code:`void const` span is the "universal" span - all other
span types implicitly convert to this type.
The memory described by the instance can be obtained with the
data method :libswoc:`MemSpan\< void \>::data` and the size with the
size method :libswoc:`MemSpan\< void \>::size`.
Typed Span
==========
A typed |MemSpan| treats the described memory as an array of the type. E.g., :code:`MemSpan<int>`
treats its memory as an array of :code:`int`, with a :libswoc:`subscript operator <MemSpan::operator[]>`.
The construction is the same as with the :code:`void` case but the pointers must be of the
span type. As with pointers and arrays, the count is the number of instances of :code:`T` not the size in
bytes.
Rebinding
=========
A new |MemSpan| of a different type can be created from an existing instance. This is is called
"rebinding" and uses the templated :libswoc:`rebind method <MemSpan::rebind>`. This is roughly
equivalent to type casting in that the original instance is unchanged. A new instance is created
that refers to the same memory but with different type information. As an example,
:libswoc:`MemArena::alloc` returns a :code:`MemSpan<void>`. If this is to be used for string data,
then the call could look like ::
auto span = arena.alloc(n).rebind<char>();
This would make :code:`span` of type :code:`MemSpan<char>` covering the same memory returned from
:code:`alloc`.
Rebinding requires the size of the memory block to be an integral multiple of the size of the target
type. That is, a binding that creates a partial object at the end of the array is forbidden.
Alignment
=========
Rebinding on void spans can be done along with alignment using the :libswoc:`align method
<MemSpan::align>`. This can be done with a templated method for a type, or with an explicit alignment
argument of type :code:`size_t`. It does not fail due to partial objects. Instead enough space is
discarded from the start of memory such that the returned span has the alignment required for
:code:`T` and memory space that would yield a partial object is discarded from the end. The result is
the largest subspan that is memory aligned and has space for an integral number of instances.
Conversions
===========
Memory spans will implicitly convert in a manner equivalent to raw pointers.
* Non-const spans implicitly convert to constant spans of the same type.
* Non-const spans implicitly convert to :code:`MemSpan<void>`.
* Any span will implicitly convert to :code:`MemSpan<void const>`.
Construction
============
The most common style of construction is to provide a pointer and size / count. For void spans this is the
number of bytes, while typed spans take a count as with arrays. E.g. for some type :code:`Alpha` ::
Alpha array[6];
the corresponding span would be ::
MemSpan<Alpha> span(array, 6);
On the other hand the constructors understand arrays so this works as well ::
MemSpan<Alpha> span(array);
There is constructor support for any contiguous memory container that supports the :code:`data`
and :code:`size` methods. A span can be contructed from :code:`std::string`, :code:`std::string_view`,
or :code:`std::vector` by passing the container. Internally this ::
std::vector<Alpha> av;
MemSpan<Alpha> aspan{av};
is treated as
std::vector<Alpha> av;
MemSpan<Alpha> aspan{av.data(), av.size};
There are template deduction guides for :code:`std::array`, :code:`std::vector`, :code:`std::string`,
and :code:`std::string_view` so that the previous could also be done as ::
std::vector<Alpha> av;
MemSpan aspan{av};