// Copyright (c) 2012, the Dart project authors.  Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.

#ifndef RUNTIME_VM_BIT_SET_H_
#define RUNTIME_VM_BIT_SET_H_

#include "platform/utils.h"
#include "vm/globals.h"

namespace dart {

// Just like its namesake in the STL, a BitSet object contains a fixed
// length sequence of bits.
template <intptr_t N>
class BitSet {
 public:
  BitSet() { Reset(); }

  void Set(intptr_t i, bool value) {
    ASSERT(i >= 0);
    ASSERT(i < N);
    uword mask = (static_cast<uword>(1) << (i & (kBitsPerWord - 1)));
    if (value) {
      data_[i >> kBitsPerWordLog2] |= mask;
    } else {
      data_[i >> kBitsPerWordLog2] &= ~mask;
    }
  }

  bool Test(intptr_t i) const {
    ASSERT(i >= 0);
    ASSERT(i < N);
    uword mask = (static_cast<uword>(1) << (i & (kBitsPerWord - 1)));
    return (data_[i >> kBitsPerWordLog2] & mask) != 0;
  }

  intptr_t Next(intptr_t i) const {
    ASSERT(i >= 0);
    ASSERT(i < N);
    intptr_t w = i >> kBitsPerWordLog2;
    uword mask = ~static_cast<uword>(0) << (i & (kBitsPerWord - 1));
    if ((data_[w] & mask) != 0) {
      uword tz = Utils::CountTrailingZerosWord(data_[w] & mask);
      return (w << kBitsPerWordLog2) + tz;
    }
    while (++w < kLengthInWords) {
      if (data_[w] != 0) {
        return (w << kBitsPerWordLog2) +
               Utils::CountTrailingZerosWord(data_[w]);
      }
    }
    return -1;
  }

  intptr_t Last() const {
    for (int w = kLengthInWords - 1; w >= 0; --w) {
      uword d = data_[w];
      if (d != 0) {
        return ((w + 1) << kBitsPerWordLog2) - Utils::CountLeadingZerosWord(d) -
               1;
      }
    }
    return -1;
  }

  intptr_t ClearLastAndFindPrevious(intptr_t current_last) {
    ASSERT(Test(current_last));
    ASSERT(Last() == current_last);
    intptr_t w = current_last >> kBitsPerWordLog2;
    uword bits = data_[w];
    // Clear the current last.
    bits ^= (static_cast<uword>(1) << (current_last & (kBitsPerWord - 1)));
    data_[w] = bits;
    // Search backwards for a non-zero word.
    while (bits == 0 && w > 0) {
      bits = data_[--w];
    }
    if (bits == 0) {
      // None found.
      return -1;
    } else {
      // Bitlength incl. w, minus leading zeroes of w, minus 1 to 0-based index.
      return ((w + 1) << kBitsPerWordLog2) -
             Utils::CountLeadingZerosWord(bits) - 1;
    }
  }

  void Reset() { memset(data_, 0, sizeof(data_)); }

  intptr_t Size() const { return N; }

 private:
  static constexpr int kLengthInWords = 1 + ((N - 1) / kBitsPerWord);
  uword data_[kLengthInWords];
};

}  // namespace dart

#endif  // RUNTIME_VM_BIT_SET_H_
