Add a bump allocator.

This CL adds a simple allocator which will provide a chunk of memory of
the given size. It allocates out of slabs of memory.

Change-Id: I9acf59fac88cd6bef260b7ebae7d7b77fd939754
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/127302
Reviewed-by: Ben Clayton <bclayton@google.com>
Kokoro: Kokoro <noreply+kokoro@google.com>
Commit-Queue: Dan Sinclair <dsinclair@chromium.org>
diff --git a/src/tint/BUILD.gn b/src/tint/BUILD.gn
index 1e084a2..5240dd2 100644
--- a/src/tint/BUILD.gn
+++ b/src/tint/BUILD.gn
@@ -222,6 +222,7 @@
     "utils/bitcast.h",
     "utils/bitset.h",
     "utils/block_allocator.h",
+    "utils/bump_allocator.h",
     "utils/compiler_macros.h",
     "utils/concat.h",
     "utils/crc32.h",
@@ -1588,6 +1589,7 @@
       "utils/bitcast_test.cc",
       "utils/bitset_test.cc",
       "utils/block_allocator_test.cc",
+      "utils/bump_allocator_test.cc",
       "utils/crc32_test.cc",
       "utils/defer_test.cc",
       "utils/enum_set_test.cc",
diff --git a/src/tint/CMakeLists.txt b/src/tint/CMakeLists.txt
index 4dd1c88..6c7acfb 100644
--- a/src/tint/CMakeLists.txt
+++ b/src/tint/CMakeLists.txt
@@ -517,6 +517,7 @@
   utils/bitcast.h
   utils/bitset.h
   utils/block_allocator.h
+  utils/bump_allocator.h
   utils/compiler_macros.h
   utils/concat.h
   utils/crc32.h
@@ -998,6 +999,7 @@
     utils/bitcast_test.cc
     utils/bitset_test.cc
     utils/block_allocator_test.cc
+    utils/bump_allocator_test.cc
     utils/crc32_test.cc
     utils/defer_test.cc
     utils/enum_set_test.cc
diff --git a/src/tint/utils/bump_allocator.h b/src/tint/utils/bump_allocator.h
new file mode 100644
index 0000000..302f924
--- /dev/null
+++ b/src/tint/utils/bump_allocator.h
@@ -0,0 +1,127 @@
+// Copyright 2023 The Tint Authors.
+//
+// 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.
+
+#ifndef SRC_TINT_UTILS_BUMP_ALLOCATOR_H_
+#define SRC_TINT_UTILS_BUMP_ALLOCATOR_H_
+
+#include <array>
+#include <cstring>
+#include <utility>
+
+#include "src/tint/utils/bitcast.h"
+#include "src/tint/utils/math.h"
+
+namespace tint::utils {
+
+constexpr size_t kBlockSize = 64 * 1024;
+
+/// A allocator for chunks of memory. The memory is owned by the BumpAllocator. When the
+/// BumpAllocator is freed all of the allocated memory is freed.
+class BumpAllocator {
+    /// Block is linked list of memory blocks.
+    /// Blocks are allocated out of heap memory.
+    struct Block {
+        uint8_t data[kBlockSize];
+        Block* next;
+    };
+
+  public:
+    /// Constructor
+    BumpAllocator() = default;
+
+    /// Move constructor
+    /// @param rhs the BumpAllocator to move
+    BumpAllocator(BumpAllocator&& rhs) { std::swap(data, rhs.data); }
+
+    /// Move assignment operator
+    /// @param rhs the BumpAllocator to move
+    /// @return this BumpAllocator
+    BumpAllocator& operator=(BumpAllocator&& rhs) {
+        if (this != &rhs) {
+            Reset();
+            std::swap(data, rhs.data);
+        }
+        return *this;
+    }
+
+    /// Destructor
+    ~BumpAllocator() { Reset(); }
+
+    /// Allocates @p size_in_bytes from the current block, or from a newly allocated block if the
+    /// current block is full.
+    /// @param size_in_bytes the number of bytes to allocate
+    /// @returns the pointer to the allocated memory or |nullptr| if the memory can not be allocated
+    char* Allocate(size_t size_in_bytes) {
+        auto& block = data.block;
+        if (block.current_offset + size_in_bytes > kBlockSize) {
+            // Allocate a new block from the heap
+            auto* prev_block = block.current;
+            block.current = new Block;
+            if (!block.current) {
+                return nullptr;  // out of memory
+            }
+            block.current->next = nullptr;
+            block.current_offset = 0;
+            if (prev_block) {
+                prev_block->next = block.current;
+            } else {
+                block.root = block.current;
+            }
+        }
+
+        auto* base = &block.current->data[0];
+        auto* ptr = reinterpret_cast<char*>(base + block.current_offset);
+        block.current_offset += size_in_bytes;
+        data.count++;
+        return ptr;
+    }
+
+    /// Frees all allocations from the allocator.
+    void Reset() {
+        auto* block = data.block.root;
+        while (block != nullptr) {
+            auto* next = block->next;
+            delete block;
+            block = next;
+        }
+        data = {};
+    }
+
+    /// @returns the total number of allocations
+    size_t Count() const { return data.count; }
+
+  private:
+    BumpAllocator(const BumpAllocator&) = delete;
+    BumpAllocator& operator=(const BumpAllocator&) = delete;
+
+    struct {
+        struct {
+            /// The root block of the block linked list
+            Block* root = nullptr;
+            /// The current (end) block of the blocked linked list.
+            /// New allocations come from this block
+            Block* current = nullptr;
+            /// The byte offset in #current for the next allocation.
+            /// Initialized with kBlockSize so that the first allocation triggers a block
+            /// allocation.
+            size_t current_offset = kBlockSize;
+        } block;
+
+        size_t count = 0;
+    } data;
+};
+
+}  // namespace tint::utils
+
+#endif  // SRC_TINT_UTILS_BUMP_ALLOCATOR_H_
diff --git a/src/tint/utils/bump_allocator_test.cc b/src/tint/utils/bump_allocator_test.cc
new file mode 100644
index 0000000..4f22455
--- /dev/null
+++ b/src/tint/utils/bump_allocator_test.cc
@@ -0,0 +1,49 @@
+// Copyright 2023 The Tint Authors.
+//
+// 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 "src/tint/utils/bump_allocator.h"
+
+#include "gtest/gtest.h"
+
+namespace tint::utils {
+namespace {
+
+using BumpAllocatorTest = testing::Test;
+
+TEST_F(BumpAllocatorTest, Count) {
+    for (size_t n : {0u, 1u, 10u, 16u, 20u, 32u, 50u, 64u, 100u, 256u, 300u, 512u, 500u, 512u}) {
+        BumpAllocator allocator;
+        EXPECT_EQ(allocator.Count(), 0u);
+        for (size_t i = 0; i < n; i++) {
+            allocator.Allocate(5);
+        }
+        EXPECT_EQ(allocator.Count(), n);
+    }
+}
+
+TEST_F(BumpAllocatorTest, MoveConstruct) {
+    for (size_t n : {0u, 1u, 10u, 16u, 20u, 32u, 50u, 64u, 100u, 256u, 300u, 512u, 500u, 512u}) {
+        BumpAllocator allocator_a;
+        for (size_t i = 0; i < n; i++) {
+            allocator_a.Allocate(5);
+        }
+        EXPECT_EQ(allocator_a.Count(), n);
+
+        BumpAllocator allocator_b{std::move(allocator_a)};
+        EXPECT_EQ(allocator_b.Count(), n);
+    }
+}
+
+}  // namespace
+}  // namespace tint::utils