Skip to content

Commit

Permalink
folly::align_floor, folly::align_ceil (facebook#2269)
Browse files Browse the repository at this point in the history
Summary:
Pull Request resolved: facebook#2269

Simple functions to do alignment for sizes and pointers.
I looked into aligning any unsinged integrals but I believe it's cleaner with just std::size_t.

Differential Revision: D60591352
  • Loading branch information
DenisYaroshevskiy authored and facebook-github-bot committed Aug 1, 2024
1 parent 6b0454f commit 7d2e9e9
Show file tree
Hide file tree
Showing 5 changed files with 144 additions and 0 deletions.
1 change: 1 addition & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -937,6 +937,7 @@ if (BUILD_TESTS OR BUILD_BENCHMARKS)
TEST ssl_errors_test SOURCES SSLErrorsTest.cpp

DIRECTORY lang/test/
TEST lang_align_test SOURCES AlignTest.cpp
TEST lang_aligned_test SOURCES AlignedTest.cpp
TEST lang_badge_test SOURCES BadgeTest.cpp
TEST lang_bits_class_test SOURCES BitsClassTest.cpp
Expand Down
27 changes: 27 additions & 0 deletions folly/lang/Align.h
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@

#pragma once

#include <cassert>
#include <cstddef>
#include <cstdint>

Expand Down Expand Up @@ -179,4 +180,30 @@ constexpr std::size_t cacheline_align_v = has_extended_alignment
: max_align_v;
struct alignas(cacheline_align_v) cacheline_align_t {};

constexpr std::size_t align_floor(std::size_t x, std::size_t alignment) {
assert(alignment > 0);
return x & ~(alignment - 1);
}

template <typename T>
T* align_floor(T* x, std::size_t alignment) {
auto asUint = reinterpret_cast<std::uintptr_t>(x);
static_assert(sizeof(std::uintptr_t) <= sizeof(std::size_t));
asUint = static_cast<std::uintptr_t>(folly::align_floor(asUint, alignment));
return reinterpret_cast<T*>(asUint);
}

constexpr std::size_t align_ceil(std::size_t x, std::size_t alignment) {
assert(alignment > 0);
return (x + alignment - 1) & (-alignment);
}

template <typename T>
T* align_ceil(T* x, std::size_t alignment) {
auto asUint = reinterpret_cast<std::uintptr_t>(x);
static_assert(sizeof(std::uintptr_t) <= sizeof(std::size_t));
asUint = static_cast<std::uintptr_t>(folly::align_ceil(asUint, alignment));
return reinterpret_cast<T*>(asUint);
}

} // namespace folly
8 changes: 8 additions & 0 deletions folly/lang/Bits.h
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,14 @@
* Endian::little(x) little <-> native
* Endian::swap(x) big <-> little
*
* Alignment
* Alignes sizes and pointers. The sizes alignment is constexpr.
* Alinment is specified in bytes.
*
* previousAligned(std::size_t x, std::size_t alignment)
* previousAligned(auto* ptr, std::size_t alignment)
* nextAligned(std::size_t x, std::size_t alignment)
* nextAligned(auto* ptr, std::size_t alignment)
*/

#pragma once
Expand Down
99 changes: 99 additions & 0 deletions folly/lang/test/AlignTest.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* Licensed 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 <folly/lang/Align.h>

#include <folly/portability/GTest.h>

namespace folly {

TEST(Align, AlignFloor) {
static_assert(1 == folly::align_floor(1, 1));
static_assert(2 == folly::align_floor(3, 2));
static_assert(8 == folly::align_floor(9, 8));
static_assert(4096 * 2 == folly::align_floor(4096 * 3 - 1, 4096));

struct Free {
void operator()(void* ptr) { std::free(ptr); }
};

std::unique_ptr<void, Free> alignedUptr(std::aligned_alloc(64, 256));

{
auto* alignedPtr = static_cast<char*>(alignedUptr.get());

EXPECT_EQ(alignedPtr, folly::align_floor(alignedPtr + 15, 16));
EXPECT_EQ(alignedPtr + 8, folly::align_floor(alignedPtr + 15, 8));
EXPECT_EQ(alignedPtr, folly::align_floor(alignedPtr + 8, 64));
}

// not char ptrs
{
auto* alignedPtrInt = static_cast<const int*>(alignedUptr.get());
EXPECT_EQ(alignedPtrInt, folly::align_floor(alignedPtrInt + 2, 16));
EXPECT_EQ(alignedPtrInt + 2, folly::align_floor(alignedPtrInt + 2, 4));
}

// void* ptrs
{
auto* alignedPtr = static_cast<char*>(alignedUptr.get());

EXPECT_EQ(
static_cast<const void*>(alignedPtr + 8),
folly::align_floor(static_cast<const void*>(alignedPtr + 15), 8));
}
}

TEST(Align, AlignCeil) {
static_assert(1 == folly::align_ceil(1, 1));
static_assert(2 == folly::align_ceil(2, 2));
static_assert(4 == folly::align_ceil(3, 2));
static_assert(8 == folly::align_ceil(8, 8));
static_assert(16 == folly::align_ceil(9, 8));
static_assert(4096 * 3 == folly::align_ceil(4096 * 3 - 1, 4096));

struct Free {
void operator()(void* ptr) { std::free(ptr); }
};

std::unique_ptr<void, Free> alignedUptr(std::aligned_alloc(64, 256));

{
auto* alignedPtr = static_cast<char*>(alignedUptr.get());

EXPECT_EQ(alignedPtr, folly::align_ceil(alignedPtr, 16));
EXPECT_EQ(alignedPtr + 16, folly::align_ceil(alignedPtr + 15, 16));
EXPECT_EQ(alignedPtr + 32, folly::align_ceil(alignedPtr + 7, 32));
}

// not char ptrs
{
auto* alignedPtrInt = static_cast<const int*>(alignedUptr.get());
EXPECT_EQ(alignedPtrInt + 4, folly::align_ceil(alignedPtrInt + 2, 16));
EXPECT_EQ(alignedPtrInt + 2, folly::align_ceil(alignedPtrInt + 2, 4));
}

// void* ptrs
{
auto* alignedPtr = static_cast<char*>(alignedUptr.get());

EXPECT_EQ(
static_cast<const void*>(alignedPtr + 16),
folly::align_ceil(static_cast<const void*>(alignedPtr + 15), 8));
}
}

} // namespace folly
9 changes: 9 additions & 0 deletions folly/lang/test/BUCK
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,15 @@ load("@fbcode_macros//build_defs:cpp_unittest.bzl", "cpp_unittest")

oncall("fbcode_entropy_wardens_folly")

cpp_unittest(
name = "align_test",
srcs = ["AlignTest.cpp"],
deps = [
"//folly/lang:align",
"//folly/portability:gtest",
],
)

cpp_unittest(
name = "aligned_test",
srcs = ["AlignedTest.cpp"],
Expand Down

0 comments on commit 7d2e9e9

Please sign in to comment.