Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 83535125 authored by Dmitri Plotnikov's avatar Dmitri Plotnikov Committed by Android (Google) Code Review
Browse files

Merge "A uint64 array wrapper for optimized allocation and copying" into main

parents 87058307 0a6dc401
Loading
Loading
Loading
Loading
+110 −34
Original line number Diff line number Diff line
@@ -21,58 +21,134 @@
namespace android {
namespace battery {

template <>
bool LongArrayMultiStateCounter::delta(const std::vector<uint64_t>& previousValue,
                                       const std::vector<uint64_t>& newValue,
                                       std::vector<uint64_t>* outValue) const {
    size_t size = previousValue.size();
    if (newValue.size() != size) {
        ALOGE("Incorrect array size: %d, should be %d", (int)newValue.size(), (int)size);
        return false;
Uint64ArrayRW::Uint64ArrayRW(const Uint64Array &copy) : Uint64Array(copy.size()) {
    if (mSize != 0 && copy.data() != nullptr) {
        mData = new uint64_t[mSize];
        memcpy(mData, copy.data(), mSize * sizeof(uint64_t));
    } else {
        mData = nullptr;
    }
}

    bool is_delta_valid = true;
    for (int i = size - 1; i >= 0; i--) {
        if (newValue[i] >= previousValue[i]) {
            (*outValue)[i] = newValue[i] - previousValue[i];
uint64_t *Uint64ArrayRW::dataRW() {
    if (mData == nullptr) {
        mData = new uint64_t[mSize];
        memset(mData, 0, mSize * sizeof(uint64_t));
    }
    return mData;
}

Uint64ArrayRW &Uint64ArrayRW::operator=(const Uint64Array &t) {
    if (t.size() != mSize) {
        delete[] mData;
        mSize = t.size();
        mData = nullptr;
    }
    if (mSize != 0) {
        if (t.data() != nullptr) {
            mData = new uint64_t[mSize];
            memcpy(mData, t.data(), mSize * sizeof(uint64_t));
        } else {
            (*outValue)[i] = 0;
            is_delta_valid = false;
            mData = nullptr;
        }
    }
    return is_delta_valid;
    return *this;
}

std::ostream &operator<<(std::ostream &os, const Uint64Array &v) {
    os << "{";
    const uint64_t *data = v.data();
    if (data != nullptr) {
        bool first = true;
        for (size_t i = 0; i < v.size(); i++) {
            if (!first) {
                os << ", ";
            }
            os << data[i];
            first = false;
        }
    }
    os << "}";
    return os;
}

// Convenience constructor for tests
Uint64ArrayRW::Uint64ArrayRW(std::initializer_list<uint64_t> init) : Uint64Array(init.size()) {
    mData = new uint64_t[mSize];
    memcpy(mData, init.begin(), mSize * sizeof(uint64_t));
}

// Used in tests only.
bool Uint64Array::operator==(const Uint64Array &other) const {
    if (size() != other.size()) {
        return false;
    }
    const uint64_t* thisData = data();
    const uint64_t* thatData = other.data();
    for (size_t i = 0; i < mSize; i++) {
        const uint64_t v1 = thisData != nullptr ? thisData[i] : 0;
        const uint64_t v2 = thatData != nullptr ? thatData[i] : 0;
        if (v1 != v2) {
            return false;
        }
    }
    return true;
}

template <>
void LongArrayMultiStateCounter::add(std::vector<uint64_t>* value1,
                                     const std::vector<uint64_t>& value2, const uint64_t numerator,
                                     const uint64_t denominator) const {
void LongArrayMultiStateCounter::add(Uint64ArrayRW *value1, const Uint64Array &value2,
                                     const uint64_t numerator, const uint64_t denominator) const {
    const uint64_t* data2 = value2.data();
    if (data2 == nullptr) {
        return;
    }

    uint64_t* data1 = value1->dataRW();
    size_t size = value2.size();
    if (numerator != denominator) {
        for (int i = value2.size() - 1; i >= 0; i--) {
        for (size_t i = 0; i < size; i++) {
            // The caller ensures that denominator != 0
            (*value1)[i] += value2[i] * numerator / denominator;
            data1[i] += data2[i] * numerator / denominator;
        }
    } else {
        for (int i = value2.size() - 1; i >= 0; i--) {
            (*value1)[i] += value2[i];
        for (size_t i = 0; i < size; i++) {
            data1[i] += data2[i];
        }
    }
}

template<>
std::string LongArrayMultiStateCounter::valueToString(const std::vector<uint64_t>& v) const {
    std::stringstream s;
    s << "{";
    bool first = true;
    for (uint64_t n : v) {
        if (!first) {
            s << ", ";
bool LongArrayMultiStateCounter::delta(const Uint64ArrayRW &previousValue,
                                       const Uint64Array &newValue, Uint64ArrayRW *outValue) const {
    size_t size = previousValue.size();
    if (newValue.size() != size) {
        ALOGE("Incorrect array size: %d, should be %d", (int) newValue.size(), (int) size);
        return false;
    }
    if (outValue->size() != size) {
        ALOGE("Incorrect outValue size: %d, should be %d", (int) outValue->size(), (int) size);
        return false;
    }

    bool is_delta_valid = true;
    const uint64_t *prevData = previousValue.data();
    const uint64_t *newData = newValue.data();
    uint64_t *outData = outValue->dataRW();
    for (size_t i = 0; i < size; i++) {
        if (prevData == nullptr) {
            if (newData == nullptr) {
                outData[i] = 0;
            } else {
                outData[i] = newData[i];
            }
        } else if (newData == nullptr || newData[i] < prevData[i]) {
            outData[i] = 0;
            is_delta_valid = false;
        } else {
            outData[i] = newData[i] - prevData[i];
        }
        s << n;
        first = false;
    }
    s << "}";
    return s.str();
    return is_delta_valid;
}

} // namespace battery
+60 −1
Original line number Diff line number Diff line
@@ -23,7 +23,66 @@
namespace android {
namespace battery {

typedef MultiStateCounter<std::vector<uint64_t>> LongArrayMultiStateCounter;
/**
 * Wrapper for an array of uint64's.
 */
class Uint64Array {
  protected:
    size_t mSize;

  public:
    Uint64Array() : Uint64Array(0) {}

    Uint64Array(size_t size) : mSize(size) {}

    virtual ~Uint64Array() {}

    size_t size() const { return mSize; }

    /**
     * Returns the wrapped array.
     *
     * Nullable! Null should be interpreted the same as an array of zeros
     */
    virtual const uint64_t *data() const { return nullptr; }

    friend std::ostream &operator<<(std::ostream &os, const Uint64Array &v);

    // Test API
    bool operator==(const Uint64Array &other) const;
};

/**
 * Mutable version of Uint64Array.
 */
class Uint64ArrayRW: public Uint64Array {
    uint64_t* mData;

public:
    Uint64ArrayRW() : Uint64ArrayRW(0) {}

    Uint64ArrayRW(size_t size) : Uint64Array(size), mData(nullptr) {}

    Uint64ArrayRW(const Uint64Array &copy);

    // Need an explicit copy constructor. In the initialization context C++ does not understand that
    // a Uint64ArrayRW is a Uint64Array.
    Uint64ArrayRW(const Uint64ArrayRW &copy) : Uint64ArrayRW((const Uint64Array &) copy) {}

    // Test API
    Uint64ArrayRW(std::initializer_list<uint64_t> init);

    ~Uint64ArrayRW() override { delete[] mData; }

    const uint64_t *data() const override { return mData; }

    // NonNull. Will initialize the wrapped array if it is null.
    uint64_t *dataRW();

    Uint64ArrayRW &operator=(const Uint64Array &t);
};

typedef MultiStateCounter<Uint64ArrayRW, Uint64Array> LongArrayMultiStateCounter;

} // namespace battery
} // namespace android
+14 −14
Original line number Diff line number Diff line
@@ -24,25 +24,25 @@ namespace battery {
class LongArrayMultiStateCounterTest : public testing::Test {};

TEST_F(LongArrayMultiStateCounterTest, stateChange) {
    LongArrayMultiStateCounter testCounter(2, std::vector<uint64_t>(4));
    testCounter.updateValue(std::vector<uint64_t>({0, 0, 0, 0}), 1000);
    LongArrayMultiStateCounter testCounter(2, Uint64Array(4));
    testCounter.updateValue(Uint64ArrayRW({0, 0, 0, 0}), 1000);
    testCounter.setState(0, 1000);
    testCounter.setState(1, 2000);
    testCounter.updateValue(std::vector<uint64_t>({100, 200, 300, 400}), 3000);
    testCounter.updateValue(Uint64ArrayRW({100, 200, 300, 400}), 3000);

    // Time was split in half between the two states, so the counts will be split 50:50 too
    EXPECT_EQ(std::vector<uint64_t>({50, 100, 150, 200}), testCounter.getCount(0));
    EXPECT_EQ(std::vector<uint64_t>({50, 100, 150, 200}), testCounter.getCount(1));
    EXPECT_EQ(Uint64ArrayRW({50, 100, 150, 200}), testCounter.getCount(0));
    EXPECT_EQ(Uint64ArrayRW({50, 100, 150, 200}), testCounter.getCount(1));
}

TEST_F(LongArrayMultiStateCounterTest, accumulation) {
    LongArrayMultiStateCounter testCounter(2, std::vector<uint64_t>(4));
    testCounter.updateValue(std::vector<uint64_t>({0, 0, 0, 0}), 1000);
    LongArrayMultiStateCounter testCounter(2, Uint64Array(4));
    testCounter.updateValue(Uint64ArrayRW({0, 0, 0, 0}), 1000);
    testCounter.setState(0, 1000);
    testCounter.setState(1, 2000);
    testCounter.updateValue(std::vector<uint64_t>({100, 200, 300, 400}), 3000);
    testCounter.updateValue(Uint64ArrayRW({100, 200, 300, 400}), 3000);
    testCounter.setState(0, 4000);
    testCounter.updateValue(std::vector<uint64_t>({200, 300, 400, 500}), 8000);
    testCounter.updateValue(Uint64ArrayRW({200, 300, 400, 500}), 8000);

    // The first delta is split 50:50:
    //   0: {50, 100, 150, 200}
@@ -50,16 +50,16 @@ TEST_F(LongArrayMultiStateCounterTest, accumulation) {
    // The second delta is split 4:1
    //   0: {80, 80, 80, 80}
    //   1: {20, 20, 20, 20}
    EXPECT_EQ(std::vector<uint64_t>({130, 180, 230, 280}), testCounter.getCount(0));
    EXPECT_EQ(std::vector<uint64_t>({70, 120, 170, 220}), testCounter.getCount(1));
    EXPECT_EQ(Uint64ArrayRW({130, 180, 230, 280}), testCounter.getCount(0));
    EXPECT_EQ(Uint64ArrayRW({70, 120, 170, 220}), testCounter.getCount(1));
}

TEST_F(LongArrayMultiStateCounterTest, toString) {
    LongArrayMultiStateCounter testCounter(2, std::vector<uint64_t>(4));
    testCounter.updateValue(std::vector<uint64_t>({0, 0, 0, 0}), 1000);
    LongArrayMultiStateCounter testCounter(2, Uint64Array(4));
    testCounter.updateValue(Uint64ArrayRW({0, 0, 0, 0}), 1000);
    testCounter.setState(0, 1000);
    testCounter.setState(1, 2000);
    testCounter.updateValue(std::vector<uint64_t>({100, 200, 300, 400}), 3000);
    testCounter.updateValue(Uint64ArrayRW({100, 200, 300, 400}), 3000);

    EXPECT_STREQ("[0: {50, 100, 150, 200}, 1: {50, 100, 150, 200}] updated: 3000 currentState: 1",
                 testCounter.toString().c_str());
+46 −45
Original line number Diff line number Diff line
@@ -35,12 +35,12 @@ namespace battery {

typedef uint16_t state_t;

template <class T>
template <class T, class V>
class MultiStateCounter {
    uint16_t stateCount;
    const uint16_t stateCount;
    const V emptyValue;
    state_t currentState;
    time_t lastStateChangeTimestamp;
    T emptyValue;
    T lastValue;
    time_t lastUpdateTimestamp;
    T deltaValue;
@@ -54,7 +54,7 @@ class MultiStateCounter {
    State* states;

public:
    MultiStateCounter(uint16_t stateCount, const T& emptyValue);
    MultiStateCounter(uint16_t stateCount, const V& emptyValue);

    virtual ~MultiStateCounter();

@@ -66,35 +66,35 @@ public:
     * Copies the current state and accumulated times-in-state from the source. Resets
     * the accumulated value.
     */
    void copyStatesFrom(const MultiStateCounter<T>& source);
    void copyStatesFrom(const MultiStateCounter<T, V> &source);

    void setValue(state_t state, const T& value);
    void setValue(state_t state, const V& value);

    /**
     * Updates the value by distributing the delta from the previously set value
     * among states according to their respective time-in-state.
     * Returns the delta from the previously set value.
     */
    const T& updateValue(const T& value, time_t timestamp);
    const V& updateValue(const V& value, time_t timestamp);

    /**
     * Updates the value by distributing the specified increment among states according
     * to their respective time-in-state.
     */
    void incrementValue(const T& increment, time_t timestamp);
    void incrementValue(const V& increment, time_t timestamp);

    /**
     * Adds the specified increment to the value for the current state, without affecting
     * the last updated value or timestamp.  Ignores partial time-in-state: the entirety of
     * the increment is given to the current state.
     */
    void addValue(const T& increment);
    void addValue(const V& increment);

    void reset();

    uint16_t getStateCount();

    const T& getCount(state_t state);
    const V& getCount(state_t state);

    std::string toString();

@@ -104,27 +104,25 @@ private:
     * Returns true iff the combination of previousValue and newValue is valid
     * (newValue >= prevValue)
     */
    bool delta(const T& previousValue, const T& newValue, T* outValue) const;
    bool delta(const T& previousValue, const V& newValue, T* outValue) const;

    /**
     * Adds value2 to value1 and stores the result in value1.  Denominator is
     * guaranteed to be non-zero.
     */
    void add(T* value1, const T& value2, const uint64_t numerator,
    void add(T* value1, const V& value2, const uint64_t numerator,
             const uint64_t denominator) const;

    std::string valueToString(const T& value) const;
};

// ---------------------- MultiStateCounter Implementation -------------------------
// Since MultiStateCounter is a template, the implementation must be inlined.

template <class T>
MultiStateCounter<T>::MultiStateCounter(uint16_t stateCount, const T& emptyValue)
template <class T, class V>
MultiStateCounter<T, V>::MultiStateCounter(uint16_t stateCount, const V& emptyValue)
      : stateCount(stateCount),
        emptyValue(emptyValue),
        currentState(0),
        lastStateChangeTimestamp(-1),
        emptyValue(emptyValue),
        lastValue(emptyValue),
        lastUpdateTimestamp(-1),
        deltaValue(emptyValue),
@@ -136,13 +134,13 @@ MultiStateCounter<T>::MultiStateCounter(uint16_t stateCount, const T& emptyValue
    }
}

template <class T>
MultiStateCounter<T>::~MultiStateCounter() {
template <class T, class V>
MultiStateCounter<T, V>::~MultiStateCounter() {
    delete[] states;
};

template <class T>
void MultiStateCounter<T>::setEnabled(bool enabled, time_t timestamp) {
template <class T, class V>
void MultiStateCounter<T, V>::setEnabled(bool enabled, time_t timestamp) {
    if (enabled == isEnabled) {
        return;
    }
@@ -167,8 +165,8 @@ void MultiStateCounter<T>::setEnabled(bool enabled, time_t timestamp) {
    }
}

template <class T>
void MultiStateCounter<T>::setState(state_t state, time_t timestamp) {
template <class T, class V>
void MultiStateCounter<T, V>::setState(state_t state, time_t timestamp) {
    if (isEnabled && lastStateChangeTimestamp >= 0 && lastUpdateTimestamp >= 0) {
        // If the update arrived out-of-order, just push back the timestamp to
        // avoid having the situation where timeInStateSinceUpdate > timeSinceUpdate
@@ -198,8 +196,8 @@ void MultiStateCounter<T>::setState(state_t state, time_t timestamp) {
    lastStateChangeTimestamp = timestamp;
}

template <class T>
void MultiStateCounter<T>::copyStatesFrom(const MultiStateCounter<T>& source) {
template <class T, class V>
void MultiStateCounter<T, V>::copyStatesFrom(const MultiStateCounter<T, V>& source) {
    if (stateCount != source.stateCount) {
        ALOGE("State count mismatch: %u vs. %u\n", stateCount, source.stateCount);
        return;
@@ -214,14 +212,14 @@ void MultiStateCounter<T>::copyStatesFrom(const MultiStateCounter<T>& source) {
    lastUpdateTimestamp = source.lastUpdateTimestamp;
}

template <class T>
void MultiStateCounter<T>::setValue(state_t state, const T& value) {
template <class T, class V>
void MultiStateCounter<T, V>::setValue(state_t state, const V& value) {
    states[state].counter = value;
}

template <class T>
const T& MultiStateCounter<T>::updateValue(const T& value, time_t timestamp) {
    T* returnValue = &emptyValue;
template <class T, class V>
const V& MultiStateCounter<T, V>::updateValue(const V& value, time_t timestamp) {
    const V* returnValue = &emptyValue;

    // If the counter is disabled, we ignore the update, except when the counter got disabled after
    // the previous update, in which case we still need to pick up the residual delta.
@@ -250,8 +248,8 @@ const T& MultiStateCounter<T>::updateValue(const T& value, time_t timestamp) {
                    }
                } else {
                    std::stringstream str;
                    str << "updateValue is called with a value " << valueToString(value)
                        << ", which is lower than the previous value " << valueToString(lastValue)
                    str << "updateValue is called with a value " << value
                        << ", which is lower than the previous value " << lastValue
                        << "\n";
                    ALOGE("%s", str.str().c_str());

@@ -276,23 +274,25 @@ const T& MultiStateCounter<T>::updateValue(const T& value, time_t timestamp) {
    return *returnValue;
}

template <class T>
void MultiStateCounter<T>::incrementValue(const T& increment, time_t timestamp) {
template <class T, class V>
void MultiStateCounter<T, V>::incrementValue(const V& increment, time_t timestamp) {
//    T newValue;
//    newValue = lastValue; // Copy assignment, not initialization.
    T newValue = lastValue;
    add(&newValue, increment, 1 /* numerator */, 1 /* denominator */);
    updateValue(newValue, timestamp);
}

template <class T>
void MultiStateCounter<T>::addValue(const T& value) {
template <class T, class V>
void MultiStateCounter<T, V>::addValue(const V& value) {
    if (!isEnabled) {
        return;
    }
    add(&states[currentState].counter, value, 1 /* numerator */, 1 /* denominator */);
}

template <class T>
void MultiStateCounter<T>::reset() {
template <class T, class V>
void MultiStateCounter<T, V>::reset() {
    lastStateChangeTimestamp = -1;
    lastUpdateTimestamp = -1;
    for (int i = 0; i < stateCount; i++) {
@@ -301,25 +301,26 @@ void MultiStateCounter<T>::reset() {
    }
}

template <class T>
uint16_t MultiStateCounter<T>::getStateCount() {
template <class T, class V>
uint16_t MultiStateCounter<T, V>::getStateCount() {
    return stateCount;
}

template <class T>
const T& MultiStateCounter<T>::getCount(state_t state) {
template <class T, class V>
const V& MultiStateCounter<T, V>::getCount(state_t state) {
    return states[state].counter;
}

template <class T>
std::string MultiStateCounter<T>::toString() {
template <class T, class V>
std::string MultiStateCounter<T, V>::toString() {
    std::stringstream str;
//    str << "LAST VALUE: " << valueToString(lastValue);
    str << "[";
    for (int i = 0; i < stateCount; i++) {
        if (i != 0) {
            str << ", ";
        }
        str << i << ": " << valueToString(states[i].counter);
        str << i << ": " << states[i].counter;
        if (states[i].timeInStateSinceUpdate > 0) {
            str << " timeInStateSinceUpdate: " << states[i].timeInStateSinceUpdate;
        }
+1 −6
Original line number Diff line number Diff line
@@ -21,7 +21,7 @@
namespace android {
namespace battery {

typedef MultiStateCounter<double> DoubleMultiStateCounter;
typedef MultiStateCounter<double, double> DoubleMultiStateCounter;

template <>
bool DoubleMultiStateCounter::delta(const double& previousValue, const double& newValue,
@@ -41,11 +41,6 @@ void DoubleMultiStateCounter::add(double* value1, const double& value2, const ui
    }
}

template <>
std::string DoubleMultiStateCounter::valueToString(const double& v) const {
    return std::to_string(v);
}

class MultiStateCounterTest : public testing::Test {};

TEST_F(MultiStateCounterTest, constructor) {