<commit_before>/*  This file is part of the Vc library.

    Copyright (C) 2009-2012 Matthias Kretz <kretz@kde.org>

    Vc is 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 3 of
    the License, or (at your option) any later version.

    Vc 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 Vc.  If not, see <http://www.gnu.org/licenses/>.

*/

#include <Vc/Vc>
#include "unittest.h"
#include <iostream>
#include "vectormemoryhelper.h"

using namespace Vc;

template<typename Vec> void testSort()
{
    typedef typename Vec::EntryType EntryType;
    typedef typename Vec::IndexType IndexType;

    typename Vec::Memory _a;
    const IndexType _ref(IndexesFromZero);
    Vec ref(_ref);
    Vec a;
    int maxPerm = 1;
    for (int x = Vec::Size; x > 0; --x) {
        maxPerm *= x;
    }
    for (int perm = 0; perm < maxPerm; ++perm) {
        int rest = perm;
        for (int i = 0; i < Vec::Size; ++i) {
            _a[i] = 0;
            for (int j = 0; j < i; ++j) {
                if (_a[i] == _a[j]) {
                    ++_a[i];
                    j = -1;
                }
            }
            _a[i] += rest % (Vec::Size - i);
            rest /= (Vec::Size - i);
            for (int j = 0; j < i; ++j) {
                if (_a[i] == _a[j]) {
                    ++_a[i];
                    j = -1;
                }
            }
        }
        a.load(_a);
        //std::cout << a << a.sorted() << std::endl;
        COMPARE(ref, a.sorted()) << ", a: " << a;
    }
}

template<typename T, typename Mem> struct Foo
{
    Foo() : i(0) {}
    void reset() { i = 0; }
    void operator()(T v) { d[i++] = v; }
    Mem d;
    int i;
};

template<typename V> void testCall()
{
    typedef typename V::EntryType T;
    typedef typename V::IndexType I;
    typedef typename V::Mask M;
    typedef typename I::Mask MI;
    const I _indexes(IndexesFromZero);
    const MI _odd = (_indexes & I(One)) > 0;
    const M odd(_odd);
    V a(_indexes);
    Foo<T, typename V::Memory> f;
    a.callWithValuesSorted(f);
    V b(f.d);
    COMPARE(b, a);

    f.reset();
    a(odd) -= 1;
    a.callWithValuesSorted(f);
    V c(f.d);
    for (int i = 0; i < V::Size / 2; ++i) {
        COMPARE(a[i * 2], c[i]);
    }
    for (int i = V::Size / 2; i < V::Size; ++i) {
        COMPARE(b[i], c[i]);
    }
}

template<typename V> void testForeachBit()
{
    typedef typename V::EntryType T;
    typedef typename V::IndexType I;
    typedef typename V::Mask M;
    typedef typename I::Mask MI;
    const I indexes(IndexesFromZero);
    for_all_masks(V, mask) {
        V tmp = V::Zero();
        foreach_bit(int j, mask) {
            tmp[j] = T(1);
        }
        COMPARE(tmp == V::One(), mask);

        int count = 0;
        foreach_bit(int j, mask) {
            ++count;
            if (j >= 0) {
                continue;
            }
        }
        COMPARE(count, mask.count());

        count = 0;
        foreach_bit(int j, mask) {
            if (j >= 0) {
                break;
            }
            ++count;
        }
        COMPARE(count, 0);
    }
}

template<typename V> void copySign()
{
    typedef typename V::EntryType T;
    V v(One);
    V positive(One);
    V negative = -positive;
    COMPARE(v, v.copySign(positive));
    COMPARE(-v, v.copySign(negative));
}

#ifdef VC_MSVC
void bzero(void *p, size_t n) { memset(p, 0, n); }
#else
#include <strings.h>
#endif

template<typename V> void Random()
{
    typedef typename V::EntryType T;
    enum {
        NBits = 3,
        NBins = 1 << NBits,                        // short int
        TotalBits = sizeof(T) * 8,                 //    16  32
        RightShift = TotalBits - NBits,            //    13  29
        NHistograms = TotalBits - NBits + 1,       //    14  30
        LeftShift = (RightShift + 1) / NHistograms,//     1   1
        Mean = 135791,
        MinGood = Mean - Mean/10,
        MaxGood = Mean + Mean/10
    };
    const V mask((1 << NBits) - 1);
    int histogram[NHistograms][NBins];
    bzero(&histogram[0][0], sizeof(histogram));
    for (size_t i = 0; i < NBins * Mean / V::Size; ++i) {
        const V rand = V::Random();
        for (size_t hist = 0; hist < NHistograms; ++hist) {
            const V bin = ((rand << (hist * LeftShift)) >> RightShift) & mask;
            for (size_t k = 0; k < V::Size; ++k) {
                ++histogram[hist][bin[k]];
            }
        }
    }
//#define PRINT_RANDOM_HISTOGRAM
#ifdef PRINT_RANDOM_HISTOGRAM
    for (size_t hist = 0; hist < NHistograms; ++hist) {
        std::cout << "histogram[" << std::setw(2) << hist << "]: ";
        for (size_t bin = 0; bin < NBins; ++bin) {
            std::cout << std::setw(3) << (histogram[hist][bin] - Mean) * 1000 / Mean << "|";
        }
        std::cout << std::endl;
    }
#endif
    for (size_t hist = 0; hist < NHistograms; ++hist) {
        for (size_t bin = 0; bin < NBins; ++bin) {
            VERIFY(histogram[hist][bin] > MinGood)
                << " bin = " << bin << " is " << histogram[0][bin];
            VERIFY(histogram[hist][bin] < MaxGood)
                << " bin = " << bin << " is " << histogram[0][bin];
        }
    }
}

template<typename V, typename I> void FloatRandom()
{
    typedef typename V::EntryType T;
    enum {
        NBins = 64,
        NHistograms = 1,
        Mean = 135791,
        MinGood = Mean - Mean/10,
        MaxGood = Mean + Mean/10
    };
    int histogram[NHistograms][NBins];
    bzero(&histogram[0][0], sizeof(histogram));
    for (size_t i = 0; i < NBins * Mean / V::Size; ++i) {
        const V rand = V::Random();
        const I bin = static_cast<I>(rand * T(NBins));
        for (size_t k = 0; k < V::Size; ++k) {
            ++histogram[0][bin[k]];
        }
    }
#ifdef PRINT_RANDOM_HISTOGRAM
    for (size_t hist = 0; hist < NHistograms; ++hist) {
        std::cout << "histogram[" << std::setw(2) << hist << "]: ";
        for (size_t bin = 0; bin < NBins; ++bin) {
            std::cout << std::setw(3) << (histogram[hist][bin] - Mean) * 1000 / Mean << "|";
        }
        std::cout << std::endl;
    }
#endif
    for (size_t hist = 0; hist < NHistograms; ++hist) {
        for (size_t bin = 0; bin < NBins; ++bin) {
            VERIFY(histogram[hist][bin] > MinGood)
                << " bin = " << bin << " is " << histogram[0][bin];
            VERIFY(histogram[hist][bin] < MaxGood)
                << " bin = " << bin << " is " << histogram[0][bin];
        }
    }
}

template<> void Random<float_v>() { FloatRandom<float_v, int_v>(); }
template<> void Random<double_v>() { FloatRandom<double_v, int_v>(); }
template<> void Random<sfloat_v>() { FloatRandom<sfloat_v, short_v>(); }

template<typename T> T add2(T x) { return x + T(2); }

template<typename T, typename V>
class CallTester
{
    public:
        CallTester() : v(Vc::Zero), i(0) {}

        void operator()(T x) {
            v[i] = x;
            ++i;
        }

        void reset() { v.setZero(); i = 0; }

        int callCount() const { return i; }
        V callValues() const { return v; }

    private:
        V v;
        int i;
};

#if __cplusplus >= 201103 && (!defined(VC_CLANG) || VC_CLANG > 0x30000)
#define DO_LAMBDA_TESTS 1
#endif

template<typename V>
void applyAndCall()
{
    typedef typename V::EntryType T;

    const V two(T(2));
    for (int i = 0; i < 1000; ++i) {
        const V rand = V::Random();
        COMPARE(rand.apply(add2<T>), rand + two);
#ifdef DO_LAMBDA_TESTS
        COMPARE(rand.apply([](T x) { return x + T(2); }), rand + two);
#endif

        CallTester<T, V> callTester;
        rand.call(callTester);
        COMPARE(callTester.callCount(), int(V::Size));
        COMPARE(callTester.callValues(), rand);

        for_all_masks(V, mask) {
            V copy1 = rand;
            V copy2 = rand;
            copy1(mask) += two;

            COMPARE(copy2(mask).apply(add2<T>), copy1) << mask;
            COMPARE(rand.apply(add2<T>, mask), copy1) << mask;
#ifdef DO_LAMBDA_TESTS
            COMPARE(copy2(mask).apply([](T x) { return x + T(2); }), copy1) << mask;
            COMPARE(rand.apply([](T x) { return x + T(2); }, mask), copy1) << mask;
#endif

            callTester.reset();
            copy2(mask).call(callTester);
            COMPARE(callTester.callCount(), mask.count());

            callTester.reset();
            rand.call(callTester, mask);
            COMPARE(callTester.callCount(), mask.count());
        }
    }
}

template<typename T, int value> T returnConstant() { return T(value); }
template<typename T, int value> T returnConstantOffset(int i) { return T(value) + T(i); }
template<typename T, int value> T returnConstantOffset2(unsigned short i) { return T(value) + T(i); }

template<typename V> void fill()
{
    typedef typename V::EntryType T;
    typedef typename V::IndexType I;
    V test = V::Random();
    test.fill(returnConstant<T, 2>);
    COMPARE(test, V(T(2)));

    test = V::Random();
    test.fill(returnConstantOffset<T, 0>);
    COMPARE(test, static_cast<V>(I::IndexesFromZero()));

    test = V::Random();
    test.fill(returnConstantOffset2<T, 0>);
    COMPARE(test, static_cast<V>(I::IndexesFromZero()));
}

template<typename V> void shifted()
{
    typedef typename V::EntryType T;
    for (int shift = -2 * V::Size; shift <= 2 * V::Size; ++shift) {
        const V reference = V::Random();
        const V test = reference.shifted(shift);
        for (int i = 0; i < V::Size; ++i) {
            if (i + shift >= 0 && i + shift < V::Size) {
                COMPARE(test[i], reference[i + shift]) << "shift: " << shift << ", i: " << i << ", test: " << test << ", reference: " << reference;
            } else {
                COMPARE(test[i], T(0)) << "shift: " << shift << ", i: " << i << ", test: " << test << ", reference: " << reference;
            }
        }
    }
}

template<typename V> void rotated()
{
    for (int shift = -2 * V::Size; shift <= 2 * V::Size; ++shift) {
        //std::cout << "amount = " << shift % V::Size << std::endl;
        const V reference = V::Random();
        const V test = reference.rotated(shift);
        for (int i = 0; i < V::Size; ++i) {
            unsigned int refShift = i + shift;
            COMPARE(test[i], reference[refShift % V::Size]) << "shift: " << shift << ", i: " << i << ", test: " << test << ", reference: " << reference;
        }
    }
}

int main()
{
    testAllTypes(testCall);
    testAllTypes(testForeachBit);
    testAllTypes(testSort);
    testRealTypes(copySign);

    testAllTypes(shifted);
    testAllTypes(rotated);
    testAllTypes(Random);

    testAllTypes(applyAndCall);
    testAllTypes(fill);

    return 0;
}
<commit_msg>test malloc alignment<commit_after>/*  This file is part of the Vc library.

    Copyright (C) 2009-2012 Matthias Kretz <kretz@kde.org>

    Vc is 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 3 of
    the License, or (at your option) any later version.

    Vc 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 Vc.  If not, see <http://www.gnu.org/licenses/>.

*/

#include <Vc/Vc>
#include "unittest.h"
#include <iostream>
#include "vectormemoryhelper.h"
#include "../cpuid.h"

using namespace Vc;

template<typename Vec> void testSort()
{
    typedef typename Vec::EntryType EntryType;
    typedef typename Vec::IndexType IndexType;

    typename Vec::Memory _a;
    const IndexType _ref(IndexesFromZero);
    Vec ref(_ref);
    Vec a;
    int maxPerm = 1;
    for (int x = Vec::Size; x > 0; --x) {
        maxPerm *= x;
    }
    for (int perm = 0; perm < maxPerm; ++perm) {
        int rest = perm;
        for (int i = 0; i < Vec::Size; ++i) {
            _a[i] = 0;
            for (int j = 0; j < i; ++j) {
                if (_a[i] == _a[j]) {
                    ++_a[i];
                    j = -1;
                }
            }
            _a[i] += rest % (Vec::Size - i);
            rest /= (Vec::Size - i);
            for (int j = 0; j < i; ++j) {
                if (_a[i] == _a[j]) {
                    ++_a[i];
                    j = -1;
                }
            }
        }
        a.load(_a);
        //std::cout << a << a.sorted() << std::endl;
        COMPARE(ref, a.sorted()) << ", a: " << a;
    }
}

template<typename T, typename Mem> struct Foo
{
    Foo() : i(0) {}
    void reset() { i = 0; }
    void operator()(T v) { d[i++] = v; }
    Mem d;
    int i;
};

template<typename V> void testCall()
{
    typedef typename V::EntryType T;
    typedef typename V::IndexType I;
    typedef typename V::Mask M;
    typedef typename I::Mask MI;
    const I _indexes(IndexesFromZero);
    const MI _odd = (_indexes & I(One)) > 0;
    const M odd(_odd);
    V a(_indexes);
    Foo<T, typename V::Memory> f;
    a.callWithValuesSorted(f);
    V b(f.d);
    COMPARE(b, a);

    f.reset();
    a(odd) -= 1;
    a.callWithValuesSorted(f);
    V c(f.d);
    for (int i = 0; i < V::Size / 2; ++i) {
        COMPARE(a[i * 2], c[i]);
    }
    for (int i = V::Size / 2; i < V::Size; ++i) {
        COMPARE(b[i], c[i]);
    }
}

template<typename V> void testForeachBit()
{
    typedef typename V::EntryType T;
    typedef typename V::IndexType I;
    typedef typename V::Mask M;
    typedef typename I::Mask MI;
    const I indexes(IndexesFromZero);
    for_all_masks(V, mask) {
        V tmp = V::Zero();
        foreach_bit(int j, mask) {
            tmp[j] = T(1);
        }
        COMPARE(tmp == V::One(), mask);

        int count = 0;
        foreach_bit(int j, mask) {
            ++count;
            if (j >= 0) {
                continue;
            }
        }
        COMPARE(count, mask.count());

        count = 0;
        foreach_bit(int j, mask) {
            if (j >= 0) {
                break;
            }
            ++count;
        }
        COMPARE(count, 0);
    }
}

template<typename V> void copySign()
{
    typedef typename V::EntryType T;
    V v(One);
    V positive(One);
    V negative = -positive;
    COMPARE(v, v.copySign(positive));
    COMPARE(-v, v.copySign(negative));
}

#ifdef VC_MSVC
void bzero(void *p, size_t n) { memset(p, 0, n); }
#else
#include <strings.h>
#endif

template<typename V> void Random()
{
    typedef typename V::EntryType T;
    enum {
        NBits = 3,
        NBins = 1 << NBits,                        // short int
        TotalBits = sizeof(T) * 8,                 //    16  32
        RightShift = TotalBits - NBits,            //    13  29
        NHistograms = TotalBits - NBits + 1,       //    14  30
        LeftShift = (RightShift + 1) / NHistograms,//     1   1
        Mean = 135791,
        MinGood = Mean - Mean/10,
        MaxGood = Mean + Mean/10
    };
    const V mask((1 << NBits) - 1);
    int histogram[NHistograms][NBins];
    bzero(&histogram[0][0], sizeof(histogram));
    for (size_t i = 0; i < NBins * Mean / V::Size; ++i) {
        const V rand = V::Random();
        for (size_t hist = 0; hist < NHistograms; ++hist) {
            const V bin = ((rand << (hist * LeftShift)) >> RightShift) & mask;
            for (size_t k = 0; k < V::Size; ++k) {
                ++histogram[hist][bin[k]];
            }
        }
    }
//#define PRINT_RANDOM_HISTOGRAM
#ifdef PRINT_RANDOM_HISTOGRAM
    for (size_t hist = 0; hist < NHistograms; ++hist) {
        std::cout << "histogram[" << std::setw(2) << hist << "]: ";
        for (size_t bin = 0; bin < NBins; ++bin) {
            std::cout << std::setw(3) << (histogram[hist][bin] - Mean) * 1000 / Mean << "|";
        }
        std::cout << std::endl;
    }
#endif
    for (size_t hist = 0; hist < NHistograms; ++hist) {
        for (size_t bin = 0; bin < NBins; ++bin) {
            VERIFY(histogram[hist][bin] > MinGood)
                << " bin = " << bin << " is " << histogram[0][bin];
            VERIFY(histogram[hist][bin] < MaxGood)
                << " bin = " << bin << " is " << histogram[0][bin];
        }
    }
}

template<typename V, typename I> void FloatRandom()
{
    typedef typename V::EntryType T;
    enum {
        NBins = 64,
        NHistograms = 1,
        Mean = 135791,
        MinGood = Mean - Mean/10,
        MaxGood = Mean + Mean/10
    };
    int histogram[NHistograms][NBins];
    bzero(&histogram[0][0], sizeof(histogram));
    for (size_t i = 0; i < NBins * Mean / V::Size; ++i) {
        const V rand = V::Random();
        const I bin = static_cast<I>(rand * T(NBins));
        for (size_t k = 0; k < V::Size; ++k) {
            ++histogram[0][bin[k]];
        }
    }
#ifdef PRINT_RANDOM_HISTOGRAM
    for (size_t hist = 0; hist < NHistograms; ++hist) {
        std::cout << "histogram[" << std::setw(2) << hist << "]: ";
        for (size_t bin = 0; bin < NBins; ++bin) {
            std::cout << std::setw(3) << (histogram[hist][bin] - Mean) * 1000 / Mean << "|";
        }
        std::cout << std::endl;
    }
#endif
    for (size_t hist = 0; hist < NHistograms; ++hist) {
        for (size_t bin = 0; bin < NBins; ++bin) {
            VERIFY(histogram[hist][bin] > MinGood)
                << " bin = " << bin << " is " << histogram[0][bin];
            VERIFY(histogram[hist][bin] < MaxGood)
                << " bin = " << bin << " is " << histogram[0][bin];
        }
    }
}

template<> void Random<float_v>() { FloatRandom<float_v, int_v>(); }
template<> void Random<double_v>() { FloatRandom<double_v, int_v>(); }
template<> void Random<sfloat_v>() { FloatRandom<sfloat_v, short_v>(); }

template<typename T> T add2(T x) { return x + T(2); }

template<typename T, typename V>
class CallTester
{
    public:
        CallTester() : v(Vc::Zero), i(0) {}

        void operator()(T x) {
            v[i] = x;
            ++i;
        }

        void reset() { v.setZero(); i = 0; }

        int callCount() const { return i; }
        V callValues() const { return v; }

    private:
        V v;
        int i;
};

#if __cplusplus >= 201103 && (!defined(VC_CLANG) || VC_CLANG > 0x30000)
#define DO_LAMBDA_TESTS 1
#endif

template<typename V>
void applyAndCall()
{
    typedef typename V::EntryType T;

    const V two(T(2));
    for (int i = 0; i < 1000; ++i) {
        const V rand = V::Random();
        COMPARE(rand.apply(add2<T>), rand + two);
#ifdef DO_LAMBDA_TESTS
        COMPARE(rand.apply([](T x) { return x + T(2); }), rand + two);
#endif

        CallTester<T, V> callTester;
        rand.call(callTester);
        COMPARE(callTester.callCount(), int(V::Size));
        COMPARE(callTester.callValues(), rand);

        for_all_masks(V, mask) {
            V copy1 = rand;
            V copy2 = rand;
            copy1(mask) += two;

            COMPARE(copy2(mask).apply(add2<T>), copy1) << mask;
            COMPARE(rand.apply(add2<T>, mask), copy1) << mask;
#ifdef DO_LAMBDA_TESTS
            COMPARE(copy2(mask).apply([](T x) { return x + T(2); }), copy1) << mask;
            COMPARE(rand.apply([](T x) { return x + T(2); }, mask), copy1) << mask;
#endif

            callTester.reset();
            copy2(mask).call(callTester);
            COMPARE(callTester.callCount(), mask.count());

            callTester.reset();
            rand.call(callTester, mask);
            COMPARE(callTester.callCount(), mask.count());
        }
    }
}

template<typename T, int value> T returnConstant() { return T(value); }
template<typename T, int value> T returnConstantOffset(int i) { return T(value) + T(i); }
template<typename T, int value> T returnConstantOffset2(unsigned short i) { return T(value) + T(i); }

template<typename V> void fill()
{
    typedef typename V::EntryType T;
    typedef typename V::IndexType I;
    V test = V::Random();
    test.fill(returnConstant<T, 2>);
    COMPARE(test, V(T(2)));

    test = V::Random();
    test.fill(returnConstantOffset<T, 0>);
    COMPARE(test, static_cast<V>(I::IndexesFromZero()));

    test = V::Random();
    test.fill(returnConstantOffset2<T, 0>);
    COMPARE(test, static_cast<V>(I::IndexesFromZero()));
}

template<typename V> void shifted()
{
    typedef typename V::EntryType T;
    for (int shift = -2 * V::Size; shift <= 2 * V::Size; ++shift) {
        const V reference = V::Random();
        const V test = reference.shifted(shift);
        for (int i = 0; i < V::Size; ++i) {
            if (i + shift >= 0 && i + shift < V::Size) {
                COMPARE(test[i], reference[i + shift]) << "shift: " << shift << ", i: " << i << ", test: " << test << ", reference: " << reference;
            } else {
                COMPARE(test[i], T(0)) << "shift: " << shift << ", i: " << i << ", test: " << test << ", reference: " << reference;
            }
        }
    }
}

template<typename V> void rotated()
{
    for (int shift = -2 * V::Size; shift <= 2 * V::Size; ++shift) {
        //std::cout << "amount = " << shift % V::Size << std::endl;
        const V reference = V::Random();
        const V test = reference.rotated(shift);
        for (int i = 0; i < V::Size; ++i) {
            unsigned int refShift = i + shift;
            COMPARE(test[i], reference[refShift % V::Size]) << "shift: " << shift << ", i: " << i << ", test: " << test << ", reference: " << reference;
        }
    }
}

void testMallocAlignment()
{
    int_v *a = Vc::malloc<int_v, Vc::AlignOnVector>(10);

    unsigned long mask = VectorAlignment - 1;
    for (int i = 0; i < 10; ++i) {
        VERIFY((reinterpret_cast<unsigned long>(&a[i]) & mask) == 0);
    }
    const char *data = reinterpret_cast<const char *>(&a[0]);
    for (int i = 0; i < 10; ++i) {
        VERIFY(&data[i * int_v::Size * sizeof(int_v::EntryType)] == reinterpret_cast<const char *>(&a[i]));
    }

    a = Vc::malloc<int_v, Vc::AlignOnCacheline>(10);
    mask = CpuId::cacheLineSize() - 1;
    COMPARE((reinterpret_cast<unsigned long>(&a[0]) & mask), 0ul);

    // I don't know how to properly check page alignment. So we check for 4 KiB alignment as this is
    // the minimum page size on x86
    a = Vc::malloc<int_v, Vc::AlignOnPage>(10);
    mask = 4096 - 1;
    COMPARE((reinterpret_cast<unsigned long>(&a[0]) & mask), 0ul);
}

int main()
{
    testAllTypes(testCall);
    testAllTypes(testForeachBit);
    testAllTypes(testSort);
    testRealTypes(copySign);

    testAllTypes(shifted);
    testAllTypes(rotated);
    testAllTypes(Random);

    testAllTypes(applyAndCall);
    testAllTypes(fill);

    runTest(testMallocAlignment);

    return 0;
}
<|endoftext|>


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

#include "attributedisklayout.h"
#include "flushableattribute.h"
#include <vespa/searchlib/attribute/attributefilesavetarget.h>
#include <vespa/searchlib/attribute/attributesaver.h>
#include <vespa/searchlib/util/dirtraverse.h>
#include <vespa/searchlib/util/filekit.h>
#include <vespa/vespalib/io/fileutil.h>
#include <vespa/vespalib/util/closuretask.h>
#include <fstream>
#include <vespa/searchlib/common/serialnumfileheadercontext.h>
#include <vespa/searchlib/common/isequencedtaskexecutor.h>
#include <vespa/searchlib/attribute/attributememorysavetarget.h>
#include <vespa/searchlib/attribute/attributevector.h>
#include <future>
#include "attribute_directory.h"
#include <vespa/vespalib/util/stringfmt.h>

#include <vespa/log/log.h>
LOG_SETUP(".proton.attribute.flushableattribute");

using namespace search;
using namespace vespalib;
using search::common::FileHeaderContext;
using search::common::SerialNumFileHeaderContext;
using vespalib::makeTask;
using vespalib::makeClosure;
using searchcorespi::IFlushTarget;

namespace proton {

/**
 * Task performing the actual flushing to disk.
 **/
class FlushableAttribute::Flusher : public Task {
private:
    FlushableAttribute              & _fattr;
    search::AttributeMemorySaveTarget      _saveTarget;
    std::unique_ptr<search::AttributeSaver> _saver;
    uint64_t                          _syncToken;
    search::AttributeVector::BaseName _flushFile;

    bool saveAttribute(); // not updating snap info.
public:
    Flusher(FlushableAttribute & fattr, uint64_t syncToken, AttributeDirectory::Writer &writer);
    ~Flusher();
    uint64_t getSyncToken() const { return _syncToken; }
    bool flush(AttributeDirectory::Writer &writer);
    void updateStats();
    bool cleanUp(AttributeDirectory::Writer &writer);
    // Implements vespalib::Executor::Task
    virtual void run() override;

    virtual SerialNum
    getFlushSerial() const override
    {
        return _syncToken;
    }
};

FlushableAttribute::Flusher::Flusher(FlushableAttribute & fattr, SerialNum syncToken, AttributeDirectory::Writer &writer)
    : _fattr(fattr),
      _saveTarget(),
      _saver(),
      _syncToken(syncToken),
      _flushFile("")
{
    fattr._attr->commit(syncToken, syncToken);
    AttributeVector &attr = *_fattr._attr;
    // Called by attribute field writer executor
    _flushFile = writer.getSnapshotDir(_syncToken) + "/" + attr.getName();
    _saver = attr.initSave(_flushFile);
    if (!_saver) {
        // New style background save not available, use old style save.
        attr.save(_saveTarget, _flushFile);
    }
}

FlushableAttribute::Flusher::~Flusher()
{
    // empty
}

bool
FlushableAttribute::Flusher::saveAttribute()
{
    vespalib::mkdir(_flushFile.getDirName(), false);
    SerialNumFileHeaderContext fileHeaderContext(_fattr._fileHeaderContext,
                                                 _syncToken);
    bool saveSuccess = true;
    if (_saver && _saver->hasGenerationGuard() &&
        _fattr._hwInfo.disk().slow()) {
        saveSuccess = _saver->save(_saveTarget);
        _saver.reset();
    }
    if (saveSuccess) {
        if (_saver) {
            search::AttributeFileSaveTarget saveTarget(_fattr._tuneFileAttributes,
                                                       fileHeaderContext);
            saveSuccess = _saver->save(saveTarget);
            _saver.reset();
        } else {
            saveSuccess = _saveTarget.writeToFile(_fattr._tuneFileAttributes,
                                                  fileHeaderContext);
        }
    }
    return saveSuccess;
}

bool
FlushableAttribute::Flusher::flush(AttributeDirectory::Writer &writer)
{
    writer.createInvalidSnapshot(_syncToken);
    if (!saveAttribute()) {
        LOG(warning, "Could not write attribute vector '%s' to disk",
            _flushFile.c_str());
        return false;
    }
    writer.markValidSnapshot(_syncToken);
    writer.setLastFlushTime(search::FileKit::getModificationTime(_flushFile.getDirName()));
    return true;
}

void
FlushableAttribute::Flusher::updateStats()
{
    _fattr._lastStats.setPath(_flushFile.getDirName());
}

bool
FlushableAttribute::Flusher::cleanUp(AttributeDirectory::Writer &writer)
{
    if (_fattr._cleanUpAfterFlush) {
        writer.invalidateOldSnapshots();
        writer.removeInvalidSnapshots();
    }
    return true;
}

void
FlushableAttribute::Flusher::run()
{
    auto writer = _fattr._attrDir->tryGetWriter();
    if (!writer || _syncToken <= _fattr.getFlushedSerialNum()) {
        // another flusher has created an equal or better snapshot
        // after this flusher was created
        return;
    }
    if (!flush(*writer)) {
        // TODO (geirst): throw exception ?
    }
    updateStats();
    if (!cleanUp(*writer)) {
        // TODO (geirst): throw exception ?
    }
}

FlushableAttribute::FlushableAttribute(const AttributeVectorSP attr,
                                       const std::shared_ptr<AttributeDirectory> &attrDir,
                                       const TuneFileAttributes &
                                       tuneFileAttributes,
                                       const FileHeaderContext &
                                       fileHeaderContext,
                                       search::ISequencedTaskExecutor &
                                       attributeFieldWriter,
                                       const HwInfo &hwInfo)
    : IFlushTarget(vespalib::make_string(
                           "attribute.flush.%s",
                           attr->getName().c_str()),
            Type::SYNC, Component::ATTRIBUTE),
      _attr(attr),
      _cleanUpAfterFlush(true),
      _lastStats(),
      _tuneFileAttributes(tuneFileAttributes),
      _fileHeaderContext(fileHeaderContext),
      _attributeFieldWriter(attributeFieldWriter),
      _hwInfo(hwInfo),
      _attrDir(attrDir)
{
    _lastStats.setPathElementsToLog(8);
}

FlushableAttribute::~FlushableAttribute()
{
}

IFlushTarget::SerialNum
FlushableAttribute::getFlushedSerialNum() const
{
    return _attrDir->getFlushedSerialNum();
}

IFlushTarget::MemoryGain
FlushableAttribute::getApproxMemoryGain() const
{
    int64_t used(_attr->getStatus().getUsed());
    return MemoryGain(used, used);
}

IFlushTarget::DiskGain
FlushableAttribute::getApproxDiskGain() const
{
    return DiskGain(0, 0);
}

IFlushTarget::Time
FlushableAttribute::getLastFlushTime() const
{
    return _attrDir->getLastFlushTime();
}

IFlushTarget::Task::UP
FlushableAttribute::internalInitFlush(SerialNum currentSerial)
{
    // Called by attribute field writer thread while document db executor waits
    _attr->removeAllOldGenerations();
    SerialNum syncToken = std::max(currentSerial,
                                   _attr->getStatus().getLastSyncToken());
    auto writer = _attrDir->tryGetWriter();
    if (!writer) {
        return Task::UP();
    }
    if (syncToken <= getFlushedSerialNum()) {
        writer->setLastFlushTime(fastos::ClockSystem::now());
        LOG(debug,
            "No attribute vector to flush."
            " Update flush time to current: lastFlushTime(%f)",
            getLastFlushTime().sec());
        return Task::UP();
    }
    return Task::UP(new Flusher(*this, syncToken, *writer));
}

IFlushTarget::Task::UP
FlushableAttribute::initFlush(SerialNum currentSerial)
{
    // Called by document db executor
    std::promise<IFlushTarget::Task::UP> promise;
    std::future<IFlushTarget::Task::UP> future = promise.get_future();
    _attributeFieldWriter.execute(_attributeFieldWriter.getExecutorId(_attr->getNamePrefix()),
                                  [&]() { promise.set_value(internalInitFlush(currentSerial)); });
    return future.get();
}

uint64_t
FlushableAttribute::getApproxBytesToWriteToDisk() const
{
    return _attr->getEstimatedSaveByteSize();
}

} // namespace proton
<commit_msg>Reduce dependency on AttributeVector::BaseName.<commit_after>// Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.

#include "attributedisklayout.h"
#include "flushableattribute.h"
#include <vespa/searchlib/attribute/attributefilesavetarget.h>
#include <vespa/searchlib/attribute/attributesaver.h>
#include <vespa/searchlib/util/dirtraverse.h>
#include <vespa/searchlib/util/filekit.h>
#include <vespa/vespalib/io/fileutil.h>
#include <vespa/vespalib/util/closuretask.h>
#include <fstream>
#include <vespa/searchlib/common/serialnumfileheadercontext.h>
#include <vespa/searchlib/common/isequencedtaskexecutor.h>
#include <vespa/searchlib/attribute/attributememorysavetarget.h>
#include <vespa/searchlib/attribute/attributevector.h>
#include <future>
#include "attribute_directory.h"
#include <vespa/vespalib/util/stringfmt.h>

#include <vespa/log/log.h>
LOG_SETUP(".proton.attribute.flushableattribute");

using namespace search;
using namespace vespalib;
using search::common::FileHeaderContext;
using search::common::SerialNumFileHeaderContext;
using vespalib::makeTask;
using vespalib::makeClosure;
using searchcorespi::IFlushTarget;

namespace proton {

/**
 * Task performing the actual flushing to disk.
 **/
class FlushableAttribute::Flusher : public Task {
private:
    FlushableAttribute              & _fattr;
    search::AttributeMemorySaveTarget      _saveTarget;
    std::unique_ptr<search::AttributeSaver> _saver;
    uint64_t                          _syncToken;
    vespalib::string                  _flushFile;

    bool saveAttribute(); // not updating snap info.
public:
    Flusher(FlushableAttribute & fattr, uint64_t syncToken, AttributeDirectory::Writer &writer);
    ~Flusher();
    uint64_t getSyncToken() const { return _syncToken; }
    bool flush(AttributeDirectory::Writer &writer);
    void updateStats();
    bool cleanUp(AttributeDirectory::Writer &writer);
    // Implements vespalib::Executor::Task
    virtual void run() override;

    virtual SerialNum
    getFlushSerial() const override
    {
        return _syncToken;
    }
};

FlushableAttribute::Flusher::Flusher(FlushableAttribute & fattr, SerialNum syncToken, AttributeDirectory::Writer &writer)
    : _fattr(fattr),
      _saveTarget(),
      _saver(),
      _syncToken(syncToken),
      _flushFile("")
{
    fattr._attr->commit(syncToken, syncToken);
    AttributeVector &attr = *_fattr._attr;
    // Called by attribute field writer executor
    _flushFile = writer.getSnapshotDir(_syncToken) + "/" + attr.getName();
    _saver = attr.initSave(_flushFile);
    if (!_saver) {
        // New style background save not available, use old style save.
        attr.save(_saveTarget, _flushFile);
    }
}

FlushableAttribute::Flusher::~Flusher()
{
    // empty
}

bool
FlushableAttribute::Flusher::saveAttribute()
{
    vespalib::mkdir(vespalib::dirname(_flushFile), false);
    SerialNumFileHeaderContext fileHeaderContext(_fattr._fileHeaderContext,
                                                 _syncToken);
    bool saveSuccess = true;
    if (_saver && _saver->hasGenerationGuard() &&
        _fattr._hwInfo.disk().slow()) {
        saveSuccess = _saver->save(_saveTarget);
        _saver.reset();
    }
    if (saveSuccess) {
        if (_saver) {
            search::AttributeFileSaveTarget saveTarget(_fattr._tuneFileAttributes,
                                                       fileHeaderContext);
            saveSuccess = _saver->save(saveTarget);
            _saver.reset();
        } else {
            saveSuccess = _saveTarget.writeToFile(_fattr._tuneFileAttributes,
                                                  fileHeaderContext);
        }
    }
    return saveSuccess;
}

bool
FlushableAttribute::Flusher::flush(AttributeDirectory::Writer &writer)
{
    writer.createInvalidSnapshot(_syncToken);
    if (!saveAttribute()) {
        LOG(warning, "Could not write attribute vector '%s' to disk",
            _flushFile.c_str());
        return false;
    }
    writer.markValidSnapshot(_syncToken);
    writer.setLastFlushTime(search::FileKit::getModificationTime(vespalib::dirname(_flushFile)));
    return true;
}

void
FlushableAttribute::Flusher::updateStats()
{
    _fattr._lastStats.setPath(vespalib::dirname(_flushFile));
}

bool
FlushableAttribute::Flusher::cleanUp(AttributeDirectory::Writer &writer)
{
    if (_fattr._cleanUpAfterFlush) {
        writer.invalidateOldSnapshots();
        writer.removeInvalidSnapshots();
    }
    return true;
}

void
FlushableAttribute::Flusher::run()
{
    auto writer = _fattr._attrDir->tryGetWriter();
    if (!writer || _syncToken <= _fattr.getFlushedSerialNum()) {
        // another flusher has created an equal or better snapshot
        // after this flusher was created
        return;
    }
    if (!flush(*writer)) {
        // TODO (geirst): throw exception ?
    }
    updateStats();
    if (!cleanUp(*writer)) {
        // TODO (geirst): throw exception ?
    }
}

FlushableAttribute::FlushableAttribute(const AttributeVectorSP attr,
                                       const std::shared_ptr<AttributeDirectory> &attrDir,
                                       const TuneFileAttributes &
                                       tuneFileAttributes,
                                       const FileHeaderContext &
                                       fileHeaderContext,
                                       search::ISequencedTaskExecutor &
                                       attributeFieldWriter,
                                       const HwInfo &hwInfo)
    : IFlushTarget(vespalib::make_string(
                           "attribute.flush.%s",
                           attr->getName().c_str()),
            Type::SYNC, Component::ATTRIBUTE),
      _attr(attr),
      _cleanUpAfterFlush(true),
      _lastStats(),
      _tuneFileAttributes(tuneFileAttributes),
      _fileHeaderContext(fileHeaderContext),
      _attributeFieldWriter(attributeFieldWriter),
      _hwInfo(hwInfo),
      _attrDir(attrDir)
{
    _lastStats.setPathElementsToLog(8);
}

FlushableAttribute::~FlushableAttribute()
{
}

IFlushTarget::SerialNum
FlushableAttribute::getFlushedSerialNum() const
{
    return _attrDir->getFlushedSerialNum();
}

IFlushTarget::MemoryGain
FlushableAttribute::getApproxMemoryGain() const
{
    int64_t used(_attr->getStatus().getUsed());
    return MemoryGain(used, used);
}

IFlushTarget::DiskGain
FlushableAttribute::getApproxDiskGain() const
{
    return DiskGain(0, 0);
}

IFlushTarget::Time
FlushableAttribute::getLastFlushTime() const
{
    return _attrDir->getLastFlushTime();
}

IFlushTarget::Task::UP
FlushableAttribute::internalInitFlush(SerialNum currentSerial)
{
    // Called by attribute field writer thread while document db executor waits
    _attr->removeAllOldGenerations();
    SerialNum syncToken = std::max(currentSerial,
                                   _attr->getStatus().getLastSyncToken());
    auto writer = _attrDir->tryGetWriter();
    if (!writer) {
        return Task::UP();
    }
    if (syncToken <= getFlushedSerialNum()) {
        writer->setLastFlushTime(fastos::ClockSystem::now());
        LOG(debug,
            "No attribute vector to flush."
            " Update flush time to current: lastFlushTime(%f)",
            getLastFlushTime().sec());
        return Task::UP();
    }
    return Task::UP(new Flusher(*this, syncToken, *writer));
}

IFlushTarget::Task::UP
FlushableAttribute::initFlush(SerialNum currentSerial)
{
    // Called by document db executor
    std::promise<IFlushTarget::Task::UP> promise;
    std::future<IFlushTarget::Task::UP> future = promise.get_future();
    _attributeFieldWriter.execute(_attributeFieldWriter.getExecutorId(_attr->getNamePrefix()),
                                  [&]() { promise.set_value(internalInitFlush(currentSerial)); });
    return future.get();
}

uint64_t
FlushableAttribute::getApproxBytesToWriteToDisk() const
{
    return _attr->getEstimatedSaveByteSize();
}

} // namespace proton
<|endoftext|>


<commit_before>
/*
  Copyright(c) 2015 - 2018 Denis Blank <denis.blank at outlook dot 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 <test-continuable.hpp>

#include <functional>
#include <numeric>
#include <vector>

using namespace cti;
using namespace cti::detail;

TEST(regression_tests, are_multiple_args_mergeable) {
  {
    auto tp = std::make_tuple(1, 2, 3);
    std::tuple_cat(tp, tp, tp, tp, tp);
  }

  auto tp2 = std::tuple_cat(std::make_tuple(), std::make_tuple(1),
                            std::make_tuple(1, 2), std::make_tuple(1, 2, 3),
                            std::make_tuple(1, 2, 3, 4));

  auto count = traits::unpack(
      [](auto... args) {
        std::vector<int> v{args...};
        return std::accumulate(v.begin(), v.end(), 0);
      },
      tp2);
  EXPECT_EQ(count, 20);
}
<commit_msg>Remove a useless unit test<commit_after>
/*
  Copyright(c) 2015 - 2018 Denis Blank <denis.blank at outlook dot 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 <test-continuable.hpp>

#include <functional>
#include <numeric>
#include <vector>

using namespace cti;
using namespace cti::detail;

TEST(regression_tests, are_multiple_args_mergeable) {
  auto tp2 = std::tuple_cat(std::make_tuple(), std::make_tuple(1),
                            std::make_tuple(1, 2), std::make_tuple(1, 2, 3),
                            std::make_tuple(1, 2, 3, 4));

  auto count = traits::unpack(
      [](auto... args) {
        std::vector<int> v{args...};
        return std::accumulate(v.begin(), v.end(), 0);
      },
      tp2);
  EXPECT_EQ(count, 20);
}
<|endoftext|>


<commit_before>#include <qtest.h>
#include <QtDeclarative/qmlengine.h>
#include <QtDeclarative/qmlcomponent.h>
#include <QtDeclarative/qfxview.h>
#include <QtDeclarative/qfxrect.h>

class tst_animations : public QObject
{
    Q_OBJECT
public:
    tst_animations() {}

private slots:
    void badTypes();
    void badProperties();
    //void mixedTypes();
};

void tst_animations::badTypes()
{
    //don't crash
    {
        QFxView *view = new QFxView;
        view->setUrl(QUrl("file://" SRCDIR "/data/badtype1.qml"));

        view->execute();
        qApp->processEvents();

        delete view;
    }

    //make sure we get a compiler error
    {
        QmlEngine engine;
        QmlComponent c(&engine, QUrl("file://" SRCDIR "/data/badtype2.qml"));
        QTest::ignoreMessage(QtWarningMsg, "QmlComponent: Component is not ready");
        c.create();

        QVERIFY(c.errors().count() == 1);
        QCOMPARE(c.errors().at(0).description(), QLatin1String("Cannot convert value \"blue\" to double number"));
    }

    //make sure we get a compiler error
    {
        QmlEngine engine;
        QmlComponent c(&engine, QUrl("file://" SRCDIR "/data/badtype3.qml"));
        QTest::ignoreMessage(QtWarningMsg, "QmlComponent: Component is not ready");
        c.create();

        QVERIFY(c.errors().count() == 1);
        QCOMPARE(c.errors().at(0).description(), QLatin1String("Cannot convert value \"10\" to color"));
    }
}

void tst_animations::badProperties()
{
    //don't crash (should be runtime error)
    {
        QFxView *view = new QFxView;
        view->setUrl(QUrl("file://" SRCDIR "/data/badproperty1.qml"));

        view->execute();
        qApp->processEvents();

        delete view;
    }
}

/*//test animating mixed types with property animation
  //for example, int + real; color + real; etc
void tst_animations::mixedTypes()
{
    //### this one isn't real robust because width will likely change to real as well
    {
        QFxView *view = new QFxView;
        view->setUrl(QUrl("file://" SRCDIR "/data/mixedtype1.qml"));

        view->execute();
        qApp->processEvents();

        delete view;
    }

    {
        QFxView *view = new QFxView;
        view->setUrl(QUrl("file://" SRCDIR "/data/mixedtype2.qml"));

        view->execute();
        qApp->processEvents();

        delete view;
    }
}*/

QTEST_MAIN(tst_animations)

#include "tst_animations.moc"
<commit_msg>update error messages<commit_after>#include <qtest.h>
#include <QtDeclarative/qmlengine.h>
#include <QtDeclarative/qmlcomponent.h>
#include <QtDeclarative/qfxview.h>
#include <QtDeclarative/qfxrect.h>

class tst_animations : public QObject
{
    Q_OBJECT
public:
    tst_animations() {}

private slots:
    void badTypes();
    void badProperties();
    //void mixedTypes();
};

void tst_animations::badTypes()
{
    //don't crash
    {
        QFxView *view = new QFxView;
        view->setUrl(QUrl("file://" SRCDIR "/data/badtype1.qml"));

        view->execute();
        qApp->processEvents();

        delete view;
    }

    //make sure we get a compiler error
    {
        QmlEngine engine;
        QmlComponent c(&engine, QUrl("file://" SRCDIR "/data/badtype2.qml"));
        QTest::ignoreMessage(QtWarningMsg, "QmlComponent: Component is not ready");
        c.create();

        QVERIFY(c.errors().count() == 1);
        QCOMPARE(c.errors().at(0).description(), QLatin1String("Invalid property assignment: double expected"));
    }

    //make sure we get a compiler error
    {
        QmlEngine engine;
        QmlComponent c(&engine, QUrl("file://" SRCDIR "/data/badtype3.qml"));
        QTest::ignoreMessage(QtWarningMsg, "QmlComponent: Component is not ready");
        c.create();

        QVERIFY(c.errors().count() == 1);
        QCOMPARE(c.errors().at(0).description(), QLatin1String("Invalid property assignment: color expected"));
    }
}

void tst_animations::badProperties()
{
    //don't crash (should be runtime error)
    {
        QFxView *view = new QFxView;
        view->setUrl(QUrl("file://" SRCDIR "/data/badproperty1.qml"));

        view->execute();
        qApp->processEvents();

        delete view;
    }
}

/*//test animating mixed types with property animation
  //for example, int + real; color + real; etc
void tst_animations::mixedTypes()
{
    //### this one isn't real robust because width will likely change to real as well
    {
        QFxView *view = new QFxView;
        view->setUrl(QUrl("file://" SRCDIR "/data/mixedtype1.qml"));

        view->execute();
        qApp->processEvents();

        delete view;
    }

    {
        QFxView *view = new QFxView;
        view->setUrl(QUrl("file://" SRCDIR "/data/mixedtype2.qml"));

        view->execute();
        qApp->processEvents();

        delete view;
    }
}*/

QTEST_MAIN(tst_animations)

#include "tst_animations.moc"
<|endoftext|>


<commit_before>/* This file is part of the KDE libraries
   Copyright (C) 2002, 2003, 2004 Anders Lund <anders.lund@lund.tdcadsl.dk>
   Copyright (C) 2002 John Firebaugh <jfirebaugh@kde.org>

   This library is free software; you can redistribute it and/or
   modify it under the terms of the GNU Library General Public
   License version 2 as published by the Free Software Foundation.

   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; see the file COPYING.LIB.  If not, write to
   the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
   Boston, MA 02111-1307, USA.
*/

#include "katebookmarks.h"
#include "katebookmarks.moc"

#include "katedocument.h"
#include "kateview.h"

#include <klocale.h>
#include <kaction.h>
#include <kpopupmenu.h>
#include <kstringhandler.h>
#include <kxmlguiclient.h>
#include <kxmlguifactory.h>

#include <qregexp.h>
#include <qmemarray.h>
#include <qevent.h>

/**
   Utility: selection sort
   sort a QMemArray<uint> in ascending order.
   max it the largest (zerobased) index to sort.
   To sort the entire array: ssort( *array, array.size() -1 );
   This is only efficient if ran only once.
*/
static void ssort( QMemArray<uint> &a, int max )
{
  uint tmp, j, maxpos;
  for ( uint h = max; h >= 1; h-- )
  {
    maxpos = 0;
    for ( j = 0; j <= h; j++ )
      maxpos = a[j] > a[maxpos] ? j : maxpos;
    tmp = a[maxpos];
    a[maxpos] = a[h];
    a[h] = tmp;
  }
}

// TODO add a insort() or bubble_sort - more efficient for aboutToShow() ?

KateBookmarks::KateBookmarks( KateView* view, Sorting sort )
  : QObject( view, "kate bookmarks" )
  , m_view( view )
  , m_sorting( sort )
{
  connect (view->getDoc(), SIGNAL(marksChanged()), this, SLOT(marksChanged()));
  _tries=0;
  m_bookmarksMenu = 0L;
}

KateBookmarks::~KateBookmarks()
{
}

void KateBookmarks::createActions( KActionCollection* ac )
{
  m_bookmarkToggle = new KToggleAction(
    i18n("Set &Bookmark"), "bookmark", CTRL+Key_B,
    this, SLOT(toggleBookmark()),
    ac, "bookmarks_toggle" );
  m_bookmarkToggle->setWhatsThis(i18n("If a line has no bookmark then add one, otherwise remove it."));
  m_bookmarkToggle->setCheckedState( i18n("Clear &Bookmark") );

  m_bookmarkClear = new KAction(
    i18n("Clear &All Bookmarks"), 0,
    this, SLOT(clearBookmarks()),
    ac, "bookmarks_clear");
  m_bookmarkClear->setWhatsThis(i18n("Remove all bookmarks of the current document."));

  m_goNext = new KAction(
    i18n("Next Bookmark"), "next", ALT + Key_PageDown,
    this, SLOT(goNext()),
    ac, "bookmarks_next");
  m_goNext->setWhatsThis(i18n("Go to the next bookmark."));

  m_goPrevious = new KAction(
    i18n("Previous Bookmark"), "previous", ALT + Key_PageUp,
    this, SLOT(goPrevious()),
    ac, "bookmarks_previous");
  m_goPrevious->setWhatsThis(i18n("Go to the previous bookmark."));

  m_bookmarksMenu = (new KActionMenu(i18n("&Bookmarks"), ac, "bookmarks"))->popupMenu();

  //connect the aboutToShow() and aboutToHide() signals with
  //the bookmarkMenuAboutToShow() and bookmarkMenuAboutToHide() slots
  connect( m_bookmarksMenu, SIGNAL(aboutToShow()), this, SLOT(bookmarkMenuAboutToShow()));
  connect( m_bookmarksMenu, SIGNAL(aboutToHide()), this, SLOT(bookmarkMenuAboutToHide()) );

  marksChanged ();
  bookmarkMenuAboutToHide();

  connect( m_view, SIGNAL( gotFocus( Kate::View * ) ), this, SLOT( slotViewGotFocus( Kate::View * ) ) );
  connect( m_view, SIGNAL( lostFocus( Kate::View * ) ), this, SLOT( slotViewLostFocus( Kate::View * ) ) );
}

void KateBookmarks::toggleBookmark ()
{
  uint mark = m_view->getDoc()->mark( m_view->cursorLine() );
  if( mark & KTextEditor::MarkInterface::markType01 )
    m_view->getDoc()->removeMark( m_view->cursorLine(),
        KTextEditor::MarkInterface::markType01 );
  else
    m_view->getDoc()->addMark( m_view->cursorLine(),
        KTextEditor::MarkInterface::markType01 );
}

void KateBookmarks::clearBookmarks ()
{

  QPtrList<KTextEditor::Mark> m = m_view->getDoc()->marks();
  for (uint i=0; i < m.count(); i++)
    m_view->getDoc()->removeMark( m.at(i)->line, KTextEditor::MarkInterface::markType01 );

  // just to be sure ;)
  marksChanged ();
}

void KateBookmarks::slotViewGotFocus( Kate::View *v )
{
  if ( v == (Kate::View*)m_view )
    bookmarkMenuAboutToHide();
}

void KateBookmarks::slotViewLostFocus( Kate::View *v )
{
  if ( v == (Kate::View*)m_view )
    m_bookmarksMenu->clear();
}

void KateBookmarks::insertBookmarks( QPopupMenu& menu )
{
  uint line = m_view->cursorLine();
  const QRegExp re("&(?!&)");
  int idx( -1 );
  int old_menu_count = menu.count();
  KTextEditor::Mark *next = 0;
  KTextEditor::Mark *prev = 0;

  QPtrList<KTextEditor::Mark> m = m_view->getDoc()->marks();
  QMemArray<uint> sortArray( m.count() );
  QPtrListIterator<KTextEditor::Mark> it( m );

  if ( it.count() > 0 )
    menu.insertSeparator();

  for( int i = 0; *it; ++it, ++i )
  {
    if( (*it)->type & KTextEditor::MarkInterface::markType01 )
    {
      QString bText = KStringHandler::rEmSqueeze
                      ( m_view->getDoc()->textLine( (*it)->line ),
                        menu.fontMetrics(), 32 );
      bText.replace(re, "&&"); // kill undesired accellerators!

      if ( m_sorting == Position )
      {
        sortArray[i] = (*it)->line;
        ssort( sortArray, i );
        idx = sortArray.find( (*it)->line ) + 3;
      }

      menu.insertItem(
          QString("%1 - \"%2\"").arg( (*it)->line+1 ).arg( bText ),
          m_view, SLOT(gotoLineNumber(int)), 0, (*it)->line, idx );

      if ( (*it)->line < line )
      {
        if ( ! prev || prev->line < (*it)->line )
          prev = (*it);
      }

      else if ( (*it)->line > line )
      {
        if ( ! next || next->line > (*it)->line )
          next = (*it);
      }
    }
  }

  idx = ++old_menu_count;
  if ( next )
  {
    m_goNext->setText( i18n("&Next: %1 - \"%2\"").arg( next->line + 1 )
        .arg( KStringHandler::rsqueeze( m_view->getDoc()->textLine( next->line ), 24 ) ) );
    m_goNext->plug( &menu, idx );
    idx++;
  }
  if ( prev )
  {
    m_goPrevious->setText( i18n("&Previous: %1 - \"%2\"").arg(prev->line + 1 )
        .arg( KStringHandler::rsqueeze( m_view->getDoc()->textLine( prev->line ), 24 ) ) );
    m_goPrevious->plug( &menu, idx );
    idx++;
  }
  if ( next || prev )
    menu.insertSeparator( idx );

}

void KateBookmarks::bookmarkMenuAboutToShow()
{

  QPtrList<KTextEditor::Mark> m = m_view->getDoc()->marks();

  m_bookmarksMenu->clear();
  m_bookmarkToggle->setChecked( m_view->getDoc()->mark( m_view->cursorLine() )
                                & KTextEditor::MarkInterface::markType01 );
  m_bookmarkToggle->plug( m_bookmarksMenu );
  m_bookmarkClear->plug( m_bookmarksMenu );

  insertBookmarks(*m_bookmarksMenu);
}

/*
   Make sure next/prev actions are plugged, and have a clean text
*/
void KateBookmarks::bookmarkMenuAboutToHide()
{
  m_bookmarkToggle->plug( m_bookmarksMenu );
  m_bookmarkClear->plug( m_bookmarksMenu );
  m_goNext->setText( i18n("Next Bookmark") );
  m_goNext->plug( m_bookmarksMenu );
  m_goPrevious->setText( i18n("Previous Bookmark") );
  m_goPrevious->plug( m_bookmarksMenu );
}

void KateBookmarks::goNext()
{
  QPtrList<KTextEditor::Mark> m = m_view->getDoc()->marks();
  if (m.isEmpty())
    return;

  uint line = m_view->cursorLine();
  int found = -1;

  for (uint z=0; z < m.count(); z++)
    if ( (m.at(z)->line > line) && ((found == -1) || (uint(found) > m.at(z)->line)) )
      found = m.at(z)->line;

  if (found != -1)
    m_view->gotoLineNumber ( found );
}

void KateBookmarks::goPrevious()
{
  QPtrList<KTextEditor::Mark> m = m_view->getDoc()->marks();
  if (m.isEmpty())
    return;

  uint line = m_view->cursorLine();
  int found = -1;

  for (uint z=0; z < m.count(); z++)
    if ((m.at(z)->line < line) && ((found == -1) || (uint(found) < m.at(z)->line)))
      found = m.at(z)->line;

  if (found != -1)
    m_view->gotoLineNumber ( found );
}

void KateBookmarks::marksChanged ()
{
  m_bookmarkClear->setEnabled( !m_view->getDoc()->marks().isEmpty() );
}

// kate: space-indent on; indent-width 2; replace-tabs on;
<commit_msg>kill tabs '\t', as they are interpreted as shortcuts in the bookmark menus.<commit_after>/* This file is part of the KDE libraries
   Copyright (C) 2002, 2003, 2004 Anders Lund <anders.lund@lund.tdcadsl.dk>
   Copyright (C) 2002 John Firebaugh <jfirebaugh@kde.org>

   This library is free software; you can redistribute it and/or
   modify it under the terms of the GNU Library General Public
   License version 2 as published by the Free Software Foundation.

   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; see the file COPYING.LIB.  If not, write to
   the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
   Boston, MA 02111-1307, USA.
*/

#include "katebookmarks.h"
#include "katebookmarks.moc"

#include "katedocument.h"
#include "kateview.h"

#include <klocale.h>
#include <kaction.h>
#include <kpopupmenu.h>
#include <kstringhandler.h>
#include <kxmlguiclient.h>
#include <kxmlguifactory.h>

#include <qregexp.h>
#include <qmemarray.h>
#include <qevent.h>

/**
   Utility: selection sort
   sort a QMemArray<uint> in ascending order.
   max it the largest (zerobased) index to sort.
   To sort the entire array: ssort( *array, array.size() -1 );
   This is only efficient if ran only once.
*/
static void ssort( QMemArray<uint> &a, int max )
{
  uint tmp, j, maxpos;
  for ( uint h = max; h >= 1; h-- )
  {
    maxpos = 0;
    for ( j = 0; j <= h; j++ )
      maxpos = a[j] > a[maxpos] ? j : maxpos;
    tmp = a[maxpos];
    a[maxpos] = a[h];
    a[h] = tmp;
  }
}

// TODO add a insort() or bubble_sort - more efficient for aboutToShow() ?

KateBookmarks::KateBookmarks( KateView* view, Sorting sort )
  : QObject( view, "kate bookmarks" )
  , m_view( view )
  , m_sorting( sort )
{
  connect (view->getDoc(), SIGNAL(marksChanged()), this, SLOT(marksChanged()));
  _tries=0;
  m_bookmarksMenu = 0L;
}

KateBookmarks::~KateBookmarks()
{
}

void KateBookmarks::createActions( KActionCollection* ac )
{
  m_bookmarkToggle = new KToggleAction(
    i18n("Set &Bookmark"), "bookmark", CTRL+Key_B,
    this, SLOT(toggleBookmark()),
    ac, "bookmarks_toggle" );
  m_bookmarkToggle->setWhatsThis(i18n("If a line has no bookmark then add one, otherwise remove it."));
  m_bookmarkToggle->setCheckedState( i18n("Clear &Bookmark") );

  m_bookmarkClear = new KAction(
    i18n("Clear &All Bookmarks"), 0,
    this, SLOT(clearBookmarks()),
    ac, "bookmarks_clear");
  m_bookmarkClear->setWhatsThis(i18n("Remove all bookmarks of the current document."));

  m_goNext = new KAction(
    i18n("Next Bookmark"), "next", ALT + Key_PageDown,
    this, SLOT(goNext()),
    ac, "bookmarks_next");
  m_goNext->setWhatsThis(i18n("Go to the next bookmark."));

  m_goPrevious = new KAction(
    i18n("Previous Bookmark"), "previous", ALT + Key_PageUp,
    this, SLOT(goPrevious()),
    ac, "bookmarks_previous");
  m_goPrevious->setWhatsThis(i18n("Go to the previous bookmark."));

  m_bookmarksMenu = (new KActionMenu(i18n("&Bookmarks"), ac, "bookmarks"))->popupMenu();

  //connect the aboutToShow() and aboutToHide() signals with
  //the bookmarkMenuAboutToShow() and bookmarkMenuAboutToHide() slots
  connect( m_bookmarksMenu, SIGNAL(aboutToShow()), this, SLOT(bookmarkMenuAboutToShow()));
  connect( m_bookmarksMenu, SIGNAL(aboutToHide()), this, SLOT(bookmarkMenuAboutToHide()) );

  marksChanged ();
  bookmarkMenuAboutToHide();

  connect( m_view, SIGNAL( gotFocus( Kate::View * ) ), this, SLOT( slotViewGotFocus( Kate::View * ) ) );
  connect( m_view, SIGNAL( lostFocus( Kate::View * ) ), this, SLOT( slotViewLostFocus( Kate::View * ) ) );
}

void KateBookmarks::toggleBookmark ()
{
  uint mark = m_view->getDoc()->mark( m_view->cursorLine() );
  if( mark & KTextEditor::MarkInterface::markType01 )
    m_view->getDoc()->removeMark( m_view->cursorLine(),
        KTextEditor::MarkInterface::markType01 );
  else
    m_view->getDoc()->addMark( m_view->cursorLine(),
        KTextEditor::MarkInterface::markType01 );
}

void KateBookmarks::clearBookmarks ()
{

  QPtrList<KTextEditor::Mark> m = m_view->getDoc()->marks();
  for (uint i=0; i < m.count(); i++)
    m_view->getDoc()->removeMark( m.at(i)->line, KTextEditor::MarkInterface::markType01 );

  // just to be sure ;)
  marksChanged ();
}

void KateBookmarks::slotViewGotFocus( Kate::View *v )
{
  if ( v == (Kate::View*)m_view )
    bookmarkMenuAboutToHide();
}

void KateBookmarks::slotViewLostFocus( Kate::View *v )
{
  if ( v == (Kate::View*)m_view )
    m_bookmarksMenu->clear();
}

void KateBookmarks::insertBookmarks( QPopupMenu& menu )
{
  uint line = m_view->cursorLine();
  const QRegExp re("&(?!&)");
  int idx( -1 );
  int old_menu_count = menu.count();
  KTextEditor::Mark *next = 0;
  KTextEditor::Mark *prev = 0;

  QPtrList<KTextEditor::Mark> m = m_view->getDoc()->marks();
  QMemArray<uint> sortArray( m.count() );
  QPtrListIterator<KTextEditor::Mark> it( m );

  if ( it.count() > 0 )
    menu.insertSeparator();

  for( int i = 0; *it; ++it, ++i )
  {
    if( (*it)->type & KTextEditor::MarkInterface::markType01 )
    {
      QString bText = KStringHandler::rEmSqueeze
                      ( m_view->getDoc()->textLine( (*it)->line ),
                        menu.fontMetrics(), 32 );
      bText.replace(re, "&&"); // kill undesired accellerators!
      bText.replace('\t', ' '); // kill tabs, as they are interpreted as shortcuts

      if ( m_sorting == Position )
      {
        sortArray[i] = (*it)->line;
        ssort( sortArray, i );
        idx = sortArray.find( (*it)->line ) + 3;
      }

      menu.insertItem(
          QString("%1 - \"%2\"").arg( (*it)->line+1 ).arg( bText ),
          m_view, SLOT(gotoLineNumber(int)), 0, (*it)->line, idx );

      if ( (*it)->line < line )
      {
        if ( ! prev || prev->line < (*it)->line )
          prev = (*it);
      }

      else if ( (*it)->line > line )
      {
        if ( ! next || next->line > (*it)->line )
          next = (*it);
      }
    }
  }

  idx = ++old_menu_count;
  if ( next )
  {
    m_goNext->setText( i18n("&Next: %1 - \"%2\"").arg( next->line + 1 )
        .arg( KStringHandler::rsqueeze( m_view->getDoc()->textLine( next->line ), 24 ) ) );
    m_goNext->plug( &menu, idx );
    idx++;
  }
  if ( prev )
  {
    m_goPrevious->setText( i18n("&Previous: %1 - \"%2\"").arg(prev->line + 1 )
        .arg( KStringHandler::rsqueeze( m_view->getDoc()->textLine( prev->line ), 24 ) ) );
    m_goPrevious->plug( &menu, idx );
    idx++;
  }
  if ( next || prev )
    menu.insertSeparator( idx );

}

void KateBookmarks::bookmarkMenuAboutToShow()
{

  QPtrList<KTextEditor::Mark> m = m_view->getDoc()->marks();

  m_bookmarksMenu->clear();
  m_bookmarkToggle->setChecked( m_view->getDoc()->mark( m_view->cursorLine() )
                                & KTextEditor::MarkInterface::markType01 );
  m_bookmarkToggle->plug( m_bookmarksMenu );
  m_bookmarkClear->plug( m_bookmarksMenu );

  insertBookmarks(*m_bookmarksMenu);
}

/*
   Make sure next/prev actions are plugged, and have a clean text
*/
void KateBookmarks::bookmarkMenuAboutToHide()
{
  m_bookmarkToggle->plug( m_bookmarksMenu );
  m_bookmarkClear->plug( m_bookmarksMenu );
  m_goNext->setText( i18n("Next Bookmark") );
  m_goNext->plug( m_bookmarksMenu );
  m_goPrevious->setText( i18n("Previous Bookmark") );
  m_goPrevious->plug( m_bookmarksMenu );
}

void KateBookmarks::goNext()
{
  QPtrList<KTextEditor::Mark> m = m_view->getDoc()->marks();
  if (m.isEmpty())
    return;

  uint line = m_view->cursorLine();
  int found = -1;

  for (uint z=0; z < m.count(); z++)
    if ( (m.at(z)->line > line) && ((found == -1) || (uint(found) > m.at(z)->line)) )
      found = m.at(z)->line;

  if (found != -1)
    m_view->gotoLineNumber ( found );
}

void KateBookmarks::goPrevious()
{
  QPtrList<KTextEditor::Mark> m = m_view->getDoc()->marks();
  if (m.isEmpty())
    return;

  uint line = m_view->cursorLine();
  int found = -1;

  for (uint z=0; z < m.count(); z++)
    if ((m.at(z)->line < line) && ((found == -1) || (uint(found) < m.at(z)->line)))
      found = m.at(z)->line;

  if (found != -1)
    m_view->gotoLineNumber ( found );
}

void KateBookmarks::marksChanged ()
{
  m_bookmarkClear->setEnabled( !m_view->getDoc()->marks().isEmpty() );
}

// kate: space-indent on; indent-width 2; replace-tabs on;
<|endoftext|>


<commit_before>/*BEGIN_LEGAL
Intel Open Source License

Copyright (c) 2002-2013 Intel Corporation. All rights reserved.

Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are
met:

Redistributions of source code must retain the above copyright notice,
this list of conditions and the following disclaimer.  Redistributions
in binary form must reproduce the above copyright notice, this list of
conditions and the following disclaimer in the documentation and/or
other materials provided with the distribution.  Neither the name of
the Intel Corporation nor the names of its contributors may be used to
endorse or promote products derived from this software without
specific prior written permission.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE INTEL OR
ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
END_LEGAL */

#include "pin.H"
#include <iostream>
#include <cstdio>
#include <set>
#include "whitelist.h"
using namespace std;

// Prototypes ; TODO: Move to Seperate Header file?
void HookFree(IMG img);
void HookMalloc(IMG img);

typedef VOID* (*FP_MALLOC)(size_t);
typedef void (*FP_FREE)(void*);

bool inMain = false;
FILE * trace;
WhiteList wl;
int freeWasCalled = 0;
// Malloc
int mallocNumber = -1;
int freeNumber = -1;

// Print a memory read record
VOID RecordHeapMemRead(VOID * ip, VOID * addr) {
    //fprintf(trace,"READ:%p: R %p\n", ip, addr);
    //fprintf(trace,"READ: %p \n",addr);
    //cout << hex << ip << " R " << hex << addr << endl << flush;
    // printf("heap read: %p\n", addr);
}

// Print a memory write record
VOID RecordHeapMemWrite(VOID * ip, VOID * addr) {
    int rtn = wl.containsAddress(addr);
    if(rtn != ERR_NOT_FOUND && !freeWasCalled) {
        fprintf(trace,"##########BAD WRITE: %p \n", addr);
        cout << "BAD WRITE" << endl;
    }
    // printf("heap write: %p\n", addr);
}

VOID RecordStackMemRead(VOID * ip, VOID * addr) {
    int rtn = wl.containsAddress(addr);
    if(rtn != ERR_NOT_FOUND && !freeWasCalled) {
        fprintf(trace,"##########BAD WRITE: %p \n", addr);
        cout << "BAD READ" << endl;
    }
    // printf("stack read: %p\n", addr);
}

// Print a memory write record
VOID RecordStackMemWrite(VOID * ip, VOID * addr) {
    int rtn = wl.containsAddress(addr);
    if(rtn != ERR_NOT_FOUND && !freeWasCalled) {
        fprintf(trace,"##########BAD WRITE: %p \n", addr);
        cout << "BAD WRITE" << endl;
    }
    freeWasCalled = 0;
    // printf("stack write: %p\n", addr);
}

// Is called for every instruction and instruments reads and writes
VOID Instruction(INS ins, VOID *v) {
    if(!inMain || (mallocNumber <= 0 && freeNumber <=0))
        return;
    // Instruments memory accesses using a predicated call, i.e.
    // the instrumentation is called iff the instruction will actually be executed.
    //
    // On the IA-32 and Intel(R) 64 architectures conditional moves and REP
    // prefixed instructions appear as predicated instructions in Pin.
    UINT32 memOperands = INS_MemoryOperandCount(ins);

    // Iterate over each memory operand of the instruction.
    for (UINT32 memOp = 0; memOp < memOperands; memOp++) {
        //if (INS_MemoryOperandIsRead(ins, memOp)) {
        if (INS_MemoryOperandIsRead(ins, memOp) && !INS_IsStackRead(ins)) {
            INS_InsertPredicatedCall(
                ins, IPOINT_BEFORE, (AFUNPTR)RecordHeapMemRead,
                IARG_INST_PTR,
                IARG_MEMORYOP_EA, memOp,
                IARG_END);
        } else if (INS_MemoryOperandIsRead(ins, memOp)) {
            INS_InsertPredicatedCall(
                ins, IPOINT_BEFORE, (AFUNPTR)RecordStackMemRead,
                IARG_INST_PTR,
                IARG_MEMORYOP_EA, memOp,
                IARG_END);
        }
        // Note that in some architectures a single memory operand can be
        // both read and written (for instance incl (%eax) on IA-32)
        // In that case we instrument it once for read and once for write.
        //if (INS_MemoryOperandIsWritten(ins, memOp)) {
        if (INS_MemoryOperandIsWritten(ins, memOp) && !INS_IsStackWrite(ins)) {
            INS_InsertPredicatedCall(
                ins, IPOINT_BEFORE, (AFUNPTR)RecordHeapMemWrite,
                IARG_INST_PTR,
                IARG_MEMORYOP_EA, memOp,
                IARG_END);
        } else if (INS_MemoryOperandIsWritten(ins, memOp)) {
            INS_InsertPredicatedCall(
                ins, IPOINT_BEFORE, (AFUNPTR)RecordStackMemWrite,
                IARG_INST_PTR,
                IARG_MEMORYOP_EA, memOp,
                IARG_END);
        }
    }
}

VOID Fini(INT32 code, VOID *v) {
    fprintf(trace, "#eof \n");
    fclose(trace);
}

// This is the replacement routine.
VOID* NewMalloc(FP_MALLOC orgFuncptr, UINT32 arg0, ADDRINT returnIp) {
    // Call the relocated entry point of the original (replaced) routine.
    void* v = orgFuncptr(arg0 + 16);
    char *cp = (char*)v;
    wl.add(cp, 8);
    fprintf(trace, "ADDED: %p %d \n", cp, 8);
    cp += (8 + arg0);
    wl.add(cp, 8);
    fprintf(trace, "ADDED: %p %d \n", cp, 8);
    return (void*)(((char*)v)+8);
}

void NewFree(FP_FREE orgFuncptr, void* ptr, ADDRINT returnIp) {
    printf("address free %p\n", ptr);
    if(ptr != NULL) {
        // TODO: -8 is the fence size; Make it a constant
        // Set the pointer to the actual start of memory
        void* realPtr = (char*)ptr - 8;
        // Check the WhiteList
        int index  = wl.containsAddress(ptr);
        if(index > 0) {
            // Remove the space
            orgFuncptr(realPtr);
        } else if(index == ERR_NOT_FOUND) {
            // This currently gets hit since a blacklist is being used
            fprintf(trace, "Address = %p not found. Bad address or stack address used.\n", ptr);
        } else if(index == ERR_MID_CHUNK) {
            fprintf(trace, "Mid-chunk memory deallocation @ %p\n", ptr);
        } else {
            fprintf(trace, "Unable to deallocate the memory @ %p\n", ptr);
        }
    } else {
        fprintf(trace, "Attempted to free NULL\n");
    }
}

// Pin calls this function every time a new img is loaded.
// It is best to do probe replacement when the image is loaded,
// because only one thread knows about the image at this time.
VOID ImageLoad(IMG img, VOID *v) {
    if(!inMain) {
        inMain = IMG_IsMainExecutable(img);
        return;
    }
    // Hook Functions
    HookMalloc(img);
    HookFree(img);
}

void HookFree(IMG img) {
    RTN rtn = RTN_FindByName(img, "free");
    // TODO: Hack for skipping hooks to /lib64/ld-linux-x86-64.so.2
    if(RTN_Valid(rtn)) {
        freeNumber++;
    }
    if(RTN_Valid(rtn) && freeNumber > 0) {
        cout << "Replacing free in " << IMG_Name(img) << endl;
        // Return type, cstype, function name, arguments...
        PROTO proto = PROTO_Allocate(PIN_PARG(void), CALLINGSTD_DEFAULT, "free", PIN_PARG(void*), PIN_PARG_END());
        // Replace free
        RTN_ReplaceSignature(rtn, AFUNPTR(NewFree),
            IARG_PROTOTYPE,
            proto,
            IARG_ORIG_FUNCPTR,
            IARG_FUNCARG_ENTRYPOINT_VALUE,
            0,
            IARG_RETURN_IP,
            IARG_END);
        // Free the function prototype
        PROTO_Free(proto);
    }
}

void HookMalloc(IMG img) {
    // See if malloc() is present in the image.  If so, replace it.
    RTN rtnMalloc = RTN_FindByName(img, "malloc");
    if(RTN_Valid(rtnMalloc)) {
        mallocNumber++;
    }
    if(RTN_Valid(rtnMalloc) && mallocNumber > 0) {
        cout << "Replacing malloc in " << IMG_Name(img) << endl;
        PROTO proto_malloc = PROTO_Allocate(PIN_PARG(void *), CALLINGSTD_DEFAULT,
            "malloc", PIN_PARG(int), PIN_PARG_END());
        RTN_ReplaceSignature(rtnMalloc, AFUNPTR(NewMalloc),
            IARG_PROTOTYPE, proto_malloc,
            IARG_ORIG_FUNCPTR,
            IARG_FUNCARG_ENTRYPOINT_VALUE, 0,
            IARG_RETURN_IP,
            IARG_END);
        // Free the function prototype.
        PROTO_Free(proto_malloc);
    }
}

/* ===================================================================== */
/* Print Help Message                                                    */
/* ===================================================================== */

INT32 Usage() {
    cerr << "This tool demonstrates how to replace an original" << endl;
    cerr << " function with a custom function defined in the tool " << endl;
    cerr << " using probes.  The replacement function has a different " << endl;
    cerr << " signature from that of the original replaced function." << endl;
    cerr << endl << KNOB_BASE::StringKnobSummary() << endl;
    return -1;
}

/* ===================================================================== */
/* Main: Initialize and start Pin in Probe mode.                         */
/* ===================================================================== */

int main(INT32 argc, CHAR *argv[]) {
    // Initialize symbol processing
    PIN_InitSymbols();
    // Initialize pin
    if (PIN_Init(argc, argv))
        return Usage();
    // Open up the trace file
    trace = fopen("zzz.out", "w");
    // Register ImageLoad to be called when an image is loaded
    IMG_AddInstrumentFunction(ImageLoad, 0);
    INS_AddInstrumentFunction(Instruction, 0);
    PIN_AddFiniFunction(Fini, 0);
    PIN_StartProgram();
    return 0;
}
<commit_msg>Removed a print statement from the NewFree function.<commit_after>/*BEGIN_LEGAL
Intel Open Source License

Copyright (c) 2002-2013 Intel Corporation. All rights reserved.

Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are
met:

Redistributions of source code must retain the above copyright notice,
this list of conditions and the following disclaimer.  Redistributions
in binary form must reproduce the above copyright notice, this list of
conditions and the following disclaimer in the documentation and/or
other materials provided with the distribution.  Neither the name of
the Intel Corporation nor the names of its contributors may be used to
endorse or promote products derived from this software without
specific prior written permission.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE INTEL OR
ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
END_LEGAL */

#include "pin.H"
#include <iostream>
#include <cstdio>
#include <set>
#include "whitelist.h"
using namespace std;

// Prototypes ; TODO: Move to Seperate Header file?
void HookFree(IMG img);
void HookMalloc(IMG img);

typedef VOID* (*FP_MALLOC)(size_t);
typedef void (*FP_FREE)(void*);

bool inMain = false;
FILE * trace;
WhiteList wl;
int freeWasCalled = 0;
// Malloc
int mallocNumber = -1;
int freeNumber = -1;

// Print a memory read record
VOID RecordHeapMemRead(VOID * ip, VOID * addr) {
    //fprintf(trace,"READ:%p: R %p\n", ip, addr);
    //fprintf(trace,"READ: %p \n",addr);
    //cout << hex << ip << " R " << hex << addr << endl << flush;
    // printf("heap read: %p\n", addr);
}

// Print a memory write record
VOID RecordHeapMemWrite(VOID * ip, VOID * addr) {
    int rtn = wl.containsAddress(addr);
    if(rtn != ERR_NOT_FOUND && !freeWasCalled) {
        fprintf(trace,"##########BAD WRITE: %p \n", addr);
        cout << "BAD WRITE" << endl;
    }
    // printf("heap write: %p\n", addr);
}

VOID RecordStackMemRead(VOID * ip, VOID * addr) {
    int rtn = wl.containsAddress(addr);
    if(rtn != ERR_NOT_FOUND && !freeWasCalled) {
        fprintf(trace,"##########BAD WRITE: %p \n", addr);
        cout << "BAD READ" << endl;
    }
    // printf("stack read: %p\n", addr);
}

// Print a memory write record
VOID RecordStackMemWrite(VOID * ip, VOID * addr) {
    int rtn = wl.containsAddress(addr);
    if(rtn != ERR_NOT_FOUND && !freeWasCalled) {
        fprintf(trace,"##########BAD WRITE: %p \n", addr);
        cout << "BAD WRITE" << endl;
    }
    freeWasCalled = 0;
    // printf("stack write: %p\n", addr);
}

// Is called for every instruction and instruments reads and writes
VOID Instruction(INS ins, VOID *v) {
    if(!inMain || (mallocNumber <= 0 && freeNumber <=0))
        return;
    // Instruments memory accesses using a predicated call, i.e.
    // the instrumentation is called iff the instruction will actually be executed.
    //
    // On the IA-32 and Intel(R) 64 architectures conditional moves and REP
    // prefixed instructions appear as predicated instructions in Pin.
    UINT32 memOperands = INS_MemoryOperandCount(ins);

    // Iterate over each memory operand of the instruction.
    for (UINT32 memOp = 0; memOp < memOperands; memOp++) {
        //if (INS_MemoryOperandIsRead(ins, memOp)) {
        if (INS_MemoryOperandIsRead(ins, memOp) && !INS_IsStackRead(ins)) {
            INS_InsertPredicatedCall(
                ins, IPOINT_BEFORE, (AFUNPTR)RecordHeapMemRead,
                IARG_INST_PTR,
                IARG_MEMORYOP_EA, memOp,
                IARG_END);
        } else if (INS_MemoryOperandIsRead(ins, memOp)) {
            INS_InsertPredicatedCall(
                ins, IPOINT_BEFORE, (AFUNPTR)RecordStackMemRead,
                IARG_INST_PTR,
                IARG_MEMORYOP_EA, memOp,
                IARG_END);
        }
        // Note that in some architectures a single memory operand can be
        // both read and written (for instance incl (%eax) on IA-32)
        // In that case we instrument it once for read and once for write.
        //if (INS_MemoryOperandIsWritten(ins, memOp)) {
        if (INS_MemoryOperandIsWritten(ins, memOp) && !INS_IsStackWrite(ins)) {
            INS_InsertPredicatedCall(
                ins, IPOINT_BEFORE, (AFUNPTR)RecordHeapMemWrite,
                IARG_INST_PTR,
                IARG_MEMORYOP_EA, memOp,
                IARG_END);
        } else if (INS_MemoryOperandIsWritten(ins, memOp)) {
            INS_InsertPredicatedCall(
                ins, IPOINT_BEFORE, (AFUNPTR)RecordStackMemWrite,
                IARG_INST_PTR,
                IARG_MEMORYOP_EA, memOp,
                IARG_END);
        }
    }
}

VOID Fini(INT32 code, VOID *v) {
    fprintf(trace, "#eof \n");
    fclose(trace);
}

// This is the replacement routine.
VOID* NewMalloc(FP_MALLOC orgFuncptr, UINT32 arg0, ADDRINT returnIp) {
    // Call the relocated entry point of the original (replaced) routine.
    void* v = orgFuncptr(arg0 + 16);
    char *cp = (char*)v;
    wl.add(cp, 8);
    fprintf(trace, "ADDED: %p %d \n", cp, 8);
    cp += (8 + arg0);
    wl.add(cp, 8);
    fprintf(trace, "ADDED: %p %d \n", cp, 8);
    return (void*)(((char*)v)+8);
}

void NewFree(FP_FREE orgFuncptr, void* ptr, ADDRINT returnIp) {
    if(ptr != NULL) {
        // TODO: -8 is the fence size; Make it a constant
        // Set the pointer to the actual start of memory
        void* realPtr = (char*)ptr - 8;
        // Check the WhiteList
        int index  = wl.containsAddress(ptr);
        if(index > 0) {
            // Remove the space
            orgFuncptr(realPtr);
        } else if(index == ERR_NOT_FOUND) {
            // This currently gets hit since a blacklist is being used
            fprintf(trace, "Address = %p not found. Bad address or stack address used.\n", ptr);
        } else if(index == ERR_MID_CHUNK) {
            fprintf(trace, "Mid-chunk memory deallocation @ %p\n", ptr);
        } else {
            fprintf(trace, "Unable to deallocate the memory @ %p\n", ptr);
        }
    } else {
        fprintf(trace, "Attempted to free NULL\n");
    }
}

// Pin calls this function every time a new img is loaded.
// It is best to do probe replacement when the image is loaded,
// because only one thread knows about the image at this time.
VOID ImageLoad(IMG img, VOID *v) {
    if(!inMain) {
        inMain = IMG_IsMainExecutable(img);
        return;
    }
    // Hook Functions
    HookMalloc(img);
    HookFree(img);
}

void HookFree(IMG img) {
    RTN rtn = RTN_FindByName(img, "free");
    // TODO: Hack for skipping hooks to /lib64/ld-linux-x86-64.so.2
    if(RTN_Valid(rtn)) {
        freeNumber++;
    }
    if(RTN_Valid(rtn) && freeNumber > 0) {
        cout << "Replacing free in " << IMG_Name(img) << endl;
        // Return type, cstype, function name, arguments...
        PROTO proto = PROTO_Allocate(PIN_PARG(void), CALLINGSTD_DEFAULT, "free", PIN_PARG(void*), PIN_PARG_END());
        // Replace free
        RTN_ReplaceSignature(rtn, AFUNPTR(NewFree),
            IARG_PROTOTYPE,
            proto,
            IARG_ORIG_FUNCPTR,
            IARG_FUNCARG_ENTRYPOINT_VALUE,
            0,
            IARG_RETURN_IP,
            IARG_END);
        // Free the function prototype
        PROTO_Free(proto);
    }
}

void HookMalloc(IMG img) {
    // See if malloc() is present in the image.  If so, replace it.
    RTN rtnMalloc = RTN_FindByName(img, "malloc");
    if(RTN_Valid(rtnMalloc)) {
        mallocNumber++;
    }
    if(RTN_Valid(rtnMalloc) && mallocNumber > 0) {
        cout << "Replacing malloc in " << IMG_Name(img) << endl;
        PROTO proto_malloc = PROTO_Allocate(PIN_PARG(void *), CALLINGSTD_DEFAULT,
            "malloc", PIN_PARG(int), PIN_PARG_END());
        RTN_ReplaceSignature(rtnMalloc, AFUNPTR(NewMalloc),
            IARG_PROTOTYPE, proto_malloc,
            IARG_ORIG_FUNCPTR,
            IARG_FUNCARG_ENTRYPOINT_VALUE, 0,
            IARG_RETURN_IP,
            IARG_END);
        // Free the function prototype.
        PROTO_Free(proto_malloc);
    }
}

/* ===================================================================== */
/* Print Help Message                                                    */
/* ===================================================================== */

INT32 Usage() {
    cerr << "This tool demonstrates how to replace an original" << endl;
    cerr << " function with a custom function defined in the tool " << endl;
    cerr << " using probes.  The replacement function has a different " << endl;
    cerr << " signature from that of the original replaced function." << endl;
    cerr << endl << KNOB_BASE::StringKnobSummary() << endl;
    return -1;
}

/* ===================================================================== */
/* Main: Initialize and start Pin in Probe mode.                         */
/* ===================================================================== */

int main(INT32 argc, CHAR *argv[]) {
    // Initialize symbol processing
    PIN_InitSymbols();
    // Initialize pin
    if (PIN_Init(argc, argv))
        return Usage();
    // Open up the trace file
    trace = fopen("zzz.out", "w");
    // Register ImageLoad to be called when an image is loaded
    IMG_AddInstrumentFunction(ImageLoad, 0);
    INS_AddInstrumentFunction(Instruction, 0);
    PIN_AddFiniFunction(Fini, 0);
    PIN_StartProgram();
    return 0;
}
<|endoftext|>


<commit_before>// fastmap.cpp : Defines the entry point for the DLL application.
//

#include "bzfsAPI.h"
#include "plugin_utils.h"
#include <map>
#include <string>

BZ_GET_PLUGIN_VERSION

class FastMapEventHandler;

class FastMapClient : public bz_NonPlayerConnectionHandler
{
public:
  FastMapClient( int connectionID = -1, FastMapEventHandler *h = NULL);
  virtual ~FastMapClient();
  virtual void pending ( int connectionID, void *d, unsigned int s );
  virtual void disconnect ( int connectionID );

  void startTransfer ( unsigned char * d, unsigned int s );
  bool updateTransfer ( void );

  int conID;

  unsigned char *data;
  unsigned int size;
  unsigned int currerntPos;

  std::string command;

  FastMapEventHandler* handler;
};

class FastMapEventHandler : public bz_EventHandler
{
public:
  FastMapEventHandler();
  virtual ~FastMapEventHandler();

  virtual void process ( bz_EventData *eventData );

  void updateHTTPServer ( void );
  void killHTTPServer ( void );

  std::string	mapName;

  std::map<int,FastMapClient*> clients;

  unsigned char* mapData;
  unsigned int mapDataSize;
};

FastMapEventHandler fastMapEventHandler;

BZF_PLUGIN_CALL int bz_Load ( const char* /*commandLine*/ )
{
  bz_debugMessage(4,"fastmap plugin loaded");
  bz_registerEvent (bz_eWorldFinalized,&fastMapEventHandler);
  bz_registerEvent (bz_eTickEvent,&fastMapEventHandler);
  bz_registerEvent (bz_eListServerUpdateEvent,&fastMapEventHandler);
  bz_registerEvent (bz_eNewNonPlayerConnection,&fastMapEventHandler);
return 0;
}

BZF_PLUGIN_CALL int bz_Unload ( void )
{
  fastMapEventHandler.killHTTPServer();

  bz_debugMessage(4,"fastmap plugin unloaded");
  bz_removeEvent (bz_eWorldFinalized,&fastMapEventHandler);
  bz_removeEvent (bz_eTickEvent,&fastMapEventHandler);
  bz_removeEvent (bz_eListServerUpdateEvent,&fastMapEventHandler);
  bz_removeEvent (bz_eNewNonPlayerConnection,&fastMapEventHandler);
return 0;
}

FastMapEventHandler::FastMapEventHandler()
{
  mapName = format("PrivateMap%d",(unsigned int)this);

  mapData = NULL;
  mapDataSize = 0;
}

FastMapEventHandler::~FastMapEventHandler()
{
  killHTTPServer();
}

void FastMapEventHandler::process ( bz_EventData *eventData )
{
  if ( eventData->eventType == bz_eListServerUpdateEvent )
  {
    bz_ListServerUpdateEvent_V1 *updateData = (bz_ListServerUpdateEvent_V1*)eventData;
    mapName = format("%s%d",updateData->address,(unsigned int)this);
  }
  else if ( eventData->eventType == bz_eWorldFinalized )
  {
    killHTTPServer();

    if (!bz_getPublic() || bz_getClientWorldDowloadURL().size())
      return;

    mapDataSize = bz_getWorldCacheSize();
    mapData = (unsigned char*)malloc(mapDataSize);
    bz_getWorldCacheData(mapData);

    if (mapDataSize)
      bz_setClientWorldDowloadURL(format("HTTP:/%s/%s",bz_getPublicAddr().c_str(),mapName.c_str()).c_str());
  }
  else if ( eventData->eventType == bz_eTickEvent)
  {
    if ( mapDataSize )
      updateHTTPServer();
  }
  else if ( eventData->eventType == bz_eNewNonPlayerConnection )
  {
    if ( mapDataSize )
    {
      bz_NewNonPlayerConnectionEventData_V1 *connData = (bz_NewNonPlayerConnectionEventData_V1*)eventData;
      if (connData->size >= 3 && strncmp((const char*)connData->data,"GET",3))
      {
	FastMapClient * handler = new FastMapClient(connData->connectionID,this);

	if(bz_registerNonPlayerConnectionHandler ( connData->connectionID, handler ) )
	{
	  clients[connData->connectionID] = handler;
	  handler->pending (connData->connectionID,connData->data, connData->size);
	}
	else
	  delete(handler);
      }
    }
  }
  else if ( eventData->eventType == bz_eIdleNewNonPlayerConnection )
  {
    bz_NewNonPlayerConnectionEventData_V1 *connData = (bz_NewNonPlayerConnectionEventData_V1*)eventData;

    std::map<int,FastMapClient*>::iterator itr = clients.find(connData->connectionID);
    if( itr != clients.end() )
    {
      bz_removeNonPlayerConnectionHandler ( itr->second->conID, itr->second );
      delete(itr->second);
      clients.erase(itr);
    }
    bz_disconectNonPlayerConnection ( connData->connectionID );
 }
}

void FastMapEventHandler::updateHTTPServer ( void )
{
  std::vector<int> killList;

  std::map<int,FastMapClient*>::iterator itr = clients.begin();
  while (itr != clients.end())
  {
    if(itr->second->conID == -1 || !itr->second->updateTransfer() )
      killList.push_back(itr->first);
    itr++;
  }

  for ( unsigned int i = 0; i < (unsigned int)killList.size(); i++)
    clients.erase(clients.find(killList[i]));

  if (clients.size())
    bz_setMaxWaitTime(0.001f);
  else
    bz_setMaxWaitTime(2.0f);
}

void FastMapEventHandler::killHTTPServer ( void )
{
  if ( mapData )
    free(mapData);

  mapData = NULL;
  mapDataSize = 0;
}

FastMapClient::FastMapClient( int connectionID, FastMapEventHandler *h )
{
  conID = connectionID;

  data = NULL;
  size = 0;
  currerntPos = 0;
  handler = h;
}

FastMapClient::~FastMapClient()
{
  if (data)
    free(data);
}

void FastMapClient::pending ( int connectionID, void *s, unsigned int d )
{
  if ( connectionID != conID )
    return;

  char *c = (char*)malloc(d+1);
  memcpy(c,s,d);
  c[d] = 0;
  command += c;
  free (c);

  // parse the command
  std::vector<std::string> commandList = tokenize(command,std::string("\r\n"),0,false);

  if ( commandList.size() == 1 && commandList[0].size() == command.size() ) // there were no delims, we are done.
    return;

  for (unsigned int i = 0; i < (unsigned int)commandList.size();i++)
  {
    command.erase(command.begin(),command.begin()+commandList[i].size());

    std::string thisLine = commandList[i];

    if (strncmp(thisLine.c_str(),"GET",3) == 0 && !data) // it's a get and we arn't in a transfer
    {
      std::vector<std::string> params = tokenize(thisLine,std::string(" "),0,false);
      if ( params.size() <= 3 )
      {
	std::string urlPath = params[1];

	if (urlPath.size()>2 && handler->mapName == urlPath.c_str()+1 )
	{
	  startTransfer(handler->mapData,handler->mapDataSize);
	}
      }
    }
  }
}

void FastMapClient::disconnect ( int connectionID )
{
  if (connectionID != conID)
    return;

  bz_removeNonPlayerConnectionHandler ( conID, this );
  bz_disconectNonPlayerConnection ( conID );
  conID = -1;

}
void FastMapClient::startTransfer ( unsigned char * d, unsigned int s )
{
  if (data)
    free (data);
  data = NULL;
  size = 0;
  currerntPos = 0;

  if (!s)
    return;

  data = (unsigned char*)malloc(s);
  memcpy(data,d,s);
  size = s;

  std::string httpHeaders;
  httpHeaders += "HTTP/1.1 200 OK\n";
  httpHeaders += format("Content-Length: %d\n", size);
  httpHeaders += "Connection: close\n";
  httpHeaders += "Content-Type: application/binary\n";
  httpHeaders += "\n";

  if (!bz_sendNonPlayerData ( conID, httpHeaders.c_str(), httpHeaders.size() ) || !updateTransfer())
    disconnect(conID);
}

bool FastMapClient::updateTransfer ( void )
{
  int chunkToSend = 1000;

  if ( currerntPos <= size )
    return false;

  // wait till the current data is sent
  if (bz_getNonPlayerConnectionOutboundPacketCount(conID) != 0)
    return true;

  if ( currerntPos + chunkToSend > size)
    chunkToSend = size-currerntPos;

  bool worked = bz_sendNonPlayerData ( conID, data+currerntPos, chunkToSend );

  currerntPos += chunkToSend;

  return worked;
}

// Local Variables: ***
// mode:C++ ***
// tab-width: 8 ***
// c-basic-offset: 2 ***
// indent-tabs-mode: t ***
// End: ***
// ex: shiftwidth=2 tabstop=8
<commit_msg>octet-stream not binary<commit_after>// fastmap.cpp : Defines the entry point for the DLL application.
//

#include "bzfsAPI.h"
#include "plugin_utils.h"
#include <map>
#include <string>

BZ_GET_PLUGIN_VERSION

class FastMapEventHandler;

class FastMapClient : public bz_NonPlayerConnectionHandler
{
public:
  FastMapClient( int connectionID = -1, FastMapEventHandler *h = NULL);
  virtual ~FastMapClient();
  virtual void pending ( int connectionID, void *d, unsigned int s );
  virtual void disconnect ( int connectionID );

  void startTransfer ( unsigned char * d, unsigned int s );
  bool updateTransfer ( void );

  int conID;

  unsigned char *data;
  unsigned int size;
  unsigned int currerntPos;

  std::string command;

  FastMapEventHandler* handler;
};

class FastMapEventHandler : public bz_EventHandler
{
public:
  FastMapEventHandler();
  virtual ~FastMapEventHandler();

  virtual void process ( bz_EventData *eventData );

  void updateHTTPServer ( void );
  void killHTTPServer ( void );

  std::string	mapName;

  std::map<int,FastMapClient*> clients;

  unsigned char* mapData;
  unsigned int mapDataSize;
};

FastMapEventHandler fastMapEventHandler;

BZF_PLUGIN_CALL int bz_Load ( const char* /*commandLine*/ )
{
  bz_debugMessage(4,"fastmap plugin loaded");
  bz_registerEvent (bz_eWorldFinalized,&fastMapEventHandler);
  bz_registerEvent (bz_eTickEvent,&fastMapEventHandler);
  bz_registerEvent (bz_eListServerUpdateEvent,&fastMapEventHandler);
  bz_registerEvent (bz_eNewNonPlayerConnection,&fastMapEventHandler);
return 0;
}

BZF_PLUGIN_CALL int bz_Unload ( void )
{
  fastMapEventHandler.killHTTPServer();

  bz_debugMessage(4,"fastmap plugin unloaded");
  bz_removeEvent (bz_eWorldFinalized,&fastMapEventHandler);
  bz_removeEvent (bz_eTickEvent,&fastMapEventHandler);
  bz_removeEvent (bz_eListServerUpdateEvent,&fastMapEventHandler);
  bz_removeEvent (bz_eNewNonPlayerConnection,&fastMapEventHandler);
return 0;
}

FastMapEventHandler::FastMapEventHandler()
{
  mapName = format("PrivateMap%d",(unsigned int)this);

  mapData = NULL;
  mapDataSize = 0;
}

FastMapEventHandler::~FastMapEventHandler()
{
  killHTTPServer();
}

void FastMapEventHandler::process ( bz_EventData *eventData )
{
  if ( eventData->eventType == bz_eListServerUpdateEvent )
  {
    bz_ListServerUpdateEvent_V1 *updateData = (bz_ListServerUpdateEvent_V1*)eventData;
    mapName = format("%s%d",updateData->address,(unsigned int)this);
  }
  else if ( eventData->eventType == bz_eWorldFinalized )
  {
    killHTTPServer();

    if (!bz_getPublic() || bz_getClientWorldDowloadURL().size())
      return;

    mapDataSize = bz_getWorldCacheSize();
    mapData = (unsigned char*)malloc(mapDataSize);
    bz_getWorldCacheData(mapData);

    if (mapDataSize)
      bz_setClientWorldDowloadURL(format("HTTP:/%s/%s",bz_getPublicAddr().c_str(),mapName.c_str()).c_str());
  }
  else if ( eventData->eventType == bz_eTickEvent)
  {
    if ( mapDataSize )
      updateHTTPServer();
  }
  else if ( eventData->eventType == bz_eNewNonPlayerConnection )
  {
    if ( mapDataSize )
    {
      bz_NewNonPlayerConnectionEventData_V1 *connData = (bz_NewNonPlayerConnectionEventData_V1*)eventData;
      if (connData->size >= 3 && strncmp((const char*)connData->data,"GET",3))
      {
	FastMapClient * handler = new FastMapClient(connData->connectionID,this);

	if(bz_registerNonPlayerConnectionHandler ( connData->connectionID, handler ) )
	{
	  clients[connData->connectionID] = handler;
	  handler->pending (connData->connectionID,connData->data, connData->size);
	}
	else
	  delete(handler);
      }
    }
  }
  else if ( eventData->eventType == bz_eIdleNewNonPlayerConnection )
  {
    bz_NewNonPlayerConnectionEventData_V1 *connData = (bz_NewNonPlayerConnectionEventData_V1*)eventData;

    std::map<int,FastMapClient*>::iterator itr = clients.find(connData->connectionID);
    if( itr != clients.end() )
    {
      bz_removeNonPlayerConnectionHandler ( itr->second->conID, itr->second );
      delete(itr->second);
      clients.erase(itr);
    }
    bz_disconectNonPlayerConnection ( connData->connectionID );
 }
}

void FastMapEventHandler::updateHTTPServer ( void )
{
  std::vector<int> killList;

  std::map<int,FastMapClient*>::iterator itr = clients.begin();
  while (itr != clients.end())
  {
    if(itr->second->conID == -1 || !itr->second->updateTransfer() )
      killList.push_back(itr->first);
    itr++;
  }

  for ( unsigned int i = 0; i < (unsigned int)killList.size(); i++)
    clients.erase(clients.find(killList[i]));

  if (clients.size())
    bz_setMaxWaitTime(0.001f);
  else
    bz_setMaxWaitTime(2.0f);
}

void FastMapEventHandler::killHTTPServer ( void )
{
  if ( mapData )
    free(mapData);

  mapData = NULL;
  mapDataSize = 0;
}

FastMapClient::FastMapClient( int connectionID, FastMapEventHandler *h )
{
  conID = connectionID;

  data = NULL;
  size = 0;
  currerntPos = 0;
  handler = h;
}

FastMapClient::~FastMapClient()
{
  if (data)
    free(data);
}

void FastMapClient::pending ( int connectionID, void *s, unsigned int d )
{
  if ( connectionID != conID )
    return;

  char *c = (char*)malloc(d+1);
  memcpy(c,s,d);
  c[d] = 0;
  command += c;
  free (c);

  // parse the command
  std::vector<std::string> commandList = tokenize(command,std::string("\r\n"),0,false);

  if ( commandList.size() == 1 && commandList[0].size() == command.size() ) // there were no delims, we are done.
    return;

  for (unsigned int i = 0; i < (unsigned int)commandList.size();i++)
  {
    command.erase(command.begin(),command.begin()+commandList[i].size());

    std::string thisLine = commandList[i];

    if (strncmp(thisLine.c_str(),"GET",3) == 0 && !data) // it's a get and we arn't in a transfer
    {
      std::vector<std::string> params = tokenize(thisLine,std::string(" "),0,false);
      if ( params.size() <= 3 )
      {
	std::string urlPath = params[1];

	if (urlPath.size()>2 && handler->mapName == urlPath.c_str()+1 )
	{
	  startTransfer(handler->mapData,handler->mapDataSize);
	}
      }
    }
  }
}

void FastMapClient::disconnect ( int connectionID )
{
  if (connectionID != conID)
    return;

  bz_removeNonPlayerConnectionHandler ( conID, this );
  bz_disconectNonPlayerConnection ( conID );
  conID = -1;

}
void FastMapClient::startTransfer ( unsigned char * d, unsigned int s )
{
  if (data)
    free (data);
  data = NULL;
  size = 0;
  currerntPos = 0;

  if (!s)
    return;

  data = (unsigned char*)malloc(s);
  memcpy(data,d,s);
  size = s;

  std::string httpHeaders;
  httpHeaders += "HTTP/1.1 200 OK\n";
  httpHeaders += format("Content-Length: %d\n", size);
  httpHeaders += "Connection: close\n";
  httpHeaders += "Content-Type: application/octet-stream\n";
  httpHeaders += "\n";

  if (!bz_sendNonPlayerData ( conID, httpHeaders.c_str(), httpHeaders.size() ) || !updateTransfer())
    disconnect(conID);
}

bool FastMapClient::updateTransfer ( void )
{
  int chunkToSend = 1000;

  if ( currerntPos <= size )
    return false;

  // wait till the current data is sent
  if (bz_getNonPlayerConnectionOutboundPacketCount(conID) != 0)
    return true;

  if ( currerntPos + chunkToSend > size)
    chunkToSend = size-currerntPos;

  bool worked = bz_sendNonPlayerData ( conID, data+currerntPos, chunkToSend );

  currerntPos += chunkToSend;

  return worked;
}

// Local Variables: ***
// mode:C++ ***
// tab-width: 8 ***
// c-basic-offset: 2 ***
// indent-tabs-mode: t ***
// End: ***
// ex: shiftwidth=2 tabstop=8
<|endoftext|>