<commit_before>/*
 * Copyright 2006 The Android Open Source Project
 *
 * Use of this source code is governed by a BSD-style license that can be
 * found in the LICENSE file.
 */

#include "SkUtils.h"

/*  0xxxxxxx    1 total
    10xxxxxx    // never a leading byte
    110xxxxx    2 total
    1110xxxx    3 total
    11110xxx    4 total

    11 10 01 01 xx xx xx xx 0...
    0xE5XX0000
    0xE5 << 24
*/

static bool utf8_byte_is_valid(uint8_t c) {
    return c < 0xF5 && (c & 0xFE) != 0xC0;
}
static bool utf8_byte_is_continuation(uint8_t c) {
    return  (c & 0xC0) == 0x80;
}
static bool utf8_byte_is_leading_byte(uint8_t c) {
    return utf8_byte_is_valid(c) && !utf8_byte_is_continuation(c);
}

#ifdef SK_DEBUG
    static void assert_utf8_leadingbyte(unsigned c) {
        SkASSERT(utf8_byte_is_leading_byte(SkToU8(c)));
    }

    int SkUTF8_LeadByteToCount(unsigned c) {
        assert_utf8_leadingbyte(c);
        return (((0xE5 << 24) >> (c >> 4 << 1)) & 3) + 1;
    }
#else
    #define assert_utf8_leadingbyte(c)
#endif

/**
 * @returns -1  iff invalid UTF8 byte,
 *           0  iff UTF8 continuation byte,
 *           1  iff ASCII byte,
 *           2  iff leading byte of 2-byte sequence,
 *           3  iff leading byte of 3-byte sequence, and
 *           4  iff leading byte of 4-byte sequence.
 *
 * I.e.: if return value > 0, then gives length of sequence.
*/
static int utf8_byte_type(uint8_t c) {
    if (c < 0x80) {
        return 1;
    } else if (c < 0xC0) {
        return 0;
    } else if (c < 0xF5 && (c & 0xFE) != 0xC0) { // "octet values C0, C1, F5 to FF never appear"
        return (((0xE5 << 24) >> ((unsigned)c >> 4 << 1)) & 3) + 1;
    } else {
        return -1;
    }
}
static bool utf8_type_is_valid_leading_byte(int type) { return type > 0; }

int SkUTF8_CountUnichars(const char utf8[]) {
    SkASSERT(utf8);

    int count = 0;

    for (;;) {
        int c = *(const uint8_t*)utf8;
        if (c == 0) {
            break;
        }
        utf8 += SkUTF8_LeadByteToCount(c);
        count += 1;
    }
    return count;
}

// SAFE: returns -1 if invalid UTF-8
int SkUTF8_CountUnichars(const void* text, size_t byteLength) {
    SkASSERT(text);
    const char* utf8 = static_cast<const char*>(text);
    if (byteLength == 0) {
        return 0;
    }

    int         count = 0;
    const char* stop = utf8 + byteLength;

    while (utf8 < stop) {
        int type = utf8_byte_type(*(const uint8_t*)utf8);
        SkASSERT(type >= -1 && type <= 4);
        if (!utf8_type_is_valid_leading_byte(type) || utf8 + type > stop) {
            // Sequence extends beyond end.
            return -1;
        }
        while(type-- > 1) {
            ++utf8;
            if (!utf8_byte_is_continuation(*(const uint8_t*)utf8)) {
                return -1;
            }
        }
        ++utf8;
        ++count;
    }
    return count;
}

SkUnichar SkUTF8_ToUnichar(const char utf8[]) {
    SkASSERT(utf8);

    const uint8_t*  p = (const uint8_t*)utf8;
    int             c = *p;
    int             hic = c << 24;

    assert_utf8_leadingbyte(c);

    if (hic < 0) {
        uint32_t mask = (uint32_t)~0x3F;
        hic = SkLeftShift(hic, 1);
        do {
            c = (c << 6) | (*++p & 0x3F);
            mask <<= 5;
        } while ((hic = SkLeftShift(hic, 1)) < 0);
        c &= ~mask;
    }
    return c;
}

// SAFE: returns -1 on invalid UTF-8 sequence.
SkUnichar SkUTF8_NextUnicharWithError(const char** ptr, const char* end) {
    SkASSERT(ptr && *ptr);
    SkASSERT(*ptr < end);
    const uint8_t*  p = (const uint8_t*)*ptr;
    int             c = *p;
    int             hic = c << 24;

    if (!utf8_byte_is_leading_byte(c)) {
        return -1;
    }
    if (hic < 0) {
        uint32_t mask = (uint32_t)~0x3F;
        hic = SkLeftShift(hic, 1);
        do {
            ++p;
            if (p >= (const uint8_t*)end) {
                return -1;
            }
            // check before reading off end of array.
            uint8_t nextByte = *p;
            if (!utf8_byte_is_continuation(nextByte)) {
                return -1;
            }
            c = (c << 6) | (nextByte & 0x3F);
            mask <<= 5;
        } while ((hic = SkLeftShift(hic, 1)) < 0);
        c &= ~mask;
    }
    *ptr = (char*)p + 1;
    return c;
}

SkUnichar SkUTF8_NextUnichar(const char** ptr) {
    SkASSERT(ptr && *ptr);

    const uint8_t*  p = (const uint8_t*)*ptr;
    int             c = *p;
    int             hic = c << 24;

    assert_utf8_leadingbyte(c);

    if (hic < 0) {
        uint32_t mask = (uint32_t)~0x3F;
        hic = SkLeftShift(hic, 1);
        do {
            c = (c << 6) | (*++p & 0x3F);
            mask <<= 5;
        } while ((hic = SkLeftShift(hic, 1)) < 0);
        c &= ~mask;
    }
    *ptr = (char*)p + 1;
    return c;
}

SkUnichar SkUTF8_PrevUnichar(const char** ptr) {
    SkASSERT(ptr && *ptr);

    const char* p = *ptr;

    if (*--p & 0x80) {
        while (*--p & 0x40) {
            ;
        }
    }

    *ptr = (char*)p;
    return SkUTF8_NextUnichar(&p);
}

size_t SkUTF8_FromUnichar(SkUnichar uni, char utf8[]) {
    if ((uint32_t)uni > 0x10FFFF) {
        SkDEBUGFAIL("bad unichar");
        return 0;
    }

    if (uni <= 127) {
        if (utf8) {
            *utf8 = (char)uni;
        }
        return 1;
    }

    char    tmp[4];
    char*   p = tmp;
    size_t  count = 1;

    SkDEBUGCODE(SkUnichar orig = uni;)

    while (uni > 0x7F >> count) {
        *p++ = (char)(0x80 | (uni & 0x3F));
        uni >>= 6;
        count += 1;
    }

    if (utf8) {
        p = tmp;
        utf8 += count;
        while (p < tmp + count - 1) {
            *--utf8 = *p++;
        }
        *--utf8 = (char)(~(0xFF >> count) | uni);
    }

    SkASSERT(utf8 == nullptr || orig == SkUTF8_ToUnichar(utf8));
    return count;
}

///////////////////////////////////////////////////////////////////////////////

int SkUTF16_CountUnichars(const uint16_t src[]) {
    SkASSERT(src);

    int count = 0;
    unsigned c;
    while ((c = *src++) != 0) {
        SkASSERT(!SkUTF16_IsLowSurrogate(c));
        if (SkUTF16_IsHighSurrogate(c)) {
            c = *src++;
            SkASSERT(SkUTF16_IsLowSurrogate(c));
        }
        count += 1;
    }
    return count;
}

// returns -1 on error
int SkUTF16_CountUnichars(const void* text, size_t byteLength) {
    SkASSERT(text);
    if (byteLength == 0) {
        return 0;
    }
    if (!SkIsAlign2(intptr_t(text)) || !SkIsAlign2(byteLength)) {
        return -1;
    }

    const uint16_t* src = static_cast<const uint16_t*>(text);
    const uint16_t* stop = src + (byteLength >> 1);
    int count = 0;
    while (src < stop) {
        unsigned c = *src++;
        SkASSERT(!SkUTF16_IsLowSurrogate(c));
        if (SkUTF16_IsHighSurrogate(c)) {
            if (src >= stop) {
                return -1;
            }
            c = *src++;
            SkASSERT(SkUTF16_IsLowSurrogate(c));
        }
        count += 1;
    }
    return count;
}

SkUnichar SkUTF16_NextUnichar(const uint16_t** srcPtr) {
    SkASSERT(srcPtr && *srcPtr);

    const uint16_t* src = *srcPtr;
    SkUnichar       c = *src++;

    SkASSERT(!SkUTF16_IsLowSurrogate(c));
    if (SkUTF16_IsHighSurrogate(c)) {
        unsigned c2 = *src++;
        SkASSERT(SkUTF16_IsLowSurrogate(c2));

        // c = ((c & 0x3FF) << 10) + (c2 & 0x3FF) + 0x10000
        // c = (((c & 0x3FF) + 64) << 10) + (c2 & 0x3FF)
        c = (c << 10) + c2 + (0x10000 - (0xD800 << 10) - 0xDC00);
    }
    *srcPtr = src;
    return c;
}

SkUnichar SkUTF16_PrevUnichar(const uint16_t** srcPtr) {
    SkASSERT(srcPtr && *srcPtr);

    const uint16_t* src = *srcPtr;
    SkUnichar       c = *--src;

    SkASSERT(!SkUTF16_IsHighSurrogate(c));
    if (SkUTF16_IsLowSurrogate(c)) {
        unsigned c2 = *--src;
        SkASSERT(SkUTF16_IsHighSurrogate(c2));
        c = (c2 << 10) + c + (0x10000 - (0xD800 << 10) - 0xDC00);
    }
    *srcPtr = src;
    return c;
}

size_t SkUTF16_FromUnichar(SkUnichar uni, uint16_t dst[]) {
    SkASSERT((unsigned)uni <= 0x10FFFF);

    int extra = (uni > 0xFFFF);

    if (dst) {
        if (extra) {
            // dst[0] = SkToU16(0xD800 | ((uni - 0x10000) >> 10));
            // dst[0] = SkToU16(0xD800 | ((uni >> 10) - 64));
            dst[0] = SkToU16((0xD800 - 64) + (uni >> 10));
            dst[1] = SkToU16(0xDC00 | (uni & 0x3FF));

            SkASSERT(SkUTF16_IsHighSurrogate(dst[0]));
            SkASSERT(SkUTF16_IsLowSurrogate(dst[1]));
        } else {
            dst[0] = SkToU16(uni);
            SkASSERT(!SkUTF16_IsHighSurrogate(dst[0]));
            SkASSERT(!SkUTF16_IsLowSurrogate(dst[0]));
        }
    }
    return 1 + extra;
}

size_t SkUTF16_ToUTF8(const uint16_t utf16[], int numberOf16BitValues,
                      char utf8[]) {
    SkASSERT(numberOf16BitValues >= 0);
    if (numberOf16BitValues <= 0) {
        return 0;
    }

    SkASSERT(utf16 != nullptr);

    const uint16_t* stop = utf16 + numberOf16BitValues;
    size_t          size = 0;

    if (utf8 == nullptr) {    // just count
        while (utf16 < stop) {
            size += SkUTF8_FromUnichar(SkUTF16_NextUnichar(&utf16), nullptr);
        }
    } else {
        char* start = utf8;
        while (utf16 < stop) {
            utf8 += SkUTF8_FromUnichar(SkUTF16_NextUnichar(&utf16), utf8);
        }
        size = utf8 - start;
    }
    return size;
}

const char SkHexadecimalDigits::gUpper[16] =
           { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' };
const char SkHexadecimalDigits::gLower[16] =
           { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' };

// returns -1 on error
int SkUTF32_CountUnichars(const void* text, size_t byteLength) {
    if (byteLength == 0) {
        return 0;
    }
    if (!SkIsAlign4(intptr_t(text)) || !SkIsAlign4(byteLength)) {
        return -1;
    }
    const uint32_t kInvalidUnicharMask = 0xFF000000;    // unichar fits in 24 bits
    const uint32_t* ptr = static_cast<const uint32_t*>(text);
    const uint32_t* stop = ptr + (byteLength >> 2);
    while (ptr < stop) {
        if (*ptr & kInvalidUnicharMask) {
            return -1;
        }
        ptr += 1;
    }
    return SkToInt(byteLength >> 2);
}

<commit_msg>Roll external/skia b6ba82ca0..a7f7ee96a (1 commits)<commit_after>/*
 * Copyright 2006 The Android Open Source Project
 *
 * Use of this source code is governed by a BSD-style license that can be
 * found in the LICENSE file.
 */

#include "SkUtils.h"

/*  0xxxxxxx    1 total
    10xxxxxx    // never a leading byte
    110xxxxx    2 total
    1110xxxx    3 total
    11110xxx    4 total

    11 10 01 01 xx xx xx xx 0...
    0xE5XX0000
    0xE5 << 24
*/

static bool utf8_byte_is_valid(uint8_t c) {
    return c < 0xF5 && (c & 0xFE) != 0xC0;
}
static bool utf8_byte_is_continuation(uint8_t c) {
    return  (c & 0xC0) == 0x80;
}
static bool utf8_byte_is_leading_byte(uint8_t c) {
    return utf8_byte_is_valid(c) && !utf8_byte_is_continuation(c);
}

#ifdef SK_DEBUG
    static void assert_utf8_leadingbyte(unsigned c) {
        SkASSERT(utf8_byte_is_leading_byte(SkToU8(c)));
    }

    int SkUTF8_LeadByteToCount(unsigned c) {
        assert_utf8_leadingbyte(c);
        return (((0xE5 << 24) >> (c >> 4 << 1)) & 3) + 1;
    }
#else
    #define assert_utf8_leadingbyte(c)
#endif

/**
 * @returns -1  iff invalid UTF8 byte,
 *           0  iff UTF8 continuation byte,
 *           1  iff ASCII byte,
 *           2  iff leading byte of 2-byte sequence,
 *           3  iff leading byte of 3-byte sequence, and
 *           4  iff leading byte of 4-byte sequence.
 *
 * I.e.: if return value > 0, then gives length of sequence.
*/
static int utf8_byte_type(uint8_t c) {
    if (c < 0x80) {
        return 1;
    } else if (c < 0xC0) {
        return 0;
    } else if (c < 0xF5 && (c & 0xFE) != 0xC0) { // "octet values C0, C1, F5 to FF never appear"
        return (((0xE5 << 24) >> ((unsigned)c >> 4 << 1)) & 3) + 1;
    } else {
        return -1;
    }
}
static bool utf8_type_is_valid_leading_byte(int type) { return type > 0; }

int SkUTF8_CountUnichars(const char utf8[]) {
    SkASSERT(utf8);

    int count = 0;

    for (;;) {
        int c = *(const uint8_t*)utf8;
        if (c == 0) {
            break;
        }
        utf8 += SkUTF8_LeadByteToCount(c);
        count += 1;
    }
    return count;
}

// SAFE: returns -1 if invalid UTF-8
int SkUTF8_CountUnichars(const void* text, size_t byteLength) {
    SkASSERT(text);
    const char* utf8 = static_cast<const char*>(text);
    if (byteLength == 0) {
        return 0;
    }

    int         count = 0;
    const char* stop = utf8 + byteLength;

    while (utf8 < stop) {
        int type = utf8_byte_type(*(const uint8_t*)utf8);
        SkASSERT(type >= -1 && type <= 4);
        if (!utf8_type_is_valid_leading_byte(type) || utf8 + type > stop) {
            // Sequence extends beyond end.
            return -1;
        }
        while(type-- > 1) {
            ++utf8;
            if (!utf8_byte_is_continuation(*(const uint8_t*)utf8)) {
                return -1;
            }
        }
        ++utf8;
        ++count;
    }
    return count;
}

SkUnichar SkUTF8_ToUnichar(const char utf8[]) {
    SkASSERT(utf8);

    const uint8_t*  p = (const uint8_t*)utf8;
    int             c = *p;
    int             hic = c << 24;

    assert_utf8_leadingbyte(c);

    if (hic < 0) {
        uint32_t mask = (uint32_t)~0x3F;
        hic = SkLeftShift(hic, 1);
        do {
            c = (c << 6) | (*++p & 0x3F);
            mask <<= 5;
        } while ((hic = SkLeftShift(hic, 1)) < 0);
        c &= ~mask;
    }
    return c;
}

// SAFE: returns -1 on invalid UTF-8 sequence.
SkUnichar SkUTF8_NextUnicharWithError(const char** ptr, const char* end) {
    SkASSERT(ptr && *ptr);
    SkASSERT(*ptr < end);
    const uint8_t*  p = (const uint8_t*)*ptr;
    int             c = *p;
    int             hic = c << 24;

    if (!utf8_byte_is_leading_byte(c)) {
        return -1;
    }
    if (hic < 0) {
        uint32_t mask = (uint32_t)~0x3F;
        hic = SkLeftShift(hic, 1);
        do {
            ++p;
            if (p >= (const uint8_t*)end) {
                return -1;
            }
            // check before reading off end of array.
            uint8_t nextByte = *p;
            if (!utf8_byte_is_continuation(nextByte)) {
                return -1;
            }
            c = (c << 6) | (nextByte & 0x3F);
            mask <<= 5;
        } while ((hic = SkLeftShift(hic, 1)) < 0);
        c &= ~mask;
    }
    *ptr = (char*)p + 1;
    return c;
}

SkUnichar SkUTF8_NextUnichar(const char** ptr) {
    SkASSERT(ptr && *ptr);

    const uint8_t*  p = (const uint8_t*)*ptr;
    int             c = *p;
    int             hic = c << 24;

    assert_utf8_leadingbyte(c);

    if (hic < 0) {
        uint32_t mask = (uint32_t)~0x3F;
        hic = SkLeftShift(hic, 1);
        do {
            c = (c << 6) | (*++p & 0x3F);
            mask <<= 5;
        } while ((hic = SkLeftShift(hic, 1)) < 0);
        c &= ~mask;
    }
    *ptr = (char*)p + 1;
    return c;
}

SkUnichar SkUTF8_PrevUnichar(const char** ptr) {
    SkASSERT(ptr && *ptr);

    const char* p = *ptr;

    if (*--p & 0x80) {
        while (*--p & 0x40) {
            ;
        }
    }

    *ptr = (char*)p;
    return SkUTF8_NextUnichar(&p);
}

size_t SkUTF8_FromUnichar(SkUnichar uni, char utf8[]) {
    if ((uint32_t)uni > 0x10FFFF) {
        SkDEBUGFAIL("bad unichar");
        return 0;
    }

    if (uni <= 127) {
        if (utf8) {
            *utf8 = (char)uni;
        }
        return 1;
    }

    char    tmp[4];
    char*   p = tmp;
    size_t  count = 1;

    SkDEBUGCODE(SkUnichar orig = uni;)

    while (uni > 0x7F >> count) {
        *p++ = (char)(0x80 | (uni & 0x3F));
        uni >>= 6;
        count += 1;
    }

    if (utf8) {
        p = tmp;
        utf8 += count;
        while (p < tmp + count - 1) {
            *--utf8 = *p++;
        }
        *--utf8 = (char)(~(0xFF >> count) | uni);
    }

    SkASSERT(utf8 == nullptr || orig == SkUTF8_ToUnichar(utf8));
    return count;
}

///////////////////////////////////////////////////////////////////////////////

int SkUTF16_CountUnichars(const uint16_t src[]) {
    SkASSERT(src);

    int count = 0;
    unsigned c;
    while ((c = *src++) != 0) {
        SkASSERT(!SkUTF16_IsLowSurrogate(c));
        if (SkUTF16_IsHighSurrogate(c)) {
            c = *src++;
            SkASSERT(SkUTF16_IsLowSurrogate(c));
        }
        count += 1;
    }
    return count;
}

// returns -1 on error
int SkUTF16_CountUnichars(const void* text, size_t byteLength) {
    SkASSERT(text);
    if (byteLength == 0) {
        return 0;
    }
    if (!SkIsAlign2(intptr_t(text)) || !SkIsAlign2(byteLength)) {
        return -1;
    }

    const uint16_t* src = static_cast<const uint16_t*>(text);
    const uint16_t* stop = src + (byteLength >> 1);
    int count = 0;
    while (src < stop) {
        unsigned c = *src++;
        SkASSERT(!SkUTF16_IsLowSurrogate(c));
        if (SkUTF16_IsHighSurrogate(c)) {
            if (src >= stop) {
                return -1;
            }
            c = *src++;
            if (!SkUTF16_IsLowSurrogate(c)) {
                return -1;
            }
        }
        count += 1;
    }
    return count;
}

SkUnichar SkUTF16_NextUnichar(const uint16_t** srcPtr) {
    SkASSERT(srcPtr && *srcPtr);

    const uint16_t* src = *srcPtr;
    SkUnichar       c = *src++;

    SkASSERT(!SkUTF16_IsLowSurrogate(c));
    if (SkUTF16_IsHighSurrogate(c)) {
        unsigned c2 = *src++;
        SkASSERT(SkUTF16_IsLowSurrogate(c2));

        // c = ((c & 0x3FF) << 10) + (c2 & 0x3FF) + 0x10000
        // c = (((c & 0x3FF) + 64) << 10) + (c2 & 0x3FF)
        c = (c << 10) + c2 + (0x10000 - (0xD800 << 10) - 0xDC00);
    }
    *srcPtr = src;
    return c;
}

SkUnichar SkUTF16_PrevUnichar(const uint16_t** srcPtr) {
    SkASSERT(srcPtr && *srcPtr);

    const uint16_t* src = *srcPtr;
    SkUnichar       c = *--src;

    SkASSERT(!SkUTF16_IsHighSurrogate(c));
    if (SkUTF16_IsLowSurrogate(c)) {
        unsigned c2 = *--src;
        SkASSERT(SkUTF16_IsHighSurrogate(c2));
        c = (c2 << 10) + c + (0x10000 - (0xD800 << 10) - 0xDC00);
    }
    *srcPtr = src;
    return c;
}

size_t SkUTF16_FromUnichar(SkUnichar uni, uint16_t dst[]) {
    SkASSERT((unsigned)uni <= 0x10FFFF);

    int extra = (uni > 0xFFFF);

    if (dst) {
        if (extra) {
            // dst[0] = SkToU16(0xD800 | ((uni - 0x10000) >> 10));
            // dst[0] = SkToU16(0xD800 | ((uni >> 10) - 64));
            dst[0] = SkToU16((0xD800 - 64) + (uni >> 10));
            dst[1] = SkToU16(0xDC00 | (uni & 0x3FF));

            SkASSERT(SkUTF16_IsHighSurrogate(dst[0]));
            SkASSERT(SkUTF16_IsLowSurrogate(dst[1]));
        } else {
            dst[0] = SkToU16(uni);
            SkASSERT(!SkUTF16_IsHighSurrogate(dst[0]));
            SkASSERT(!SkUTF16_IsLowSurrogate(dst[0]));
        }
    }
    return 1 + extra;
}

size_t SkUTF16_ToUTF8(const uint16_t utf16[], int numberOf16BitValues,
                      char utf8[]) {
    SkASSERT(numberOf16BitValues >= 0);
    if (numberOf16BitValues <= 0) {
        return 0;
    }

    SkASSERT(utf16 != nullptr);

    const uint16_t* stop = utf16 + numberOf16BitValues;
    size_t          size = 0;

    if (utf8 == nullptr) {    // just count
        while (utf16 < stop) {
            size += SkUTF8_FromUnichar(SkUTF16_NextUnichar(&utf16), nullptr);
        }
    } else {
        char* start = utf8;
        while (utf16 < stop) {
            utf8 += SkUTF8_FromUnichar(SkUTF16_NextUnichar(&utf16), utf8);
        }
        size = utf8 - start;
    }
    return size;
}

const char SkHexadecimalDigits::gUpper[16] =
           { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' };
const char SkHexadecimalDigits::gLower[16] =
           { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' };

// returns -1 on error
int SkUTF32_CountUnichars(const void* text, size_t byteLength) {
    if (byteLength == 0) {
        return 0;
    }
    if (!SkIsAlign4(intptr_t(text)) || !SkIsAlign4(byteLength)) {
        return -1;
    }
    const uint32_t kInvalidUnicharMask = 0xFF000000;    // unichar fits in 24 bits
    const uint32_t* ptr = static_cast<const uint32_t*>(text);
    const uint32_t* stop = ptr + (byteLength >> 2);
    while (ptr < stop) {
        if (*ptr & kInvalidUnicharMask) {
            return -1;
        }
        ptr += 1;
    }
    return SkToInt(byteLength >> 2);
}

<|endoftext|>


<commit_before>/**
* @file infectionStep.cpp
* @author Yibo G (https://github.com/nilyibo)
* This function is used to calcualte N(n = 6, r, p) for percolation on regualr lattice
*/

#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <iostream>
#include <cmath>
using namespace std;

// Parameters
#define	n	6	// side per face
#define	r	3	// infection threhold
#define	pmin	0.0
#define	pmax	1.0
#define	pstep	0.01

#define rows 10
#define columns 10
#define simulations 1000

int countInfectedNeighbor(bool grid[rows][columns], int x, int y)
{
	int count = 0;
#if n == 4
	if (x > 0)
		count += (grid[x - 1][y]) ? 1 : 0;
	if (y > 0)
		count += (grid[x][y - 1]) ? 1 : 0;
	if (x + 1 < rows)
		count += (grid[x + 1][y]) ? 1 : 0;
	if (y + 1 < columns)
		count += (grid[x][y + 1]) ? 1 : 0;
#elif n == 6
	// Assume in hexagon grid,
	// odd columns are half above even columns

	// Same for both odd and even
	if (x > 0)
		count += (grid[x - 1][y]) ? 1 : 0;
	if (x + 1 < rows)
		count += (grid[x + 1][y]) ? 1 : 0;

	if (y % 2 == 0) // Odd column
	{
		if (y > 0)
		{
			count += (grid[x][y - 1]) ? 1 : 0;
			if (x > 0)
				count += (grid[x - 1][y - 1]) ? 1 : 0;
		}
		if (y + 1 < columns)
		{
			count += (grid[x][y + 1]) ? 1 : 0;
			if (x > 0)
				count += (grid[x - 1][y + 1]) ? 1 : 0;
		}
	}
	else	// Even column
	{
		if (y > 0)
		{
			count += (grid[x][y - 1]) ? 1 : 0;
			if (x + 1 < rows)
				count += (grid[x + 1][y - 1]) ? 1 : 0;
		}
		if (y + 1 < columns)
		{
			count += (grid[x][y + 1]) ? 1 : 0;
			if (x + 1 < rows)
				count += (grid[x + 1][y + 1]) ? 1 : 0;
		}
	}
#endif
	return count;
}

// Random initial seed
void buildGrid(bool grid[rows][columns], double p)
{
	for (int i = 0; i < rows; ++i)
		for (int j = 0; j < columns; ++j)
			if ((double)rand() / RAND_MAX < p)
				grid[i][j] = true;
			else
				grid[i][j] = false;
}

// Run one round of spread on the grid and set grid changed flag
void oneRound(bool grid[rows][columns], bool & gridChanged)
{
	gridChanged = false;
	bool newGrid[rows][columns];

	for (int i = 0; i < rows; ++i)
		for (int j = 0; j < columns; ++j)
		{
			newGrid[i][j] = grid[i][j];
			if (!grid[i][j] && countInfectedNeighbor(grid, i, j) >= r)
			{
				gridChanged = true;
				newGrid[i][j] = true;
			}
		}

	for (int i = 0; i < rows; ++i)
		for (int j = 0; j < columns; ++j)
			grid[i][j] = newGrid[i][j];
}

// Run one simulation with the given probability
// Returns number of steps
int oneSimulation(double p)
{
	bool grid[rows][columns];
	bool gridChanged = true;
	int count = -1; // Counter is added once even if steps is 0

	buildGrid(grid, p);
	while (gridChanged)
	{
		oneRound(grid, gridChanged);
		++count;
	}

	return count;
}

int main()
{
	// Initialize random seed
	srand(static_cast<unsigned int>(time(NULL)));

	printf("This function calculates N(%d, %d, p).\n\n", n, r);
	printf("Parameters:\nrows = %d, columns = %d, #simulations = %d.\n", rows, columns, simulations);
	printf("p from %f to %f step %f.\n", pmin, pmax, pstep);
	printf("\np, N(%d, %d, p), SD.\n", n, r);

	clock_t t = clock();
	for (double p = pmin; p < pmax; p += pstep)
	{
		int steps = 0, steps2 = 0; // sum of x and sum of x^2
		for (int i = 0; i < simulations; ++i)
		{
			int count = oneSimulation(p);
			steps += count;
			steps2 += count * count;
		}
		double avg = (double)steps / simulations;
		double sd = sqrt((double)steps2 / simulations - avg * avg);
		printf("%f, %f, %f\n", p, avg, sd);
	}

	t = clock() - t;
	printf("\nProcessor time: %f.\n", t / (double)CLOCKS_PER_SEC);

	system("pause");
	return 0;
}
<commit_msg>Added calculation for multiple sizes.<commit_after>/**
* @file infectionStep.cpp
* @author Yibo G (https://github.com/nilyibo)
* This function is used to calcualte N(n = 6, r, p) for percolation on regualr lattice
*/

#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <iostream>
#include <cmath>
#include <omp.h>
using namespace std;

// Parameters
#define	n	6	// side per face
#define	r	3	// infection threhold
#define	pmin	0.05
#define	pmax	0.35
#define	pstep	0.01
#define simulations 100

#define sizemin 5
#define sizemax 200
#define sizestep 5

#define rows size
#define columns size

static int size;
static bool ** grid;		// Current infection status
static bool ** newGrid;	// temp: next round infection status

int countInfectedNeighbor(int x, int y)
{
	int count = 0;
#if n == 4
	if (x > 0)
		count += (grid[x - 1][y]) ? 1 : 0;
	if (y > 0)
		count += (grid[x][y - 1]) ? 1 : 0;
	if (x + 1 < rows)
		count += (grid[x + 1][y]) ? 1 : 0;
	if (y + 1 < columns)
		count += (grid[x][y + 1]) ? 1 : 0;
#elif n == 6
	// Assume in hexagon grid,
	// odd columns are half above even columns

	// Same for both odd and even
	if (x > 0)
		count += (grid[x - 1][y]) ? 1 : 0;
	if (x + 1 < rows)
		count += (grid[x + 1][y]) ? 1 : 0;

	if (y % 2 == 0) // Odd column
	{
		if (y > 0)
		{
			count += (grid[x][y - 1]) ? 1 : 0;
			if (x > 0)
				count += (grid[x - 1][y - 1]) ? 1 : 0;
		}
		if (y + 1 < columns)
		{
			count += (grid[x][y + 1]) ? 1 : 0;
			if (x > 0)
				count += (grid[x - 1][y + 1]) ? 1 : 0;
		}
	}
	else	// Even column
	{
		if (y > 0)
		{
			count += (grid[x][y - 1]) ? 1 : 0;
			if (x + 1 < rows)
				count += (grid[x + 1][y - 1]) ? 1 : 0;
		}
		if (y + 1 < columns)
		{
			count += (grid[x][y + 1]) ? 1 : 0;
			if (x + 1 < rows)
				count += (grid[x + 1][y + 1]) ? 1 : 0;
		}
	}
#endif
	return count;
}

// Random initial seed
void buildGrid(double p)
{
	for (int i = 0; i < rows; ++i)
		for (int j = 0; j < columns; ++j)
			if ((double)rand() / RAND_MAX < p)
				grid[i][j] = true;
			else
				grid[i][j] = false;
}

// Run one round of spread on the grid and set grid changed flag
void oneRound(bool & gridChanged)
{
	gridChanged = false;

	for (int i = 0; i < rows; ++i)
		for (int j = 0; j < columns; ++j)
		{
			if (!grid[i][j] && countInfectedNeighbor(i, j) >= r)
			{
				gridChanged = true;
				newGrid[i][j] = true;
			}
			else
				newGrid[i][j] = grid[i][j];
		}

	for (int i = 0; i < rows; ++i)
		for (int j = 0; j < columns; ++j)
			grid[i][j] = newGrid[i][j];
}

// Run one simulation with the given probability
// Returns number of steps
int oneSimulation(double p)
{
	bool gridChanged = true;
	int count = -1; // Counter is added once even if steps is 0

	buildGrid(p);
	while (gridChanged)
	{
		oneRound(gridChanged);
		++count;
	}

	return count;
}

int main()
{
	// Initialize random seed
	srand(static_cast<unsigned int>(time(NULL)));

	printf("This function calculates N(%d, %d, p).\n\n", n, r);
	printf("Parameters: #simulations = %d.\n", simulations);
	printf("p from %f to %f step %f.\n", pmin, pmax, pstep);
	cout << endl;

	for (size = sizemin; size <= sizemax; size += sizestep)
	{
		printf("p, N(p), SD, size.\n");
		cout << endl;

		clock_t t = clock();

		grid = new bool*[rows];
		for (int i = 0; i < rows; ++i)
			grid[i] = new bool[columns];
		newGrid = new bool*[rows];
		for (int i = 0; i < rows; ++i)
			newGrid[i] = new bool[columns];

		for (double p = pmin; p <= pmax; p += pstep)
		{
			int steps = 0, steps2 = 0; // sum of x and sum of x^2
			for (int i = 0; i < simulations; ++i)
			{
				int count = oneSimulation(p);
				steps += count;
				steps2 += count * count;
			}
			double avg = (double)steps / simulations;
			double sd = sqrt((double)steps2 / simulations - avg * avg);
			printf("%f, %f, %f, %d", p, avg, sd, size);
			cout << endl;
		}

		for (int i = 0; i < rows; ++i)
			delete[] grid[i];
		delete[] grid;
		grid = NULL;
		for (int i = 0; i < rows; ++i)
			delete[] newGrid[i];
		delete[] newGrid;
		newGrid = NULL;

		t = clock() - t;

		printf("\nProcessor time: %f.\n", t / (double)CLOCKS_PER_SEC);
		cout << endl;
	}

	system("pause");
	return 0;
}
<|endoftext|>


<commit_before>// Licensed to the Apache Software Foundation (ASF) under one
// or more contributor license agreements.  See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership.  The ASF licenses this file
// to you 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 <gtest/gtest.h>

#include <algorithm>
#include <random>
#include <string>

#include "arrow/io/file.h"
#include "parquet/bloom_filter.h"
#include "parquet/murmur3.h"
#include "parquet/util/memory.h"
#include "parquet/util/test-common.h"

namespace parquet {
namespace test {
TEST(Murmur3Test, TestBloomFilter) {
  uint64_t result;
  const uint8_t bitset[8] = {0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7};
  ByteArray byteArray(8, bitset);
  MurmurHash3 murmur3;
  result = murmur3.Hash(&byteArray);
  EXPECT_EQ(result, UINT64_C(913737700387071329));
}

TEST(ConstructorTest, TestBloomFilter) {
  BlockSplitBloomFilter bloom_filter;
  EXPECT_NO_THROW(bloom_filter.Init(1000));

  // It throws because the length cannot be zero
  std::unique_ptr<uint8_t[]> bitset1(new uint8_t[1024]());
  EXPECT_THROW(bloom_filter.Init(bitset1.get(), 0), ParquetException);

  // It throws because the number of bytes of Bloom filter bitset must be a power of 2.
  std::unique_ptr<uint8_t[]> bitset2(new uint8_t[1024]());
  EXPECT_THROW(bloom_filter.Init(bitset2.get(), 1023), ParquetException);
}

// The BasicTest is used to test basic operations including InsertHash, FindHash and
// serializing and de-serializing.
TEST(BasicTest, TestBloomFilter) {
  BlockSplitBloomFilter bloom_filter;
  bloom_filter.Init(1024);

  for (int i = 0; i < 10; i++) {
    bloom_filter.InsertHash(bloom_filter.Hash(i));
  }

  for (int i = 0; i < 10; i++) {
    EXPECT_TRUE(bloom_filter.FindHash(bloom_filter.Hash(i)));
  }

  // Serialize Bloom filter to memory output stream
  InMemoryOutputStream sink;
  bloom_filter.WriteTo(&sink);

  // Deserialize Bloom filter from memory
  InMemoryInputStream source(sink.GetBuffer());

  BlockSplitBloomFilter de_bloom = BlockSplitBloomFilter::Deserialize(&source);

  for (int i = 0; i < 10; i++) {
    EXPECT_TRUE(de_bloom.FindHash(de_bloom.Hash(i)));
  }
}

// Helper function to generate random string.
std::string GetRandomString(uint32_t length) {
  // Character set used to generate random string
  const std::string charset =
      "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";

  // The uuid_seed was generated by "uuidgen -r"
  const std::string uuid_seed = "8de406aa-fb59-4195-a81c-5152af26433f";
  std::seed_seq seed(uuid_seed.begin(), uuid_seed.end());
  std::mt19937 generator(seed);
  std::uniform_int_distribution<uint32_t> dist(0, static_cast<int>(charset.size() - 1));
  std::string ret = "";

  for (uint32_t i = 0; i < length; i++) {
    ret += charset[dist(generator)];
  }

  return ret;
}

TEST(FPPTest, TestBloomFilter) {
  // It counts the number of times FindHash returns true.
  int exist = 0;

  // Total count of elements that will be used
#ifdef PARQUET_VALGRIND
  const int total_count = 5000;
#else
  const int total_count = 100000;
#endif

  // Bloom filter fpp parameter
  const double fpp = 0.01;

  std::vector<std::string> members;
  BlockSplitBloomFilter bloom_filter;
  bloom_filter.Init(BlockSplitBloomFilter::OptimalNumOfBits(total_count, fpp));

  // Insert elements into the Bloom filter
  for (int i = 0; i < total_count; i++) {
    // Insert random string which length is 8
    std::string tmp = GetRandomString(8);
    const ByteArray byte_array(8, reinterpret_cast<const uint8_t*>(tmp.c_str()));
    members.push_back(tmp);
    bloom_filter.InsertHash(bloom_filter.Hash(&byte_array));
  }

  for (int i = 0; i < total_count; i++) {
    const ByteArray byte_array1(8, reinterpret_cast<const uint8_t*>(members[i].c_str()));
    ASSERT_TRUE(bloom_filter.FindHash(bloom_filter.Hash(&byte_array1)));
    std::string tmp = GetRandomString(7);
    const ByteArray byte_array2(7, reinterpret_cast<const uint8_t*>(tmp.c_str()));

    if (bloom_filter.FindHash(bloom_filter.Hash(&byte_array2))) {
      exist++;
    }
  }

  // The exist should be probably less than 1000 according default FPP 0.01.
  EXPECT_TRUE(exist < total_count * fpp);
}

// The CompatibilityTest is used to test cross compatibility with parquet-mr, it reads
// the Bloom filter binary generated by the Bloom filter class in the parquet-mr project
// and tests whether the values inserted before could be filtered or not.

// The Bloom filter binary is generated by three steps in from Parquet-mr.
// Step 1: Construct a Bloom filter with 1024 bytes bitset.
// Step 2: Insert "hello", "parquet", "bloom", "filter" to Bloom filter.
// Step 3: Call writeTo API to write to File.
TEST(CompatibilityTest, TestBloomFilter) {
  const std::string test_string[4] = {"hello", "parquet", "bloom", "filter"};
  const std::string bloom_filter_test_binary =
      std::string(test::get_data_dir()) + "/bloom_filter.bin";
  std::shared_ptr<::arrow::io::ReadableFile> handle;

  int64_t size;
  PARQUET_THROW_NOT_OK(
      ::arrow::io::ReadableFile::Open(bloom_filter_test_binary, &handle));
  PARQUET_THROW_NOT_OK(handle->GetSize(&size));

  // 1024 bytes (bitset) + 4 bytes (hash) + 4 bytes (algorithm) + 4 bytes (length)
  EXPECT_EQ(size, 1036);

  std::unique_ptr<uint8_t[]> bitset(new uint8_t[size]());
  std::shared_ptr<Buffer> buffer(new Buffer(bitset.get(), size));
  handle->Read(size, &buffer);

  InMemoryInputStream source(buffer);
  BlockSplitBloomFilter bloom_filter1 = BlockSplitBloomFilter::Deserialize(&source);

  for (int i = 0; i < 4; i++) {
    const ByteArray tmp(static_cast<uint32_t>(test_string[i].length()),
                        reinterpret_cast<const uint8_t*>(test_string[i].c_str()));
    EXPECT_TRUE(bloom_filter1.FindHash(bloom_filter1.Hash(&tmp)));
  }

  // The following is used to check whether the new created Bloom filter in parquet-cpp is
  // byte-for-byte identical to file at bloom_data_path which is created from parquet-mr
  // with same inserted hashes.
  BlockSplitBloomFilter bloom_filter2;
  bloom_filter2.Init(bloom_filter1.GetBitsetSize());
  for (int i = 0; i < 4; i++) {
    const ByteArray byte_array(static_cast<uint32_t>(test_string[i].length()),
                               reinterpret_cast<const uint8_t*>(test_string[i].c_str()));
    bloom_filter2.InsertHash(bloom_filter2.Hash(&byte_array));
  }

  // Serialize Bloom filter to memory output stream
  InMemoryOutputStream sink;
  bloom_filter2.WriteTo(&sink);
  std::shared_ptr<Buffer> buffer1 = sink.GetBuffer();

  handle->Seek(0);
  handle->GetSize(&size);
  std::shared_ptr<Buffer> buffer2;
  handle->Read(size, &buffer2);

  EXPECT_TRUE((*buffer1).Equals(*buffer2));
}

// OptmialValueTest is used to test whether OptimalNumOfBits returns expected
// numbers according to formula:
//     num_of_bits = -8.0 * ndv / log(1 - pow(fpp, 1.0 / 8.0))
// where ndv is the number of distinct values and fpp is the false positive probability.
// Also it is used to test whether OptimalNumOfBits returns value between
// [MINIMUM_BLOOM_FILTER_SIZE, MAXIMUM_BLOOM_FILTER_SIZE].
TEST(OptimalValueTest, TestBloomFilter) {
  EXPECT_EQ(BlockSplitBloomFilter::OptimalNumOfBits(256, 0.01), UINT32_C(4096));
  EXPECT_EQ(BlockSplitBloomFilter::OptimalNumOfBits(512, 0.01), UINT32_C(8192));
  EXPECT_EQ(BlockSplitBloomFilter::OptimalNumOfBits(1024, 0.01), UINT32_C(16384));
  EXPECT_EQ(BlockSplitBloomFilter::OptimalNumOfBits(2048, 0.01), UINT32_C(32768));

  EXPECT_EQ(BlockSplitBloomFilter::OptimalNumOfBits(200, 0.01), UINT32_C(2048));
  EXPECT_EQ(BlockSplitBloomFilter::OptimalNumOfBits(300, 0.01), UINT32_C(4096));
  EXPECT_EQ(BlockSplitBloomFilter::OptimalNumOfBits(700, 0.01), UINT32_C(8192));
  EXPECT_EQ(BlockSplitBloomFilter::OptimalNumOfBits(1500, 0.01), UINT32_C(16384));

  EXPECT_EQ(BlockSplitBloomFilter::OptimalNumOfBits(200, 0.025), UINT32_C(2048));
  EXPECT_EQ(BlockSplitBloomFilter::OptimalNumOfBits(300, 0.025), UINT32_C(4096));
  EXPECT_EQ(BlockSplitBloomFilter::OptimalNumOfBits(700, 0.025), UINT32_C(8192));
  EXPECT_EQ(BlockSplitBloomFilter::OptimalNumOfBits(1500, 0.025), UINT32_C(16384));

  EXPECT_EQ(BlockSplitBloomFilter::OptimalNumOfBits(200, 0.05), UINT32_C(2048));
  EXPECT_EQ(BlockSplitBloomFilter::OptimalNumOfBits(300, 0.05), UINT32_C(4096));
  EXPECT_EQ(BlockSplitBloomFilter::OptimalNumOfBits(700, 0.05), UINT32_C(8192));
  EXPECT_EQ(BlockSplitBloomFilter::OptimalNumOfBits(1500, 0.05), UINT32_C(16384));

  // Boundary check
  EXPECT_EQ(BlockSplitBloomFilter::OptimalNumOfBits(4, 0.01), UINT32_C(256));
  EXPECT_EQ(BlockSplitBloomFilter::OptimalNumOfBits(4, 0.25), UINT32_C(256));

  EXPECT_EQ(
      BlockSplitBloomFilter::OptimalNumOfBits(std::numeric_limits<uint32_t>::max(), 0.01),
      UINT32_C(1073741824));
  EXPECT_EQ(
      BlockSplitBloomFilter::OptimalNumOfBits(std::numeric_limits<uint32_t>::max(), 0.25),
      UINT32_C(1073741824));
}

}  // namespace test

}  // namespace parquet
<commit_msg>PARQUET-1384: fix clang build error for bloom_filter-test.cc<commit_after>// Licensed to the Apache Software Foundation (ASF) under one
// or more contributor license agreements.  See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership.  The ASF licenses this file
// to you 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 <gtest/gtest.h>

#include <algorithm>
#include <random>
#include <string>

#include "arrow/io/file.h"
#include "parquet/bloom_filter.h"
#include "parquet/murmur3.h"
#include "parquet/util/memory.h"
#include "parquet/util/test-common.h"

namespace parquet {
namespace test {
TEST(Murmur3Test, TestBloomFilter) {
  uint64_t result;
  const uint8_t bitset[8] = {0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7};
  ByteArray byteArray(8, bitset);
  MurmurHash3 murmur3;
  result = murmur3.Hash(&byteArray);
  EXPECT_EQ(result, UINT64_C(913737700387071329));
}

TEST(ConstructorTest, TestBloomFilter) {
  BlockSplitBloomFilter bloom_filter;
  EXPECT_NO_THROW(bloom_filter.Init(1000));

  // It throws because the length cannot be zero
  std::unique_ptr<uint8_t[]> bitset1(new uint8_t[1024]());
  EXPECT_THROW(bloom_filter.Init(bitset1.get(), 0), ParquetException);

  // It throws because the number of bytes of Bloom filter bitset must be a power of 2.
  std::unique_ptr<uint8_t[]> bitset2(new uint8_t[1024]());
  EXPECT_THROW(bloom_filter.Init(bitset2.get(), 1023), ParquetException);
}

// The BasicTest is used to test basic operations including InsertHash, FindHash and
// serializing and de-serializing.
TEST(BasicTest, TestBloomFilter) {
  BlockSplitBloomFilter bloom_filter;
  bloom_filter.Init(1024);

  for (int i = 0; i < 10; i++) {
    bloom_filter.InsertHash(bloom_filter.Hash(i));
  }

  for (int i = 0; i < 10; i++) {
    EXPECT_TRUE(bloom_filter.FindHash(bloom_filter.Hash(i)));
  }

  // Serialize Bloom filter to memory output stream
  InMemoryOutputStream sink;
  bloom_filter.WriteTo(&sink);

  // Deserialize Bloom filter from memory
  InMemoryInputStream source(sink.GetBuffer());

  BlockSplitBloomFilter de_bloom = BlockSplitBloomFilter::Deserialize(&source);

  for (int i = 0; i < 10; i++) {
    EXPECT_TRUE(de_bloom.FindHash(de_bloom.Hash(i)));
  }
}

// Helper function to generate random string.
std::string GetRandomString(uint32_t length) {
  // Character set used to generate random string
  const std::string charset =
      "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";

  // The uuid_seed was generated by "uuidgen -r"
  const std::string uuid_seed = "8de406aa-fb59-4195-a81c-5152af26433f";
  std::seed_seq seed(uuid_seed.begin(), uuid_seed.end());
  std::mt19937 generator(seed);
  std::uniform_int_distribution<uint32_t> dist(0, static_cast<int>(charset.size() - 1));
  std::string ret = "";

  for (uint32_t i = 0; i < length; i++) {
    ret += charset[dist(generator)];
  }

  return ret;
}

TEST(FPPTest, TestBloomFilter) {
  // It counts the number of times FindHash returns true.
  int exist = 0;

  // Total count of elements that will be used
#ifdef PARQUET_VALGRIND
  const int total_count = 5000;
#else
  const int total_count = 100000;
#endif

  // Bloom filter fpp parameter
  const double fpp = 0.01;

  std::vector<std::string> members;
  BlockSplitBloomFilter bloom_filter;
  bloom_filter.Init(BlockSplitBloomFilter::OptimalNumOfBits(total_count, fpp));

  // Insert elements into the Bloom filter
  for (int i = 0; i < total_count; i++) {
    // Insert random string which length is 8
    std::string tmp = GetRandomString(8);
    const ByteArray byte_array(8, reinterpret_cast<const uint8_t*>(tmp.c_str()));
    members.push_back(tmp);
    bloom_filter.InsertHash(bloom_filter.Hash(&byte_array));
  }

  for (int i = 0; i < total_count; i++) {
    const ByteArray byte_array1(8, reinterpret_cast<const uint8_t*>(members[i].c_str()));
    ASSERT_TRUE(bloom_filter.FindHash(bloom_filter.Hash(&byte_array1)));
    std::string tmp = GetRandomString(7);
    const ByteArray byte_array2(7, reinterpret_cast<const uint8_t*>(tmp.c_str()));

    if (bloom_filter.FindHash(bloom_filter.Hash(&byte_array2))) {
      exist++;
    }
  }

  // The exist should be probably less than 1000 according default FPP 0.01.
  EXPECT_TRUE(exist < total_count * fpp);
}

// The CompatibilityTest is used to test cross compatibility with parquet-mr, it reads
// the Bloom filter binary generated by the Bloom filter class in the parquet-mr project
// and tests whether the values inserted before could be filtered or not.

// The Bloom filter binary is generated by three steps in from Parquet-mr.
// Step 1: Construct a Bloom filter with 1024 bytes bitset.
// Step 2: Insert "hello", "parquet", "bloom", "filter" to Bloom filter.
// Step 3: Call writeTo API to write to File.
TEST(CompatibilityTest, TestBloomFilter) {
  const std::string test_string[4] = {"hello", "parquet", "bloom", "filter"};
  const std::string bloom_filter_test_binary =
      std::string(test::get_data_dir()) + "/bloom_filter.bin";
  std::shared_ptr<::arrow::io::ReadableFile> handle;

  int64_t size;
  PARQUET_THROW_NOT_OK(
      ::arrow::io::ReadableFile::Open(bloom_filter_test_binary, &handle));
  PARQUET_THROW_NOT_OK(handle->GetSize(&size));

  // 1024 bytes (bitset) + 4 bytes (hash) + 4 bytes (algorithm) + 4 bytes (length)
  EXPECT_EQ(size, 1036);

  std::unique_ptr<uint8_t[]> bitset(new uint8_t[size]());
  std::shared_ptr<Buffer> buffer(new Buffer(bitset.get(), size));
  PARQUET_THROW_NOT_OK(handle->Read(size, &buffer));

  InMemoryInputStream source(buffer);
  BlockSplitBloomFilter bloom_filter1 = BlockSplitBloomFilter::Deserialize(&source);

  for (int i = 0; i < 4; i++) {
    const ByteArray tmp(static_cast<uint32_t>(test_string[i].length()),
                        reinterpret_cast<const uint8_t*>(test_string[i].c_str()));
    EXPECT_TRUE(bloom_filter1.FindHash(bloom_filter1.Hash(&tmp)));
  }

  // The following is used to check whether the new created Bloom filter in parquet-cpp is
  // byte-for-byte identical to file at bloom_data_path which is created from parquet-mr
  // with same inserted hashes.
  BlockSplitBloomFilter bloom_filter2;
  bloom_filter2.Init(bloom_filter1.GetBitsetSize());
  for (int i = 0; i < 4; i++) {
    const ByteArray byte_array(static_cast<uint32_t>(test_string[i].length()),
                               reinterpret_cast<const uint8_t*>(test_string[i].c_str()));
    bloom_filter2.InsertHash(bloom_filter2.Hash(&byte_array));
  }

  // Serialize Bloom filter to memory output stream
  InMemoryOutputStream sink;
  bloom_filter2.WriteTo(&sink);
  std::shared_ptr<Buffer> buffer1 = sink.GetBuffer();

  PARQUET_THROW_NOT_OK(handle->Seek(0));
  PARQUET_THROW_NOT_OK(handle->GetSize(&size));
  std::shared_ptr<Buffer> buffer2;
  PARQUET_THROW_NOT_OK(handle->Read(size, &buffer2));

  EXPECT_TRUE((*buffer1).Equals(*buffer2));
}

// OptmialValueTest is used to test whether OptimalNumOfBits returns expected
// numbers according to formula:
//     num_of_bits = -8.0 * ndv / log(1 - pow(fpp, 1.0 / 8.0))
// where ndv is the number of distinct values and fpp is the false positive probability.
// Also it is used to test whether OptimalNumOfBits returns value between
// [MINIMUM_BLOOM_FILTER_SIZE, MAXIMUM_BLOOM_FILTER_SIZE].
TEST(OptimalValueTest, TestBloomFilter) {
  EXPECT_EQ(BlockSplitBloomFilter::OptimalNumOfBits(256, 0.01), UINT32_C(4096));
  EXPECT_EQ(BlockSplitBloomFilter::OptimalNumOfBits(512, 0.01), UINT32_C(8192));
  EXPECT_EQ(BlockSplitBloomFilter::OptimalNumOfBits(1024, 0.01), UINT32_C(16384));
  EXPECT_EQ(BlockSplitBloomFilter::OptimalNumOfBits(2048, 0.01), UINT32_C(32768));

  EXPECT_EQ(BlockSplitBloomFilter::OptimalNumOfBits(200, 0.01), UINT32_C(2048));
  EXPECT_EQ(BlockSplitBloomFilter::OptimalNumOfBits(300, 0.01), UINT32_C(4096));
  EXPECT_EQ(BlockSplitBloomFilter::OptimalNumOfBits(700, 0.01), UINT32_C(8192));
  EXPECT_EQ(BlockSplitBloomFilter::OptimalNumOfBits(1500, 0.01), UINT32_C(16384));

  EXPECT_EQ(BlockSplitBloomFilter::OptimalNumOfBits(200, 0.025), UINT32_C(2048));
  EXPECT_EQ(BlockSplitBloomFilter::OptimalNumOfBits(300, 0.025), UINT32_C(4096));
  EXPECT_EQ(BlockSplitBloomFilter::OptimalNumOfBits(700, 0.025), UINT32_C(8192));
  EXPECT_EQ(BlockSplitBloomFilter::OptimalNumOfBits(1500, 0.025), UINT32_C(16384));

  EXPECT_EQ(BlockSplitBloomFilter::OptimalNumOfBits(200, 0.05), UINT32_C(2048));
  EXPECT_EQ(BlockSplitBloomFilter::OptimalNumOfBits(300, 0.05), UINT32_C(4096));
  EXPECT_EQ(BlockSplitBloomFilter::OptimalNumOfBits(700, 0.05), UINT32_C(8192));
  EXPECT_EQ(BlockSplitBloomFilter::OptimalNumOfBits(1500, 0.05), UINT32_C(16384));

  // Boundary check
  EXPECT_EQ(BlockSplitBloomFilter::OptimalNumOfBits(4, 0.01), UINT32_C(256));
  EXPECT_EQ(BlockSplitBloomFilter::OptimalNumOfBits(4, 0.25), UINT32_C(256));

  EXPECT_EQ(
      BlockSplitBloomFilter::OptimalNumOfBits(std::numeric_limits<uint32_t>::max(), 0.01),
      UINT32_C(1073741824));
  EXPECT_EQ(
      BlockSplitBloomFilter::OptimalNumOfBits(std::numeric_limits<uint32_t>::max(), 0.25),
      UINT32_C(1073741824));
}

}  // namespace test

}  // namespace parquet
<|endoftext|>


<commit_before>/*************************************************************************
 *
 *  $RCSfile: renderer.hxx,v $
 *
 *  $Revision: 1.4 $
 *
 *  last change: $Author: rt $ $Date: 2004-11-26 20:52:01 $
 *
 *  The Contents of this file are made available subject to the terms of
 *  either of the following licenses
 *
 *         - GNU Lesser General Public License Version 2.1
 *         - Sun Industry Standards Source License Version 1.1
 *
 *  Sun Microsystems Inc., October, 2000
 *
 *  GNU Lesser General Public License Version 2.1
 *  =============================================
 *  Copyright 2000 by Sun Microsystems, Inc.
 *  901 San Antonio Road, Palo Alto, CA 94303, USA
 *
 *  This library is free software; you can redistribute it and/or
 *  modify it under the terms of the GNU Lesser General Public
 *  License version 2.1, as published by the Free Software Foundation.
 *
 *  This library is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 *  Lesser General Public License for more details.
 *
 *  You should have received a copy of the GNU Lesser General Public
 *  License along with this library; if not, write to the Free Software
 *  Foundation, Inc., 59 Temple Place, Suite 330, Boston,
 *  MA  02111-1307  USA
 *
 *
 *  Sun Industry Standards Source License Version 1.1
 *  =================================================
 *  The contents of this file are subject to the Sun Industry Standards
 *  Source License Version 1.1 (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.openoffice.org/license.html.
 *
 *  Software provided under this License is provided on an "AS IS" basis,
 *  WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING,
 *  WITHOUT LIMITATION, WARRANTIES THAT THE SOFTWARE IS FREE OF DEFECTS,
 *  MERCHANTABLE, FIT FOR A PARTICULAR PURPOSE, OR NON-INFRINGING.
 *  See the License for the specific provisions governing your rights and
 *  obligations concerning the Software.
 *
 *  The Initial Developer of the Original Code is: Sun Microsystems, Inc.
 *
 *  Copyright: 2000 by Sun Microsystems, Inc.
 *
 *  All Rights Reserved.
 *
 *  Contributor(s): _______________________________________
 *
 *
 ************************************************************************/

#ifndef _CPPCANVAS_RENDERER_HXX
#define _CPPCANVAS_RENDERER_HXX

#ifndef _SAL_TYPES_H_
#include <sal/types.h>
#endif

#ifndef _RTL_USTRING_HXX_
#include <rtl/ustring.hxx>
#endif

#ifndef BOOST_SHARED_PTR_HPP_INCLUDED
#include <boost/shared_ptr.hpp>
#endif

#ifndef _COMPHELPER_OPTIONALVALUE_HXX
#include <comphelper/optionalvalue.hxx>
#endif

#ifndef _BGFX_MATRIX_B2DHOMMATRIX_HXX
#include <basegfx/matrix/b2dhommatrix.hxx>
#endif

#ifndef _CPPCANVAS_CANVASGRAPHIC_HXX
#include <cppcanvas/canvasgraphic.hxx>
#endif
#ifndef _CPPCANVAS_COLOR_HXX
#include <cppcanvas/color.hxx>
#endif

/* Definition of Renderer interface */

namespace cppcanvas
{

    class Renderer : public virtual CanvasGraphic
    {
    public:
        /** Render subset of metafile to given canvas

            This method renders the given subset of the content to the
            associated canvas.

            @param nStartIndex
            The index of the first action to be rendered (the indices
            correspond to the action indices of the originating
            GDIMetaFile).

            @param nEndIndex
            The index of the first action _not_ painted anymore,
            i.e. the action after the last action rendered (the
            indices correspond to the action indices of the
            originating GDIMetaFile).

            @return whether the rendering finished successfully.
         */
        virtual bool drawSubset( int    nStartIndex,
                                 int    nEndIndex ) const = 0;

        /** Parameters for the Renderer
         */
        struct Parameters
        {
            /// Optionally forces the fill color attribute for all actions
            ::comphelper::OptionalValue< Color::IntSRGBA >          maFillColor;

            /// Optionally forces the line color attribute for all actions
            ::comphelper::OptionalValue< Color::IntSRGBA >          maLineColor;

            /// Optionally forces the text color attribute for all actions
            ::comphelper::OptionalValue< Color::IntSRGBA >          maTextColor;

            /// Optionally forces the given fontname for all text actions
            ::comphelper::OptionalValue< ::rtl::OUString >          maFontName;

            /** Optionally transforms all text output actions with the
                given matrix (in addition to the overall canvas
                transformation).

                Note that the matrix given here is applied to the unit
                rect coordinate system, i.e. the metafile is assumed
                to be contained in the unit rect.
             */
            ::comphelper::OptionalValue< ::basegfx::B2DHomMatrix >  maTextTransformation;

            /// Optionally forces the given font weight for all text actions
            ::comphelper::OptionalValue< sal_Int8 >                 maFontWeight;

            /// Optionally forces the given font letter form (italics etc.) for all text actions
            ::comphelper::OptionalValue< sal_Int8 >                 maFontLetterForm;

            /// Optionally forces underlining for all text actions
            ::comphelper::OptionalValue< bool >                     maFontUnderline;
        };
    };

    typedef ::boost::shared_ptr< ::cppcanvas::Renderer > RendererSharedPtr;
}

#endif /* _CPPCANVAS_RENDERER_HXX */
<commit_msg>INTEGRATION: CWS presfixes02 (1.4.14); FILE MERGED 2005/03/18 18:57:04 thb 1.4.14.3: #i44515# Finished subsetting rework (removed plain int from interfaces) 2005/03/16 17:40:38 thb 1.4.14.2: #i35136# For bitmap textures with a transparent gradient, falling back to TransparencyGroupAction (XCanvas currently cannot handle both alpha gradient and texture) 2005/03/14 16:04:50 thb 1.4.14.1: #i35136# #i36914# #i41113# #i44100# #i40115# #i41839# #i44404# Merge from presfixes01 patches<commit_after>/*************************************************************************
 *
 *  $RCSfile: renderer.hxx,v $
 *
 *  $Revision: 1.5 $
 *
 *  last change: $Author: rt $ $Date: 2005-03-30 08:22:27 $
 *
 *  The Contents of this file are made available subject to the terms of
 *  either of the following licenses
 *
 *         - GNU Lesser General Public License Version 2.1
 *         - Sun Industry Standards Source License Version 1.1
 *
 *  Sun Microsystems Inc., October, 2000
 *
 *  GNU Lesser General Public License Version 2.1
 *  =============================================
 *  Copyright 2000 by Sun Microsystems, Inc.
 *  901 San Antonio Road, Palo Alto, CA 94303, USA
 *
 *  This library is free software; you can redistribute it and/or
 *  modify it under the terms of the GNU Lesser General Public
 *  License version 2.1, as published by the Free Software Foundation.
 *
 *  This library is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 *  Lesser General Public License for more details.
 *
 *  You should have received a copy of the GNU Lesser General Public
 *  License along with this library; if not, write to the Free Software
 *  Foundation, Inc., 59 Temple Place, Suite 330, Boston,
 *  MA  02111-1307  USA
 *
 *
 *  Sun Industry Standards Source License Version 1.1
 *  =================================================
 *  The contents of this file are subject to the Sun Industry Standards
 *  Source License Version 1.1 (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.openoffice.org/license.html.
 *
 *  Software provided under this License is provided on an "AS IS" basis,
 *  WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING,
 *  WITHOUT LIMITATION, WARRANTIES THAT THE SOFTWARE IS FREE OF DEFECTS,
 *  MERCHANTABLE, FIT FOR A PARTICULAR PURPOSE, OR NON-INFRINGING.
 *  See the License for the specific provisions governing your rights and
 *  obligations concerning the Software.
 *
 *  The Initial Developer of the Original Code is: Sun Microsystems, Inc.
 *
 *  Copyright: 2000 by Sun Microsystems, Inc.
 *
 *  All Rights Reserved.
 *
 *  Contributor(s): _______________________________________
 *
 *
 ************************************************************************/

#ifndef _CPPCANVAS_RENDERER_HXX
#define _CPPCANVAS_RENDERER_HXX

#ifndef _SAL_TYPES_H_
#include <sal/types.h>
#endif

#ifndef _RTL_USTRING_HXX_
#include <rtl/ustring.hxx>
#endif

#ifndef BOOST_SHARED_PTR_HPP_INCLUDED
#include <boost/shared_ptr.hpp>
#endif

#ifndef _COMPHELPER_OPTIONALVALUE_HXX
#include <comphelper/optionalvalue.hxx>
#endif

#ifndef _BGFX_MATRIX_B2DHOMMATRIX_HXX
#include <basegfx/matrix/b2dhommatrix.hxx>
#endif

#ifndef _CPPCANVAS_CANVASGRAPHIC_HXX
#include <cppcanvas/canvasgraphic.hxx>
#endif
#ifndef _CPPCANVAS_COLOR_HXX
#include <cppcanvas/color.hxx>
#endif

/* Definition of Renderer interface */

namespace cppcanvas
{

    class Renderer : public virtual CanvasGraphic
    {
    public:
        /** Render subset of metafile to given canvas

            This method renders the given subset of the content to the
            associated canvas.

            @param nStartIndex
            The index of the first action to be rendered (the indices
            correspond roughly to the action indices of the
            originating GDIMetaFile. Note, although, that certain
            actions, e.g. text, accounts for more than one index: a
            text produces as many addressable indices as it has
            characters).

            @param nEndIndex
            The index of the first action _not_ painted anymore,
            i.e. the action after the last action rendered (the
            indices correspond roughly to the action indices of the
            originating GDIMetaFile. Note, although, that certain
            actions, e.g. text, accounts for more than one index: a
            text produces as many addressable indices as it has
            characters).

            @return whether the rendering finished successfully.
         */
        virtual bool drawSubset( sal_Int32  nStartIndex,
                                 sal_Int32  nEndIndex ) const = 0;

        /** Parameters for the Renderer
         */
        struct Parameters
        {
            /// Optionally forces the fill color attribute for all actions
            ::comphelper::OptionalValue< Color::IntSRGBA >          maFillColor;

            /// Optionally forces the line color attribute for all actions
            ::comphelper::OptionalValue< Color::IntSRGBA >          maLineColor;

            /// Optionally forces the text color attribute for all actions
            ::comphelper::OptionalValue< Color::IntSRGBA >          maTextColor;

            /// Optionally forces the given fontname for all text actions
            ::comphelper::OptionalValue< ::rtl::OUString >          maFontName;

            /** Optionally transforms all text output actions with the
                given matrix (in addition to the overall canvas
                transformation).

                Note that the matrix given here is applied to the unit
                rect coordinate system, i.e. the metafile is assumed
                to be contained in the unit rect.
             */
            ::comphelper::OptionalValue< ::basegfx::B2DHomMatrix >  maTextTransformation;

            /// Optionally forces the given font weight for all text actions
            ::comphelper::OptionalValue< sal_Int8 >                 maFontWeight;

            /// Optionally forces the given font letter form (italics etc.) for all text actions
            ::comphelper::OptionalValue< sal_Int8 >                 maFontLetterForm;

            /// Optionally forces underlining for all text actions
            ::comphelper::OptionalValue< bool >                     maFontUnderline;
        };
    };

    typedef ::boost::shared_ptr< ::cppcanvas::Renderer > RendererSharedPtr;
}

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


<commit_before>/*************************************************************************
 *
 *  $RCSfile: clipboardctl.cxx,v $
 *
 *  $Revision: 1.10 $
 *
 *  last change: $Author: obo $ $Date: 2005-03-15 09:28:39 $
 *
 *  The Contents of this file are made available subject to the terms of
 *  either of the following licenses
 *
 *         - GNU Lesser General Public License Version 2.1
 *         - Sun Industry Standards Source License Version 1.1
 *
 *  Sun Microsystems Inc., October, 2000
 *
 *  GNU Lesser General Public License Version 2.1
 *  =============================================
 *  Copyright 2000 by Sun Microsystems, Inc.
 *  901 San Antonio Road, Palo Alto, CA 94303, USA
 *
 *  This library is free software; you can redistribute it and/or
 *  modify it under the terms of the GNU Lesser General Public
 *  License version 2.1, as published by the Free Software Foundation.
 *
 *  This library is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 *  Lesser General Public License for more details.
 *
 *  You should have received a copy of the GNU Lesser General Public
 *  License along with this library; if not, write to the Free Software
 *  Foundation, Inc., 59 Temple Place, Suite 330, Boston,
 *  MA  02111-1307  USA
 *
 *
 *  Sun Industry Standards Source License Version 1.1
 *  =================================================
 *  The contents of this file are subject to the Sun Industry Standards
 *  Source License Version 1.1 (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.openoffice.org/license.html.
 *
 *  Software provided under this License is provided on an "AS IS" basis,
 *  WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING,
 *  WITHOUT LIMITATION, WARRANTIES THAT THE SOFTWARE IS FREE OF DEFECTS,
 *  MERCHANTABLE, FIT FOR A PARTICULAR PURPOSE, OR NON-INFRINGING.
 *  See the License for the specific provisions governing your rights and
 *  obligations concerning the Software.
 *
 *  The Initial Developer of the Original Code is: Sun Microsystems, Inc.
 *
 *  Copyright: 2000 by Sun Microsystems, Inc.
 *
 *  All Rights Reserved.
 *
 *  Contributor(s): _______________________________________
 *
 *
 ************************************************************************/

#ifndef _SFXAPP_HXX
#include <sfx2/app.hxx>
#endif
#ifndef _SFXTBXCTRL_HXX
#include <sfx2/tbxctrl.hxx>
#endif
#ifndef _SFX_BINDINGS_HXX
#include <sfx2/bindings.hxx>
#endif
#ifndef _SFXDISPATCH_HXX
#include <sfx2/dispatch.hxx>
#endif
#ifndef _SV_GEN_HXX
#include <tools/gen.hxx>
#endif
#ifndef _SFXINTITEM_HXX
#include <svtools/intitem.hxx>
#endif
#ifndef _SOT_EXCHANGE_HXX
#include <sot/exchange.hxx>
#endif
#ifndef _SFXENUMITEM_HXX
#include <svtools/eitem.hxx>
#endif
#ifndef _SV_TOOLBOX_HXX
#include <vcl/toolbox.hxx>
#endif

#ifndef _SVX_CLIPBOARDCTL_HXX_
#include <clipboardctl.hxx>
#endif
#ifndef _SVX_CLIPFMTITEM_HXX
#include <clipfmtitem.hxx>
#endif

#include <svtools/insdlg.hxx>
#include "svxids.hrc"

using namespace ::com::sun::star::uno;
using namespace ::com::sun::star::beans;

/////////////////////////////////////////////////////////////////

SFX_IMPL_TOOLBOX_CONTROL( SvxClipBoardControl, SfxVoidItem /*SfxUInt16Item*/ );

SvxClipBoardControl::SvxClipBoardControl(
        USHORT nSlotId, USHORT nId, ToolBox& rTbx ) :

    SfxToolBoxControl( nSlotId, nId, rTbx ),
    pPopup      (0),
    nItemId     (nId),
    pClipboardFmtItem( 0 ),
    bDisabled( FALSE )
{
    addStatusListener( rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( ".uno:ClipboardFormatItems" )));
    ToolBox& rBox = GetToolBox();
    rBox.SetItemBits( nId, TIB_DROPDOWN | rBox.GetItemBits( nId ) );
    rBox.Invalidate();
}

SvxClipBoardControl::~SvxClipBoardControl()
{
    DelPopup();
    delete pClipboardFmtItem;
}

SfxPopupWindow* SvxClipBoardControl::CreatePopupWindow()
{
    const SvxClipboardFmtItem* pFmtItem = PTR_CAST( SvxClipboardFmtItem, pClipboardFmtItem );
    if ( pFmtItem )
    {
        if (pPopup)
            pPopup->Clear();
        else
            pPopup = new PopupMenu;

        USHORT nCount = pFmtItem->Count();
        for (USHORT i = 0;  i < nCount;  ++i)
        {
            ULONG nFmtID =  pFmtItem->GetClipbrdFormatId( i );
            String aFmtStr( pFmtItem->GetClipbrdFormatName( i ) );
            if (!aFmtStr.Len())
              aFmtStr = SvPasteObjectHelper::GetSotFormatUIName( nFmtID );
            pPopup->InsertItem( (USHORT)nFmtID, aFmtStr );
        }

        ToolBox& rBox = GetToolBox();
        USHORT nId = GetId();
        rBox.SetItemDown( nId, TRUE );

        pPopup->Execute( &rBox, rBox.GetItemRect( nId ),
            (rBox.GetAlign() == WINDOWALIGN_TOP || rBox.GetAlign() == WINDOWALIGN_BOTTOM) ?
                POPUPMENU_EXECUTE_DOWN : POPUPMENU_EXECUTE_RIGHT );

        rBox.SetItemDown( nId, FALSE );

        SfxUInt32Item aItem( SID_CLIPBOARD_FORMAT_ITEMS, pPopup->GetCurItemId() );

        Any a;
        Sequence< PropertyValue > aArgs( 1 );
        aArgs[0].Name = rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "SelectedFormat" ));
        aItem.QueryValue( a );
        aArgs[0].Value = a;
        Dispatch( rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( ".uno:ClipboardFormatItems" )),
                  aArgs );
    }

    GetToolBox().EndSelection();
    DelPopup();
    return 0;
}

SfxPopupWindowType SvxClipBoardControl::GetPopupWindowType() const
{
    return SFX_POPUPWINDOW_ONTIMEOUT;
}

void SvxClipBoardControl::StateChanged( USHORT nSID, SfxItemState eState, const SfxPoolItem* pState )
{
    if ( SID_CLIPBOARD_FORMAT_ITEMS == nSID )
    {
        DELETEZ( pClipboardFmtItem );
        if ( eState >= SFX_ITEM_AVAILABLE )
        {
            pClipboardFmtItem = pState->Clone();
            GetToolBox().SetItemBits( GetId(), GetToolBox().GetItemBits( GetId() ) | TIB_DROPDOWN );
        }
        else if ( !bDisabled )
            GetToolBox().SetItemBits( GetId(), GetToolBox().GetItemBits( GetId() ) & ~TIB_DROPDOWN );
        GetToolBox().Invalidate( GetToolBox().GetItemRect( GetId() ) );
    }
    else
    {
        // enable the item as a whole
        bDisabled = (GetItemState(pState) == SFX_ITEM_DISABLED);
        GetToolBox().EnableItem( GetId(), (GetItemState(pState) != SFX_ITEM_DISABLED) );
    }
}

void SvxClipBoardControl::DelPopup()
{
    if(pPopup)
    {
        delete pPopup;
        pPopup = 0;
    }
}

/////////////////////////////////////////////////////////////////

<commit_msg>INTEGRATION: CWS ooo19126 (1.10.264); FILE MERGED 2005/09/05 14:25:52 rt 1.10.264.1: #i54170# Change license header: remove SISSL<commit_after>/*************************************************************************
 *
 *  OpenOffice.org - a multi-platform office productivity suite
 *
 *  $RCSfile: clipboardctl.cxx,v $
 *
 *  $Revision: 1.11 $
 *
 *  last change: $Author: rt $ $Date: 2005-09-08 23:42:39 $
 *
 *  The Contents of this file are made available subject to
 *  the terms of GNU Lesser General Public License Version 2.1.
 *
 *
 *    GNU Lesser General Public License Version 2.1
 *    =============================================
 *    Copyright 2005 by Sun Microsystems, Inc.
 *    901 San Antonio Road, Palo Alto, CA 94303, USA
 *
 *    This library is free software; you can redistribute it and/or
 *    modify it under the terms of the GNU Lesser General Public
 *    License version 2.1, as published by the Free Software Foundation.
 *
 *    This library is distributed in the hope that it will be useful,
 *    but WITHOUT ANY WARRANTY; without even the implied warranty of
 *    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 *    Lesser General Public License for more details.
 *
 *    You should have received a copy of the GNU Lesser General Public
 *    License along with this library; if not, write to the Free Software
 *    Foundation, Inc., 59 Temple Place, Suite 330, Boston,
 *    MA  02111-1307  USA
 *
 ************************************************************************/

#ifndef _SFXAPP_HXX
#include <sfx2/app.hxx>
#endif
#ifndef _SFXTBXCTRL_HXX
#include <sfx2/tbxctrl.hxx>
#endif
#ifndef _SFX_BINDINGS_HXX
#include <sfx2/bindings.hxx>
#endif
#ifndef _SFXDISPATCH_HXX
#include <sfx2/dispatch.hxx>
#endif
#ifndef _SV_GEN_HXX
#include <tools/gen.hxx>
#endif
#ifndef _SFXINTITEM_HXX
#include <svtools/intitem.hxx>
#endif
#ifndef _SOT_EXCHANGE_HXX
#include <sot/exchange.hxx>
#endif
#ifndef _SFXENUMITEM_HXX
#include <svtools/eitem.hxx>
#endif
#ifndef _SV_TOOLBOX_HXX
#include <vcl/toolbox.hxx>
#endif

#ifndef _SVX_CLIPBOARDCTL_HXX_
#include <clipboardctl.hxx>
#endif
#ifndef _SVX_CLIPFMTITEM_HXX
#include <clipfmtitem.hxx>
#endif

#include <svtools/insdlg.hxx>
#include "svxids.hrc"

using namespace ::com::sun::star::uno;
using namespace ::com::sun::star::beans;

/////////////////////////////////////////////////////////////////

SFX_IMPL_TOOLBOX_CONTROL( SvxClipBoardControl, SfxVoidItem /*SfxUInt16Item*/ );

SvxClipBoardControl::SvxClipBoardControl(
        USHORT nSlotId, USHORT nId, ToolBox& rTbx ) :

    SfxToolBoxControl( nSlotId, nId, rTbx ),
    pPopup      (0),
    nItemId     (nId),
    pClipboardFmtItem( 0 ),
    bDisabled( FALSE )
{
    addStatusListener( rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( ".uno:ClipboardFormatItems" )));
    ToolBox& rBox = GetToolBox();
    rBox.SetItemBits( nId, TIB_DROPDOWN | rBox.GetItemBits( nId ) );
    rBox.Invalidate();
}

SvxClipBoardControl::~SvxClipBoardControl()
{
    DelPopup();
    delete pClipboardFmtItem;
}

SfxPopupWindow* SvxClipBoardControl::CreatePopupWindow()
{
    const SvxClipboardFmtItem* pFmtItem = PTR_CAST( SvxClipboardFmtItem, pClipboardFmtItem );
    if ( pFmtItem )
    {
        if (pPopup)
            pPopup->Clear();
        else
            pPopup = new PopupMenu;

        USHORT nCount = pFmtItem->Count();
        for (USHORT i = 0;  i < nCount;  ++i)
        {
            ULONG nFmtID =  pFmtItem->GetClipbrdFormatId( i );
            String aFmtStr( pFmtItem->GetClipbrdFormatName( i ) );
            if (!aFmtStr.Len())
              aFmtStr = SvPasteObjectHelper::GetSotFormatUIName( nFmtID );
            pPopup->InsertItem( (USHORT)nFmtID, aFmtStr );
        }

        ToolBox& rBox = GetToolBox();
        USHORT nId = GetId();
        rBox.SetItemDown( nId, TRUE );

        pPopup->Execute( &rBox, rBox.GetItemRect( nId ),
            (rBox.GetAlign() == WINDOWALIGN_TOP || rBox.GetAlign() == WINDOWALIGN_BOTTOM) ?
                POPUPMENU_EXECUTE_DOWN : POPUPMENU_EXECUTE_RIGHT );

        rBox.SetItemDown( nId, FALSE );

        SfxUInt32Item aItem( SID_CLIPBOARD_FORMAT_ITEMS, pPopup->GetCurItemId() );

        Any a;
        Sequence< PropertyValue > aArgs( 1 );
        aArgs[0].Name = rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "SelectedFormat" ));
        aItem.QueryValue( a );
        aArgs[0].Value = a;
        Dispatch( rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( ".uno:ClipboardFormatItems" )),
                  aArgs );
    }

    GetToolBox().EndSelection();
    DelPopup();
    return 0;
}

SfxPopupWindowType SvxClipBoardControl::GetPopupWindowType() const
{
    return SFX_POPUPWINDOW_ONTIMEOUT;
}

void SvxClipBoardControl::StateChanged( USHORT nSID, SfxItemState eState, const SfxPoolItem* pState )
{
    if ( SID_CLIPBOARD_FORMAT_ITEMS == nSID )
    {
        DELETEZ( pClipboardFmtItem );
        if ( eState >= SFX_ITEM_AVAILABLE )
        {
            pClipboardFmtItem = pState->Clone();
            GetToolBox().SetItemBits( GetId(), GetToolBox().GetItemBits( GetId() ) | TIB_DROPDOWN );
        }
        else if ( !bDisabled )
            GetToolBox().SetItemBits( GetId(), GetToolBox().GetItemBits( GetId() ) & ~TIB_DROPDOWN );
        GetToolBox().Invalidate( GetToolBox().GetItemRect( GetId() ) );
    }
    else
    {
        // enable the item as a whole
        bDisabled = (GetItemState(pState) == SFX_ITEM_DISABLED);
        GetToolBox().EnableItem( GetId(), (GetItemState(pState) != SFX_ITEM_DISABLED) );
    }
}

void SvxClipBoardControl::DelPopup()
{
    if(pPopup)
    {
        delete pPopup;
        pPopup = 0;
    }
}

/////////////////////////////////////////////////////////////////

<|endoftext|>


<commit_before>#include <Windows.h>
#include <hidsdi.h>

#include <ViGEmUM.h>

#include <chrono>
#include <iostream>
#include <memory>

#include <cstring>

#include "common.h"
#include "ProControllerDevice.h"
#include "switch-pro-x.h"

//#define PRO_CONTROLLER_DEBUG_OUTPUT

namespace
{
    constexpr uint8_t EP_IN = 0x81;
    constexpr uint8_t EP_OUT = 0x01;

    constexpr uint8_t PACKET_TYPE_STATUS = 0x81;
    constexpr uint8_t PACKET_TYPE_CONTROLLER_DATA = 0x30;

    constexpr uint8_t STATUS_TYPE_SERIAL = 0x01;
    constexpr uint8_t STATUS_TYPE_INIT = 0x02;

    enum {
        SWITCH_BUTTON_MASK_A = 0x00000800,
        SWITCH_BUTTON_MASK_B = 0x00000400,
        SWITCH_BUTTON_MASK_X = 0x00000200,
        SWITCH_BUTTON_MASK_Y = 0x00000100,

        SWITCH_BUTTON_MASK_DPAD_UP = 0x02000000,
        SWITCH_BUTTON_MASK_DPAD_DOWN = 0x01000000,
        SWITCH_BUTTON_MASK_DPAD_LEFT = 0x08000000,
        SWITCH_BUTTON_MASK_DPAD_RIGHT = 0x04000000,

        SWITCH_BUTTON_MASK_PLUS = 0x00020000,
        SWITCH_BUTTON_MASK_MINUS = 0x00010000,
        SWITCH_BUTTON_MASK_HOME = 0x00100000,
        SWITCH_BUTTON_MASK_SHARE = 0x00200000,

        SWITCH_BUTTON_MASK_L = 0x40000000,
        SWITCH_BUTTON_MASK_ZL = 0x80000000,
        SWITCH_BUTTON_MASK_THUMB_L = 0x00080000,

        SWITCH_BUTTON_MASK_R = 0x00004000,
        SWITCH_BUTTON_MASK_ZR = 0x00008000,
        SWITCH_BUTTON_MASK_THUMB_R = 0x00040000,
    };

#pragma pack(push, 1)
    typedef struct
    {
        uint8_t type;
        union
        {
            struct
            {
                uint8_t type;
                uint8_t serial[8];
            } status_response;
            struct
            {
                uint8_t timestamp;
                uint32_t buttons;
                uint8_t analog[6];
            } controller_data;
            uint8_t padding[63];
        } data;
    } ProControllerPacket;
#pragma pack(pop)
}

ProControllerDevice::ProControllerDevice(libusb_device *dev)
    : counter(0)
    , Device(dev)
    , handle(nullptr)
    , quitting(false)
    , last_rumble()
    , led_number(0xFF)
{
    if (libusb_open(Device, &handle) != 0)
    {
        std::cerr << "Error opening Device " << Device << std::endl;
        return;
    }

    if (libusb_kernel_driver_active(handle, 0) == 1 && libusb_detach_kernel_driver(handle, 0))
    {
        std::cerr << "Error detaching handle from " << Device << " from kernel" << std::endl;
        return;
    }

    if (libusb_claim_interface(handle, 0) != 0)
    {
        std::cerr << "Error claiming interface on " << Device << std::endl;
        return;
    }

    VIGEM_TARGET_INIT(&ViGEm_Target);

    // We don't want to match the libusb driver again, don't set vid/pid and use the default one from ViGEm
    //vigem_target_set_vid(&ViGEm_Target, PRO_CONTROLLER_VID);
    //vigem_target_set_pid(&ViGEm_Target, PRO_CONTROLLER_PID);

    auto ret = vigem_target_plugin(Xbox360Wired, &ViGEm_Target);

    if (!VIGEM_SUCCESS(ret))
    {
        std::cerr << "error creating controller: " << std::hex << std::showbase << ret << std::endl;

        return;
    }

    ret = vigem_register_xusb_notification(XUSBCallback, ViGEm_Target);

    if (!VIGEM_SUCCESS(ret))
    {
        std::cerr << "error creating notification callback: " << std::hex << std::showbase << ret << std::endl;

        return;
    }

    uint8_t data[] = { 0x80, 0x01 };
    WriteData(data, sizeof(data));

    read_thread = std::thread(&ProControllerDevice::ReadThread, this);

    connected = true;
}

void ProControllerDevice::ReadThread()
{
    UCHAR last_led = 0xFF;
    XUSB_REPORT last_report = { 0 };

    while (!quitting)
    {
        unsigned char data[64] = { 0 };
        int size = 0;
        int err = libusb_interrupt_transfer(handle, EP_IN, data, sizeof(data), &size, 100);

        ProControllerPacket *payload = (ProControllerPacket *)data;

        auto now = std::chrono::steady_clock::now();

        if (now > last_rumble + std::chrono::milliseconds(100))
        {
            if (led_number != last_led)
            {
                uint8_t buf[65] = { 0x01, counter++ & 0x0F, 0x00, 0x01, 0x40, 0x40, 0x00, 0x01, 0x40, 0x40, 0x30, 1 << led_number };
                WriteData(buf, sizeof(buf));

                last_led = led_number;
            }
            else
            {
                //uint8_t buf[65] = { 0x10, counter++ & 0x0F, 0x04, 0xBF, 0x40, 0x40, 0x04, 0xBF, 0x40, 0x40 };
                uint8_t buf[65] = { 0x10, counter++ & 0x0F, 0x04, 0x00, 0x40, 0x40, 0x04, 0x00, 0x40, 0x40 };
                WriteData(buf, sizeof(buf));
            }

            last_rumble = now;
        }

        switch (payload->type)
        {
        case PACKET_TYPE_STATUS:
        {
            switch (payload->data.status_response.type)
            {
            case STATUS_TYPE_SERIAL:
            {
                uint8_t payload[] = { 0x80, 0x02 };
                WriteData(payload, sizeof(payload));
                break;
            }
            case STATUS_TYPE_INIT:
            {
                uint8_t payload[] = { 0x80, 0x04 };
                WriteData(payload, sizeof(payload));
                break;
            }
            }
            break;
        }
        case PACKET_TYPE_CONTROLLER_DATA:
        {
            const auto analog = payload->data.controller_data.analog;
            const auto buttons = payload->data.controller_data.buttons;

            int8_t lx = static_cast<int8_t>(((analog[1] & 0x0F) << 4) | ((analog[0] & 0xF0) >> 4)) + 127;
            int8_t ly = analog[2] + 127;
            int8_t rx = static_cast<int8_t>(((analog[4] & 0x0F) << 4) | ((analog[3] & 0xF0) >> 4)) + 127;
            int8_t ry = analog[5] + 127;

#ifdef PRO_CONTROLLER_DEBUG_OUTPUT
            std::cout << "A: " << !!(buttons & SWITCH_BUTTON_MASK_A) << ", ";
            std::cout << "B: " << !!(buttons & SWITCH_BUTTON_MASK_B) << ", ";
            std::cout << "X: " << !!(buttons & SWITCH_BUTTON_MASK_X) << ", ";
            std::cout << "Y: " << !!(buttons & SWITCH_BUTTON_MASK_Y) << ", ";

            std::cout << "DU: " << !!(buttons & SWITCH_BUTTON_MASK_DPAD_UP) << ", ";
            std::cout << "DD: " << !!(buttons & SWITCH_BUTTON_MASK_DPAD_DOWN) << ", ";
            std::cout << "DL: " << !!(buttons & SWITCH_BUTTON_MASK_DPAD_LEFT) << ", ";
            std::cout << "DR: " << !!(buttons & SWITCH_BUTTON_MASK_DPAD_RIGHT) << ", ";

            std::cout << "P: " << !!(buttons & SWITCH_BUTTON_MASK_PLUS) << ", ";
            std::cout << "M: " << !!(buttons & SWITCH_BUTTON_MASK_MINUS) << ", ";
            std::cout << "H: " << !!(buttons & SWITCH_BUTTON_MASK_HOME) << ", ";
            std::cout << "S: " << !!(buttons & SWITCH_BUTTON_MASK_SHARE) << ", ";

            std::cout << "L: " << !!(buttons & SWITCH_BUTTON_MASK_L) << ", ";
            std::cout << "ZL: " << !!(buttons & SWITCH_BUTTON_MASK_ZL) << ", ";
            std::cout << "TL: " << !!(buttons & SWITCH_BUTTON_MASK_THUMB_L) << ", ";

            std::cout << "R: " << !!(buttons & SWITCH_BUTTON_MASK_R) << ", ";
            std::cout << "ZR: " << !!(buttons & SWITCH_BUTTON_MASK_ZR) << ", ";
            std::cout << "TR: " << !!(buttons & SWITCH_BUTTON_MASK_THUMB_R) << ", ";

            std::cout << "LX: " << +lx << ", ";
            std::cout << "LY: " << +ly << ", ";

            std::cout << "RX: " << +rx << ", ";
            std::cout << "RY: " << +ry;

            std::cout << std::endl;
#endif

            XUSB_REPORT report = { 0 };

            // assign a/b/x/y so they match the positions on the xbox layout
            report.wButtons |= !!(buttons & SWITCH_BUTTON_MASK_A) ? XUSB_GAMEPAD_B : 0;
            report.wButtons |= !!(buttons & SWITCH_BUTTON_MASK_B) ? XUSB_GAMEPAD_A : 0;
            report.wButtons |= !!(buttons & SWITCH_BUTTON_MASK_X) ? XUSB_GAMEPAD_Y : 0;
            report.wButtons |= !!(buttons & SWITCH_BUTTON_MASK_Y) ? XUSB_GAMEPAD_X : 0;

            report.wButtons |= !!(buttons & SWITCH_BUTTON_MASK_DPAD_UP) ? XUSB_GAMEPAD_DPAD_UP : 0;
            report.wButtons |= !!(buttons & SWITCH_BUTTON_MASK_DPAD_DOWN) ? XUSB_GAMEPAD_DPAD_DOWN : 0;
            report.wButtons |= !!(buttons & SWITCH_BUTTON_MASK_DPAD_LEFT) ? XUSB_GAMEPAD_DPAD_LEFT : 0;
            report.wButtons |= !!(buttons & SWITCH_BUTTON_MASK_DPAD_RIGHT) ? XUSB_GAMEPAD_DPAD_RIGHT : 0;

            report.wButtons |= !!(buttons & SWITCH_BUTTON_MASK_PLUS) ? XUSB_GAMEPAD_START : 0;
            report.wButtons |= !!(buttons & SWITCH_BUTTON_MASK_MINUS) ? XUSB_GAMEPAD_BACK : 0;
            report.wButtons |= !!(buttons & SWITCH_BUTTON_MASK_HOME) ? XUSB_GAMEPAD_GUIDE : 0;

            report.wButtons |= !!(buttons & SWITCH_BUTTON_MASK_L) ? XUSB_GAMEPAD_LEFT_SHOULDER : 0;
            report.bLeftTrigger = !!(buttons & SWITCH_BUTTON_MASK_ZL) * 0xFF;
            report.wButtons |= !!(buttons & SWITCH_BUTTON_MASK_THUMB_L) ? XUSB_GAMEPAD_LEFT_THUMB : 0;

            report.wButtons |= !!(buttons & SWITCH_BUTTON_MASK_R) ? XUSB_GAMEPAD_RIGHT_SHOULDER : 0;
            report.bRightTrigger = !!(buttons & SWITCH_BUTTON_MASK_ZR) * 0xFF;
            report.wButtons |= !!(buttons & SWITCH_BUTTON_MASK_THUMB_R) ? XUSB_GAMEPAD_RIGHT_THUMB : 0;

            // 257 is not a typo, that's so we get the full range from 0x0000 to 0xffff
            report.sThumbLX = lx * 257;
            report.sThumbLY = ly * 257;
            report.sThumbRX= rx * 257;
            report.sThumbRY = ry * 257;

            if (report != last_report)
            {
                // work around weird xusb driver quirk: https://github.com/nefarius/ViGEm/issues/4
                for (auto i = 0; i < 3; i++)
                {
                    auto ret = vigem_xusb_submit_report(ViGEm_Target, report);

                    if (!VIGEM_SUCCESS(ret))
                    {
                        std::cerr << "error sending report: " << std::hex << std::showbase << ret << std::endl;

                        quitting = true;
                    }
                }

                last_report = report;
            }

            break;
        }
        }
    }

    std::this_thread::sleep_for(std::chrono::milliseconds(100));

    {
        uint8_t buf[65] = { 0x10, counter++ & 0x0F, 0x00, 0x00, 0x40, 0x40, 0x00, 0x00, 0x40, 0x40 };
        WriteData(buf, sizeof(buf));
    }

    std::this_thread::sleep_for(std::chrono::milliseconds(100));

    {
        uint8_t buf[65] = { 0x01, counter++ & 0x0F, 0x00, 0x01, 0x40, 0x40, 0x00, 0x01, 0x40, 0x40, 0x30, 0x00 };
        WriteData(buf, sizeof(buf));
    }
}

ProControllerDevice::~ProControllerDevice()
{
    quitting = true;

    if (read_thread.joinable())
    {
        read_thread.join();
    }

    if (connected)
    {
        vigem_target_unplug(&ViGEm_Target);
    }
}

bool ProControllerDevice::Valid() {
    return connected;
}

void ProControllerDevice::WriteData(uint8_t *buf, size_t size)
{
    int tmp;
    int err = libusb_interrupt_transfer(handle, EP_OUT, buf, static_cast<int>(size), &tmp, 100);
}

void ProControllerDevice::HandleXUSBCallback(UCHAR _large_motor, UCHAR _small_motor, UCHAR _led_number)
{
#ifdef PRO_CONTROLLER_DEBUG_OUTPUT
    std::cout << "XUSB CALLBACK (" << this << ") LARGE MOTOR: " << +_large_motor << ", SMALL MOTOR: " << +_small_motor << ", LED: " << +_led_number << std::endl;
#endif

    large_motor = _large_motor;
    small_motor = _small_motor;
    led_number = _led_number;
}
<commit_msg>implement rumble<commit_after>#include <Windows.h>
#include <hidsdi.h>

#include <ViGEmUM.h>

#include <chrono>
#include <iostream>
#include <memory>

#include <cstring>

#include "common.h"
#include "ProControllerDevice.h"
#include "switch-pro-x.h"

//#define PRO_CONTROLLER_DEBUG_OUTPUT

namespace
{
    constexpr uint8_t EP_IN = 0x81;
    constexpr uint8_t EP_OUT = 0x01;

    constexpr uint8_t PACKET_TYPE_STATUS = 0x81;
    constexpr uint8_t PACKET_TYPE_CONTROLLER_DATA = 0x30;

    constexpr uint8_t STATUS_TYPE_SERIAL = 0x01;
    constexpr uint8_t STATUS_TYPE_INIT = 0x02;

    enum {
        SWITCH_BUTTON_MASK_A = 0x00000800,
        SWITCH_BUTTON_MASK_B = 0x00000400,
        SWITCH_BUTTON_MASK_X = 0x00000200,
        SWITCH_BUTTON_MASK_Y = 0x00000100,

        SWITCH_BUTTON_MASK_DPAD_UP = 0x02000000,
        SWITCH_BUTTON_MASK_DPAD_DOWN = 0x01000000,
        SWITCH_BUTTON_MASK_DPAD_LEFT = 0x08000000,
        SWITCH_BUTTON_MASK_DPAD_RIGHT = 0x04000000,

        SWITCH_BUTTON_MASK_PLUS = 0x00020000,
        SWITCH_BUTTON_MASK_MINUS = 0x00010000,
        SWITCH_BUTTON_MASK_HOME = 0x00100000,
        SWITCH_BUTTON_MASK_SHARE = 0x00200000,

        SWITCH_BUTTON_MASK_L = 0x40000000,
        SWITCH_BUTTON_MASK_ZL = 0x80000000,
        SWITCH_BUTTON_MASK_THUMB_L = 0x00080000,

        SWITCH_BUTTON_MASK_R = 0x00004000,
        SWITCH_BUTTON_MASK_ZR = 0x00008000,
        SWITCH_BUTTON_MASK_THUMB_R = 0x00040000,
    };

#pragma pack(push, 1)
    typedef struct
    {
        uint8_t type;
        union
        {
            struct
            {
                uint8_t type;
                uint8_t serial[8];
            } status_response;
            struct
            {
                uint8_t timestamp;
                uint32_t buttons;
                uint8_t analog[6];
            } controller_data;
            uint8_t padding[63];
        } data;
    } ProControllerPacket;
#pragma pack(pop)
}

ProControllerDevice::ProControllerDevice(libusb_device *dev)
    : counter(0)
    , Device(dev)
    , handle(nullptr)
    , quitting(false)
    , last_rumble()
    , led_number(0xFF)
{
    if (libusb_open(Device, &handle) != 0)
    {
        std::cerr << "Error opening Device " << Device << std::endl;
        return;
    }

    if (libusb_kernel_driver_active(handle, 0) == 1 && libusb_detach_kernel_driver(handle, 0))
    {
        std::cerr << "Error detaching handle from " << Device << " from kernel" << std::endl;
        return;
    }

    if (libusb_claim_interface(handle, 0) != 0)
    {
        std::cerr << "Error claiming interface on " << Device << std::endl;
        return;
    }

    VIGEM_TARGET_INIT(&ViGEm_Target);

    // We don't want to match the libusb driver again, don't set vid/pid and use the default one from ViGEm
    //vigem_target_set_vid(&ViGEm_Target, PRO_CONTROLLER_VID);
    //vigem_target_set_pid(&ViGEm_Target, PRO_CONTROLLER_PID);

    auto ret = vigem_target_plugin(Xbox360Wired, &ViGEm_Target);

    if (!VIGEM_SUCCESS(ret))
    {
        std::cerr << "error creating controller: " << std::hex << std::showbase << ret << std::endl;

        return;
    }

    ret = vigem_register_xusb_notification(XUSBCallback, ViGEm_Target);

    if (!VIGEM_SUCCESS(ret))
    {
        std::cerr << "error creating notification callback: " << std::hex << std::showbase << ret << std::endl;

        return;
    }

    uint8_t data[] = { 0x80, 0x01 };
    WriteData(data, sizeof(data));

    read_thread = std::thread(&ProControllerDevice::ReadThread, this);

    connected = true;
}

void ProControllerDevice::ReadThread()
{
    UCHAR last_led = 0xFF;
    XUSB_REPORT last_report = { 0 };

    while (!quitting)
    {
        unsigned char data[64] = { 0 };
        int size = 0;
        int err = libusb_interrupt_transfer(handle, EP_IN, data, sizeof(data), &size, 100);

        ProControllerPacket *payload = (ProControllerPacket *)data;

        auto now = std::chrono::steady_clock::now();

        if (now > last_rumble + std::chrono::milliseconds(100))
        {
            if (led_number != last_led)
            {
                uint8_t buf[65] = { 0x01, static_cast<uint8_t>(counter++ & 0x0F), 0x00, 0x01, 0x40, 0x40, 0x00, 0x01, 0x40, 0x40, 0x30, static_cast<uint8_t>(1 << led_number) };
                WriteData(buf, sizeof(buf));

                last_led = led_number;
            }
            else
            {
                uint8_t buf[65] = { 0x10, static_cast<uint8_t>(counter++ & 0x0F), 0x80, 0x00, 0x40, 0x40, 0x80, 0x00, 0x40, 0x40 };

                if (large_motor != 0)
                {
                    buf[2] = buf[6] = 0x08;
                    buf[3] = buf[7] = large_motor;
                }
                else if (small_motor != 0)
                {
                    buf[2] = buf[6] = 0x10;
                    buf[3] = buf[7] = small_motor;
                }

                WriteData(buf, sizeof(buf));
            }

            last_rumble = now;
        }

        switch (payload->type)
        {
        case PACKET_TYPE_STATUS:
        {
            switch (payload->data.status_response.type)
            {
            case STATUS_TYPE_SERIAL:
            {
                uint8_t payload[] = { 0x80, 0x02 };
                WriteData(payload, sizeof(payload));
                break;
            }
            case STATUS_TYPE_INIT:
            {
                uint8_t payload[] = { 0x80, 0x04 };
                WriteData(payload, sizeof(payload));
                break;
            }
            }
            break;
        }
        case PACKET_TYPE_CONTROLLER_DATA:
        {
            const auto analog = payload->data.controller_data.analog;
            const auto buttons = payload->data.controller_data.buttons;

            int8_t lx = static_cast<int8_t>(((analog[1] & 0x0F) << 4) | ((analog[0] & 0xF0) >> 4)) + 127;
            int8_t ly = analog[2] + 127;
            int8_t rx = static_cast<int8_t>(((analog[4] & 0x0F) << 4) | ((analog[3] & 0xF0) >> 4)) + 127;
            int8_t ry = analog[5] + 127;

#ifdef PRO_CONTROLLER_DEBUG_OUTPUT
            std::cout << "A: " << !!(buttons & SWITCH_BUTTON_MASK_A) << ", ";
            std::cout << "B: " << !!(buttons & SWITCH_BUTTON_MASK_B) << ", ";
            std::cout << "X: " << !!(buttons & SWITCH_BUTTON_MASK_X) << ", ";
            std::cout << "Y: " << !!(buttons & SWITCH_BUTTON_MASK_Y) << ", ";

            std::cout << "DU: " << !!(buttons & SWITCH_BUTTON_MASK_DPAD_UP) << ", ";
            std::cout << "DD: " << !!(buttons & SWITCH_BUTTON_MASK_DPAD_DOWN) << ", ";
            std::cout << "DL: " << !!(buttons & SWITCH_BUTTON_MASK_DPAD_LEFT) << ", ";
            std::cout << "DR: " << !!(buttons & SWITCH_BUTTON_MASK_DPAD_RIGHT) << ", ";

            std::cout << "P: " << !!(buttons & SWITCH_BUTTON_MASK_PLUS) << ", ";
            std::cout << "M: " << !!(buttons & SWITCH_BUTTON_MASK_MINUS) << ", ";
            std::cout << "H: " << !!(buttons & SWITCH_BUTTON_MASK_HOME) << ", ";
            std::cout << "S: " << !!(buttons & SWITCH_BUTTON_MASK_SHARE) << ", ";

            std::cout << "L: " << !!(buttons & SWITCH_BUTTON_MASK_L) << ", ";
            std::cout << "ZL: " << !!(buttons & SWITCH_BUTTON_MASK_ZL) << ", ";
            std::cout << "TL: " << !!(buttons & SWITCH_BUTTON_MASK_THUMB_L) << ", ";

            std::cout << "R: " << !!(buttons & SWITCH_BUTTON_MASK_R) << ", ";
            std::cout << "ZR: " << !!(buttons & SWITCH_BUTTON_MASK_ZR) << ", ";
            std::cout << "TR: " << !!(buttons & SWITCH_BUTTON_MASK_THUMB_R) << ", ";

            std::cout << "LX: " << +lx << ", ";
            std::cout << "LY: " << +ly << ", ";

            std::cout << "RX: " << +rx << ", ";
            std::cout << "RY: " << +ry;

            std::cout << std::endl;
#endif

            XUSB_REPORT report = { 0 };

            // assign a/b/x/y so they match the positions on the xbox layout
            report.wButtons |= !!(buttons & SWITCH_BUTTON_MASK_A) ? XUSB_GAMEPAD_B : 0;
            report.wButtons |= !!(buttons & SWITCH_BUTTON_MASK_B) ? XUSB_GAMEPAD_A : 0;
            report.wButtons |= !!(buttons & SWITCH_BUTTON_MASK_X) ? XUSB_GAMEPAD_Y : 0;
            report.wButtons |= !!(buttons & SWITCH_BUTTON_MASK_Y) ? XUSB_GAMEPAD_X : 0;

            report.wButtons |= !!(buttons & SWITCH_BUTTON_MASK_DPAD_UP) ? XUSB_GAMEPAD_DPAD_UP : 0;
            report.wButtons |= !!(buttons & SWITCH_BUTTON_MASK_DPAD_DOWN) ? XUSB_GAMEPAD_DPAD_DOWN : 0;
            report.wButtons |= !!(buttons & SWITCH_BUTTON_MASK_DPAD_LEFT) ? XUSB_GAMEPAD_DPAD_LEFT : 0;
            report.wButtons |= !!(buttons & SWITCH_BUTTON_MASK_DPAD_RIGHT) ? XUSB_GAMEPAD_DPAD_RIGHT : 0;

            report.wButtons |= !!(buttons & SWITCH_BUTTON_MASK_PLUS) ? XUSB_GAMEPAD_START : 0;
            report.wButtons |= !!(buttons & SWITCH_BUTTON_MASK_MINUS) ? XUSB_GAMEPAD_BACK : 0;
            report.wButtons |= !!(buttons & SWITCH_BUTTON_MASK_HOME) ? XUSB_GAMEPAD_GUIDE : 0;

            report.wButtons |= !!(buttons & SWITCH_BUTTON_MASK_L) ? XUSB_GAMEPAD_LEFT_SHOULDER : 0;
            report.bLeftTrigger = !!(buttons & SWITCH_BUTTON_MASK_ZL) * 0xFF;
            report.wButtons |= !!(buttons & SWITCH_BUTTON_MASK_THUMB_L) ? XUSB_GAMEPAD_LEFT_THUMB : 0;

            report.wButtons |= !!(buttons & SWITCH_BUTTON_MASK_R) ? XUSB_GAMEPAD_RIGHT_SHOULDER : 0;
            report.bRightTrigger = !!(buttons & SWITCH_BUTTON_MASK_ZR) * 0xFF;
            report.wButtons |= !!(buttons & SWITCH_BUTTON_MASK_THUMB_R) ? XUSB_GAMEPAD_RIGHT_THUMB : 0;

            // 257 is not a typo, that's so we get the full range from 0x0000 to 0xffff
            report.sThumbLX = lx * 257;
            report.sThumbLY = ly * 257;
            report.sThumbRX= rx * 257;
            report.sThumbRY = ry * 257;

            if (report != last_report)
            {
                // work around weird xusb driver quirk: https://github.com/nefarius/ViGEm/issues/4
                for (auto i = 0; i < 3; i++)
                {
                    auto ret = vigem_xusb_submit_report(ViGEm_Target, report);

                    if (!VIGEM_SUCCESS(ret))
                    {
                        std::cerr << "error sending report: " << std::hex << std::showbase << ret << std::endl;

                        quitting = true;
                    }
                }

                last_report = report;
            }

            break;
        }
        }
    }

    std::this_thread::sleep_for(std::chrono::milliseconds(100));

    {
        uint8_t buf[65] = { 0x10, static_cast<uint8_t>(counter++ & 0x0F), 0x80, 0x00, 0x40, 0x40, 0x80, 0x00, 0x40, 0x40 };
        WriteData(buf, sizeof(buf));
    }

    std::this_thread::sleep_for(std::chrono::milliseconds(100));

    {
        uint8_t buf[65] = { 0x01, static_cast<uint8_t>(counter++ & 0x0F), 0x00, 0x01, 0x40, 0x40, 0x00, 0x01, 0x40, 0x40, 0x30, 0x00 };
        WriteData(buf, sizeof(buf));
    }
}

ProControllerDevice::~ProControllerDevice()
{
    quitting = true;

    if (read_thread.joinable())
    {
        read_thread.join();
    }

    if (connected)
    {
        vigem_target_unplug(&ViGEm_Target);
    }
}

bool ProControllerDevice::Valid() {
    return connected;
}

void ProControllerDevice::WriteData(uint8_t *buf, size_t size)
{
    int tmp;
    int err = libusb_interrupt_transfer(handle, EP_OUT, buf, static_cast<int>(size), &tmp, 100);
}

void ProControllerDevice::HandleXUSBCallback(UCHAR _large_motor, UCHAR _small_motor, UCHAR _led_number)
{
#ifdef PRO_CONTROLLER_DEBUG_OUTPUT
    std::cout << "XUSB CALLBACK (" << this << ") LARGE MOTOR: " << +_large_motor << ", SMALL MOTOR: " << +_small_motor << ", LED: " << +_led_number << std::endl;
#endif

    large_motor = _large_motor;
    small_motor = _small_motor;
    led_number = _led_number;
}
<|endoftext|>