<commit_before>/*
 * Process CM4all commands in a HTML stream, e.g. embeddings.
 *
 * author: Max Kellermann <mk@cm4all.com>
 */

#include "istream_replace.hxx"
#include "FacadeIstream.hxx"
#include "Sink.hxx"
#include "growing_buffer.hxx"
#include "util/ConstBuffer.hxx"
#include "pool.hxx"

#include <inline/poison.h>
#include <daemon/log.h>

#include <glib.h>

#include <assert.h>

struct ReplaceIstream final : FacadeIstream {
    struct Substitution final : IstreamSink {
        Substitution *next = nullptr;
        ReplaceIstream &replace;
        const off_t start;
        off_t end;

        Substitution(ReplaceIstream &_replace, off_t _start, off_t _end,
                     Istream &_input)
            :IstreamSink(_input),
             replace(_replace),
             start(_start), end(_end)
        {
        }

        bool IsDefined() const {
            return input.IsDefined();
        }

        off_t GetAvailable(bool partial) const {
            return input.GetAvailable(partial);
        }

        void Read() {
            input.Read();
        }

        using IstreamSink::ClearAndCloseInput;

        gcc_pure
        bool IsActive() const;

        /* virtual methods from class IstreamHandler */
        size_t OnData(const void *data, size_t length) override;
        void OnEof() override;
        void OnError(GError *error) override;
    };

    bool finished = false, read_locked = false;
    bool had_input, had_output;

    GrowingBuffer *const buffer;
    off_t source_length = 0, position = 0;

    /**
     * The offset given by istream_replace_settle() or the end offset
     * of the last substitution (whichever is bigger).
     */
    off_t settled_position = 0;

    GrowingBufferReader reader;

    Substitution *first_substitution = nullptr,
        **append_substitution_p = &first_substitution;

#ifndef NDEBUG
    off_t last_substitution_end = 0;
#endif

    ReplaceIstream(struct pool &p, Istream &_input);

    using FacadeIstream::GetPool;
    using FacadeIstream::HasInput;

    void DestroyReplace();

    /**
     * Is the buffer at the end-of-file position?
     */
    bool IsBufferAtEOF() const {
        return position == source_length;
    }

    /**
     * Is the object at end-of-file?
     */
    bool IsEOF() const {
        return !input.IsDefined() && finished &&
            first_substitution == nullptr &&
            IsBufferAtEOF();
    }

    /**
     * Copy the next chunk from the source buffer to the istream
     * handler.
     *
     * @return 0 if the istream handler is not blocking; the number of
     * bytes remaining in the buffer if it is blocking
     */
    size_t TryReadFromBuffer();

    /**
     * Copy data from the source buffer to the istream handler.
     *
     * @return 0 if the istream handler is not blocking; the number of
     * bytes remaining in the buffer if it is blocking
     */
    size_t ReadFromBuffer(size_t max_length);

    size_t ReadFromBufferLoop(off_t end);

    void TryRead();

    void ReadCheckEmpty();

    /**
     * Read data from substitution objects.
     */
    bool ReadSubstitution();

    /**
     * Activate the next substitution object after s.
     */
    void ToNextSubstitution(ReplaceIstream::Substitution *s);

    /* virtual methods from class IstreamHandler */
    size_t OnData(const void *data, size_t length) override;
    void OnEof() override;
    void OnError(GError *error) override;

    /* virtual methods from class Istream */

    off_t _GetAvailable(bool partial) override;
    void _Read() override;
    void _Close() override;
};

static GQuark
replace_quark(void)
{
    return g_quark_from_static_string("replace");
}

/**
 * Is this substitution object is active, i.e. its data is the next
 * being written?
 */
bool
ReplaceIstream::Substitution::IsActive() const
{
    assert(replace.first_substitution != nullptr);
    assert(replace.first_substitution->start <= start);
    assert(start >= replace.position);

    return this == replace.first_substitution && replace.position == start;
}

void
ReplaceIstream::ToNextSubstitution(ReplaceIstream::Substitution *s)
{
    assert(first_substitution == s);
    assert(s->IsActive());
    assert(!s->IsDefined());
    assert(s->start <= s->end);

    reader.Skip(s->end - s->start);
    position = s->end;

    first_substitution = s->next;
    if (first_substitution == nullptr) {
        assert(append_substitution_p == &s->next);
        append_substitution_p = &first_substitution;
    }

    p_free(&GetPool(), s);

    assert(first_substitution == nullptr ||
           first_substitution->start >= position);

    if (IsEOF()) {
        DestroyEof();
        return;
    }

    /* don't recurse if we're being called from ReadSubstitution() */
    if (!read_locked) {
        const ScopePoolRef ref(GetPool() TRACE_ARGS);
        TryRead();
    }
}

/*
 * istream handler
 *
 */

inline size_t
ReplaceIstream::Substitution::OnData(const void *data, size_t length)
{
    if (IsActive()) {
        replace.had_output = true;
        return replace.InvokeData(data, length);
    } else
        return 0;
}

inline void
ReplaceIstream::Substitution::OnEof()
{
    input.Clear();

    if (IsActive())
        replace.ToNextSubstitution(this);
}

inline void
ReplaceIstream::Substitution::OnError(GError *error)
{
    ClearInput();

    replace.DestroyReplace();

    if (replace.HasInput())
        replace.ClearAndCloseInput();

    replace.DestroyError(error);
}

/*
 * misc methods
 *
 */

void
ReplaceIstream::DestroyReplace()
{
    assert(source_length != (off_t)-1);

    /* source_length -1 is the "destroyed" marker */
    source_length = (off_t)-1;

    while (first_substitution != nullptr) {
        auto *s = first_substitution;
        first_substitution = s->next;

        if (s->IsDefined())
            s->ClearAndCloseInput();
    }
}

bool
ReplaceIstream::ReadSubstitution()
{
    while (first_substitution != nullptr && first_substitution->IsActive()) {
        auto *s = first_substitution;

        read_locked = true;

        if (s->IsDefined())
            s->Read();
        else
            ToNextSubstitution(s);

        read_locked = false;

        /* we assume the substitution object is blocking if it hasn't
           reached EOF with this one call */
        if (s == first_substitution)
            return true;
    }

    return false;
}

inline size_t
ReplaceIstream::ReadFromBuffer(size_t max_length)
{
    assert(max_length > 0);

    auto src = reader.Read();
    assert(!src.IsNull());
    assert(!src.IsEmpty());

    if (src.size > max_length)
        src.size = max_length;

    had_output = true;
    size_t nbytes = InvokeData(src.data, src.size);
    assert(nbytes <= src.size);

    if (nbytes == 0)
        /* istream_replace has been closed */
        return src.size;

    reader.Consume(nbytes);
    position += nbytes;

    assert(position <= source_length);

    return src.size - nbytes;
}

inline size_t
ReplaceIstream::ReadFromBufferLoop(off_t end)
{
    assert(end > position);
    assert(end <= source_length);

    /* this loop is required to cross the GrowingBuffer borders */
    size_t rest;
    do {
#ifndef NDEBUG
        PoolNotify notify(GetPool());
#endif

        size_t max_length = (size_t)(end - position);
        rest = ReadFromBuffer(max_length);

#ifndef NDEBUG
        if (notify.Denotify()) {
            assert(rest > 0);
            break;
        }
#endif

        assert(position <= end);
    } while (rest == 0 && position < end);

    return rest;
}

size_t
ReplaceIstream::TryReadFromBuffer()
{
    off_t end;
    if (first_substitution == nullptr) {
        if (finished)
            end = source_length;
        else if (position < settled_position)
            end = settled_position;
        else
            /* block after the last substitution, unless the caller
               has already set the "finished" flag */
            return 1;

        assert(position < source_length);
    } else {
        end = first_substitution->start;
        assert(end >= position);

        if (end == position)
            return 0;
    }

    size_t rest = ReadFromBufferLoop(end);
    if (rest == 0 && position == source_length &&
        first_substitution == nullptr &&
        !input.IsDefined())
        DestroyEof();

    return rest;
}

void
ReplaceIstream::TryRead()
{
    assert(position <= source_length);

    /* read until someone (input or output) blocks */
    size_t rest;
    do {
        bool blocking = ReadSubstitution();
        if (blocking || IsBufferAtEOF() || source_length == (off_t)-1)
            break;

        rest = TryReadFromBuffer();
    } while (rest == 0 && first_substitution != nullptr);
}

void
ReplaceIstream::ReadCheckEmpty()
{
    assert(finished);
    assert(!input.IsDefined());

    if (IsEOF())
        DestroyEof();
    else {
        const ScopePoolRef ref(GetPool() TRACE_ARGS);
        TryRead();
    }
}

/*
 * input handler
 *
 */

inline size_t
ReplaceIstream::OnData(const void *data, size_t length)
{
    had_input = true;

    if (source_length >= 8 * 1024 * 1024) {
        ClearAndCloseInput();
        DestroyReplace();

        GError *error =
            g_error_new_literal(replace_quark(), 0,
                                "file too large for processor");
        DestroyError(error);
        return 0;
    }

    growing_buffer_write_buffer(buffer, data, length);
    source_length += (off_t)length;

    reader.Update();

    const ScopePoolRef ref(GetPool() TRACE_ARGS);

    TryReadFromBuffer();
    if (!input.IsDefined())
        /* the istream API mandates that we must return 0 if the
           stream is finished */
        length = 0;

    return length;
}

inline void
ReplaceIstream::OnEof()
{
    input.Clear();

    if (finished)
        ReadCheckEmpty();
}

inline void
ReplaceIstream::OnError(GError *error)
{
    DestroyReplace();
    input.Clear();
    DestroyError(error);
}

/*
 * istream implementation
 *
 */

off_t
ReplaceIstream::_GetAvailable(bool partial)
{
    off_t length, position2 = 0, l;

    if (!partial && !finished)
        /* we don't know yet how many substitutions will come, so we
           cannot calculate the exact rest */
        return (off_t)-1;

    /* get available bytes from input */

    if (HasInput() && finished) {
        length = input.GetAvailable(partial);
        if (length == (off_t)-1) {
            if (!partial)
                return (off_t)-1;
            length = 0;
        }
    } else
        length = 0;

    /* add available bytes from substitutions (and the source buffers
       before the substitutions) */

    position2 = position;

    for (auto subst = first_substitution;
         subst != nullptr; subst = subst->next) {
        assert(position2 <= subst->start);

        length += subst->start - position2;

        if (subst->IsDefined()) {
            l = subst->GetAvailable(partial);
            if (l != (off_t)-1)
                length += l;
            else if (!partial)
                return (off_t)-1;
        }

        position2 = subst->end;
    }

    /* add available bytes from tail (if known yet) */

    if (finished)
        length += source_length - position2;

    return length;
}

void
ReplaceIstream::_Read()
{
    const ScopePoolRef ref(GetPool() TRACE_ARGS);

    TryRead();

    if (!HasInput())
        return;

    had_output = false;

    do {
        had_input = false;
        input.Read();
    } while (had_input && !had_output && HasInput());
}

void
ReplaceIstream::_Close()
{
    DestroyReplace();

    if (HasInput())
        ClearAndCloseInput();

    Destroy();
}

/*
 * constructor
 *
 */

inline ReplaceIstream::ReplaceIstream(struct pool &p, Istream &_input)
    :FacadeIstream(p, _input),
     buffer(growing_buffer_new(&p, 4096)),
     reader(*buffer)
{
}

Istream *
istream_replace_new(struct pool &pool, Istream &input)
{
    return NewIstream<ReplaceIstream>(pool, input);
}

void
istream_replace_add(Istream &istream, off_t start, off_t end,
                    Istream *contents)
{
    auto &replace = (ReplaceIstream &)istream;

    assert(!replace.finished);
    assert(start >= 0);
    assert(start <= end);
    assert(start >= replace.settled_position);
    assert(start >= replace.last_substitution_end);

    if (contents == nullptr && start == end)
        return;

    auto s =
        NewFromPool<ReplaceIstream::Substitution>(replace.GetPool(),
                                                  replace, start, end,
                                                  *contents);

    replace.settled_position = end;

#ifndef NDEBUG
    replace.last_substitution_end = end;
#endif

    *replace.append_substitution_p = s;
    replace.append_substitution_p = &s->next;
}

static ReplaceIstream::Substitution *
replace_get_last_substitution(ReplaceIstream &replace)
{
    auto *substitution = replace.first_substitution;
    assert(substitution != nullptr);

    while (substitution->next != nullptr)
        substitution = substitution->next;

    assert(substitution->end <= replace.settled_position);
    assert(substitution->end == replace.last_substitution_end);
    return substitution;
}

void
istream_replace_extend(Istream &istream, gcc_unused off_t start, off_t end)
{
    auto &replace = (ReplaceIstream &)istream;
    assert(!replace.finished);

    auto *substitution = replace_get_last_substitution(replace);
    assert(substitution->start == start);
    assert(substitution->end == replace.settled_position);
    assert(substitution->end == replace.last_substitution_end);
    assert(end >= substitution->end);

    substitution->end = end;
    replace.settled_position = end;
#ifndef NDEBUG
    replace.last_substitution_end = end;
#endif
}

void
istream_replace_settle(Istream &istream, off_t offset)
{
    auto &replace = (ReplaceIstream &)istream;
    assert(!replace.finished);
    assert(offset >= replace.settled_position);

    replace.settled_position = offset;
}

void
istream_replace_finish(Istream &istream)
{
    auto &replace = (ReplaceIstream &)istream;
    assert(!replace.finished);

    replace.finished = true;

    if (!replace.HasInput())
        replace.ReadCheckEmpty();
}
<commit_msg>istream/replace: fix nullptr dereference<commit_after>/*
 * Process CM4all commands in a HTML stream, e.g. embeddings.
 *
 * author: Max Kellermann <mk@cm4all.com>
 */

#include "istream_replace.hxx"
#include "FacadeIstream.hxx"
#include "Sink.hxx"
#include "growing_buffer.hxx"
#include "util/ConstBuffer.hxx"
#include "pool.hxx"

#include <inline/poison.h>
#include <daemon/log.h>

#include <glib.h>

#include <assert.h>

struct ReplaceIstream final : FacadeIstream {
    struct Substitution final : IstreamSink {
        Substitution *next = nullptr;
        ReplaceIstream &replace;
        const off_t start;
        off_t end;

        Substitution(ReplaceIstream &_replace, off_t _start, off_t _end,
                     std::nullptr_t)
            :replace(_replace),
             start(_start), end(_end)
        {
        }

        Substitution(ReplaceIstream &_replace, off_t _start, off_t _end,
                     Istream &_input)
            :IstreamSink(_input),
             replace(_replace),
             start(_start), end(_end)
        {
        }

        bool IsDefined() const {
            return input.IsDefined();
        }

        off_t GetAvailable(bool partial) const {
            return input.GetAvailable(partial);
        }

        void Read() {
            input.Read();
        }

        using IstreamSink::ClearAndCloseInput;

        gcc_pure
        bool IsActive() const;

        /* virtual methods from class IstreamHandler */
        size_t OnData(const void *data, size_t length) override;
        void OnEof() override;
        void OnError(GError *error) override;
    };

    bool finished = false, read_locked = false;
    bool had_input, had_output;

    GrowingBuffer *const buffer;
    off_t source_length = 0, position = 0;

    /**
     * The offset given by istream_replace_settle() or the end offset
     * of the last substitution (whichever is bigger).
     */
    off_t settled_position = 0;

    GrowingBufferReader reader;

    Substitution *first_substitution = nullptr,
        **append_substitution_p = &first_substitution;

#ifndef NDEBUG
    off_t last_substitution_end = 0;
#endif

    ReplaceIstream(struct pool &p, Istream &_input);

    using FacadeIstream::GetPool;
    using FacadeIstream::HasInput;

    void DestroyReplace();

    /**
     * Is the buffer at the end-of-file position?
     */
    bool IsBufferAtEOF() const {
        return position == source_length;
    }

    /**
     * Is the object at end-of-file?
     */
    bool IsEOF() const {
        return !input.IsDefined() && finished &&
            first_substitution == nullptr &&
            IsBufferAtEOF();
    }

    /**
     * Copy the next chunk from the source buffer to the istream
     * handler.
     *
     * @return 0 if the istream handler is not blocking; the number of
     * bytes remaining in the buffer if it is blocking
     */
    size_t TryReadFromBuffer();

    /**
     * Copy data from the source buffer to the istream handler.
     *
     * @return 0 if the istream handler is not blocking; the number of
     * bytes remaining in the buffer if it is blocking
     */
    size_t ReadFromBuffer(size_t max_length);

    size_t ReadFromBufferLoop(off_t end);

    void TryRead();

    void ReadCheckEmpty();

    /**
     * Read data from substitution objects.
     */
    bool ReadSubstitution();

    /**
     * Activate the next substitution object after s.
     */
    void ToNextSubstitution(ReplaceIstream::Substitution *s);

    /* virtual methods from class IstreamHandler */
    size_t OnData(const void *data, size_t length) override;
    void OnEof() override;
    void OnError(GError *error) override;

    /* virtual methods from class Istream */

    off_t _GetAvailable(bool partial) override;
    void _Read() override;
    void _Close() override;
};

static GQuark
replace_quark(void)
{
    return g_quark_from_static_string("replace");
}

/**
 * Is this substitution object is active, i.e. its data is the next
 * being written?
 */
bool
ReplaceIstream::Substitution::IsActive() const
{
    assert(replace.first_substitution != nullptr);
    assert(replace.first_substitution->start <= start);
    assert(start >= replace.position);

    return this == replace.first_substitution && replace.position == start;
}

void
ReplaceIstream::ToNextSubstitution(ReplaceIstream::Substitution *s)
{
    assert(first_substitution == s);
    assert(s->IsActive());
    assert(!s->IsDefined());
    assert(s->start <= s->end);

    reader.Skip(s->end - s->start);
    position = s->end;

    first_substitution = s->next;
    if (first_substitution == nullptr) {
        assert(append_substitution_p == &s->next);
        append_substitution_p = &first_substitution;
    }

    p_free(&GetPool(), s);

    assert(first_substitution == nullptr ||
           first_substitution->start >= position);

    if (IsEOF()) {
        DestroyEof();
        return;
    }

    /* don't recurse if we're being called from ReadSubstitution() */
    if (!read_locked) {
        const ScopePoolRef ref(GetPool() TRACE_ARGS);
        TryRead();
    }
}

/*
 * istream handler
 *
 */

inline size_t
ReplaceIstream::Substitution::OnData(const void *data, size_t length)
{
    if (IsActive()) {
        replace.had_output = true;
        return replace.InvokeData(data, length);
    } else
        return 0;
}

inline void
ReplaceIstream::Substitution::OnEof()
{
    input.Clear();

    if (IsActive())
        replace.ToNextSubstitution(this);
}

inline void
ReplaceIstream::Substitution::OnError(GError *error)
{
    ClearInput();

    replace.DestroyReplace();

    if (replace.HasInput())
        replace.ClearAndCloseInput();

    replace.DestroyError(error);
}

/*
 * misc methods
 *
 */

void
ReplaceIstream::DestroyReplace()
{
    assert(source_length != (off_t)-1);

    /* source_length -1 is the "destroyed" marker */
    source_length = (off_t)-1;

    while (first_substitution != nullptr) {
        auto *s = first_substitution;
        first_substitution = s->next;

        if (s->IsDefined())
            s->ClearAndCloseInput();
    }
}

bool
ReplaceIstream::ReadSubstitution()
{
    while (first_substitution != nullptr && first_substitution->IsActive()) {
        auto *s = first_substitution;

        read_locked = true;

        if (s->IsDefined())
            s->Read();
        else
            ToNextSubstitution(s);

        read_locked = false;

        /* we assume the substitution object is blocking if it hasn't
           reached EOF with this one call */
        if (s == first_substitution)
            return true;
    }

    return false;
}

inline size_t
ReplaceIstream::ReadFromBuffer(size_t max_length)
{
    assert(max_length > 0);

    auto src = reader.Read();
    assert(!src.IsNull());
    assert(!src.IsEmpty());

    if (src.size > max_length)
        src.size = max_length;

    had_output = true;
    size_t nbytes = InvokeData(src.data, src.size);
    assert(nbytes <= src.size);

    if (nbytes == 0)
        /* istream_replace has been closed */
        return src.size;

    reader.Consume(nbytes);
    position += nbytes;

    assert(position <= source_length);

    return src.size - nbytes;
}

inline size_t
ReplaceIstream::ReadFromBufferLoop(off_t end)
{
    assert(end > position);
    assert(end <= source_length);

    /* this loop is required to cross the GrowingBuffer borders */
    size_t rest;
    do {
#ifndef NDEBUG
        PoolNotify notify(GetPool());
#endif

        size_t max_length = (size_t)(end - position);
        rest = ReadFromBuffer(max_length);

#ifndef NDEBUG
        if (notify.Denotify()) {
            assert(rest > 0);
            break;
        }
#endif

        assert(position <= end);
    } while (rest == 0 && position < end);

    return rest;
}

size_t
ReplaceIstream::TryReadFromBuffer()
{
    off_t end;
    if (first_substitution == nullptr) {
        if (finished)
            end = source_length;
        else if (position < settled_position)
            end = settled_position;
        else
            /* block after the last substitution, unless the caller
               has already set the "finished" flag */
            return 1;

        assert(position < source_length);
    } else {
        end = first_substitution->start;
        assert(end >= position);

        if (end == position)
            return 0;
    }

    size_t rest = ReadFromBufferLoop(end);
    if (rest == 0 && position == source_length &&
        first_substitution == nullptr &&
        !input.IsDefined())
        DestroyEof();

    return rest;
}

void
ReplaceIstream::TryRead()
{
    assert(position <= source_length);

    /* read until someone (input or output) blocks */
    size_t rest;
    do {
        bool blocking = ReadSubstitution();
        if (blocking || IsBufferAtEOF() || source_length == (off_t)-1)
            break;

        rest = TryReadFromBuffer();
    } while (rest == 0 && first_substitution != nullptr);
}

void
ReplaceIstream::ReadCheckEmpty()
{
    assert(finished);
    assert(!input.IsDefined());

    if (IsEOF())
        DestroyEof();
    else {
        const ScopePoolRef ref(GetPool() TRACE_ARGS);
        TryRead();
    }
}

/*
 * input handler
 *
 */

inline size_t
ReplaceIstream::OnData(const void *data, size_t length)
{
    had_input = true;

    if (source_length >= 8 * 1024 * 1024) {
        ClearAndCloseInput();
        DestroyReplace();

        GError *error =
            g_error_new_literal(replace_quark(), 0,
                                "file too large for processor");
        DestroyError(error);
        return 0;
    }

    growing_buffer_write_buffer(buffer, data, length);
    source_length += (off_t)length;

    reader.Update();

    const ScopePoolRef ref(GetPool() TRACE_ARGS);

    TryReadFromBuffer();
    if (!input.IsDefined())
        /* the istream API mandates that we must return 0 if the
           stream is finished */
        length = 0;

    return length;
}

inline void
ReplaceIstream::OnEof()
{
    input.Clear();

    if (finished)
        ReadCheckEmpty();
}

inline void
ReplaceIstream::OnError(GError *error)
{
    DestroyReplace();
    input.Clear();
    DestroyError(error);
}

/*
 * istream implementation
 *
 */

off_t
ReplaceIstream::_GetAvailable(bool partial)
{
    off_t length, position2 = 0, l;

    if (!partial && !finished)
        /* we don't know yet how many substitutions will come, so we
           cannot calculate the exact rest */
        return (off_t)-1;

    /* get available bytes from input */

    if (HasInput() && finished) {
        length = input.GetAvailable(partial);
        if (length == (off_t)-1) {
            if (!partial)
                return (off_t)-1;
            length = 0;
        }
    } else
        length = 0;

    /* add available bytes from substitutions (and the source buffers
       before the substitutions) */

    position2 = position;

    for (auto subst = first_substitution;
         subst != nullptr; subst = subst->next) {
        assert(position2 <= subst->start);

        length += subst->start - position2;

        if (subst->IsDefined()) {
            l = subst->GetAvailable(partial);
            if (l != (off_t)-1)
                length += l;
            else if (!partial)
                return (off_t)-1;
        }

        position2 = subst->end;
    }

    /* add available bytes from tail (if known yet) */

    if (finished)
        length += source_length - position2;

    return length;
}

void
ReplaceIstream::_Read()
{
    const ScopePoolRef ref(GetPool() TRACE_ARGS);

    TryRead();

    if (!HasInput())
        return;

    had_output = false;

    do {
        had_input = false;
        input.Read();
    } while (had_input && !had_output && HasInput());
}

void
ReplaceIstream::_Close()
{
    DestroyReplace();

    if (HasInput())
        ClearAndCloseInput();

    Destroy();
}

/*
 * constructor
 *
 */

inline ReplaceIstream::ReplaceIstream(struct pool &p, Istream &_input)
    :FacadeIstream(p, _input),
     buffer(growing_buffer_new(&p, 4096)),
     reader(*buffer)
{
}

Istream *
istream_replace_new(struct pool &pool, Istream &input)
{
    return NewIstream<ReplaceIstream>(pool, input);
}

void
istream_replace_add(Istream &istream, off_t start, off_t end,
                    Istream *contents)
{
    auto &replace = (ReplaceIstream &)istream;

    assert(!replace.finished);
    assert(start >= 0);
    assert(start <= end);
    assert(start >= replace.settled_position);
    assert(start >= replace.last_substitution_end);

    if (contents == nullptr && start == end)
        return;

    auto s = contents != nullptr
        ? NewFromPool<ReplaceIstream::Substitution>(replace.GetPool(),
                                                    replace, start, end,
                                                    *contents)
        : NewFromPool<ReplaceIstream::Substitution>(replace.GetPool(),
                                                    replace, start, end,
                                                    nullptr);

    replace.settled_position = end;

#ifndef NDEBUG
    replace.last_substitution_end = end;
#endif

    *replace.append_substitution_p = s;
    replace.append_substitution_p = &s->next;
}

static ReplaceIstream::Substitution *
replace_get_last_substitution(ReplaceIstream &replace)
{
    auto *substitution = replace.first_substitution;
    assert(substitution != nullptr);

    while (substitution->next != nullptr)
        substitution = substitution->next;

    assert(substitution->end <= replace.settled_position);
    assert(substitution->end == replace.last_substitution_end);
    return substitution;
}

void
istream_replace_extend(Istream &istream, gcc_unused off_t start, off_t end)
{
    auto &replace = (ReplaceIstream &)istream;
    assert(!replace.finished);

    auto *substitution = replace_get_last_substitution(replace);
    assert(substitution->start == start);
    assert(substitution->end == replace.settled_position);
    assert(substitution->end == replace.last_substitution_end);
    assert(end >= substitution->end);

    substitution->end = end;
    replace.settled_position = end;
#ifndef NDEBUG
    replace.last_substitution_end = end;
#endif
}

void
istream_replace_settle(Istream &istream, off_t offset)
{
    auto &replace = (ReplaceIstream &)istream;
    assert(!replace.finished);
    assert(offset >= replace.settled_position);

    replace.settled_position = offset;
}

void
istream_replace_finish(Istream &istream)
{
    auto &replace = (ReplaceIstream &)istream;
    assert(!replace.finished);

    replace.finished = true;

    if (!replace.HasInput())
        replace.ReadCheckEmpty();
}
<|endoftext|>


<commit_before>/*
The Battle Grounds 3 - A Source modification
Copyright (C) 2017, The Battle Grounds 3 Team and Contributors

The Battle Grounds 3 free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.

The Battle Grounds 3 is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
Lesser General Public License for more details.

You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA

Contact information:
Chel "Awesome" Trunk		mail, in reverse: com . gmail @ latrunkster

You may also contact the (future) team via the Battle Grounds website and/or forum at:
battlegrounds3.com

Note that because of the sheer volume of files in the Source SDK this
notice cannot be put in all of them, but merely the ones that have any
changes from the original SDK.
In order to facilitate easy searching, all changes are and must be
commented on the following form:

//BG3 - <name of contributer>[ - <small description>]
*/

#include "cbase.h"
#include "bg3_soft_info.h"
#include "../shared/bg3/bg3_class.h"
#include "hl2mp/c_hl2mp_player.h"

namespace NSoftInfo {

	static int g_iAmericanClassCounts[TOTAL_AMER_CLASSES];
	static int g_iBritishClassCounts[TOTAL_BRIT_CLASSES];

	static float g_flNextUpdateTime = -FLT_MAX;

	void Reset() {
		g_flNextUpdateTime = -FLT_MAX;
		Update();
	}

	void UpdateClasses() {
		memset(g_iAmericanClassCounts, 0, sizeof(g_iAmericanClassCounts));
		memset(g_iBritishClassCounts, 0, sizeof(g_iBritishClassCounts));
		for (int i = 1; i <= gpGlobals->maxClients; i++) {
			CHL2MP_Player* pPlayer = ToHL2MPPlayer(UTIL_PlayerByIndex(i));
			if (pPlayer && pPlayer->GetNextClass() >= 0) {
				if (pPlayer->GetTeamNumber() == TEAM_AMERICANS) {
					g_iAmericanClassCounts[pPlayer->GetNextClass()]++;
				}
				else if (pPlayer->GetTeamNumber() == TEAM_BRITISH) {
					g_iBritishClassCounts[pPlayer->GetNextClass()]++;
				}
			}
		}
	}

	static const float UPDATE_INTERVAL = 1.f;
	void Update() {
		if (gpGlobals->curtime > g_flNextUpdateTime) {
			g_flNextUpdateTime = gpGlobals->curtime + UPDATE_INTERVAL;

			UpdateClasses();
		}
	}

	int GetNumPlayersOfClass(const CPlayerClass* pClass) {
		if (pClass->m_iDefaultTeam == TEAM_AMERICANS)
			return g_iAmericanClassCounts[pClass->m_iClassNumber];
		else
			return g_iBritishClassCounts[pClass->m_iClassNumber];

	}
}<commit_msg>Added csv_export command, will finally move off this branch after this commit<commit_after>/*
The Battle Grounds 3 - A Source modification
Copyright (C) 2017, The Battle Grounds 3 Team and Contributors

The Battle Grounds 3 free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.

The Battle Grounds 3 is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
Lesser General Public License for more details.

You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA

Contact information:
Chel "Awesome" Trunk		mail, in reverse: com . gmail @ latrunkster

You may also contact the (future) team via the Battle Grounds website and/or forum at:
battlegrounds3.com

Note that because of the sheer volume of files in the Source SDK this
notice cannot be put in all of them, but merely the ones that have any
changes from the original SDK.
In order to facilitate easy searching, all changes are and must be
commented on the following form:

//BG3 - <name of contributer>[ - <small description>]
*/

#include "cbase.h"
#include "bg3_soft_info.h"
#include "../shared/bg3/bg3_class.h"
#include "../shared/hl2mp/hl2mp_gamerules.h"
#include "hl2mp/c_hl2mp_player.h"
#include "c_team.h"
#include "clientsteamcontext.h"
#include "filesystem.h"

namespace NSoftInfo {

	static int g_iAmericanClassCounts[TOTAL_AMER_CLASSES];
	static int g_iBritishClassCounts[TOTAL_BRIT_CLASSES];

	static float g_flNextUpdateTime = -FLT_MAX;

	void Reset() {
		g_flNextUpdateTime = -FLT_MAX;
		Update();
	}

	void UpdateClasses() {
		memset(g_iAmericanClassCounts, 0, sizeof(g_iAmericanClassCounts));
		memset(g_iBritishClassCounts, 0, sizeof(g_iBritishClassCounts));
		for (int i = 1; i <= gpGlobals->maxClients; i++) {
			CHL2MP_Player* pPlayer = ToHL2MPPlayer(UTIL_PlayerByIndex(i));
			if (pPlayer && pPlayer->GetNextClass() >= 0) {
				if (pPlayer->GetTeamNumber() == TEAM_AMERICANS) {
					g_iAmericanClassCounts[pPlayer->GetNextClass()]++;
				}
				else if (pPlayer->GetTeamNumber() == TEAM_BRITISH) {
					g_iBritishClassCounts[pPlayer->GetNextClass()]++;
				}
			}
		}
	}

	static const float UPDATE_INTERVAL = 1.f;
	void Update() {
		if (gpGlobals->curtime > g_flNextUpdateTime) {
			g_flNextUpdateTime = gpGlobals->curtime + UPDATE_INTERVAL;

			UpdateClasses();
		}
	}

	int GetNumPlayersOfClass(const CPlayerClass* pClass) {
		if (pClass->m_iDefaultTeam == TEAM_AMERICANS)
			return g_iAmericanClassCounts[pClass->m_iClassNumber];
		else
			return g_iBritishClassCounts[pClass->m_iClassNumber];

	}
}

void convertSteamIDToString(const CSteamID& id, char* buffer, int bufferSize);

CON_COMMAND(csv_export, "Generates a CSV inside a \'csv\' folder containing current player stats, name paramater optional") {
	char filename[128];

	if (args.ArgC() == 2) {
		//block custom point_clientcommand entities from writing to other directories
		if (args[1][0] == '.') {
			return;
		}
		Q_snprintf(filename, sizeof(filename), "%s.csv", args[1]);
	}
	else {
		int americanScore = g_Teams[TEAM_AMERICANS]->Get_Score();
		int britishScore = g_Teams[TEAM_BRITISH]->Get_Score();
		Q_snprintf(filename, sizeof(filename), "%s_%i_%i.csv", g_pGameRules->MapName(), americanScore, britishScore);
	}

	CUtlBuffer buffer;
	buffer.SetBufferType(true, false);
	buffer.PutString("steamid,name,team,score,damage,ping\n");
	for (int i = 0; i <= gpGlobals->maxClients; i++) {
		if (g_PR->IsConnected(i)) {

			//steamid
			CSteamID id;
			player_info_t pi;
			bool bSteamIdFound = false;
			if (engine->GetPlayerInfo(i, &pi))
			{
				if (pi.friendsID && steamapicontext && steamapicontext->SteamUtils())
				{
					static EUniverse universe = k_EUniverseInvalid;

					if (universe == k_EUniverseInvalid)
						universe = steamapicontext->SteamUtils()->GetConnectedUniverse();

					id.InstancedSet(pi.friendsID, 1, universe, k_EAccountTypeIndividual);
					bSteamIdFound = true;
				}
			}
			if (bSteamIdFound) {
				char idBuffer[64];
				convertSteamIDToString(id, idBuffer, 64);
				buffer.PutString(idBuffer);
			}
			else {
				buffer.PutString("NULL");
			}
			buffer.PutChar(',');

			//name, convert commas to periods
			char nameBuffer[MAX_PLAYER_NAME_LENGTH + 1];
			strcpy_s(nameBuffer, g_PR->GetPlayerName(i));
			for (int j = 0; j < MAX_PLAYER_NAME_LENGTH + 1; j++) {
				if (nameBuffer[j] == ',') {
					nameBuffer[j] = '.';
				}
			}
			buffer.PutString(nameBuffer);
			buffer.PutChar(',');

			//team
			buffer.PutInt(g_PR->GetTeam(i));
			buffer.PutChar(',');

			//score
			buffer.PutInt(g_PR->GetPlayerScore(i));
			buffer.PutChar(',');

			//damage score
			buffer.PutInt(g_PR->GetDamageScore(i));
			buffer.PutChar(',');

			//ping
			buffer.PutInt(g_PR->GetPing(i));
			buffer.PutChar(',');

			//line delimiter
			buffer.PutChar('\n');
		}
	}

	filesystem->WriteFile(filename, "", buffer);

}<|endoftext|>


<commit_before>/*
 * Summary: Exceptions for the C++ interface
 *
 * Copy: See Copyright for the status of this software.
 *
 */

/**
 * @file
 * @brief Exception declarations
 */

#ifndef LIBMEMACHED_EXCEPTION_HPP
#define LIBMEMACHED_EXCEPTION_HPP

#include <stdexcept>

namespace memcache
{
  class Exception : public std::runtime_error
  {
  public:
    Exception(const std::string& msg, bool in_errno)
      :
        std::runtime_error(msg),
        errno(in_errno)
    {}

    Exception(const char *msg, bool in_errno)
      :
        std::runtime_error(msg),
        errno(in_errno) {}

    virtual ~Exception() throw() {}

    int getErrno() const
    {
      return errno;
    }

  private:
    int errno;
  };

  class Warning : public Exception
  {
  public:
    Warning(const std::string& msg, bool errno) : Exception(msg, errno) {}
    Warning(const char *msg, bool errno) : Exception(msg, errno) {}
  };

  class Error : public Exception
  {
  public:
    Error(const std::string& msg, bool errno) : Exception(msg, errno) {}
    Error(const char *msg, bool errno) : Exception(msg, errno) {}
    virtual ~Error() throw() {}
  };

} /* namespace libmemcached */

#endif /* LIBMEMACHED_EXCEPTION_HPP */
<commit_msg>Correcting mistake I made in exceptions header file.<commit_after>/*
 * Summary: Exceptions for the C++ interface
 *
 * Copy: See Copyright for the status of this software.
 *
 */

/**
 * @file
 * @brief Exception declarations
 */

#ifndef LIBMEMACHED_EXCEPTION_HPP
#define LIBMEMACHED_EXCEPTION_HPP

#include <stdexcept>

namespace memcache
{
  class Exception : public std::runtime_error
  {
  public:
    Exception(const std::string& msg, bool in_errno)
      :
        std::runtime_error(msg),
        errno(in_errno)
    {}

    Exception(const char *msg, bool in_errno)
      :
        std::runtime_error(msg),
        errno(in_errno) {}

    virtual ~Exception() throw() {}

    int getErrno() const
    {
      return errno;
    }

  private:
    int errno;
  };

  class Warning : public Exception
  {
  public:
    Warning(const std::string& msg, bool in_errno) : Exception(msg, in_errno) {}
    Warning(const char *msg, bool in_errno) : Exception(msg, in_errno) {}
  };

  class Error : public Exception
  {
  public:
    Error(const std::string& msg, bool in_errno) : Exception(msg, in_errno) {}
    Error(const char *msg, bool in_errno) : Exception(msg, in_errno) {}
    virtual ~Error() throw() {}
  };

} /* namespace libmemcached */

#endif /* LIBMEMACHED_EXCEPTION_HPP */
<|endoftext|>


<commit_before>// Copyright (c) 2010 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "views/controls/button/custom_button.h"

#include "app/throb_animation.h"
#include "base/keyboard_codes.h"
#include "views/screen.h"

namespace views {

// How long the hover animation takes if uninterrupted.
static const int kHoverFadeDurationMs = 150;

////////////////////////////////////////////////////////////////////////////////
// CustomButton, public:

CustomButton::~CustomButton() {
}

void CustomButton::SetState(ButtonState state) {
  if (state == state_)
    return;

  if (animate_on_state_change_ || !hover_animation_->is_animating()) {
    animate_on_state_change_ = true;
    if (state_ == BS_NORMAL && state == BS_HOT) {
      // Button is hovered from a normal state, start hover animation.
      hover_animation_->Show();
    } else if (state_ == BS_HOT && state == BS_NORMAL) {
      // Button is returning to a normal state from hover, start hover
      // fade animation.
      hover_animation_->Hide();
    } else {
      hover_animation_->Stop();
    }
  }

  state_ = state;
  SchedulePaint();
}

void CustomButton::StartThrobbing(int cycles_til_stop) {
  animate_on_state_change_ = false;
  hover_animation_->StartThrobbing(cycles_til_stop);
}

void CustomButton::SetAnimationDuration(int duration) {
  hover_animation_->SetSlideDuration(duration);
}

////////////////////////////////////////////////////////////////////////////////
// CustomButton, View overrides:

bool CustomButton::GetAccessibleState(AccessibilityTypes::State* state) {
  *state = 0;
  switch (state_) {
    case BS_HOT:
      *state = AccessibilityTypes::STATE_HOTTRACKED;
    case BS_PUSHED:
      *state = AccessibilityTypes::STATE_PRESSED;
    case BS_DISABLED:
      *state = AccessibilityTypes::STATE_UNAVAILABLE;
    case BS_NORMAL:
    case BS_COUNT:
      // No additional accessibility state set for this button state.
      break;
  }

  return true;
}

void CustomButton::SetEnabled(bool enabled) {
  if (enabled ? (state_ != BS_DISABLED) : (state_ == BS_DISABLED))
    return;

  if (enabled)
    SetState(IsMouseHovered() ? BS_HOT : BS_NORMAL);
  else
    SetState(BS_DISABLED);
}

bool CustomButton::IsEnabled() const {
  return state_ != BS_DISABLED;
}

bool CustomButton::IsFocusable() const {
  return (state_ != BS_DISABLED) && View::IsFocusable();
}

bool CustomButton::IsMouseHovered() const {
  // If we haven't yet been placed in an onscreen view hierarchy, we can't be
  // hovered.
  if (!GetWidget())
    return false;

  gfx::Point cursor_pos(Screen::GetCursorScreenPoint());
  ConvertPointToView(NULL, this, &cursor_pos);
  return HitTest(cursor_pos);
}

////////////////////////////////////////////////////////////////////////////////
// CustomButton, protected:

CustomButton::CustomButton(ButtonListener* listener)
    : Button(listener),
      state_(BS_NORMAL),
      animate_on_state_change_(true),
      triggerable_event_flags_(MouseEvent::EF_LEFT_BUTTON_DOWN),
      request_focus_on_press_(true) {
  hover_animation_.reset(new ThrobAnimation(this));
  hover_animation_->SetSlideDuration(kHoverFadeDurationMs);
}

bool CustomButton::IsTriggerableEvent(const MouseEvent& e) {
  return (triggerable_event_flags_ & e.GetFlags()) != 0;
}

////////////////////////////////////////////////////////////////////////////////
// CustomButton, View overrides (protected):

bool CustomButton::AcceleratorPressed(const Accelerator& accelerator) {
  if (!enabled_)
    return false;

  SetState(BS_NORMAL);
  KeyEvent key_event(Event::ET_KEY_RELEASED, accelerator.GetKeyCode(),
                     accelerator.modifiers(), 0, 0);
  NotifyClick(key_event);
  return true;
}

bool CustomButton::OnMousePressed(const MouseEvent& e) {
  if (state_ != BS_DISABLED) {
    if (ShouldEnterPushedState(e) && HitTest(e.location()))
      SetState(BS_PUSHED);
    if (request_focus_on_press_)
      RequestFocus();
  }
  return true;
}

bool CustomButton::OnMouseDragged(const MouseEvent& e) {
  if (state_ != BS_DISABLED) {
    if (HitTest(e.location()))
      SetState(ShouldEnterPushedState(e) ? BS_PUSHED : BS_HOT);
    else
      SetState(BS_NORMAL);
  }
  return true;
}

void CustomButton::OnMouseReleased(const MouseEvent& e, bool canceled) {
  // Starting a drag results in a MouseReleased, we need to ignore it.
  if ((state_ == BS_DISABLED) || InDrag())
    return;

  if (!HitTest(e.location())) {
    SetState(BS_NORMAL);
    return;
  }

  SetState(BS_HOT);
  if (!canceled && IsTriggerableEvent(e)) {
    NotifyClick(e);
    // NOTE: We may be deleted at this point (by the listener's notification
    // handler).
  }
}

void CustomButton::OnMouseEntered(const MouseEvent& e) {
  if (state_ != BS_DISABLED)
    SetState(BS_HOT);
}

void CustomButton::OnMouseMoved(const MouseEvent& e) {
  if (state_ != BS_DISABLED)
    SetState(HitTest(e.location()) ? BS_HOT : BS_NORMAL);
}

void CustomButton::OnMouseExited(const MouseEvent& e) {
  // Starting a drag results in a MouseExited, we need to ignore it.
  if (state_ != BS_DISABLED && !InDrag())
    SetState(BS_NORMAL);
}

bool CustomButton::OnKeyPressed(const KeyEvent& e) {
  if (state_ == BS_DISABLED)
    return false;

  // Space sets button state to pushed. Enter clicks the button. This matches
  // the Windows native behavior of buttons, where Space clicks the button on
  // KeyRelease and Enter clicks the button on KeyPressed.
  if (e.GetKeyCode() == base::VKEY_SPACE) {
    SetState(BS_PUSHED);
  } else if (e.GetKeyCode() == base::VKEY_RETURN) {
    SetState(BS_NORMAL);
    NotifyClick(e);
  }
  return true;
}

bool CustomButton::OnKeyReleased(const KeyEvent& e) {
  if ((state_ == BS_DISABLED) || (e.GetKeyCode() != base::VKEY_SPACE))
    return false;

  SetState(BS_NORMAL);
  NotifyClick(e);
  return true;
}

void CustomButton::OnDragDone() {
  SetState(BS_NORMAL);
}

void CustomButton::ShowContextMenu(const gfx::Point& p, bool is_mouse_gesture) {
  if (!GetContextMenuController())
    return;

  // We're about to show the context menu. Showing the context menu likely means
  // we won't get a mouse exited and reset state. Reset it now to be sure.
  if (state_ != BS_DISABLED)
    SetState(BS_NORMAL);
  View::ShowContextMenu(p, is_mouse_gesture);
}

void CustomButton::ViewHierarchyChanged(bool is_add, View *parent,
                                        View *child) {
  if (!is_add && state_ != BS_DISABLED)
    SetState(BS_NORMAL);
}

void CustomButton::SetHotTracked(bool flag) {
  if (state_ != BS_DISABLED)
    SetState(flag ? BS_HOT : BS_NORMAL);

  if (flag)
    NotifyAccessibilityEvent(AccessibilityTypes::EVENT_FOCUS);
}

bool CustomButton::IsHotTracked() const {
  return state_ == BS_HOT;
}

void CustomButton::WillLoseFocus() {
  if (IsHotTracked())
    SetState(BS_NORMAL);
}

////////////////////////////////////////////////////////////////////////////////
// CustomButton, AnimationDelegate implementation:

void CustomButton::AnimationProgressed(const Animation* animation) {
  SchedulePaint();
}

bool CustomButton::ShouldEnterPushedState(const MouseEvent& e) {
  return IsTriggerableEvent(e);
}

}  // namespace views
<commit_msg>Fix a regression in r56766  CustomButton::OnKeyEvent should return false when the event is not handled<commit_after>// Copyright (c) 2010 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "views/controls/button/custom_button.h"

#include "app/throb_animation.h"
#include "base/keyboard_codes.h"
#include "views/screen.h"

namespace views {

// How long the hover animation takes if uninterrupted.
static const int kHoverFadeDurationMs = 150;

////////////////////////////////////////////////////////////////////////////////
// CustomButton, public:

CustomButton::~CustomButton() {
}

void CustomButton::SetState(ButtonState state) {
  if (state == state_)
    return;

  if (animate_on_state_change_ || !hover_animation_->is_animating()) {
    animate_on_state_change_ = true;
    if (state_ == BS_NORMAL && state == BS_HOT) {
      // Button is hovered from a normal state, start hover animation.
      hover_animation_->Show();
    } else if (state_ == BS_HOT && state == BS_NORMAL) {
      // Button is returning to a normal state from hover, start hover
      // fade animation.
      hover_animation_->Hide();
    } else {
      hover_animation_->Stop();
    }
  }

  state_ = state;
  SchedulePaint();
}

void CustomButton::StartThrobbing(int cycles_til_stop) {
  animate_on_state_change_ = false;
  hover_animation_->StartThrobbing(cycles_til_stop);
}

void CustomButton::SetAnimationDuration(int duration) {
  hover_animation_->SetSlideDuration(duration);
}

////////////////////////////////////////////////////////////////////////////////
// CustomButton, View overrides:

bool CustomButton::GetAccessibleState(AccessibilityTypes::State* state) {
  *state = 0;
  switch (state_) {
    case BS_HOT:
      *state = AccessibilityTypes::STATE_HOTTRACKED;
    case BS_PUSHED:
      *state = AccessibilityTypes::STATE_PRESSED;
    case BS_DISABLED:
      *state = AccessibilityTypes::STATE_UNAVAILABLE;
    case BS_NORMAL:
    case BS_COUNT:
      // No additional accessibility state set for this button state.
      break;
  }

  return true;
}

void CustomButton::SetEnabled(bool enabled) {
  if (enabled ? (state_ != BS_DISABLED) : (state_ == BS_DISABLED))
    return;

  if (enabled)
    SetState(IsMouseHovered() ? BS_HOT : BS_NORMAL);
  else
    SetState(BS_DISABLED);
}

bool CustomButton::IsEnabled() const {
  return state_ != BS_DISABLED;
}

bool CustomButton::IsFocusable() const {
  return (state_ != BS_DISABLED) && View::IsFocusable();
}

bool CustomButton::IsMouseHovered() const {
  // If we haven't yet been placed in an onscreen view hierarchy, we can't be
  // hovered.
  if (!GetWidget())
    return false;

  gfx::Point cursor_pos(Screen::GetCursorScreenPoint());
  ConvertPointToView(NULL, this, &cursor_pos);
  return HitTest(cursor_pos);
}

////////////////////////////////////////////////////////////////////////////////
// CustomButton, protected:

CustomButton::CustomButton(ButtonListener* listener)
    : Button(listener),
      state_(BS_NORMAL),
      animate_on_state_change_(true),
      triggerable_event_flags_(MouseEvent::EF_LEFT_BUTTON_DOWN),
      request_focus_on_press_(true) {
  hover_animation_.reset(new ThrobAnimation(this));
  hover_animation_->SetSlideDuration(kHoverFadeDurationMs);
}

bool CustomButton::IsTriggerableEvent(const MouseEvent& e) {
  return (triggerable_event_flags_ & e.GetFlags()) != 0;
}

////////////////////////////////////////////////////////////////////////////////
// CustomButton, View overrides (protected):

bool CustomButton::AcceleratorPressed(const Accelerator& accelerator) {
  if (!enabled_)
    return false;

  SetState(BS_NORMAL);
  KeyEvent key_event(Event::ET_KEY_RELEASED, accelerator.GetKeyCode(),
                     accelerator.modifiers(), 0, 0);
  NotifyClick(key_event);
  return true;
}

bool CustomButton::OnMousePressed(const MouseEvent& e) {
  if (state_ != BS_DISABLED) {
    if (ShouldEnterPushedState(e) && HitTest(e.location()))
      SetState(BS_PUSHED);
    if (request_focus_on_press_)
      RequestFocus();
  }
  return true;
}

bool CustomButton::OnMouseDragged(const MouseEvent& e) {
  if (state_ != BS_DISABLED) {
    if (HitTest(e.location()))
      SetState(ShouldEnterPushedState(e) ? BS_PUSHED : BS_HOT);
    else
      SetState(BS_NORMAL);
  }
  return true;
}

void CustomButton::OnMouseReleased(const MouseEvent& e, bool canceled) {
  // Starting a drag results in a MouseReleased, we need to ignore it.
  if ((state_ == BS_DISABLED) || InDrag())
    return;

  if (!HitTest(e.location())) {
    SetState(BS_NORMAL);
    return;
  }

  SetState(BS_HOT);
  if (!canceled && IsTriggerableEvent(e)) {
    NotifyClick(e);
    // NOTE: We may be deleted at this point (by the listener's notification
    // handler).
  }
}

void CustomButton::OnMouseEntered(const MouseEvent& e) {
  if (state_ != BS_DISABLED)
    SetState(BS_HOT);
}

void CustomButton::OnMouseMoved(const MouseEvent& e) {
  if (state_ != BS_DISABLED)
    SetState(HitTest(e.location()) ? BS_HOT : BS_NORMAL);
}

void CustomButton::OnMouseExited(const MouseEvent& e) {
  // Starting a drag results in a MouseExited, we need to ignore it.
  if (state_ != BS_DISABLED && !InDrag())
    SetState(BS_NORMAL);
}

bool CustomButton::OnKeyPressed(const KeyEvent& e) {
  if (state_ == BS_DISABLED)
    return false;

  // Space sets button state to pushed. Enter clicks the button. This matches
  // the Windows native behavior of buttons, where Space clicks the button on
  // KeyRelease and Enter clicks the button on KeyPressed.
  if (e.GetKeyCode() == base::VKEY_SPACE) {
    SetState(BS_PUSHED);
  } else if (e.GetKeyCode() == base::VKEY_RETURN) {
    SetState(BS_NORMAL);
    NotifyClick(e);
  } else {
    return false;
  }
  return true;
}

bool CustomButton::OnKeyReleased(const KeyEvent& e) {
  if ((state_ == BS_DISABLED) || (e.GetKeyCode() != base::VKEY_SPACE))
    return false;

  SetState(BS_NORMAL);
  NotifyClick(e);
  return true;
}

void CustomButton::OnDragDone() {
  SetState(BS_NORMAL);
}

void CustomButton::ShowContextMenu(const gfx::Point& p, bool is_mouse_gesture) {
  if (!GetContextMenuController())
    return;

  // We're about to show the context menu. Showing the context menu likely means
  // we won't get a mouse exited and reset state. Reset it now to be sure.
  if (state_ != BS_DISABLED)
    SetState(BS_NORMAL);
  View::ShowContextMenu(p, is_mouse_gesture);
}

void CustomButton::ViewHierarchyChanged(bool is_add, View *parent,
                                        View *child) {
  if (!is_add && state_ != BS_DISABLED)
    SetState(BS_NORMAL);
}

void CustomButton::SetHotTracked(bool flag) {
  if (state_ != BS_DISABLED)
    SetState(flag ? BS_HOT : BS_NORMAL);

  if (flag)
    NotifyAccessibilityEvent(AccessibilityTypes::EVENT_FOCUS);
}

bool CustomButton::IsHotTracked() const {
  return state_ == BS_HOT;
}

void CustomButton::WillLoseFocus() {
  if (IsHotTracked())
    SetState(BS_NORMAL);
}

////////////////////////////////////////////////////////////////////////////////
// CustomButton, AnimationDelegate implementation:

void CustomButton::AnimationProgressed(const Animation* animation) {
  SchedulePaint();
}

bool CustomButton::ShouldEnterPushedState(const MouseEvent& e) {
  return IsTriggerableEvent(e);
}

}  // namespace views
<|endoftext|>


<commit_before>// Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.

#include <vespa/vsm/vsm/docsumconfig.h>
#include <vespa/searchsummary/docsummary/docsumfieldwriter.h>

using search::docsummary::IDocsumFieldWriter;
using search::docsummary::EmptyDFW;

namespace vsm {

IDocsumFieldWriter::UP
DynamicDocsumConfig::createFieldWriter(const string & fieldName, const string & overrideName, const string & argument, bool & rc)
{
    IDocsumFieldWriter::UP fieldWriter;
    if ((overrideName == "staticrank") ||
        (overrideName == "ranklog") ||
        (overrideName == "label") ||
        (overrideName == "project") ||
        (overrideName == "positions") ||
        (overrideName == "absdist") ||
        (overrideName == "subproject"))
    {
        fieldWriter.reset(new EmptyDFW());
        rc = true;
    } else if ((overrideName == "attribute") ||
               ((overrideName == "geopos"))) {
        rc = true;
    } else {
        fieldWriter = search::docsummary::DynamicDocsumConfig::createFieldWriter(fieldName, overrideName, argument, rc);
    }
    return fieldWriter;
}

}
<commit_msg>Ignore attributecombiner rewriter for streaming search.<commit_after>// Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.

#include <vespa/vsm/vsm/docsumconfig.h>
#include <vespa/searchsummary/docsummary/docsumfieldwriter.h>

using search::docsummary::IDocsumFieldWriter;
using search::docsummary::EmptyDFW;

namespace vsm {

IDocsumFieldWriter::UP
DynamicDocsumConfig::createFieldWriter(const string & fieldName, const string & overrideName, const string & argument, bool & rc)
{
    IDocsumFieldWriter::UP fieldWriter;
    if ((overrideName == "staticrank") ||
        (overrideName == "ranklog") ||
        (overrideName == "label") ||
        (overrideName == "project") ||
        (overrideName == "positions") ||
        (overrideName == "absdist") ||
        (overrideName == "subproject"))
    {
        fieldWriter.reset(new EmptyDFW());
        rc = true;
    } else if ((overrideName == "attribute") ||
            (overrideName == "attributecombiner") ||
            (overrideName == "geopos")) {
        rc = true;
    } else {
        fieldWriter = search::docsummary::DynamicDocsumConfig::createFieldWriter(fieldName, overrideName, argument, rc);
    }
    return fieldWriter;
}

}
<|endoftext|>


<commit_before>// Copyright 2010 Google Inc. All Rights Reserved.
// Author: jacobsa@google.com (Aaron Jacobs)
//
// 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 "gjstest/internal/cpp/run_tests.h"

#include <string>
#include <vector>

#include <re2/re2.h>
#include <v8.h>

#include "base/basictypes.h"
#include "base/integral_types.h"
#include "base/logging.h"
#include "base/macros.h"
#include "base/stl_decl.h"
#include "base/stringprintf.h"
#include "base/timer.h"
#include "gjstest/internal/cpp/test_case.h"
#include "gjstest/internal/cpp/v8_utils.h"
#include "gjstest/internal/proto/named_scripts.pb.h"
#include "strings/strutil.h"
#include "util/gtl/map-util.h"
#include "util/hash/hash.h"
#include "webutil/xml/xml_writer.h"

using v8::Array;
using v8::Arguments;
using v8::Context;
using v8::Function;
using v8::Handle;
using v8::HandleScope;
using v8::Local;
using v8::Object;
using v8::ObjectTemplate;
using v8::Persistent;
using v8::String;
using v8::TryCatch;
using v8::Undefined;
using v8::Value;

namespace gjstest {

// Create XML output given an overall duration in seconds, a list of test names
// in the order of execution, a map from test names to durations, and a map from
// failed test names to failure messages.
static string MakeXml(
    uint32 duration_ms,
    const vector<string>& tests_run,
    const hash_map<string, double>& test_durations,
    const hash_map<string, string>& test_failure_messages) {
  webutil_xml::XmlWriter xml_writer("UTF-8", true);
  xml_writer.StartDocument("UTF-8");

  xml_writer.StartElement("testsuite");
  xml_writer.AddAttribute("name", "Google JS tests");
  xml_writer.AddAttribute("failures", SimpleItoa(test_failure_messages.size()));
  xml_writer.AddAttribute("time", SimpleDtoa(duration_ms / 1000.0));

  for (uint32 i = 0; i < tests_run.size(); ++i) {
    const string& name = tests_run[i];

    // Skip the tests that weren't actually run.
    if (!ContainsKey(test_durations, name)) continue;

    const double duration = FindOrDie(test_durations, name);

    xml_writer.StartElement("testcase");
    xml_writer.AddAttribute("name", name);
    xml_writer.AddAttribute("time", SimpleDtoa(duration));

    // Add a failure element if the test failed.
    if (ContainsKey(test_failure_messages, name)) {
      const string& failure_message = FindOrDie(test_failure_messages, name);

      xml_writer.StartElement("failure");
      xml_writer.WriteCData(failure_message);
      xml_writer.EndElement();  // failure
    }

    xml_writer.EndElement();  // testcase
  }

  xml_writer.EndElement();  // testsuite

  return xml_writer.GetContent();
}

// Get a reference to the function of the supplied name.
static Local<Function> GetFunctionNamed(const string& name) {
  const Local<Value> result = ExecuteJs(name, "");
  CHECK(!result.IsEmpty());
  CHECK(result->IsFunction());

  return Local<Function>::Cast(result);
}

static void ProcessTestCase(
    const string& name,
    const Handle<Function>& test_function,
    bool* success,
    string* output,
    hash_map<string, string>* test_failure_messages,
    hash_map<string, double>* test_durations) {
  // Run the test.
  TestCase test_case(test_function);
  test_case.Run();

  // Append the appropriate stuff to our output.
  StringAppendF(output, "[ RUN      ] %s\n", name.c_str());

  string status_message = "[       OK ]";
  if (!test_case.succeeded) {
    *success = false;
    status_message = "[  FAILED  ]";

    // Record the failure output for use in the XML later. Strip any
    // surrounding whitespace first.
    StripWhiteSpace(&test_case.failure_output);
    InsertOrDie(test_failure_messages, name, test_case.failure_output);
  }

  // Append the test output and the status message.
  StringAppendF(
      output,
      "%s%s %s (%u ms)\n",
      test_case.output.c_str(),
      status_message.c_str(),
      name.c_str(),
      test_case.duration_ms);

  // Record test duration.
  InsertOrDie(test_durations, name, test_case.duration_ms / 1000.0);
}

// Iterate over a map from test names to test functions, running each test
// function.
static void ProcessTestSuite(
    const RE2& test_filter,
    const Handle<Object>& test_functions,
    bool* success,
    string* output,
    vector<string>* tests_run,
    hash_map<string, string>* test_failure_messages,
    hash_map<string, double>* test_durations) {
  StringAppendF(output, "[----------]\n");

  const Local<Array> test_names = test_functions->GetPropertyNames();
  for (uint32 i = 0; i < test_names->Length(); ++i) {
    const Local<Value> name = test_names->Get(i);
    const Local<Value> test_function = test_functions->Get(name);
    CHECK(test_function->IsFunction());

    // Skip this test if it doesn't match our filter.
    const string string_name = ConvertToString(name);
    if (!RE2::FullMatch(string_name, test_filter)) continue;

    tests_run->push_back(string_name);
    ProcessTestCase(
        string_name,
        Local<Function>::Cast(test_function),
        success,
        output,
        test_failure_messages,
        test_durations);
  }

  StringAppendF(output, "[----------]\n\n");
}

bool RunTests(
    const NamedScripts& scripts,
    const string& test_filter_string,
    string* output,
    string* xml) {
  const RE2 test_filter(test_filter_string.empty() ? ".*" : test_filter_string);

  // Take ownership of all handles created.
  HandleScope handle_owner;

  // Create a context in which to run scripts and ensure that it's used whenever
  // a context is needed below. Export the global functions configured above.
  Persistent<Context> context = Context::New();
  Context::Scope context_scope(context);

  // Run all of the scripts.
  for (uint32 i = 0; i < scripts.script_size(); ++i) {
    const NamedScript& script = scripts.script(i);

    TryCatch try_catch;
    const Local<Value> result = ExecuteJs(script.source(), script.name());
    if (result.IsEmpty()) {
      *output += DescribeError(try_catch);
      return false;
    }
  }

  // Get a reference to gjstest.internal.getTestFunctions for later.
  const Local<Function> get_test_functions =
      GetFunctionNamed("gjstest.internal.getTestFunctions");

  // Keep maps from test name to failure message (if the test failed) and
  // duration in seconds.
  hash_map<string, string> test_failure_messages;
  hash_map<string, double> test_durations;
  vector<string> tests_run;

  // Keep track of how long the whole process takes, and whether there are any
  // failures.
  CycleTimer overall_timer;
  overall_timer.Start();
  bool success = true;

  // Iterate over all of the registered test suites.
  const Local<Value> test_suites_value =
      ExecuteJs("gjstest.internal.testSuites", "");
  CHECK(test_suites_value->IsArray());
  const Local<Array> test_suites = Local<Array>::Cast(test_suites_value);

  for (uint32 i = 0; i < test_suites->Length(); ++i) {
    const Local<Value> test_suite = test_suites->Get(i);
    CHECK(test_suite->IsObject());

    // Get the map of test functions registered for this test suite.
    Handle<Value> args[] = { test_suite };
    const Local<Value> test_functions_value =
        get_test_functions->Call(
            context->Global(),
            arraysize(args),
            args);
    CHECK(test_functions_value->IsObject());
    const Local<Object> test_functions =
        Local<Object>::Cast(test_functions_value);

    // Process this test suite.
    ProcessTestSuite(
        test_filter,
        test_functions,
        &success,
        output,
        &tests_run,
        &test_failure_messages,
        &test_durations);
  }

  overall_timer.Stop();

  StringAppendF(
      output,
      success ? "[  PASSED  ]\n" : "[  FAILED  ]\n");

  // Make sure that at least one test ran. This catches common errors with
  // mis-registering tests and so on.
  if (test_durations.empty()) {
    *output = "No tests found.\n";
    return false;
  }

  // Create an XML document describing the execution.
  *xml =
      MakeXml(
          overall_timer.GetInMs(),
          tests_run,
          test_durations,
          test_failure_messages);

  return success;
}

}  // namespace gjstest
<commit_msg>Added a missing newline.<commit_after>// Copyright 2010 Google Inc. All Rights Reserved.
// Author: jacobsa@google.com (Aaron Jacobs)
//
// 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 "gjstest/internal/cpp/run_tests.h"

#include <string>
#include <vector>

#include <re2/re2.h>
#include <v8.h>

#include "base/basictypes.h"
#include "base/integral_types.h"
#include "base/logging.h"
#include "base/macros.h"
#include "base/stl_decl.h"
#include "base/stringprintf.h"
#include "base/timer.h"
#include "gjstest/internal/cpp/test_case.h"
#include "gjstest/internal/cpp/v8_utils.h"
#include "gjstest/internal/proto/named_scripts.pb.h"
#include "strings/strutil.h"
#include "util/gtl/map-util.h"
#include "util/hash/hash.h"
#include "webutil/xml/xml_writer.h"

using v8::Array;
using v8::Arguments;
using v8::Context;
using v8::Function;
using v8::Handle;
using v8::HandleScope;
using v8::Local;
using v8::Object;
using v8::ObjectTemplate;
using v8::Persistent;
using v8::String;
using v8::TryCatch;
using v8::Undefined;
using v8::Value;

namespace gjstest {

// Create XML output given an overall duration in seconds, a list of test names
// in the order of execution, a map from test names to durations, and a map from
// failed test names to failure messages.
static string MakeXml(
    uint32 duration_ms,
    const vector<string>& tests_run,
    const hash_map<string, double>& test_durations,
    const hash_map<string, string>& test_failure_messages) {
  webutil_xml::XmlWriter xml_writer("UTF-8", true);
  xml_writer.StartDocument("UTF-8");

  xml_writer.StartElement("testsuite");
  xml_writer.AddAttribute("name", "Google JS tests");
  xml_writer.AddAttribute("failures", SimpleItoa(test_failure_messages.size()));
  xml_writer.AddAttribute("time", SimpleDtoa(duration_ms / 1000.0));

  for (uint32 i = 0; i < tests_run.size(); ++i) {
    const string& name = tests_run[i];

    // Skip the tests that weren't actually run.
    if (!ContainsKey(test_durations, name)) continue;

    const double duration = FindOrDie(test_durations, name);

    xml_writer.StartElement("testcase");
    xml_writer.AddAttribute("name", name);
    xml_writer.AddAttribute("time", SimpleDtoa(duration));

    // Add a failure element if the test failed.
    if (ContainsKey(test_failure_messages, name)) {
      const string& failure_message = FindOrDie(test_failure_messages, name);

      xml_writer.StartElement("failure");
      xml_writer.WriteCData(failure_message);
      xml_writer.EndElement();  // failure
    }

    xml_writer.EndElement();  // testcase
  }

  xml_writer.EndElement();  // testsuite

  return xml_writer.GetContent();
}

// Get a reference to the function of the supplied name.
static Local<Function> GetFunctionNamed(const string& name) {
  const Local<Value> result = ExecuteJs(name, "");
  CHECK(!result.IsEmpty());
  CHECK(result->IsFunction());

  return Local<Function>::Cast(result);
}

static void ProcessTestCase(
    const string& name,
    const Handle<Function>& test_function,
    bool* success,
    string* output,
    hash_map<string, string>* test_failure_messages,
    hash_map<string, double>* test_durations) {
  // Run the test.
  TestCase test_case(test_function);
  test_case.Run();

  // Append the appropriate stuff to our output.
  StringAppendF(output, "[ RUN      ] %s\n", name.c_str());

  string status_message = "[       OK ]";
  if (!test_case.succeeded) {
    *success = false;
    status_message = "[  FAILED  ]";

    // Record the failure output for use in the XML later. Strip any
    // surrounding whitespace first.
    StripWhiteSpace(&test_case.failure_output);
    InsertOrDie(test_failure_messages, name, test_case.failure_output);
  }

  // Append the test output and the status message.
  StringAppendF(
      output,
      "%s%s %s (%u ms)\n",
      test_case.output.c_str(),
      status_message.c_str(),
      name.c_str(),
      test_case.duration_ms);

  // Record test duration.
  InsertOrDie(test_durations, name, test_case.duration_ms / 1000.0);
}

// Iterate over a map from test names to test functions, running each test
// function.
static void ProcessTestSuite(
    const RE2& test_filter,
    const Handle<Object>& test_functions,
    bool* success,
    string* output,
    vector<string>* tests_run,
    hash_map<string, string>* test_failure_messages,
    hash_map<string, double>* test_durations) {
  StringAppendF(output, "[----------]\n");

  const Local<Array> test_names = test_functions->GetPropertyNames();
  for (uint32 i = 0; i < test_names->Length(); ++i) {
    const Local<Value> name = test_names->Get(i);
    const Local<Value> test_function = test_functions->Get(name);
    CHECK(test_function->IsFunction());

    // Skip this test if it doesn't match our filter.
    const string string_name = ConvertToString(name);
    if (!RE2::FullMatch(string_name, test_filter)) continue;

    tests_run->push_back(string_name);
    ProcessTestCase(
        string_name,
        Local<Function>::Cast(test_function),
        success,
        output,
        test_failure_messages,
        test_durations);
  }

  StringAppendF(output, "[----------]\n\n");
}

bool RunTests(
    const NamedScripts& scripts,
    const string& test_filter_string,
    string* output,
    string* xml) {
  const RE2 test_filter(test_filter_string.empty() ? ".*" : test_filter_string);

  // Take ownership of all handles created.
  HandleScope handle_owner;

  // Create a context in which to run scripts and ensure that it's used whenever
  // a context is needed below. Export the global functions configured above.
  Persistent<Context> context = Context::New();
  Context::Scope context_scope(context);

  // Run all of the scripts.
  for (uint32 i = 0; i < scripts.script_size(); ++i) {
    const NamedScript& script = scripts.script(i);

    TryCatch try_catch;
    const Local<Value> result = ExecuteJs(script.source(), script.name());
    if (result.IsEmpty()) {
      *output += DescribeError(try_catch) + "\n";
      return false;
    }
  }

  // Get a reference to gjstest.internal.getTestFunctions for later.
  const Local<Function> get_test_functions =
      GetFunctionNamed("gjstest.internal.getTestFunctions");

  // Keep maps from test name to failure message (if the test failed) and
  // duration in seconds.
  hash_map<string, string> test_failure_messages;
  hash_map<string, double> test_durations;
  vector<string> tests_run;

  // Keep track of how long the whole process takes, and whether there are any
  // failures.
  CycleTimer overall_timer;
  overall_timer.Start();
  bool success = true;

  // Iterate over all of the registered test suites.
  const Local<Value> test_suites_value =
      ExecuteJs("gjstest.internal.testSuites", "");
  CHECK(test_suites_value->IsArray());
  const Local<Array> test_suites = Local<Array>::Cast(test_suites_value);

  for (uint32 i = 0; i < test_suites->Length(); ++i) {
    const Local<Value> test_suite = test_suites->Get(i);
    CHECK(test_suite->IsObject());

    // Get the map of test functions registered for this test suite.
    Handle<Value> args[] = { test_suite };
    const Local<Value> test_functions_value =
        get_test_functions->Call(
            context->Global(),
            arraysize(args),
            args);
    CHECK(test_functions_value->IsObject());
    const Local<Object> test_functions =
        Local<Object>::Cast(test_functions_value);

    // Process this test suite.
    ProcessTestSuite(
        test_filter,
        test_functions,
        &success,
        output,
        &tests_run,
        &test_failure_messages,
        &test_durations);
  }

  overall_timer.Stop();

  StringAppendF(
      output,
      success ? "[  PASSED  ]\n" : "[  FAILED  ]\n");

  // Make sure that at least one test ran. This catches common errors with
  // mis-registering tests and so on.
  if (test_durations.empty()) {
    *output = "No tests found.\n";
    return false;
  }

  // Create an XML document describing the execution.
  *xml =
      MakeXml(
          overall_timer.GetInMs(),
          tests_run,
          test_durations,
          test_failure_messages);

  return success;
}

}  // namespace gjstest
<|endoftext|>


<commit_before>/* GStreamer
 * Copyright (C) <1999> Erik Walthinsen <omega@cse.ogi.edu>
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Library General Public License for more details.
 *
 * You should have received a copy of the GNU Library General Public
 * License along with this library; if not, write to the
 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
 * Boston, MA 02111-1307, USA.
 *
 */

#include <string.h>
#include <gst/gst.h>

#include "gstmonkeydec.h"
#include "gstmonkeyenc.h"

extern GstElementDetails gst_monkeydec_details;
extern GstElementDetails gst_monkeyenc_details;

static GstCaps* monkey_type_find (GstBuffer *buf, gpointer priv);

GstPadTemplate *monkeydec_sink_template, *monkeydec_src_template;
GstPadTemplate *monkeyenc_sink_template, *monkeyenc_src_template;

static GstCaps*
monkey_caps_factory (void)
{
  return gst_caps_new ("monkey_application",
                       "application/x-ape",
                       NULL
		      );
}

static GstCaps*
raw_caps_factory (void)
{
  return gst_caps_new ("monkey_raw",
                       "audio/raw",
                       gst_props_new (
                         "format",      GST_PROPS_STRING ("int"),
                         "law",         GST_PROPS_INT (0),
                         "endianness",  GST_PROPS_INT (G_BYTE_ORDER),
                         "signed",      GST_PROPS_BOOLEAN (TRUE),
                         "width",       GST_PROPS_INT (16),
                         "depth",       GST_PROPS_INT (16),
                         "rate",        GST_PROPS_INT_RANGE (11025, 44100),
                         "channels",    GST_PROPS_INT_RANGE (1, 2),
                         NULL
                       ));
}

static GstCaps*
wav_caps_factory (void)
{
  return gst_caps_new ("monkey_wav",
                       "audio/x-wav",
                       NULL );
}

static GstTypeDefinition monkeydefinition = {
  "monkey_application/x-ape", "application/x-ape", ".ape", monkey_type_find,
};

static GstCaps*
monkey_type_find (GstBuffer *buf, gpointer priv)
{
  if (strncmp ((gchar *)GST_BUFFER_DATA (buf), "MAC ", 4) != 0)
    return NULL;

  return gst_caps_new ("monkey_type_find", "application/x-ape", NULL);
}

static gboolean
plugin_init (GModule * module, GstPlugin * plugin)
{
  GstElementFactory *monkey_dec, *monkey_enc;
  GstTypeFactory *type;
  GstCaps *raw_caps, *wav_caps, *monkey_caps;

  /* this filter needs the bytestream package */
  if (!gst_library_load ("gstbytestream"))
    return FALSE;

  raw_caps = raw_caps_factory ();
  monkey_caps = monkey_caps_factory ();
  wav_caps = wav_caps_factory ();

  /* create an elementfactory for the monkeydec element */
  monkey_dec = gst_element_factory_new ("monkeydec", GST_TYPE_MONKEYDEC, &gst_monkeydec_details);
  g_return_val_if_fail(monkey_dec != NULL, FALSE);

  /* register sink pads */
  monkeydec_sink_template = gst_pad_template_new ("sink", GST_PAD_SINK,
						                                      GST_PAD_ALWAYS,
                                    						  monkey_caps, NULL);
  gst_element_factory_add_pad_template (monkey_dec, monkeydec_sink_template);

  /* register src pads */
  monkeydec_src_template = gst_pad_template_new ("src", GST_PAD_SRC,
                              					         GST_PAD_ALWAYS,
                              					         raw_caps, NULL);
  gst_element_factory_add_pad_template (monkey_dec, monkeydec_src_template);

  gst_element_factory_set_rank (monkey_dec, GST_ELEMENT_RANK_PRIMARY);
  gst_plugin_add_feature (plugin, GST_PLUGIN_FEATURE (monkey_dec));

  /* create an elementfactory for the monkeydec element */
  monkey_enc = gst_element_factory_new ("monkeyenc", GST_TYPE_MONKEYENC, &gst_monkeyenc_details);
  g_return_val_if_fail(monkey_enc != NULL, FALSE);

  /* register sink pads */
  monkeyenc_sink_template = gst_pad_template_new ("sink", GST_PAD_SINK,
						                                      GST_PAD_ALWAYS,
                                    						  raw_caps, NULL);
  gst_element_factory_add_pad_template (monkey_enc, monkeyenc_sink_template);

  /* register src pads */
  monkeyenc_src_template = gst_pad_template_new ("src", GST_PAD_SRC,
                                  			         GST_PAD_ALWAYS,
                              					         monkey_caps, NULL);
  gst_element_factory_add_pad_template (monkey_enc, monkeyenc_src_template);

  gst_plugin_add_feature (plugin, GST_PLUGIN_FEATURE (monkey_enc));

  type = gst_type_factory_new (&monkeydefinition);
  gst_plugin_add_feature (plugin, GST_PLUGIN_FEATURE (type));

  return TRUE;
}

GstPluginDesc plugin_desc = {
  GST_VERSION_MAJOR,
  GST_VERSION_MINOR,
  "monkey audio",
  plugin_init
};
<commit_msg>MonkeyAudio support little endian data only<commit_after>/* GStreamer
 * Copyright (C) <1999> Erik Walthinsen <omega@cse.ogi.edu>
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Library General Public License for more details.
 *
 * You should have received a copy of the GNU Library General Public
 * License along with this library; if not, write to the
 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
 * Boston, MA 02111-1307, USA.
 *
 */

#include <string.h>
#include <gst/gst.h>

#include "gstmonkeydec.h"
#include "gstmonkeyenc.h"

extern GstElementDetails gst_monkeydec_details;
extern GstElementDetails gst_monkeyenc_details;

static GstCaps* monkey_type_find (GstBuffer *buf, gpointer priv);

GstPadTemplate *monkeydec_sink_template, *monkeydec_src_template;
GstPadTemplate *monkeyenc_sink_template, *monkeyenc_src_template;

static GstCaps*
monkey_caps_factory (void)
{
  return gst_caps_new ("monkey_application",
                       "application/x-ape",
                       NULL
		      );
}

static GstCaps*
raw_caps_factory (void)
{
  return gst_caps_new ("monkey_raw",
                       "audio/raw",
                       gst_props_new (
                         "format",      GST_PROPS_STRING ("int"),
                         "law",         GST_PROPS_INT (0),
                         "endianness",  GST_PROPS_INT (G_LITTLE_ENDIAN),
                         "signed",      GST_PROPS_BOOLEAN (TRUE),
                         "width",       GST_PROPS_INT (16),
                         "depth",       GST_PROPS_INT (16),
                         "rate",        GST_PROPS_INT_RANGE (11025, 44100),
                         "channels",    GST_PROPS_INT_RANGE (1, 2),
                         NULL
                       ));
}

static GstCaps*
wav_caps_factory (void)
{
  return gst_caps_new ("monkey_wav",
                       "audio/x-wav",
                       NULL );
}

static GstTypeDefinition monkeydefinition = {
  "monkey_application/x-ape", "application/x-ape", ".ape", monkey_type_find,
};

static GstCaps*
monkey_type_find (GstBuffer *buf, gpointer priv)
{
  if (strncmp ((gchar *)GST_BUFFER_DATA (buf), "MAC ", 4) != 0)
    return NULL;

  return gst_caps_new ("monkey_type_find", "application/x-ape", NULL);
}

static gboolean
plugin_init (GModule * module, GstPlugin * plugin)
{
  GstElementFactory *monkey_dec, *monkey_enc;
  GstTypeFactory *type;
  GstCaps *raw_caps, *wav_caps, *monkey_caps;

  /* this filter needs the bytestream package */
  if (!gst_library_load ("gstbytestream"))
    return FALSE;

  raw_caps = raw_caps_factory ();
  monkey_caps = monkey_caps_factory ();
  wav_caps = wav_caps_factory ();

  /* create an elementfactory for the monkeydec element */
  monkey_dec = gst_element_factory_new ("monkeydec", GST_TYPE_MONKEYDEC, &gst_monkeydec_details);
  g_return_val_if_fail(monkey_dec != NULL, FALSE);

  /* register sink pads */
  monkeydec_sink_template = gst_pad_template_new ("sink", GST_PAD_SINK,
						                                      GST_PAD_ALWAYS,
                                    						  monkey_caps, NULL);
  gst_element_factory_add_pad_template (monkey_dec, monkeydec_sink_template);

  /* register src pads */
  monkeydec_src_template = gst_pad_template_new ("src", GST_PAD_SRC,
                              					         GST_PAD_ALWAYS,
                              					         raw_caps, NULL);
  gst_element_factory_add_pad_template (monkey_dec, monkeydec_src_template);

  gst_element_factory_set_rank (monkey_dec, GST_ELEMENT_RANK_PRIMARY);
  gst_plugin_add_feature (plugin, GST_PLUGIN_FEATURE (monkey_dec));

  /* create an elementfactory for the monkeydec element */
  monkey_enc = gst_element_factory_new ("monkeyenc", GST_TYPE_MONKEYENC, &gst_monkeyenc_details);
  g_return_val_if_fail(monkey_enc != NULL, FALSE);

  /* register sink pads */
  monkeyenc_sink_template = gst_pad_template_new ("sink", GST_PAD_SINK,
						                                      GST_PAD_ALWAYS,
                                    						  raw_caps, NULL);
  gst_element_factory_add_pad_template (monkey_enc, monkeyenc_sink_template);

  /* register src pads */
  monkeyenc_src_template = gst_pad_template_new ("src", GST_PAD_SRC,
                                  			         GST_PAD_ALWAYS,
                              					         monkey_caps, NULL);
  gst_element_factory_add_pad_template (monkey_enc, monkeyenc_src_template);

  gst_plugin_add_feature (plugin, GST_PLUGIN_FEATURE (monkey_enc));

  type = gst_type_factory_new (&monkeydefinition);
  gst_plugin_add_feature (plugin, GST_PLUGIN_FEATURE (type));

  return TRUE;
}

GstPluginDesc plugin_desc = {
  GST_VERSION_MAJOR,
  GST_VERSION_MINOR,
  "monkey audio",
  plugin_init
};
<|endoftext|>