// Copyright 2014 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include <algorithm>

#include "src/compiler/zone-stats.h"

namespace v8 {
namespace internal {
namespace compiler {

ZoneStats::StatsScope::StatsScope(ZoneStats* zone_stats)
    : zone_stats_(zone_stats),
      total_allocated_bytes_at_start_(zone_stats->GetTotalAllocatedBytes()),
      max_allocated_bytes_(0) {
  zone_stats_->stats_.push_back(this);
  for (Zone* zone : zone_stats_->zones_) {
    size_t size = static_cast<size_t>(zone->allocation_size());
    std::pair<InitialValues::iterator, bool> res =
        initial_values_.insert(std::make_pair(zone, size));
    USE(res);
    DCHECK(res.second);
  }
}

ZoneStats::StatsScope::~StatsScope() {
  DCHECK_EQ(zone_stats_->stats_.back(), this);
  zone_stats_->stats_.pop_back();
}

size_t ZoneStats::StatsScope::GetMaxAllocatedBytes() {
  return std::max(max_allocated_bytes_, GetCurrentAllocatedBytes());
}

size_t ZoneStats::StatsScope::GetCurrentAllocatedBytes() {
  size_t total = 0;
  for (Zone* zone : zone_stats_->zones_) {
    total += static_cast<size_t>(zone->allocation_size());
    // Adjust for initial values.
    InitialValues::iterator it = initial_values_.find(zone);
    if (it != initial_values_.end()) {
      total -= it->second;
    }
  }
  return total;
}

size_t ZoneStats::StatsScope::GetTotalAllocatedBytes() {
  return zone_stats_->GetTotalAllocatedBytes() -
         total_allocated_bytes_at_start_;
}

void ZoneStats::StatsScope::ZoneReturned(Zone* zone) {
  size_t current_total = GetCurrentAllocatedBytes();
  // Update max.
  max_allocated_bytes_ = std::max(max_allocated_bytes_, current_total);
  // Drop zone from initial value map.
  InitialValues::iterator it = initial_values_.find(zone);
  if (it != initial_values_.end()) {
    initial_values_.erase(it);
  }
}

ZoneStats::ZoneStats(AccountingAllocator* allocator)
    : max_allocated_bytes_(0), total_deleted_bytes_(0), allocator_(allocator) {}

ZoneStats::~ZoneStats() {
  DCHECK(zones_.empty());
  DCHECK(stats_.empty());
}

size_t ZoneStats::GetMaxAllocatedBytes() const {
  return std::max(max_allocated_bytes_, GetCurrentAllocatedBytes());
}

size_t ZoneStats::GetCurrentAllocatedBytes() const {
  size_t total = 0;
  for (Zone* zone : zones_) {
    total += static_cast<size_t>(zone->allocation_size());
  }
  return total;
}

size_t ZoneStats::GetTotalAllocatedBytes() const {
  return total_deleted_bytes_ + GetCurrentAllocatedBytes();
}

Zone* ZoneStats::NewEmptyZone(const char* zone_name,
                              bool support_zone_compression) {
  Zone* zone = new Zone(allocator_, zone_name, support_zone_compression);
  zones_.push_back(zone);
  return zone;
}

void ZoneStats::ReturnZone(Zone* zone) {
  size_t current_total = GetCurrentAllocatedBytes();
  // Update max.
  max_allocated_bytes_ = std::max(max_allocated_bytes_, current_total);
  // Update stats.
  for (StatsScope* stat_scope : stats_) {
    stat_scope->ZoneReturned(zone);
  }
  // Remove from used.
  Zones::iterator it = std::find(zones_.begin(), zones_.end(), zone);
  DCHECK(it != zones_.end());
  zones_.erase(it);
  total_deleted_bytes_ += static_cast<size_t>(zone->allocation_size());
  delete zone;
}

}  // namespace compiler
}  // namespace internal
}  // namespace v8