[RFC,v3,06/22] libcamera: base: Add alignment utility functions
diff mbox series

Message ID 20251030165816.1095180-7-barnabas.pocze@ideasonboard.com
State New
Headers show
Series
  • libcamera: Add `MetadataList`
Related show

Commit Message

Barnabás Pőcze Oct. 30, 2025, 4:58 p.m. UTC
Add a couple internal functions to check alignment, and to
align integers, pointers up to a given alignment.

Signed-off-by: Barnabás Pőcze <barnabas.pocze@ideasonboard.com>
Reviewed-by: Paul Elder <paul.elder@ideasonboard.com>
---
changes in v3:
  * documentation
---
 include/libcamera/base/internal/align.h | 135 ++++++++++++++++++++++++
 include/libcamera/base/meson.build      |   1 +
 2 files changed, 136 insertions(+)
 create mode 100644 include/libcamera/base/internal/align.h

Comments

Kieran Bingham Oct. 31, 2025, 11:29 a.m. UTC | #1
Quoting Barnabás Pőcze (2025-10-30 16:58:00)
> Add a couple internal functions to check alignment, and to
> align integers, pointers up to a given alignment.
> 
> Signed-off-by: Barnabás Pőcze <barnabas.pocze@ideasonboard.com>
> Reviewed-by: Paul Elder <paul.elder@ideasonboard.com>
> ---
> changes in v3:
>   * documentation
> ---
>  include/libcamera/base/internal/align.h | 135 ++++++++++++++++++++++++
>  include/libcamera/base/meson.build      |   1 +
>  2 files changed, 136 insertions(+)
>  create mode 100644 include/libcamera/base/internal/align.h
> 
> diff --git a/include/libcamera/base/internal/align.h b/include/libcamera/base/internal/align.h
> new file mode 100644
> index 0000000000..d8ee4e3695
> --- /dev/null
> +++ b/include/libcamera/base/internal/align.h
> @@ -0,0 +1,135 @@
> +/* SPDX-License-Identifier: LGPL-2.1-or-later */
> +/*
> + * Copyright (C) 2025, Ideas on Board Oy
> + *
> + * Alignment utilities
> + */
> +
> +#pragma once
> +
> +#include <cassert>
> +#include <cstddef>
> +#include <cstdint>
> +
> +#include <libcamera/base/internal/cxx20.h>
> +
> +/**
> + * \internal
> + * \file align.h
> + * \brief Utilities for handling alignment
> + */
> +
> +namespace libcamera::internal::align {
> +
> +/**
> + * \internal
> + * \brief Check if pointer is aligned
> + * \param[in] p pointer to check
> + * \param[in] alignment desired alignment
> + * \return true if \a p is at least \a alignment aligned, false otherwise
> + */
> +inline bool is(const void *p, std::uintptr_t alignment)
> +{
> +       assert(alignment > 0);
> +
> +       return reinterpret_cast<std::uintptr_t>(p) % alignment == 0;
> +}
> +
> +/**
> + * \internal
> + * \brief Align number up
> + * \param[in] x number to check
> + * \param[in] alignment desired alignment
> + * \return true if \a p is at least \a alignment aligned, false otherwise
> + */
> +template<typename T>
> +constexpr T up(T x, cxx20::type_identity_t<T> alignment)
> +{
> +       static_assert(std::is_unsigned_v<T>);
> +       assert(alignment > 0);
> +
> +       const auto padding = (alignment - (x % alignment)) % alignment;
> +       assert(x + padding >= x);
> +
> +       return x + padding;
> +}
> +
> +/**
> + * \internal
> + * \brief Align pointer up
> + * \param[in] p pointer to align
> + * \param[in] alignment desired alignment
> + * \return \a p up-aligned to \a alignment
> + */
> +template<typename T>
> +auto *up(T *p, std::uintptr_t alignment)
> +{
> +       using U = std::conditional_t<
> +               std::is_const_v<T>,
> +               const std::byte,
> +               std::byte
> +       >;
> +
> +       return reinterpret_cast<U *>(up(reinterpret_cast<std::uintptr_t>(p), alignment));
> +}
> +
> +/**
> + * \internal
> + * \brief Align pointer up
> + * \param[in] size required number of bytes
> + * \param[in] alignment required alignment
> + * \param[in] ptr base pointer
> + * \param[in] avail number of available bytes
> + *
> + * This function checks if the storage starting at \a ptr and continuing for \a avail
> + * bytes (if \a avail is nullptr, then no size checking is done) can accommodate \a size
> + * bytes of data aligned to \a alignment. If so, then a pointer to the start is the returned
> + * and \a ptr is adjusted to point right after the section of \a size bytes. If present,
> + * \a avail is also adjusted.
> + *
> + * Similar to std::align().
> + *
> + * \return the appropriately aligned pointer, or nullptr if there is not enough space
> + */
> +template<typename T>
> +T *up(std::size_t size, std::size_t alignment, T *&ptr, std::size_t *avail = nullptr)
> +{
> +       assert(alignment > 0);
> +
> +       auto p = reinterpret_cast<std::uintptr_t>(ptr);
> +       const auto padding = (alignment - (p % alignment)) % alignment;
> +
> +       if (avail) {
> +               if (size > *avail || padding > *avail - size)
> +                       return nullptr;

This is where unsigned size types are annoying ;-) If we could just
write if (answer < 0) { } :-D

But this answers the only concern I jumped to so:


Reviewed-by: Kieran Bingham <kieran.bingham@ideasonboard.com>

> +
> +               *avail -= size + padding;
> +       }
> +
> +       p += padding;
> +       ptr = reinterpret_cast<T *>(p + size);
> +
> +       return reinterpret_cast<T *>(p);
> +}
> +
> +/**
> + * \internal
> + * \brief Align pointer up
> + * \tparam U desired type
> + * \param[in] ptr base pointer
> + * \param[in] avail number of available bytes
> + *
> + * A convenience wrapper around libcamera::internal::align::up(std::size_t, std::size_t, T *&, std::size_t *)
> + * that takes the size and alignment information from the type \a U.
> + *
> + * \sa libcamera::internal::align::up(std::size_t, std::size_t, T *&, std::size_t *)
> + */
> +template<typename U, typename T>
> +U *up(T *&ptr, std::size_t *avail = nullptr)
> +{
> +       return reinterpret_cast<std::conditional_t<std::is_const_v<T>, const U, U> *>(
> +               up(sizeof(U), alignof(U), ptr, avail)
> +       );
> +}
> +
> +} /* namespace libcamera::internal::align */
> diff --git a/include/libcamera/base/meson.build b/include/libcamera/base/meson.build
> index ee42c9910f..723f42b362 100644
> --- a/include/libcamera/base/meson.build
> +++ b/include/libcamera/base/meson.build
> @@ -33,6 +33,7 @@ libcamera_base_private_headers = files([
>  
>  libcamera_base_internal_headers = files([
>      'internal/cxx20.h',
> +    'internal/align.h',
>  ])
>  
>  libcamera_base_headers = [
> -- 
> 2.51.1
>

Patch
diff mbox series

diff --git a/include/libcamera/base/internal/align.h b/include/libcamera/base/internal/align.h
new file mode 100644
index 0000000000..d8ee4e3695
--- /dev/null
+++ b/include/libcamera/base/internal/align.h
@@ -0,0 +1,135 @@ 
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+/*
+ * Copyright (C) 2025, Ideas on Board Oy
+ *
+ * Alignment utilities
+ */
+
+#pragma once
+
+#include <cassert>
+#include <cstddef>
+#include <cstdint>
+
+#include <libcamera/base/internal/cxx20.h>
+
+/**
+ * \internal
+ * \file align.h
+ * \brief Utilities for handling alignment
+ */
+
+namespace libcamera::internal::align {
+
+/**
+ * \internal
+ * \brief Check if pointer is aligned
+ * \param[in] p pointer to check
+ * \param[in] alignment desired alignment
+ * \return true if \a p is at least \a alignment aligned, false otherwise
+ */
+inline bool is(const void *p, std::uintptr_t alignment)
+{
+	assert(alignment > 0);
+
+	return reinterpret_cast<std::uintptr_t>(p) % alignment == 0;
+}
+
+/**
+ * \internal
+ * \brief Align number up
+ * \param[in] x number to check
+ * \param[in] alignment desired alignment
+ * \return true if \a p is at least \a alignment aligned, false otherwise
+ */
+template<typename T>
+constexpr T up(T x, cxx20::type_identity_t<T> alignment)
+{
+	static_assert(std::is_unsigned_v<T>);
+	assert(alignment > 0);
+
+	const auto padding = (alignment - (x % alignment)) % alignment;
+	assert(x + padding >= x);
+
+	return x + padding;
+}
+
+/**
+ * \internal
+ * \brief Align pointer up
+ * \param[in] p pointer to align
+ * \param[in] alignment desired alignment
+ * \return \a p up-aligned to \a alignment
+ */
+template<typename T>
+auto *up(T *p, std::uintptr_t alignment)
+{
+	using U = std::conditional_t<
+		std::is_const_v<T>,
+		const std::byte,
+		std::byte
+	>;
+
+	return reinterpret_cast<U *>(up(reinterpret_cast<std::uintptr_t>(p), alignment));
+}
+
+/**
+ * \internal
+ * \brief Align pointer up
+ * \param[in] size required number of bytes
+ * \param[in] alignment required alignment
+ * \param[in] ptr base pointer
+ * \param[in] avail number of available bytes
+ *
+ * This function checks if the storage starting at \a ptr and continuing for \a avail
+ * bytes (if \a avail is nullptr, then no size checking is done) can accommodate \a size
+ * bytes of data aligned to \a alignment. If so, then a pointer to the start is the returned
+ * and \a ptr is adjusted to point right after the section of \a size bytes. If present,
+ * \a avail is also adjusted.
+ *
+ * Similar to std::align().
+ *
+ * \return the appropriately aligned pointer, or nullptr if there is not enough space
+ */
+template<typename T>
+T *up(std::size_t size, std::size_t alignment, T *&ptr, std::size_t *avail = nullptr)
+{
+	assert(alignment > 0);
+
+	auto p = reinterpret_cast<std::uintptr_t>(ptr);
+	const auto padding = (alignment - (p % alignment)) % alignment;
+
+	if (avail) {
+		if (size > *avail || padding > *avail - size)
+			return nullptr;
+
+		*avail -= size + padding;
+	}
+
+	p += padding;
+	ptr = reinterpret_cast<T *>(p + size);
+
+	return reinterpret_cast<T *>(p);
+}
+
+/**
+ * \internal
+ * \brief Align pointer up
+ * \tparam U desired type
+ * \param[in] ptr base pointer
+ * \param[in] avail number of available bytes
+ *
+ * A convenience wrapper around libcamera::internal::align::up(std::size_t, std::size_t, T *&, std::size_t *)
+ * that takes the size and alignment information from the type \a U.
+ *
+ * \sa libcamera::internal::align::up(std::size_t, std::size_t, T *&, std::size_t *)
+ */
+template<typename U, typename T>
+U *up(T *&ptr, std::size_t *avail = nullptr)
+{
+	return reinterpret_cast<std::conditional_t<std::is_const_v<T>, const U, U> *>(
+		up(sizeof(U), alignof(U), ptr, avail)
+	);
+}
+
+} /* namespace libcamera::internal::align */
diff --git a/include/libcamera/base/meson.build b/include/libcamera/base/meson.build
index ee42c9910f..723f42b362 100644
--- a/include/libcamera/base/meson.build
+++ b/include/libcamera/base/meson.build
@@ -33,6 +33,7 @@  libcamera_base_private_headers = files([
 
 libcamera_base_internal_headers = files([
     'internal/cxx20.h',
+    'internal/align.h',
 ])
 
 libcamera_base_headers = [