<commit_before>//
// Copyright 2002 The ANGLE 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.
//

// Buffer.cpp: Implements the gl::Buffer class, representing storage of vertex and/or
// index data. Implements GL buffer objects and related functionality.
// [OpenGL ES 2.0.24] section 2.9 page 21.

#include "libANGLE/Buffer.h"

#include "libANGLE/Context.h"
#include "libANGLE/renderer/BufferImpl.h"
#include "libANGLE/renderer/GLImplFactory.h"

namespace gl
{
namespace
{
constexpr angle::SubjectIndex kImplementationSubjectIndex = 0;
}  // anonymous namespace

BufferState::BufferState()
    : mLabel(),
      mUsage(BufferUsage::StaticDraw),
      mSize(0),
      mAccessFlags(0),
      mAccess(GL_WRITE_ONLY_OES),
      mMapped(GL_FALSE),
      mMapPointer(nullptr),
      mMapOffset(0),
      mMapLength(0),
      mBindingCount(0),
      mTransformFeedbackIndexedBindingCount(0),
      mTransformFeedbackGenericBindingCount(0)
{}

BufferState::~BufferState() {}

Buffer::Buffer(rx::GLImplFactory *factory, BufferID id)
    : RefCountObject(factory->generateSerial(), id),
      mImpl(factory->createBuffer(mState)),
      mImplObserver(this, kImplementationSubjectIndex)
{
    mImplObserver.bind(mImpl);
}

Buffer::~Buffer()
{
    SafeDelete(mImpl);
}

void Buffer::onDestroy(const Context *context)
{
    // In tests, mImpl might be null.
    if (mImpl)
        mImpl->destroy(context);
}

void Buffer::setLabel(const Context *context, const std::string &label)
{
    mState.mLabel = label;
}

const std::string &Buffer::getLabel() const
{
    return mState.mLabel;
}

angle::Result Buffer::bufferData(Context *context,
                                 BufferBinding target,
                                 const void *data,
                                 GLsizeiptr size,
                                 BufferUsage usage)
{
    const void *dataForImpl = data;

    // If we are using robust resource init, make sure the buffer starts cleared.
    // Note: the Context is checked for nullptr because of some testing code.
    // TODO(jmadill): Investigate lazier clearing.
    if (context && context->getState().isRobustResourceInitEnabled() && !data && size > 0)
    {
        angle::MemoryBuffer *scratchBuffer = nullptr;
        ANGLE_CHECK_GL_ALLOC(
            context, context->getZeroFilledBuffer(static_cast<size_t>(size), &scratchBuffer));
        dataForImpl = scratchBuffer->data();
    }

    ANGLE_TRY(mImpl->setData(context, target, dataForImpl, size, usage));

    mIndexRangeCache.clear();
    mState.mUsage = usage;
    mState.mSize  = size;

    // Notify when storage changes.
    onStateChange(angle::SubjectMessage::SubjectChanged);

    return angle::Result::Continue;
}

angle::Result Buffer::bufferSubData(const Context *context,
                                    BufferBinding target,
                                    const void *data,
                                    GLsizeiptr size,
                                    GLintptr offset)
{
    ANGLE_TRY(mImpl->setSubData(context, target, data, size, offset));

    mIndexRangeCache.invalidateRange(static_cast<unsigned int>(offset),
                                     static_cast<unsigned int>(size));

    // Notify when data changes.
    onStateChange(angle::SubjectMessage::ContentsChanged);

    return angle::Result::Continue;
}

angle::Result Buffer::copyBufferSubData(const Context *context,
                                        Buffer *source,
                                        GLintptr sourceOffset,
                                        GLintptr destOffset,
                                        GLsizeiptr size)
{
    ANGLE_TRY(
        mImpl->copySubData(context, source->getImplementation(), sourceOffset, destOffset, size));

    mIndexRangeCache.invalidateRange(static_cast<unsigned int>(destOffset),
                                     static_cast<unsigned int>(size));

    // Notify when data changes.
    onStateChange(angle::SubjectMessage::ContentsChanged);

    return angle::Result::Continue;
}

angle::Result Buffer::map(const Context *context, GLenum access)
{
    ASSERT(!mState.mMapped);

    mState.mMapPointer = nullptr;
    ANGLE_TRY(mImpl->map(context, access, &mState.mMapPointer));

    ASSERT(access == GL_WRITE_ONLY_OES);

    mState.mMapped      = GL_TRUE;
    mState.mMapOffset   = 0;
    mState.mMapLength   = mState.mSize;
    mState.mAccess      = access;
    mState.mAccessFlags = GL_MAP_WRITE_BIT;
    mIndexRangeCache.clear();

    // Notify when state changes.
    onStateChange(angle::SubjectMessage::SubjectMapped);

    return angle::Result::Continue;
}

angle::Result Buffer::mapRange(const Context *context,
                               GLintptr offset,
                               GLsizeiptr length,
                               GLbitfield access)
{
    ASSERT(!mState.mMapped);
    ASSERT(offset + length <= mState.mSize);

    mState.mMapPointer = nullptr;
    ANGLE_TRY(mImpl->mapRange(context, offset, length, access, &mState.mMapPointer));

    mState.mMapped      = GL_TRUE;
    mState.mMapOffset   = static_cast<GLint64>(offset);
    mState.mMapLength   = static_cast<GLint64>(length);
    mState.mAccess      = GL_WRITE_ONLY_OES;
    mState.mAccessFlags = access;

    // The OES_mapbuffer extension states that GL_WRITE_ONLY_OES is the only valid
    // value for GL_BUFFER_ACCESS_OES because it was written against ES2.  Since there is
    // no update for ES3 and the GL_READ_ONLY and GL_READ_WRITE enums don't exist for ES,
    // we cannot properly set GL_BUFFER_ACCESS_OES when glMapBufferRange is called.

    if ((access & GL_MAP_WRITE_BIT) > 0)
    {
        mIndexRangeCache.invalidateRange(static_cast<unsigned int>(offset),
                                         static_cast<unsigned int>(length));
    }

    // Notify when state changes.
    onStateChange(angle::SubjectMessage::SubjectMapped);

    return angle::Result::Continue;
}

angle::Result Buffer::unmap(const Context *context, GLboolean *result)
{
    ASSERT(mState.mMapped);

    *result = GL_FALSE;
    ANGLE_TRY(mImpl->unmap(context, result));

    mState.mMapped      = GL_FALSE;
    mState.mMapPointer  = nullptr;
    mState.mMapOffset   = 0;
    mState.mMapLength   = 0;
    mState.mAccess      = GL_WRITE_ONLY_OES;
    mState.mAccessFlags = 0;

    // Notify when data changes.
    onStateChange(angle::SubjectMessage::SubjectUnmapped);

    return angle::Result::Continue;
}

void Buffer::onDataChanged()
{
    mIndexRangeCache.clear();

    // Notify when data changes.
    onStateChange(angle::SubjectMessage::ContentsChanged);

    mImpl->onDataChanged();
}

angle::Result Buffer::getIndexRange(const gl::Context *context,
                                    DrawElementsType type,
                                    size_t offset,
                                    size_t count,
                                    bool primitiveRestartEnabled,
                                    IndexRange *outRange) const
{
    if (mIndexRangeCache.findRange(type, offset, count, primitiveRestartEnabled, outRange))
    {
        return angle::Result::Continue;
    }

    ANGLE_TRY(
        mImpl->getIndexRange(context, type, offset, count, primitiveRestartEnabled, outRange));

    mIndexRangeCache.addRange(type, offset, count, primitiveRestartEnabled, *outRange);

    return angle::Result::Continue;
}

GLint64 Buffer::getMemorySize() const
{
    GLint64 implSize = mImpl->getMemorySize();
    return implSize > 0 ? implSize : mState.mSize;
}

bool Buffer::isDoubleBoundForTransformFeedback() const
{
    return mState.mTransformFeedbackIndexedBindingCount > 1;
}

void Buffer::onTFBindingChanged(const Context *context, bool bound, bool indexed)
{
    ASSERT(bound || mState.mBindingCount > 0);
    mState.mBindingCount += bound ? 1 : -1;
    if (indexed)
    {
        ASSERT(bound || mState.mTransformFeedbackIndexedBindingCount > 0);
        mState.mTransformFeedbackIndexedBindingCount += bound ? 1 : -1;

        onStateChange(angle::SubjectMessage::BindingChanged);
    }
    else
    {
        mState.mTransformFeedbackGenericBindingCount += bound ? 1 : -1;
    }
}

void Buffer::onSubjectStateChange(angle::SubjectIndex index, angle::SubjectMessage message)
{
    // Pass it along!
    ASSERT(index == kImplementationSubjectIndex);
    ASSERT(message == angle::SubjectMessage::SubjectChanged);
    onStateChange(angle::SubjectMessage::SubjectChanged);
}
}  // namespace gl
<commit_msg>Set gl::Buffer to zero size when allocations fail.<commit_after>//
// Copyright 2002 The ANGLE 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.
//

// Buffer.cpp: Implements the gl::Buffer class, representing storage of vertex and/or
// index data. Implements GL buffer objects and related functionality.
// [OpenGL ES 2.0.24] section 2.9 page 21.

#include "libANGLE/Buffer.h"

#include "libANGLE/Context.h"
#include "libANGLE/renderer/BufferImpl.h"
#include "libANGLE/renderer/GLImplFactory.h"

namespace gl
{
namespace
{
constexpr angle::SubjectIndex kImplementationSubjectIndex = 0;
}  // anonymous namespace

BufferState::BufferState()
    : mLabel(),
      mUsage(BufferUsage::StaticDraw),
      mSize(0),
      mAccessFlags(0),
      mAccess(GL_WRITE_ONLY_OES),
      mMapped(GL_FALSE),
      mMapPointer(nullptr),
      mMapOffset(0),
      mMapLength(0),
      mBindingCount(0),
      mTransformFeedbackIndexedBindingCount(0),
      mTransformFeedbackGenericBindingCount(0)
{}

BufferState::~BufferState() {}

Buffer::Buffer(rx::GLImplFactory *factory, BufferID id)
    : RefCountObject(factory->generateSerial(), id),
      mImpl(factory->createBuffer(mState)),
      mImplObserver(this, kImplementationSubjectIndex)
{
    mImplObserver.bind(mImpl);
}

Buffer::~Buffer()
{
    SafeDelete(mImpl);
}

void Buffer::onDestroy(const Context *context)
{
    // In tests, mImpl might be null.
    if (mImpl)
        mImpl->destroy(context);
}

void Buffer::setLabel(const Context *context, const std::string &label)
{
    mState.mLabel = label;
}

const std::string &Buffer::getLabel() const
{
    return mState.mLabel;
}

angle::Result Buffer::bufferData(Context *context,
                                 BufferBinding target,
                                 const void *data,
                                 GLsizeiptr size,
                                 BufferUsage usage)
{
    const void *dataForImpl = data;

    // If we are using robust resource init, make sure the buffer starts cleared.
    // Note: the Context is checked for nullptr because of some testing code.
    // TODO(jmadill): Investigate lazier clearing.
    if (context && context->getState().isRobustResourceInitEnabled() && !data && size > 0)
    {
        angle::MemoryBuffer *scratchBuffer = nullptr;
        ANGLE_CHECK_GL_ALLOC(
            context, context->getZeroFilledBuffer(static_cast<size_t>(size), &scratchBuffer));
        dataForImpl = scratchBuffer->data();
    }

    if (mImpl->setData(context, target, dataForImpl, size, usage) == angle::Result::Stop)
    {
        // If setData fails, the buffer contents are undefined. Set a zero size to indicate that.
        mIndexRangeCache.clear();
        mState.mSize = 0;

        // Notify when storage changes.
        onStateChange(angle::SubjectMessage::SubjectChanged);

        return angle::Result::Stop;
    }

    mIndexRangeCache.clear();
    mState.mUsage = usage;
    mState.mSize  = size;

    // Notify when storage changes.
    onStateChange(angle::SubjectMessage::SubjectChanged);

    return angle::Result::Continue;
}

angle::Result Buffer::bufferSubData(const Context *context,
                                    BufferBinding target,
                                    const void *data,
                                    GLsizeiptr size,
                                    GLintptr offset)
{
    ANGLE_TRY(mImpl->setSubData(context, target, data, size, offset));

    mIndexRangeCache.invalidateRange(static_cast<unsigned int>(offset),
                                     static_cast<unsigned int>(size));

    // Notify when data changes.
    onStateChange(angle::SubjectMessage::ContentsChanged);

    return angle::Result::Continue;
}

angle::Result Buffer::copyBufferSubData(const Context *context,
                                        Buffer *source,
                                        GLintptr sourceOffset,
                                        GLintptr destOffset,
                                        GLsizeiptr size)
{
    ANGLE_TRY(
        mImpl->copySubData(context, source->getImplementation(), sourceOffset, destOffset, size));

    mIndexRangeCache.invalidateRange(static_cast<unsigned int>(destOffset),
                                     static_cast<unsigned int>(size));

    // Notify when data changes.
    onStateChange(angle::SubjectMessage::ContentsChanged);

    return angle::Result::Continue;
}

angle::Result Buffer::map(const Context *context, GLenum access)
{
    ASSERT(!mState.mMapped);

    mState.mMapPointer = nullptr;
    ANGLE_TRY(mImpl->map(context, access, &mState.mMapPointer));

    ASSERT(access == GL_WRITE_ONLY_OES);

    mState.mMapped      = GL_TRUE;
    mState.mMapOffset   = 0;
    mState.mMapLength   = mState.mSize;
    mState.mAccess      = access;
    mState.mAccessFlags = GL_MAP_WRITE_BIT;
    mIndexRangeCache.clear();

    // Notify when state changes.
    onStateChange(angle::SubjectMessage::SubjectMapped);

    return angle::Result::Continue;
}

angle::Result Buffer::mapRange(const Context *context,
                               GLintptr offset,
                               GLsizeiptr length,
                               GLbitfield access)
{
    ASSERT(!mState.mMapped);
    ASSERT(offset + length <= mState.mSize);

    mState.mMapPointer = nullptr;
    ANGLE_TRY(mImpl->mapRange(context, offset, length, access, &mState.mMapPointer));

    mState.mMapped      = GL_TRUE;
    mState.mMapOffset   = static_cast<GLint64>(offset);
    mState.mMapLength   = static_cast<GLint64>(length);
    mState.mAccess      = GL_WRITE_ONLY_OES;
    mState.mAccessFlags = access;

    // The OES_mapbuffer extension states that GL_WRITE_ONLY_OES is the only valid
    // value for GL_BUFFER_ACCESS_OES because it was written against ES2.  Since there is
    // no update for ES3 and the GL_READ_ONLY and GL_READ_WRITE enums don't exist for ES,
    // we cannot properly set GL_BUFFER_ACCESS_OES when glMapBufferRange is called.

    if ((access & GL_MAP_WRITE_BIT) > 0)
    {
        mIndexRangeCache.invalidateRange(static_cast<unsigned int>(offset),
                                         static_cast<unsigned int>(length));
    }

    // Notify when state changes.
    onStateChange(angle::SubjectMessage::SubjectMapped);

    return angle::Result::Continue;
}

angle::Result Buffer::unmap(const Context *context, GLboolean *result)
{
    ASSERT(mState.mMapped);

    *result = GL_FALSE;
    ANGLE_TRY(mImpl->unmap(context, result));

    mState.mMapped      = GL_FALSE;
    mState.mMapPointer  = nullptr;
    mState.mMapOffset   = 0;
    mState.mMapLength   = 0;
    mState.mAccess      = GL_WRITE_ONLY_OES;
    mState.mAccessFlags = 0;

    // Notify when data changes.
    onStateChange(angle::SubjectMessage::SubjectUnmapped);

    return angle::Result::Continue;
}

void Buffer::onDataChanged()
{
    mIndexRangeCache.clear();

    // Notify when data changes.
    onStateChange(angle::SubjectMessage::ContentsChanged);

    mImpl->onDataChanged();
}

angle::Result Buffer::getIndexRange(const gl::Context *context,
                                    DrawElementsType type,
                                    size_t offset,
                                    size_t count,
                                    bool primitiveRestartEnabled,
                                    IndexRange *outRange) const
{
    if (mIndexRangeCache.findRange(type, offset, count, primitiveRestartEnabled, outRange))
    {
        return angle::Result::Continue;
    }

    ANGLE_TRY(
        mImpl->getIndexRange(context, type, offset, count, primitiveRestartEnabled, outRange));

    mIndexRangeCache.addRange(type, offset, count, primitiveRestartEnabled, *outRange);

    return angle::Result::Continue;
}

GLint64 Buffer::getMemorySize() const
{
    GLint64 implSize = mImpl->getMemorySize();
    return implSize > 0 ? implSize : mState.mSize;
}

bool Buffer::isDoubleBoundForTransformFeedback() const
{
    return mState.mTransformFeedbackIndexedBindingCount > 1;
}

void Buffer::onTFBindingChanged(const Context *context, bool bound, bool indexed)
{
    ASSERT(bound || mState.mBindingCount > 0);
    mState.mBindingCount += bound ? 1 : -1;
    if (indexed)
    {
        ASSERT(bound || mState.mTransformFeedbackIndexedBindingCount > 0);
        mState.mTransformFeedbackIndexedBindingCount += bound ? 1 : -1;

        onStateChange(angle::SubjectMessage::BindingChanged);
    }
    else
    {
        mState.mTransformFeedbackGenericBindingCount += bound ? 1 : -1;
    }
}

void Buffer::onSubjectStateChange(angle::SubjectIndex index, angle::SubjectMessage message)
{
    // Pass it along!
    ASSERT(index == kImplementationSubjectIndex);
    ASSERT(message == angle::SubjectMessage::SubjectChanged);
    onStateChange(angle::SubjectMessage::SubjectChanged);
}
}  // namespace gl
<|endoftext|>


<commit_before>#pragma once

#include "nixexpr.hh"
#include "symbol-table.hh"

#include <algorithm>
#include <optional>

namespace nix {

class EvalState;
struct Value;

/* Map one attribute name to its value. */
struct Attr
{
    SymbolIdx name;
    Value * value;
    PosIdx pos;
    Attr(SymbolIdx name, Value * value, PosIdx pos = noPos)
        : name(name), value(value), pos(pos) { };
    Attr() { };
    bool operator < (const Attr & a) const
    {
        return name < a.name;
    }
};

/* Bindings contains all the attributes of an attribute set. It is defined
   by its size and its capacity, the capacity being the number of Attr
   elements allocated after this structure, while the size corresponds to
   the number of elements already inserted in this structure. */
class Bindings
{
public:
    typedef uint32_t size_t;
    PosIdx pos;

private:
    size_t size_, capacity_;
    Attr attrs[0];

    Bindings(size_t capacity) : size_(0), capacity_(capacity) { }
    Bindings(const Bindings & bindings) = delete;

public:
    size_t size() const { return size_; }

    bool empty() const { return !size_; }

    typedef Attr * iterator;

    void push_back(const Attr & attr)
    {
        assert(size_ < capacity_);
        attrs[size_++] = attr;
    }

    iterator find(const SymbolIdx & name)
    {
        Attr key(name, 0);
        iterator i = std::lower_bound(begin(), end(), key);
        if (i != end() && i->name == name) return i;
        return end();
    }

    Attr * get(const SymbolIdx & name)
    {
        Attr key(name, 0);
        iterator i = std::lower_bound(begin(), end(), key);
        if (i != end() && i->name == name) return &*i;
        return nullptr;
    }

    iterator begin() { return &attrs[0]; }
    iterator end() { return &attrs[size_]; }

    Attr & operator[](size_t pos)
    {
        return attrs[pos];
    }

    void sort();

    size_t capacity() { return capacity_; }

    /* Returns the attributes in lexicographically sorted order. */
    std::vector<const Attr *> lexicographicOrder(const SymbolTable & symbols) const
    {
        std::vector<const Attr *> res;
        res.reserve(size_);
        for (size_t n = 0; n < size_; n++)
            res.emplace_back(&attrs[n]);
        std::sort(res.begin(), res.end(), [&](const Attr * a, const Attr * b) {
            std::string_view sa = symbols[a->name], sb = symbols[b->name];
            return sa < sb;
        });
        return res;
    }

    friend class EvalState;
};

/* A wrapper around Bindings that ensures that its always in sorted
   order at the end. The only way to consume a BindingsBuilder is to
   call finish(), which sorts the bindings. */
class BindingsBuilder
{
    Bindings * bindings;

public:
    // needed by std::back_inserter
    using value_type = Attr;

    EvalState & state;

    BindingsBuilder(EvalState & state, Bindings * bindings)
        : bindings(bindings), state(state)
    { }

    void insert(SymbolIdx name, Value * value, PosIdx pos = noPos)
    {
        insert(Attr(name, value, pos));
    }

    void insert(const Attr & attr)
    {
        push_back(attr);
    }

    void push_back(const Attr & attr)
    {
        bindings->push_back(attr);
    }

    Value & alloc(const SymbolIdx & name, PosIdx pos = noPos);

    Value & alloc(std::string_view name, PosIdx pos = noPos);

    Bindings * finish()
    {
        bindings->sort();
        return bindings;
    }

    Bindings * alreadySorted()
    {
        return bindings;
    }
};

}
<commit_msg>shrink Attr by 8 bytes on 64bit machines<commit_after>#pragma once

#include "nixexpr.hh"
#include "symbol-table.hh"

#include <algorithm>
#include <optional>

namespace nix {

class EvalState;
struct Value;

/* Map one attribute name to its value. */
struct Attr
{
    /* the placement of `name` and `pos` in this struct is important.
       both of them are uint32 wrappers, they are next to each other
       to make sure that Attr has no padding on 64 bit machines. that
       way we keep Attr size at two words with no wasted space. */
    SymbolIdx name;
    PosIdx pos;
    Value * value;
    Attr(SymbolIdx name, Value * value, PosIdx pos = noPos)
        : name(name), pos(pos), value(value) { };
    Attr() { };
    bool operator < (const Attr & a) const
    {
        return name < a.name;
    }
};

static_assert(sizeof(Attr) == 2 * sizeof(uint32_t) + sizeof(Value *),
    "performance of the evaluator is highly sensitive to the size of Attr. "
    "avoid introducing any padding into Attr if at all possible, and do not "
    "introduce new fields that need not be present for almost every instance.");

/* Bindings contains all the attributes of an attribute set. It is defined
   by its size and its capacity, the capacity being the number of Attr
   elements allocated after this structure, while the size corresponds to
   the number of elements already inserted in this structure. */
class Bindings
{
public:
    typedef uint32_t size_t;
    PosIdx pos;

private:
    size_t size_, capacity_;
    Attr attrs[0];

    Bindings(size_t capacity) : size_(0), capacity_(capacity) { }
    Bindings(const Bindings & bindings) = delete;

public:
    size_t size() const { return size_; }

    bool empty() const { return !size_; }

    typedef Attr * iterator;

    void push_back(const Attr & attr)
    {
        assert(size_ < capacity_);
        attrs[size_++] = attr;
    }

    iterator find(const SymbolIdx & name)
    {
        Attr key(name, 0);
        iterator i = std::lower_bound(begin(), end(), key);
        if (i != end() && i->name == name) return i;
        return end();
    }

    Attr * get(const SymbolIdx & name)
    {
        Attr key(name, 0);
        iterator i = std::lower_bound(begin(), end(), key);
        if (i != end() && i->name == name) return &*i;
        return nullptr;
    }

    iterator begin() { return &attrs[0]; }
    iterator end() { return &attrs[size_]; }

    Attr & operator[](size_t pos)
    {
        return attrs[pos];
    }

    void sort();

    size_t capacity() { return capacity_; }

    /* Returns the attributes in lexicographically sorted order. */
    std::vector<const Attr *> lexicographicOrder(const SymbolTable & symbols) const
    {
        std::vector<const Attr *> res;
        res.reserve(size_);
        for (size_t n = 0; n < size_; n++)
            res.emplace_back(&attrs[n]);
        std::sort(res.begin(), res.end(), [&](const Attr * a, const Attr * b) {
            std::string_view sa = symbols[a->name], sb = symbols[b->name];
            return sa < sb;
        });
        return res;
    }

    friend class EvalState;
};

/* A wrapper around Bindings that ensures that its always in sorted
   order at the end. The only way to consume a BindingsBuilder is to
   call finish(), which sorts the bindings. */
class BindingsBuilder
{
    Bindings * bindings;

public:
    // needed by std::back_inserter
    using value_type = Attr;

    EvalState & state;

    BindingsBuilder(EvalState & state, Bindings * bindings)
        : bindings(bindings), state(state)
    { }

    void insert(SymbolIdx name, Value * value, PosIdx pos = noPos)
    {
        insert(Attr(name, value, pos));
    }

    void insert(const Attr & attr)
    {
        push_back(attr);
    }

    void push_back(const Attr & attr)
    {
        bindings->push_back(attr);
    }

    Value & alloc(const SymbolIdx & name, PosIdx pos = noPos);

    Value & alloc(std::string_view name, PosIdx pos = noPos);

    Bindings * finish()
    {
        bindings->sort();
        return bindings;
    }

    Bindings * alreadySorted()
    {
        return bindings;
    }
};

}
<|endoftext|>


<commit_before>/* Copyright (c) 2020-2022 hors<horsicq@gmail.com>
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in all
 * copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE.
 */
#include "xcom.h"

XCOM::XCOM(QIODevice *pDevice,bool bIsImage,qint64 nModuleAddress) : XBinary(pDevice,bIsImage,nModuleAddress)
{
    XBinary::setBaseAddress(XCOM_DEF::ADDRESS_BEGIN);
}

XCOM::~XCOM()
{

}

bool XCOM::isValid()
{
    bool bResult=false;

    // mb TODO mb
    if(getSize()<XCOM_DEF::IMAGESIZE)
    {
        bResult=true;
    }

    return bResult;
}

bool XCOM::isValid(QIODevice *pDevice,bool bIsImage,qint64 nModuleAddress)
{
    XCOM xcom(pDevice,bIsImage,nModuleAddress);

    return xcom.isValid();
}

XBinary::MODE XCOM::getMode(QIODevice *pDevice, bool bIsImage, qint64 nModuleAddress)
{
    XCOM xcom(pDevice,bIsImage,nModuleAddress);

    return xcom.getMode();
}

XBinary::_MEMORY_MAP XCOM::getMemoryMap()
{
    _MEMORY_MAP result={};

    qint64 nTotalSize=getSize();

    result.nModuleAddress=getModuleAddress();
    result.nEntryPointAddress=result.nModuleAddress;
    result.nRawSize=nTotalSize;
    result.nImageSize=getImageSize();
    result.fileType=getFileType();
    result.mode=getMode();
    result.sArch=getArch();
    result.bIsBigEndian=isBigEndian();
    result.sType=getTypeAsString();

    qint64 nCodeSize=qMin(nTotalSize,(qint64)(XCOM_DEF::IMAGESIZE-XCOM_DEF::ADDRESS_BEGIN));

    _MEMORY_RECORD record={};
    record.nAddress=0;
    record.segment=ADDRESS_SEGMENT_FLAT;
    record.nOffset=-1;
    record.nSize=XCOM_DEF::ADDRESS_BEGIN;
    record.nIndex++;
    record.bIsVirtual=true;

    result.listRecords.append(record);

    _MEMORY_RECORD recordMain={};
    recordMain.nAddress=XCOM_DEF::ADDRESS_BEGIN;
    recordMain.segment=ADDRESS_SEGMENT_FLAT;
    recordMain.nOffset=0;
    recordMain.nSize=nCodeSize;
    recordMain.nIndex++;

    result.listRecords.append(recordMain);

    qint64 nVirtualSize=(qint64)(XCOM_DEF::IMAGESIZE-XCOM_DEF::ADDRESS_BEGIN)-nTotalSize;

    if(nVirtualSize>0)
    {
        _MEMORY_RECORD record={};
        record.nAddress=XCOM_DEF::ADDRESS_BEGIN+nCodeSize;
        record.segment=ADDRESS_SEGMENT_FLAT;
        record.nOffset=-1;
        record.nSize=nVirtualSize;
        record.nIndex++;
        record.bIsVirtual=true;

        result.listRecords.append(record);
    }

    if(nTotalSize>nCodeSize)
    {
        _MEMORY_RECORD recordOverlay={};
        recordOverlay.nAddress=-1;
        recordOverlay.nOffset=nCodeSize;
        recordOverlay.nSize=nTotalSize-nCodeSize;
        recordOverlay.nIndex++;
        recordOverlay.type=MMT_OVERLAY;

        result.listRecords.append(recordOverlay);
    }

    return result;
}

QString XCOM::getArch()
{
    return QString("8086");
}

XBinary::MODE XCOM::getMode()
{
    return MODE_16;
}

bool XCOM::isBigEndian()
{
    return false;
}

qint64 XCOM::getImageSize()
{
    return 0x10000;
}

XBinary::FT XCOM::getFileType()
{
    return FT_COM;
}

qint32 XCOM::getType()
{
    return TYPE_EXE;
}

XBinary::OSINFO XCOM::getOsInfo()
{
    OSINFO result={};

    result.osName=OSNAME_MSDOS;
    result.sOsVersion=""; // TODO
    result.sArch=getArch();
    result.mode=getMode();
    result.sType=typeIdToString(getType());
    result.bIsBigEndian=isBigEndian();

    return result;
}

QString XCOM::typeIdToString(qint32 nType)
{
    QString sResult=tr("Unknown");

    switch(nType)
    {
        case TYPE_UNKNOWN:      sResult=tr("Unknown");          break;
        case TYPE_EXE:          sResult=QString("EXE");         break;
    }

    return sResult;
}
<commit_msg>Fix: 2022-03-27<commit_after>/* Copyright (c) 2020-2022 hors<horsicq@gmail.com>
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in all
 * copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE.
 */
#include "xcom.h"

XCOM::XCOM(QIODevice *pDevice,bool bIsImage,qint64 nModuleAddress) : XBinary(pDevice,bIsImage,nModuleAddress)
{
    XBinary::setBaseAddress(XCOM_DEF::ADDRESS_BEGIN);
}

XCOM::~XCOM()
{

}

bool XCOM::isValid()
{
    bool bResult=false;

    // mb TODO mb
    if(getSize()<XCOM_DEF::IMAGESIZE)
    {
        bResult=true;
    }

    return bResult;
}

bool XCOM::isValid(QIODevice *pDevice,bool bIsImage,qint64 nModuleAddress)
{
    XCOM xcom(pDevice,bIsImage,nModuleAddress);

    return xcom.isValid();
}

XBinary::MODE XCOM::getMode(QIODevice *pDevice,bool bIsImage,qint64 nModuleAddress)
{
    XCOM xcom(pDevice,bIsImage,nModuleAddress);

    return xcom.getMode();
}

XBinary::_MEMORY_MAP XCOM::getMemoryMap()
{
    _MEMORY_MAP result={};

    qint64 nTotalSize=getSize();

    result.nModuleAddress=getModuleAddress();
    result.nEntryPointAddress=result.nModuleAddress;
    result.nRawSize=nTotalSize;
    result.nImageSize=getImageSize();
    result.fileType=getFileType();
    result.mode=getMode();
    result.sArch=getArch();
    result.bIsBigEndian=isBigEndian();
    result.sType=getTypeAsString();

    qint64 nCodeSize=qMin(nTotalSize,(qint64)(XCOM_DEF::IMAGESIZE-XCOM_DEF::ADDRESS_BEGIN));

    _MEMORY_RECORD record={};
    record.nAddress=0;
    record.segment=ADDRESS_SEGMENT_FLAT;
    record.nOffset=-1;
    record.nSize=XCOM_DEF::ADDRESS_BEGIN;
    record.nIndex++;
    record.bIsVirtual=true;

    result.listRecords.append(record);

    _MEMORY_RECORD recordMain={};
    recordMain.nAddress=XCOM_DEF::ADDRESS_BEGIN;
    recordMain.segment=ADDRESS_SEGMENT_FLAT;
    recordMain.nOffset=0;
    recordMain.nSize=nCodeSize;
    recordMain.nIndex++;

    result.listRecords.append(recordMain);

    qint64 nVirtualSize=(qint64)(XCOM_DEF::IMAGESIZE-XCOM_DEF::ADDRESS_BEGIN)-nTotalSize;

    if(nVirtualSize>0)
    {
        _MEMORY_RECORD record={};
        record.nAddress=XCOM_DEF::ADDRESS_BEGIN+nCodeSize;
        record.segment=ADDRESS_SEGMENT_FLAT;
        record.nOffset=-1;
        record.nSize=nVirtualSize;
        record.nIndex++;
        record.bIsVirtual=true;

        result.listRecords.append(record);
    }

    if(nTotalSize>nCodeSize)
    {
        _MEMORY_RECORD recordOverlay={};
        recordOverlay.nAddress=-1;
        recordOverlay.nOffset=nCodeSize;
        recordOverlay.nSize=nTotalSize-nCodeSize;
        recordOverlay.nIndex++;
        recordOverlay.type=MMT_OVERLAY;

        result.listRecords.append(recordOverlay);
    }

    return result;
}

QString XCOM::getArch()
{
    return QString("8086");
}

XBinary::MODE XCOM::getMode()
{
    return MODE_16;
}

bool XCOM::isBigEndian()
{
    return false;
}

qint64 XCOM::getImageSize()
{
    return 0x10000;
}

XBinary::FT XCOM::getFileType()
{
    return FT_COM;
}

qint32 XCOM::getType()
{
    return TYPE_EXE;
}

XBinary::OSINFO XCOM::getOsInfo()
{
    OSINFO result={};

    result.osName=OSNAME_MSDOS;
    result.sOsVersion=""; // TODO
    result.sArch=getArch();
    result.mode=getMode();
    result.sType=typeIdToString(getType());
    result.bIsBigEndian=isBigEndian();

    return result;
}

QString XCOM::typeIdToString(qint32 nType)
{
    QString sResult=tr("Unknown");

    switch(nType)
    {
        case TYPE_UNKNOWN:      sResult=tr("Unknown");          break;
        case TYPE_EXE:          sResult=QString("EXE");         break;
    }

    return sResult;
}
<|endoftext|>


<commit_before>// lighting_window.cpp
// lighting settings window

// Copyright 2013 Matthew Chandler

// Permission is hereby granted, free of charge, to any person obtaining a copy of
// this software and associated documentation files (the "Software"), to deal in
// the Software without restriction, including without limitation the rights to
// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
// the Software, and to permit persons to whom the Software is furnished to do so,
// subject to the following conditions:

// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.

// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
// FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
// IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

#include <iostream>

#include <gdkmm/rgba.h>
#include <gtkmm/adjustment.h>
#include <gtkmm/grid.h>
#include <gtkmm/separator.h>
#include <gtkmm/stock.h>

#include "lighting_window.hpp"

// ctor takes ref to 2 light types
Lighting_window::Lighting_window(Light & dir_light, Light & cam_light):
    _x_dir(Gtk::Adjustment::create(dir_light.pos.x, -100.0, 100.0, 0.1), 0.1, 1),
    _y_dir(Gtk::Adjustment::create(dir_light.pos.y, -100.0, 100.0, 0.1), 0.1, 1),
    _z_dir(Gtk::Adjustment::create(dir_light.pos.z, -100.0, 100.0, 0.1), 0.1, 1),
    _dir_strength(Gtk::Adjustment::create(dir_light.strength, 0.0, 10.0, 0.1), Gtk::ORIENTATION_HORIZONTAL),
    _cam_strength(Gtk::Adjustment::create(cam_light.strength, 0.0, 10.0, 0.1), Gtk::ORIENTATION_HORIZONTAL),
    _cam_const_atten(Gtk::Adjustment::create(cam_light.const_atten, 0.0, 1.0, 0.01)),
    _cam_linear_atten(Gtk::Adjustment::create(cam_light.linear_atten, 0.0, 1.0, 0.01)),
    _cam_quad_atten(Gtk::Adjustment::create(cam_light.quad_atten, 0.0, 10., 0.01)),
    _dir_light(dir_light),
    _cam_light(cam_light)
{
    Gtk::manage(add_button(Gtk::Stock::CANCEL, Gtk::RESPONSE_CANCEL));
    Gtk::manage(add_button(Gtk::Stock::OK, Gtk::RESPONSE_OK));

    Gdk::RGBA start_color;
    start_color.set_rgba(dir_light.color.r, dir_light.color.g, dir_light.color.b, 1.0);
    _dir_color.set_rgba(start_color);
    start_color.set_rgba(cam_light.color.r, cam_light.color.g, cam_light.color.b, 1.0);
    _cam_color.set_rgba(start_color);

    Gtk::Grid * grid = new Gtk::Grid;
    get_content_area()->pack_start(*Gtk::manage(grid));

    grid->set_border_width(3);
    grid->set_row_spacing(3);
    grid->set_column_spacing(3);

    // TODO: labels, cleanup
    grid->attach(_x_dir, 0, 0, 1, 1);
    grid->attach(_y_dir, 1, 0, 1, 1);
    grid->attach(_z_dir, 2, 0, 1, 1);

    grid->attach(_dir_color, 0, 1, 3, 1);

    grid->attach(_dir_strength, 0, 2, 3, 1);

    grid->attach(*Gtk::manage(new Gtk::Separator(Gtk::ORIENTATION_HORIZONTAL)), 0, 3, 3, 1);

    grid->attach(_cam_color, 0, 4, 3, 1);

    grid->attach(_cam_strength, 0, 5, 3, 1);

    grid->attach(_cam_const_atten, 0, 6, 1, 1);
    grid->attach(_cam_linear_atten, 1, 6, 1, 1);
    grid->attach(_cam_quad_atten, 2, 6, 1, 1);

    show_all_children();

    // attach handler for user's response
    signal_response().connect(sigc::mem_fun(*this, &Lighting_window::store));
}

// store values to referenced light objs before closing
void Lighting_window::store(int response)
{
    _dir_light.pos.x = _x_dir.get_value();
    _dir_light.pos.y = _y_dir.get_value();
    _dir_light.pos.z = _z_dir.get_value();

    _dir_light.color.r = _dir_color.get_rgba().get_red();
    _dir_light.color.g = _dir_color.get_rgba().get_green();
    _dir_light.color.b = _dir_color.get_rgba().get_blue();

    _dir_light.strength = _dir_strength.get_value();

    _cam_light.color.r = _cam_color.get_rgba().get_red();
    _cam_light.color.g = _cam_color.get_rgba().get_green();
    _cam_light.color.b = _cam_color.get_rgba().get_blue();

    _cam_light.strength = _cam_strength.get_value();

    _cam_light.const_atten = _cam_const_atten.get_value();
    _cam_light.linear_atten = _cam_linear_atten.get_value();
    _cam_light.quad_atten = _cam_quad_atten.get_value();
}
<commit_msg>Arrange & label lighting settings<commit_after>// lighting_window.cpp
// lighting settings window

// Copyright 2013 Matthew Chandler

// Permission is hereby granted, free of charge, to any person obtaining a copy of
// this software and associated documentation files (the "Software"), to deal in
// the Software without restriction, including without limitation the rights to
// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
// the Software, and to permit persons to whom the Software is furnished to do so,
// subject to the following conditions:

// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.

// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
// FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
// IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

#include <iostream>

#include <gdkmm/rgba.h>
#include <gtkmm/adjustment.h>
#include <gtkmm/grid.h>
#include <gtkmm/separator.h>
#include <gtkmm/stock.h>

#include "lighting_window.hpp"

// ctor takes ref to 2 light types
Lighting_window::Lighting_window(Light & dir_light, Light & cam_light):
    _x_dir(Gtk::Adjustment::create(dir_light.pos.x, -100.0, 100.0, 0.1), 0.1, 1),
    _y_dir(Gtk::Adjustment::create(dir_light.pos.y, -100.0, 100.0, 0.1), 0.1, 1),
    _z_dir(Gtk::Adjustment::create(dir_light.pos.z, -100.0, 100.0, 0.1), 0.1, 1),
    _dir_strength(Gtk::Adjustment::create(dir_light.strength, 0.0, 10.0, 0.1), Gtk::ORIENTATION_HORIZONTAL),
    _cam_strength(Gtk::Adjustment::create(cam_light.strength, 0.0, 10.0, 0.1), Gtk::ORIENTATION_HORIZONTAL),
    _cam_const_atten(Gtk::Adjustment::create(cam_light.const_atten, 0.0, 1.0, 0.01)),
    _cam_linear_atten(Gtk::Adjustment::create(cam_light.linear_atten, 0.0, 1.0, 0.01)),
    _cam_quad_atten(Gtk::Adjustment::create(cam_light.quad_atten, 0.0, 10., 0.01)),
    _dir_light(dir_light),
    _cam_light(cam_light)
{
    set_title("Lighting Options");
    Gtk::manage(add_button(Gtk::Stock::CANCEL, Gtk::RESPONSE_CANCEL));
    Gtk::manage(add_button(Gtk::Stock::OK, Gtk::RESPONSE_OK));

    Gdk::RGBA start_color;
    start_color.set_rgba(dir_light.color.r, dir_light.color.g, dir_light.color.b, 1.0);
    _dir_color.set_rgba(start_color);
    start_color.set_rgba(cam_light.color.r, cam_light.color.g, cam_light.color.b, 1.0);
    _cam_color.set_rgba(start_color);

    Gtk::Grid * grid = new Gtk::Grid;
    get_content_area()->add(*Gtk::manage(grid));

    grid->set_border_width(3);
    grid->set_row_spacing(3);
    grid->set_column_spacing(3);
    grid->set_hexpand(true);
    grid->set_vexpand(true);

    grid->attach(*Gtk::manage(new Gtk::Label("Directional Light")), 0, 0, 7, 1);
    Gtk::Separator * spacer = new Gtk::Separator(Gtk::ORIENTATION_HORIZONTAL);
    spacer->set_hexpand(true);
    grid->attach(*Gtk::manage(spacer), 0, 1, 7, 1);

    grid->attach(*Gtk::manage(new Gtk::Label("Direction (x,y,z) = (")), 0, 2, 1, 1);
    grid->attach(_x_dir, 1, 2, 1, 1);
    grid->attach(*Gtk::manage(new Gtk::Label(",")), 2, 2, 1, 1);
    grid->attach(_y_dir, 3, 2, 1, 1);
    grid->attach(*Gtk::manage(new Gtk::Label(",")), 4, 2, 1, 1);
    grid->attach(_z_dir, 5, 2, 1, 1);
    grid->attach(*Gtk::manage(new Gtk::Label(")")), 6, 2, 1, 1);

    grid->attach(*Gtk::manage(new Gtk::Label("Color:")), 0, 3, 1, 1);
    grid->attach(_dir_color, 1, 3, 1, 1);

    grid->attach(*Gtk::manage(new Gtk::Label("Strength:")), 3, 3, 1, 1);
    grid->attach(_dir_strength, 4, 3, 3, 1);

    grid->attach(*Gtk::manage(new Gtk::Label("Camera Light")), 0, 4, 7, 1);
    grid->attach(*Gtk::manage(new Gtk::Separator(Gtk::ORIENTATION_HORIZONTAL)), 0, 5, 7, 1);

    grid->attach(*Gtk::manage(new Gtk::Label("Color:")), 0, 6, 1, 1);
    grid->attach(_cam_color, 1, 6, 1, 1);

    grid->attach(*Gtk::manage(new Gtk::Label("Strength:")), 3, 6, 1, 1);
    grid->attach(_cam_strength, 4, 6, 3, 1);

    grid->attach(*Gtk::manage(new Gtk::Label("")), 0, 7, 7, 1);

    grid->attach(*Gtk::manage(new Gtk::Label("Attenuation")), 0, 8, 1, 2);

    grid->attach(*Gtk::manage(new Gtk::Label("Constant")), 1, 9, 1, 1);
    grid->attach(*Gtk::manage(new Gtk::Label("Linear")), 3, 9, 1, 1);
    grid->attach(*Gtk::manage(new Gtk::Label("Quadratic")), 5, 9, 1, 1);

    grid->attach(_cam_const_atten, 1, 8, 1, 1);
    grid->attach(_cam_linear_atten, 3, 8, 1, 1);
    grid->attach(_cam_quad_atten, 5, 8, 1, 1);

    show_all_children();

    // attach handler for user's response
    signal_response().connect(sigc::mem_fun(*this, &Lighting_window::store));
}

// store values to referenced light objs before closing
void Lighting_window::store(int response)
{
    _dir_light.pos.x = _x_dir.get_value();
    _dir_light.pos.y = _y_dir.get_value();
    _dir_light.pos.z = _z_dir.get_value();

    _dir_light.color.r = _dir_color.get_rgba().get_red();
    _dir_light.color.g = _dir_color.get_rgba().get_green();
    _dir_light.color.b = _dir_color.get_rgba().get_blue();

    _dir_light.strength = _dir_strength.get_value();

    _cam_light.color.r = _cam_color.get_rgba().get_red();
    _cam_light.color.g = _cam_color.get_rgba().get_green();
    _cam_light.color.b = _cam_color.get_rgba().get_blue();

    _cam_light.strength = _cam_strength.get_value();

    _cam_light.const_atten = _cam_const_atten.get_value();
    _cam_light.linear_atten = _cam_linear_atten.get_value();
    _cam_light.quad_atten = _cam_quad_atten.get_value();
}
<|endoftext|>


<commit_before>#include <gtest/gtest.h>
#include "../../../../application/interface/analog/Analog.h"
#include "../../../../application/interface/CInfo.h"
#include "../../../../modules/midi/src/MIDI.h"
#include "../../../../modules/core/src/general/Timing.h"
#include "../../../../modules/core/src/general/Misc.h"
#include "../../../../application/database/Database.h"
#include "../../stubs/database/DB_ReadWrite.h"

//this represents theoretical maximum amount of analog components
//this test is run for all board variants
//make sure that the default is largest amount of analog components
#define MAX_MIDI_MESSAGES   96

namespace MIDIstub
{
    namespace detail
    {
        uint32_t ccMessageCounter = 0;
        uint32_t pbMessageCounter = 0;
        uint8_t ccValue[MAX_MIDI_MESSAGES] = {};
        uint16_t pbValue[MAX_MIDI_MESSAGES] = {};
    }

    void reset()
    {
        using namespace detail;

        ccMessageCounter = 0;
        pbMessageCounter = 0;
    }
}

void MIDI::sendControlChange(uint8_t inControlNumber, uint8_t inControlValue, uint8_t inChannel)
{
    MIDIstub::detail::ccValue[MIDIstub::detail::ccMessageCounter] = inControlValue;
    MIDIstub::detail::ccMessageCounter++;
}

void MIDI::sendPitchBend(uint16_t inPitchValue, uint8_t inChannel)
{
    MIDIstub::detail::pbValue[MIDIstub::detail::pbMessageCounter] = inPitchValue;
    MIDIstub::detail::pbMessageCounter++;
}

void MIDI::sendNoteOn(uint8_t inNoteNumber, uint8_t inVelocity, uint8_t inChannel)
{

}

void MIDI::sendNoteOff(uint8_t inNoteNumber, uint8_t inVelocity, uint8_t inChannel)
{

}

class PotentiometerTest : public ::testing::Test
{
    protected:
    virtual void SetUp()
    {
        cinfoHandler = nullptr;

        //init checks - no point in running further tests if these conditions fail
        EXPECT_TRUE(database.init());
        EXPECT_TRUE(database.getDBsize() < LESSDB_SIZE);
        EXPECT_TRUE(database.isSignatureValid());
    }

    virtual void TearDown()
    {

    }

    Database database = Database(DatabaseStub::memoryRead, DatabaseStub::memoryWrite);
    MIDI midi;
    Analog analog = Analog(database, midi);
};

namespace Board
{
    namespace detail
    {
        uint32_t adcReturnValue;
    }

    int16_t getAnalogValue(uint8_t analogID)
    {
        return detail::adcReturnValue;
    }

    void continueADCreadout()
    {

    }

    bool analogDataAvailable()
    {
        return true;
    }
}

TEST_F(PotentiometerTest, CCtest)
{
    //set known state
    for (int i=0; i<MAX_NUMBER_OF_ANALOG; i++)
    {
        //enable all analog components
        EXPECT_EQ(database.update(DB_BLOCK_ANALOG, dbSection_analog_enable, i, 1), true);

        //disable invert state
        EXPECT_EQ(database.update(DB_BLOCK_ANALOG, dbSection_analog_invert, i, 0), true);

        //configure all analog components as potentiometers with CC MIDI message
        EXPECT_EQ(database.update(DB_BLOCK_ANALOG, dbSection_analog_type, i, aType_potentiometer_cc), true);

        //set all lower limits to 0
        EXPECT_EQ(database.update(DB_BLOCK_ANALOG, dbSection_analog_lowerLimit, i, 0), true);

        //set all upper limits to MIDI_7_BIT_VALUE_MAX
        EXPECT_EQ(database.update(DB_BLOCK_ANALOG, dbSection_analog_upperLimit, i, MIDI_7_BIT_VALUE_MAX), true);
    }

    uint16_t expectedValue;

    Board::detail::adcReturnValue = 1000;

    MIDIstub::reset();
    expectedValue = mapRange(Board::detail::adcReturnValue, static_cast<uint32_t>(ADC_MIN_VALUE), static_cast<uint32_t>(ADC_MAX_VALUE), static_cast<uint32_t>(0), static_cast<uint32_t>(MIDI_7_BIT_VALUE_MAX));
    analog.update();

    for (int i=0; i<MAX_NUMBER_OF_ANALOG; i++)
        EXPECT_EQ(MIDIstub::detail::ccValue[i], expectedValue);

    EXPECT_EQ(MIDIstub::detail::ccMessageCounter, MAX_NUMBER_OF_ANALOG);

    //try again with the same values
    //no values should be sent
    MIDIstub::reset();
    analog.update();

    EXPECT_EQ(MIDIstub::detail::ccMessageCounter, 0);

    //reset all values
    for (int i=0; i<MAX_NUMBER_OF_ANALOG; i++)
        analog.debounceReset(i);

    //try with max value
    Board::detail::adcReturnValue = ADC_MAX_VALUE;

    MIDIstub::reset();
    analog.update();

    for (int i=0; i<MAX_NUMBER_OF_ANALOG; i++)
        EXPECT_EQ(MIDIstub::detail::ccValue[i], MIDI_7_BIT_VALUE_MAX);

    EXPECT_EQ(MIDIstub::detail::ccMessageCounter, MAX_NUMBER_OF_ANALOG);

    //test the following scenario:
    //1) record new value
    //2) change direction
    //3) verify that the new value cannot be recorded unless difference between them is ANALOG_STEP_MIN_DIFF_7_BIT*2

    for (int i=0; i<MAX_NUMBER_OF_ANALOG; i++)
        analog.debounceReset(i);

    MIDIstub::reset();
    Board::detail::adcReturnValue = 512;
    expectedValue = mapRange(Board::detail::adcReturnValue, static_cast<uint32_t>(0), static_cast<uint32_t>(ADC_MAX_VALUE), static_cast<uint32_t>(0), static_cast<uint32_t>(MIDI_7_BIT_VALUE_MAX));
    analog.update();
    EXPECT_EQ(MIDIstub::detail::ccMessageCounter, MAX_NUMBER_OF_ANALOG);

    //no values should be sent for now
    MIDIstub::reset();
    Board::detail::adcReturnValue = 512-ANALOG_STEP_MIN_DIFF_7_BIT;
    expectedValue = mapRange(Board::detail::adcReturnValue, static_cast<uint32_t>(0), static_cast<uint32_t>(ADC_MAX_VALUE), static_cast<uint32_t>(0), static_cast<uint32_t>(MIDI_7_BIT_VALUE_MAX));
    analog.update();
    EXPECT_EQ(MIDIstub::detail::ccMessageCounter, 0);

    //enough difference has been reached -> new values should be sent
    MIDIstub::reset();
    Board::detail::adcReturnValue = 512-(ANALOG_STEP_MIN_DIFF_7_BIT*2);
    expectedValue = mapRange(Board::detail::adcReturnValue, static_cast<uint32_t>(0), static_cast<uint32_t>(ADC_MAX_VALUE), static_cast<uint32_t>(0), static_cast<uint32_t>(MIDI_7_BIT_VALUE_MAX));
    analog.update();
    EXPECT_EQ(MIDIstub::detail::ccMessageCounter, MAX_NUMBER_OF_ANALOG);
}

TEST_F(PotentiometerTest, PitchBendTest)
{
    //set known state
    for (int i=0; i<MAX_NUMBER_OF_ANALOG; i++)
    {
        //enable all analog components
        EXPECT_EQ(database.update(DB_BLOCK_ANALOG, dbSection_analog_enable, i, 1), true);

        //disable invert state
        EXPECT_EQ(database.update(DB_BLOCK_ANALOG, dbSection_analog_invert, i, 0), true);

        //configure all analog components as potentiometers with Pitch Bend MIDI message
        EXPECT_EQ(database.update(DB_BLOCK_ANALOG, dbSection_analog_type, i, aType_PitchBend), true);

        //set all lower limits to 0
        EXPECT_EQ(database.update(DB_BLOCK_ANALOG, dbSection_analog_lowerLimit, i, 0), true);

        //set all upper limits to MIDI_14_BIT_VALUE_MAX
        EXPECT_EQ(database.update(DB_BLOCK_ANALOG, dbSection_analog_upperLimit, i, MIDI_14_BIT_VALUE_MAX), true);
    }

    uint16_t expectedValue;

    Board::detail::adcReturnValue = 1000;

    MIDIstub::reset();
    expectedValue = mapRange(Board::detail::adcReturnValue, static_cast<uint32_t>(ADC_MIN_VALUE), static_cast<uint32_t>(ADC_MAX_VALUE), static_cast<uint32_t>(0), static_cast<uint32_t>(MIDI_14_BIT_VALUE_MAX));
    analog.update();

    for (int i=0; i<MAX_NUMBER_OF_ANALOG; i++)
        EXPECT_EQ(MIDIstub::detail::pbValue[i], expectedValue);

    EXPECT_EQ(MIDIstub::detail::pbMessageCounter, MAX_NUMBER_OF_ANALOG);

    //call update again, verify no values have been sent
    MIDIstub::reset();

    analog.update();
    EXPECT_EQ(MIDIstub::detail::pbMessageCounter, 0);

    //try with max value
    Board::detail::adcReturnValue = ADC_MAX_VALUE;
    analog.update();

    for (int i=0; i<MAX_NUMBER_OF_ANALOG; i++)
        EXPECT_EQ(MIDIstub::detail::pbValue[i], MIDI_14_BIT_VALUE_MAX);
}

TEST_F(PotentiometerTest, ScalingAndInversion)
{
    //set known state
    for (int i=0; i<MAX_NUMBER_OF_ANALOG; i++)
    {
        //enable all analog components
        EXPECT_EQ(database.update(DB_BLOCK_ANALOG, dbSection_analog_enable, i, 1), true);

        //disable invert state
        EXPECT_EQ(database.update(DB_BLOCK_ANALOG, dbSection_analog_invert, i, 0), true);

        //configure all analog components as potentiometers with CC MIDI message
        EXPECT_EQ(database.update(DB_BLOCK_ANALOG, dbSection_analog_type, i, aType_potentiometer_cc), true);

        //set all lower limits to 0
        EXPECT_EQ(database.update(DB_BLOCK_ANALOG, dbSection_analog_lowerLimit, i, 0), true);

        //set all upper limits to MIDI_14_BIT_VALUE_MAX
        EXPECT_EQ(database.update(DB_BLOCK_ANALOG, dbSection_analog_upperLimit, i, MIDI_14_BIT_VALUE_MAX), true);
    }

    for (uint32_t i=ADC_MAX_VALUE+1; i-- > 0; )
    {
        MIDIstub::reset();
        Board::detail::adcReturnValue = i;
        uint16_t expectedValue = mapRange(i, static_cast<uint32_t>(ADC_MIN_VALUE), static_cast<uint32_t>(ADC_MAX_VALUE), static_cast<uint32_t>(0), static_cast<uint32_t>(MIDI_7_BIT_VALUE_MAX));

        analog.update();
        EXPECT_EQ(MIDIstub::detail::ccMessageCounter, MAX_NUMBER_OF_ANALOG);

        for (int j=0; j<MAX_NUMBER_OF_ANALOG; j++)
        {
            EXPECT_EQ(MIDIstub::detail::ccValue[j], expectedValue);
            //reset debouncing state for easier testing - otherwise Board::detail::adcReturnValue
            //won't be accepted in next analog.update() call
            analog.debounceReset(j);
        }
    }

    //now enable inversion
    for (int i=0; i<MAX_NUMBER_OF_ANALOG; i++)
    {
        //enable inversion for all analog components
        EXPECT_EQ(database.update(DB_BLOCK_ANALOG, dbSection_analog_invert, i, 1), true);
        analog.debounceReset(i);
    }

    for (uint32_t i=ADC_MAX_VALUE+1; i-- > 0; )
    {
        MIDIstub::reset();
        Board::detail::adcReturnValue = i;
        uint32_t expectedValue = static_cast<uint32_t>(MIDI_7_BIT_VALUE_MAX) - mapRange(i, static_cast<uint32_t>(ADC_MIN_VALUE), static_cast<uint32_t>(ADC_MAX_VALUE), static_cast<uint32_t>(0), static_cast<uint32_t>(MIDI_7_BIT_VALUE_MAX));

        analog.update();
        EXPECT_EQ(MIDIstub::detail::ccMessageCounter, MAX_NUMBER_OF_ANALOG);

        for (int j=0; j<MAX_NUMBER_OF_ANALOG; j++)
        {
            EXPECT_EQ(MIDIstub::detail::ccValue[j], expectedValue);
            analog.debounceReset(j);
        }
    }

    //now do the same thing for pitch bend analog type
    for (int i=0; i<MAX_NUMBER_OF_ANALOG; i++)
    {
        //disable invert state
        EXPECT_EQ(database.update(DB_BLOCK_ANALOG, dbSection_analog_invert, i, 0), true);

        //configure all analog components as potentiometers with Pitch Bend MIDI message
        EXPECT_EQ(database.update(DB_BLOCK_ANALOG, dbSection_analog_type, i, aType_PitchBend), true);

        analog.debounceReset(i);
    }

    for (uint32_t i=ADC_MAX_VALUE+1; i-- > 0; )
    {
        MIDIstub::reset();
        Board::detail::adcReturnValue = i;
        auto expectedValue = mapRange(i, static_cast<uint32_t>(ADC_MIN_VALUE), static_cast<uint32_t>(ADC_MAX_VALUE), static_cast<uint32_t>(0), static_cast<uint32_t>(MIDI_14_BIT_VALUE_MAX));

        analog.update();
        EXPECT_EQ(MIDIstub::detail::pbMessageCounter, MAX_NUMBER_OF_ANALOG);

        for (int j=0; j<MAX_NUMBER_OF_ANALOG; j++)
        {
            EXPECT_EQ(MIDIstub::detail::pbValue[j], expectedValue);
            analog.debounceReset(j);
        }
    }

    //now enable inversion
    for (uint32_t i=0; i<MAX_NUMBER_OF_ANALOG; i++)
    {
        EXPECT_EQ(database.update(DB_BLOCK_ANALOG, dbSection_analog_invert, i, 1), true);
        analog.debounceReset(i);
    }

    for (uint32_t i=ADC_MAX_VALUE+1; i-- > 0; )
    {
        MIDIstub::reset();
        Board::detail::adcReturnValue = i;
        uint16_t expectedValue = MIDI_14_BIT_VALUE_MAX - mapRange(i, static_cast<uint32_t>(ADC_MIN_VALUE), static_cast<uint32_t>(ADC_MAX_VALUE), static_cast<uint32_t>(0), static_cast<uint32_t>(MIDI_14_BIT_VALUE_MAX));

        analog.update();
        EXPECT_EQ(MIDIstub::detail::pbMessageCounter, MAX_NUMBER_OF_ANALOG);

        for (int j=0; j<MAX_NUMBER_OF_ANALOG; j++)
        {
            EXPECT_EQ(MIDIstub::detail::pbValue[j], expectedValue);
            analog.debounceReset(j);
        }
    }

    //try with scaled upper value
    //still using pitch bend message type

    for (int i=0; i<MAX_NUMBER_OF_ANALOG; i++)
        analog.debounceReset(i);

    const uint32_t scaledUpperValue = 100;

    for (int i=0; i<MAX_NUMBER_OF_ANALOG; i++)
    {
        EXPECT_EQ(database.update(DB_BLOCK_ANALOG, dbSection_analog_invert, i, 0), true);
        EXPECT_EQ(database.update(DB_BLOCK_ANALOG, dbSection_analog_upperLimit, i, scaledUpperValue), true);
    }

    MIDIstub::reset();
    Board::detail::adcReturnValue = ADC_MAX_VALUE;
    auto expectedValue = scaledUpperValue;
    analog.update();
    EXPECT_EQ(MIDIstub::detail::pbMessageCounter, MAX_NUMBER_OF_ANALOG);

    for (int i=0; i<MAX_NUMBER_OF_ANALOG; i++)
        EXPECT_EQ(MIDIstub::detail::pbValue[i], expectedValue);
}<commit_msg>change code order<commit_after>#include <gtest/gtest.h>
#include "../../../../application/interface/analog/Analog.h"
#include "../../../../application/interface/CInfo.h"
#include "../../../../modules/midi/src/MIDI.h"
#include "../../../../modules/core/src/general/Timing.h"
#include "../../../../modules/core/src/general/Misc.h"
#include "../../../../application/database/Database.h"
#include "../../stubs/database/DB_ReadWrite.h"

//this represents theoretical maximum amount of analog components
//this test is run for all board variants
//make sure that the default is largest amount of analog components
#define MAX_MIDI_MESSAGES   96

namespace Board
{
    namespace detail
    {
        uint32_t adcReturnValue;
    }

    int16_t getAnalogValue(uint8_t analogID)
    {
        return detail::adcReturnValue;
    }

    void continueADCreadout()
    {

    }

    bool analogDataAvailable()
    {
        return true;
    }
}

namespace MIDIstub
{
    namespace detail
    {
        uint32_t ccMessageCounter = 0;
        uint32_t pbMessageCounter = 0;
        uint8_t ccValue[MAX_MIDI_MESSAGES] = {};
        uint16_t pbValue[MAX_MIDI_MESSAGES] = {};
    }

    void reset()
    {
        using namespace detail;

        ccMessageCounter = 0;
        pbMessageCounter = 0;
    }
}

void MIDI::sendControlChange(uint8_t inControlNumber, uint8_t inControlValue, uint8_t inChannel)
{
    MIDIstub::detail::ccValue[MIDIstub::detail::ccMessageCounter] = inControlValue;
    MIDIstub::detail::ccMessageCounter++;
}

void MIDI::sendPitchBend(uint16_t inPitchValue, uint8_t inChannel)
{
    MIDIstub::detail::pbValue[MIDIstub::detail::pbMessageCounter] = inPitchValue;
    MIDIstub::detail::pbMessageCounter++;
}

void MIDI::sendNoteOn(uint8_t inNoteNumber, uint8_t inVelocity, uint8_t inChannel)
{

}

void MIDI::sendNoteOff(uint8_t inNoteNumber, uint8_t inVelocity, uint8_t inChannel)
{

}

class PotentiometerTest : public ::testing::Test
{
    protected:
    virtual void SetUp()
    {
        cinfoHandler = nullptr;

        //init checks - no point in running further tests if these conditions fail
        EXPECT_TRUE(database.init());
        EXPECT_TRUE(database.getDBsize() < LESSDB_SIZE);
        EXPECT_TRUE(database.isSignatureValid());
    }

    virtual void TearDown()
    {

    }

    Database database = Database(DatabaseStub::memoryRead, DatabaseStub::memoryWrite);
    MIDI midi;
    Analog analog = Analog(database, midi);
};

TEST_F(PotentiometerTest, CCtest)
{
    //set known state
    for (int i=0; i<MAX_NUMBER_OF_ANALOG; i++)
    {
        //enable all analog components
        EXPECT_EQ(database.update(DB_BLOCK_ANALOG, dbSection_analog_enable, i, 1), true);

        //disable invert state
        EXPECT_EQ(database.update(DB_BLOCK_ANALOG, dbSection_analog_invert, i, 0), true);

        //configure all analog components as potentiometers with CC MIDI message
        EXPECT_EQ(database.update(DB_BLOCK_ANALOG, dbSection_analog_type, i, aType_potentiometer_cc), true);

        //set all lower limits to 0
        EXPECT_EQ(database.update(DB_BLOCK_ANALOG, dbSection_analog_lowerLimit, i, 0), true);

        //set all upper limits to MIDI_7_BIT_VALUE_MAX
        EXPECT_EQ(database.update(DB_BLOCK_ANALOG, dbSection_analog_upperLimit, i, MIDI_7_BIT_VALUE_MAX), true);
    }

    uint16_t expectedValue;

    Board::detail::adcReturnValue = 1000;

    MIDIstub::reset();
    expectedValue = mapRange(Board::detail::adcReturnValue, static_cast<uint32_t>(ADC_MIN_VALUE), static_cast<uint32_t>(ADC_MAX_VALUE), static_cast<uint32_t>(0), static_cast<uint32_t>(MIDI_7_BIT_VALUE_MAX));
    analog.update();

    for (int i=0; i<MAX_NUMBER_OF_ANALOG; i++)
        EXPECT_EQ(MIDIstub::detail::ccValue[i], expectedValue);

    EXPECT_EQ(MIDIstub::detail::ccMessageCounter, MAX_NUMBER_OF_ANALOG);

    //try again with the same values
    //no values should be sent
    MIDIstub::reset();
    analog.update();

    EXPECT_EQ(MIDIstub::detail::ccMessageCounter, 0);

    //reset all values
    for (int i=0; i<MAX_NUMBER_OF_ANALOG; i++)
        analog.debounceReset(i);

    //try with max value
    Board::detail::adcReturnValue = ADC_MAX_VALUE;

    MIDIstub::reset();
    analog.update();

    for (int i=0; i<MAX_NUMBER_OF_ANALOG; i++)
        EXPECT_EQ(MIDIstub::detail::ccValue[i], MIDI_7_BIT_VALUE_MAX);

    EXPECT_EQ(MIDIstub::detail::ccMessageCounter, MAX_NUMBER_OF_ANALOG);

    //test the following scenario:
    //1) record new value
    //2) change direction
    //3) verify that the new value cannot be recorded unless difference between them is ANALOG_STEP_MIN_DIFF_7_BIT*2

    for (int i=0; i<MAX_NUMBER_OF_ANALOG; i++)
        analog.debounceReset(i);

    MIDIstub::reset();
    Board::detail::adcReturnValue = 512;
    expectedValue = mapRange(Board::detail::adcReturnValue, static_cast<uint32_t>(0), static_cast<uint32_t>(ADC_MAX_VALUE), static_cast<uint32_t>(0), static_cast<uint32_t>(MIDI_7_BIT_VALUE_MAX));
    analog.update();
    EXPECT_EQ(MIDIstub::detail::ccMessageCounter, MAX_NUMBER_OF_ANALOG);

    //no values should be sent for now
    MIDIstub::reset();
    Board::detail::adcReturnValue = 512-ANALOG_STEP_MIN_DIFF_7_BIT;
    expectedValue = mapRange(Board::detail::adcReturnValue, static_cast<uint32_t>(0), static_cast<uint32_t>(ADC_MAX_VALUE), static_cast<uint32_t>(0), static_cast<uint32_t>(MIDI_7_BIT_VALUE_MAX));
    analog.update();
    EXPECT_EQ(MIDIstub::detail::ccMessageCounter, 0);

    //enough difference has been reached -> new values should be sent
    MIDIstub::reset();
    Board::detail::adcReturnValue = 512-(ANALOG_STEP_MIN_DIFF_7_BIT*2);
    expectedValue = mapRange(Board::detail::adcReturnValue, static_cast<uint32_t>(0), static_cast<uint32_t>(ADC_MAX_VALUE), static_cast<uint32_t>(0), static_cast<uint32_t>(MIDI_7_BIT_VALUE_MAX));
    analog.update();
    EXPECT_EQ(MIDIstub::detail::ccMessageCounter, MAX_NUMBER_OF_ANALOG);
}

TEST_F(PotentiometerTest, PitchBendTest)
{
    //set known state
    for (int i=0; i<MAX_NUMBER_OF_ANALOG; i++)
    {
        //enable all analog components
        EXPECT_EQ(database.update(DB_BLOCK_ANALOG, dbSection_analog_enable, i, 1), true);

        //disable invert state
        EXPECT_EQ(database.update(DB_BLOCK_ANALOG, dbSection_analog_invert, i, 0), true);

        //configure all analog components as potentiometers with Pitch Bend MIDI message
        EXPECT_EQ(database.update(DB_BLOCK_ANALOG, dbSection_analog_type, i, aType_PitchBend), true);

        //set all lower limits to 0
        EXPECT_EQ(database.update(DB_BLOCK_ANALOG, dbSection_analog_lowerLimit, i, 0), true);

        //set all upper limits to MIDI_14_BIT_VALUE_MAX
        EXPECT_EQ(database.update(DB_BLOCK_ANALOG, dbSection_analog_upperLimit, i, MIDI_14_BIT_VALUE_MAX), true);
    }

    uint16_t expectedValue;

    Board::detail::adcReturnValue = 1000;

    MIDIstub::reset();
    expectedValue = mapRange(Board::detail::adcReturnValue, static_cast<uint32_t>(ADC_MIN_VALUE), static_cast<uint32_t>(ADC_MAX_VALUE), static_cast<uint32_t>(0), static_cast<uint32_t>(MIDI_14_BIT_VALUE_MAX));
    analog.update();

    for (int i=0; i<MAX_NUMBER_OF_ANALOG; i++)
        EXPECT_EQ(MIDIstub::detail::pbValue[i], expectedValue);

    EXPECT_EQ(MIDIstub::detail::pbMessageCounter, MAX_NUMBER_OF_ANALOG);

    //call update again, verify no values have been sent
    MIDIstub::reset();

    analog.update();
    EXPECT_EQ(MIDIstub::detail::pbMessageCounter, 0);

    //try with max value
    Board::detail::adcReturnValue = ADC_MAX_VALUE;
    analog.update();

    for (int i=0; i<MAX_NUMBER_OF_ANALOG; i++)
        EXPECT_EQ(MIDIstub::detail::pbValue[i], MIDI_14_BIT_VALUE_MAX);
}

TEST_F(PotentiometerTest, ScalingAndInversion)
{
    //set known state
    for (int i=0; i<MAX_NUMBER_OF_ANALOG; i++)
    {
        //enable all analog components
        EXPECT_EQ(database.update(DB_BLOCK_ANALOG, dbSection_analog_enable, i, 1), true);

        //disable invert state
        EXPECT_EQ(database.update(DB_BLOCK_ANALOG, dbSection_analog_invert, i, 0), true);

        //configure all analog components as potentiometers with CC MIDI message
        EXPECT_EQ(database.update(DB_BLOCK_ANALOG, dbSection_analog_type, i, aType_potentiometer_cc), true);

        //set all lower limits to 0
        EXPECT_EQ(database.update(DB_BLOCK_ANALOG, dbSection_analog_lowerLimit, i, 0), true);

        //set all upper limits to MIDI_14_BIT_VALUE_MAX
        EXPECT_EQ(database.update(DB_BLOCK_ANALOG, dbSection_analog_upperLimit, i, MIDI_14_BIT_VALUE_MAX), true);
    }

    for (uint32_t i=ADC_MAX_VALUE+1; i-- > 0; )
    {
        MIDIstub::reset();
        Board::detail::adcReturnValue = i;
        uint16_t expectedValue = mapRange(i, static_cast<uint32_t>(ADC_MIN_VALUE), static_cast<uint32_t>(ADC_MAX_VALUE), static_cast<uint32_t>(0), static_cast<uint32_t>(MIDI_7_BIT_VALUE_MAX));

        analog.update();
        EXPECT_EQ(MIDIstub::detail::ccMessageCounter, MAX_NUMBER_OF_ANALOG);

        for (int j=0; j<MAX_NUMBER_OF_ANALOG; j++)
        {
            EXPECT_EQ(MIDIstub::detail::ccValue[j], expectedValue);
            //reset debouncing state for easier testing - otherwise Board::detail::adcReturnValue
            //won't be accepted in next analog.update() call
            analog.debounceReset(j);
        }
    }

    //now enable inversion
    for (int i=0; i<MAX_NUMBER_OF_ANALOG; i++)
    {
        //enable inversion for all analog components
        EXPECT_EQ(database.update(DB_BLOCK_ANALOG, dbSection_analog_invert, i, 1), true);
        analog.debounceReset(i);
    }

    for (uint32_t i=ADC_MAX_VALUE+1; i-- > 0; )
    {
        MIDIstub::reset();
        Board::detail::adcReturnValue = i;
        uint32_t expectedValue = static_cast<uint32_t>(MIDI_7_BIT_VALUE_MAX) - mapRange(i, static_cast<uint32_t>(ADC_MIN_VALUE), static_cast<uint32_t>(ADC_MAX_VALUE), static_cast<uint32_t>(0), static_cast<uint32_t>(MIDI_7_BIT_VALUE_MAX));

        analog.update();
        EXPECT_EQ(MIDIstub::detail::ccMessageCounter, MAX_NUMBER_OF_ANALOG);

        for (int j=0; j<MAX_NUMBER_OF_ANALOG; j++)
        {
            EXPECT_EQ(MIDIstub::detail::ccValue[j], expectedValue);
            analog.debounceReset(j);
        }
    }

    //now do the same thing for pitch bend analog type
    for (int i=0; i<MAX_NUMBER_OF_ANALOG; i++)
    {
        //disable invert state
        EXPECT_EQ(database.update(DB_BLOCK_ANALOG, dbSection_analog_invert, i, 0), true);

        //configure all analog components as potentiometers with Pitch Bend MIDI message
        EXPECT_EQ(database.update(DB_BLOCK_ANALOG, dbSection_analog_type, i, aType_PitchBend), true);

        analog.debounceReset(i);
    }

    for (uint32_t i=ADC_MAX_VALUE+1; i-- > 0; )
    {
        MIDIstub::reset();
        Board::detail::adcReturnValue = i;
        auto expectedValue = mapRange(i, static_cast<uint32_t>(ADC_MIN_VALUE), static_cast<uint32_t>(ADC_MAX_VALUE), static_cast<uint32_t>(0), static_cast<uint32_t>(MIDI_14_BIT_VALUE_MAX));

        analog.update();
        EXPECT_EQ(MIDIstub::detail::pbMessageCounter, MAX_NUMBER_OF_ANALOG);

        for (int j=0; j<MAX_NUMBER_OF_ANALOG; j++)
        {
            EXPECT_EQ(MIDIstub::detail::pbValue[j], expectedValue);
            analog.debounceReset(j);
        }
    }

    //now enable inversion
    for (uint32_t i=0; i<MAX_NUMBER_OF_ANALOG; i++)
    {
        EXPECT_EQ(database.update(DB_BLOCK_ANALOG, dbSection_analog_invert, i, 1), true);
        analog.debounceReset(i);
    }

    for (uint32_t i=ADC_MAX_VALUE+1; i-- > 0; )
    {
        MIDIstub::reset();
        Board::detail::adcReturnValue = i;
        uint16_t expectedValue = MIDI_14_BIT_VALUE_MAX - mapRange(i, static_cast<uint32_t>(ADC_MIN_VALUE), static_cast<uint32_t>(ADC_MAX_VALUE), static_cast<uint32_t>(0), static_cast<uint32_t>(MIDI_14_BIT_VALUE_MAX));

        analog.update();
        EXPECT_EQ(MIDIstub::detail::pbMessageCounter, MAX_NUMBER_OF_ANALOG);

        for (int j=0; j<MAX_NUMBER_OF_ANALOG; j++)
        {
            EXPECT_EQ(MIDIstub::detail::pbValue[j], expectedValue);
            analog.debounceReset(j);
        }
    }

    //try with scaled upper value
    //still using pitch bend message type

    for (int i=0; i<MAX_NUMBER_OF_ANALOG; i++)
        analog.debounceReset(i);

    const uint32_t scaledUpperValue = 100;

    for (int i=0; i<MAX_NUMBER_OF_ANALOG; i++)
    {
        EXPECT_EQ(database.update(DB_BLOCK_ANALOG, dbSection_analog_invert, i, 0), true);
        EXPECT_EQ(database.update(DB_BLOCK_ANALOG, dbSection_analog_upperLimit, i, scaledUpperValue), true);
    }

    MIDIstub::reset();
    Board::detail::adcReturnValue = ADC_MAX_VALUE;
    auto expectedValue = scaledUpperValue;
    analog.update();
    EXPECT_EQ(MIDIstub::detail::pbMessageCounter, MAX_NUMBER_OF_ANALOG);

    for (int i=0; i<MAX_NUMBER_OF_ANALOG; i++)
        EXPECT_EQ(MIDIstub::detail::pbValue[i], expectedValue);
}<|endoftext|>


<commit_before>#include <cassert>
#include <cstdio>
#include <chrono>
#include <future>
#include <functional>
#include <iostream>
#include <map>
#include <string>
#include <vector>
#include "xxhash.h"
#include "lz4mt.h"
#include "lz4mt_benchmark.h"

namespace {

typedef std::chrono::high_resolution_clock Clock;
typedef Clock::time_point TimePoint;

TimePoint getTime() {
	return Clock::now();
}

TimePoint getSyncTime() {
	auto t0 = getTime();
	for(;;) {
		auto t = getTime();
		if(t0 != t) {
			return t;
		}
	}
}

double getTimeSpan(const TimePoint& tStart, const TimePoint& tEnd) {
	using namespace std::chrono;
	auto dt = duration_cast<seconds>(tEnd - tStart).count();
	return static_cast<double>(dt);
}

} // anonymous namespace

namespace Lz4Mt {

Benchmark::Benchmark()
	: enable(false)
	, nIter(3)
{}

Benchmark::~Benchmark()
{}

int Benchmark::measure(
	  Lz4MtContext& cx
	, const Lz4MtStreamDescriptor& sd
) {
	using namespace std;

	auto* ctx = &cx;
	const bool singleThread = 0 != (ctx->mode & LZ4MT_MODE_SEQUENTIAL);

	auto totalFileSize = 0.0;
	auto totalCompressSize = 0.0;
	auto totalCompressTime = 0.0;
	auto totalDecompressTime = 0.0;

	const auto* fmt = "%1i-%-14.14s :%10.0f ->%10.0f (%6.2f%%)"
					  ",%7.1f MiB/s, %7.1f MiB/s\r";

	const auto TIMELOOP = 2.0;	// sec
	for(const auto& filename : files) {
		vector<char> inpBuf(static_cast<size_t>(getFilesize(filename)));
		if(!openIstream(ctx, filename)) {
			std::cerr << "Pb opening " << filename << "\n";
			return 11;
		} else {
			std::cerr << "Loading " << filename << "...        \r";
			size_t readSize = ctx->read(ctx, inpBuf.data()
									  , static_cast<int>(inpBuf.size()));
			closeIstream(ctx);

			if(readSize != inpBuf.size()) {
				std::cerr << "\nError: problem reading file '"
						  << filename << "' !!    \n";
				return 13;
			}
		}

		const auto inpHash = XXH32(inpBuf.data()
								   , static_cast<int>(inpBuf.size()), 0);

		vector<char> outBuf;
		struct ChunkParamters {
			int		id;
			char*	inpBuffer;
			size_t	inpSize;
			char*	outBuffer;
			size_t	outSize;
			size_t	cmpSize;
			size_t	decSize;
		};
		std::vector<ChunkParamters> chunkParameterss;

		size_t chunkSize	= (1 << (8 + (2 * sd.bd.blockMaximumSize)));
		auto maxChunkSize	= ctx->compressBound(static_cast<int>(chunkSize));
		auto nChunk			= (inpBuf.size() / chunkSize) + 1;

		{
			outBuf.resize(nChunk * maxChunkSize);
			chunkParameterss.resize(nChunk);

			auto r = inpBuf.size();
			for(auto& e : chunkParameterss) {
				auto i = static_cast<int>(&e - &chunkParameterss[0]);
				e.id		= i;
				e.inpBuffer	= &inpBuf[i * chunkSize];
				e.inpSize	= r > chunkSize ? chunkSize : r;
				e.outBuffer	= &outBuf[i * maxChunkSize];
				e.outSize	= maxChunkSize;
				e.cmpSize	= 0;
				e.decSize	= 0;
				r -= e.inpSize;
			}
		}

		std::vector<future<void>> futures(chunkParameterss.size());

		fprintf(stderr, "\r%79s\r", "");

		const auto filesize = static_cast<double>(inpBuf.size());
		auto cmpSize = 0.0;
		auto minCompressionTime   = 10000000.0;
		auto minDecompressionTime = 10000000.0;

		auto b = [=, &futures, &chunkParameterss] (
			std::function<void(ChunkParamters*)> fChunk
		) -> double
		{
			auto ms0 = getSyncTime();
			auto ms1 = ms0;
			int loopCount = 0;

			while(getTimeSpan(ms0, ms1 = getTime()) < TIMELOOP) {
				for(auto& e : chunkParameterss) {
					futures[e.id] = async(launch::async, fChunk, &e);
				}
				for(auto& e : futures) {
					e.wait();
				}
				++loopCount;
			}

			return getTimeSpan(ms0, ms1) / static_cast<double>(loopCount);
		};

		auto report = [&](int iLoop) {
			fprintf(stderr, fmt
				, iLoop
				, filename.c_str()
				, filesize
				, cmpSize
				, cmpSize / filesize * 100.0
				, filesize / 1024.0 / 1024.0 / minCompressionTime
				, filesize / 1024.0 / 1024.0 / minDecompressionTime
			);
		};

		for(int iLoop = 1, nLoop = nIter; iLoop <= nLoop; ++iLoop) {
			//	compression
			for(size_t i = 0; i < outBuf.size(); ++i) {
				outBuf[i] = static_cast<char>(i);
			}
			auto tComp = b(
				[ctx, singleThread, &futures] (ChunkParamters* cp) {
					if(singleThread && cp->id > 0) {
						futures[cp->id-1].wait();
					}
					cp->cmpSize = ctx->compress(
						  cp->inpBuffer
						, cp->outBuffer
						, static_cast<int>(cp->inpSize)
						, static_cast<int>(cp->outSize)
					);
				}
			);
			minCompressionTime = min(minCompressionTime, tComp);

			cmpSize = 0.0;
			for(auto& c : chunkParameterss) {
				cmpSize += static_cast<double>(c.cmpSize);
			}

			report(iLoop);

			//	decompression
			for(auto& e : inpBuf) {
				e = 0;
			}
			auto tDecomp = b(
				[ctx, singleThread, &futures] (ChunkParamters* cp) {
					if(singleThread && cp->id > 0) {
						futures[cp->id-1].wait();
					}
					cp->decSize = ctx->decompress(
						  cp->outBuffer
						, cp->inpBuffer
						, static_cast<int>(cp->cmpSize)
						, static_cast<int>(cp->inpSize)
					);
				}
			);
			minDecompressionTime = min(minDecompressionTime, tDecomp);

			report(iLoop);

			const auto outHash = XXH32(
				  inpBuf.data()
				, static_cast<int>(inpBuf.size())
				, 0
			);

			if(inpHash != outHash) {
		 		fprintf(stderr
					, "\n!!! WARNING !!! "
					  "%14s : Invalid Checksum : %x != %x\n"
					, filename.c_str()
					, inpHash
					, outHash
				);
				break;
			}
		}
		fprintf(stderr, "\n");

		totalFileSize += filesize;
		totalCompressSize += cmpSize;
		totalCompressTime += minCompressionTime;
		totalDecompressTime += minDecompressionTime;
	}

	if(!files.empty()) {
		fprintf(stderr, fmt
			, 0
			, "  TOTAL"
			, totalFileSize
			, totalCompressSize
			, totalCompressSize / totalFileSize * 100.0
			, totalFileSize / 1024.0 / 1024.0 / totalCompressTime
			, totalFileSize / 1024.0 / 1024.0 / totalDecompressTime
		);
		fprintf(stderr, "\n");
	}

	return 0;
}

} // namespace Lz4Mt
<commit_msg>refactoring benchmark function<commit_after>#include <cassert>
#include <cstdio>
#include <chrono>
#include <future>
#include <functional>
#include <iostream>
#include <map>
#include <string>
#include <vector>
#include "xxhash.h"
#include "lz4mt.h"
#include "lz4mt_benchmark.h"

namespace {

typedef std::chrono::high_resolution_clock Clock;
typedef Clock::time_point TimePoint;

TimePoint getTime() {
	return Clock::now();
}

TimePoint getSyncTime() {
	const auto t0 = getTime();
	for(;;) {
		auto t = getTime();
		if(t0 != t) {
			return t;
		}
	}
}

double getTimeSpan(const TimePoint& tStart, const TimePoint& tEnd) {
	using namespace std::chrono;
	const auto dt = duration_cast<seconds>(tEnd - tStart).count();
	return static_cast<double>(dt);
}

} // anonymous namespace

namespace Lz4Mt {

Benchmark::Benchmark()
	: enable(false)
	, nIter(3)
{}

Benchmark::~Benchmark()
{}

int Benchmark::measure(
	  Lz4MtContext& cx
	, const Lz4MtStreamDescriptor& sd
) {
	using namespace std;

	const auto msgClearLine = [] {
		fprintf(stderr, "\r%79s\r", "");
	};

	const auto msgNewline = [] {
		fprintf(stderr, "\n");
	};

	const auto msgErrOpening = [](const string& filename) {
		fprintf(stderr, "Error: problem opening %s\n", filename.c_str());
	};

	const auto msgLoading = [](const string& filename) {
		fprintf(stderr, "Loading %s...\r", filename.c_str());
	};

	const auto msgErrReading = [](const string& filename) {
		fprintf(stderr, "\nError: problem reading file %s\n"
				, filename.c_str());
	};

	const auto msgErrChecksum = []
		(const string& filename, uint32_t inpHash, uint32_t outHash)
	{
		fprintf(stderr
			, "\n!!! WARNING !!! %14s : Invalid Checksum : %08x != %08x\n"
			, filename.c_str()
			, inpHash
			, outHash
		);
	};

	const auto msgReport = []
		(  int iLoop
		 , const string& filename
		 , size_t filesize
		 , size_t cmpSize
		 , double minCompressionTime
		 , double minDecompressionTime
		)
	{
		const auto* fmt = "%1i-%-14.14s :%10.0f ->%10.0f (%6.2f%%)"
						  ",%7.1f MiB/s, %7.1f MiB/s\r";

		fprintf(stderr, fmt
			, iLoop
			, filename.c_str()
			, (double) (filesize)
			, (double) (cmpSize)
			, (double) (cmpSize * 100.0 / filesize)
			, (double) (filesize / 1024.0 / 1024.0 / minCompressionTime)
			, (double) (filesize / 1024.0 / 1024.0 / minDecompressionTime)
		);
	};

	auto* ctx = &cx;
	const bool singleThread = 0 != (ctx->mode & LZ4MT_MODE_SEQUENTIAL);
	size_t totalFileSize = 0;
	size_t totalCompressSize = 0;
	double totalCompressTime = 0.0;
	double totalDecompressTime = 0.0;
	const auto TIMELOOP = 2.0;	// sec

	for(const auto& filename : files) {
		vector<char> inpBuf(static_cast<size_t>(getFilesize(filename)));
		{
			if(!openIstream(ctx, filename)) {
				msgErrOpening(filename);
				return 11;
			}

			msgLoading(filename);
			const size_t readSize = ctx->read(ctx, inpBuf.data()
									  , static_cast<int>(inpBuf.size()));
			closeIstream(ctx);

			if(inpBuf.size() != readSize) {
				msgErrReading(filename);
				return 13;
			}
		}
		msgClearLine();

		const auto inpHash =
			XXH32(inpBuf.data(), static_cast<int>(inpBuf.size()), 0);
		const size_t chunkSize	= (1 << (8 + (2 * sd.bd.blockMaximumSize)));
		const auto nChunk		= (inpBuf.size() / chunkSize) + 1;
		const auto maxChunkSize	=
			static_cast<size_t>(
				ctx->compressBound(static_cast<int>(chunkSize)));

		vector<char> outBuf(nChunk * maxChunkSize);

		struct Chunk {
			int		id;
			char*	inpPtr;
			size_t	inpSize;
			char*	outPtr;
			size_t	outSize;
			size_t	cmpSize;
			size_t	decSize;
		};

		vector<Chunk> chunks(nChunk);
		{
			auto r = inpBuf.size();
			for(auto& e : chunks) {
				auto i = static_cast<int>(&e - chunks.data());
				e.id		= i;
				e.inpPtr	= &inpBuf[i * chunkSize];
				e.inpSize	= r > chunkSize ? chunkSize : r;
				e.outPtr	= &outBuf[i * maxChunkSize];
				e.outSize	= maxChunkSize;
				e.cmpSize	= 0;
				e.decSize	= 0;
				r -= e.inpSize;
			}
		}

		vector<future<void>> futures(chunks.size());

		auto b = [=, &futures, &chunks]
			(function<void(Chunk*)> fChunk)
		{
			const auto t0 = getSyncTime();
			auto t1 = t0;
			int loopCount = 0;

			while(getTimeSpan(t0, t1 = getTime()) < TIMELOOP) {
				for(auto& e : chunks) {
					futures[e.id] = async(launch::async, fChunk, &e);
				}
				for(auto& e : futures) {
					e.wait();
				}
				++loopCount;
			}

			return getTimeSpan(t0, t1) / static_cast<double>(loopCount);
		};

		auto minCmpTime = 10000000.0;
		auto minDecTime = 10000000.0;
		size_t cmpSize = 0;
		for(int iLoop = 1, nLoop = nIter; iLoop <= nLoop; ++iLoop) {
			msgReport(iLoop, filename, inpBuf.size()
					  , cmpSize, minCmpTime, minDecTime);

			// compression
			for(size_t i = 0; i < outBuf.size(); ++i) {
				outBuf[i] = static_cast<char>(i);
			}
			auto cmpTime = b(
				[ctx, singleThread, &futures] (Chunk* cp) {
					if(singleThread && cp->id > 0) {
						futures[cp->id-1].wait();
					}
					cp->cmpSize = ctx->compress(
						  cp->inpPtr
						, cp->outPtr
						, static_cast<int>(cp->inpSize)
						, static_cast<int>(cp->outSize)
					);
				}
			);
			minCmpTime = min(minCmpTime, cmpTime);

			if(1 == iLoop) {
				cmpSize = 0;
				for(auto& c : chunks) {
					cmpSize += c.cmpSize;
				}
			}

			msgReport(iLoop, filename, inpBuf.size()
					  , cmpSize, minCmpTime, minDecTime);

			// decompression
			for(auto& e : inpBuf) {
				e = 0;
			}
			auto decTime = b(
				[ctx, singleThread, &futures] (Chunk* cp) {
					if(singleThread && cp->id > 0) {
						futures[cp->id-1].wait();
					}
					cp->decSize = ctx->decompress(
						  cp->outPtr
						, cp->inpPtr
						, static_cast<int>(cp->cmpSize)
						, static_cast<int>(cp->inpSize)
					);
				}
			);
			minDecTime = min(minDecTime, decTime);

			msgReport(iLoop, filename, inpBuf.size()
					  , cmpSize, minCmpTime, minDecTime);

			const auto outHash =
				XXH32(inpBuf.data(), static_cast<int>(inpBuf.size()), 0);

			if(inpHash != outHash) {
				msgErrChecksum(filename, inpHash, outHash);
				break;
			}
		}
		msgNewline();

		totalFileSize += inpBuf.size();
		totalCompressSize += cmpSize;
		totalCompressTime += minCmpTime;
		totalDecompressTime += minDecTime;
	}

	if(!files.empty()) {
		msgReport(0, "  TOTAL", totalFileSize, totalCompressSize
				  , totalCompressTime, totalDecompressTime);
		msgNewline();
	}

	return 0;
}

} // namespace Lz4Mt
<|endoftext|>


<commit_before>/*
 * Copyright (C) 2018 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 "src/trace_processor/storage/trace_storage.h"

#include <string.h>
#include <algorithm>
#include <limits>

#include "perfetto/ext/base/no_destructor.h"

namespace perfetto {
namespace trace_processor {

namespace {

void DbTableMaybeUpdateMinMax(const TypedColumn<int64_t>& ts_col,
                              int64_t* min_value,
                              int64_t* max_value,
                              const TypedColumn<int64_t>* dur_col = nullptr) {
  if (ts_col.row_map().empty())
    return;

  int64_t col_min = ts_col.Min()->AsLong();
  int64_t col_max = ts_col.Max()->AsLong();

  if (dur_col) {
    PERFETTO_CHECK(ts_col.IsSorted());
    PERFETTO_CHECK(dur_col->row_map().size() == ts_col.row_map().size());
    for (uint32_t i = 0; i < dur_col->row_map().size(); i++) {
      col_max =
          std::max(ts_col.Get(i).AsLong() + dur_col->Get(i).AsLong(), col_max);
    }
  }

  *min_value = std::min(*min_value, col_min);
  *max_value = std::max(*max_value, col_max);
}

std::vector<NullTermStringView> CreateRefTypeStringMap() {
  std::vector<NullTermStringView> map(static_cast<size_t>(RefType::kRefMax));
  map[static_cast<size_t>(RefType::kRefNoRef)] = NullTermStringView();
  map[static_cast<size_t>(RefType::kRefUtid)] = "utid";
  map[static_cast<size_t>(RefType::kRefCpuId)] = "cpu";
  map[static_cast<size_t>(RefType::kRefGpuId)] = "gpu";
  map[static_cast<size_t>(RefType::kRefIrq)] = "irq";
  map[static_cast<size_t>(RefType::kRefSoftIrq)] = "softirq";
  map[static_cast<size_t>(RefType::kRefUpid)] = "upid";
  map[static_cast<size_t>(RefType::kRefTrack)] = "track";
  return map;
}

}  // namespace

const std::vector<NullTermStringView>& GetRefTypeStringMap() {
  static const base::NoDestructor<std::vector<NullTermStringView>> map(
      CreateRefTypeStringMap());
  return map.ref();
}

TraceStorage::TraceStorage(const Config&) {
  for (uint32_t i = 0; i < variadic_type_ids_.size(); ++i) {
    variadic_type_ids_[i] = InternString(Variadic::kTypeNames[i]);
  }
}

TraceStorage::~TraceStorage() {}

uint32_t TraceStorage::SqlStats::RecordQueryBegin(const std::string& query,
                                                  int64_t time_queued,
                                                  int64_t time_started) {
  if (queries_.size() >= kMaxLogEntries) {
    queries_.pop_front();
    times_queued_.pop_front();
    times_started_.pop_front();
    times_first_next_.pop_front();
    times_ended_.pop_front();
    popped_queries_++;
  }
  queries_.push_back(query);
  times_queued_.push_back(time_queued);
  times_started_.push_back(time_started);
  times_first_next_.push_back(0);
  times_ended_.push_back(0);
  return static_cast<uint32_t>(popped_queries_ + queries_.size() - 1);
}

void TraceStorage::SqlStats::RecordQueryFirstNext(uint32_t row,
                                                  int64_t time_first_next) {
  // This means we've popped this query off the queue of queries before it had
  // a chance to finish. Just silently drop this number.
  if (popped_queries_ > row)
    return;
  uint32_t queue_row = row - popped_queries_;
  PERFETTO_DCHECK(queue_row < queries_.size());
  times_first_next_[queue_row] = time_first_next;
}

void TraceStorage::SqlStats::RecordQueryEnd(uint32_t row, int64_t time_ended) {
  // This means we've popped this query off the queue of queries before it had
  // a chance to finish. Just silently drop this number.
  if (popped_queries_ > row)
    return;
  uint32_t queue_row = row - popped_queries_;
  PERFETTO_DCHECK(queue_row < queries_.size());
  times_ended_[queue_row] = time_ended;
}

std::pair<int64_t, int64_t> TraceStorage::GetTraceTimestampBoundsNs() const {
  int64_t start_ns = std::numeric_limits<int64_t>::max();
  int64_t end_ns = std::numeric_limits<int64_t>::min();

  DbTableMaybeUpdateMinMax(raw_table_.ts(), &start_ns, &end_ns);
  DbTableMaybeUpdateMinMax(sched_slice_table_.ts(), &start_ns, &end_ns,
                           &sched_slice_table_.dur());
  DbTableMaybeUpdateMinMax(counter_table_.ts(), &start_ns, &end_ns);
  DbTableMaybeUpdateMinMax(slice_table_.ts(), &start_ns, &end_ns,
                           &slice_table_.dur());
  DbTableMaybeUpdateMinMax(heap_profile_allocation_table_.ts(), &start_ns,
                           &end_ns);
  DbTableMaybeUpdateMinMax(instant_table_.ts(), &start_ns, &end_ns);
  DbTableMaybeUpdateMinMax(android_log_table_.ts(), &start_ns, &end_ns);
  DbTableMaybeUpdateMinMax(heap_graph_object_table_.graph_sample_ts(),
                           &start_ns, &end_ns);

  if (start_ns == std::numeric_limits<int64_t>::max()) {
    return std::make_pair(0, 0);
  }
  if (start_ns == end_ns) {
    end_ns += 1;
  }
  return std::make_pair(start_ns, end_ns);
}

}  // namespace trace_processor
}  // namespace perfetto
<commit_msg>tp: trace_bounds: include the ts column of |perf_sample| table<commit_after>/*
 * Copyright (C) 2018 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 "src/trace_processor/storage/trace_storage.h"

#include <string.h>
#include <algorithm>
#include <limits>

#include "perfetto/ext/base/no_destructor.h"

namespace perfetto {
namespace trace_processor {

namespace {

void DbTableMaybeUpdateMinMax(const TypedColumn<int64_t>& ts_col,
                              int64_t* min_value,
                              int64_t* max_value,
                              const TypedColumn<int64_t>* dur_col = nullptr) {
  if (ts_col.row_map().empty())
    return;

  int64_t col_min = ts_col.Min()->AsLong();
  int64_t col_max = ts_col.Max()->AsLong();

  if (dur_col) {
    PERFETTO_CHECK(ts_col.IsSorted());
    PERFETTO_CHECK(dur_col->row_map().size() == ts_col.row_map().size());
    for (uint32_t i = 0; i < dur_col->row_map().size(); i++) {
      col_max =
          std::max(ts_col.Get(i).AsLong() + dur_col->Get(i).AsLong(), col_max);
    }
  }

  *min_value = std::min(*min_value, col_min);
  *max_value = std::max(*max_value, col_max);
}

std::vector<NullTermStringView> CreateRefTypeStringMap() {
  std::vector<NullTermStringView> map(static_cast<size_t>(RefType::kRefMax));
  map[static_cast<size_t>(RefType::kRefNoRef)] = NullTermStringView();
  map[static_cast<size_t>(RefType::kRefUtid)] = "utid";
  map[static_cast<size_t>(RefType::kRefCpuId)] = "cpu";
  map[static_cast<size_t>(RefType::kRefGpuId)] = "gpu";
  map[static_cast<size_t>(RefType::kRefIrq)] = "irq";
  map[static_cast<size_t>(RefType::kRefSoftIrq)] = "softirq";
  map[static_cast<size_t>(RefType::kRefUpid)] = "upid";
  map[static_cast<size_t>(RefType::kRefTrack)] = "track";
  return map;
}

}  // namespace

const std::vector<NullTermStringView>& GetRefTypeStringMap() {
  static const base::NoDestructor<std::vector<NullTermStringView>> map(
      CreateRefTypeStringMap());
  return map.ref();
}

TraceStorage::TraceStorage(const Config&) {
  for (uint32_t i = 0; i < variadic_type_ids_.size(); ++i) {
    variadic_type_ids_[i] = InternString(Variadic::kTypeNames[i]);
  }
}

TraceStorage::~TraceStorage() {}

uint32_t TraceStorage::SqlStats::RecordQueryBegin(const std::string& query,
                                                  int64_t time_queued,
                                                  int64_t time_started) {
  if (queries_.size() >= kMaxLogEntries) {
    queries_.pop_front();
    times_queued_.pop_front();
    times_started_.pop_front();
    times_first_next_.pop_front();
    times_ended_.pop_front();
    popped_queries_++;
  }
  queries_.push_back(query);
  times_queued_.push_back(time_queued);
  times_started_.push_back(time_started);
  times_first_next_.push_back(0);
  times_ended_.push_back(0);
  return static_cast<uint32_t>(popped_queries_ + queries_.size() - 1);
}

void TraceStorage::SqlStats::RecordQueryFirstNext(uint32_t row,
                                                  int64_t time_first_next) {
  // This means we've popped this query off the queue of queries before it had
  // a chance to finish. Just silently drop this number.
  if (popped_queries_ > row)
    return;
  uint32_t queue_row = row - popped_queries_;
  PERFETTO_DCHECK(queue_row < queries_.size());
  times_first_next_[queue_row] = time_first_next;
}

void TraceStorage::SqlStats::RecordQueryEnd(uint32_t row, int64_t time_ended) {
  // This means we've popped this query off the queue of queries before it had
  // a chance to finish. Just silently drop this number.
  if (popped_queries_ > row)
    return;
  uint32_t queue_row = row - popped_queries_;
  PERFETTO_DCHECK(queue_row < queries_.size());
  times_ended_[queue_row] = time_ended;
}

std::pair<int64_t, int64_t> TraceStorage::GetTraceTimestampBoundsNs() const {
  int64_t start_ns = std::numeric_limits<int64_t>::max();
  int64_t end_ns = std::numeric_limits<int64_t>::min();

  DbTableMaybeUpdateMinMax(raw_table_.ts(), &start_ns, &end_ns);
  DbTableMaybeUpdateMinMax(sched_slice_table_.ts(), &start_ns, &end_ns,
                           &sched_slice_table_.dur());
  DbTableMaybeUpdateMinMax(counter_table_.ts(), &start_ns, &end_ns);
  DbTableMaybeUpdateMinMax(slice_table_.ts(), &start_ns, &end_ns,
                           &slice_table_.dur());
  DbTableMaybeUpdateMinMax(heap_profile_allocation_table_.ts(), &start_ns,
                           &end_ns);
  DbTableMaybeUpdateMinMax(instant_table_.ts(), &start_ns, &end_ns);
  DbTableMaybeUpdateMinMax(android_log_table_.ts(), &start_ns, &end_ns);
  DbTableMaybeUpdateMinMax(heap_graph_object_table_.graph_sample_ts(),
                           &start_ns, &end_ns);
  DbTableMaybeUpdateMinMax(perf_sample_table_.ts(), &start_ns, &end_ns);

  if (start_ns == std::numeric_limits<int64_t>::max()) {
    return std::make_pair(0, 0);
  }
  if (start_ns == end_ns) {
    end_ns += 1;
  }
  return std::make_pair(start_ns, end_ns);
}

}  // namespace trace_processor
}  // namespace perfetto
<|endoftext|>