/*
 * Copyright (C) 2016 The Android Open Source Project
 *
 * 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 _GTS_NANOAPPS_SHARED_DUMB_ALLOCATOR_H_
#define _GTS_NANOAPPS_SHARED_DUMB_ALLOCATOR_H_

#include <cstddef>
#include <cstdint>

namespace nanoapp_testing {

// Implementation Note: We chose the pattern of having DumbAllocatorBase to
// reduce the code duplication from multiple instances of DumbAllocator with
// different template parameters.
// See DumbAllocator below for usage and API documentation.
class DumbAllocatorBase {
 protected:
  DumbAllocatorBase(size_t allocSize, size_t slotCount, uint8_t *rawMemory);

  void *alloc(size_t bytes);
  bool free(void *ptr);
  bool contains(const void *ptr) const;

  static constexpr size_t MaxSlotCount() {
    // Our mAllocatedSlots is treated as a bit array, so we get 8 slots for
    // each byte it has.
    return (sizeof(mAllocatedSlots) * 8);
  }

 private:
  const size_t mAllocSize;
  const size_t mSlotCount;
  uint8_t * const mRawMemory;
  uint32_t mAllocatedSlots;

  bool getSlot(const void *ptr, size_t *slot) const;
};


/**
 * This dumb allocator is designed to allow us to easily get chunks of
 * memory without needing to go through heap allocation.  The idea is to
 * reduce our dependency on CHRE for some aspects of our tests.
 *
 * This allocator is non-reentrant.  It's also inefficient and a bad idea
 * for shipping code, but useful for reducing dependencies during testing.
 *
 * This will allow up to kSlotCount allocations of up to kAllocSize bytes
 * each, and costs (kSlotCount * kAllocSize) bytes of underlying storage.
 */
template<size_t kAllocSize, size_t kSlotCount>
class DumbAllocator : DumbAllocatorBase {
 public:
  DumbAllocator()
      : DumbAllocatorBase(kAllocSize, kSlotCount, mRawMemoryArray) {}

  /**
   * If "bytes" <= kAllocSize, and there are less than kSlotCount allocations,
   * return a valid pointer.  Otherwise, nullptr.
   *
   * Reminder this is non-reentrant.
   */
  void *alloc(size_t bytes) {
    return DumbAllocatorBase::alloc(bytes);
  }

  /**
   * If contains(ptr) is true, free the allocation and return true.
   * Otherwise, do nothing and return false.
   *
   * Reminder this is non-reentrant.
   */
  bool free(void *ptr) {
    return DumbAllocatorBase::free(ptr);
  }

  /**
   * If "ptr" was a non-null pointer returned from alloc() on this instance,
   * return true.  Otherwise, do nothing and return false.
   */
  bool contains(const void *ptr) const {
    return DumbAllocatorBase::contains(ptr);
  }

 private:
  uint8_t mRawMemoryArray[kAllocSize * kSlotCount];

  static_assert(kSlotCount <= MaxSlotCount(), "kSlotCount is too high");
};


}  // namespace nanoapp_testing

#endif  // _GTS_NANOAPPS_SHARED_DUMB_ALLOCATOR_H_