text: /*
   * Copyright 2019 The libgav1 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 LIBGAV1_SRC_UTILS_MEMORY_H_
  #define LIBGAV1_SRC_UTILS_MEMORY_H_
  
  #if defined(__ANDROID__) || defined(_MSC_VER) || defined(__MINGW32__)
  #include <malloc.h>
  #endif
  
  #include <cerrno>
  #include <cstddef>
  #include <cstdint>
  #include <cstdlib>
  #include <cstring>
  #include <memory>
  #include <new>
  
  namespace libgav1 {
  
  enum {
  // The byte alignment required for buffers used with SIMD code to be read or
  // written with aligned operations.
  #if defined(__i386__) || defined(_M_IX86) || defined(__x86_64__) || \
      defined(_M_X64)
    kMaxAlignment = 32,  // extended alignment is safe on x86.
  #else
    kMaxAlignment = alignof(max_align_t),
  #endif
  };
  
  // AlignedAlloc, AlignedFree
  //
  // void* AlignedAlloc(size_t alignment, size_t size);
  //   Allocate aligned memory.
  //   |alignment| must be a power of 2.
  //   Unlike posix_memalign(), |alignment| may be smaller than sizeof(void*).
  //   Unlike aligned_alloc(), |size| does not need to be a multiple of
  //   |alignment|.
  //   The returned pointer should be freed by AlignedFree().
  //
  // void AlignedFree(void* aligned_memory);
  //   Free aligned memory.
  
  #if defined(_MSC_VER) || defined(__MINGW32__)
  
  inline void* AlignedAlloc(size_t alignment, size_t size) {
    return _aligned_malloc(size, alignment);
  }
  
  inline void AlignedFree(void* aligned_memory) { _aligned_free(aligned_memory); }
  
  #else  // !(defined(_MSC_VER) || defined(__MINGW32__))
  
  inline void* AlignedAlloc(size_t alignment, size_t size) {
  #if defined(__ANDROID__)
    // Although posix_memalign() was introduced in Android API level 17, it is
    // more convenient to use memalign(). Unlike glibc, Android does not consider
    // memalign() an obsolete function.
    return memalign(alignment, size);
  #else   // !defined(__ANDROID__)
    void* ptr = nullptr;
    // posix_memalign requires that the requested alignment be at least
    // sizeof(void*). In this case, fall back on malloc which should return
    // memory aligned to at least the size of a pointer.
    const size_t required_alignment = sizeof(void*);
    if (alignment < required_alignment) return malloc(size);
    const int error = posix_memalign(&ptr, alignment, size);
    if (error != 0) {
      errno = error;
      return nullptr;
    }
    return ptr;
  #endif  // defined(__ANDROID__)
  }
  
  inline void AlignedFree(void* aligned_memory) { free(aligned_memory); }
  
  #endif  // defined(_MSC_VER) || defined(__MINGW32__)
  
  inline void Memset(uint8_t* const dst, int value, size_t count) {
    memset(dst, value, count);
  }
  
  inline void Memset(uint16_t* const dst, int value, size_t count) {
    for (size_t i = 0; i < count; ++i) {
      dst[i] = static_cast<uint16_t>(value);
    }
  }
  
  inline void Memset(int16_t* const dst, int value, size_t count) {
    for (size_t i = 0; i < count; ++i) {
      dst[i] = static_cast<int16_t>(value);
    }
  }
  
  struct MallocDeleter {
    void operator()(void* ptr) const { free(ptr); }
  };
  
  struct AlignedDeleter {
    void operator()(void* ptr) const { AlignedFree(ptr); }
  };
  
  template <typename T>
  using AlignedUniquePtr = std::unique_ptr<T, AlignedDeleter>;
  
  // Allocates aligned memory for an array of |count| elements of type T.
  template <typename T>
  inline AlignedUniquePtr<T> MakeAlignedUniquePtr(size_t alignment,
                                                  size_t count) {
    return AlignedUniquePtr<T>(
        static_cast<T*>(AlignedAlloc(alignment, count * sizeof(T))));
  }
  
  // A base class with custom new and delete operators. The exception-throwing
  // new operators are deleted. The "new (std::nothrow)" form must be used.
  //
  // The new operators return nullptr if the requested size is greater than
  // 0x40000000 bytes (1 GB). TODO(wtc): Make the maximum allocable memory size
  // a compile-time configuration macro.
  //
  // See https://en.cppreference.com/w/cpp/memory/new/operator_new and
  // https://en.cppreference.com/w/cpp/memory/new/operator_delete.
  //
  // NOTE: The allocation and deallocation functions are static member functions
  // whether the keyword 'static' is used or not.
  struct Allocable {
    // Class-specific allocation functions.
    static void* operator new(size_t size) = delete;
    static void* operator new[](size_t size) = delete;
  
    // Class-specific non-throwing allocation functions
    static void* operator new(size_t size, const std::nothrow_t& tag) noexcept {
      if (size > 0x40000000) return nullptr;
      return ::operator new(size, tag);
    }
    static void* operator new[](size_t size, const std::nothrow_t& tag) noexcept {
      if (size > 0x40000000) return nullptr;
      return ::operator new[](size, tag);
    }
  
    // Class-specific deallocation functions.
    static void operator delete(void* ptr) noexcept { ::operator delete(ptr); }
    static void operator delete[](void* ptr) noexcept {
      ::operator delete[](ptr);
    }
  
    // Only called if new (std::nothrow) is used and the constructor throws an
    // exception.
    static void operator delete(void* ptr, const std::nothrow_t& tag) noexcept {
      ::operator delete(ptr, tag);
    }
    // Only called if new[] (std::nothrow) is used and the constructor throws an
    // exception.
    static void operator delete[](void* ptr, const std::nothrow_t& tag) noexcept {
      ::operator delete[](ptr, tag);
    }
  };
  
  // A variant of Allocable that forces allocations to be aligned to
  // kMaxAlignment bytes. This is intended for use with classes that use
  // alignas() with this value. C++17 aligned new/delete are used if available,
  // otherwise we use AlignedAlloc/Free.
  struct MaxAlignedAllocable {
    // Class-specific allocation functions.
    static void* operator new(size_t size) = delete;
    static void* operator new[](size_t size) = delete;
  
    // Class-specific non-throwing allocation functions
    static void* operator new(size_t size, const std::nothrow_t& tag) noexcept {
      if (size > 0x40000000) return nullptr;
  #ifdef __cpp_aligned_new
      return ::operator new(size, std::align_val_t(kMaxAlignment), tag);
  #else
      static_cast<void>(tag);
      return AlignedAlloc(kMaxAlignment, size);
  #endif
    }
    static void* operator new[](size_t size, const std::nothrow_t& tag) noexcept {
      if (size > 0x40000000) return nullptr;
  #ifdef __cpp_aligned_new
      return ::operator new[](size, std::align_val_t(kMaxAlignment), tag);
  #else
      static_cast<void>(tag);
      return AlignedAlloc(kMaxAlignment, size);
  #endif
    }
  
    // Class-specific deallocation functions.
    static void operator delete(void* ptr) noexcept {
  #ifdef __cpp_aligned_new
      ::operator delete(ptr, std::align_val_t(kMaxAlignment));
  #else
      AlignedFree(ptr);
  #endif
    }
    static void operator delete[](void* ptr) noexcept {
  #ifdef __cpp_aligned_new
      ::operator delete[](ptr, std::align_val_t(kMaxAlignment));
  #else
      AlignedFree(ptr);
  #endif
    }
  
    // Only called if new (std::nothrow) is used and the constructor throws an
    // exception.
    static void operator delete(void* ptr, const std::nothrow_t& tag) noexcept {
  #ifdef __cpp_aligned_new
      ::operator delete(ptr, std::align_val_t(kMaxAlignment), tag);
  #else
      static_cast<void>(tag);
      AlignedFree(ptr);
  #endif
    }
    // Only called if new[] (std::nothrow) is used and the constructor throws an
    // exception.
    static void operator delete[](void* ptr, const std::nothrow_t& tag) noexcept {
  #ifdef __cpp_aligned_new
      ::operator delete[](ptr, std::align_val_t(kMaxAlignment), tag);
  #else
      static_cast<void>(tag);
      AlignedFree(ptr);
  #endif
    }
  };
  
  }  // namespace libgav1
  
  #endif  // LIBGAV1_SRC_UTILS_MEMORY_H_
  