<commit_before>/*
  This file is part of QBlowfish and is licensed under the MIT License

  Copyright  2012 Roopesh Chander <roop@forwardbias.in>

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

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

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

#include "qblowfish.h"
#include "qblowfish_p.h"
#include <QtEndian>
#include <QDebug>

QBlowfish::QBlowfish(const QByteArray &key)
    : m_key(key)
    , m_initialized(false)
    , m_paddingEnabled(false)
{
}

void QBlowfish::setPaddingEnabled(bool enabled)
{
    m_paddingEnabled = enabled;
}

bool QBlowfish::paddingEnabled() const
{
    return m_paddingEnabled;
}

QByteArray QBlowfish::encrypted(const QByteArray &_clearText)
{
    QByteArray clearText(_clearText);
    if (clearText.isEmpty()) {
        return QByteArray();
    }

    if (paddingEnabled()) {
        // Add padding as per PKCS5
        // Ref: RFC 5652 http://tools.ietf.org/html/rfc5652#section-6.3
        quint8 paddingLength = 8 - (clearText.size() % 8);
        if (paddingLength == 0) {
            paddingLength = 8;
        }
        QByteArray paddingBa(paddingLength, static_cast<char>(paddingLength));
        clearText.append(paddingBa);
    } else {
        if (clearText.size() % 8 != 0) {
            qWarning("Cannot encrypt. Clear-text length is not a multiple of 8 and padding is not enabled.");
            return QByteArray();
        }
    }

    Q_ASSERT(clearText.size() % 8 == 0);
    if ((clearText.size() % 8 == 0) && init()) {

        QByteArray copyBa(clearText.constData(), clearText.size());
        for (int i = 0; i < clearText.size(); i += 8) {
            coreEncrypt(copyBa.data() + i);
        }
        return copyBa;

    }
    return QByteArray();
}

QByteArray QBlowfish::decrypted(const QByteArray &cipherText)
{
    if (cipherText.isEmpty()) {
        return QByteArray();
    }

    Q_ASSERT(cipherText.size() % 8 == 0);
    if ((cipherText.size() % 8 == 0) && init()) {

        QByteArray copyBa(cipherText.constData(), cipherText.size());
        for (int i = 0; i < cipherText.size(); i += 8) {
            coreDecrypt(copyBa.data() + i);
        }

        if (paddingEnabled()) {
            // Remove padding as per PKCS5
            quint8 paddingLength = static_cast<quint8>(copyBa.right(1).at(0));
            QByteArray paddingBa(paddingLength, static_cast<char>(paddingLength));
            if (copyBa.right(paddingLength) == paddingBa) {
                return copyBa.left(copyBa.length() - paddingLength);
            }
            return QByteArray();
        }
        return copyBa;
    }
    return QByteArray();
}

/*
  Core encryption code follows. This is an implementation of the Blowfish algorithm as described at:
  http://www.schneier.com/paper-blowfish-fse.html
*/

bool QBlowfish::init()
{
    if (m_initialized) {
        return true;
    }

    if (m_key.isEmpty()) {
        qWarning("Cannot init. Key is empty.");
        return false;
    }

    m_sbox1 = QByteArray::fromHex(QByteArray::fromRawData(sbox0, SBOX_SIZE_BYTES * 2));
    m_sbox2 = QByteArray::fromHex(QByteArray::fromRawData(sbox1, SBOX_SIZE_BYTES * 2));
    m_sbox3 = QByteArray::fromHex(QByteArray::fromRawData(sbox2, SBOX_SIZE_BYTES * 2));
    m_sbox4 = QByteArray::fromHex(QByteArray::fromRawData(sbox3, SBOX_SIZE_BYTES * 2));
    m_parray = QByteArray::fromHex(QByteArray::fromRawData(parray, PARRAY_SIZE_BYTES * 2));

    const QByteArray &key = m_key;
    int keyLength = key.length();
    for (int i = 0; i < PARRAY_SIZE_BYTES; i++) {
        m_parray[i] = static_cast<char>(static_cast<quint8>(m_parray[i]) ^ static_cast<quint8>(key[i % keyLength]));
    }

    char seed[8] = { 0, 0, 0, 0, 0, 0, 0, 0 };

    // Update p-array
    for (int i = 0; i < 18; i += 2) {
        coreEncrypt(seed);
        for (int j = 0; j < 8; j++) {
            // P1 = xL; P2 = xR
            m_parray[i * 4 + j] = seed[j];
        }
    }

    // Update s-boxes
    for (int sboxIndex = 1; sboxIndex <= 4; sboxIndex++) {
        QByteArray *sbox = 0;
        switch (sboxIndex) {
        case 1: sbox = &m_sbox1; break;
        case 2: sbox = &m_sbox2; break;
        case 3: sbox = &m_sbox3; break;
        case 4: sbox = &m_sbox4; break;
        default: Q_ASSERT(false);
        }
        Q_ASSERT(sbox != 0);

        for (int i = 0; i < 256; i += 2) {
            coreEncrypt(seed);
            for (int j = 0; j < 8; j++) {
                // S1,1 = xL; S1,2 = xR
                sbox->operator[](i * 4 + j) = seed[j];
            }
        }
    }

    m_initialized = true;
    return true;
}

void QBlowfish::coreEncrypt(char *x) // encrypts 8 bytes pointed to by x, result is written to the same location
{
    // Divide x into two 32-bit halves: xL, xR
    char *xL = x;
    char *xR = x + 4;
    uchar f_xL_bytes[4] = { 0, 0, 0, 0 };

    for (int i = 0; i < 16; i++) {

        // xL = xL XOR Pi
        for (int j = 0; j < 4; j++) {
            // quint8 old_xL = xL[j];
            xL[j] = static_cast<char>(static_cast<quint8>(xL[j]) ^ static_cast<quint8>(m_parray[i * 4 + j]));
        }

        // Divide xL into four eight-bit quarters: a, b, c, and d
        quint8 a = static_cast<quint8>(xL[0]);
        quint8 b = static_cast<quint8>(xL[1]);
        quint8 c = static_cast<quint8>(xL[2]);
        quint8 d = static_cast<quint8>(xL[3]);

        // F(xL) = ((S1,a + S2,b mod 2**32) XOR S3,c) + S4,d mod 2**32
        quint32 s1a = qFromBigEndian<quint32>(reinterpret_cast<const uchar *>(m_sbox1.constData() + a * 4));
        quint32 s2b = qFromBigEndian<quint32>(reinterpret_cast<const uchar *>(m_sbox2.constData() + b * 4));
        quint32 s3c = qFromBigEndian<quint32>(reinterpret_cast<const uchar *>(m_sbox3.constData() + c * 4));
        quint32 s4d = qFromBigEndian<quint32>(reinterpret_cast<const uchar *>(m_sbox4.constData() + d * 4));
        quint32 f_xL = ((((s1a + s2b) & 0xffffffff) ^ s3c) + s4d) & 0xffffffff;
        qToBigEndian<quint32>(f_xL, f_xL_bytes);

        // xR = F(xL) XOR xR
        for (int j = 0; j < 4; j++) {
            xR[j] = static_cast<char>(static_cast<quint8>(f_xL_bytes[j]) ^ static_cast<quint8>(xR[j]));
        }

        // Swap xL and xR, but not in the last iteration
        if (i != 15) {
            for (int j = 0; j < 4; j++) {
                char temp = xL[j];
                xL[j] = xR[j];
                xR[j] = temp;
            }
        }

    }

    // xR = xR XOR P17
    // xL = xL XOR P18
    for (int j = 0; j < 4; j++) {
        xR[j] = static_cast<char>(static_cast<quint8>(xR[j]) ^ static_cast<quint8>(m_parray[16 * 4 + j]));
        xL[j] = static_cast<char>(static_cast<quint8>(xL[j]) ^ static_cast<quint8>(m_parray[17 * 4 + j]));
    }
}

void QBlowfish::coreDecrypt(char *x) // decrypts 8 bytes pointed to by x, result is written to the same location
{
    // Divide x into two 32-bit halves: xL, xR
    char *xL = x;
    char *xR = x + 4;
    uchar f_xL_bytes[4] = { 0, 0, 0, 0 };

    // xL = xL XOR P18
    // xR = xR XOR P17
    for (int j = 0; j < 4; j++) {
        xL[j] = static_cast<char>(static_cast<quint8>(xL[j]) ^ static_cast<quint8>(m_parray[17 * 4 + j]));
        xR[j] = static_cast<char>(static_cast<quint8>(xR[j]) ^ static_cast<quint8>(m_parray[16 * 4 + j]));
    }

    for (int i = 15; i >= 0; i--) {

        // Swap xL and xR, but not in the first iteration
        if (i != 15) {
            for (int j = 0; j < 4; j++) {
                char temp = xL[j];
                xL[j] = xR[j];
                xR[j] = temp;
            }
        }

        // Divide xL into four eight-bit quarters: a, b, c, and d
        quint8 a = static_cast<quint8>(xL[0]);
        quint8 b = static_cast<quint8>(xL[1]);
        quint8 c = static_cast<quint8>(xL[2]);
        quint8 d = static_cast<quint8>(xL[3]);

        // F(xL) = ((S1,a + S2,b mod 2**32) XOR S3,c) + S4,d mod 2**32
        quint32 s1a = qFromBigEndian<quint32>(reinterpret_cast<const uchar *>(m_sbox1.constData() + a * 4));
        quint32 s2b = qFromBigEndian<quint32>(reinterpret_cast<const uchar *>(m_sbox2.constData() + b * 4));
        quint32 s3c = qFromBigEndian<quint32>(reinterpret_cast<const uchar *>(m_sbox3.constData() + c * 4));
        quint32 s4d = qFromBigEndian<quint32>(reinterpret_cast<const uchar *>(m_sbox4.constData() + d * 4));
        quint32 f_xL = ((((s1a + s2b) & 0xffffffff) ^ s3c) + s4d) & 0xffffffff;
        qToBigEndian<quint32>(f_xL, f_xL_bytes);

        // xR = F(xL) XOR xR
        for (int j = 0; j < 4; j++) {
            xR[j] = static_cast<char>(static_cast<quint8>(f_xL_bytes[j]) ^ static_cast<quint8>(xR[j]));
        }

        // xL = xL XOR Pi
        for (int j = 0; j < 4; j++) {
            xL[j] = static_cast<char>(static_cast<quint8>(xL[j]) ^ static_cast<quint8>(m_parray[i * 4 + j]));
        }

    }
}
<commit_msg>Use the #defined values instead of hardcoding in code<commit_after>/*
  This file is part of QBlowfish and is licensed under the MIT License

  Copyright  2012 Roopesh Chander <roop@forwardbias.in>

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

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

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

#include "qblowfish.h"
#include "qblowfish_p.h"
#include <QtEndian>
#include <QDebug>

QBlowfish::QBlowfish(const QByteArray &key)
    : m_key(key)
    , m_initialized(false)
    , m_paddingEnabled(false)
{
}

void QBlowfish::setPaddingEnabled(bool enabled)
{
    m_paddingEnabled = enabled;
}

bool QBlowfish::paddingEnabled() const
{
    return m_paddingEnabled;
}

QByteArray QBlowfish::encrypted(const QByteArray &_clearText)
{
    QByteArray clearText(_clearText);
    if (clearText.isEmpty()) {
        return QByteArray();
    }

    if (paddingEnabled()) {
        // Add padding as per PKCS5
        // Ref: RFC 5652 http://tools.ietf.org/html/rfc5652#section-6.3
        quint8 paddingLength = 8 - (clearText.size() % 8);
        if (paddingLength == 0) {
            paddingLength = 8;
        }
        QByteArray paddingBa(paddingLength, static_cast<char>(paddingLength));
        clearText.append(paddingBa);
    } else {
        if (clearText.size() % 8 != 0) {
            qWarning("Cannot encrypt. Clear-text length is not a multiple of 8 and padding is not enabled.");
            return QByteArray();
        }
    }

    Q_ASSERT(clearText.size() % 8 == 0);
    if ((clearText.size() % 8 == 0) && init()) {

        QByteArray copyBa(clearText.constData(), clearText.size());
        for (int i = 0; i < clearText.size(); i += 8) {
            coreEncrypt(copyBa.data() + i);
        }
        return copyBa;

    }
    return QByteArray();
}

QByteArray QBlowfish::decrypted(const QByteArray &cipherText)
{
    if (cipherText.isEmpty()) {
        return QByteArray();
    }

    Q_ASSERT(cipherText.size() % 8 == 0);
    if ((cipherText.size() % 8 == 0) && init()) {

        QByteArray copyBa(cipherText.constData(), cipherText.size());
        for (int i = 0; i < cipherText.size(); i += 8) {
            coreDecrypt(copyBa.data() + i);
        }

        if (paddingEnabled()) {
            // Remove padding as per PKCS5
            quint8 paddingLength = static_cast<quint8>(copyBa.right(1).at(0));
            QByteArray paddingBa(paddingLength, static_cast<char>(paddingLength));
            if (copyBa.right(paddingLength) == paddingBa) {
                return copyBa.left(copyBa.length() - paddingLength);
            }
            return QByteArray();
        }
        return copyBa;
    }
    return QByteArray();
}

/*
  Core encryption code follows. This is an implementation of the Blowfish algorithm as described at:
  http://www.schneier.com/paper-blowfish-fse.html
*/

bool QBlowfish::init()
{
    if (m_initialized) {
        return true;
    }

    if (m_key.isEmpty()) {
        qWarning("Cannot init. Key is empty.");
        return false;
    }

    m_sbox1 = QByteArray::fromHex(QByteArray::fromRawData(sbox0, SBOX_SIZE_BYTES * 2));
    m_sbox2 = QByteArray::fromHex(QByteArray::fromRawData(sbox1, SBOX_SIZE_BYTES * 2));
    m_sbox3 = QByteArray::fromHex(QByteArray::fromRawData(sbox2, SBOX_SIZE_BYTES * 2));
    m_sbox4 = QByteArray::fromHex(QByteArray::fromRawData(sbox3, SBOX_SIZE_BYTES * 2));
    m_parray = QByteArray::fromHex(QByteArray::fromRawData(parray, PARRAY_SIZE_BYTES * 2));

    const QByteArray &key = m_key;
    int keyLength = key.length();
    for (int i = 0; i < PARRAY_SIZE_BYTES; i++) {
        m_parray[i] = static_cast<char>(static_cast<quint8>(m_parray[i]) ^ static_cast<quint8>(key[i % keyLength]));
    }

    char seed[8] = { 0, 0, 0, 0, 0, 0, 0, 0 };

    // Update p-array
    for (int i = 0; i < (PARRAY_SIZE_BYTES / 4); i += 2) {
        coreEncrypt(seed);
        for (int j = 0; j < 8; j++) {
            // P1 = xL; P2 = xR
            m_parray[i * 4 + j] = seed[j];
        }
    }

    // Update s-boxes
    for (int sboxIndex = 1; sboxIndex <= 4; sboxIndex++) {
        QByteArray *sbox = 0;
        switch (sboxIndex) {
        case 1: sbox = &m_sbox1; break;
        case 2: sbox = &m_sbox2; break;
        case 3: sbox = &m_sbox3; break;
        case 4: sbox = &m_sbox4; break;
        default: Q_ASSERT(false);
        }
        Q_ASSERT(sbox != 0);

        for (int i = 0; i < (SBOX_SIZE_BYTES / 4); i += 2) {
            coreEncrypt(seed);
            for (int j = 0; j < 8; j++) {
                // S1,1 = xL; S1,2 = xR
                sbox->operator[](i * 4 + j) = seed[j];
            }
        }
    }

    m_initialized = true;
    return true;
}

void QBlowfish::coreEncrypt(char *x) // encrypts 8 bytes pointed to by x, result is written to the same location
{
    // Divide x into two 32-bit halves: xL, xR
    char *xL = x;
    char *xR = x + 4;
    uchar f_xL_bytes[4] = { 0, 0, 0, 0 };

    for (int i = 0; i < 16; i++) {

        // xL = xL XOR Pi
        for (int j = 0; j < 4; j++) {
            // quint8 old_xL = xL[j];
            xL[j] = static_cast<char>(static_cast<quint8>(xL[j]) ^ static_cast<quint8>(m_parray[i * 4 + j]));
        }

        // Divide xL into four eight-bit quarters: a, b, c, and d
        quint8 a = static_cast<quint8>(xL[0]);
        quint8 b = static_cast<quint8>(xL[1]);
        quint8 c = static_cast<quint8>(xL[2]);
        quint8 d = static_cast<quint8>(xL[3]);

        // F(xL) = ((S1,a + S2,b mod 2**32) XOR S3,c) + S4,d mod 2**32
        quint32 s1a = qFromBigEndian<quint32>(reinterpret_cast<const uchar *>(m_sbox1.constData() + a * 4));
        quint32 s2b = qFromBigEndian<quint32>(reinterpret_cast<const uchar *>(m_sbox2.constData() + b * 4));
        quint32 s3c = qFromBigEndian<quint32>(reinterpret_cast<const uchar *>(m_sbox3.constData() + c * 4));
        quint32 s4d = qFromBigEndian<quint32>(reinterpret_cast<const uchar *>(m_sbox4.constData() + d * 4));
        quint32 f_xL = ((((s1a + s2b) & 0xffffffff) ^ s3c) + s4d) & 0xffffffff;
        qToBigEndian<quint32>(f_xL, f_xL_bytes);

        // xR = F(xL) XOR xR
        for (int j = 0; j < 4; j++) {
            xR[j] = static_cast<char>(static_cast<quint8>(f_xL_bytes[j]) ^ static_cast<quint8>(xR[j]));
        }

        // Swap xL and xR, but not in the last iteration
        if (i != 15) {
            for (int j = 0; j < 4; j++) {
                char temp = xL[j];
                xL[j] = xR[j];
                xR[j] = temp;
            }
        }

    }

    // xR = xR XOR P17
    // xL = xL XOR P18
    for (int j = 0; j < 4; j++) {
        xR[j] = static_cast<char>(static_cast<quint8>(xR[j]) ^ static_cast<quint8>(m_parray[16 * 4 + j]));
        xL[j] = static_cast<char>(static_cast<quint8>(xL[j]) ^ static_cast<quint8>(m_parray[17 * 4 + j]));
    }
}

void QBlowfish::coreDecrypt(char *x) // decrypts 8 bytes pointed to by x, result is written to the same location
{
    // Divide x into two 32-bit halves: xL, xR
    char *xL = x;
    char *xR = x + 4;
    uchar f_xL_bytes[4] = { 0, 0, 0, 0 };

    // xL = xL XOR P18
    // xR = xR XOR P17
    for (int j = 0; j < 4; j++) {
        xL[j] = static_cast<char>(static_cast<quint8>(xL[j]) ^ static_cast<quint8>(m_parray[17 * 4 + j]));
        xR[j] = static_cast<char>(static_cast<quint8>(xR[j]) ^ static_cast<quint8>(m_parray[16 * 4 + j]));
    }

    for (int i = 15; i >= 0; i--) {

        // Swap xL and xR, but not in the first iteration
        if (i != 15) {
            for (int j = 0; j < 4; j++) {
                char temp = xL[j];
                xL[j] = xR[j];
                xR[j] = temp;
            }
        }

        // Divide xL into four eight-bit quarters: a, b, c, and d
        quint8 a = static_cast<quint8>(xL[0]);
        quint8 b = static_cast<quint8>(xL[1]);
        quint8 c = static_cast<quint8>(xL[2]);
        quint8 d = static_cast<quint8>(xL[3]);

        // F(xL) = ((S1,a + S2,b mod 2**32) XOR S3,c) + S4,d mod 2**32
        quint32 s1a = qFromBigEndian<quint32>(reinterpret_cast<const uchar *>(m_sbox1.constData() + a * 4));
        quint32 s2b = qFromBigEndian<quint32>(reinterpret_cast<const uchar *>(m_sbox2.constData() + b * 4));
        quint32 s3c = qFromBigEndian<quint32>(reinterpret_cast<const uchar *>(m_sbox3.constData() + c * 4));
        quint32 s4d = qFromBigEndian<quint32>(reinterpret_cast<const uchar *>(m_sbox4.constData() + d * 4));
        quint32 f_xL = ((((s1a + s2b) & 0xffffffff) ^ s3c) + s4d) & 0xffffffff;
        qToBigEndian<quint32>(f_xL, f_xL_bytes);

        // xR = F(xL) XOR xR
        for (int j = 0; j < 4; j++) {
            xR[j] = static_cast<char>(static_cast<quint8>(f_xL_bytes[j]) ^ static_cast<quint8>(xR[j]));
        }

        // xL = xL XOR Pi
        for (int j = 0; j < 4; j++) {
            xL[j] = static_cast<char>(static_cast<quint8>(xL[j]) ^ static_cast<quint8>(m_parray[i * 4 + j]));
        }

    }
}
<|endoftext|>


<commit_before>#ifndef regex_impl_hh_INCLUDED
#define regex_impl_hh_INCLUDED

#include "unicode.hh"
#include "utf8.hh"
#include "utf8_iterator.hh"
#include "vector.hh"
#include "flags.hh"

namespace Kakoune
{

struct CompiledRegex
{
    enum Op : char
    {
        Match,
        Literal,
        LiteralIgnoreCase,
        AnyChar,
        Matcher,
        Jump,
        Split_PrioritizeParent,
        Split_PrioritizeChild,
        Save,
        LineStart,
        LineEnd,
        WordBoundary,
        NotWordBoundary,
        SubjectBegin,
        SubjectEnd,
        LookAhead,
        LookBehind,
        NegativeLookAhead,
        NegativeLookBehind,
    };

    using Offset = unsigned;
    static constexpr Offset search_prefix_size = 3 + 2 * sizeof(Offset);

    explicit operator bool() const { return not bytecode.empty(); }

    Vector<char> bytecode;
    Vector<std::function<bool (Codepoint)>> matchers;
    size_t save_count;
};

CompiledRegex compile_regex(StringView re);

enum class RegexExecFlags
{
    None              = 0,
    Search            = 1 << 0,
    NotBeginOfLine    = 1 << 1,
    NotEndOfLine      = 1 << 2,
    NotBeginOfWord    = 1 << 3,
    NotEndOfWord      = 1 << 4,
    NotBeginOfSubject = 1 << 5,
    NotInitialNull    = 1 << 6,
    AnyMatch          = 1 << 7
};

constexpr bool with_bit_ops(Meta::Type<RegexExecFlags>) { return true; }

template<typename Iterator>
struct ThreadedRegexVM
{
    ThreadedRegexVM(const CompiledRegex& program)
      : m_program{program} { kak_assert(m_program); }

    struct Saves
    {
        int refcount;
        Vector<Iterator> pos;
    };

    Saves* clone_saves(Saves* saves)
    {
        Saves* res = nullptr;
        if (not m_free_saves.empty())
        {
            res = m_free_saves.back();
            m_free_saves.pop_back();
        }
        else
        {
            m_saves.push_back(std::make_unique<Saves>());
            res = m_saves.back().get();
        }

        res->refcount = 1;
        res->pos = saves->pos;
        return res;
    }

    struct Thread
    {
        const char* inst;
        Saves* saves;
    };

    enum class StepResult { Consumed, Matched, Failed };
    StepResult step(size_t thread_index)
    {
        const auto prog_start = m_program.bytecode.data();
        const auto prog_end = prog_start + m_program.bytecode.size();
        while (true)
        {
            auto& thread = m_threads[thread_index];
            const Codepoint cp = m_pos == m_end ? 0 : *m_pos;
            const CompiledRegex::Op op = (CompiledRegex::Op)*thread.inst++;
            switch (op)
            {
                case CompiledRegex::Literal:
                    if (utf8::read_codepoint(thread.inst, prog_end) == cp)
                        return StepResult::Consumed;
                    return StepResult::Failed;
                case CompiledRegex::LiteralIgnoreCase:
                    if (utf8::read_codepoint(thread.inst, prog_end) == to_lower(cp))
                        return StepResult::Consumed;
                    return StepResult::Failed;
                case CompiledRegex::AnyChar:
                    return StepResult::Consumed;
                case CompiledRegex::Jump:
                    thread.inst = prog_start + *reinterpret_cast<const CompiledRegex::Offset*>(thread.inst);
                    break;
                case CompiledRegex::Split_PrioritizeParent:
                {
                    auto parent = thread.inst + sizeof(CompiledRegex::Offset);
                    auto child = prog_start + *reinterpret_cast<const CompiledRegex::Offset*>(thread.inst);
                    thread.inst = parent;
                    ++thread.saves->refcount;
                    m_threads.insert(m_threads.begin() + thread_index + 1, {child, thread.saves});
                    break;
                }
                case CompiledRegex::Split_PrioritizeChild:
                {
                    auto parent = thread.inst + sizeof(CompiledRegex::Offset);
                    auto child = prog_start + *reinterpret_cast<const CompiledRegex::Offset*>(thread.inst);
                    thread.inst = child;
                    ++thread.saves->refcount;
                    m_threads.insert(m_threads.begin() + thread_index + 1, {parent, thread.saves});
                    break;
                }
                case CompiledRegex::Save:
                {
                    const char index = *thread.inst++;
                    if (thread.saves->refcount > 1)
                    {
                        --thread.saves->refcount;
                        thread.saves = clone_saves(thread.saves);
                    }
                    thread.saves->pos[index] = m_pos.base();
                    break;
                }
                case CompiledRegex::Matcher:
                {
                    const int matcher_id = *thread.inst++;
                    return m_program.matchers[matcher_id](*m_pos) ?
                        StepResult::Consumed : StepResult::Failed;
                }
                case CompiledRegex::LineStart:
                    if (not is_line_start())
                        return StepResult::Failed;
                    break;
                case CompiledRegex::LineEnd:
                    if (not is_line_end())
                        return StepResult::Failed;
                    break;
                case CompiledRegex::WordBoundary:
                    if (not is_word_boundary())
                        return StepResult::Failed;
                    break;
                case CompiledRegex::NotWordBoundary:
                    if (is_word_boundary())
                        return StepResult::Failed;
                    break;
                case CompiledRegex::SubjectBegin:
                    if (m_pos != m_begin or m_flags & RegexExecFlags::NotBeginOfSubject)
                        return StepResult::Failed;
                    break;
                case CompiledRegex::SubjectEnd:
                    if (m_pos != m_end)
                        return StepResult::Failed;
                    break;
                case CompiledRegex::LookAhead:
                case CompiledRegex::NegativeLookAhead:
                {
                    int count = *thread.inst++;
                    for (auto it = m_pos; count and it != m_end; ++it, --count)
                        if (*it != utf8::read(thread.inst))
                            break;
                    if ((op == CompiledRegex::LookAhead and count != 0) or
                        (op == CompiledRegex::NegativeLookAhead and count == 0))
                        return StepResult::Failed;
                    thread.inst = utf8::advance(thread.inst, prog_end, CharCount{count - 1});
                    break;
                }
                case CompiledRegex::LookBehind:
                case CompiledRegex::NegativeLookBehind:
                {
                    int count = *thread.inst++;
                    for (auto it = m_pos-1; count and it >= m_begin; --it, --count)
                        if (*it != utf8::read(thread.inst))
                            break;
                    if ((op == CompiledRegex::LookBehind and count != 0) or
                        (op == CompiledRegex::NegativeLookBehind and count == 0))
                        return StepResult::Failed;
                    thread.inst = utf8::advance(thread.inst, prog_end, CharCount{count - 1});
                    break;
                }
                case CompiledRegex::Match:
                    return StepResult::Matched;
            }
        }
        return StepResult::Failed;
    }

    bool exec(Iterator begin, Iterator end, RegexExecFlags flags)
    {
        bool found_match = false;
        m_threads.clear();
        const auto start_offset = (flags & RegexExecFlags::Search) ? 0 : CompiledRegex::search_prefix_size;
        m_saves.push_back(std::make_unique<Saves>(Saves{1, Vector<Iterator>(m_program.save_count, Iterator{})}));
        m_threads.push_back({m_program.bytecode.data() + start_offset, m_saves.back().get()});

        m_begin = begin;
        m_end = end;
        m_flags = flags;

        if (flags & RegexExecFlags::NotInitialNull and m_begin == m_end)
            return false;

        auto release_saves = [this](Saves* saves) {
            if (--saves->refcount == 0)
                m_free_saves.push_back(saves);
        };

        for (m_pos = Utf8It{m_begin, m_begin, m_end}; m_pos != m_end; ++m_pos)
        {
            for (int i = 0; i < m_threads.size(); ++i)
            {
                const auto res = step(i);
                if (res == StepResult::Matched)
                {
                    if (not (flags & RegexExecFlags::Search) or // We are not at end, this is not a full match
                        (flags & RegexExecFlags::NotInitialNull and m_pos == m_begin))
                    {
                        m_threads[i].inst = nullptr;
                        release_saves(m_threads[i].saves);
                        continue;
                    }

                    m_captures = std::move(m_threads[i].saves->pos);
                    if (flags & RegexExecFlags::AnyMatch)
                        return true;

                    found_match = true;
                    m_threads.resize(i); // remove this and lower priority threads
                }
                else if (res == StepResult::Failed)
                {
                    m_threads[i].inst = nullptr;
                    release_saves(m_threads[i].saves);
                }
                else
                {
                    auto it = m_threads.begin() + i;
                    if (std::find_if(m_threads.begin(), it, [inst = it->inst](auto& t)
                                     { return t.inst == inst; }) != it)
                    {
                        m_threads[i].inst = nullptr;
                        release_saves(m_threads[i].saves);
                    }
                }
            }
            // Remove dead threads
            m_threads.erase(std::remove_if(m_threads.begin(), m_threads.end(),
                                           [](auto& t) { return t.inst == nullptr; }),
                            m_threads.end());
            // we should never have more than one thread on the same instruction
            kak_assert(m_threads.size() <= m_program.bytecode.size());
            if (m_threads.empty())
                return found_match;
        }
        if (found_match)
            return true;

        // Step remaining threads to see if they match without consuming anything else
        for (int i = 0; i < m_threads.size(); ++i)
        {
            if (step(i) == StepResult::Matched)
            {
                m_captures = std::move(m_threads[i].saves->pos);
                return true;
            }
        }
        return false;
    }

    bool is_line_start() const
    {
        return (m_pos == m_begin and not (m_flags & RegexExecFlags::NotBeginOfLine)) or
               *(m_pos-1) == '\n';
    }

    bool is_line_end() const
    {
        return (m_pos == m_end and not (m_flags & RegexExecFlags::NotEndOfLine)) or
               *m_pos == '\n';
    }

    bool is_word_boundary() const
    {
        return (m_pos == m_begin and not (m_flags & RegexExecFlags::NotBeginOfWord)) or
               (m_pos == m_end and not (m_flags & RegexExecFlags::NotEndOfWord)) or
               is_word(*(m_pos-1)) != is_word(*m_pos);
    }

    const CompiledRegex& m_program;
    Vector<Thread> m_threads;

    using Utf8It = utf8::iterator<Iterator>;

    Iterator m_begin;
    Iterator m_end;
    Utf8It m_pos;
    RegexExecFlags m_flags;

    Vector<std::unique_ptr<Saves>> m_saves;
    Vector<Saves*> m_free_saves;

    Vector<Iterator> m_captures;
};

template<typename It>
bool regex_match(It begin, It end, const CompiledRegex& re, RegexExecFlags flags = RegexExecFlags::None)
{
    ThreadedRegexVM<It> vm{re};
    return vm.exec(begin, end, (RegexExecFlags)(flags & ~(RegexExecFlags::Search)) | RegexExecFlags::AnyMatch);
}

template<typename It>
bool regex_match(It begin, It end, Vector<It>& captures, const CompiledRegex& re,
                 RegexExecFlags flags = RegexExecFlags::None)
{
    ThreadedRegexVM<It> vm{re};
    if (vm.exec(begin, end,  flags & ~(RegexExecFlags::Search)))
    {
        captures = std::move(vm.m_captures);
        return true;
    }
    return false;
}

template<typename It>
bool regex_search(It begin, It end, const CompiledRegex& re,
                  RegexExecFlags flags = RegexExecFlags::None)
{
    ThreadedRegexVM<It> vm{re};
    return vm.exec(begin, end, flags | RegexExecFlags::Search | RegexExecFlags::AnyMatch);
}

template<typename It>
bool regex_search(It begin, It end, Vector<It>& captures, const CompiledRegex& re,
                  RegexExecFlags flags = RegexExecFlags::None)
{
    ThreadedRegexVM<It> vm{re};
    if (vm.exec(begin, end, flags | RegexExecFlags::Search))
    {
        captures = std::move(vm.m_captures);
        return true;
    }
    return false;
}

}

#endif // regex_impl_hh_INCLUDED
<commit_msg>Regex: Refactor thread handling in ThreadedRegexVM<commit_after>#ifndef regex_impl_hh_INCLUDED
#define regex_impl_hh_INCLUDED

#include "unicode.hh"
#include "utf8.hh"
#include "utf8_iterator.hh"
#include "vector.hh"
#include "flags.hh"

namespace Kakoune
{

struct CompiledRegex
{
    enum Op : char
    {
        Match,
        Literal,
        LiteralIgnoreCase,
        AnyChar,
        Matcher,
        Jump,
        Split_PrioritizeParent,
        Split_PrioritizeChild,
        Save,
        LineStart,
        LineEnd,
        WordBoundary,
        NotWordBoundary,
        SubjectBegin,
        SubjectEnd,
        LookAhead,
        LookBehind,
        NegativeLookAhead,
        NegativeLookBehind,
    };

    using Offset = unsigned;
    static constexpr Offset search_prefix_size = 3 + 2 * sizeof(Offset);

    explicit operator bool() const { return not bytecode.empty(); }

    Vector<char> bytecode;
    Vector<std::function<bool (Codepoint)>> matchers;
    size_t save_count;
};

CompiledRegex compile_regex(StringView re);

enum class RegexExecFlags
{
    None              = 0,
    Search            = 1 << 0,
    NotBeginOfLine    = 1 << 1,
    NotEndOfLine      = 1 << 2,
    NotBeginOfWord    = 1 << 3,
    NotEndOfWord      = 1 << 4,
    NotBeginOfSubject = 1 << 5,
    NotInitialNull    = 1 << 6,
    AnyMatch          = 1 << 7
};

constexpr bool with_bit_ops(Meta::Type<RegexExecFlags>) { return true; }

template<typename Iterator>
struct ThreadedRegexVM
{
    ThreadedRegexVM(const CompiledRegex& program)
      : m_program{program} { kak_assert(m_program); }

    struct Saves
    {
        int refcount;
        Vector<Iterator> pos;
    };

    Saves* clone_saves(Saves* saves)
    {
        Saves* res = nullptr;
        if (not m_free_saves.empty())
        {
            res = m_free_saves.back();
            m_free_saves.pop_back();
        }
        else
        {
            m_saves.push_back(std::make_unique<Saves>());
            res = m_saves.back().get();
        }

        res->refcount = 1;
        res->pos = saves->pos;
        return res;
    }

    struct Thread
    {
        const char* inst;
        Saves* saves;
    };

    enum class StepResult { Consumed, Matched, Failed };
    StepResult step(Thread& thread)
    {
        const auto prog_start = m_program.bytecode.data();
        const auto prog_end = prog_start + m_program.bytecode.size();
        while (true)
        {
            const Codepoint cp = m_pos == m_end ? 0 : *m_pos;
            const CompiledRegex::Op op = (CompiledRegex::Op)*thread.inst++;
            switch (op)
            {
                case CompiledRegex::Literal:
                    if (utf8::read_codepoint(thread.inst, prog_end) == cp)
                        return StepResult::Consumed;
                    return StepResult::Failed;
                case CompiledRegex::LiteralIgnoreCase:
                    if (utf8::read_codepoint(thread.inst, prog_end) == to_lower(cp))
                        return StepResult::Consumed;
                    return StepResult::Failed;
                case CompiledRegex::AnyChar:
                    return StepResult::Consumed;
                case CompiledRegex::Jump:
                    thread.inst = prog_start + *reinterpret_cast<const CompiledRegex::Offset*>(thread.inst);
                    break;
                case CompiledRegex::Split_PrioritizeParent:
                {
                    auto parent = thread.inst + sizeof(CompiledRegex::Offset);
                    auto child = prog_start + *reinterpret_cast<const CompiledRegex::Offset*>(thread.inst);
                    thread.inst = parent;
                    ++thread.saves->refcount;
                    m_current_threads.push_back({child, thread.saves});
                    break;
                }
                case CompiledRegex::Split_PrioritizeChild:
                {
                    auto parent = thread.inst + sizeof(CompiledRegex::Offset);
                    auto child = prog_start + *reinterpret_cast<const CompiledRegex::Offset*>(thread.inst);
                    thread.inst = child;
                    ++thread.saves->refcount;
                    m_current_threads.push_back({parent, thread.saves});
                    break;
                }
                case CompiledRegex::Save:
                {
                    const char index = *thread.inst++;
                    if (thread.saves->refcount > 1)
                    {
                        --thread.saves->refcount;
                        thread.saves = clone_saves(thread.saves);
                    }
                    thread.saves->pos[index] = m_pos.base();
                    break;
                }
                case CompiledRegex::Matcher:
                {
                    const int matcher_id = *thread.inst++;
                    return m_program.matchers[matcher_id](*m_pos) ?
                        StepResult::Consumed : StepResult::Failed;
                }
                case CompiledRegex::LineStart:
                    if (not is_line_start())
                        return StepResult::Failed;
                    break;
                case CompiledRegex::LineEnd:
                    if (not is_line_end())
                        return StepResult::Failed;
                    break;
                case CompiledRegex::WordBoundary:
                    if (not is_word_boundary())
                        return StepResult::Failed;
                    break;
                case CompiledRegex::NotWordBoundary:
                    if (is_word_boundary())
                        return StepResult::Failed;
                    break;
                case CompiledRegex::SubjectBegin:
                    if (m_pos != m_begin or m_flags & RegexExecFlags::NotBeginOfSubject)
                        return StepResult::Failed;
                    break;
                case CompiledRegex::SubjectEnd:
                    if (m_pos != m_end)
                        return StepResult::Failed;
                    break;
                case CompiledRegex::LookAhead:
                case CompiledRegex::NegativeLookAhead:
                {
                    int count = *thread.inst++;
                    for (auto it = m_pos; count and it != m_end; ++it, --count)
                        if (*it != utf8::read(thread.inst))
                            break;
                    if ((op == CompiledRegex::LookAhead and count != 0) or
                        (op == CompiledRegex::NegativeLookAhead and count == 0))
                        return StepResult::Failed;
                    thread.inst = utf8::advance(thread.inst, prog_end, CharCount{count - 1});
                    break;
                }
                case CompiledRegex::LookBehind:
                case CompiledRegex::NegativeLookBehind:
                {
                    int count = *thread.inst++;
                    for (auto it = m_pos-1; count and it >= m_begin; --it, --count)
                        if (*it != utf8::read(thread.inst))
                            break;
                    if ((op == CompiledRegex::LookBehind and count != 0) or
                        (op == CompiledRegex::NegativeLookBehind and count == 0))
                        return StepResult::Failed;
                    thread.inst = utf8::advance(thread.inst, prog_end, CharCount{count - 1});
                    break;
                }
                case CompiledRegex::Match:
                    return StepResult::Matched;
            }
        }
        return StepResult::Failed;
    }

    bool exec(Iterator begin, Iterator end, RegexExecFlags flags)
    {
        bool found_match = false;
        m_current_threads.clear();
        m_next_threads.clear();

        const auto start_offset = (flags & RegexExecFlags::Search) ? 0 : CompiledRegex::search_prefix_size;
        m_saves.push_back(std::make_unique<Saves>(Saves{1, Vector<Iterator>(m_program.save_count, Iterator{})}));
        m_current_threads.push_back({m_program.bytecode.data() + start_offset, m_saves.back().get()});

        m_begin = begin;
        m_end = end;
        m_flags = flags;

        if (flags & RegexExecFlags::NotInitialNull and m_begin == m_end)
            return false;

        auto release_saves = [this](Saves* saves) {
            if (--saves->refcount == 0)
                m_free_saves.push_back(saves);
        };

        for (m_pos = Utf8It{m_begin, m_begin, m_end}; m_pos != m_end; ++m_pos)
        {
            while (not m_current_threads.empty())
            {
                auto thread = m_current_threads.back();
                m_current_threads.pop_back();
                switch (step(thread))
                {
                case StepResult::Matched:
                    if (not (flags & RegexExecFlags::Search) or // We are not at end, this is not a full match
                        (flags & RegexExecFlags::NotInitialNull and m_pos == m_begin))
                    {
                        release_saves(thread.saves);
                        continue;
                    }

                    m_captures = std::move(thread.saves->pos);
                    if (flags & RegexExecFlags::AnyMatch)
                        return true;

                    found_match = true;
                    m_current_threads.clear(); // remove this and lower priority threads
                    break;
                case StepResult::Failed:
                    release_saves(thread.saves);
                    break;
                case StepResult::Consumed:
                    if (contains_that(m_next_threads, [&](auto& t) { return t.inst == thread.inst; }))
                        release_saves(thread.saves);
                    else
                        m_next_threads.push_back(thread);
                    break;
                }
            }
            if (m_next_threads.empty())
                return found_match;

            std::swap(m_current_threads, m_next_threads);
            std::reverse(m_current_threads.begin(), m_current_threads.end());
        }
        if (found_match)
            return true;

        // Step remaining threads to see if they match without consuming anything else
        while (not m_current_threads.empty())
        {
            auto thread = m_current_threads.back();
            m_current_threads.pop_back();
            if (step(thread) == StepResult::Matched)
            {
                m_captures = std::move(thread.saves->pos);
                return true;
            }
        }
        return false;
    }

    bool is_line_start() const
    {
        return (m_pos == m_begin and not (m_flags & RegexExecFlags::NotBeginOfLine)) or
               *(m_pos-1) == '\n';
    }

    bool is_line_end() const
    {
        return (m_pos == m_end and not (m_flags & RegexExecFlags::NotEndOfLine)) or
               *m_pos == '\n';
    }

    bool is_word_boundary() const
    {
        return (m_pos == m_begin and not (m_flags & RegexExecFlags::NotBeginOfWord)) or
               (m_pos == m_end and not (m_flags & RegexExecFlags::NotEndOfWord)) or
               is_word(*(m_pos-1)) != is_word(*m_pos);
    }

    const CompiledRegex& m_program;
    Vector<Thread> m_current_threads;
    Vector<Thread> m_next_threads;

    using Utf8It = utf8::iterator<Iterator>;

    Iterator m_begin;
    Iterator m_end;
    Utf8It m_pos;
    RegexExecFlags m_flags;

    Vector<std::unique_ptr<Saves>> m_saves;
    Vector<Saves*> m_free_saves;

    Vector<Iterator> m_captures;
};

template<typename It>
bool regex_match(It begin, It end, const CompiledRegex& re, RegexExecFlags flags = RegexExecFlags::None)
{
    ThreadedRegexVM<It> vm{re};
    return vm.exec(begin, end, (RegexExecFlags)(flags & ~(RegexExecFlags::Search)) | RegexExecFlags::AnyMatch);
}

template<typename It>
bool regex_match(It begin, It end, Vector<It>& captures, const CompiledRegex& re,
                 RegexExecFlags flags = RegexExecFlags::None)
{
    ThreadedRegexVM<It> vm{re};
    if (vm.exec(begin, end,  flags & ~(RegexExecFlags::Search)))
    {
        captures = std::move(vm.m_captures);
        return true;
    }
    return false;
}

template<typename It>
bool regex_search(It begin, It end, const CompiledRegex& re,
                  RegexExecFlags flags = RegexExecFlags::None)
{
    ThreadedRegexVM<It> vm{re};
    return vm.exec(begin, end, flags | RegexExecFlags::Search | RegexExecFlags::AnyMatch);
}

template<typename It>
bool regex_search(It begin, It end, Vector<It>& captures, const CompiledRegex& re,
                  RegexExecFlags flags = RegexExecFlags::None)
{
    ThreadedRegexVM<It> vm{re};
    if (vm.exec(begin, end, flags | RegexExecFlags::Search))
    {
        captures = std::move(vm.m_captures);
        return true;
    }
    return false;
}

}

#endif // regex_impl_hh_INCLUDED
<|endoftext|>


<commit_before>#include "resources.h"

#include <SFML/System.hpp>
#ifdef _MSC_VER
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#else
#include <dirent.h>
#endif
#include <cstdio>
#include <filesystem>

PVector<ResourceProvider> resourceProviders;

ResourceProvider::ResourceProvider()
{
    resourceProviders.push_back(this);
}

bool ResourceProvider::searchMatch(const string name, const string searchPattern)
{
    std::vector<string> parts = searchPattern.split("*");
    int pos = 0;
    if (parts[0].length() > 0)
    {
        if (name.find(parts[0]) != 0)
            return false;
    }
    for(unsigned int n=1; n<parts.size(); n++)
    {
        int offset = name.find(parts[n], pos);
        if (offset < 0)
            return false;
        pos = offset + static_cast<int>(parts[n].length());
    }
    return pos == static_cast<int>(name.length());
}

string ResourceStream::readLine()
{
    string ret;
    char c;
    while(true)
    {
        if (read(&c, 1) < 1)
            return ret;
        if (c == '\n')
            return ret;
        ret += string(c);
    }
}

class FileResourceStream : public ResourceStream
{
    sf::FileInputStream stream;
    bool open_success;
public:
    FileResourceStream(string filename)
    {
#ifndef ANDROID
        std::error_code ec;
        if(!std::filesystem::is_regular_file(filename.c_str(), ec))
        {
            if(ec)
            {
                LOG(ERROR) << "OS error on file check : " << ec.message();
            }
            open_success = false;
        }
        else
            open_success = stream.open(filename);
#else
        //Android reads from the assets bundle, so we cannot check if the file exists and is a regular file
        open_success = stream.open(filename);
#endif
    }
    virtual ~FileResourceStream()
    {
    }

    bool isOpen()
    {
        return open_success;
    }

    virtual sf::Int64 read(void* data, sf::Int64 size)
    {
        return stream.read(data, size);
    }
    virtual sf::Int64 seek(sf::Int64 position)
    {
        return stream.seek(position);
    }
    virtual sf::Int64 tell()
    {
        return stream.tell();
    }
    virtual sf::Int64 getSize()
    {
        return stream.getSize();
    }
};

DirectoryResourceProvider::DirectoryResourceProvider(string basepath)
: basepath(basepath)
{
}

P<ResourceStream> DirectoryResourceProvider::getResourceStream(string filename)
{
    P<FileResourceStream> stream = new FileResourceStream(basepath + filename);
    if (stream->isOpen())
        return stream;
    return nullptr;
}

std::vector<string> DirectoryResourceProvider::findResources(string searchPattern)
{
    std::vector<string> found_files;
    findResources(found_files, "", searchPattern);
    return found_files;
}

void DirectoryResourceProvider::findResources(std::vector<string>& found_files, const string path, const string searchPattern)
{
#ifdef _MSC_VER
    WIN32_FIND_DATAA data;
    string search_root(basepath + path);
    if (!search_root.endswith("/"))
    {
        search_root += "/";
    }
    HANDLE handle = FindFirstFileA((search_root + "*").c_str(), &data);
    if (handle == INVALID_HANDLE_VALUE)
        return;
    do
    {
        if (data.cFileName[0] == '.')
            continue;
        string name = path + string(data.cFileName);
        if (data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
        {
            findResources(found_files, name + "/", searchPattern);
        }
        else
        {
            if (searchMatch(name, searchPattern))
                found_files.push_back(name);
        }
    } while (FindNextFileA(handle, &data));

    FindClose(handle);
#else
    DIR* dir = opendir((basepath + path).c_str());
    if (!dir)
        return;

    struct dirent *entry;
    while ((entry = readdir(dir)) != nullptr)
    {
        if (entry->d_name[0] == '.')
            continue;
        string name = path + string(entry->d_name);
        if (searchMatch(name, searchPattern))
            found_files.push_back(name);
        findResources(found_files, path + string(entry->d_name) + "/", searchPattern);
    }
    closedir(dir);
#endif
}

P<ResourceStream> getResourceStream(string filename)
{
    foreach(ResourceProvider, rp, resourceProviders)
    {
        P<ResourceStream> stream = rp->getResourceStream(filename);
        if (stream)
            return stream;
    }
    return NULL;
}

std::vector<string> findResources(string searchPattern)
{
    std::vector<string> foundFiles;
    foreach(ResourceProvider, rp, resourceProviders)
    {
        std::vector<string> res = rp->findResources(searchPattern);
        foundFiles.insert(foundFiles.end(), res.begin(), res.end());
    }
    return foundFiles;
}
<commit_msg>Being able to list android files (#109)<commit_after>#include "resources.h"

#include <SFML/System.hpp>
#ifdef _MSC_VER
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#else
#include <dirent.h>
#endif
#include <cstdio>
#include <filesystem>

#if defined(ANDROID)
#include <SFML/System/NativeActivity.hpp>
#include <android/native_activity.h>
#include <android/asset_manager.h>
#endif

PVector<ResourceProvider> resourceProviders;

ResourceProvider::ResourceProvider()
{
    resourceProviders.push_back(this);
}

bool ResourceProvider::searchMatch(const string name, const string searchPattern)
{
    std::vector<string> parts = searchPattern.split("*");
    int pos = 0;
    if (parts[0].length() > 0)
    {
        if (name.find(parts[0]) != 0)
            return false;
    }
    for(unsigned int n=1; n<parts.size(); n++)
    {
        int offset = name.find(parts[n], pos);
        if (offset < 0)
            return false;
        pos = offset + static_cast<int>(parts[n].length());
    }
    return pos == static_cast<int>(name.length());
}

string ResourceStream::readLine()
{
    string ret;
    char c;
    while(true)
    {
        if (read(&c, 1) < 1)
            return ret;
        if (c == '\n')
            return ret;
        ret += string(c);
    }
}

class FileResourceStream : public ResourceStream
{
    sf::FileInputStream stream;
    bool open_success;
public:
    FileResourceStream(string filename)
    {
#ifndef ANDROID
        std::error_code ec;
        if(!std::filesystem::is_regular_file(filename.c_str(), ec))
        {
            //Error code "no such file or directory" thrown really often, so no trace here
            //not to spam the log
            open_success = false;
        }
        else
            open_success = stream.open(filename);
#else
       //Android reads from the assets bundle, so we cannot check if the file exists and is a regular file
       open_success = stream.open(filename);
#endif
    }
    virtual ~FileResourceStream()
    {
    }

    bool isOpen()
    {
        return open_success;
    }

    virtual sf::Int64 read(void* data, sf::Int64 size)
    {
        return stream.read(data, size);
    }
    virtual sf::Int64 seek(sf::Int64 position)
    {
        return stream.seek(position);
    }
    virtual sf::Int64 tell()
    {
        return stream.tell();
    }
    virtual sf::Int64 getSize()
    {
        return stream.getSize();
    }
};

DirectoryResourceProvider::DirectoryResourceProvider(string basepath)
{
    this->basepath = basepath.rstrip("\\/");
}

P<ResourceStream> DirectoryResourceProvider::getResourceStream(string filename)
{
    P<FileResourceStream> stream = new FileResourceStream(basepath + "/" + filename);
    if (stream->isOpen())
        return stream;
    return nullptr;
}

std::vector<string> DirectoryResourceProvider::findResources(string searchPattern)
{
    std::vector<string> found_files;
#if defined(ANDROID)
    //Limitation :
    //As far as I know, Android NDK won't provide a way to list subdirectories
    //So we will only list files in the first level directory
    ANativeActivity *nactivity {sf::getNativeActivity()};

    AAssetManager *asset_manager{nullptr};
    if(nactivity)
    {
        asset_manager = nactivity->assetManager;
    }
    if(asset_manager)
    {
        int idx = searchPattern.rfind("/");
        string forced_path = basepath;
        string prefix = "";
        if (idx > -1)
        {
            prefix = searchPattern.substr(0, idx);
            forced_path += "/" + prefix;
            prefix += "/";
        }
        AAssetDir* dir = AAssetManager_openDir(asset_manager, (forced_path).c_str());
        if (dir)
        {
            const char* filename;
            while ((filename = AAssetDir_getNextFileName(dir)) != nullptr)
            {
                if (searchMatch(prefix + filename, searchPattern))
                    found_files.push_back(prefix + filename);
            }
            AAssetDir_close(dir);
        }
    }
#else
    findResources(found_files, "", searchPattern);
#endif
    return found_files;
}

void DirectoryResourceProvider::findResources(std::vector<string>& found_files, const string path, const string searchPattern)
{
#ifdef _MSC_VER
    WIN32_FIND_DATAA data;
    string search_root(basepath + "/" + path);
    if (!search_root.endswith("/"))
    {
        search_root += "/";
    }
    HANDLE handle = FindFirstFileA((search_root + "*").c_str(), &data);
    if (handle == INVALID_HANDLE_VALUE)
        return;
    do
    {
        if (data.cFileName[0] == '.')
            continue;
        string name = path + string(data.cFileName);
        if (data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
        {
            findResources(found_files, name + "/", searchPattern);
        }
        else
        {
            if (searchMatch(name, searchPattern))
                found_files.push_back(name);
        }
    } while (FindNextFileA(handle, &data));

    FindClose(handle);
#else
    DIR* dir = opendir((basepath + "/" + path).c_str());
    if (!dir)
        return;

    struct dirent *entry;
    while ((entry = readdir(dir)) != nullptr)
    {
        if (entry->d_name[0] == '.')
            continue;
        string name = path + string(entry->d_name);
        if (searchMatch(name, searchPattern))
            found_files.push_back(name);
        findResources(found_files, path + string(entry->d_name) + "/", searchPattern);
    }
    closedir(dir);
#endif
}

P<ResourceStream> getResourceStream(string filename)
{
    foreach(ResourceProvider, rp, resourceProviders)
    {
        P<ResourceStream> stream = rp->getResourceStream(filename);
        if (stream)
            return stream;
    }
    return NULL;
}

std::vector<string> findResources(string searchPattern)
{
    std::vector<string> foundFiles;
    foreach(ResourceProvider, rp, resourceProviders)
    {
        std::vector<string> res = rp->findResources(searchPattern);
        foundFiles.insert(foundFiles.end(), res.begin(), res.end());
    }
    return foundFiles;
}
<|endoftext|>


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

#include "chrome/browser/autocomplete/history_quick_provider.h"

#include <vector>

#include "base/basictypes.h"
#include "base/i18n/break_iterator.h"
#include "base/logging.h"
#include "base/metrics/histogram.h"
#include "base/string_number_conversions.h"
#include "base/string_util.h"
#include "base/time.h"
#include "base/utf_string_conversions.h"
#include "chrome/browser/history/history.h"
#include "chrome/browser/history/in_memory_url_index.h"
#include "chrome/browser/net/url_fixer_upper.h"
#include "chrome/browser/prefs/pref_service.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/common/pref_names.h"
#include "chrome/common/url_constants.h"
#include "googleurl/src/url_parse.h"
#include "content/common/notification_source.h"
#include "content/common/notification_type.h"
#include "googleurl/src/url_util.h"
#include "net/base/escape.h"
#include "net/base/net_util.h"

using history::InMemoryURLIndex;
using history::ScoredHistoryMatch;
using history::ScoredHistoryMatches;

// The initial maximum allowable score for a match which cannot be inlined.
const int kMaxNonInliningScore = 1199;

HistoryQuickProvider::HistoryQuickProvider(ACProviderListener* listener,
                                           Profile* profile)
    : HistoryProvider(listener, profile, "HistoryQuickProvider"),
      languages_(profile_->GetPrefs()->GetString(prefs::kAcceptLanguages)) {}

HistoryQuickProvider::~HistoryQuickProvider() {}

void HistoryQuickProvider::Start(const AutocompleteInput& input,
                                 bool minimal_changes) {
  matches_.clear();

  if ((input.type() == AutocompleteInput::INVALID) ||
      (input.type() == AutocompleteInput::FORCED_QUERY))
    return;

  autocomplete_input_ = input;

  // Do some fixup on the user input before matching against it, so we provide
  // good results for local file paths, input with spaces, etc.
  // NOTE: This purposefully doesn't take input.desired_tld() into account; if
  // it did, then holding "ctrl" would change all the results from the
  // HistoryQuickProvider provider, not just the What You Typed Result.
  const string16 fixed_text(FixupUserInput(input));
  if (fixed_text.empty()) {
    // Conceivably fixup could result in an empty string (although I don't
    // have cases where this happens offhand).  We can't do anything with
    // empty input, so just bail; otherwise we'd crash later.
    return;
  }
  autocomplete_input_.set_text(fixed_text);

  // TODO(pkasting): We should just block here until this loads.  Any time
  // someone unloads the history backend, we'll get inconsistent inline
  // autocomplete behavior here.
  if (GetIndex()) {
    base::TimeTicks start_time = base::TimeTicks::Now();
    DoAutocomplete();
    if (input.text().size() < 6) {
      base::TimeTicks end_time = base::TimeTicks::Now();
      std::string name = "HistoryQuickProvider.QueryIndexTime." +
          base::IntToString(input.text().size());
      base::Histogram* counter = base::Histogram::FactoryGet(
          name, 1, 1000, 50, base::Histogram::kUmaTargetedHistogramFlag);
      counter->Add(static_cast<int>((end_time - start_time).InMilliseconds()));
    }
    UpdateStarredStateOfMatches();
  }
}

// HistoryQuickProvider matches are currently not deletable.
// TODO(mrossetti): Determine when a match should be deletable.
void HistoryQuickProvider::DeleteMatch(const AutocompleteMatch& match) {}

void HistoryQuickProvider::DoAutocomplete() {
  // Get the matching URLs from the DB.
  string16 term_string = autocomplete_input_.text();
  term_string = UnescapeURLComponent(term_string,
      UnescapeRule::SPACES | UnescapeRule::URL_SPECIAL_CHARS);
  history::InMemoryURLIndex::String16Vector terms(
      InMemoryURLIndex::WordVectorFromString16(term_string, false));
  ScoredHistoryMatches matches = GetIndex()->HistoryItemsForTerms(terms);
  if (matches.empty())
    return;

  // Artificially reduce the score of high-scoring matches which should not be
  // inline autocompletd. Each such result gets the next available
  // |max_match_score|. Upon use of |max_match_score| it is decremented.
  // All subsequent matches must be clamped to retain match results ordering.
  int max_match_score = autocomplete_input_.prevent_inline_autocomplete() ?
      kMaxNonInliningScore : 1425;
  for (ScoredHistoryMatches::const_iterator match_iter = matches.begin();
       match_iter != matches.end(); ++match_iter) {
    const ScoredHistoryMatch& history_match(*match_iter);
    if (history_match.raw_score > 0) {
      AutocompleteMatch ac_match = QuickMatchToACMatch(history_match,
                                                       &max_match_score);
      matches_.push_back(ac_match);
    }
  }
}

AutocompleteMatch HistoryQuickProvider::QuickMatchToACMatch(
    const ScoredHistoryMatch& history_match,
    int* max_match_score) {
  DCHECK(max_match_score);
  const history::URLRow& info = history_match.url_info;
  int score = CalculateRelevance(history_match, max_match_score);
  AutocompleteMatch match(this, score, !!info.visit_count(),
                          history_match.url_matches.empty() ?
                          AutocompleteMatch::HISTORY_URL :
                          AutocompleteMatch::HISTORY_TITLE);
  match.destination_url = info.url();
  DCHECK(match.destination_url.is_valid());

  // Format the URL autocomplete presentation.
  std::vector<size_t> offsets =
      InMemoryURLIndex::OffsetsFromTermMatches(history_match.url_matches);
  match.contents =
      net::FormatUrlWithOffsets(info.url(), languages_, net::kFormatUrlOmitAll,
                                UnescapeRule::SPACES, NULL, NULL, &offsets);
  history::TermMatches new_matches =
      InMemoryURLIndex::ReplaceOffsetsInTermMatches(history_match.url_matches,
                                                    offsets);
  match.contents_class =
      SpansFromTermMatch(new_matches, match.contents.size(), true);
  match.fill_into_edit = match.contents;

  if (autocomplete_input_.prevent_inline_autocomplete() ||
      !history_match.can_inline) {
    match.inline_autocomplete_offset = string16::npos;
  } else {
    match.inline_autocomplete_offset =
        history_match.input_location + autocomplete_input_.text().length();
    DCHECK_LE(match.inline_autocomplete_offset, match.fill_into_edit.length());
  }

  // Format the description autocomplete presentation.
  match.description = info.title();
  match.description_class = SpansFromTermMatch(history_match.title_matches,
                                               match.description.size(), false);

  return match;
}

history::InMemoryURLIndex* HistoryQuickProvider::GetIndex() {
  if (index_for_testing_.get())
    return index_for_testing_.get();

  HistoryService* const history_service =
      profile_->GetHistoryService(Profile::EXPLICIT_ACCESS);
  if (!history_service)
    return NULL;

  return history_service->InMemoryIndex();
}

void HistoryQuickProvider::SetIndexForTesting(
    history::InMemoryURLIndex* index) {
  DCHECK(index);
  index_for_testing_.reset(index);
}

// static
int HistoryQuickProvider::CalculateRelevance(
    const ScoredHistoryMatch& history_match,
    int* max_match_score) {
  DCHECK(max_match_score);
  // Note that |can_inline| will only be true if what the user typed starts
  // at the beginning of the result's URL and there is exactly one substring
  // match in the URL.
  int score = (history_match.can_inline) ? history_match.raw_score :
      std::min(kMaxNonInliningScore, history_match.raw_score);
  *max_match_score = ((*max_match_score < 0) ? score :
                      std::min(score, *max_match_score)) - 1;
  return *max_match_score + 1;
}

// static
ACMatchClassifications HistoryQuickProvider::SpansFromTermMatch(
    const history::TermMatches& matches,
    size_t text_length,
    bool is_url) {
  ACMatchClassification::Style url_style =
      is_url ? ACMatchClassification::URL : ACMatchClassification::NONE;
  ACMatchClassifications spans;
  if (matches.empty()) {
    if (text_length)
      spans.push_back(ACMatchClassification(0, url_style));
    return spans;
  }
  if (matches[0].offset)
    spans.push_back(ACMatchClassification(0, url_style));
  size_t match_count = matches.size();
  for (size_t i = 0; i < match_count;) {
    size_t offset = matches[i].offset;
    spans.push_back(ACMatchClassification(offset,
        ACMatchClassification::MATCH | url_style));
    // Skip all adjacent matches.
    do {
      offset += matches[i].length;
      ++i;
    } while ((i < match_count) && (offset == matches[i].offset));
    if (offset < text_length)
      spans.push_back(ACMatchClassification(offset, url_style));
  }

  return spans;
}
<commit_msg>Properly set max_match_score to -1 rather than 1425 and adjust some indentation.<commit_after>// Copyright (c) 2011 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "chrome/browser/autocomplete/history_quick_provider.h"

#include <vector>

#include "base/basictypes.h"
#include "base/i18n/break_iterator.h"
#include "base/logging.h"
#include "base/metrics/histogram.h"
#include "base/string_number_conversions.h"
#include "base/string_util.h"
#include "base/time.h"
#include "base/utf_string_conversions.h"
#include "chrome/browser/history/history.h"
#include "chrome/browser/history/in_memory_url_index.h"
#include "chrome/browser/net/url_fixer_upper.h"
#include "chrome/browser/prefs/pref_service.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/common/pref_names.h"
#include "chrome/common/url_constants.h"
#include "googleurl/src/url_parse.h"
#include "content/common/notification_source.h"
#include "content/common/notification_type.h"
#include "googleurl/src/url_util.h"
#include "net/base/escape.h"
#include "net/base/net_util.h"

using history::InMemoryURLIndex;
using history::ScoredHistoryMatch;
using history::ScoredHistoryMatches;

// The initial maximum allowable score for a match which cannot be inlined.
const int kMaxNonInliningScore = 1199;

HistoryQuickProvider::HistoryQuickProvider(ACProviderListener* listener,
                                           Profile* profile)
    : HistoryProvider(listener, profile, "HistoryQuickProvider"),
      languages_(profile_->GetPrefs()->GetString(prefs::kAcceptLanguages)) {}

HistoryQuickProvider::~HistoryQuickProvider() {}

void HistoryQuickProvider::Start(const AutocompleteInput& input,
                                 bool minimal_changes) {
  matches_.clear();

  if ((input.type() == AutocompleteInput::INVALID) ||
      (input.type() == AutocompleteInput::FORCED_QUERY))
    return;

  autocomplete_input_ = input;

  // Do some fixup on the user input before matching against it, so we provide
  // good results for local file paths, input with spaces, etc.
  // NOTE: This purposefully doesn't take input.desired_tld() into account; if
  // it did, then holding "ctrl" would change all the results from the
  // HistoryQuickProvider provider, not just the What You Typed Result.
  const string16 fixed_text(FixupUserInput(input));
  if (fixed_text.empty()) {
    // Conceivably fixup could result in an empty string (although I don't
    // have cases where this happens offhand).  We can't do anything with
    // empty input, so just bail; otherwise we'd crash later.
    return;
  }
  autocomplete_input_.set_text(fixed_text);

  // TODO(pkasting): We should just block here until this loads.  Any time
  // someone unloads the history backend, we'll get inconsistent inline
  // autocomplete behavior here.
  if (GetIndex()) {
    base::TimeTicks start_time = base::TimeTicks::Now();
    DoAutocomplete();
    if (input.text().size() < 6) {
      base::TimeTicks end_time = base::TimeTicks::Now();
      std::string name = "HistoryQuickProvider.QueryIndexTime." +
          base::IntToString(input.text().size());
      base::Histogram* counter = base::Histogram::FactoryGet(
          name, 1, 1000, 50, base::Histogram::kUmaTargetedHistogramFlag);
      counter->Add(static_cast<int>((end_time - start_time).InMilliseconds()));
    }
    UpdateStarredStateOfMatches();
  }
}

// HistoryQuickProvider matches are currently not deletable.
// TODO(mrossetti): Determine when a match should be deletable.
void HistoryQuickProvider::DeleteMatch(const AutocompleteMatch& match) {}

void HistoryQuickProvider::DoAutocomplete() {
  // Get the matching URLs from the DB.
  string16 term_string = autocomplete_input_.text();
  term_string = UnescapeURLComponent(term_string,
      UnescapeRule::SPACES | UnescapeRule::URL_SPECIAL_CHARS);
  history::InMemoryURLIndex::String16Vector terms(
      InMemoryURLIndex::WordVectorFromString16(term_string, false));
  ScoredHistoryMatches matches = GetIndex()->HistoryItemsForTerms(terms);
  if (matches.empty())
    return;

  // Artificially reduce the score of high-scoring matches which should not be
  // inline autocompletd. Each such result gets the next available
  // |max_match_score|. Upon use of |max_match_score| it is decremented.
  // All subsequent matches must be clamped to retain match results ordering.
  int max_match_score = autocomplete_input_.prevent_inline_autocomplete() ?
      kMaxNonInliningScore : -1;
  for (ScoredHistoryMatches::const_iterator match_iter = matches.begin();
       match_iter != matches.end(); ++match_iter) {
    const ScoredHistoryMatch& history_match(*match_iter);
    if (history_match.raw_score > 0) {
      AutocompleteMatch ac_match = QuickMatchToACMatch(history_match,
                                                       &max_match_score);
      matches_.push_back(ac_match);
    }
  }
}

AutocompleteMatch HistoryQuickProvider::QuickMatchToACMatch(
    const ScoredHistoryMatch& history_match,
    int* max_match_score) {
  DCHECK(max_match_score);
  const history::URLRow& info = history_match.url_info;
  int score = CalculateRelevance(history_match, max_match_score);
  AutocompleteMatch match(this, score, !!info.visit_count(),
                          history_match.url_matches.empty() ?
                          AutocompleteMatch::HISTORY_URL :
                          AutocompleteMatch::HISTORY_TITLE);
  match.destination_url = info.url();
  DCHECK(match.destination_url.is_valid());

  // Format the URL autocomplete presentation.
  std::vector<size_t> offsets =
      InMemoryURLIndex::OffsetsFromTermMatches(history_match.url_matches);
  match.contents =
      net::FormatUrlWithOffsets(info.url(), languages_, net::kFormatUrlOmitAll,
                                UnescapeRule::SPACES, NULL, NULL, &offsets);
  history::TermMatches new_matches =
      InMemoryURLIndex::ReplaceOffsetsInTermMatches(history_match.url_matches,
                                                    offsets);
  match.contents_class =
      SpansFromTermMatch(new_matches, match.contents.size(), true);
  match.fill_into_edit = match.contents;

  if (autocomplete_input_.prevent_inline_autocomplete() ||
      !history_match.can_inline) {
    match.inline_autocomplete_offset = string16::npos;
  } else {
    match.inline_autocomplete_offset =
        history_match.input_location + autocomplete_input_.text().length();
    DCHECK_LE(match.inline_autocomplete_offset, match.fill_into_edit.length());
  }

  // Format the description autocomplete presentation.
  match.description = info.title();
  match.description_class = SpansFromTermMatch(history_match.title_matches,
                                               match.description.size(), false);

  return match;
}

history::InMemoryURLIndex* HistoryQuickProvider::GetIndex() {
  if (index_for_testing_.get())
    return index_for_testing_.get();

  HistoryService* const history_service =
      profile_->GetHistoryService(Profile::EXPLICIT_ACCESS);
  if (!history_service)
    return NULL;

  return history_service->InMemoryIndex();
}

void HistoryQuickProvider::SetIndexForTesting(
    history::InMemoryURLIndex* index) {
  DCHECK(index);
  index_for_testing_.reset(index);
}

// static
int HistoryQuickProvider::CalculateRelevance(
    const ScoredHistoryMatch& history_match,
    int* max_match_score) {
  DCHECK(max_match_score);
  // Note that |can_inline| will only be true if what the user typed starts
  // at the beginning of the result's URL and there is exactly one substring
  // match in the URL.
  int score = (history_match.can_inline) ? history_match.raw_score :
      std::min(kMaxNonInliningScore, history_match.raw_score);
  *max_match_score = ((*max_match_score < 0) ?
      score : std::min(score, *max_match_score)) - 1;
  return *max_match_score + 1;
}

// static
ACMatchClassifications HistoryQuickProvider::SpansFromTermMatch(
    const history::TermMatches& matches,
    size_t text_length,
    bool is_url) {
  ACMatchClassification::Style url_style =
      is_url ? ACMatchClassification::URL : ACMatchClassification::NONE;
  ACMatchClassifications spans;
  if (matches.empty()) {
    if (text_length)
      spans.push_back(ACMatchClassification(0, url_style));
    return spans;
  }
  if (matches[0].offset)
    spans.push_back(ACMatchClassification(0, url_style));
  size_t match_count = matches.size();
  for (size_t i = 0; i < match_count;) {
    size_t offset = matches[i].offset;
    spans.push_back(ACMatchClassification(offset,
        ACMatchClassification::MATCH | url_style));
    // Skip all adjacent matches.
    do {
      offset += matches[i].length;
      ++i;
    } while ((i < match_count) && (offset == matches[i].offset));
    if (offset < text_length)
      spans.push_back(ACMatchClassification(offset, url_style));
  }

  return spans;
}
<|endoftext|>


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

#include "chrome/browser/chromeos/app_mode/kiosk_app_manager.h"

#include <map>

#include "base/path_service.h"
#include "base/prefs/pref_registry_simple.h"
#include "base/stl_util.h"
#include "chrome/browser/chromeos/app_mode/kiosk_app_data.h"
#include "chrome/browser/chromeos/app_mode/kiosk_app_manager_observer.h"
#include "chrome/browser/chromeos/app_mode/kiosk_app_prefs_local_state.h"
#include "chrome/common/chrome_paths.h"

namespace chromeos {

namespace {

void CreateDirectory(const base::FilePath& dir) {
}

}  // namespace

// static
const char KioskAppManager::kKioskDictionaryName[] = "kiosk";
const char KioskAppManager::kKeyApps[] = "apps";
const char KioskAppManager::kIconCacheDir[] = "kiosk";

// static
static base::LazyInstance<KioskAppManager> instance = LAZY_INSTANCE_INITIALIZER;
KioskAppManager* KioskAppManager::Get() {
  return instance.Pointer();
}

// static
void KioskAppManager::RegisterPrefs(PrefRegistrySimple* registry) {
  registry->RegisterDictionaryPref(kKioskDictionaryName);
}

KioskAppManager::App::App(const KioskAppData& data)
    : id(data.id()),
      name(data.name()),
      icon(data.icon()),
      is_loading(data.IsLoading()) {
}

KioskAppManager::App::App() : is_loading(false) {}
KioskAppManager::App::~App() {}

std::string KioskAppManager::GetAutoLaunchApp() const {
  return prefs_->GetAutoLaunchApp();
}

void KioskAppManager::SetAutoLaunchApp(const std::string& app_id) {
  prefs_->SetAutoLaunchApp(app_id);
}

bool KioskAppManager::GetSuppressAutoLaunch() const {
  return prefs_->GetSuppressAutoLaunch();
}

void KioskAppManager::SetSuppressAutoLaunch(bool suppress) {
  prefs_->SetSuppressAutoLaunch(suppress);
}

void KioskAppManager::AddApp(const std::string& app_id) {
  prefs_->AddApp(app_id);
}

void KioskAppManager::RemoveApp(const std::string& app_id) {
  prefs_->RemoveApp(app_id);
}

void KioskAppManager::GetApps(Apps* apps) const {
  apps->reserve(apps_.size());
  for (size_t i = 0; i < apps_.size(); ++i)
    apps->push_back(App(*apps_[i]));
}

bool KioskAppManager::GetApp(const std::string& app_id, App* app) const {
  const KioskAppData* data = GetAppData(app_id);
  if (!data)
    return false;

  *app = App(*data);
  return true;
}

const base::RefCountedString* KioskAppManager::GetAppRawIcon(
    const std::string& app_id) const {
  const KioskAppData* data = GetAppData(app_id);
  if (!data)
    return NULL;

  return data->raw_icon();
}

void KioskAppManager::AddObserver(KioskAppManagerObserver* observer) {
  observers_.AddObserver(observer);
}

void KioskAppManager::RemoveObserver(KioskAppManagerObserver* observer) {
  observers_.RemoveObserver(observer);
}

KioskAppManager::KioskAppManager()
    : prefs_(new KioskAppPrefsLocalState) {
  UpdateAppData();
  prefs_->AddObserver(this);
}

KioskAppManager::~KioskAppManager() {
  prefs_->RemoveObserver(this);
}

const KioskAppData* KioskAppManager::GetAppData(
    const std::string& app_id) const {
  for (size_t i = 0; i < apps_.size(); ++i) {
    const KioskAppData* data = apps_[i];
    if (data->id() == app_id)
      return data;
  }

  return NULL;
}

void KioskAppManager::UpdateAppData() {
  // Gets app id to data mapping for existing apps.
  std::map<std::string, KioskAppData*> old_apps;
  for (size_t i = 0; i < apps_.size(); ++i)
    old_apps[apps_[i]->id()] = apps_[i];
  apps_.weak_clear();  // |old_apps| takes ownership

  // Re-populates |apps_| and reuses existing KioskAppData when possible.
  KioskAppPrefs::AppIds app_ids;
  prefs_->GetApps(&app_ids);
  for (size_t i = 0; i < app_ids.size(); ++i) {
    const std::string& id = app_ids[i];
    std::map<std::string, KioskAppData*>::iterator it = old_apps.find(id);
    if (it != old_apps.end()) {
      apps_.push_back(it->second);
      old_apps.erase(it);
    } else {
      KioskAppData* new_app = new KioskAppData(this, id);
      apps_.push_back(new_app);  // Takes ownership of |new_app|.
      new_app->Load();
    }
  }

  // Clears cache and deletes the remaining old data.
  for (std::map<std::string, KioskAppData*>::iterator it = old_apps.begin();
       it != old_apps.end(); ++it) {
    it->second->ClearCache();
  }
  STLDeleteValues(&old_apps);
}

void KioskAppManager::OnKioskAutoLaunchAppChanged()  {
  FOR_EACH_OBSERVER(KioskAppManagerObserver,
                    observers_,
                    OnKioskAutoLaunchAppChanged());
}

void KioskAppManager::OnKioskAppsChanged() {
  UpdateAppData();

  FOR_EACH_OBSERVER(KioskAppManagerObserver,
                    observers_,
                    OnKioskAppsChanged());
}

void KioskAppManager::GetKioskAppIconCacheDir(base::FilePath* cache_dir) {
  base::FilePath user_data_dir;
  CHECK(PathService::Get(chrome::DIR_USER_DATA, &user_data_dir));
  *cache_dir = user_data_dir.AppendASCII(kIconCacheDir);
}

void KioskAppManager::OnKioskAppDataChanged(const std::string& app_id) {
  FOR_EACH_OBSERVER(KioskAppManagerObserver,
                    observers_,
                    OnKioskAppDataChanged(app_id));
}

void KioskAppManager::OnKioskAppDataLoadFailure(const std::string& app_id) {
  FOR_EACH_OBSERVER(KioskAppManagerObserver,
                    observers_,
                    OnKioskAppDataLoadFailure(app_id));
  RemoveApp(app_id);
}

}  // namespace chromeos
<commit_msg>cros: Remove app cryptohome with the app.<commit_after>// Copyright 2013 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "chrome/browser/chromeos/app_mode/kiosk_app_manager.h"

#include <map>

#include "base/bind.h"
#include "base/path_service.h"
#include "base/prefs/pref_registry_simple.h"
#include "base/stl_util.h"
#include "chrome/browser/chromeos/app_mode/kiosk_app_data.h"
#include "chrome/browser/chromeos/app_mode/kiosk_app_manager_observer.h"
#include "chrome/browser/chromeos/app_mode/kiosk_app_prefs_local_state.h"
#include "chrome/common/chrome_paths.h"
#include "chromeos/cryptohome/async_method_caller.h"

namespace chromeos {

namespace {

void OnRemoveAppCryptohomeComplete(const std::string& app,
                                   bool success,
                                   cryptohome::MountError return_code) {
  if (!success) {
    LOG(ERROR) << "Remove cryptohome for " << app
        << " failed, return code: " << return_code;
  }
}

}  // namespace

// static
const char KioskAppManager::kKioskDictionaryName[] = "kiosk";
const char KioskAppManager::kKeyApps[] = "apps";
const char KioskAppManager::kIconCacheDir[] = "kiosk";

// static
static base::LazyInstance<KioskAppManager> instance = LAZY_INSTANCE_INITIALIZER;
KioskAppManager* KioskAppManager::Get() {
  return instance.Pointer();
}

// static
void KioskAppManager::RegisterPrefs(PrefRegistrySimple* registry) {
  registry->RegisterDictionaryPref(kKioskDictionaryName);
}

KioskAppManager::App::App(const KioskAppData& data)
    : id(data.id()),
      name(data.name()),
      icon(data.icon()),
      is_loading(data.IsLoading()) {
}

KioskAppManager::App::App() : is_loading(false) {}
KioskAppManager::App::~App() {}

std::string KioskAppManager::GetAutoLaunchApp() const {
  return prefs_->GetAutoLaunchApp();
}

void KioskAppManager::SetAutoLaunchApp(const std::string& app_id) {
  prefs_->SetAutoLaunchApp(app_id);
}

bool KioskAppManager::GetSuppressAutoLaunch() const {
  return prefs_->GetSuppressAutoLaunch();
}

void KioskAppManager::SetSuppressAutoLaunch(bool suppress) {
  prefs_->SetSuppressAutoLaunch(suppress);
}

void KioskAppManager::AddApp(const std::string& app_id) {
  prefs_->AddApp(app_id);
}

void KioskAppManager::RemoveApp(const std::string& app_id) {
  prefs_->RemoveApp(app_id);
}

void KioskAppManager::GetApps(Apps* apps) const {
  apps->reserve(apps_.size());
  for (size_t i = 0; i < apps_.size(); ++i)
    apps->push_back(App(*apps_[i]));
}

bool KioskAppManager::GetApp(const std::string& app_id, App* app) const {
  const KioskAppData* data = GetAppData(app_id);
  if (!data)
    return false;

  *app = App(*data);
  return true;
}

const base::RefCountedString* KioskAppManager::GetAppRawIcon(
    const std::string& app_id) const {
  const KioskAppData* data = GetAppData(app_id);
  if (!data)
    return NULL;

  return data->raw_icon();
}

void KioskAppManager::AddObserver(KioskAppManagerObserver* observer) {
  observers_.AddObserver(observer);
}

void KioskAppManager::RemoveObserver(KioskAppManagerObserver* observer) {
  observers_.RemoveObserver(observer);
}

KioskAppManager::KioskAppManager()
    : prefs_(new KioskAppPrefsLocalState) {
  UpdateAppData();
  prefs_->AddObserver(this);
}

KioskAppManager::~KioskAppManager() {
  prefs_->RemoveObserver(this);
}

const KioskAppData* KioskAppManager::GetAppData(
    const std::string& app_id) const {
  for (size_t i = 0; i < apps_.size(); ++i) {
    const KioskAppData* data = apps_[i];
    if (data->id() == app_id)
      return data;
  }

  return NULL;
}

void KioskAppManager::UpdateAppData() {
  // Gets app id to data mapping for existing apps.
  std::map<std::string, KioskAppData*> old_apps;
  for (size_t i = 0; i < apps_.size(); ++i)
    old_apps[apps_[i]->id()] = apps_[i];
  apps_.weak_clear();  // |old_apps| takes ownership

  // Re-populates |apps_| and reuses existing KioskAppData when possible.
  KioskAppPrefs::AppIds app_ids;
  prefs_->GetApps(&app_ids);
  for (size_t i = 0; i < app_ids.size(); ++i) {
    const std::string& id = app_ids[i];
    std::map<std::string, KioskAppData*>::iterator it = old_apps.find(id);
    if (it != old_apps.end()) {
      apps_.push_back(it->second);
      old_apps.erase(it);
    } else {
      KioskAppData* new_app = new KioskAppData(this, id);
      apps_.push_back(new_app);  // Takes ownership of |new_app|.
      new_app->Load();
    }
  }

  // Clears cache and deletes the remaining old data.
  for (std::map<std::string, KioskAppData*>::iterator it = old_apps.begin();
       it != old_apps.end(); ++it) {
    it->second->ClearCache();
    cryptohome::AsyncMethodCaller::GetInstance()->AsyncRemove(
        it->first, base::Bind(&OnRemoveAppCryptohomeComplete, it->first));
  }
  STLDeleteValues(&old_apps);
}

void KioskAppManager::OnKioskAutoLaunchAppChanged()  {
  FOR_EACH_OBSERVER(KioskAppManagerObserver,
                    observers_,
                    OnKioskAutoLaunchAppChanged());
}

void KioskAppManager::OnKioskAppsChanged() {
  UpdateAppData();

  FOR_EACH_OBSERVER(KioskAppManagerObserver,
                    observers_,
                    OnKioskAppsChanged());
}

void KioskAppManager::GetKioskAppIconCacheDir(base::FilePath* cache_dir) {
  base::FilePath user_data_dir;
  CHECK(PathService::Get(chrome::DIR_USER_DATA, &user_data_dir));
  *cache_dir = user_data_dir.AppendASCII(kIconCacheDir);
}

void KioskAppManager::OnKioskAppDataChanged(const std::string& app_id) {
  FOR_EACH_OBSERVER(KioskAppManagerObserver,
                    observers_,
                    OnKioskAppDataChanged(app_id));
}

void KioskAppManager::OnKioskAppDataLoadFailure(const std::string& app_id) {
  FOR_EACH_OBSERVER(KioskAppManagerObserver,
                    observers_,
                    OnKioskAppDataLoadFailure(app_id));
  RemoveApp(app_id);
}

}  // namespace chromeos
<|endoftext|>