diff --git a/src/libutil-tests/alignment.cc b/src/libutil-tests/alignment.cc new file mode 100644 index 000000000..bef0c435d --- /dev/null +++ b/src/libutil-tests/alignment.cc @@ -0,0 +1,18 @@ +#include "nix/util/alignment.hh" + +#include + +namespace nix { + +TEST(alignUp, value) +{ + for (uint64_t i = 1; i <= 8; ++i) + EXPECT_EQ(alignUp(i, 8), 8); +} + +TEST(alignUp, notAPowerOf2) +{ + ASSERT_DEATH({ alignUp(1u, 42); }, "alignment must be a power of 2"); +} + +} // namespace nix diff --git a/src/libutil-tests/meson.build b/src/libutil-tests/meson.build index d84dbbb68..c75f4d90a 100644 --- a/src/libutil-tests/meson.build +++ b/src/libutil-tests/meson.build @@ -44,6 +44,7 @@ config_priv_h = configure_file( subdir('nix-meson-build-support/common') sources = files( + 'alignment.cc', 'archive.cc', 'args.cc', 'base-n.cc', diff --git a/src/libutil/archive.cc b/src/libutil/archive.cc index b8fef9ef3..73ec0cab7 100644 --- a/src/libutil/archive.cc +++ b/src/libutil/archive.cc @@ -6,6 +6,7 @@ #include // for strcasecmp #include "nix/util/archive.hh" +#include "nix/util/alignment.hh" #include "nix/util/config-global.hh" #include "nix/util/posix-source-accessor.hh" #include "nix/util/source-path.hh" @@ -133,7 +134,7 @@ static void parseContents(CreateRegularFileSink & sink, Source & source) sink.preallocateContents(size); if (sink.skipContents) { - source.skip(size + (size % 8 ? 8 - (size % 8) : 0)); + source.skip(alignUp(size, 8)); return; } diff --git a/src/libutil/include/nix/util/alignment.hh b/src/libutil/include/nix/util/alignment.hh new file mode 100644 index 000000000..a4e5af4d6 --- /dev/null +++ b/src/libutil/include/nix/util/alignment.hh @@ -0,0 +1,23 @@ +#pragma once +///@file + +#include +#include +#include +#include + +namespace nix { + +/// Aligns val upwards to be a multiple of alignment. +/// +/// @pre alignment must be a power of 2. +template + requires std::is_unsigned_v +constexpr T alignUp(T val, unsigned alignment) +{ + assert(std::has_single_bit(alignment) && "alignment must be a power of 2"); + T mask = ~(T{alignment} - 1u); + return (val + alignment - 1) & mask; +} + +} // namespace nix diff --git a/src/libutil/include/nix/util/meson.build b/src/libutil/include/nix/util/meson.build index dcfaa8e3f..9a606e15d 100644 --- a/src/libutil/include/nix/util/meson.build +++ b/src/libutil/include/nix/util/meson.build @@ -4,6 +4,7 @@ include_dirs = [ include_directories('../..') ] headers = files( 'abstract-setting-to-json.hh', + 'alignment.hh', 'ansicolor.hh', 'archive.hh', 'args.hh',