.. 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| 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` refers to writable memory and :code:`MemSpan` 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` 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 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` 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` treats its memory as an array of :code:`int`, with a :libswoc:`subscript 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 `. 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`. If this is to be used for string data, then the call could look like :: auto span = arena.alloc(n).rebind(); This would make :code:`span` of type :code:`MemSpan` 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 `. 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`. * Any span will implicitly convert to :code:`MemSpan`. 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 span(array, 6); On the other hand the constructors understand arrays so this works as well :: MemSpan 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 av; MemSpan aspan{av}; is treated as std::vector av; MemSpan 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 av; MemSpan aspan{av};