/*
 * 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.
 */

#include <shared/dumb_allocator.h>

#include <shared/nano_string.h>

namespace nanoapp_testing {

DumbAllocatorBase::DumbAllocatorBase(size_t allocSize, size_t slotCount,
                                     uint8_t *rawMemory)
    : mAllocSize(allocSize),
      mSlotCount(slotCount),
      mRawMemory(rawMemory),
      mAllocatedSlots(0) {
  // We're not worried about runtime efficiency, since this is testing
  // code.  In case of an issue within the tests, though, we do want
  // to have consistent behavior.  Thus, we initialize this memory to
  // aid tracking problems.
  memset(mRawMemory, 0xCD, mSlotCount * mAllocSize);
}

void *DumbAllocatorBase::alloc(size_t bytes) {
  if (bytes > mAllocSize) {
    // Oversized for our allocator.
    return nullptr;
  }
  size_t slot = 0;
  for (uint32_t mask = 1; slot < mSlotCount; slot++, mask <<= 1) {
    if ((mAllocatedSlots & mask) == 0) {
      // This space is available, let's claim it.
      mAllocatedSlots |= mask;
      break;
    }
  }
  if (slot == mSlotCount) {
    // We're out of space.
    return nullptr;
  }
  return mRawMemory + (slot * mAllocSize);
}

bool DumbAllocatorBase::free(void *pointer) {
  size_t slot;
  if (!getSlot(pointer, &slot)) {
    return false;
  }
  mAllocatedSlots &= ~(1 << slot);
  return true;
}

bool DumbAllocatorBase::contains(const void *pointer) const {
  size_t slot;
  return getSlot(pointer, &slot);
}

bool DumbAllocatorBase::getSlot(const void *pointer, size_t *slot) const {
  const uint8_t *ptr = static_cast<const uint8_t *>(pointer);
  if (ptr < mRawMemory) {
    // Out of range.
    return false;
  }
  *slot = static_cast<size_t>(ptr - mRawMemory) / mAllocSize;
  if (*slot >= mSlotCount) {
    // Out of range.
    return false;
  }
  // Also confirm alignment.
  return ((mRawMemory + (*slot * mAllocSize)) == ptr);
}


}  // namespace nanoapp_testing