<commit_before>#include <UnitTest++/src/UnitTest++.h>
#include "rdestl/fixed_vector.h"

namespace
{
	typedef rde::fixed_vector<int, 64, true>			tTestVector;
	typedef rde::fixed_vector<std::string, 64, true>	tStringVector;

	const int array [] = { 1, 4, 9, 16, 25, 36 };

	void PrintVector(const tStringVector& v)
	{
		for (tStringVector::const_iterator it = v.begin(); it != v.end(); ++it)
			printf("%s, ", it->c_str());
		printf("\n");
	}

	TEST(DefaultCtorEmpty)
	{
		tTestVector v;
		CHECK(v.empty());
		CHECK_EQUAL(0, v.size());
		CHECK_EQUAL(v.begin(), v.end());
		printf("Sizeof(v) = %d\n", sizeof(v));
	}
	TEST(PushBack)
	{
		tTestVector v;
		v.push_back(2);
		CHECK_EQUAL(1, v.size());
		CHECK_EQUAL(2, v[0]);
		CHECK(!v.empty());
		CHECK(v.begin() != v.end());
	}
	TEST(PopBack)
	{
		tTestVector v;
		v.push_back(2);
		v.push_back(3);
		CHECK_EQUAL(2, v.front());
		CHECK_EQUAL(3, v.back());
		v.pop_back();
		CHECK(!v.empty());
		CHECK_EQUAL(1, v.size());
		CHECK_EQUAL(2, v[0]);
		tTestVector::const_iterator it = v.begin();
		++it;
		CHECK_EQUAL(v.end(), it);
	}
	TEST(AssignTab)
	{
		tTestVector v;
		CHECK(v.empty());
		v.assign(array, array + 6);
		CHECK_EQUAL(6, v.size());
		CHECK_EQUAL(0, memcmp(array, v.data(), sizeof(array)));
	}
	TEST(AssignTabConstruct)
	{
		tTestVector v(array, array + 6);
		CHECK_EQUAL(6, v.size());
#if RDESTL_RECORD_WATERMARKS
		CHECK_EQUAL(6, v.get_high_watermark());
#endif
		CHECK_EQUAL(0, memcmp(array, v.data(), sizeof(array)));
	}

	TEST(Clear)
	{
		tTestVector v(array, array + 6);
		CHECK_EQUAL(6, v.size());
		v.clear();
		CHECK_EQUAL(0, v.size());
		CHECK(v.empty());
		// Make sure it doesnt free mem.
		CHECK(v.capacity() > 0);
	}

	TEST(EraseBegin)
	{
		tTestVector v(array, array + 6);
		v.erase(v.begin());
		// 4, 9, 16, 25, 36
		CHECK_EQUAL(4, v.front());
	}
	TEST(Erase)
	{
		tTestVector v(array, array + 6);

		// Remove 4 & 9
		tTestVector::iterator it = v.begin();
		it += 1;
		tTestVector::iterator it2 = it;
		it2 += 2;
		v.erase(it, it2);
		CHECK_EQUAL(4, v.size());
		CHECK_EQUAL(1, v.front());
		CHECK_EQUAL(16, v[1]);
		CHECK_EQUAL(25, v[2]);
		CHECK_EQUAL(36, v[3]);
	}
	TEST(EraseSingle)
	{
		tTestVector v(array, array + 6);
		tTestVector::iterator it = v.erase(v.begin() + 1); // 4
		CHECK_EQUAL(9, *it);
		v.erase(it + 1); // 16

		CHECK_EQUAL(4, v.size());
		//PrintVector(v);
	}
	TEST(EraseEnd)
	{
		tTestVector v(array, array + 6);
		tTestVector::iterator it = v.begin();
		it += 2;
		v.erase(it, v.end());
		CHECK_EQUAL(2, v.size());
	}
	TEST(EraseAll)
	{
		tTestVector v(array, array + 6);
		v.erase(v.begin(), v.end());
		CHECK(v.empty());

		//rde::vector<MyStruct> v2;
		//v2.erase(v2.begin(), v2.end());
	}

	TEST(InsertString)
	{
		tStringVector v;
		v.push_back("brave");
		v.push_back("new");
		v.push_back("world");
		v.insert(0, 1, "Hello");
		CHECK_EQUAL(4, v.size());
		v.insert(4, 3, ".");
		CHECK_EQUAL(7, v.size());
		PrintVector(v);
		v.insert(1, 10, "very");
		CHECK_EQUAL(17, v.size());
		PrintVector(v);
	}

	TEST(EraseString)
	{
		tStringVector v;
		v.push_back("Hello");
		v.push_back("brave");
		v.push_back("new");
		v.push_back("world");
		CHECK_EQUAL(4, v.size());
		v.erase(v.begin() + 1, v.end() - 1);
		CHECK_EQUAL(2, v.size());
	}
	TEST(IndexOf)
	{
		tStringVector v;
		v.push_back("Hello");
		v.push_back("brave");
		v.push_back("new");
		v.push_back("world");
		CHECK_EQUAL(2, v.index_of("new"));
		CHECK_EQUAL(tStringVector::npos, v.index_of("blah"));
		CHECK_EQUAL(tStringVector::npos, v.index_of("brave", 2));
	}

	TEST(InsertFront)
	{
		tTestVector v(array, array + 6);
		v.insert(v.begin(), 2, -1);
		CHECK_EQUAL(8, v.size());
		CHECK_EQUAL(-1, v.front());
		CHECK_EQUAL(-1, v[1]);
		CHECK_EQUAL(1, v[2]);
		CHECK_EQUAL(4, v[3]);
		CHECK_EQUAL(36, v[7]);
	}
	TEST(InsertMiddle)
	{
		tTestVector v(array, array + 6);
		tTestVector::iterator it = v.begin() + 2;
		v.insert(it, 3, 5);
		CHECK_EQUAL(9, v.size());
		CHECK_EQUAL(5, v[2]);
		it = v.begin() + 2;
		v.insert(it, 4);
		CHECK_EQUAL(4, v[2]);
	}
	TEST(InsertEnd)
	{
		tTestVector v(array, array + 6);
		v.insert(v.end(), 1, 49);
		CHECK_EQUAL(7, v.size());
		CHECK_EQUAL(49, v[6]);
	}
	TEST(InsertToEmpty)
	{
		tTestVector v;
		v.insert(v.begin(), 1, 2);
		CHECK_EQUAL(1, v.size());
		CHECK_EQUAL(2, v.front());
	}

	TEST(ResizeSmaller)
	{
		tTestVector v(array, array + 6);
		v.resize(4);
		CHECK_EQUAL(4, v.size());
		CHECK_EQUAL(16, v[3]);
	}

	TEST(CopyConstructor)
	{
		tTestVector v(array, array + 6);
		tTestVector v2(v);
		CHECK_EQUAL(6, v2.size());
		CHECK(memcmp(v.begin(), v2.begin(), 6*sizeof(int)) == 0);
	}

	TEST(AssignmentOp)
	{
		tTestVector v(array, array + 6);
		tTestVector v2;
		v2 = v;
		CHECK_EQUAL(6, v2.size());
		CHECK(memcmp(v.begin(), v2.begin(), 6*sizeof(int)) == 0);
	}

	TEST(Reserve)
	{
		tTestVector v;
		CHECK_EQUAL(64, v.capacity());
		v.push_back(1); v.push_back(2); v.push_back(3);
		v.reserve(120);
		CHECK(v.capacity() >= 120);
		CHECK_EQUAL(3, v.size());
		CHECK_EQUAL(1, v[0]);
		CHECK_EQUAL(2, v[1]);
		CHECK_EQUAL(3, v[2]);
	}
#if RDESTL_RECORD_WATERMARKS
	TEST(Watermarks)
	{
		tTestVector v;
		CHECK_EQUAL(0, v.get_high_watermark());
		v.push_back(1);
		CHECK_EQUAL(1, v.get_high_watermark());
		v.push_back(2); v.push_back(3); v.push_back(4);
		CHECK_EQUAL(4, v.get_high_watermark());
		v.pop_back();
		CHECK_EQUAL(4, v.get_high_watermark());
		v.clear();
		CHECK_EQUAL(4, v.get_high_watermark());
	}
#endif
#if !RDESTL_STANDALONE && RDE_DEBUG
	int numFailedAssertions(0);
	bool AssertionHandler(const char*, const char*, int)
	{
		++numFailedAssertions;
		return true;
	}
	TEST(AssertOnOverflow)
	{
		rde::fixed_vector<int, 10, false> v;
		rde::Assertion::Handler prevHandler = rde::Assertion::SetHandler(AssertionHandler);
		for (int i = 0; i < 11; ++i)
			v.push_back(i);
		CHECK_EQUAL(1, numFailedAssertions);
		rde::Assertion::SetHandler(prevHandler);
	}
#endif
}

<commit_msg>alignment test<commit_after>#include <UnitTest++/src/UnitTest++.h>
#include "rdestl/fixed_vector.h"
#include <xmmintrin.h>

namespace
{
	typedef rde::fixed_vector<int, 64, true>			tTestVector;
	typedef rde::fixed_vector<std::string, 64, true>	tStringVector;

	const int array [] = { 1, 4, 9, 16, 25, 36 };

	void PrintVector(const tStringVector& v)
	{
		for (tStringVector::const_iterator it = v.begin(); it != v.end(); ++it)
			printf("%s, ", it->c_str());
		printf("\n");
	}

	TEST(NonStandardAlign)
	{
		rde::fixed_vector<__m128, 64, false> v;
		CHECK_EQUAL(16, rde::alignof<__m128>::res);
		CHECK(v.empty());
		CHECK_EQUAL(0, v.size());
		CHECK_EQUAL(v.begin(), v.end());
	}

	TEST(DefaultCtorEmpty)
	{
		tTestVector v;
		CHECK(v.empty());
		CHECK_EQUAL(0, v.size());
		CHECK_EQUAL(v.begin(), v.end());
		printf("Sizeof(v) = %d\n", sizeof(v));
	}
	TEST(PushBack)
	{
		tTestVector v;
		v.push_back(2);
		CHECK_EQUAL(1, v.size());
		CHECK_EQUAL(2, v[0]);
		CHECK(!v.empty());
		CHECK(v.begin() != v.end());
	}
	TEST(PopBack)
	{
		tTestVector v;
		v.push_back(2);
		v.push_back(3);
		CHECK_EQUAL(2, v.front());
		CHECK_EQUAL(3, v.back());
		v.pop_back();
		CHECK(!v.empty());
		CHECK_EQUAL(1, v.size());
		CHECK_EQUAL(2, v[0]);
		tTestVector::const_iterator it = v.begin();
		++it;
		CHECK_EQUAL(v.end(), it);
	}
	TEST(AssignTab)
	{
		tTestVector v;
		CHECK(v.empty());
		v.assign(array, array + 6);
		CHECK_EQUAL(6, v.size());
		CHECK_EQUAL(0, memcmp(array, v.data(), sizeof(array)));
	}
	TEST(AssignTabConstruct)
	{
		tTestVector v(array, array + 6);
		CHECK_EQUAL(6, v.size());
#if RDESTL_RECORD_WATERMARKS
		CHECK_EQUAL(6, v.get_high_watermark());
#endif
		CHECK_EQUAL(0, memcmp(array, v.data(), sizeof(array)));
	}

	TEST(Clear)
	{
		tTestVector v(array, array + 6);
		CHECK_EQUAL(6, v.size());
		v.clear();
		CHECK_EQUAL(0, v.size());
		CHECK(v.empty());
		// Make sure it doesnt free mem.
		CHECK(v.capacity() > 0);
	}

	TEST(EraseBegin)
	{
		tTestVector v(array, array + 6);
		v.erase(v.begin());
		// 4, 9, 16, 25, 36
		CHECK_EQUAL(4, v.front());
	}
	TEST(Erase)
	{
		tTestVector v(array, array + 6);

		// Remove 4 & 9
		tTestVector::iterator it = v.begin();
		it += 1;
		tTestVector::iterator it2 = it;
		it2 += 2;
		v.erase(it, it2);
		CHECK_EQUAL(4, v.size());
		CHECK_EQUAL(1, v.front());
		CHECK_EQUAL(16, v[1]);
		CHECK_EQUAL(25, v[2]);
		CHECK_EQUAL(36, v[3]);
	}
	TEST(EraseSingle)
	{
		tTestVector v(array, array + 6);
		tTestVector::iterator it = v.erase(v.begin() + 1); // 4
		CHECK_EQUAL(9, *it);
		v.erase(it + 1); // 16

		CHECK_EQUAL(4, v.size());
		//PrintVector(v);
	}
	TEST(EraseEnd)
	{
		tTestVector v(array, array + 6);
		tTestVector::iterator it = v.begin();
		it += 2;
		v.erase(it, v.end());
		CHECK_EQUAL(2, v.size());
	}
	TEST(EraseAll)
	{
		tTestVector v(array, array + 6);
		v.erase(v.begin(), v.end());
		CHECK(v.empty());

		//rde::vector<MyStruct> v2;
		//v2.erase(v2.begin(), v2.end());
	}

	TEST(InsertString)
	{
		tStringVector v;
		v.push_back("brave");
		v.push_back("new");
		v.push_back("world");
		v.insert(0, 1, "Hello");
		CHECK_EQUAL(4, v.size());
		v.insert(4, 3, ".");
		CHECK_EQUAL(7, v.size());
		PrintVector(v);
		v.insert(1, 10, "very");
		CHECK_EQUAL(17, v.size());
		PrintVector(v);
	}

	TEST(EraseString)
	{
		tStringVector v;
		v.push_back("Hello");
		v.push_back("brave");
		v.push_back("new");
		v.push_back("world");
		CHECK_EQUAL(4, v.size());
		v.erase(v.begin() + 1, v.end() - 1);
		CHECK_EQUAL(2, v.size());
	}
	TEST(IndexOf)
	{
		tStringVector v;
		v.push_back("Hello");
		v.push_back("brave");
		v.push_back("new");
		v.push_back("world");
		CHECK_EQUAL(2, v.index_of("new"));
		CHECK_EQUAL(tStringVector::npos, v.index_of("blah"));
		CHECK_EQUAL(tStringVector::npos, v.index_of("brave", 2));
	}

	TEST(InsertFront)
	{
		tTestVector v(array, array + 6);
		v.insert(v.begin(), 2, -1);
		CHECK_EQUAL(8, v.size());
		CHECK_EQUAL(-1, v.front());
		CHECK_EQUAL(-1, v[1]);
		CHECK_EQUAL(1, v[2]);
		CHECK_EQUAL(4, v[3]);
		CHECK_EQUAL(36, v[7]);
	}
	TEST(InsertMiddle)
	{
		tTestVector v(array, array + 6);
		tTestVector::iterator it = v.begin() + 2;
		v.insert(it, 3, 5);
		CHECK_EQUAL(9, v.size());
		CHECK_EQUAL(5, v[2]);
		it = v.begin() + 2;
		v.insert(it, 4);
		CHECK_EQUAL(4, v[2]);
	}
	TEST(InsertEnd)
	{
		tTestVector v(array, array + 6);
		v.insert(v.end(), 1, 49);
		CHECK_EQUAL(7, v.size());
		CHECK_EQUAL(49, v[6]);
	}
	TEST(InsertToEmpty)
	{
		tTestVector v;
		v.insert(v.begin(), 1, 2);
		CHECK_EQUAL(1, v.size());
		CHECK_EQUAL(2, v.front());
	}

	TEST(ResizeSmaller)
	{
		tTestVector v(array, array + 6);
		v.resize(4);
		CHECK_EQUAL(4, v.size());
		CHECK_EQUAL(16, v[3]);
	}

	TEST(CopyConstructor)
	{
		tTestVector v(array, array + 6);
		tTestVector v2(v);
		CHECK_EQUAL(6, v2.size());
		CHECK(memcmp(v.begin(), v2.begin(), 6*sizeof(int)) == 0);
	}

	TEST(AssignmentOp)
	{
		tTestVector v(array, array + 6);
		tTestVector v2;
		v2 = v;
		CHECK_EQUAL(6, v2.size());
		CHECK(memcmp(v.begin(), v2.begin(), 6*sizeof(int)) == 0);
	}

	TEST(Reserve)
	{
		tTestVector v;
		CHECK_EQUAL(64, v.capacity());
		v.push_back(1); v.push_back(2); v.push_back(3);
		v.reserve(120);
		CHECK(v.capacity() >= 120);
		CHECK_EQUAL(3, v.size());
		CHECK_EQUAL(1, v[0]);
		CHECK_EQUAL(2, v[1]);
		CHECK_EQUAL(3, v[2]);
	}
#if RDESTL_RECORD_WATERMARKS
	TEST(Watermarks)
	{
		tTestVector v;
		CHECK_EQUAL(0, v.get_high_watermark());
		v.push_back(1);
		CHECK_EQUAL(1, v.get_high_watermark());
		v.push_back(2); v.push_back(3); v.push_back(4);
		CHECK_EQUAL(4, v.get_high_watermark());
		v.pop_back();
		CHECK_EQUAL(4, v.get_high_watermark());
		v.clear();
		CHECK_EQUAL(4, v.get_high_watermark());
	}
#endif
#if !RDESTL_STANDALONE && RDE_DEBUG
	int numFailedAssertions(0);
	bool AssertionHandler(const char*, const char*, int)
	{
		++numFailedAssertions;
		return true;
	}
	TEST(AssertOnOverflow)
	{
		rde::fixed_vector<int, 10, false> v;
		rde::Assertion::Handler prevHandler = rde::Assertion::SetHandler(AssertionHandler);
		for (int i = 0; i < 11; ++i)
			v.push_back(i);
		CHECK_EQUAL(1, numFailedAssertions);
		rde::Assertion::SetHandler(prevHandler);
	}
#endif
}

<|endoftext|>


<commit_before>#include "Hardware.h"

#include <unistd.h>
#include <stdlib.h>
#include <iostream>

#define I_MIN_SAFE_ANG -65
#define I_MAX_SAFE_ANG 65

#define J_MIN_SAFE_ANG 35
#define J_MAX_SAFE_ANG 180

#define K_MIN_SAFE_ANG 0
#define K_MAX_SAFE_ANG 90

double randOutput() {
	return ((double) rand() / (RAND_MAX))*2.0 - 1.0;
}

bool executeScaledAngles(Hardware *hardware, double i, double j, double k) {
	double iAng = Hardware::map(i, -1, 1, I_MIN_SAFE_ANG, I_MAX_SAFE_ANG);
	double jAng = Hardware::map(j, -1, 1, J_MIN_SAFE_ANG, J_MAX_SAFE_ANG);
	double kAng = Hardware::map(k, -1, 1, K_MIN_SAFE_ANG, K_MAX_SAFE_ANG);

	return hardware->setJoints(iAng, jAng, kAng);
}

void stressTest(Hardware *hardware, int trials) {
	for (int i=0; i<trials; i++) {
		if (executeScaledAngles(hardware,randOutput(),randOutput(),randOutput())) {
			std::cout << "Move executed.\n";
		} else {
			std::cout << "Move fail-safed.\n";
		} usleep(3000000);
	}
}

void testSonars(Hardware *hardware, int trials) {
	for (int i=0; i<trials; i++) {
		int l, r; hardware->getSonars(&l, &r);
		std::cout << "Sonars: (" << l << ", " << r << ")\n";
		std::cout.flush();
	}
}

int main() {
	srand(time(NULL));
	Hardware hand;
	hand.poise();

	int THRESH = 300;
	int NUM_SONAR_READINGS = 3;

	while (true) {
		int l, r;
		for (int j=0; j<NUM_SONAR_READINGS; j++) {
			int tempL, tempR; hand.getSonars(&tempL, &tempR);
			l += tempL; r += tempR;
		} l /= NUM_SONAR_READINGS; r /= NUM_SONAR_READINGS;
		std::cout << "Sonars: (" << l << "," << r << ")\n";

		int delay = 0;
		if (abs(r-l) > THRESH) {
			i = ((r > l) ? -30:30);
			delay = 500000;
		} else i = 0;

		hand.setJoints(i, 180, 40, true);
		usleep(delay);
	}
}
<commit_msg>modify invection of dynamic convets<commit_after>#include "Hardware.h"

#include <unistd.h>
#include <stdlib.h>
#include <iostream>

#define I_MIN_SAFE_ANG -65
#define I_MAX_SAFE_ANG 65

#define J_MIN_SAFE_ANG 35
#define J_MAX_SAFE_ANG 180

#define K_MIN_SAFE_ANG 0
#define K_MAX_SAFE_ANG 90

double randOutput() {
	return ((double) rand() / (RAND_MAX))*2.0 - 1.0;
}

bool executeScaledAngles(Hardware *hardware, double i, double j, double k) {
	double iAng = Hardware::map(i, -1, 1, I_MIN_SAFE_ANG, I_MAX_SAFE_ANG);
	double jAng = Hardware::map(j, -1, 1, J_MIN_SAFE_ANG, J_MAX_SAFE_ANG);
	double kAng = Hardware::map(k, -1, 1, K_MIN_SAFE_ANG, K_MAX_SAFE_ANG);

	return hardware->setJoints(iAng, jAng, kAng);
}

void stressTest(Hardware *hardware, int trials) {
	for (int i=0; i<trials; i++) {
		if (executeScaledAngles(hardware,randOutput(),randOutput(),randOutput())) {
			std::cout << "Move executed.\n";
		} else {
			std::cout << "Move fail-safed.\n";
		} usleep(3000000);
	}
}

void testSonars(Hardware *hardware, int trials) {
	for (int i=0; i<trials; i++) {
		int l, r; hardware->getSonars(&l, &r);
		std::cout << "Sonars: (" << l << ", " << r << ")\n";
		std::cout.flush();
	}
}

int main() {
	srand(time(NULL));
	Hardware hand;
	hand.poise();

	int THRESH = 300;
	int NUM_SONAR_READINGS = 3;

	while (true) {
		int l, r;
		for (int j=0; j<NUM_SONAR_READINGS; j++) {
			int tempL, tempR; hand.getSonars(&tempL, &tempR);
			l += tempL; r += tempR;
		} l /= NUM_SONAR_READINGS; r /= NUM_SONAR_READINGS;
		std::cout << "Sonars: (" << l << "," << r << ")\n";

		int delay = 0;
		double i = 0;
		if (abs(r-l) > THRESH) {
			i = ((r > l) ? -30:30);
			delay = 500000;
		}

		hand.setJoints(i, 180, 40, true);
		usleep(delay);
	}
}
<|endoftext|>


<commit_before>#include <immintrin.h>

#include <cstdio>
#include <cstdint>
#include <cassert>

__m128i blend_epi32(__m128i cond, __m128i true_val, __m128i false_val) {
#if 0
    const __m128i t0 = _mm_and_si128(cond, true_val);
    const __m128i t1 = _mm_andnot_si128(cond, false_val);

    return _mm_or_si128(t0, t1);
#else
    return (__m128i)_mm_blendv_ps((__m128)false_val, (__m128)true_val, (__m128)cond);
#endif
}

__m128i lzcnt_epu32(__m128i x) {
    // 1. convert int to float
    const __m128  fp = _mm_cvtepi32_ps(x);

    // 2. get the exponent part and the sign bit
    const __m128i t0 = _mm_srli_epi32((__m128i)fp, 23);
    const int fp32bias = 127;
    const __m128i t1 = _mm_sub_epi32(_mm_set1_epi32(fp32bias + 31), t0);

    // 3. We need fixup negative numbers, i.e. the MSB set -- lzcnt is 0
    const __m128i msb_set = _mm_cmplt_epi32(x, _mm_set1_epi32(0));
    const __m128i t2 = blend_epi32(msb_set, _mm_set1_epi32(0), t1);

    // 4. there's also need for fixup for lzcnt(0) = 32
    const __m128i is_zero = _mm_cmpeq_epi32(_mm_set1_epi32(0), x);
    const __m128i t3 = blend_epi32(is_zero, _mm_set1_epi32(32), t2);

    return t3;
}

// --------------------------------------------------

int lzcnt_vec(uint32_t value) {
    const __m128i x = _mm_set1_epi32(value);
    const __m128i r = lzcnt_epu32(x);

    return _mm_extract_epi32(r, 0);
}

int lzcnt_scalar(uint32_t value) {
    if (value == 0)
        return 32;

    int result = 0;
    while ((value & 0x80000000) == 0) {
        result += 1;
        value <<= 1;
    }

    return result;
}

void test(uint32_t value) {
    const int vec = lzcnt_vec(value);
    const int ref = lzcnt_scalar(value);
    printf("lzcnt(%08x) = %d (ref = %d)\n", value, vec, ref);
    assert(vec == ref);
}

int main()
{
    test(0);
    test(1243);
    test(7612545);
    for (int i=0; i < 32; i++) {
        test(uint32_t(1) << i);
    }
}
<commit_msg>One blend less<commit_after>#include <immintrin.h>

#include <cstdio>
#include <cstdint>
#include <cassert>

__m128i blend_epi32(__m128i cond, __m128i true_val, __m128i false_val) {
#if 0
    const __m128i t0 = _mm_and_si128(cond, true_val);
    const __m128i t1 = _mm_andnot_si128(cond, false_val);

    return _mm_or_si128(t0, t1);
#else
    return (__m128i)_mm_blendv_ps((__m128)false_val, (__m128)true_val, (__m128)cond);
#endif
}

__m128i lzcnt_epu32(__m128i x) {
    // 1. convert int to float
    const __m128  fp = _mm_cvtepi32_ps(x);

    // 2. get the exponent part and the sign bit
    const __m128i t0 = _mm_srli_epi32((__m128i)fp, 23);
    const int fp32bias = 127;
    const __m128i t1 = _mm_sub_epi32(_mm_set1_epi32(fp32bias + 31), t0);

    // 3. We need fixup negative numbers, i.e. the MSB set -- lzcnt is 0
    const __m128i msb_set = _mm_cmplt_epi32(x, _mm_set1_epi32(0));
    const __m128i t2 = _mm_andnot_si128(msb_set, t1);

    // 4. there's also need for fixup for lzcnt(0) = 32
    const __m128i is_zero = _mm_cmpeq_epi32(_mm_set1_epi32(0), x);
    const __m128i t3 = blend_epi32(is_zero, _mm_set1_epi32(32), t2);

    return t3;
}

// --------------------------------------------------

int lzcnt_vec(uint32_t value) {
    const __m128i x = _mm_set1_epi32(value);
    const __m128i r = lzcnt_epu32(x);

    return _mm_extract_epi32(r, 0);
}

int lzcnt_scalar(uint32_t value) {
    if (value == 0)
        return 32;

    int result = 0;
    while ((value & 0x80000000) == 0) {
        result += 1;
        value <<= 1;
    }

    return result;
}

void test(uint32_t value) {
    const int vec = lzcnt_vec(value);
    const int ref = lzcnt_scalar(value);
    printf("lzcnt(%08x) = %d (ref = %d)\n", value, vec, ref);
    assert(vec == ref);
}

int main()
{
    test(0);
    test(1243);
    test(7612545);
    for (int i=0; i < 32; i++) {
        test(uint32_t(1) << i);
    }
}
<|endoftext|>


<commit_before>#include <stddef.h>
#include <stdint.h>

/* Check if the compiler thinks if we are targeting the wrong operating system. */
#if defined(__linux__)
#error "You are not using a cross-compiler, you will most certainly run into trouble"
#endif

/* For now, it only work for the 32-bit x86 targets. */
#if !defined(__i386__)
#error "This tutorial needs to be compiled with a ix86-elf compiler"
#endif

/* Hardware text mode color constants. */
enum vga_color
{
    COLOR_BLACK = 0,
    COLOR_BLUE = 1,
    COLOR_GREEN = 2,
    COLOR_CYAN = 3,
    COLOR_RED = 4,
    COLOR_MAGENTA = 5,
    COLOR_BROWN = 6,
    COLOR_LIGHT_GREY = 7,
    COLOR_DARK_GREY = 8,
    COLOR_LIGHT_BLUE = 9,
    COLOR_LIGHT_GREEN = 10,
    COLOR_LIGHT_CYAN = 11,
    COLOR_LIGHT_RED = 12,
    COLOR_LIGHT_MAGENTA = 13,
    COLOR_LIGHT_BROWN = 14,
    COLOR_WHITE = 15,
};

/* make_color
 * ----------
 *
 * Create a 8 bytes color for vga input.
 *
 * Merge foreground and background color in a single 8 bytes variables.
 *
 * bytes :  1 | 2 | 3 | 4 | 5 | 6 | 7 | 8
 * values: bg |bg |bg |bg |fg |fg |fg |fg
 */
uint8_t make_color(enum vga_color fg, enum vga_color bg)
{
    return fg | bg << 4;
}

/* make_vgaentry
 * -------------
 *
 * Create a 16 bytes word for vga test input.
 *
 * Merge color and text in a single 16 bytes variables.
 *
 * bytes : 1 ... 8 | 9 ... 16
 * values: colors  | texts
 *
 */
uint16_t make_vgaentry(char c, uint8_t color)
{
    uint16_t c16 = c;
    uint16_t color16 = color;
    return c16 | color16 << 8;
}

//Default size for VGA buffer
static const size_t VGA_WIDTH = 80;
static const size_t VGA_HEIGHT = 25;

// Current row location
size_t terminal_row;
// Current column location
size_t terminal_column;
// Current color
uint8_t terminal_color;
// VGA buffer to write text
uint16_t* terminal_buffer;

/*terminal_initialize
 *-------------------
 *
 * Initialize terminal setting cursor at upper right corner and setting
 * the default color. Fill the VGA buffer with space as it could be
 * unititialize.
 */
void terminal_initialize()
{
    terminal_row = 0;
    terminal_column = 0;
    terminal_color = make_color(COLOR_LIGHT_GREY, COLOR_BLACK);
    // hardware VGA buffer location
    terminal_buffer = (uint16_t*) 0xB8000;
    for(size_t y = 0; y < VGA_HEIGHT; y++)
    {
        for(size_t x = 0; x < VGA_WIDTH; x++)
        {
            const size_t index = y * VGA_WIDTH + x;
            terminal_buffer[index] = make_vgaentry(' ', terminal_color);
        }
    }
}

/*terminal_putentryat
 *-------------------
 *
 * Write a char in the terminal.
 */
void terminal_putentryat(char c, uint8_t color, size_t x, size_t y)
{
    const size_t index = y * VGA_WIDTH + x;
    terminal_buffer[index] = make_vgaentry(c, color);
}

/*terminal_putchar
 *----------------
 *
 * Write a char in the terminal and move cursor accordingly
 */
void terminal_putchar(char c)
{
    if(c == '\n')
    {
        terminal_column = 0;
        if(++terminal_row == VGA_HEIGHT)
        {
            terminal_row = 0;
        }
    } else {
        terminal_putentryat(c, terminal_color, terminal_column, terminal_row);
        if(++terminal_column == VGA_WIDTH)
        {
            terminal_column = 0;
            if(++terminal_row == VGA_HEIGHT)
            {
                terminal_row = 0;
            }
        }
    }
}

/*terminal_writestring
 *--------------------
 *
 * Write the given string in the terminal.
 */
void terminal_writestring(const char* data)
{
    size_t i = 0;
    while(data[i] != 0)
        terminal_putchar(data[i++]);
}

/*kernel_main
 *-----------
 *
 * Startup function for the kernel.
 */
extern "C"
void kernel_main()
{
    terminal_initialize();
    for(size_t i = 0; i < 25; i++)
    {
        terminal_writestring("Hello World!\n");
    }
    terminal_writestring("Hello, kernel World!");
}
<commit_msg>Add scrolling in terminal<commit_after>#include <stddef.h>
#include <stdint.h>

/* Check if the compiler thinks if we are targeting the wrong operating system. */
#if defined(__linux__)
#error "You are not using a cross-compiler, you will most certainly run into trouble"
#endif

/* For now, it only work for the 32-bit x86 targets. */
#if !defined(__i386__)
#error "This tutorial needs to be compiled with a ix86-elf compiler"
#endif

/* Hardware text mode color constants. */
enum vga_color
{
    COLOR_BLACK = 0,
    COLOR_BLUE = 1,
    COLOR_GREEN = 2,
    COLOR_CYAN = 3,
    COLOR_RED = 4,
    COLOR_MAGENTA = 5,
    COLOR_BROWN = 6,
    COLOR_LIGHT_GREY = 7,
    COLOR_DARK_GREY = 8,
    COLOR_LIGHT_BLUE = 9,
    COLOR_LIGHT_GREEN = 10,
    COLOR_LIGHT_CYAN = 11,
    COLOR_LIGHT_RED = 12,
    COLOR_LIGHT_MAGENTA = 13,
    COLOR_LIGHT_BROWN = 14,
    COLOR_WHITE = 15,
};

/* make_color
 * ----------
 *
 * Create a 8 bytes color for vga input.
 *
 * Merge foreground and background color in a single 8 bytes variables.
 *
 * bytes :  1 | 2 | 3 | 4 | 5 | 6 | 7 | 8
 * values: bg |bg |bg |bg |fg |fg |fg |fg
 */
uint8_t make_color(enum vga_color fg, enum vga_color bg)
{
    return fg | bg << 4;
}

/* make_vgaentry
 * -------------
 *
 * Create a 16 bytes word for vga test input.
 *
 * Merge color and text in a single 16 bytes variables.
 *
 * bytes : 1 ... 8 | 9 ... 16
 * values: colors  | texts
 *
 */
uint16_t make_vgaentry(char c, uint8_t color)
{
    uint16_t c16 = c;
    uint16_t color16 = color;
    return c16 | color16 << 8;
}

//Default size for VGA buffer
static const size_t VGA_WIDTH = 80;
static const size_t VGA_HEIGHT = 25;

// Current row location
size_t terminal_row;
// Current column location
size_t terminal_column;
// Current color
uint8_t terminal_color;
// VGA buffer to write text
uint16_t* terminal_buffer;

/*terminal_initialize
 *-------------------
 *
 * Initialize terminal setting cursor at upper right corner and setting
 * the default color. Fill the VGA buffer with space as it could be
 * unititialize.
 */
void terminal_initialize()
{
    terminal_row = 0;
    terminal_column = 0;
    terminal_color = make_color(COLOR_LIGHT_GREY, COLOR_BLACK);
    // hardware VGA buffer location
    terminal_buffer = (uint16_t*) 0xB8000;
    for(size_t y = 0; y < VGA_HEIGHT; y++)
    {
        for(size_t x = 0; x < VGA_WIDTH; x++)
        {
            const size_t index = y * VGA_WIDTH + x;
            terminal_buffer[index] = make_vgaentry(' ', terminal_color);
        }
    }
}

/*terminal_putentryat
 *-------------------
 *
 * Write a char in the terminal.
 */
void terminal_putentryat(char c, uint8_t color, size_t x, size_t y)
{
    const size_t index = y * VGA_WIDTH + x;
    terminal_buffer[index] = make_vgaentry(c, color);
}

/*terminal_scrollup
 *-----------------
 *
 * Scroll text up for one line in the terminal.
 */
void terminal_scrollup()
{
    for(size_t y = 1; y < VGA_HEIGHT; y++)
    {
        for(size_t x = 0; x < VGA_WIDTH; x++)
        {
            const size_t index = y * VGA_WIDTH + x;
            terminal_buffer[index] = terminal_buffer[index - VGA_WIDTH];
        }
    }
}

/*terminal_putchar
 *----------------
 *
 * Write a char in the terminal and move cursor and view accordingly
 */
void terminal_putchar(char c)
{
    if(c == '\n')
    {
        terminal_column = 0;
        if(++terminal_row == VGA_HEIGHT)
        {
            terminal_row--;
            terminal_scrollup();
        }
    } else {
        terminal_putentryat(c, terminal_color, terminal_column, terminal_row);
        if(++terminal_column == VGA_WIDTH)
        {
            terminal_column = 0;
            if(++terminal_row == VGA_HEIGHT)
            {
                terminal_row--;
                terminal_scrollup();
            }
        }
    }
}

/*terminal_writestring
 *--------------------
 *
 * Write the given string in the terminal.
 */
void terminal_writestring(const char* data)
{
    size_t i = 0;
    while(data[i] != 0)
        terminal_putchar(data[i++]);
}

/*kernel_main
 *-----------
 *
 * Startup function for the kernel.
 */
extern "C"
void kernel_main()
{
    terminal_initialize();
    for(size_t i = 0; i < 25; i++)
    {
        terminal_writestring("Hello World!\n");
    }
    terminal_writestring("Hello, kernel World!");
}
<|endoftext|>


<commit_before>// Copyright 2018 The clvk authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#include <algorithm>

#include "memory.hpp"
#include "kernel.hpp"

VkPipeline cvk_kernel_pipeline_cache::get_pipeline(uint32_t x, uint32_t y, uint32_t z) {
    VkPipeline ret = VK_NULL_HANDLE;

    m_lock.lock();
    for (auto &entry : m_entries) {
        if ((entry.lws[0] == x) && (entry.lws[1] == y) && (entry.lws[2] == z)) {
            ret = entry.pipeline;
            break;
        }
    }

    if (ret == VK_NULL_HANDLE) {
        ret = create_and_insert_pipeline(x, y, z);
    }
    m_lock.unlock();
    return ret;
}

VkPipeline cvk_kernel_pipeline_cache::create_and_insert_pipeline(uint32_t x, uint32_t y, uint32_t z) {
    uint32_t lws[3] = {x,y,z};

    VkSpecializationMapEntry mapEntries[3] = {
        {0, 0 * sizeof(uint32_t), sizeof(uint32_t)},
        {1, 1 * sizeof(uint32_t), sizeof(uint32_t)},
        {2, 2 * sizeof(uint32_t), sizeof(uint32_t)},
    };

    VkSpecializationInfo specialiaztionInfo = {
        3,
        mapEntries,
        sizeof(lws),
        &lws,
    };

    const VkComputePipelineCreateInfo createInfo = {
        VK_STRUCTURE_TYPE_COMPUTE_PIPELINE_CREATE_INFO, // sType
        nullptr, // pNext
        0, // flags
        {
            VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO, // sType
            nullptr, // pNext
            0, // flags
            VK_SHADER_STAGE_COMPUTE_BIT, // stage
            m_kernel->program()->shader_module(), // module
            m_kernel->name().c_str(),
            &specialiaztionInfo // pSpecializationInfo
        }, // stage
        m_kernel->pipeline_layout(), // layout
        VK_NULL_HANDLE, // basePipelineHandle
        0 // basePipelineIndex
    };

    VkPipeline pipeline;
    auto vkdev = m_device->vulkan_device();
    VkResult res = vkCreateComputePipelines(vkdev, VK_NULL_HANDLE, 1, &createInfo, nullptr, &pipeline);

    if (res != VK_SUCCESS) {
        cvk_error_fn("Could not create compute pipeline: %s", vulkan_error_string(res));
        return VK_NULL_HANDLE;
    }

    insert_pipeline(x, y, z, pipeline);

    return pipeline;
}

void cvk_kernel::build_descriptor_sets_layout_bindings()
{
    bool pod_found = false;

    for (auto &arg : m_args) {
        VkDescriptorType dt = VK_DESCRIPTOR_TYPE_MAX_ENUM;

        switch (arg.kind) {
        case kernel_argument_kind::buffer:
            dt = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER;
            break;
        case kernel_argument_kind::buffer_ubo:
            dt = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER;
            break;
        case kernel_argument_kind::ro_image:
            dt = VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE;
            break;
        case kernel_argument_kind::wo_image:
            dt = VK_DESCRIPTOR_TYPE_STORAGE_IMAGE;
            break;
        case kernel_argument_kind::sampler:
            dt = VK_DESCRIPTOR_TYPE_SAMPLER;
            break;
        case kernel_argument_kind::local:
            continue;
        case kernel_argument_kind::pod:
        case kernel_argument_kind::pod_ubo:
            if (!pod_found) {
                if (arg.kind == kernel_argument_kind::pod) {
                    dt = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER;
                } else if (arg.kind == kernel_argument_kind::pod_ubo) {
                    dt = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER;
                }

                m_pod_descriptor_type = dt;

                pod_found = true;
            } else {
                continue;
            }
        }

        VkDescriptorSetLayoutBinding binding = {
            arg.binding, // binding
            dt, // descriptorType
            1, // decriptorCount
            VK_SHADER_STAGE_COMPUTE_BIT, // stageFlags
            nullptr // pImmutableSamplers
        };

        m_layout_bindings.push_back(binding);
    }
}

std::unique_ptr<cvk_buffer> cvk_kernel::allocate_pod_buffer()
{
    cl_int err;
    auto buffer = cvk_buffer::create(m_context, 0, m_pod_buffer_size, nullptr, &err);
    if (err != CL_SUCCESS) {
        return nullptr;
    }

    return buffer;
}

cl_int cvk_kernel::init()
{
    // Get a pointer to the arguments from the program
    auto args = m_program->args_for_kernel(m_name);

    if (args == nullptr) {
        cvk_error("Kernel %s doesn't exist in program", m_name.c_str());
        return CL_INVALID_KERNEL_NAME;
    }

    // Store a sorted copy of the arguments
    m_args = *args;
    std::sort(m_args.begin(), m_args.end(), [](kernel_argument a, kernel_argument b) {
        return a.pos < b.pos;
    });

    // Create Descriptor Sets Layout Bindings
    build_descriptor_sets_layout_bindings();

    // Create Descriptor Sets Layout
    VkDescriptorSetLayoutCreateInfo descriptorSetLayoutCreateInfo = {
        VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO,
        0,
        0,
        static_cast<uint32_t>(m_layout_bindings.size()),
        m_layout_bindings.data()
    };

    auto vkdev = m_context->device()->vulkan_device();

    VkResult res = vkCreateDescriptorSetLayout(vkdev, &descriptorSetLayoutCreateInfo, 0, &m_descriptor_set_layout);

    if (res != VK_SUCCESS) {
        cvk_error("Could not create descriptor set layout");
        return CL_INVALID_VALUE;
    }

    // Do we have POD arguments?
    for (auto &arg : m_args) {
        if (arg.is_pod()) {
            m_has_pod_arguments = true;
        }
    }

    // Init POD arguments
    if (has_pod_arguments()) {

        // Find out POD binding
        for (auto &arg : m_args) {
            if (arg.is_pod()) {
                m_pod_binding = arg.binding;
                break;
            }
        }

        if (m_pod_binding == INVALID_POD_BINDING) {
            return CL_INVALID_PROGRAM;
        }

        // Check we know the POD buffer's descriptor type
        if (m_pod_descriptor_type == VK_DESCRIPTOR_TYPE_MAX_ENUM) {
            return CL_INVALID_PROGRAM;
        }

        // Find how big the POD buffer should be
        int max_offset = 0;
        int max_offset_arg_size = 0;

        for (auto &arg : m_args) {
            if (arg.is_pod() && (arg.offset >= max_offset)) {
                max_offset = arg.offset;
                max_offset_arg_size = arg.size;
            }
        }

        m_pod_buffer_size = max_offset + max_offset_arg_size;
    }

    // Init argument values
    m_argument_values = cvk_kernel_argument_values::create(this);
    if (m_argument_values == nullptr) {
        return CL_OUT_OF_RESOURCES;
    }

    // Create pipeline layout
    VkPipelineLayoutCreateInfo pipelineLayoutCreateInfo = {
        VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO,
        0,
        0,
        1,
        &m_descriptor_set_layout,
        0,
        0
    };

    res = vkCreatePipelineLayout(vkdev, &pipelineLayoutCreateInfo, 0, &m_pipeline_layout);
    if (res != VK_SUCCESS) {
        cvk_error("Could not create pipeline layout.");
        return CL_INVALID_VALUE;
    }

    // Determine number and types of bindings
    std::unordered_map<VkDescriptorType, uint32_t> bindingTypes;
    for (auto &lb : m_layout_bindings) {
        bindingTypes[lb.descriptorType]++;
    }

    std::vector<VkDescriptorPoolSize> poolSizes(bindingTypes.size());

    int bidx = 0;
    for (auto &bt : bindingTypes) {
        poolSizes[bidx].type = bt.first;
        poolSizes[bidx].descriptorCount = bt.second;
        bidx++;
    }

    // Create descriptor pool
    VkDescriptorPoolCreateInfo descriptorPoolCreateInfo = {
        VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO,
        nullptr,
        VK_DESCRIPTOR_POOL_CREATE_FREE_DESCRIPTOR_SET_BIT, // flags
        cvk_kernel::MAX_INSTANCES, // maxSets
        static_cast<uint32_t>(poolSizes.size()), // poolSizeCount
        poolSizes.data(), // pPoolSizes
    };

    res = vkCreateDescriptorPool(vkdev, &descriptorPoolCreateInfo, 0, &m_descriptor_pool);

    if (res != VK_SUCCESS) {
        cvk_error("Could not create descriptor pool.");
        return CL_INVALID_VALUE;
    }

    return CL_SUCCESS;
}

bool cvk_kernel::setup_descriptor_set(VkDescriptorSet *ds,
                                      std::unique_ptr<cvk_kernel_argument_values> &arg_values)
{
    std::lock_guard<std::mutex> lock(m_lock);

    // Allocate descriptor sets
    VkDescriptorSetAllocateInfo descriptorSetAllocateInfo = {
        VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO,
        nullptr,
        m_descriptor_pool,
        1, // descriptorSetCount
        &m_descriptor_set_layout
    };

    auto dev = m_context->device()->vulkan_device();

    VkResult res = vkAllocateDescriptorSets(dev, &descriptorSetAllocateInfo, ds);

    if (res != VK_SUCCESS) {
        cvk_error_fn("could not allocate descriptor sets: %s", vulkan_error_string(res));
        return false;
    }

    // Transfer ownership of the argument values to the command
    arg_values = std::move(m_argument_values);

    // Create a new set, copy the argument values
    m_argument_values = cvk_kernel_argument_values::create(*arg_values.get());
    if (m_argument_values == nullptr) {
        return false;
    }

    // Setup descriptors for POD arguments
    if (has_pod_arguments()) {

        // Update desciptors
        VkDescriptorBufferInfo bufferInfo = {
            arg_values->pod_vulkan_buffer(),
            0, // offset
            VK_WHOLE_SIZE
        };

        VkWriteDescriptorSet writeDescriptorSet = {
            VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET,
            nullptr,
            *ds,
            m_pod_binding, // dstBinding
            0, // dstArrayElement
            1, // descriptorCount
            m_pod_descriptor_type, // descriptorType
            nullptr, // pImageInfo
            &bufferInfo,
            nullptr, // pTexelBufferView
        };
        vkUpdateDescriptorSets(dev, 1, &writeDescriptorSet, 0, nullptr);
    }

    // Setup other descriptors
    for (cl_uint i = 0; i < m_args.size(); i++) {

        auto const &arg = m_args[i];

        switch (arg.kind){

        case kernel_argument_kind::buffer:
        case kernel_argument_kind::buffer_ubo: {
            auto buffer = static_cast<cvk_buffer*>(arg_values->get_arg_value(arg));
            auto vkbuf = buffer->vulkan_buffer();
            cvk_debug_fn("buffer = %p", buffer);
            VkDescriptorBufferInfo bufferInfo = {
                vkbuf,
                0, // offset
                VK_WHOLE_SIZE
            };

            auto descriptor_type = arg.kind == kernel_argument_kind::buffer
                                       ? VK_DESCRIPTOR_TYPE_STORAGE_BUFFER
                                       : VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER;
            VkWriteDescriptorSet writeDescriptorSet = {
                VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET,
                nullptr,
                *ds,
                arg.binding, // dstBinding
                0, // dstArrayElement
                1, // descriptorCount
                descriptor_type,
                nullptr, // pImageInfo
                &bufferInfo,
                nullptr, // pTexelBufferView
            };
            vkUpdateDescriptorSets(dev, 1, &writeDescriptorSet, 0, nullptr);
            break;
        }
        case kernel_argument_kind::sampler: {
            auto clsampler = static_cast<cvk_sampler*>(arg_values->get_arg_value(arg));
            auto sampler = clsampler->vulkan_sampler();

            VkDescriptorImageInfo imageInfo = {
                sampler,
                VK_NULL_HANDLE, // imageView
                VK_IMAGE_LAYOUT_UNDEFINED // imageLayout
            };

            VkWriteDescriptorSet writeDescriptorSet = {
                VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET,
                nullptr,
                *ds,
                arg.binding, // dstBinding
                0, // dstArrayElement
                1, // descriptorCount
                VK_DESCRIPTOR_TYPE_SAMPLER,
                &imageInfo, // pImageInfo
                nullptr, // pBufferInfo
                nullptr, // pTexelBufferView
            };
            vkUpdateDescriptorSets(dev, 1, &writeDescriptorSet, 0, nullptr);
            break;
        }
        case kernel_argument_kind::wo_image: {
            auto image = static_cast<cvk_image*>(arg_values->get_arg_value(arg));

            VkDescriptorImageInfo imageInfo = {
                VK_NULL_HANDLE,
                image->vulkan_image_view(), // imageView
                VK_IMAGE_LAYOUT_GENERAL // imageLayout
            };

            VkWriteDescriptorSet writeDescriptorSet = {
                VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET,
                nullptr,
                *ds,
                arg.binding, // dstBinding
                0, // dstArrayElement
                1, // descriptorCount
                VK_DESCRIPTOR_TYPE_STORAGE_IMAGE,
                &imageInfo, // pImageInfo
                nullptr, // pBufferInfo
                nullptr, // pTexelBufferView
            };
            vkUpdateDescriptorSets(dev, 1, &writeDescriptorSet, 0, nullptr);
            break;
        }
        case kernel_argument_kind::ro_image: {
            auto image = static_cast<cvk_image*>(arg_values->get_arg_value(arg));

            VkDescriptorImageInfo imageInfo = {
                VK_NULL_HANDLE,
                image->vulkan_image_view(), // imageView
                VK_IMAGE_LAYOUT_GENERAL // imageLayout
            };

            VkWriteDescriptorSet writeDescriptorSet = {
                VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET,
                nullptr,
                *ds,
                arg.binding, // dstBinding
                0, // dstArrayElement
                1, // descriptorCount
                VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE,
                &imageInfo, // pImageInfo
                nullptr, // pBufferInfo
                nullptr, // pTexelBufferView
            };
            vkUpdateDescriptorSets(dev, 1, &writeDescriptorSet, 0, nullptr);
            break;
        }
        case kernel_argument_kind::pod: // skip POD arguments
        case kernel_argument_kind::pod_ubo:
            break;
        default:
            cvk_error_fn("unsupported argument type");
            return false;
        }
    }

    return true;
}

cl_int cvk_kernel::set_arg(cl_uint index, size_t size, const void *value)
{
    std::lock_guard<std::mutex> lock(m_lock);

    auto const &arg = m_args[index];

    return m_argument_values->set_arg(arg, size, value);
}

<commit_msg>Fix descriptor pool init<commit_after>// Copyright 2018 The clvk authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#include <algorithm>

#include "memory.hpp"
#include "kernel.hpp"

VkPipeline cvk_kernel_pipeline_cache::get_pipeline(uint32_t x, uint32_t y, uint32_t z) {
    VkPipeline ret = VK_NULL_HANDLE;

    m_lock.lock();
    for (auto &entry : m_entries) {
        if ((entry.lws[0] == x) && (entry.lws[1] == y) && (entry.lws[2] == z)) {
            ret = entry.pipeline;
            break;
        }
    }

    if (ret == VK_NULL_HANDLE) {
        ret = create_and_insert_pipeline(x, y, z);
    }
    m_lock.unlock();
    return ret;
}

VkPipeline cvk_kernel_pipeline_cache::create_and_insert_pipeline(uint32_t x, uint32_t y, uint32_t z) {
    uint32_t lws[3] = {x,y,z};

    VkSpecializationMapEntry mapEntries[3] = {
        {0, 0 * sizeof(uint32_t), sizeof(uint32_t)},
        {1, 1 * sizeof(uint32_t), sizeof(uint32_t)},
        {2, 2 * sizeof(uint32_t), sizeof(uint32_t)},
    };

    VkSpecializationInfo specialiaztionInfo = {
        3,
        mapEntries,
        sizeof(lws),
        &lws,
    };

    const VkComputePipelineCreateInfo createInfo = {
        VK_STRUCTURE_TYPE_COMPUTE_PIPELINE_CREATE_INFO, // sType
        nullptr, // pNext
        0, // flags
        {
            VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO, // sType
            nullptr, // pNext
            0, // flags
            VK_SHADER_STAGE_COMPUTE_BIT, // stage
            m_kernel->program()->shader_module(), // module
            m_kernel->name().c_str(),
            &specialiaztionInfo // pSpecializationInfo
        }, // stage
        m_kernel->pipeline_layout(), // layout
        VK_NULL_HANDLE, // basePipelineHandle
        0 // basePipelineIndex
    };

    VkPipeline pipeline;
    auto vkdev = m_device->vulkan_device();
    VkResult res = vkCreateComputePipelines(vkdev, VK_NULL_HANDLE, 1, &createInfo, nullptr, &pipeline);

    if (res != VK_SUCCESS) {
        cvk_error_fn("Could not create compute pipeline: %s", vulkan_error_string(res));
        return VK_NULL_HANDLE;
    }

    insert_pipeline(x, y, z, pipeline);

    return pipeline;
}

void cvk_kernel::build_descriptor_sets_layout_bindings()
{
    bool pod_found = false;

    for (auto &arg : m_args) {
        VkDescriptorType dt = VK_DESCRIPTOR_TYPE_MAX_ENUM;

        switch (arg.kind) {
        case kernel_argument_kind::buffer:
            dt = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER;
            break;
        case kernel_argument_kind::buffer_ubo:
            dt = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER;
            break;
        case kernel_argument_kind::ro_image:
            dt = VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE;
            break;
        case kernel_argument_kind::wo_image:
            dt = VK_DESCRIPTOR_TYPE_STORAGE_IMAGE;
            break;
        case kernel_argument_kind::sampler:
            dt = VK_DESCRIPTOR_TYPE_SAMPLER;
            break;
        case kernel_argument_kind::local:
            continue;
        case kernel_argument_kind::pod:
        case kernel_argument_kind::pod_ubo:
            if (!pod_found) {
                if (arg.kind == kernel_argument_kind::pod) {
                    dt = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER;
                } else if (arg.kind == kernel_argument_kind::pod_ubo) {
                    dt = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER;
                }

                m_pod_descriptor_type = dt;

                pod_found = true;
            } else {
                continue;
            }
        }

        VkDescriptorSetLayoutBinding binding = {
            arg.binding, // binding
            dt, // descriptorType
            1, // decriptorCount
            VK_SHADER_STAGE_COMPUTE_BIT, // stageFlags
            nullptr // pImmutableSamplers
        };

        m_layout_bindings.push_back(binding);
    }
}

std::unique_ptr<cvk_buffer> cvk_kernel::allocate_pod_buffer()
{
    cl_int err;
    auto buffer = cvk_buffer::create(m_context, 0, m_pod_buffer_size, nullptr, &err);
    if (err != CL_SUCCESS) {
        return nullptr;
    }

    return buffer;
}

cl_int cvk_kernel::init()
{
    // Get a pointer to the arguments from the program
    auto args = m_program->args_for_kernel(m_name);

    if (args == nullptr) {
        cvk_error("Kernel %s doesn't exist in program", m_name.c_str());
        return CL_INVALID_KERNEL_NAME;
    }

    // Store a sorted copy of the arguments
    m_args = *args;
    std::sort(m_args.begin(), m_args.end(), [](kernel_argument a, kernel_argument b) {
        return a.pos < b.pos;
    });

    // Create Descriptor Sets Layout Bindings
    build_descriptor_sets_layout_bindings();

    // Create Descriptor Sets Layout
    VkDescriptorSetLayoutCreateInfo descriptorSetLayoutCreateInfo = {
        VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO,
        0,
        0,
        static_cast<uint32_t>(m_layout_bindings.size()),
        m_layout_bindings.data()
    };

    auto vkdev = m_context->device()->vulkan_device();

    VkResult res = vkCreateDescriptorSetLayout(vkdev, &descriptorSetLayoutCreateInfo, 0, &m_descriptor_set_layout);

    if (res != VK_SUCCESS) {
        cvk_error("Could not create descriptor set layout");
        return CL_INVALID_VALUE;
    }

    // Do we have POD arguments?
    for (auto &arg : m_args) {
        if (arg.is_pod()) {
            m_has_pod_arguments = true;
        }
    }

    // Init POD arguments
    if (has_pod_arguments()) {

        // Find out POD binding
        for (auto &arg : m_args) {
            if (arg.is_pod()) {
                m_pod_binding = arg.binding;
                break;
            }
        }

        if (m_pod_binding == INVALID_POD_BINDING) {
            return CL_INVALID_PROGRAM;
        }

        // Check we know the POD buffer's descriptor type
        if (m_pod_descriptor_type == VK_DESCRIPTOR_TYPE_MAX_ENUM) {
            return CL_INVALID_PROGRAM;
        }

        // Find how big the POD buffer should be
        int max_offset = 0;
        int max_offset_arg_size = 0;

        for (auto &arg : m_args) {
            if (arg.is_pod() && (arg.offset >= max_offset)) {
                max_offset = arg.offset;
                max_offset_arg_size = arg.size;
            }
        }

        m_pod_buffer_size = max_offset + max_offset_arg_size;
    }

    // Init argument values
    m_argument_values = cvk_kernel_argument_values::create(this);
    if (m_argument_values == nullptr) {
        return CL_OUT_OF_RESOURCES;
    }

    // Create pipeline layout
    VkPipelineLayoutCreateInfo pipelineLayoutCreateInfo = {
        VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO,
        0,
        0,
        1,
        &m_descriptor_set_layout,
        0,
        0
    };

    res = vkCreatePipelineLayout(vkdev, &pipelineLayoutCreateInfo, 0, &m_pipeline_layout);
    if (res != VK_SUCCESS) {
        cvk_error("Could not create pipeline layout.");
        return CL_INVALID_VALUE;
    }

    // Determine number and types of bindings
    std::unordered_map<VkDescriptorType, uint32_t> bindingTypes;
    for (auto &lb : m_layout_bindings) {
        bindingTypes[lb.descriptorType]++;
    }

    std::vector<VkDescriptorPoolSize> poolSizes(bindingTypes.size());

    int bidx = 0;
    for (auto &bt : bindingTypes) {
        poolSizes[bidx].type = bt.first;
        poolSizes[bidx].descriptorCount = bt.second * cvk_kernel::MAX_INSTANCES;
        bidx++;
    }

    // Create descriptor pool
    VkDescriptorPoolCreateInfo descriptorPoolCreateInfo = {
        VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO,
        nullptr,
        VK_DESCRIPTOR_POOL_CREATE_FREE_DESCRIPTOR_SET_BIT, // flags
        cvk_kernel::MAX_INSTANCES, // maxSets
        static_cast<uint32_t>(poolSizes.size()), // poolSizeCount
        poolSizes.data(), // pPoolSizes
    };

    res = vkCreateDescriptorPool(vkdev, &descriptorPoolCreateInfo, 0, &m_descriptor_pool);

    if (res != VK_SUCCESS) {
        cvk_error("Could not create descriptor pool.");
        return CL_INVALID_VALUE;
    }

    return CL_SUCCESS;
}

bool cvk_kernel::setup_descriptor_set(VkDescriptorSet *ds,
                                      std::unique_ptr<cvk_kernel_argument_values> &arg_values)
{
    std::lock_guard<std::mutex> lock(m_lock);

    // Allocate descriptor sets
    VkDescriptorSetAllocateInfo descriptorSetAllocateInfo = {
        VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO,
        nullptr,
        m_descriptor_pool,
        1, // descriptorSetCount
        &m_descriptor_set_layout
    };

    auto dev = m_context->device()->vulkan_device();

    VkResult res = vkAllocateDescriptorSets(dev, &descriptorSetAllocateInfo, ds);

    if (res != VK_SUCCESS) {
        cvk_error_fn("could not allocate descriptor sets: %s", vulkan_error_string(res));
        return false;
    }

    // Transfer ownership of the argument values to the command
    arg_values = std::move(m_argument_values);

    // Create a new set, copy the argument values
    m_argument_values = cvk_kernel_argument_values::create(*arg_values.get());
    if (m_argument_values == nullptr) {
        return false;
    }

    // Setup descriptors for POD arguments
    if (has_pod_arguments()) {

        // Update desciptors
        VkDescriptorBufferInfo bufferInfo = {
            arg_values->pod_vulkan_buffer(),
            0, // offset
            VK_WHOLE_SIZE
        };

        VkWriteDescriptorSet writeDescriptorSet = {
            VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET,
            nullptr,
            *ds,
            m_pod_binding, // dstBinding
            0, // dstArrayElement
            1, // descriptorCount
            m_pod_descriptor_type, // descriptorType
            nullptr, // pImageInfo
            &bufferInfo,
            nullptr, // pTexelBufferView
        };
        vkUpdateDescriptorSets(dev, 1, &writeDescriptorSet, 0, nullptr);
    }

    // Setup other descriptors
    for (cl_uint i = 0; i < m_args.size(); i++) {

        auto const &arg = m_args[i];

        switch (arg.kind){

        case kernel_argument_kind::buffer:
        case kernel_argument_kind::buffer_ubo: {
            auto buffer = static_cast<cvk_buffer*>(arg_values->get_arg_value(arg));
            auto vkbuf = buffer->vulkan_buffer();
            cvk_debug_fn("buffer = %p", buffer);
            VkDescriptorBufferInfo bufferInfo = {
                vkbuf,
                0, // offset
                VK_WHOLE_SIZE
            };

            auto descriptor_type = arg.kind == kernel_argument_kind::buffer
                                       ? VK_DESCRIPTOR_TYPE_STORAGE_BUFFER
                                       : VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER;
            VkWriteDescriptorSet writeDescriptorSet = {
                VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET,
                nullptr,
                *ds,
                arg.binding, // dstBinding
                0, // dstArrayElement
                1, // descriptorCount
                descriptor_type,
                nullptr, // pImageInfo
                &bufferInfo,
                nullptr, // pTexelBufferView
            };
            vkUpdateDescriptorSets(dev, 1, &writeDescriptorSet, 0, nullptr);
            break;
        }
        case kernel_argument_kind::sampler: {
            auto clsampler = static_cast<cvk_sampler*>(arg_values->get_arg_value(arg));
            auto sampler = clsampler->vulkan_sampler();

            VkDescriptorImageInfo imageInfo = {
                sampler,
                VK_NULL_HANDLE, // imageView
                VK_IMAGE_LAYOUT_UNDEFINED // imageLayout
            };

            VkWriteDescriptorSet writeDescriptorSet = {
                VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET,
                nullptr,
                *ds,
                arg.binding, // dstBinding
                0, // dstArrayElement
                1, // descriptorCount
                VK_DESCRIPTOR_TYPE_SAMPLER,
                &imageInfo, // pImageInfo
                nullptr, // pBufferInfo
                nullptr, // pTexelBufferView
            };
            vkUpdateDescriptorSets(dev, 1, &writeDescriptorSet, 0, nullptr);
            break;
        }
        case kernel_argument_kind::wo_image: {
            auto image = static_cast<cvk_image*>(arg_values->get_arg_value(arg));

            VkDescriptorImageInfo imageInfo = {
                VK_NULL_HANDLE,
                image->vulkan_image_view(), // imageView
                VK_IMAGE_LAYOUT_GENERAL // imageLayout
            };

            VkWriteDescriptorSet writeDescriptorSet = {
                VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET,
                nullptr,
                *ds,
                arg.binding, // dstBinding
                0, // dstArrayElement
                1, // descriptorCount
                VK_DESCRIPTOR_TYPE_STORAGE_IMAGE,
                &imageInfo, // pImageInfo
                nullptr, // pBufferInfo
                nullptr, // pTexelBufferView
            };
            vkUpdateDescriptorSets(dev, 1, &writeDescriptorSet, 0, nullptr);
            break;
        }
        case kernel_argument_kind::ro_image: {
            auto image = static_cast<cvk_image*>(arg_values->get_arg_value(arg));

            VkDescriptorImageInfo imageInfo = {
                VK_NULL_HANDLE,
                image->vulkan_image_view(), // imageView
                VK_IMAGE_LAYOUT_GENERAL // imageLayout
            };

            VkWriteDescriptorSet writeDescriptorSet = {
                VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET,
                nullptr,
                *ds,
                arg.binding, // dstBinding
                0, // dstArrayElement
                1, // descriptorCount
                VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE,
                &imageInfo, // pImageInfo
                nullptr, // pBufferInfo
                nullptr, // pTexelBufferView
            };
            vkUpdateDescriptorSets(dev, 1, &writeDescriptorSet, 0, nullptr);
            break;
        }
        case kernel_argument_kind::pod: // skip POD arguments
        case kernel_argument_kind::pod_ubo:
            break;
        default:
            cvk_error_fn("unsupported argument type");
            return false;
        }
    }

    return true;
}

cl_int cvk_kernel::set_arg(cl_uint index, size_t size, const void *value)
{
    std::lock_guard<std::mutex> lock(m_lock);

    auto const &arg = m_args[index];

    return m_argument_values->set_arg(arg, size, value);
}

<|endoftext|>


<commit_before>// Copyright (c) 2014, Baidu.com, Inc. All Rights Reserved
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
//
// Author: yanshiguang02@baidu.com

#include "logging.h"

#include <assert.h>
#include <boost/bind.hpp>
#include <queue>
#include <stdarg.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string>
#include <syscall.h>
#include <sys/time.h>
#include <time.h>
#include <unistd.h>

#include "mutex.h"
#include "thread.h"

namespace baidu {
namespace common {

int g_log_level = INFO;
FILE* g_log_file = stdout;
FILE* g_warning_file = NULL;

void SetLogLevel(int level) {
    g_log_level = level;
}

class AsyncLogger {
public:
    AsyncLogger()
      : jobs_(&mu_), done_(&mu_), stopped_(false) {
        thread_.Start(boost::bind(&AsyncLogger::AsyncWriter, this));
    }
    ~AsyncLogger() {
        stopped_ = true;
        {
            MutexLock lock(&mu_);
            jobs_.Signal();
        }
        thread_.Join();
        // close fd
    }
    void WriteLog(int log_level, const char* buffer, int32_t len) {
        MutexLock lock(&mu_);
        buffer_queue_.push(make_pair(log_level, new std::string(buffer, len)));
        jobs_.Signal();
    }
    void AsyncWriter() {
        MutexLock lock(&mu_);
        while (1) {
            int loglen = 0;
            int wflen = 0;
            while (!buffer_queue_.empty()) {
                int log_level = buffer_queue_.front().first;
                std::string* str = buffer_queue_.front().second;
                buffer_queue_.pop();
                mu_.Unlock();
                if (str && !str->empty()) {
                    fwrite(str->data(), 1, str->size(), g_log_file);
                    loglen += str->size();
                    if (g_warning_file && log_level >= 8) {
                        fwrite(str->data(), 1, str->size(), g_warning_file);
                        wflen += str->size();
                    }
                }
                delete str;
                mu_.Lock();
            }
            if (loglen) fflush(g_log_file);
            if (wflen) fflush(g_warning_file);
            if (stopped_) {
                break;
            }
            done_.Broadcast();
            jobs_.Wait();
        }
    }
    void Flush() {
        MutexLock lock(&mu_);
        buffer_queue_.push(std::make_pair(0, reinterpret_cast<std::string*>(NULL)));
        jobs_.Signal();
        done_.Wait();
    }
private:
    Mutex mu_;
    CondVar jobs_;
    CondVar done_;
    bool stopped_;
    Thread thread_;
    std::queue<std::pair<int, std::string*> > buffer_queue_;
};

AsyncLogger g_logger;

bool SetWarningFile(const char* path, bool append) {
    const char* mode = append ? "ab" : "wb";
    FILE* fp = fopen(path, mode);
    if (fp == NULL) {
        return false;
    }
    if (g_warning_file) {
        fclose(g_warning_file);
    }
    g_warning_file = fp;
    return true;
}

bool SetLogFile(const char* path, bool append) {
    const char* mode = append ? "ab" : "wb";
    FILE* fp = fopen(path, mode);
    if (fp == NULL) {
        g_log_file = stdout;
        return false;
    }
    if (g_log_file != stdout) {
        fclose(g_log_file);
    }
    g_log_file = fp;
    return true;
}

void Logv(int log_level, const char* format, va_list ap) {
    static __thread uint64_t thread_id = 0;
    if (thread_id == 0) {
        thread_id = syscall(__NR_gettid);
    }

    // We try twice: the first time with a fixed-size stack allocated buffer,
    // and the second time with a much larger dynamically allocated buffer.
    char buffer[500];
    for (int iter = 0; iter < 2; iter++) {
        char* base;
        int bufsize;
        if (iter == 0) {
            bufsize = sizeof(buffer);
            base = buffer;
        } else {
            bufsize = 30000;
            base = new char[bufsize];
        }
        char* p = base;
        char* limit = base + bufsize;

        struct timeval now_tv;
        gettimeofday(&now_tv, NULL);
        const time_t seconds = now_tv.tv_sec;
        struct tm t;
        localtime_r(&seconds, &t);
        p += snprintf(p, limit - p,
                "%02d/%02d %02d:%02d:%02d.%06d %lld ",
                t.tm_mon + 1,
                t.tm_mday,
                t.tm_hour,
                t.tm_min,
                t.tm_sec,
                static_cast<int>(now_tv.tv_usec),
                static_cast<long long unsigned int>(thread_id));

        // Print the message
        if (p < limit) {
            va_list backup_ap;
            va_copy(backup_ap, ap);
            p += vsnprintf(p, limit - p, format, backup_ap);
            va_end(backup_ap);
        }

        // Truncate to available space if necessary
        if (p >= limit) {
            if (iter == 0) {
                continue;       // Try again with larger buffer
            } else {
                p = limit - 1;
            }
        }

        // Add newline if necessary
        if (p == base || p[-1] != '\n') {
            *p++ = '\n';
        }

        assert(p <= limit);
        //fwrite(base, 1, p - base, g_log_file);
        //fflush(g_log_file);
        //if (g_warning_file && log_level >= 8) {
        //    fwrite(base, 1, p - base, g_warning_file);
        //    fflush(g_warning_file);
        //}
        g_logger.WriteLog(log_level, base, p - base);
        if (log_level == FATAL) {
            g_logger.Flush();
        }
        if (base != buffer) {
            delete[] base;
        }
        break;
    }
}

void Log(int level, const char* fmt, ...) {
    va_list ap;
    va_start(ap, fmt);

    if (level >= g_log_level) {
        Logv(level, fmt, ap);
    }
    va_end(ap);
    if (level == FATAL) {
        abort();
    }
}

LogStream::LogStream(int level) : level_(level) {}

LogStream::~LogStream() {
    Log(level_, "%s", oss_.str().c_str());
}

} // namespace common
} // namespace baidu

/* vim: set expandtab ts=4 sw=4 sts=4 tw=100: */
<commit_msg>Optimize logging.cc WriteLog<commit_after>// Copyright (c) 2014, Baidu.com, Inc. All Rights Reserved
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
//
// Author: yanshiguang02@baidu.com

#include "logging.h"

#include <assert.h>
#include <boost/bind.hpp>
#include <queue>
#include <stdarg.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string>
#include <syscall.h>
#include <sys/time.h>
#include <time.h>
#include <unistd.h>

#include "mutex.h"
#include "thread.h"

namespace baidu {
namespace common {

int g_log_level = INFO;
FILE* g_log_file = stdout;
FILE* g_warning_file = NULL;

void SetLogLevel(int level) {
    g_log_level = level;
}

class AsyncLogger {
public:
    AsyncLogger()
      : jobs_(&mu_), done_(&mu_), stopped_(false) {
        thread_.Start(boost::bind(&AsyncLogger::AsyncWriter, this));
    }
    ~AsyncLogger() {
        stopped_ = true;
        {
            MutexLock lock(&mu_);
            jobs_.Signal();
        }
        thread_.Join();
        // close fd
    }
    void WriteLog(int log_level, const char* buffer, int32_t len) {
        std::string* log_str = new std::string(buffer, len);
        MutexLock lock(&mu_);
        buffer_queue_.push(make_pair(log_level, log_str));
        jobs_.Signal();
    }
    void AsyncWriter() {
        MutexLock lock(&mu_);
        while (1) {
            int loglen = 0;
            int wflen = 0;
            while (!buffer_queue_.empty()) {
                int log_level = buffer_queue_.front().first;
                std::string* str = buffer_queue_.front().second;
                buffer_queue_.pop();
                mu_.Unlock();
                if (str && !str->empty()) {
                    fwrite(str->data(), 1, str->size(), g_log_file);
                    loglen += str->size();
                    if (g_warning_file && log_level >= 8) {
                        fwrite(str->data(), 1, str->size(), g_warning_file);
                        wflen += str->size();
                    }
                }
                delete str;
                mu_.Lock();
            }
            if (loglen) fflush(g_log_file);
            if (wflen) fflush(g_warning_file);
            if (stopped_) {
                break;
            }
            done_.Broadcast();
            jobs_.Wait();
        }
    }
    void Flush() {
        MutexLock lock(&mu_);
        buffer_queue_.push(std::make_pair(0, reinterpret_cast<std::string*>(NULL)));
        jobs_.Signal();
        done_.Wait();
    }
private:
    Mutex mu_;
    CondVar jobs_;
    CondVar done_;
    bool stopped_;
    Thread thread_;
    std::queue<std::pair<int, std::string*> > buffer_queue_;
};

AsyncLogger g_logger;

bool SetWarningFile(const char* path, bool append) {
    const char* mode = append ? "ab" : "wb";
    FILE* fp = fopen(path, mode);
    if (fp == NULL) {
        return false;
    }
    if (g_warning_file) {
        fclose(g_warning_file);
    }
    g_warning_file = fp;
    return true;
}

bool SetLogFile(const char* path, bool append) {
    const char* mode = append ? "ab" : "wb";
    FILE* fp = fopen(path, mode);
    if (fp == NULL) {
        g_log_file = stdout;
        return false;
    }
    if (g_log_file != stdout) {
        fclose(g_log_file);
    }
    g_log_file = fp;
    return true;
}

void Logv(int log_level, const char* format, va_list ap) {
    static __thread uint64_t thread_id = 0;
    if (thread_id == 0) {
        thread_id = syscall(__NR_gettid);
    }

    // We try twice: the first time with a fixed-size stack allocated buffer,
    // and the second time with a much larger dynamically allocated buffer.
    char buffer[500];
    for (int iter = 0; iter < 2; iter++) {
        char* base;
        int bufsize;
        if (iter == 0) {
            bufsize = sizeof(buffer);
            base = buffer;
        } else {
            bufsize = 30000;
            base = new char[bufsize];
        }
        char* p = base;
        char* limit = base + bufsize;

        struct timeval now_tv;
        gettimeofday(&now_tv, NULL);
        const time_t seconds = now_tv.tv_sec;
        struct tm t;
        localtime_r(&seconds, &t);
        p += snprintf(p, limit - p,
                "%02d/%02d %02d:%02d:%02d.%06d %lld ",
                t.tm_mon + 1,
                t.tm_mday,
                t.tm_hour,
                t.tm_min,
                t.tm_sec,
                static_cast<int>(now_tv.tv_usec),
                static_cast<long long unsigned int>(thread_id));

        // Print the message
        if (p < limit) {
            va_list backup_ap;
            va_copy(backup_ap, ap);
            p += vsnprintf(p, limit - p, format, backup_ap);
            va_end(backup_ap);
        }

        // Truncate to available space if necessary
        if (p >= limit) {
            if (iter == 0) {
                continue;       // Try again with larger buffer
            } else {
                p = limit - 1;
            }
        }

        // Add newline if necessary
        if (p == base || p[-1] != '\n') {
            *p++ = '\n';
        }

        assert(p <= limit);
        //fwrite(base, 1, p - base, g_log_file);
        //fflush(g_log_file);
        //if (g_warning_file && log_level >= 8) {
        //    fwrite(base, 1, p - base, g_warning_file);
        //    fflush(g_warning_file);
        //}
        g_logger.WriteLog(log_level, base, p - base);
        if (log_level == FATAL) {
            g_logger.Flush();
        }
        if (base != buffer) {
            delete[] base;
        }
        break;
    }
}

void Log(int level, const char* fmt, ...) {
    va_list ap;
    va_start(ap, fmt);

    if (level >= g_log_level) {
        Logv(level, fmt, ap);
    }
    va_end(ap);
    if (level == FATAL) {
        abort();
    }
}

LogStream::LogStream(int level) : level_(level) {}

LogStream::~LogStream() {
    Log(level_, "%s", oss_.str().c_str());
}

} // namespace common
} // namespace baidu

/* vim: set expandtab ts=4 sw=4 sts=4 tw=100: */
<|endoftext|>


<commit_before>/*
  Copyright (c) 2012 Stuart Walsh

  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 "stdinc.h"
#include <string.h>
#include "config.h"
#include "client.h"

using std::ios;

const char *log_levels[] =
{
  "TRACE",
  "DEBUG",
  "INFO",
  "NOTICE",
  "WARNING",
  "ERROR",
  "CRITICAL",
  NULL
};

LoggingSection Logging::config;
ofstream Logging::log_stream;
Logging Logging::trace(TRACE);
Logging Logging::debug(DEBUGL);
Logging Logging::info(INFO);
Logging Logging::warning(WARNING);
Logging Logging::error(ERRORL);
Logging Logging::critical(CRITICAL);
bool Logging::flush(false);
bool Logging::dostamp(true);
stringstream Logging::stream;

Logging::Logging(LogLevel level) : log_level(level)
{
}

Logging& Logging::operator <<(manip fp)
{
  return fp(*this);
}

// Statics
LogLevel Logging::string_to_level(const string name)
{
  const char **p = log_levels;

  while(p != NULL)
  {
    if(strcasecmp(*p, name.c_str()) == 0)
      return static_cast<LogLevel>(p - log_levels);
    p++;
  }

  return static_cast<LogLevel>(-1);
}

string Logging::level_to_string(const LogLevel level)
{
  return log_levels[level];
}

void Logging::init()
{
  Config::add_section("logging", &config);
}

void Logging::start()
{
  log_stream.open(config.get_log_path(), ios::out | ios::app);
}

Logging& Logging::endl(Logging &log)
{
  Logging& tmp = log << "\n";

  log_stream << stream.str();
  log_stream.flush();
  flush = false;
  dostamp = true;
  stream.str(string());
  stream.clear();

  return tmp;
}
<commit_msg>fix bug in logging<commit_after>/*
  Copyright (c) 2012 Stuart Walsh

  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 "stdinc.h"
#include <string.h>
#include "config.h"
#include "client.h"

using std::ios;

const char *log_levels[] =
{
  "TRACE",
  "DEBUG",
  "INFO",
  "NOTICE",
  "WARNING",
  "ERROR",
  "CRITICAL",
  NULL
};

LoggingSection Logging::config;
ofstream Logging::log_stream;
Logging Logging::trace(TRACE);
Logging Logging::debug(DEBUGL);
Logging Logging::info(INFO);
Logging Logging::warning(WARNING);
Logging Logging::error(ERRORL);
Logging Logging::critical(CRITICAL);
bool Logging::flush(false);
bool Logging::dostamp(true);
stringstream Logging::stream;

Logging::Logging(LogLevel level) : log_level(level)
{
}

Logging& Logging::operator <<(manip fp)
{
  return fp(*this);
}

// Statics
LogLevel Logging::string_to_level(const string name)
{
  const char **p = log_levels;

  while(p != NULL)
  {
    if(strcasecmp(*p, name.c_str()) == 0)
      return static_cast<LogLevel>(p - log_levels);
    p++;
  }

  return static_cast<LogLevel>(-1);
}

string Logging::level_to_string(const LogLevel level)
{
  return log_levels[level];
}

void Logging::init()
{
  Config::add_section("logging", &config);
}

void Logging::start()
{
  log_stream.open(config.get_log_path(), ios::out | ios::app);
}

Logging& Logging::endl(Logging &log)
{
  Logging& tmp = log << "\n";

  if(!log_stream.is_open())
    return tmp;

  log_stream << stream.str();
  log_stream.flush();
  flush = false;
  dostamp = true;
  stream.str(string());
  stream.clear();

  return tmp;
}
<|endoftext|>


<commit_before>#include <node_buffer.h>

#include <stdint.h>
#include <iostream>
#include <string>

#include "talk/app/webrtc/jsep.h"
#include "webrtc/system_wrappers/interface/ref_count.h"
#include "common.h"
#include "datachannel.h"

using namespace node_webrtc;

Nan::Persistent<v8::Function> DataChannel::constructor;
#if NODE_MODULE_VERSION < 0x000C
Nan::Persistent<v8::Function> DataChannel::ArrayBufferConstructor;
#endif

DataChannelObserver::DataChannelObserver(rtc::scoped_refptr<webrtc::DataChannelInterface> jingleDataChannel) {
  TRACE_CALL;
  uv_mutex_init(&lock);
  _jingleDataChannel = jingleDataChannel;
  _jingleDataChannel->RegisterObserver(this);
  TRACE_END;
}

DataChannelObserver::~DataChannelObserver() {
  _jingleDataChannel = NULL;
}

void DataChannelObserver::OnStateChange()
{
  TRACE_CALL;
  DataChannel::StateEvent* data = new DataChannel::StateEvent(_jingleDataChannel->state());
  QueueEvent(DataChannel::STATE, static_cast<void*>(data));
  TRACE_END;
}

void DataChannelObserver::OnMessage(const webrtc::DataBuffer& buffer)
{
  TRACE_CALL;
    DataChannel::MessageEvent* data = new DataChannel::MessageEvent(&buffer);
    QueueEvent(DataChannel::MESSAGE, static_cast<void*>(data));
  TRACE_END;
}

void DataChannelObserver::QueueEvent(DataChannel::AsyncEventType type, void* data) {
  TRACE_CALL;
  DataChannel::AsyncEvent evt;
  evt.type = type;
  evt.data = data;
  uv_mutex_lock(&lock);
  _events.push(evt);
  uv_mutex_unlock(&lock);
  TRACE_END;
}

DataChannel::DataChannel(node_webrtc::DataChannelObserver* observer)
: loop(uv_default_loop()),
  _observer(observer),
  _binaryType(DataChannel::ARRAY_BUFFER)
{
  uv_mutex_init(&lock);
  uv_async_init(loop, &async, reinterpret_cast<uv_async_cb>(Run));

  _jingleDataChannel = observer->_jingleDataChannel;
  _jingleDataChannel->RegisterObserver(this);

  async.data = this;

  // Re-queue cached observer events
  while(true) {
    uv_mutex_lock(&observer->lock);
    bool empty = observer->_events.empty();
    if(empty)
    {
      uv_mutex_unlock(&observer->lock);
      break;
    }
    AsyncEvent evt = observer->_events.front();
    observer->_events.pop();
    uv_mutex_unlock(&observer->lock);
    QueueEvent(evt.type, evt.data);
  }

  delete observer;
}

DataChannel::~DataChannel()
{
  TRACE_CALL;
  TRACE_END;
}

NAN_METHOD(DataChannel::New) {
  TRACE_CALL;
  Nan::HandleScope scope;

  if(!info.IsConstructCall()) {
    return Nan::ThrowTypeError("Use the new operator to construct the DataChannel.");
  }

  v8::Local<v8::External> _observer = v8::Local<v8::External>::Cast(info[0]);
  node_webrtc::DataChannelObserver* observer = static_cast<node_webrtc::DataChannelObserver*>(_observer->Value());

  DataChannel* obj = new DataChannel(observer);
  obj->Wrap( info.This() );

  TRACE_END;
  info.GetReturnValue().Set( info.This() );
}

void DataChannel::QueueEvent(AsyncEventType type, void* data)
{
  TRACE_CALL;
  AsyncEvent evt;
  evt.type = type;
  evt.data = data;
  uv_mutex_lock(&lock);
  _events.push(evt);
  uv_mutex_unlock(&lock);

  uv_async_send(&async);
  TRACE_END;
}

NAN_WEAK_CALLBACK(MessageWeakCallback)
{
  P* parameter = data.GetParameter();
  delete[] parameter->message;
  parameter->message = NULL;
  delete parameter;
  //Nan::AdjustExternalMemory(-parameter->size);
}

void DataChannel::Run(uv_async_t* handle, int status)
{
  Nan::HandleScope scope;
  DataChannel* self = static_cast<DataChannel*>(handle->data);
  TRACE_CALL_P((uintptr_t)self);
  v8::Handle<v8::Object> dc = self->handle();
  bool do_shutdown = false;

  while(true)
  {
    uv_mutex_lock(&self->lock);
    bool empty = self->_events.empty();
    if(empty)
    {
      uv_mutex_unlock(&self->lock);
      break;
    }
    AsyncEvent evt = self->_events.front();
    self->_events.pop();
    uv_mutex_unlock(&self->lock);

    TRACE_U("evt.type", evt.type);
    if(DataChannel::ERROR & evt.type)
    {
      DataChannel::ErrorEvent* data = static_cast<DataChannel::ErrorEvent*>(evt.data);
      v8::Local<v8::Function> callback = v8::Local<v8::Function>::Cast(dc->Get(Nan::New("onerror").ToLocalChecked()));
      v8::Local<v8::Value> argv[1];
      argv[0] = v8::Exception::Error(Nan::New(data->msg));
      Nan::MakeCallback(dc, callback, 1, argv);
    } else if(DataChannel::STATE & evt.type)
    {
      StateEvent* data = static_cast<StateEvent*>(evt.data);
      v8::Local<v8::Function> callback = v8::Local<v8::Function>::Cast(dc->Get(Nan::New("onstatechange").ToLocalChecked()));
      v8::Local<v8::Value> argv[1];
      v8::Local<v8::Integer> state = Nan::New<v8::Integer>((data->state));
      argv[0] = state;
      Nan::MakeCallback(dc, callback, 1, argv);

      if(self->_jingleDataChannel && webrtc::DataChannelInterface::kClosed == self->_jingleDataChannel->state()) {
        do_shutdown = true;
      }
    } else if(DataChannel::MESSAGE & evt.type)
    {
      MessageEvent* data = static_cast<MessageEvent*>(evt.data);
      v8::Local<v8::Function> callback = v8::Local<v8::Function>::Cast(dc->Get(Nan::New("onmessage").ToLocalChecked()));

      v8::Local<v8::Value> argv[1];

      if(data->binary) {
#if NODE_MODULE_VERSION > 0x000B
        v8::Local<v8::ArrayBuffer> array = v8::ArrayBuffer::New(
            v8::Isolate::GetCurrent(), data->message, data->size);
#else
        v8::Local<v8::Object> array = Nan::New(ArrayBufferConstructor)->NewInstance();
        array->SetIndexedPropertiesToExternalArrayData(
            data->message, v8::kExternalByteArray, data->size);
        array->ForceSet(Nan::New("byteLength").ToLocalChecked(), Nan::New<v8::Integer>(static_cast<uint32_t>(data->size)));
#endif
        NanMakeWeakPersistent(callback, data, &MessageWeakCallback);

        argv[0] = array;
        Nan::MakeCallback(dc, callback, 1, argv);
      } else {
        v8::Local<v8::String> str = Nan::New(data->message, data->size);

        // cleanup message event
        delete[] data->message;
        data->message = NULL;
        delete data;

        argv[0] = str;
        Nan::MakeCallback(dc, callback, 1, argv);
      }
    }
  }

  if(do_shutdown) {
    uv_close((uv_handle_t*)(&self->async), NULL);
    self->_jingleDataChannel->UnregisterObserver();
    self->_jingleDataChannel = NULL;
  }

  TRACE_END;
}

void DataChannel::OnStateChange()
{
  TRACE_CALL;
  StateEvent* data = new StateEvent(_jingleDataChannel->state());
  QueueEvent(DataChannel::STATE, static_cast<void*>(data));
  TRACE_END;
}

void DataChannel::OnMessage(const webrtc::DataBuffer& buffer)
{
  TRACE_CALL;
  MessageEvent* data = new MessageEvent(&buffer);
  QueueEvent(DataChannel::MESSAGE, static_cast<void*>(data));
  TRACE_END;
}

NAN_METHOD(DataChannel::Send) {
  TRACE_CALL;
  Nan::HandleScope scope;

  DataChannel* self = Nan::ObjectWrap::Unwrap<DataChannel>( info.This() );

  if(info[0]->IsString()) {
    v8::Local<v8::String> str = v8::Local<v8::String>::Cast(info[0]);
    std::string data = *v8::String::Utf8Value(str);

    webrtc::DataBuffer buffer(data);
    self->_jingleDataChannel->Send(buffer);
  } else {

#if NODE_MINOR_VERSION >= 11
    v8::Local<v8::ArrayBuffer> arraybuffer;

    if (info[0]->IsArrayBuffer()) {
      arraybuffer = v8::Local<v8::ArrayBuffer>::Cast(info[0]);
    } else {
      v8::Local<v8::ArrayBufferView> view = v8::Local<v8::ArrayBufferView>::Cast(info[0]);
      arraybuffer = view->Buffer();
    }

    v8::ArrayBuffer::Contents content = arraybuffer->Externalize();
    rtc::Buffer buffer(content.Data(), content.ByteLength());

#else
    v8::Local<v8::Object> arraybuffer = v8::Local<v8::Object>::Cast(info[0]);
    void* data = arraybuffer->GetIndexedPropertiesExternalArrayData();
    uint32_t data_len = arraybuffer->GetIndexedPropertiesExternalArrayDataLength();

    rtc::Buffer buffer(data, data_len);

#endif

    webrtc::DataBuffer data_buffer(buffer, true);
    self->_jingleDataChannel->Send(data_buffer);

#if NODE_MINOR_VERSION >= 11
    arraybuffer->Neuter();
#endif
  }

  TRACE_END;
  return;
}

NAN_METHOD(DataChannel::Close) {
  TRACE_CALL;
  Nan::HandleScope scope;

  DataChannel* self = Nan::ObjectWrap::Unwrap<DataChannel>( info.This() );
  self->_jingleDataChannel->Close();

  TRACE_END;
  return;
}

NAN_METHOD(DataChannel::Shutdown) {
  TRACE_CALL;
  Nan::HandleScope scope;

  DataChannel* self = Nan::ObjectWrap::Unwrap<DataChannel>( info.This() );
  if(!uv_is_closing((uv_handle_t*)(&self->async)))
    uv_close((uv_handle_t*)(&self->async), NULL);

  TRACE_END;
  return;
}

NAN_GETTER(DataChannel::GetBufferedAmount) {
  TRACE_CALL;
  Nan::HandleScope scope;

  DataChannel* self = Nan::ObjectWrap::Unwrap<DataChannel>( info.Holder() );

  uint64_t buffered_amount = self->_jingleDataChannel->buffered_amount();

  TRACE_END;
  info.GetReturnValue().Set(Nan::New<v8::Number>(buffered_amount));
}

NAN_GETTER(DataChannel::GetLabel) {
  TRACE_CALL;
  Nan::HandleScope scope;

  DataChannel* self = Nan::ObjectWrap::Unwrap<DataChannel>( info.Holder() );

  std::string label = self->_jingleDataChannel->label();

  TRACE_END;
  info.GetReturnValue().Set(Nan::New(label));
}

NAN_GETTER(DataChannel::GetReadyState) {
  TRACE_CALL;
  Nan::HandleScope scope;

  DataChannel* self = Nan::ObjectWrap::Unwrap<DataChannel>( info.Holder() );

  webrtc::DataChannelInterface::DataState state = self->_jingleDataChannel->state();

  TRACE_END;
  info.GetReturnValue().Set(Nan::New<v8::Number>(static_cast<uint32_t>(state)));
}

NAN_GETTER(DataChannel::GetBinaryType) {
  TRACE_CALL;
  Nan::HandleScope scope;

  DataChannel* self = Nan::ObjectWrap::Unwrap<DataChannel>( info.Holder() );

  TRACE_END;
  info.GetReturnValue().Set(Nan::New<v8::Number>(static_cast<uint32_t>(self->_binaryType)));
}

NAN_SETTER(DataChannel::SetBinaryType) {
  TRACE_CALL;

  DataChannel* self = Nan::ObjectWrap::Unwrap<DataChannel>( info.Holder() );
  self->_binaryType = static_cast<BinaryType>(value->Uint32Value());

  TRACE_END;
}

NAN_SETTER(DataChannel::ReadOnly) {
  INFO("PeerConnection::ReadOnly");
}

void DataChannel::Init( v8::Handle<v8::Object> exports ) {
  v8::Local<v8::FunctionTemplate> tpl = Nan::New<v8::FunctionTemplate>( New );
  tpl->SetClassName( Nan::New( "DataChannel" ) );
  tpl->InstanceTemplate()->SetInternalFieldCount(1);
  tpl->PrototypeTemplate()->Set( Nan::New( "close" ),
    Nan::New<v8::FunctionTemplate>( Close )->GetFunction() );
  tpl->PrototypeTemplate()->Set( Nan::New( "shutdown" ),
    Nan::New<v8::FunctionTemplate>( Shutdown )->GetFunction() );

  tpl->PrototypeTemplate()->Set( Nan::New( "send" ),
    Nan::New<v8::FunctionTemplate>( Send )->GetFunction() );

  tpl->InstanceTemplate()->SetAccessor(Nan::New("bufferedAmount").ToLocalChecked(), GetBufferedAmount, ReadOnly);
  tpl->InstanceTemplate()->SetAccessor(Nan::New("label").ToLocalChecked(), GetLabel, ReadOnly);
  tpl->InstanceTemplate()->SetAccessor(Nan::New("binaryType").ToLocalChecked(), GetBinaryType, SetBinaryType);
  tpl->InstanceTemplate()->SetAccessor(Nan::New("readyState").ToLocalChecked(), GetReadyState, ReadOnly);

  constructor.Reset(tpl->GetFunction());
  exports->Set( Nan::New("DataChannel").ToLocalChecked(), tpl->GetFunction() );

  v8::Local<v8::Object> global = Nan::GetCurrentContext()->Global();
#if NODE_MODULE_VERSION < 0x000C
  v8::Local<v8::Value> obj = global->Get(Nan::New("ArrayBuffer").ToLocalChecked());
  ArrayBufferConstructor.Reset(obj.As<v8::Function>());
#endif
}
<commit_msg>data channel upgraded, weak callback logic disabled for now<commit_after>#include <node_buffer.h>

#include <stdint.h>
#include <iostream>
#include <string>

#include "talk/app/webrtc/jsep.h"
#include "webrtc/system_wrappers/interface/ref_count.h"
#include "common.h"
#include "datachannel.h"

using namespace node_webrtc;

Nan::Persistent<v8::Function> DataChannel::constructor;
#if NODE_MODULE_VERSION < 0x000C
Nan::Persistent<v8::Function> DataChannel::ArrayBufferConstructor;
#endif

DataChannelObserver::DataChannelObserver(rtc::scoped_refptr<webrtc::DataChannelInterface> jingleDataChannel) {
  TRACE_CALL;
  uv_mutex_init(&lock);
  _jingleDataChannel = jingleDataChannel;
  _jingleDataChannel->RegisterObserver(this);
  TRACE_END;
}

DataChannelObserver::~DataChannelObserver() {
  _jingleDataChannel = NULL;
}

void DataChannelObserver::OnStateChange()
{
  TRACE_CALL;
  DataChannel::StateEvent* data = new DataChannel::StateEvent(_jingleDataChannel->state());
  QueueEvent(DataChannel::STATE, static_cast<void*>(data));
  TRACE_END;
}

void DataChannelObserver::OnMessage(const webrtc::DataBuffer& buffer)
{
  TRACE_CALL;
    DataChannel::MessageEvent* data = new DataChannel::MessageEvent(&buffer);
    QueueEvent(DataChannel::MESSAGE, static_cast<void*>(data));
  TRACE_END;
}

void DataChannelObserver::QueueEvent(DataChannel::AsyncEventType type, void* data) {
  TRACE_CALL;
  DataChannel::AsyncEvent evt;
  evt.type = type;
  evt.data = data;
  uv_mutex_lock(&lock);
  _events.push(evt);
  uv_mutex_unlock(&lock);
  TRACE_END;
}

DataChannel::DataChannel(node_webrtc::DataChannelObserver* observer)
: loop(uv_default_loop()),
  _observer(observer),
  _binaryType(DataChannel::ARRAY_BUFFER)
{
  uv_mutex_init(&lock);
  uv_async_init(loop, &async, reinterpret_cast<uv_async_cb>(Run));

  _jingleDataChannel = observer->_jingleDataChannel;
  _jingleDataChannel->RegisterObserver(this);

  async.data = this;

  // Re-queue cached observer events
  while(true) {
    uv_mutex_lock(&observer->lock);
    bool empty = observer->_events.empty();
    if(empty)
    {
      uv_mutex_unlock(&observer->lock);
      break;
    }
    AsyncEvent evt = observer->_events.front();
    observer->_events.pop();
    uv_mutex_unlock(&observer->lock);
    QueueEvent(evt.type, evt.data);
  }

  delete observer;
}

DataChannel::~DataChannel()
{
  TRACE_CALL;
  TRACE_END;
}

NAN_METHOD(DataChannel::New) {
  TRACE_CALL;
  Nan::HandleScope scope;

  if(!info.IsConstructCall()) {
    return Nan::ThrowTypeError("Use the new operator to construct the DataChannel.");
  }

  v8::Local<v8::External> _observer = v8::Local<v8::External>::Cast(info[0]);
  node_webrtc::DataChannelObserver* observer = static_cast<node_webrtc::DataChannelObserver*>(_observer->Value());

  DataChannel* obj = new DataChannel(observer);
  obj->Wrap( info.This() );

  TRACE_END;
  info.GetReturnValue().Set( info.This() );
}

void DataChannel::QueueEvent(AsyncEventType type, void* data)
{
  TRACE_CALL;
  AsyncEvent evt;
  evt.type = type;
  evt.data = data;
  uv_mutex_lock(&lock);
  _events.push(evt);
  uv_mutex_unlock(&lock);

  uv_async_send(&async);
  TRACE_END;
}

/*NAN_WEAK_CALLBACK(MessageWeakCallback)
{
  P* parameter = data.GetParameter();
  delete[] parameter->message;
  parameter->message = NULL;
  delete parameter;
  //Nan::AdjustExternalMemory(-parameter->size);
}*/

void DataChannel::Run(uv_async_t* handle, int status)
{
  Nan::HandleScope scope;
  DataChannel* self = static_cast<DataChannel*>(handle->data);
  TRACE_CALL_P((uintptr_t)self);
  v8::Handle<v8::Object> dc = self->handle();
  bool do_shutdown = false;

  while(true)
  {
    uv_mutex_lock(&self->lock);
    bool empty = self->_events.empty();
    if(empty)
    {
      uv_mutex_unlock(&self->lock);
      break;
    }
    AsyncEvent evt = self->_events.front();
    self->_events.pop();
    uv_mutex_unlock(&self->lock);

    TRACE_U("evt.type", evt.type);
    if(DataChannel::ERROR & evt.type)
    {
      DataChannel::ErrorEvent* data = static_cast<DataChannel::ErrorEvent*>(evt.data);
      v8::Local<v8::Function> callback = v8::Local<v8::Function>::Cast(dc->Get(Nan::New("onerror").ToLocalChecked()));
      v8::Local<v8::Value> argv[1];
      argv[0] = Nan::Error(data->msg.c_str());
      Nan::MakeCallback(dc, callback, 1, argv);
    } else if(DataChannel::STATE & evt.type)
    {
      StateEvent* data = static_cast<StateEvent*>(evt.data);
      v8::Local<v8::Function> callback = v8::Local<v8::Function>::Cast(dc->Get(Nan::New("onstatechange").ToLocalChecked()));
      v8::Local<v8::Value> argv[1];
      v8::Local<v8::Integer> state = Nan::New<v8::Integer>((data->state));
      argv[0] = state;
      Nan::MakeCallback(dc, callback, 1, argv);

      if(self->_jingleDataChannel && webrtc::DataChannelInterface::kClosed == self->_jingleDataChannel->state()) {
        do_shutdown = true;
      }
    } else if(DataChannel::MESSAGE & evt.type)
    {
      MessageEvent* data = static_cast<MessageEvent*>(evt.data);
      v8::Local<v8::Function> callback = v8::Local<v8::Function>::Cast(dc->Get(Nan::New("onmessage").ToLocalChecked()));

      v8::Local<v8::Value> argv[1];

      if(data->binary) {
#if NODE_MODULE_VERSION > 0x000B
        v8::Local<v8::ArrayBuffer> array = v8::ArrayBuffer::New(
            v8::Isolate::GetCurrent(), data->message, data->size);
#else
        v8::Local<v8::Object> array = Nan::New(ArrayBufferConstructor)->NewInstance();
        array->SetIndexedPropertiesToExternalArrayData(
            data->message, v8::kExternalByteArray, data->size);
        array->ForceSet(Nan::New("byteLength").ToLocalChecked(), Nan::New<v8::Integer>(static_cast<uint32_t>(data->size)));
#endif
        //NanMakeWeakPersistent(callback, data, &MessageWeakCallback);

        argv[0] = array;
        Nan::MakeCallback(dc, callback, 1, argv);
      } else {
        v8::Local<v8::String> str = Nan::New(data->message, data->size).ToLocalChecked();

        // cleanup message event
        delete[] data->message;
        data->message = NULL;
        delete data;

        argv[0] = str;
        Nan::MakeCallback(dc, callback, 1, argv);
      }
    }
  }

  if(do_shutdown) {
    uv_close((uv_handle_t*)(&self->async), NULL);
    self->_jingleDataChannel->UnregisterObserver();
    self->_jingleDataChannel = NULL;
  }

  TRACE_END;
}

void DataChannel::OnStateChange()
{
  TRACE_CALL;
  StateEvent* data = new StateEvent(_jingleDataChannel->state());
  QueueEvent(DataChannel::STATE, static_cast<void*>(data));
  TRACE_END;
}

void DataChannel::OnMessage(const webrtc::DataBuffer& buffer)
{
  TRACE_CALL;
  MessageEvent* data = new MessageEvent(&buffer);
  QueueEvent(DataChannel::MESSAGE, static_cast<void*>(data));
  TRACE_END;
}

NAN_METHOD(DataChannel::Send) {
  TRACE_CALL;
  Nan::HandleScope scope;

  DataChannel* self = Nan::ObjectWrap::Unwrap<DataChannel>( info.This() );

  if(info[0]->IsString()) {
    v8::Local<v8::String> str = v8::Local<v8::String>::Cast(info[0]);
    std::string data = *v8::String::Utf8Value(str);

    webrtc::DataBuffer buffer(data);
    self->_jingleDataChannel->Send(buffer);
  } else {

#if NODE_MINOR_VERSION >= 11 || NODE_MAJOR_VERSION >= 0
    v8::Local<v8::ArrayBuffer> arraybuffer;

    if (info[0]->IsArrayBuffer()) {
      arraybuffer = v8::Local<v8::ArrayBuffer>::Cast(info[0]);
    } else {
      v8::Local<v8::ArrayBufferView> view = v8::Local<v8::ArrayBufferView>::Cast(info[0]);
      arraybuffer = view->Buffer();
    }

    v8::ArrayBuffer::Contents content = arraybuffer->Externalize();
    rtc::Buffer buffer(content.Data(), content.ByteLength());

#else
    v8::Local<v8::Object> arraybuffer = v8::Local<v8::Object>::Cast(info[0]);
    void* data = arraybuffer->GetIndexedPropertiesExternalArrayData();
    uint32_t data_len = arraybuffer->GetIndexedPropertiesExternalArrayDataLength();

    rtc::Buffer buffer(data, data_len);

#endif

    webrtc::DataBuffer data_buffer(buffer, true);
    self->_jingleDataChannel->Send(data_buffer);

#if NODE_MINOR_VERSION >= 11 || NODE_MAJOR_VERSION >= 0
    arraybuffer->Neuter();
#endif
  }

  TRACE_END;
  return;
}

NAN_METHOD(DataChannel::Close) {
  TRACE_CALL;
  Nan::HandleScope scope;

  DataChannel* self = Nan::ObjectWrap::Unwrap<DataChannel>( info.This() );
  self->_jingleDataChannel->Close();

  TRACE_END;
  return;
}

NAN_METHOD(DataChannel::Shutdown) {
  TRACE_CALL;
  Nan::HandleScope scope;

  DataChannel* self = Nan::ObjectWrap::Unwrap<DataChannel>( info.This() );
  if(!uv_is_closing((uv_handle_t*)(&self->async)))
    uv_close((uv_handle_t*)(&self->async), NULL);

  TRACE_END;
  return;
}

NAN_GETTER(DataChannel::GetBufferedAmount) {
  TRACE_CALL;
  Nan::HandleScope scope;

  DataChannel* self = Nan::ObjectWrap::Unwrap<DataChannel>( info.Holder() );

  uint64_t buffered_amount = self->_jingleDataChannel->buffered_amount();

  TRACE_END;
  info.GetReturnValue().Set(Nan::New<v8::Number>(buffered_amount));
}

NAN_GETTER(DataChannel::GetLabel) {
  TRACE_CALL;
  Nan::HandleScope scope;

  DataChannel* self = Nan::ObjectWrap::Unwrap<DataChannel>( info.Holder() );

  std::string label = self->_jingleDataChannel->label();

  TRACE_END;
  info.GetReturnValue().Set(Nan::New(label).ToLocalChecked());
}

NAN_GETTER(DataChannel::GetReadyState) {
  TRACE_CALL;
  Nan::HandleScope scope;

  DataChannel* self = Nan::ObjectWrap::Unwrap<DataChannel>( info.Holder() );

  webrtc::DataChannelInterface::DataState state = self->_jingleDataChannel->state();

  TRACE_END;
  info.GetReturnValue().Set(Nan::New<v8::Number>(static_cast<uint32_t>(state)));
}

NAN_GETTER(DataChannel::GetBinaryType) {
  TRACE_CALL;
  Nan::HandleScope scope;

  DataChannel* self = Nan::ObjectWrap::Unwrap<DataChannel>( info.Holder() );

  TRACE_END;
  info.GetReturnValue().Set(Nan::New<v8::Number>(static_cast<uint32_t>(self->_binaryType)));
}

NAN_SETTER(DataChannel::SetBinaryType) {
  TRACE_CALL;

  DataChannel* self = Nan::ObjectWrap::Unwrap<DataChannel>( info.Holder() );
  self->_binaryType = static_cast<BinaryType>(value->Uint32Value());

  TRACE_END;
}

NAN_SETTER(DataChannel::ReadOnly) {
  INFO("PeerConnection::ReadOnly");
}

void DataChannel::Init( v8::Handle<v8::Object> exports ) {
  v8::Local<v8::FunctionTemplate> tpl = Nan::New<v8::FunctionTemplate>( New );
  tpl->SetClassName( Nan::New( "DataChannel" ).ToLocalChecked() );
  tpl->InstanceTemplate()->SetInternalFieldCount(1);
  tpl->PrototypeTemplate()->Set( Nan::New( "close" ).ToLocalChecked(),
    Nan::New<v8::FunctionTemplate>( Close )->GetFunction() );
  tpl->PrototypeTemplate()->Set( Nan::New( "shutdown" ).ToLocalChecked(),
    Nan::New<v8::FunctionTemplate>( Shutdown )->GetFunction() );

  tpl->PrototypeTemplate()->Set( Nan::New( "send" ).ToLocalChecked(),
    Nan::New<v8::FunctionTemplate>( Send )->GetFunction() );

  Nan::SetAccessor(tpl->InstanceTemplate(), Nan::New("bufferedAmount").ToLocalChecked(), GetBufferedAmount, ReadOnly);
  Nan::SetAccessor(tpl->InstanceTemplate(), Nan::New("label").ToLocalChecked(), GetLabel, ReadOnly);
  Nan::SetAccessor(tpl->InstanceTemplate(), Nan::New("binaryType").ToLocalChecked(), GetBinaryType, SetBinaryType);
  Nan::SetAccessor(tpl->InstanceTemplate(), Nan::New("readyState").ToLocalChecked(), GetReadyState, ReadOnly);

  constructor.Reset(tpl->GetFunction());
  exports->Set( Nan::New("DataChannel").ToLocalChecked(), tpl->GetFunction() );

  v8::Local<v8::Object> global = Nan::GetCurrentContext()->Global();
#if NODE_MODULE_VERSION < 0x000C
  v8::Local<v8::Value> obj = global->Get(Nan::New("ArrayBuffer").ToLocalChecked());
  ArrayBufferConstructor.Reset(obj.As<v8::Function>());
#endif
}
<|endoftext|>