| //===----------------------------------------------------------------------===// | |
| // | |
| // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. | |
| // See https://llvm.org/LICENSE.txt for license information. | |
| // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception | |
| // | |
| //===----------------------------------------------------------------------===// | |
| /// | |
| /// \file | |
| /// Growable byte buffer with explicit lifetime management. | |
| /// | |
| //===----------------------------------------------------------------------===// | |
| #ifndef LLVM_LIBC_SRC___SUPPORT_PWD_DYNAMIC_BUFFER_H | |
| #define LLVM_LIBC_SRC___SUPPORT_PWD_DYNAMIC_BUFFER_H | |
| #include "hdr/func/free.h" | |
| #include "hdr/func/realloc.h" | |
| #include "hdr/types/size_t.h" | |
| #include "src/__support/CPP/limits.h" | |
| #include "src/__support/CPP/span.h" | |
| #include "src/__support/CPP/type_traits/is_trivially_destructible.h" | |
| #include "src/__support/macros/attributes.h" | |
| #include "src/__support/macros/config.h" | |
| namespace LIBC_NAMESPACE_DECL { | |
| namespace pwd { | |
| // A heap-backed, growable byte buffer for the flat-file database engine. | |
| // | |
| // This type is trivially destructible by design, so an instance can be a | |
| // constant-initialised process global. The owner releases the storage | |
| // explicitly when done. | |
| class DynamicBuffer { | |
| static constexpr size_t INITIAL_CAPACITY = 1024; | |
| char *ptr = nullptr; | |
| size_t cap = 0; | |
| public: | |
| LIBC_INLINE constexpr DynamicBuffer() = default; | |
| DynamicBuffer(const DynamicBuffer &) = delete; | |
| DynamicBuffer &operator=(const DynamicBuffer &) = delete; | |
| // Grows the buffer to hold at least new_capacity bytes, preserving the | |
| // existing contents. Capacity doubles so that repeated growth stays linear | |
| // in the number of bytes read. Returns false if allocation failed, in which | |
| // case the buffer is left untouched. | |
| [[nodiscard]] LIBC_INLINE bool reserve(size_t new_capacity) { | |
| if (new_capacity <= cap) | |
| return true; | |
| size_t next = cap == 0 ? INITIAL_CAPACITY : cap; | |
| while (next < new_capacity) { | |
| if (next > cpp::numeric_limits<size_t>::max() / 2) | |
| return false; | |
| next *= 2; | |
| } | |
| void *new_ptr = ::realloc(ptr, next); | |
| if (new_ptr == nullptr) | |
| return false; | |
| ptr = static_cast<char *>(new_ptr); | |
| cap = next; | |
| return true; | |
| } | |
| // Doubles the current capacity, or allocates the initial capacity when the | |
| // buffer is empty. Returns false if allocation failed. | |
| [[nodiscard]] LIBC_INLINE bool grow() { | |
| if (cap == cpp::numeric_limits<size_t>::max()) | |
| return false; | |
| return reserve(cap == 0 ? INITIAL_CAPACITY : cap + 1); | |
| } | |
| // Frees the storage and returns the buffer to its empty state. Safe to call | |
| // more than once. | |
| LIBC_INLINE void release() { | |
| ::free(ptr); | |
| ptr = nullptr; | |
| cap = 0; | |
| } | |
| [[nodiscard]] LIBC_INLINE cpp::span<char> span() const { return {ptr, cap}; } | |
| [[nodiscard]] LIBC_INLINE size_t capacity() const { return cap; } | |
| }; | |
| static_assert(cpp::is_trivially_destructible<DynamicBuffer>::value, | |
| "DynamicBuffer must be trivially destructible"); | |
| // RAII wrapper around DynamicBuffer for stack-local buffers. | |
| class ScopedDynamicBuffer : public DynamicBuffer { | |
| public: | |
| using DynamicBuffer::DynamicBuffer; | |
| LIBC_INLINE ~ScopedDynamicBuffer() { this->release(); } | |
| }; | |
| } // namespace pwd | |
| } // namespace LIBC_NAMESPACE_DECL | |
| #endif // LLVM_LIBC_SRC___SUPPORT_PWD_DYNAMIC_BUFFER_H |