[{"id":36573,"web_url":"https://patchwork.libcamera.org/comment/36573/","msgid":"<176191014070.567526.16277348515300263023@ping.linuxembedded.co.uk>","date":"2025-10-31T11:29:00","subject":"Re: [RFC PATCH v3 06/22] libcamera: base: Add alignment utility\n\tfunctions","submitter":{"id":4,"url":"https://patchwork.libcamera.org/api/people/4/","name":"Kieran Bingham","email":"kieran.bingham@ideasonboard.com"},"content":"Quoting Barnabás Pőcze (2025-10-30 16:58:00)\n> Add a couple internal functions to check alignment, and to\n> align integers, pointers up to a given alignment.\n> \n> Signed-off-by: Barnabás Pőcze <barnabas.pocze@ideasonboard.com>\n> Reviewed-by: Paul Elder <paul.elder@ideasonboard.com>\n> ---\n> changes in v3:\n>   * documentation\n> ---\n>  include/libcamera/base/internal/align.h | 135 ++++++++++++++++++++++++\n>  include/libcamera/base/meson.build      |   1 +\n>  2 files changed, 136 insertions(+)\n>  create mode 100644 include/libcamera/base/internal/align.h\n> \n> diff --git a/include/libcamera/base/internal/align.h b/include/libcamera/base/internal/align.h\n> new file mode 100644\n> index 0000000000..d8ee4e3695\n> --- /dev/null\n> +++ b/include/libcamera/base/internal/align.h\n> @@ -0,0 +1,135 @@\n> +/* SPDX-License-Identifier: LGPL-2.1-or-later */\n> +/*\n> + * Copyright (C) 2025, Ideas on Board Oy\n> + *\n> + * Alignment utilities\n> + */\n> +\n> +#pragma once\n> +\n> +#include <cassert>\n> +#include <cstddef>\n> +#include <cstdint>\n> +\n> +#include <libcamera/base/internal/cxx20.h>\n> +\n> +/**\n> + * \\internal\n> + * \\file align.h\n> + * \\brief Utilities for handling alignment\n> + */\n> +\n> +namespace libcamera::internal::align {\n> +\n> +/**\n> + * \\internal\n> + * \\brief Check if pointer is aligned\n> + * \\param[in] p pointer to check\n> + * \\param[in] alignment desired alignment\n> + * \\return true if \\a p is at least \\a alignment aligned, false otherwise\n> + */\n> +inline bool is(const void *p, std::uintptr_t alignment)\n> +{\n> +       assert(alignment > 0);\n> +\n> +       return reinterpret_cast<std::uintptr_t>(p) % alignment == 0;\n> +}\n> +\n> +/**\n> + * \\internal\n> + * \\brief Align number up\n> + * \\param[in] x number to check\n> + * \\param[in] alignment desired alignment\n> + * \\return true if \\a p is at least \\a alignment aligned, false otherwise\n> + */\n> +template<typename T>\n> +constexpr T up(T x, cxx20::type_identity_t<T> alignment)\n> +{\n> +       static_assert(std::is_unsigned_v<T>);\n> +       assert(alignment > 0);\n> +\n> +       const auto padding = (alignment - (x % alignment)) % alignment;\n> +       assert(x + padding >= x);\n> +\n> +       return x + padding;\n> +}\n> +\n> +/**\n> + * \\internal\n> + * \\brief Align pointer up\n> + * \\param[in] p pointer to align\n> + * \\param[in] alignment desired alignment\n> + * \\return \\a p up-aligned to \\a alignment\n> + */\n> +template<typename T>\n> +auto *up(T *p, std::uintptr_t alignment)\n> +{\n> +       using U = std::conditional_t<\n> +               std::is_const_v<T>,\n> +               const std::byte,\n> +               std::byte\n> +       >;\n> +\n> +       return reinterpret_cast<U *>(up(reinterpret_cast<std::uintptr_t>(p), alignment));\n> +}\n> +\n> +/**\n> + * \\internal\n> + * \\brief Align pointer up\n> + * \\param[in] size required number of bytes\n> + * \\param[in] alignment required alignment\n> + * \\param[in] ptr base pointer\n> + * \\param[in] avail number of available bytes\n> + *\n> + * This function checks if the storage starting at \\a ptr and continuing for \\a avail\n> + * bytes (if \\a avail is nullptr, then no size checking is done) can accommodate \\a size\n> + * bytes of data aligned to \\a alignment. If so, then a pointer to the start is the returned\n> + * and \\a ptr is adjusted to point right after the section of \\a size bytes. If present,\n> + * \\a avail is also adjusted.\n> + *\n> + * Similar to std::align().\n> + *\n> + * \\return the appropriately aligned pointer, or nullptr if there is not enough space\n> + */\n> +template<typename T>\n> +T *up(std::size_t size, std::size_t alignment, T *&ptr, std::size_t *avail = nullptr)\n> +{\n> +       assert(alignment > 0);\n> +\n> +       auto p = reinterpret_cast<std::uintptr_t>(ptr);\n> +       const auto padding = (alignment - (p % alignment)) % alignment;\n> +\n> +       if (avail) {\n> +               if (size > *avail || padding > *avail - size)\n> +                       return nullptr;\n\nThis is where unsigned size types are annoying ;-) If we could just\nwrite if (answer < 0) { } :-D\n\nBut this answers the only concern I jumped to so:\n\n\nReviewed-by: Kieran Bingham <kieran.bingham@ideasonboard.com>\n\n> +\n> +               *avail -= size + padding;\n> +       }\n> +\n> +       p += padding;\n> +       ptr = reinterpret_cast<T *>(p + size);\n> +\n> +       return reinterpret_cast<T *>(p);\n> +}\n> +\n> +/**\n> + * \\internal\n> + * \\brief Align pointer up\n> + * \\tparam U desired type\n> + * \\param[in] ptr base pointer\n> + * \\param[in] avail number of available bytes\n> + *\n> + * A convenience wrapper around libcamera::internal::align::up(std::size_t, std::size_t, T *&, std::size_t *)\n> + * that takes the size and alignment information from the type \\a U.\n> + *\n> + * \\sa libcamera::internal::align::up(std::size_t, std::size_t, T *&, std::size_t *)\n> + */\n> +template<typename U, typename T>\n> +U *up(T *&ptr, std::size_t *avail = nullptr)\n> +{\n> +       return reinterpret_cast<std::conditional_t<std::is_const_v<T>, const U, U> *>(\n> +               up(sizeof(U), alignof(U), ptr, avail)\n> +       );\n> +}\n> +\n> +} /* namespace libcamera::internal::align */\n> diff --git a/include/libcamera/base/meson.build b/include/libcamera/base/meson.build\n> index ee42c9910f..723f42b362 100644\n> --- a/include/libcamera/base/meson.build\n> +++ b/include/libcamera/base/meson.build\n> @@ -33,6 +33,7 @@ libcamera_base_private_headers = files([\n>  \n>  libcamera_base_internal_headers = files([\n>      'internal/cxx20.h',\n> +    'internal/align.h',\n>  ])\n>  \n>  libcamera_base_headers = [\n> -- \n> 2.51.1\n>","headers":{"Return-Path":"<libcamera-devel-bounces@lists.libcamera.org>","X-Original-To":"parsemail@patchwork.libcamera.org","Delivered-To":"parsemail@patchwork.libcamera.org","Received":["from lancelot.ideasonboard.com (lancelot.ideasonboard.com\n\t[92.243.16.209])\n\tby patchwork.libcamera.org (Postfix) with ESMTPS id CE140C3259\n\tfor <parsemail@patchwork.libcamera.org>;\n\tFri, 31 Oct 2025 11:29:05 +0000 (UTC)","from lancelot.ideasonboard.com (localhost [IPv6:::1])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTP id BCD8D60970;\n\tFri, 31 Oct 2025 12:29:04 +0100 (CET)","from perceval.ideasonboard.com (perceval.ideasonboard.com\n\t[IPv6:2001:4b98:dc2:55:216:3eff:fef7:d647])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTPS id 0A258600CC\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tFri, 31 Oct 2025 12:29:04 +0100 (CET)","from pendragon.ideasonboard.com\n\t(cpc89244-aztw30-2-0-cust6594.18-1.cable.virginm.net [86.31.185.195])\n\tby perceval.ideasonboard.com (Postfix) with ESMTPSA id 2D0B015D2;\n\tFri, 31 Oct 2025 12:27:13 +0100 (CET)"],"Authentication-Results":"lancelot.ideasonboard.com; dkim=pass (1024-bit key;\n\tunprotected) header.d=ideasonboard.com header.i=@ideasonboard.com\n\theader.b=\"goQI+AjQ\"; dkim-atps=neutral","DKIM-Signature":"v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com;\n\ts=mail; t=1761910033;\n\tbh=jg4jy91UucoSFsZXRM0Q53z8vO5ARVp0oGEVQK8g+yA=;\n\th=In-Reply-To:References:Subject:From:Cc:To:Date:From;\n\tb=goQI+AjQQDRQPTSMlPnEKPOXz6HkiSOKAHyzuklEwk2qRoBhWcL1vKc1tgCHyOdL6\n\t+abkw/IFxn9RLVG7SA5wlkhJ9LFdJP9zRuoMZdvxkMZcPqoD75LS5h8eHaWofRducB\n\tQYB8GLdepSOphGuhU2eK2xGTWCDfXLxe99ULDjW8=","Content-Type":"text/plain; charset=\"utf-8\"","MIME-Version":"1.0","Content-Transfer-Encoding":"quoted-printable","In-Reply-To":"<20251030165816.1095180-7-barnabas.pocze@ideasonboard.com>","References":"<20251030165816.1095180-1-barnabas.pocze@ideasonboard.com>\n\t<20251030165816.1095180-7-barnabas.pocze@ideasonboard.com>","Subject":"Re: [RFC PATCH v3 06/22] libcamera: base: Add alignment utility\n\tfunctions","From":"Kieran Bingham <kieran.bingham@ideasonboard.com>","Cc":"Paul Elder <paul.elder@ideasonboard.com>","To":"=?utf-8?q?Barnab=C3=A1s_P=C5=91cze?= <barnabas.pocze@ideasonboard.com>,\n\tlibcamera-devel@lists.libcamera.org","Date":"Fri, 31 Oct 2025 11:29:00 +0000","Message-ID":"<176191014070.567526.16277348515300263023@ping.linuxembedded.co.uk>","User-Agent":"alot/0.9.1","X-BeenThere":"libcamera-devel@lists.libcamera.org","X-Mailman-Version":"2.1.29","Precedence":"list","List-Id":"<libcamera-devel.lists.libcamera.org>","List-Unsubscribe":"<https://lists.libcamera.org/options/libcamera-devel>,\n\t<mailto:libcamera-devel-request@lists.libcamera.org?subject=unsubscribe>","List-Archive":"<https://lists.libcamera.org/pipermail/libcamera-devel/>","List-Post":"<mailto:libcamera-devel@lists.libcamera.org>","List-Help":"<mailto:libcamera-devel-request@lists.libcamera.org?subject=help>","List-Subscribe":"<https://lists.libcamera.org/listinfo/libcamera-devel>,\n\t<mailto:libcamera-devel-request@lists.libcamera.org?subject=subscribe>","Errors-To":"libcamera-devel-bounces@lists.libcamera.org","Sender":"\"libcamera-devel\" <libcamera-devel-bounces@lists.libcamera.org>"}}]