<commit_before>// Copyright (c) 2009-2015 The Bitcoin Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.

#include "crypter.h"

#include "script/script.h"
#include "script/standard.h"
#include "util.h"

#include <string>
#include <vector>
#include <boost/foreach.hpp>
#include <openssl/aes.h>
#include <openssl/evp.h>

bool CCrypter::SetKeyFromPassphrase(const SecureString& strKeyData, const std::vector<unsigned char>& chSalt, const unsigned int nRounds, const unsigned int nDerivationMethod)
{
    if (nRounds < 1 || chSalt.size() != WALLET_CRYPTO_SALT_SIZE)
        return false;

    int i = 0;
    if (nDerivationMethod == 0)
        i = EVP_BytesToKey(EVP_aes_256_cbc(), EVP_sha512(), &chSalt[0],
                          (unsigned char *)&strKeyData[0], strKeyData.size(), nRounds, chKey, chIV);

    if (i != (int)WALLET_CRYPTO_KEY_SIZE)
    {
        memory_cleanse(chKey, sizeof(chKey));
        memory_cleanse(chIV, sizeof(chIV));
        return false;
    }

    fKeySet = true;
    return true;
}

bool CCrypter::SetKey(const CKeyingMaterial& chNewKey, const std::vector<unsigned char>& chNewIV)
{
    if (chNewKey.size() != WALLET_CRYPTO_KEY_SIZE || chNewIV.size() != WALLET_CRYPTO_IV_SIZE)
        return false;

    memcpy(&chKey[0], &chNewKey[0], sizeof chKey);
    memcpy(&chIV[0], &chNewIV[0], sizeof chIV);

    fKeySet = true;
    return true;
}

bool CCrypter::Encrypt(const CKeyingMaterial& vchPlaintext, std::vector<unsigned char> &vchCiphertext)
{
    if (!fKeySet)
        return false;

    // max ciphertext len for a n bytes of plaintext is
    // n + AES_BLOCK_SIZE - 1 bytes
    int nLen = vchPlaintext.size();
    int nCLen = nLen + AES_BLOCK_SIZE, nFLen = 0;
    vchCiphertext = std::vector<unsigned char> (nCLen);

    EVP_CIPHER_CTX ctx;

    bool fOk = true;

    EVP_CIPHER_CTX_init(&ctx);
    if (fOk) fOk = EVP_EncryptInit_ex(&ctx, EVP_aes_256_cbc(), NULL, chKey, chIV) != 0;
    if (fOk) fOk = EVP_EncryptUpdate(&ctx, &vchCiphertext[0], &nCLen, &vchPlaintext[0], nLen) != 0;
    if (fOk) fOk = EVP_EncryptFinal_ex(&ctx, (&vchCiphertext[0]) + nCLen, &nFLen) != 0;
    EVP_CIPHER_CTX_cleanup(&ctx);

    if (!fOk) return false;

    vchCiphertext.resize(nCLen + nFLen);
    return true;
}

bool CCrypter::Decrypt(const std::vector<unsigned char>& vchCiphertext, CKeyingMaterial& vchPlaintext)
{
    if (!fKeySet)
        return false;

    // plaintext will always be equal to or lesser than length of ciphertext
    int nLen = vchCiphertext.size();
    int nPLen = nLen, nFLen = 0;

    vchPlaintext = CKeyingMaterial(nPLen);

    EVP_CIPHER_CTX ctx;

    bool fOk = true;

    EVP_CIPHER_CTX_init(&ctx);
    if (fOk) fOk = EVP_DecryptInit_ex(&ctx, EVP_aes_256_cbc(), NULL, chKey, chIV) != 0;
    if (fOk) fOk = EVP_DecryptUpdate(&ctx, &vchPlaintext[0], &nPLen, &vchCiphertext[0], nLen) != 0;
    if (fOk) fOk = EVP_DecryptFinal_ex(&ctx, (&vchPlaintext[0]) + nPLen, &nFLen) != 0;
    EVP_CIPHER_CTX_cleanup(&ctx);

    if (!fOk) return false;

    vchPlaintext.resize(nPLen + nFLen);
    return true;
}

static bool EncryptSecret(const CKeyingMaterial& vMasterKey, const CKeyingMaterial &vchPlaintext, const uint256& nIV, std::vector<unsigned char> &vchCiphertext)
{
    CCrypter cKeyCrypter;
    std::vector<unsigned char> chIV(WALLET_CRYPTO_IV_SIZE);
    memcpy(&chIV[0], &nIV, WALLET_CRYPTO_IV_SIZE);
    if(!cKeyCrypter.SetKey(vMasterKey, chIV))
        return false;
    return cKeyCrypter.Encrypt(*((const CKeyingMaterial*)&vchPlaintext), vchCiphertext);
}

static bool DecryptSecret(const CKeyingMaterial& vMasterKey, const std::vector<unsigned char>& vchCiphertext, const uint256& nIV, CKeyingMaterial& vchPlaintext)
{
    CCrypter cKeyCrypter;
    std::vector<unsigned char> chIV(WALLET_CRYPTO_IV_SIZE);
    memcpy(&chIV[0], &nIV, WALLET_CRYPTO_IV_SIZE);
    if(!cKeyCrypter.SetKey(vMasterKey, chIV))
        return false;
    return cKeyCrypter.Decrypt(vchCiphertext, *((CKeyingMaterial*)&vchPlaintext));
}

static bool DecryptKey(const CKeyingMaterial& vMasterKey, const std::vector<unsigned char>& vchCryptedSecret, const CPubKey& vchPubKey, CKey& key)
{
    CKeyingMaterial vchSecret;
    if(!DecryptSecret(vMasterKey, vchCryptedSecret, vchPubKey.GetHash(), vchSecret))
        return false;

    if (vchSecret.size() != 32)
        return false;

    key.Set(vchSecret.begin(), vchSecret.end(), vchPubKey.IsCompressed());
    return key.VerifyPubKey(vchPubKey);
}

bool CCryptoKeyStore::SetCrypted()
{
    LOCK(cs_KeyStore);
    if (fUseCrypto)
        return true;
    if (!mapKeys.empty())
        return false;
    fUseCrypto = true;
    return true;
}

bool CCryptoKeyStore::Lock()
{
    if (!SetCrypted())
        return false;

    {
        LOCK(cs_KeyStore);
        vMasterKey.clear();
    }

    NotifyStatusChanged(this);
    return true;
}

bool CCryptoKeyStore::Unlock(const CKeyingMaterial& vMasterKeyIn)
{
    {
        LOCK(cs_KeyStore);
        if (!SetCrypted())
            return false;

        bool keyPass = false;
        bool keyFail = false;
        CryptedKeyMap::const_iterator mi = mapCryptedKeys.begin();
        for (; mi != mapCryptedKeys.end(); ++mi)
        {
            const CPubKey &vchPubKey = (*mi).second.first;
            const std::vector<unsigned char> &vchCryptedSecret = (*mi).second.second;
            CKey key;
            if (!DecryptKey(vMasterKeyIn, vchCryptedSecret, vchPubKey, key))
            {
                keyFail = true;
                break;
            }
            keyPass = true;
            if (fDecryptionThoroughlyChecked)
                break;
        }
        if (keyPass && keyFail)
        {
            LogPrintf("The wallet is probably corrupted: Some keys decrypt but not all.\n");
            assert(false);
        }
        if (keyFail || !keyPass)
            return false;
        vMasterKey = vMasterKeyIn;
        fDecryptionThoroughlyChecked = true;
    }
    NotifyStatusChanged(this);
    return true;
}

bool CCryptoKeyStore::AddKeyPubKey(const CKey& key, const CPubKey &pubkey)
{
    {
        LOCK(cs_KeyStore);
        if (!IsCrypted())
            return CBasicKeyStore::AddKeyPubKey(key, pubkey);

        if (IsLocked())
            return false;

        std::vector<unsigned char> vchCryptedSecret;
        CKeyingMaterial vchSecret(key.begin(), key.end());
        if (!EncryptSecret(vMasterKey, vchSecret, pubkey.GetHash(), vchCryptedSecret))
            return false;

        if (!AddCryptedKey(pubkey, vchCryptedSecret))
            return false;
    }
    return true;
}

bool CCryptoKeyStore::AddCryptedKey(const CPubKey &vchPubKey, const std::vector<unsigned char> &vchCryptedSecret)
{
    {
        LOCK(cs_KeyStore);
        if (!SetCrypted())
            return false;

        mapCryptedKeys[vchPubKey.GetID()] = make_pair(vchPubKey, vchCryptedSecret);
    }
    return true;
}

bool CCryptoKeyStore::GetKey(const CKeyID &address, CKey& keyOut) const
{
    {
        LOCK(cs_KeyStore);
        if (!IsCrypted())
            return CBasicKeyStore::GetKey(address, keyOut);

        CryptedKeyMap::const_iterator mi = mapCryptedKeys.find(address);
        if (mi != mapCryptedKeys.end())
        {
            const CPubKey &vchPubKey = (*mi).second.first;
            const std::vector<unsigned char> &vchCryptedSecret = (*mi).second.second;
            return DecryptKey(vMasterKey, vchCryptedSecret, vchPubKey, keyOut);
        }
    }
    return false;
}

bool CCryptoKeyStore::GetPubKey(const CKeyID &address, CPubKey& vchPubKeyOut) const
{
    {
        LOCK(cs_KeyStore);
        if (!IsCrypted())
            return CBasicKeyStore::GetPubKey(address, vchPubKeyOut);

        CryptedKeyMap::const_iterator mi = mapCryptedKeys.find(address);
        if (mi != mapCryptedKeys.end())
        {
            vchPubKeyOut = (*mi).second.first;
            return true;
        }
        // Check for watch-only pubkeys
        return CBasicKeyStore::GetPubKey(address, vchPubKeyOut);
    }
    return false;
}

bool CCryptoKeyStore::EncryptKeys(CKeyingMaterial& vMasterKeyIn)
{
    {
        LOCK(cs_KeyStore);
        if (!mapCryptedKeys.empty() || IsCrypted())
            return false;

        fUseCrypto = true;
        BOOST_FOREACH(KeyMap::value_type& mKey, mapKeys)
        {
            const CKey &key = mKey.second;
            CPubKey vchPubKey = key.GetPubKey();
            CKeyingMaterial vchSecret(key.begin(), key.end());
            std::vector<unsigned char> vchCryptedSecret;
            if (!EncryptSecret(vMasterKeyIn, vchSecret, vchPubKey.GetHash(), vchCryptedSecret))
                return false;
            if (!AddCryptedKey(vchPubKey, vchCryptedSecret))
                return false;
        }
        mapKeys.clear();
    }
    return true;
}
<commit_msg>crypter: hook up the new aes cbc classes<commit_after>// Copyright (c) 2009-2015 The Bitcoin Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.

#include "crypter.h"

#include "crypto/aes.h"
#include "script/script.h"
#include "script/standard.h"
#include "util.h"

#include <string>
#include <vector>
#include <boost/foreach.hpp>
#include <openssl/aes.h>
#include <openssl/evp.h>

bool CCrypter::SetKeyFromPassphrase(const SecureString& strKeyData, const std::vector<unsigned char>& chSalt, const unsigned int nRounds, const unsigned int nDerivationMethod)
{
    if (nRounds < 1 || chSalt.size() != WALLET_CRYPTO_SALT_SIZE)
        return false;

    int i = 0;
    if (nDerivationMethod == 0)
        i = EVP_BytesToKey(EVP_aes_256_cbc(), EVP_sha512(), &chSalt[0],
                          (unsigned char *)&strKeyData[0], strKeyData.size(), nRounds, chKey, chIV);

    if (i != (int)WALLET_CRYPTO_KEY_SIZE)
    {
        memory_cleanse(chKey, sizeof(chKey));
        memory_cleanse(chIV, sizeof(chIV));
        return false;
    }

    fKeySet = true;
    return true;
}

bool CCrypter::SetKey(const CKeyingMaterial& chNewKey, const std::vector<unsigned char>& chNewIV)
{
    if (chNewKey.size() != WALLET_CRYPTO_KEY_SIZE || chNewIV.size() != WALLET_CRYPTO_IV_SIZE)
        return false;

    memcpy(&chKey[0], &chNewKey[0], sizeof chKey);
    memcpy(&chIV[0], &chNewIV[0], sizeof chIV);

    fKeySet = true;
    return true;
}

bool CCrypter::Encrypt(const CKeyingMaterial& vchPlaintext, std::vector<unsigned char> &vchCiphertext)
{
    if (!fKeySet)
        return false;

    // max ciphertext len for a n bytes of plaintext is
    // n + AES_BLOCKSIZE bytes
    vchCiphertext.resize(vchPlaintext.size() + AES_BLOCKSIZE);

    AES256CBCEncrypt enc(chKey, chIV, true);
    size_t nLen = enc.Encrypt(&vchPlaintext[0], vchPlaintext.size(), &vchCiphertext[0]);
    if(nLen < vchPlaintext.size())
        return false;
    vchCiphertext.resize(nLen);

    return true;
}

bool CCrypter::Decrypt(const std::vector<unsigned char>& vchCiphertext, CKeyingMaterial& vchPlaintext)
{
    if (!fKeySet)
        return false;

    // plaintext will always be equal to or lesser than length of ciphertext
    int nLen = vchCiphertext.size();

    vchPlaintext.resize(nLen);

    AES256CBCDecrypt dec(chKey, chIV, true);
    nLen = dec.Decrypt(&vchCiphertext[0], vchCiphertext.size(), &vchPlaintext[0]);
    if(nLen == 0)
        return false;
    vchPlaintext.resize(nLen);
    return true;
}

static bool EncryptSecret(const CKeyingMaterial& vMasterKey, const CKeyingMaterial &vchPlaintext, const uint256& nIV, std::vector<unsigned char> &vchCiphertext)
{
    CCrypter cKeyCrypter;
    std::vector<unsigned char> chIV(WALLET_CRYPTO_IV_SIZE);
    memcpy(&chIV[0], &nIV, WALLET_CRYPTO_IV_SIZE);
    if(!cKeyCrypter.SetKey(vMasterKey, chIV))
        return false;
    return cKeyCrypter.Encrypt(*((const CKeyingMaterial*)&vchPlaintext), vchCiphertext);
}

static bool DecryptSecret(const CKeyingMaterial& vMasterKey, const std::vector<unsigned char>& vchCiphertext, const uint256& nIV, CKeyingMaterial& vchPlaintext)
{
    CCrypter cKeyCrypter;
    std::vector<unsigned char> chIV(WALLET_CRYPTO_IV_SIZE);
    memcpy(&chIV[0], &nIV, WALLET_CRYPTO_IV_SIZE);
    if(!cKeyCrypter.SetKey(vMasterKey, chIV))
        return false;
    return cKeyCrypter.Decrypt(vchCiphertext, *((CKeyingMaterial*)&vchPlaintext));
}

static bool DecryptKey(const CKeyingMaterial& vMasterKey, const std::vector<unsigned char>& vchCryptedSecret, const CPubKey& vchPubKey, CKey& key)
{
    CKeyingMaterial vchSecret;
    if(!DecryptSecret(vMasterKey, vchCryptedSecret, vchPubKey.GetHash(), vchSecret))
        return false;

    if (vchSecret.size() != 32)
        return false;

    key.Set(vchSecret.begin(), vchSecret.end(), vchPubKey.IsCompressed());
    return key.VerifyPubKey(vchPubKey);
}

bool CCryptoKeyStore::SetCrypted()
{
    LOCK(cs_KeyStore);
    if (fUseCrypto)
        return true;
    if (!mapKeys.empty())
        return false;
    fUseCrypto = true;
    return true;
}

bool CCryptoKeyStore::Lock()
{
    if (!SetCrypted())
        return false;

    {
        LOCK(cs_KeyStore);
        vMasterKey.clear();
    }

    NotifyStatusChanged(this);
    return true;
}

bool CCryptoKeyStore::Unlock(const CKeyingMaterial& vMasterKeyIn)
{
    {
        LOCK(cs_KeyStore);
        if (!SetCrypted())
            return false;

        bool keyPass = false;
        bool keyFail = false;
        CryptedKeyMap::const_iterator mi = mapCryptedKeys.begin();
        for (; mi != mapCryptedKeys.end(); ++mi)
        {
            const CPubKey &vchPubKey = (*mi).second.first;
            const std::vector<unsigned char> &vchCryptedSecret = (*mi).second.second;
            CKey key;
            if (!DecryptKey(vMasterKeyIn, vchCryptedSecret, vchPubKey, key))
            {
                keyFail = true;
                break;
            }
            keyPass = true;
            if (fDecryptionThoroughlyChecked)
                break;
        }
        if (keyPass && keyFail)
        {
            LogPrintf("The wallet is probably corrupted: Some keys decrypt but not all.\n");
            assert(false);
        }
        if (keyFail || !keyPass)
            return false;
        vMasterKey = vMasterKeyIn;
        fDecryptionThoroughlyChecked = true;
    }
    NotifyStatusChanged(this);
    return true;
}

bool CCryptoKeyStore::AddKeyPubKey(const CKey& key, const CPubKey &pubkey)
{
    {
        LOCK(cs_KeyStore);
        if (!IsCrypted())
            return CBasicKeyStore::AddKeyPubKey(key, pubkey);

        if (IsLocked())
            return false;

        std::vector<unsigned char> vchCryptedSecret;
        CKeyingMaterial vchSecret(key.begin(), key.end());
        if (!EncryptSecret(vMasterKey, vchSecret, pubkey.GetHash(), vchCryptedSecret))
            return false;

        if (!AddCryptedKey(pubkey, vchCryptedSecret))
            return false;
    }
    return true;
}

bool CCryptoKeyStore::AddCryptedKey(const CPubKey &vchPubKey, const std::vector<unsigned char> &vchCryptedSecret)
{
    {
        LOCK(cs_KeyStore);
        if (!SetCrypted())
            return false;

        mapCryptedKeys[vchPubKey.GetID()] = make_pair(vchPubKey, vchCryptedSecret);
    }
    return true;
}

bool CCryptoKeyStore::GetKey(const CKeyID &address, CKey& keyOut) const
{
    {
        LOCK(cs_KeyStore);
        if (!IsCrypted())
            return CBasicKeyStore::GetKey(address, keyOut);

        CryptedKeyMap::const_iterator mi = mapCryptedKeys.find(address);
        if (mi != mapCryptedKeys.end())
        {
            const CPubKey &vchPubKey = (*mi).second.first;
            const std::vector<unsigned char> &vchCryptedSecret = (*mi).second.second;
            return DecryptKey(vMasterKey, vchCryptedSecret, vchPubKey, keyOut);
        }
    }
    return false;
}

bool CCryptoKeyStore::GetPubKey(const CKeyID &address, CPubKey& vchPubKeyOut) const
{
    {
        LOCK(cs_KeyStore);
        if (!IsCrypted())
            return CBasicKeyStore::GetPubKey(address, vchPubKeyOut);

        CryptedKeyMap::const_iterator mi = mapCryptedKeys.find(address);
        if (mi != mapCryptedKeys.end())
        {
            vchPubKeyOut = (*mi).second.first;
            return true;
        }
        // Check for watch-only pubkeys
        return CBasicKeyStore::GetPubKey(address, vchPubKeyOut);
    }
    return false;
}

bool CCryptoKeyStore::EncryptKeys(CKeyingMaterial& vMasterKeyIn)
{
    {
        LOCK(cs_KeyStore);
        if (!mapCryptedKeys.empty() || IsCrypted())
            return false;

        fUseCrypto = true;
        BOOST_FOREACH(KeyMap::value_type& mKey, mapKeys)
        {
            const CKey &key = mKey.second;
            CPubKey vchPubKey = key.GetPubKey();
            CKeyingMaterial vchSecret(key.begin(), key.end());
            std::vector<unsigned char> vchCryptedSecret;
            if (!EncryptSecret(vMasterKeyIn, vchSecret, vchPubKey.GetHash(), vchCryptedSecret))
                return false;
            if (!AddCryptedKey(vchPubKey, vchCryptedSecret))
                return false;
        }
        mapKeys.clear();
    }
    return true;
}
<|endoftext|>


<commit_before>/*
MIT License

Copyright (c) 2017-2018 Eren Okka

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 <map>
#include <memory>

#include <windows.h>
#include <winternl.h>

#include "open_files.h"
#include "util.h"

namespace anisthesia {
namespace win {

// WARNING: This file uses internal Windows APIs. The functions and structures
// that are defined here and in <winternl.h> are subject to change.

constexpr NTSTATUS STATUS_INFO_LENGTH_MISMATCH = 0xC0000004L;
constexpr NTSTATUS STATUS_SUCCESS = 0x00000000L;

enum {
  // SystemHandleInformation is limited to 16-bit process IDs, so we use
  // SystemExtendedHandleInformation instead.
  SystemExtendedHandleInformation = 64,
};

struct SYSTEM_HANDLE_TABLE_ENTRY_INFO_EX {
  PVOID Object;
  ULONG_PTR UniqueProcessId;
  HANDLE HandleValue;
  ACCESS_MASK GrantedAccess;
  USHORT CreatorBackTraceIndex;
  USHORT ObjectTypeIndex;
  ULONG HandleAttributes;
  ULONG Reserved;
};

struct SYSTEM_HANDLE_INFORMATION_EX {
  ULONG_PTR NumberOfHandles;
  ULONG_PTR Reserved;
  SYSTEM_HANDLE_TABLE_ENTRY_INFO_EX Handles[1];
};

// This structure is defined as PUBLIC_OBJECT_TYPE_INFORMATION in Microsoft's
// <winternl.h>, while it is defined as OBJECT_TYPE_INFORMATION in GCC's header
// with actual data members instead of 'Reserved[22]'.
struct OBJECT_TYPE_INFORMATION {
  UNICODE_STRING TypeName;
  ULONG Reserved[22];
};

using buffer_t = std::unique_ptr<BYTE[]>;

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

PVOID GetNtProcAddress(LPCSTR proc_name) {
  // We have to use run-time dynamic linking, because there is no associated
  // import library for the internal functions.
  return reinterpret_cast<PVOID>(
      GetProcAddress(GetModuleHandleA("ntdll.dll"), proc_name));
}

NTSTATUS NtQuerySystemInformation(
    SYSTEM_INFORMATION_CLASS system_information_class,
    PVOID system_information,
    ULONG system_information_length,
    PULONG return_length) {
  static const auto nt_query_system_information =
      reinterpret_cast<decltype(::NtQuerySystemInformation)*>(
          GetNtProcAddress("NtQuerySystemInformation"));
  return nt_query_system_information(system_information_class,
                                     system_information,
                                     system_information_length,
                                     return_length);
}

NTSTATUS NtQueryObject(
    HANDLE handle,
    OBJECT_INFORMATION_CLASS object_information_class,
    PVOID object_information,
    ULONG object_information_length,
    PULONG return_length) {
  static const auto nt_query_object =
      reinterpret_cast<decltype(::NtQueryObject)*>(
          GetNtProcAddress("NtQueryObject"));
  return nt_query_object(handle,
                         object_information_class,
                         object_information,
                         object_information_length,
                         return_length);
}

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

inline bool NtSuccess(NTSTATUS status) {
  return status >= 0;
}

buffer_t QuerySystemInformation(
      SYSTEM_INFORMATION_CLASS system_information_class) {
  ULONG size = 1 << 20;                //  1 MiB
  constexpr ULONG kMaxSize = 1 << 24;  // 16 MiB

  buffer_t buffer(new BYTE[size]);
  NTSTATUS status = STATUS_SUCCESS;

  do {
    ULONG return_length = 0;
    status = win::NtQuerySystemInformation(
        system_information_class, buffer.get(), size, &return_length);
    if (status == STATUS_INFO_LENGTH_MISMATCH) {
      size = (return_length > size) ? return_length : (size * 2);
      buffer.reset(new BYTE[size]);
    }
  } while (status == STATUS_INFO_LENGTH_MISMATCH && size < kMaxSize);

  if (!NtSuccess(status))
    buffer.reset();

  return buffer;
}

buffer_t QueryObject(HANDLE handle,
                     OBJECT_INFORMATION_CLASS object_information_class,
                     ULONG size) {
  buffer_t buffer(new BYTE[size]);
  ULONG return_length = 0;

  const auto query_object = [&]() {
    return win::NtQueryObject(handle, object_information_class,
                              buffer.get(), size, &return_length);
  };

  auto status = query_object();
  if (status == STATUS_INFO_LENGTH_MISMATCH) {
    size = return_length;
    buffer.reset(new BYTE[size]);
    status = query_object();
  }

  if (!NtSuccess(status))
    buffer.reset();

  return buffer;
}

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

HANDLE OpenProcess(DWORD process_id) {
  // If we try to open a SYSTEM process, this function fails and the last error
  // code is ERROR_ACCESS_DENIED.
  return ::OpenProcess(PROCESS_DUP_HANDLE, false, process_id);
}

HANDLE DuplicateHandle(HANDLE process_handle, HANDLE handle) {
  HANDLE dup_handle = nullptr;
  const auto result = ::DuplicateHandle(process_handle, handle,
      ::GetCurrentProcess(), &dup_handle, 0, false, DUPLICATE_SAME_ACCESS);
  return result ? dup_handle : nullptr;
}

buffer_t GetSystemHandleInformation() {
  return QuerySystemInformation(
      static_cast<SYSTEM_INFORMATION_CLASS>(SystemExtendedHandleInformation));
}

std::wstring GetUnicodeString(const UNICODE_STRING& unicode_string) {
  if (!unicode_string.Length)
    return std::wstring();

  return std::wstring(unicode_string.Buffer,
                      unicode_string.Length / sizeof(WCHAR));
}

std::wstring GetObjectTypeName(HANDLE handle) {
  const auto buffer = QueryObject(handle, ObjectTypeInformation,
                                  sizeof(OBJECT_TYPE_INFORMATION));
  if (!buffer)
    return std::wstring();

  const auto& type_information =
      *reinterpret_cast<OBJECT_TYPE_INFORMATION*>(buffer.get());

  return GetUnicodeString(type_information.TypeName);
}

std::wstring GetFinalPathNameByHandle(HANDLE handle) {
  std::wstring buffer(MAX_PATH, '\0');

  auto get_final_path_name_by_handle = [&]() {
    return ::GetFinalPathNameByHandle(handle, &buffer.front(), buffer.size(),
        FILE_NAME_NORMALIZED | VOLUME_NAME_DOS);
  };

  auto result = get_final_path_name_by_handle();
  if (result > buffer.size()) {
    buffer.resize(result, '\0');
    result = get_final_path_name_by_handle();
  }

  if (result < buffer.size())
    buffer.resize(result);

  return buffer;
}

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

bool VerifyObjectType(HANDLE handle, USHORT object_type_index) {
  // File type index varies between OS versions:
  //
  // - 25: Windows Vista
  // - 28: Windows XP, Windows 7
  // - 30: Windows 8.1
  // - 31: Windows 8, Windows 10
  // - 34: Windows 10 Anniversary Update
  // - 35: Windows 10 Creators Update
  // - 36: Windows 10 Fall Creators Update
  //
  // Here we initialize the value with 0, so that it is determined at run time.
  // This is more reliable than hard-coding the values for each OS version.
  static USHORT file_type_index = 0;

  if (file_type_index)
    return object_type_index == file_type_index;

  if (!handle)
    return true;

  if (GetObjectTypeName(handle) == L"File") {
    file_type_index = object_type_index;
    return true;
  }

  return false;
}

bool VerifyAccessMask(ACCESS_MASK access_mask) {
  // Certain kinds of handles, mostly those which refer to named pipes, cause
  // some functions such as NtQueryObject and GetFinalPathNameByHandle to hang.
  // By examining the access mask, we can get some clues about the object type
  // and bail out if necessary.
  //
  // One method would be to hard-code individual masks that are known to cause
  // trouble:
  //
  // - 0x00100000 (SYNCHRONIZE access)
  // - 0x0012008d (e.g. "\Device\NamedPipe\DropboxDataPipe")
  // - 0x00120189
  // - 0x0012019f
  // - 0x0016019f (e.g. "\Device\Afd\Endpoint")
  // - 0x001a019f
  //
  // While this works in most situations, we occasionally end up skipping valid
  // file handles. This method also requires updating the blacklist when we
  // encounter other masks that cause our application to hang on users' end.
  //
  // The most common access mask for the valid files we are interested in is
  // 0x00120089, which is made up of the following access rights:
  //
  // - 0x00000001 FILE_READ_DATA
  // - 0x00000008 FILE_READ_EA
  // - 0x00000080 FILE_READ_ATTRIBUTES
  // - 0x00020000 READ_CONTROL
  // - 0x00100000 SYNCHRONIZE
  //
  // Media players must have read-access in order to play a video file, so we
  // can safely skip a handle in the absence of this basic right:
  if (!(access_mask & FILE_READ_DATA))
    return false;

  // We further assume that media players do not have any kind of write access
  // to video files:
  if ((access_mask & FILE_APPEND_DATA) ||
      (access_mask & FILE_WRITE_EA) ||
      (access_mask & FILE_WRITE_ATTRIBUTES)) {
    return false;
  }

  return true;
}

bool VerifyFileType(HANDLE handle) {
  // Skip character files, sockets, pipes, and files of unknown type
  return ::GetFileType(handle) == FILE_TYPE_DISK;
}

bool VerifyPath(const std::wstring& path) {
  if (path.empty())
    return false;

  // Skip files under system directories
  if (IsSystemDirectory(path))
    return false;

  // Skip invalid files, and directories
  const auto file_attributes = ::GetFileAttributes(path.c_str());
  if ((file_attributes == INVALID_FILE_ATTRIBUTES) ||
      (file_attributes & FILE_ATTRIBUTE_DIRECTORY)) {
    return false;
  }

  return true;
}

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

bool EnumerateOpenFiles(const std::set<DWORD>& process_ids,
                        open_file_proc_t open_file_proc) {
  if (!open_file_proc)
    return false;

  std::map<DWORD, Handle> process_handles;
  for (const auto& process_id : process_ids) {
    const auto handle = OpenProcess(process_id);
    if (handle)
      process_handles[process_id] = Handle(handle);
  }
  if (process_handles.empty())
    return false;

  auto system_handle_information_buffer = GetSystemHandleInformation();
  if (!system_handle_information_buffer)
    return false;

  const auto& system_handle_information =
      *reinterpret_cast<SYSTEM_HANDLE_INFORMATION_EX*>(
          system_handle_information_buffer.get());
  if (!system_handle_information.NumberOfHandles)
    return false;

  for (size_t i = 0; i < system_handle_information.NumberOfHandles; ++i) {
    const auto& handle = system_handle_information.Handles[i];

    // Skip if this handle does not belong to one of our PIDs
    const auto process_id = static_cast<DWORD>(handle.UniqueProcessId);
    if (!process_ids.count(process_id))
      continue;

    // Skip if this is not a file handle
    if (!VerifyObjectType(nullptr, handle.ObjectTypeIndex))
      continue;

    // Skip if the file handle has an inappropriate access mask
    if (!VerifyAccessMask(handle.GrantedAccess))
      continue;

    // Duplicate the handle so that we can query it
    const auto process_handle = process_handles[process_id].get();
    Handle dup_handle(DuplicateHandle(process_handle, handle.HandleValue));
    if (!dup_handle)
      continue;

    // Skip if this is not a file handle, while determining file type index
    if (!VerifyObjectType(dup_handle.get(), handle.ObjectTypeIndex))
      continue;

    // Skip if this is not a disk file
    if (!VerifyFileType(dup_handle.get()))
      continue;

    OpenFile open_file;
    open_file.process_id = process_id;
    open_file.path = GetFinalPathNameByHandle(dup_handle.get());

    if (!VerifyPath(open_file.path))
      continue;

    if (!open_file_proc(open_file))
      return false;
  }

  return true;
}

}  // namespace win
}  // namespace anisthesia
<commit_msg>Update file type index table<commit_after>/*
MIT License

Copyright (c) 2017-2018 Eren Okka

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 <map>
#include <memory>

#include <windows.h>
#include <winternl.h>

#include "open_files.h"
#include "util.h"

namespace anisthesia {
namespace win {

// WARNING: This file uses internal Windows APIs. The functions and structures
// that are defined here and in <winternl.h> are subject to change.

constexpr NTSTATUS STATUS_INFO_LENGTH_MISMATCH = 0xC0000004L;
constexpr NTSTATUS STATUS_SUCCESS = 0x00000000L;

enum {
  // SystemHandleInformation is limited to 16-bit process IDs, so we use
  // SystemExtendedHandleInformation instead.
  SystemExtendedHandleInformation = 64,
};

struct SYSTEM_HANDLE_TABLE_ENTRY_INFO_EX {
  PVOID Object;
  ULONG_PTR UniqueProcessId;
  HANDLE HandleValue;
  ACCESS_MASK GrantedAccess;
  USHORT CreatorBackTraceIndex;
  USHORT ObjectTypeIndex;
  ULONG HandleAttributes;
  ULONG Reserved;
};

struct SYSTEM_HANDLE_INFORMATION_EX {
  ULONG_PTR NumberOfHandles;
  ULONG_PTR Reserved;
  SYSTEM_HANDLE_TABLE_ENTRY_INFO_EX Handles[1];
};

// This structure is defined as PUBLIC_OBJECT_TYPE_INFORMATION in Microsoft's
// <winternl.h>, while it is defined as OBJECT_TYPE_INFORMATION in GCC's header
// with actual data members instead of 'Reserved[22]'.
struct OBJECT_TYPE_INFORMATION {
  UNICODE_STRING TypeName;
  ULONG Reserved[22];
};

using buffer_t = std::unique_ptr<BYTE[]>;

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

PVOID GetNtProcAddress(LPCSTR proc_name) {
  // We have to use run-time dynamic linking, because there is no associated
  // import library for the internal functions.
  return reinterpret_cast<PVOID>(
      GetProcAddress(GetModuleHandleA("ntdll.dll"), proc_name));
}

NTSTATUS NtQuerySystemInformation(
    SYSTEM_INFORMATION_CLASS system_information_class,
    PVOID system_information,
    ULONG system_information_length,
    PULONG return_length) {
  static const auto nt_query_system_information =
      reinterpret_cast<decltype(::NtQuerySystemInformation)*>(
          GetNtProcAddress("NtQuerySystemInformation"));
  return nt_query_system_information(system_information_class,
                                     system_information,
                                     system_information_length,
                                     return_length);
}

NTSTATUS NtQueryObject(
    HANDLE handle,
    OBJECT_INFORMATION_CLASS object_information_class,
    PVOID object_information,
    ULONG object_information_length,
    PULONG return_length) {
  static const auto nt_query_object =
      reinterpret_cast<decltype(::NtQueryObject)*>(
          GetNtProcAddress("NtQueryObject"));
  return nt_query_object(handle,
                         object_information_class,
                         object_information,
                         object_information_length,
                         return_length);
}

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

inline bool NtSuccess(NTSTATUS status) {
  return status >= 0;
}

buffer_t QuerySystemInformation(
      SYSTEM_INFORMATION_CLASS system_information_class) {
  ULONG size = 1 << 20;                //  1 MiB
  constexpr ULONG kMaxSize = 1 << 24;  // 16 MiB

  buffer_t buffer(new BYTE[size]);
  NTSTATUS status = STATUS_SUCCESS;

  do {
    ULONG return_length = 0;
    status = win::NtQuerySystemInformation(
        system_information_class, buffer.get(), size, &return_length);
    if (status == STATUS_INFO_LENGTH_MISMATCH) {
      size = (return_length > size) ? return_length : (size * 2);
      buffer.reset(new BYTE[size]);
    }
  } while (status == STATUS_INFO_LENGTH_MISMATCH && size < kMaxSize);

  if (!NtSuccess(status))
    buffer.reset();

  return buffer;
}

buffer_t QueryObject(HANDLE handle,
                     OBJECT_INFORMATION_CLASS object_information_class,
                     ULONG size) {
  buffer_t buffer(new BYTE[size]);
  ULONG return_length = 0;

  const auto query_object = [&]() {
    return win::NtQueryObject(handle, object_information_class,
                              buffer.get(), size, &return_length);
  };

  auto status = query_object();
  if (status == STATUS_INFO_LENGTH_MISMATCH) {
    size = return_length;
    buffer.reset(new BYTE[size]);
    status = query_object();
  }

  if (!NtSuccess(status))
    buffer.reset();

  return buffer;
}

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

HANDLE OpenProcess(DWORD process_id) {
  // If we try to open a SYSTEM process, this function fails and the last error
  // code is ERROR_ACCESS_DENIED.
  return ::OpenProcess(PROCESS_DUP_HANDLE, false, process_id);
}

HANDLE DuplicateHandle(HANDLE process_handle, HANDLE handle) {
  HANDLE dup_handle = nullptr;
  const auto result = ::DuplicateHandle(process_handle, handle,
      ::GetCurrentProcess(), &dup_handle, 0, false, DUPLICATE_SAME_ACCESS);
  return result ? dup_handle : nullptr;
}

buffer_t GetSystemHandleInformation() {
  return QuerySystemInformation(
      static_cast<SYSTEM_INFORMATION_CLASS>(SystemExtendedHandleInformation));
}

std::wstring GetUnicodeString(const UNICODE_STRING& unicode_string) {
  if (!unicode_string.Length)
    return std::wstring();

  return std::wstring(unicode_string.Buffer,
                      unicode_string.Length / sizeof(WCHAR));
}

std::wstring GetObjectTypeName(HANDLE handle) {
  const auto buffer = QueryObject(handle, ObjectTypeInformation,
                                  sizeof(OBJECT_TYPE_INFORMATION));
  if (!buffer)
    return std::wstring();

  const auto& type_information =
      *reinterpret_cast<OBJECT_TYPE_INFORMATION*>(buffer.get());

  return GetUnicodeString(type_information.TypeName);
}

std::wstring GetFinalPathNameByHandle(HANDLE handle) {
  std::wstring buffer(MAX_PATH, '\0');

  auto get_final_path_name_by_handle = [&]() {
    return ::GetFinalPathNameByHandle(handle, &buffer.front(), buffer.size(),
        FILE_NAME_NORMALIZED | VOLUME_NAME_DOS);
  };

  auto result = get_final_path_name_by_handle();
  if (result > buffer.size()) {
    buffer.resize(result, '\0');
    result = get_final_path_name_by_handle();
  }

  if (result < buffer.size())
    buffer.resize(result);

  return buffer;
}

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

bool VerifyObjectType(HANDLE handle, USHORT object_type_index) {
  // File type index varies between OS versions:
  //
  // Index | OS version
  // ------|-----------
  //    25 | Vista
  //    28 | XP
  //       | 7
  //    30 | 8.1
  //    31 | 8
  //       | 10
  //    34 | 10 1607 (Anniversary Update)
  //    35 | 10 1703 (Creators Update)
  //    36 | 10 1709 (Fall Creators Update)
  //       | 10 1803 (April 2018 Update)
  //
  // Here we initialize the value with 0, so that it is determined at run time.
  // This is more reliable than hard-coding the values for each OS version.
  static USHORT file_type_index = 0;

  if (file_type_index)
    return object_type_index == file_type_index;

  if (!handle)
    return true;

  if (GetObjectTypeName(handle) == L"File") {
    file_type_index = object_type_index;
    return true;
  }

  return false;
}

bool VerifyAccessMask(ACCESS_MASK access_mask) {
  // Certain kinds of handles, mostly those which refer to named pipes, cause
  // some functions such as NtQueryObject and GetFinalPathNameByHandle to hang.
  // By examining the access mask, we can get some clues about the object type
  // and bail out if necessary.
  //
  // One method would be to hard-code individual masks that are known to cause
  // trouble:
  //
  // - 0x00100000 (SYNCHRONIZE access)
  // - 0x0012008d (e.g. "\Device\NamedPipe\DropboxDataPipe")
  // - 0x00120189
  // - 0x0012019f
  // - 0x0016019f (e.g. "\Device\Afd\Endpoint")
  // - 0x001a019f
  //
  // While this works in most situations, we occasionally end up skipping valid
  // file handles. This method also requires updating the blacklist when we
  // encounter other masks that cause our application to hang on users' end.
  //
  // The most common access mask for the valid files we are interested in is
  // 0x00120089, which is made up of the following access rights:
  //
  // - 0x00000001 FILE_READ_DATA
  // - 0x00000008 FILE_READ_EA
  // - 0x00000080 FILE_READ_ATTRIBUTES
  // - 0x00020000 READ_CONTROL
  // - 0x00100000 SYNCHRONIZE
  //
  // Media players must have read-access in order to play a video file, so we
  // can safely skip a handle in the absence of this basic right:
  if (!(access_mask & FILE_READ_DATA))
    return false;

  // We further assume that media players do not have any kind of write access
  // to video files:
  if ((access_mask & FILE_APPEND_DATA) ||
      (access_mask & FILE_WRITE_EA) ||
      (access_mask & FILE_WRITE_ATTRIBUTES)) {
    return false;
  }

  return true;
}

bool VerifyFileType(HANDLE handle) {
  // Skip character files, sockets, pipes, and files of unknown type
  return ::GetFileType(handle) == FILE_TYPE_DISK;
}

bool VerifyPath(const std::wstring& path) {
  if (path.empty())
    return false;

  // Skip files under system directories
  if (IsSystemDirectory(path))
    return false;

  // Skip invalid files, and directories
  const auto file_attributes = ::GetFileAttributes(path.c_str());
  if ((file_attributes == INVALID_FILE_ATTRIBUTES) ||
      (file_attributes & FILE_ATTRIBUTE_DIRECTORY)) {
    return false;
  }

  return true;
}

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

bool EnumerateOpenFiles(const std::set<DWORD>& process_ids,
                        open_file_proc_t open_file_proc) {
  if (!open_file_proc)
    return false;

  std::map<DWORD, Handle> process_handles;
  for (const auto& process_id : process_ids) {
    const auto handle = OpenProcess(process_id);
    if (handle)
      process_handles[process_id] = Handle(handle);
  }
  if (process_handles.empty())
    return false;

  auto system_handle_information_buffer = GetSystemHandleInformation();
  if (!system_handle_information_buffer)
    return false;

  const auto& system_handle_information =
      *reinterpret_cast<SYSTEM_HANDLE_INFORMATION_EX*>(
          system_handle_information_buffer.get());
  if (!system_handle_information.NumberOfHandles)
    return false;

  for (size_t i = 0; i < system_handle_information.NumberOfHandles; ++i) {
    const auto& handle = system_handle_information.Handles[i];

    // Skip if this handle does not belong to one of our PIDs
    const auto process_id = static_cast<DWORD>(handle.UniqueProcessId);
    if (!process_ids.count(process_id))
      continue;

    // Skip if this is not a file handle
    if (!VerifyObjectType(nullptr, handle.ObjectTypeIndex))
      continue;

    // Skip if the file handle has an inappropriate access mask
    if (!VerifyAccessMask(handle.GrantedAccess))
      continue;

    // Duplicate the handle so that we can query it
    const auto process_handle = process_handles[process_id].get();
    Handle dup_handle(DuplicateHandle(process_handle, handle.HandleValue));
    if (!dup_handle)
      continue;

    // Skip if this is not a file handle, while determining file type index
    if (!VerifyObjectType(dup_handle.get(), handle.ObjectTypeIndex))
      continue;

    // Skip if this is not a disk file
    if (!VerifyFileType(dup_handle.get()))
      continue;

    OpenFile open_file;
    open_file.process_id = process_id;
    open_file.path = GetFinalPathNameByHandle(dup_handle.get());

    if (!VerifyPath(open_file.path))
      continue;

    if (!open_file_proc(open_file))
      return false;
  }

  return true;
}

}  // namespace win
}  // namespace anisthesia
<|endoftext|>


<commit_before>#include "selection.hh"

#include "utf8.hh"

namespace Kakoune
{

void Range::merge_with(const Range& range)
{
    if (m_first < m_last)
        m_first = std::min(m_first, range.m_first);
    if (m_first > m_last)
        m_first = std::max(m_first, range.m_first);
    m_last = range.m_last;
}

BufferIterator Range::begin() const
{
    return std::min(m_first, m_last);
}

BufferIterator Range::end() const
{
    return utf8::next(std::max(m_first, m_last));
}

String Range::content() const
{
    return m_first.buffer().string(begin(), end());
}

void Range::check_invariant() const
{
#ifdef KAK_DEBUG
    kak_assert(m_first.is_valid());
    kak_assert(m_last.is_valid());
    kak_assert(utf8::is_character_start(m_first));
    kak_assert(utf8::is_character_start(m_last));
#endif
}

static void avoid_eol(BufferIterator& it)
{
    const auto column = it.column();
    if (column != 0 and column == it.buffer().line_length(it.line()) - 1)
        it = utf8::previous(it);
}

void Selection::avoid_eol()
{
    Kakoune::avoid_eol(first());
    Kakoune::avoid_eol(last());
}

namespace
{

template<template <bool, bool> class UpdateFunc>
void on_buffer_change(SelectionList& sels, const BufferCoord& begin, const BufferCoord& end, LineCount end_line)
{
    auto update_beg = std::lower_bound(sels.begin(), sels.end(), begin,
                                       [](const Selection& s, const BufferCoord& c) { return std::max(s.first(), s.last()).coord() < c; });
    auto update_only_line_beg = std::upper_bound(sels.begin(), sels.end(), end_line,
                                                 [](LineCount l, const Selection& s) { return l < std::min(s.first(), s.last()).line(); });

    if (update_beg != update_only_line_beg)
    {
        // for the first one, we are not sure if min < begin
        UpdateFunc<false, false>{}(update_beg->first(), begin, end);
        UpdateFunc<false, false>{}(update_beg->last(), begin, end);
    }
    for (auto it = update_beg+1; it < update_only_line_beg; ++it)
    {
        UpdateFunc<false, true>{}(it->first(), begin, end);
        UpdateFunc<false, true>{}(it->last(), begin, end);
    }
    if (end.line > begin.line)
    {
        for (auto it = update_only_line_beg; it != sels.end(); ++it)
        {
            UpdateFunc<true, true>{}(it->first(), begin, end);
            UpdateFunc<true, true>{}(it->last(), begin, end);
        }
    }
}

template<bool assume_different_line, bool assume_greater_than_begin>
struct UpdateInsert
{
    void operator()(BufferIterator& it,
                    const BufferCoord& begin, const BufferCoord& end) const
    {
        BufferCoord coord = it.coord();
        if (assume_different_line)
            kak_assert(begin.line < coord.line);
        if (not assume_greater_than_begin and coord < begin)
            return;
        if (not assume_different_line and begin.line == coord.line)
            coord.column = end.column + coord.column - begin.column;

        coord.line += end.line - begin.line;
        it = coord;
    }
};

template<bool assume_different_line, bool assume_greater_than_begin>
struct UpdateErase
{
    void operator()(BufferIterator& it,
                    const BufferCoord& begin, const BufferCoord& end) const
    {
        BufferCoord coord = it.coord();
        if (not assume_greater_than_begin and coord < begin)
            return;
        if (assume_different_line)
            kak_assert(end.line < coord.line);
        if (not assume_different_line and coord <= end)
            coord = it.buffer().clamp(begin);
        else
        {
            if (not assume_different_line and end.line == coord.line)
            {
                coord.line = begin.line;
                coord.column = begin.column + coord.column - end.column;
            }
            else
                coord.line -= end.line - begin.line;
        }
        it = coord;
    }
};

}

void SelectionList::update_insert(const BufferCoord& begin, const BufferCoord& end)
{
    on_buffer_change<UpdateInsert>(*this, begin, end, begin.line);
}

void SelectionList::update_erase(const BufferCoord& begin, const BufferCoord& end)
{
    on_buffer_change<UpdateErase>(*this, begin, end, end.line);
}

void SelectionList::check_invariant() const
{
#ifdef KAK_DEBUG
    for (size_t i = 0; i < size(); ++i)
    {
        auto& sel = (*this)[i];
        sel.check_invariant();
        if (i+1 < size())
            kak_assert(sel.begin() <= (*this)[i+1].begin());
    }
#endif
}

}
<commit_msg>tweak Range::merge_with behaviour<commit_after>#include "selection.hh"

#include "utf8.hh"

namespace Kakoune
{

void Range::merge_with(const Range& range)
{
    m_last = range.m_last;
    if (m_first < m_last)
        m_first = std::min(m_first, range.m_first);
    if (m_first > m_last)
        m_first = std::max(m_first, range.m_first);
}

BufferIterator Range::begin() const
{
    return std::min(m_first, m_last);
}

BufferIterator Range::end() const
{
    return utf8::next(std::max(m_first, m_last));
}

String Range::content() const
{
    return m_first.buffer().string(begin(), end());
}

void Range::check_invariant() const
{
#ifdef KAK_DEBUG
    kak_assert(m_first.is_valid());
    kak_assert(m_last.is_valid());
    kak_assert(utf8::is_character_start(m_first));
    kak_assert(utf8::is_character_start(m_last));
#endif
}

static void avoid_eol(BufferIterator& it)
{
    const auto column = it.column();
    if (column != 0 and column == it.buffer().line_length(it.line()) - 1)
        it = utf8::previous(it);
}

void Selection::avoid_eol()
{
    Kakoune::avoid_eol(first());
    Kakoune::avoid_eol(last());
}

namespace
{

template<template <bool, bool> class UpdateFunc>
void on_buffer_change(SelectionList& sels, const BufferCoord& begin, const BufferCoord& end, LineCount end_line)
{
    auto update_beg = std::lower_bound(sels.begin(), sels.end(), begin,
                                       [](const Selection& s, const BufferCoord& c) { return std::max(s.first(), s.last()).coord() < c; });
    auto update_only_line_beg = std::upper_bound(sels.begin(), sels.end(), end_line,
                                                 [](LineCount l, const Selection& s) { return l < std::min(s.first(), s.last()).line(); });

    if (update_beg != update_only_line_beg)
    {
        // for the first one, we are not sure if min < begin
        UpdateFunc<false, false>{}(update_beg->first(), begin, end);
        UpdateFunc<false, false>{}(update_beg->last(), begin, end);
    }
    for (auto it = update_beg+1; it < update_only_line_beg; ++it)
    {
        UpdateFunc<false, true>{}(it->first(), begin, end);
        UpdateFunc<false, true>{}(it->last(), begin, end);
    }
    if (end.line > begin.line)
    {
        for (auto it = update_only_line_beg; it != sels.end(); ++it)
        {
            UpdateFunc<true, true>{}(it->first(), begin, end);
            UpdateFunc<true, true>{}(it->last(), begin, end);
        }
    }
}

template<bool assume_different_line, bool assume_greater_than_begin>
struct UpdateInsert
{
    void operator()(BufferIterator& it,
                    const BufferCoord& begin, const BufferCoord& end) const
    {
        BufferCoord coord = it.coord();
        if (assume_different_line)
            kak_assert(begin.line < coord.line);
        if (not assume_greater_than_begin and coord < begin)
            return;
        if (not assume_different_line and begin.line == coord.line)
            coord.column = end.column + coord.column - begin.column;

        coord.line += end.line - begin.line;
        it = coord;
    }
};

template<bool assume_different_line, bool assume_greater_than_begin>
struct UpdateErase
{
    void operator()(BufferIterator& it,
                    const BufferCoord& begin, const BufferCoord& end) const
    {
        BufferCoord coord = it.coord();
        if (not assume_greater_than_begin and coord < begin)
            return;
        if (assume_different_line)
            kak_assert(end.line < coord.line);
        if (not assume_different_line and coord <= end)
            coord = it.buffer().clamp(begin);
        else
        {
            if (not assume_different_line and end.line == coord.line)
            {
                coord.line = begin.line;
                coord.column = begin.column + coord.column - end.column;
            }
            else
                coord.line -= end.line - begin.line;
        }
        it = coord;
    }
};

}

void SelectionList::update_insert(const BufferCoord& begin, const BufferCoord& end)
{
    on_buffer_change<UpdateInsert>(*this, begin, end, begin.line);
}

void SelectionList::update_erase(const BufferCoord& begin, const BufferCoord& end)
{
    on_buffer_change<UpdateErase>(*this, begin, end, end.line);
}

void SelectionList::check_invariant() const
{
#ifdef KAK_DEBUG
    for (size_t i = 0; i < size(); ++i)
    {
        auto& sel = (*this)[i];
        sel.check_invariant();
        if (i+1 < size())
            kak_assert(sel.begin() <= (*this)[i+1].begin());
    }
#endif
}

}
<|endoftext|>


<commit_before>/**
 * \file   hop.cc
 * \Author Andrea Barberio <insomniac@slackware.it>
 * \date   October 2015
 * \brief  Definition of the Hop class
 *
 * This file contains the definition of the Hop class, which represent every
 * single hop in a traceroute. A Hop includes the sent packet, the matching
 * received packet (if any), NAT information and last-hop information.
 *
 * This module currently offers the set-up of the logging facilities.
 *
 * \sa hop.h
 */

#include <memory>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netdb.h>
#include <sys/socket.h>

#include "dublintraceroute/hop.h"
#include "dublintraceroute/common.h"
#include "dublintraceroute/exceptions.h"
#include "dublintraceroute/icmp_messages.h"

/** \brief setter the sent packet
 */
void Hop::sent(IP &packet) {
	sent_ = std::make_shared<IP>(packet);
}

/** \brief setter for the timestamp of the sent packet
 */
void Hop::sent_timestamp(const Tins::Timestamp &timestamp) {
	sent_timestamp_ = std::make_shared<Timestamp>(timestamp);
}

/** \brief setter for the host name of the responding IP
 */
void Hop::name(std::string &name) {
	name_ = std::make_shared<std::string>(name);
}

std::string Hop::resolve() {
	if (!received())
		return std::string();

	struct sockaddr_in sa;
	memset(&sa, 0, sizeof(sa));
	sa.sin_family = AF_INET;
	sa.sin_port = htons(0);
	if (inet_pton(AF_INET, received()->src_addr().to_string().c_str(), &sa.sin_addr) != 1)
		throw (std::runtime_error("inet_pton failed"));
	char host[NI_MAXHOST], service[NI_MAXSERV];

	std::string name;
	if (getnameinfo((struct sockaddr *)&sa, sizeof(sa), host, sizeof(host), service, sizeof(service), NI_NUMERICSERV) == 0)
		name = std::string(host);
	else
		name = received()->src_addr().to_string();
	name_ = std::make_shared<std::string>(name);

	return name;
}

/** \brief setter for the received packet and its timestamp
 */
void Hop::received(IP &packet, const Tins::Timestamp &timestamp) {
	received_ = std::make_shared<IP>(packet);
	received_timestamp_ = std::make_shared<Timestamp>(timestamp);
}

/** \brief return the NAT ID of the hop
 *
 * This method returns the NAT identifier for this hop. The NAT identifier is
 * calculated as the difference between the checksum of the inner UDP layer of
 * the received packet and the checksum of the sent UDP packet.
 */
uint16_t Hop::nat_id() {

	if (!received()) {
		throw DublinTracerouteException(
			"Cannot get NAT ID for unmatched packets"
		);
	}
	uint16_t chk1 = sent_->rfind_pdu<UDP>().checksum();
	IP inner_ip = received_->rfind_pdu<RawPDU>().to<IP>();
	uint16_t chk2 = inner_ip.rfind_pdu<UDP>().checksum();
	return chk2 - chk1;
}

/** \brief return the RTT in microseconds
 *
 * This method returns the Round-Trip Time in microseconds, if a matching packet
 * was received, 0 otherwise.
 */
unsigned int Hop::rtt() {
	if (received()) {
		unsigned long long ts1 = sent_timestamp()->seconds() * 1000000 + sent_timestamp()->microseconds();
		unsigned long long ts2 = received_timestamp()->seconds() * 1000000 + received_timestamp()->microseconds();
		return ts2 - ts1;
	} else {
		return 0;
	}
}

/** \brief Convert the hop to JSON
 *
 * This method converts the hop data to a JSON representation. The
 * representation is lossy and cannot be used to rebuild the original packet.
 */
Json::Value Hop::to_json() {
	icmpmessages icmpm;
	Json::Value root;
	Json::Value nullvalue;

	// Serialize the sent packet
	root["is_last"] = is_last_hop();
	// IP layer
	root["sent"]["ip"]["src"] = sent()->src_addr().to_string();
	root["sent"]["ip"]["dst"] = sent()->dst_addr().to_string();
	root["sent"]["ip"]["ttl"] = sent()->ttl();

	// UDP layer
	try {
		auto udp = sent()->rfind_pdu<UDP>();
		root["sent"]["udp"]["sport"] = udp.sport();
		root["sent"]["udp"]["dport"] = udp.dport();
	} catch (Tins::pdu_not_found) {
	}

	// If present, serialize the received packet
	if (received()) {
		root["rtt_usec"] = rtt();

		// IP layer
		root["received"]["ip"]["src"] = received()->src_addr().to_string();
		root["received"]["ip"]["dst"] = received()->dst_addr().to_string();
		root["received"]["ip"]["ttl"] = received()->ttl();

		// ICMP layer
		try {
			auto icmp = received()->rfind_pdu<ICMP>();
			root["received"]["icmp"]["type"] = static_cast<int>(icmp.code());
			root["received"]["icmp"]["code"] = static_cast<int>(icmp.type());
			root["received"]["icmp"]["description"] = icmpm.get(icmp.type(), icmp.code());
			root["received"]["icmp"]["extensions"] = Json::Value(Json::arrayValue);
			root["received"]["icmp"]["mpls_labels"] = Json::Value(Json::arrayValue);
			if (icmp.has_extensions()) {
				for (auto &extension : icmp.extensions().extensions()) {
					Json::Value ext_node = Json::Value();
					unsigned int size = static_cast<unsigned int>(extension.size());
					unsigned int ext_class = static_cast<unsigned int>(extension.extension_class());
					unsigned int ext_type = static_cast<unsigned int>(extension.extension_type());
					auto &payload = extension.payload();
					ext_node["size"] = size;  // 16 bits
					ext_node["class"] = ext_class;  // 8 bits
					ext_node["type"] = ext_type;  // 8 bits
					ext_node["payload"] = std::string(payload.begin(), payload.end()); // size - 4 bytes
					root["received"]["icmp"]["extensions"].append(ext_node);

					// if MPLS was encountered, also add parsed extension
					if (ext_class == ICMP_EXTENSION_MPLS_CLASS && ext_type == ICMP_EXTENSION_MPLS_TYPE) {
						// FIXME here I am assuming that size is always a multiple of 4
						for (unsigned int idx = 0; idx < payload.size(); idx += 4) {
							unsigned int label = (payload[idx] << 12) + (payload[idx + 1] << 4) + (payload[idx + 2] >> 4);
							unsigned int experimental = (payload[idx + 2] & 0x0f) >> 1;
							unsigned int bottom_of_stack = payload[idx + 2] & 0x01;
							unsigned int ttl = payload[idx + 3];
							Json::Value mpls_node = Json::Value();
							mpls_node["label"] = label;
							mpls_node["experimental"] = experimental;
							mpls_node["bottom_of_stack"] = bottom_of_stack;
							mpls_node["ttl"] = ttl;
							root["received"]["icmp"]["mpls_labels"].append(mpls_node);
						}
					}
				}
			}
		} catch (Tins::pdu_not_found) {
		}
	} else {
		root["received"] = nullvalue;
		root["rtt_usec"] = nullvalue;
	}

	// set the DNS name
	root["name"] = std::string(*name());

	try {
		root["nat_id"] = nat_id();
	} catch (DublinTracerouteException) {
	}

	return root;
}

<commit_msg>Added hex encoding for binary payloads in JSON<commit_after>/**
 * \file   hop.cc
 * \Author Andrea Barberio <insomniac@slackware.it>
 * \date   October 2015
 * \brief  Definition of the Hop class
 *
 * This file contains the definition of the Hop class, which represent every
 * single hop in a traceroute. A Hop includes the sent packet, the matching
 * received packet (if any), NAT information and last-hop information.
 *
 * This module currently offers the set-up of the logging facilities.
 *
 * \sa hop.h
 */

#include <memory>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netdb.h>
#include <sys/socket.h>

#include "dublintraceroute/hop.h"
#include "dublintraceroute/common.h"
#include "dublintraceroute/exceptions.h"
#include "dublintraceroute/icmp_messages.h"

/** \brief setter the sent packet
 */
void Hop::sent(IP &packet) {
	sent_ = std::make_shared<IP>(packet);
}

/** \brief setter for the timestamp of the sent packet
 */
void Hop::sent_timestamp(const Tins::Timestamp &timestamp) {
	sent_timestamp_ = std::make_shared<Timestamp>(timestamp);
}

/** \brief setter for the host name of the responding IP
 */
void Hop::name(std::string &name) {
	name_ = std::make_shared<std::string>(name);
}

std::string Hop::resolve() {
	if (!received())
		return std::string();

	struct sockaddr_in sa;
	memset(&sa, 0, sizeof(sa));
	sa.sin_family = AF_INET;
	sa.sin_port = htons(0);
	if (inet_pton(AF_INET, received()->src_addr().to_string().c_str(), &sa.sin_addr) != 1)
		throw (std::runtime_error("inet_pton failed"));
	char host[NI_MAXHOST], service[NI_MAXSERV];

	std::string name;
	if (getnameinfo((struct sockaddr *)&sa, sizeof(sa), host, sizeof(host), service, sizeof(service), NI_NUMERICSERV) == 0)
		name = std::string(host);
	else
		name = received()->src_addr().to_string();
	name_ = std::make_shared<std::string>(name);

	return name;
}

/** \brief setter for the received packet and its timestamp
 */
void Hop::received(IP &packet, const Tins::Timestamp &timestamp) {
	received_ = std::make_shared<IP>(packet);
	received_timestamp_ = std::make_shared<Timestamp>(timestamp);
}

/** \brief return the NAT ID of the hop
 *
 * This method returns the NAT identifier for this hop. The NAT identifier is
 * calculated as the difference between the checksum of the inner UDP layer of
 * the received packet and the checksum of the sent UDP packet.
 */
uint16_t Hop::nat_id() {

	if (!received()) {
		throw DublinTracerouteException(
			"Cannot get NAT ID for unmatched packets"
		);
	}
	uint16_t chk1 = sent_->rfind_pdu<UDP>().checksum();
	IP inner_ip = received_->rfind_pdu<RawPDU>().to<IP>();
	uint16_t chk2 = inner_ip.rfind_pdu<UDP>().checksum();
	return chk2 - chk1;
}

/** \brief return the RTT in microseconds
 *
 * This method returns the Round-Trip Time in microseconds, if a matching packet
 * was received, 0 otherwise.
 */
unsigned int Hop::rtt() {
	if (received()) {
		unsigned long long ts1 = sent_timestamp()->seconds() * 1000000 + sent_timestamp()->microseconds();
		unsigned long long ts2 = received_timestamp()->seconds() * 1000000 + received_timestamp()->microseconds();
		return ts2 - ts1;
	} else {
		return 0;
	}
}

/** \brief Convert the hop to JSON
 *
 * This method converts the hop data to a JSON representation. The
 * representation is lossy and cannot be used to rebuild the original packet.
 */
Json::Value Hop::to_json() {
	icmpmessages icmpm;
	Json::Value root;
	Json::Value nullvalue;

	// Serialize the sent packet
	root["is_last"] = is_last_hop();
	// IP layer
	root["sent"]["ip"]["src"] = sent()->src_addr().to_string();
	root["sent"]["ip"]["dst"] = sent()->dst_addr().to_string();
	root["sent"]["ip"]["ttl"] = sent()->ttl();

	// UDP layer
	try {
		auto udp = sent()->rfind_pdu<UDP>();
		root["sent"]["udp"]["sport"] = udp.sport();
		root["sent"]["udp"]["dport"] = udp.dport();
	} catch (Tins::pdu_not_found) {
	}

	// If present, serialize the received packet
	if (received()) {
		root["rtt_usec"] = rtt();

		// IP layer
		root["received"]["ip"]["src"] = received()->src_addr().to_string();
		root["received"]["ip"]["dst"] = received()->dst_addr().to_string();
		root["received"]["ip"]["ttl"] = received()->ttl();

		// ICMP layer
		try {
			auto icmp = received()->rfind_pdu<ICMP>();
			root["received"]["icmp"]["type"] = static_cast<int>(icmp.code());
			root["received"]["icmp"]["code"] = static_cast<int>(icmp.type());
			root["received"]["icmp"]["description"] = icmpm.get(icmp.type(), icmp.code());
			root["received"]["icmp"]["extensions"] = Json::Value(Json::arrayValue);
			root["received"]["icmp"]["mpls_labels"] = Json::Value(Json::arrayValue);
			if (icmp.has_extensions()) {
				for (auto &extension : icmp.extensions().extensions()) {
					Json::Value ext_node = Json::Value();
					unsigned int size = static_cast<unsigned int>(extension.size());
					unsigned int ext_class = static_cast<unsigned int>(extension.extension_class());
					unsigned int ext_type = static_cast<unsigned int>(extension.extension_type());
					auto &payload = extension.payload();
					// hex-encoding every byte so the JSON file doesn't contain binary sequences
					// I could have used base64 or other more efficient encoding, but this is simple and requires no deps
					std::stringstream payload_hex;
					for (auto &ch : payload) {
						payload_hex << std::setfill('0') << std::setw(2) << std::hex << static_cast<int>(ch);
					}
					ext_node["size"] = size;  // 16 bits
					ext_node["class"] = ext_class;  // 8 bits
					ext_node["type"] = ext_type;  // 8 bits
					ext_node["payload"] = payload_hex.str();
					root["received"]["icmp"]["extensions"].append(ext_node);

					// if MPLS was encountered, also add parsed extension
					if (ext_class == ICMP_EXTENSION_MPLS_CLASS && ext_type == ICMP_EXTENSION_MPLS_TYPE) {
						// FIXME here I am assuming that size is always a multiple of 4
						for (unsigned int idx = 0; idx < payload.size(); idx += 4) {
							unsigned int label = (payload[idx] << 12) + (payload[idx + 1] << 4) + (payload[idx + 2] >> 4);
							unsigned int experimental = (payload[idx + 2] & 0x0f) >> 1;
							unsigned int bottom_of_stack = payload[idx + 2] & 0x01;
							unsigned int ttl = payload[idx + 3];
							Json::Value mpls_node = Json::Value();
							mpls_node["label"] = label;
							mpls_node["experimental"] = experimental;
							mpls_node["bottom_of_stack"] = bottom_of_stack;
							mpls_node["ttl"] = ttl;
							root["received"]["icmp"]["mpls_labels"].append(mpls_node);
						}
					}
				}
			}
		} catch (Tins::pdu_not_found) {
		}
	} else {
		root["received"] = nullvalue;
		root["rtt_usec"] = nullvalue;
	}

	// set the DNS name
	root["name"] = std::string(*name());

	try {
		root["nat_id"] = nat_id();
	} catch (DublinTracerouteException) {
	}

	return root;
}

<|endoftext|>


<commit_before>#include "include/icm.h"

namespace ginf {
	template <typename T>
	void decodeIcmSync(Grid<T> *grid, Matrix<int> *initial, int numIters, Matrix<int> *result) {
		// Start with initial configuration
		result->copyFrom(initial);

		// Create another 2D matrix to store the labeling (synchronous updates)
		Matrix<int> oldLabels(2, grid->getWidth(), grid->getHeight());
		oldLabels.copyFrom(result);

		// For each iteration, set each node's label to one that minimizes the
		// sum of its data cost and its smoothness costs
		for (int t = 0; t < numIters; t++) {
			for (int y = 1; y < grid->getHeight() - 1; y++) {
				for (int x = 1; x < grid->getWidth() - 1; x++) {
					// Find the minimum state
					T minCost = grid->getLabelingCost(&oldLabels, x, y, 0);
					int minLabel = 0;

					for (int i = 1; i < grid->getNumLabels(); i++) {
						T cost = grid->getLabelingCost(&oldLabels, x, y, i);
						if (cost < minCost) {
							minLabel = i;
							minCost = cost;
						}
					}

					// Update this node's label
					result->get(x, y) = minLabel;
				}
			}

			// Copy labeling over to old labeling
			oldLabels.copyFrom(result);
		}
	}

	template <typename T>
	void decodeIcmAsync(Grid<T> *grid, Matrix<int> *initial, int numIters, Matrix<int> *result) {
		// Start with initial configuration
		result->copyFrom(initial);

		// For each iteration, set each node's label to one that minimizes the
		// sum of its data cost and its smoothness costs
		for (int t = 0; t < numIters; t++) {
			for (int y = 0; y < grid->getHeight(); y++) {
				for (int x = 0; x < grid->getWidth(); x++) {
					// Find the minimum state
					T minCost = grid->getLabelingCost(result, x, y, 0);
					int minLabel = 0;

					for (int i = 1; i < grid->getNumLabels(); i++) {
						T cost = grid->getLabelingCost(result, x, y, i);
						if (cost < minCost) {
							minLabel = i;
							minCost = cost;
						}
					}

					// Update this node's label
					result->get(x, y) = minLabel;
				}
			}
		}
	}

	// Instantiate templates
	template void decodeIcmSync<float>(Grid<float>*, Matrix<int>*, int, Matrix<int>*);
	template void decodeIcmSync<int>(Grid<int>*, Matrix<int>*, int, Matrix<int>*);

	template void decodeIcmAsync<float>(Grid<float>*, Matrix<int>*, int, Matrix<int>*);
	template void decodeIcmAsync<int>(Grid<int>*, Matrix<int>*, int, Matrix<int>*);
}
<commit_msg>Ignore boundary pixels<commit_after>#include "include/icm.h"

namespace ginf {
	template <typename T>
	void decodeIcmSync(Grid<T> *grid, Matrix<int> *initial, int numIters, Matrix<int> *result) {
		// Start with initial configuration
		result->copyFrom(initial);

		// Create another 2D matrix to store the labeling (synchronous updates)
		Matrix<int> oldLabels(2, grid->getWidth(), grid->getHeight());
		oldLabels.copyFrom(result);

		// For each iteration, set each node's label to one that minimizes the
		// sum of its data cost and its smoothness costs
		for (int t = 0; t < numIters; t++) {
			for (int y = 1; y < grid->getHeight() - 1; y++) {
				for (int x = 1; x < grid->getWidth() - 1; x++) {
					// Find the minimum state
					T minCost = grid->getLabelingCost(&oldLabels, x, y, 0);
					int minLabel = 0;

					for (int i = 1; i < grid->getNumLabels(); i++) {
						T cost = grid->getLabelingCost(&oldLabels, x, y, i);
						if (cost < minCost) {
							minLabel = i;
							minCost = cost;
						}
					}

					// Update this node's label
					result->get(x, y) = minLabel;
				}
			}

			// Copy labeling over to old labeling
			oldLabels.copyFrom(result);
		}
	}

	template <typename T>
	void decodeIcmAsync(Grid<T> *grid, Matrix<int> *initial, int numIters, Matrix<int> *result) {
		// Start with initial configuration
		result->copyFrom(initial);

		// For each iteration, set each node's label to one that minimizes the
		// sum of its data cost and its smoothness costs
		for (int t = 0; t < numIters; t++) {
			for (int y = 1; y < grid->getHeight() - 1; y++) {
				for (int x = 1; x < grid->getWidth() - 1; x++) {
					// Find the minimum state
					T minCost = grid->getLabelingCost(result, x, y, 0);
					int minLabel = 0;

					for (int i = 1; i < grid->getNumLabels(); i++) {
						T cost = grid->getLabelingCost(result, x, y, i);
						if (cost < minCost) {
							minLabel = i;
							minCost = cost;
						}
					}

					// Update this node's label
					result->get(x, y) = minLabel;
				}
			}
		}
	}

	// Instantiate templates
	template void decodeIcmSync<float>(Grid<float>*, Matrix<int>*, int, Matrix<int>*);
	template void decodeIcmSync<int>(Grid<int>*, Matrix<int>*, int, Matrix<int>*);

	template void decodeIcmAsync<float>(Grid<float>*, Matrix<int>*, int, Matrix<int>*);
	template void decodeIcmAsync<int>(Grid<int>*, Matrix<int>*, int, Matrix<int>*);
}
<|endoftext|>


<commit_before>#include "selectors.hh"

#include "string.hh"

#include <algorithm>

#include <boost/optional.hpp>

namespace Kakoune
{

Selection select_line(const Buffer& buffer, const Selection& selection)
{
    Utf8Iterator first = buffer.iterator_at(selection.last());
    if (*first == '\n' and first + 1 != buffer.end())
        ++first;

    while (first != buffer.begin() and *(first - 1) != '\n')
        --first;

    Utf8Iterator last = first;
    while (last + 1 != buffer.end() and *last != '\n')
        ++last;
    return utf8_range(first, last);
}

Selection select_matching(const Buffer& buffer, const Selection& selection)
{
    std::vector<Codepoint> matching_pairs = { '(', ')', '{', '}', '[', ']', '<', '>' };
    Utf8Iterator it = buffer.iterator_at(selection.last());
    std::vector<Codepoint>::iterator match = matching_pairs.end();
    while (not is_eol(*it))
    {
        match = std::find(matching_pairs.begin(), matching_pairs.end(), *it);
        if (match != matching_pairs.end())
            break;
        ++it;
    }
    if (match == matching_pairs.end())
        return selection;

    Utf8Iterator begin = it;

    if (((match - matching_pairs.begin()) % 2) == 0)
    {
        int level = 0;
        const Codepoint opening = *match;
        const Codepoint closing = *(match+1);
        while (it != buffer.end())
        {
            if (*it == opening)
                ++level;
            else if (*it == closing and --level == 0)
                return utf8_range(begin, it);
            ++it;
        }
    }
    else
    {
        int level = 0;
        const Codepoint opening = *(match-1);
        const Codepoint closing = *match;
        while (true)
        {
            if (*it == closing)
                ++level;
            else if (*it == opening and --level == 0)
                return utf8_range(begin, it);
            if (it == buffer.begin())
                break;
            --it;
        }
    }
    return selection;
}

// c++14 will add std::optional, so we use boost::optional until then
using boost::optional;
static optional<Range> find_surrounding(const Buffer& buffer,
                                        BufferCoord coord,
                                        CodepointPair matching,
                                        ObjectFlags flags, int init_level)
{
    const bool to_begin = flags & ObjectFlags::ToBegin;
    const bool to_end   = flags & ObjectFlags::ToEnd;
    const bool nestable = matching.first != matching.second;
    auto pos = buffer.iterator_at(coord);
    Utf8Iterator first = pos;
    if (to_begin)
    {
        int level = nestable ? init_level : 0;
        while (first != buffer.begin())
        {
            if (nestable and first != pos and *first == matching.second)
                ++level;
            else if (*first == matching.first)
            {
                if (level == 0)
                    break;
                else
                    --level;
            }
            --first;
        }
        if (level != 0 or *first != matching.first)
            return optional<Range>{};
    }

    Utf8Iterator last = pos;
    if (to_end)
    {
        int level = nestable ? init_level : 0;
        while (last != buffer.end())
        {
            if (nestable and last != pos and *last == matching.first)
                ++level;
            else if (*last == matching.second)
            {
                if (level == 0)
                    break;
                else
                    --level;
            }
            ++last;
        }
        if (level != 0 or last == buffer.end())
            return optional<Range>{};
    }

    if (flags & ObjectFlags::Inner)
    {
        if (to_begin and first != last)
            ++first;
        if (to_end and first != last)
            --last;
    }
    return to_end ? utf8_range(first, last) : utf8_range(last, first);
}

Selection select_surrounding(const Buffer& buffer, const Selection& selection,
                             CodepointPair matching, int level,
                             ObjectFlags flags)
{
	const bool nestable = matching.first != matching.second;
	auto pos = selection.last();
	if (not nestable or flags & ObjectFlags::Inner)
	{
        if (auto res = find_surrounding(buffer, pos, matching, flags, level))
            return *res;
        return selection;
	}

	auto c = buffer.byte_at(pos);
    if ((flags == ObjectFlags::ToBegin and c == matching.first) or
        (flags == ObjectFlags::ToEnd and c == matching.second))
        ++level;

    auto res = find_surrounding(buffer, pos, matching, flags, level);
    if (not res)
        return selection;

    if (flags == (ObjectFlags::ToBegin | ObjectFlags::ToEnd) and
        res->min() == selection.min() and res->max() == selection.max())
    {
        if (auto res_parent = find_surrounding(buffer, pos, matching, flags, level+1))
            return Selection{*res_parent};
    }
    return *res;
}

Selection select_to(const Buffer& buffer, const Selection& selection,
                    Codepoint c, int count, bool inclusive)
{
    Utf8Iterator begin = buffer.iterator_at(selection.last());
    Utf8Iterator end = begin;
    do
    {
        ++end;
        skip_while(end, buffer.end(), [c](Codepoint cur) { return cur != c; });
        if (end == buffer.end())
            return selection;
    }
    while (--count > 0);

    return utf8_range(begin, inclusive ? end : end-1);
}

Selection select_to_reverse(const Buffer& buffer, const Selection& selection,
                            Codepoint c, int count, bool inclusive)
{
    Utf8Iterator begin = buffer.iterator_at(selection.last());
    Utf8Iterator end = begin;
    do
    {
        --end;
        skip_while_reverse(end, buffer.begin(), [c](Codepoint cur) { return cur != c; });
        if (end == buffer.begin())
            return selection;
    }
    while (--count > 0);

    return utf8_range(begin, inclusive ? end : end+1);
}

Selection select_to_eol(const Buffer& buffer, const Selection& selection)
{
    Utf8Iterator begin = buffer.iterator_at(selection.last());
    Utf8Iterator end = begin;
    skip_while(end, buffer.end(), [](Codepoint cur) { return not is_eol(cur); });
    return utf8_range(begin, end != begin ? end-1 : end);
}

Selection select_to_eol_reverse(const Buffer& buffer, const Selection& selection)
{
    Utf8Iterator begin = buffer.iterator_at(selection.last());
    Utf8Iterator end = begin - 1;
    skip_while_reverse(end, buffer.begin(), [](Codepoint cur) { return not is_eol(cur); });
    return utf8_range(begin, end == buffer.begin() ? end : end+1);
}

Selection select_whole_sentence(const Buffer& buffer, const Selection& selection, ObjectFlags flags)
{
    BufferIterator first = buffer.iterator_at(selection.last());
    BufferIterator last = first;

    if (flags & ObjectFlags::ToBegin)
    {
        bool saw_non_blank = false;
        while (first != buffer.begin())
        {
            char cur = *first;
            char prev = *(first-1);
            if (not is_blank(cur))
                saw_non_blank = true;
            if (is_eol(prev) and is_eol(cur))
            {
                ++first;
                break;
            }
            else if (prev == '.' or prev == ';' or prev == '!' or prev == '?')
            {
                if (saw_non_blank)
                    break;
                else if (flags & ObjectFlags::ToEnd)
                    last = first-1;
            }
            --first;
        }
        skip_while(first, buffer.end(), is_blank);
    }
    if (flags & ObjectFlags::ToEnd)
    {
        while (last != buffer.end())
        {
            char cur = *last;
            if (cur == '.' or cur == ';' or cur == '!' or cur == '?' or
                (is_eol(cur) and (last+1 == buffer.end() or is_eol(*(last+1)))))
                break;
            ++last;
        }
        if (not (flags & ObjectFlags::Inner) and last != buffer.end())
        {
            ++last;
            skip_while(last, buffer.end(), is_blank);
            --last;
        }
    }
    return (flags & ObjectFlags::ToEnd) ? Selection{first.coord(), last.coord()}
                                        : Selection{last.coord(), first.coord()};
}

Selection select_whole_paragraph(const Buffer& buffer, const Selection& selection, ObjectFlags flags)
{
    BufferIterator first = buffer.iterator_at(selection.last());
    BufferIterator last = first;

    if (flags & ObjectFlags::ToBegin and first != buffer.begin())
    {
        skip_while_reverse(first, buffer.begin(), is_eol);
        if (flags & ObjectFlags::ToEnd)
            last = first;
        while (first != buffer.begin())
        {
            char cur = *first;
            char prev = *(first-1);
            if (is_eol(prev) and is_eol(cur))
            {
                ++first;
                break;
            }
            --first;
        }
    }
    if (flags & ObjectFlags::ToEnd)
    {
        while (last != buffer.end())
        {
            char cur = *last;
            char prev = *(last-1);
            if (is_eol(cur) and is_eol(prev))
            {
                if (not (flags & ObjectFlags::Inner))
                    skip_while(last, buffer.end(), is_eol);
                break;
            }
            ++last;
        }
        --last;
    }
    return (flags & ObjectFlags::ToEnd) ? Selection{first.coord(), last.coord()}
                                        : Selection{last.coord(), first.coord()};
}

static CharCount get_indent(const String& str, int tabstop)
{
    CharCount indent = 0;
    for (auto& c : str)
    {
    if (c == ' ')
        ++indent;
    else if (c =='\t')
        indent = (indent / tabstop + 1) * tabstop;
    else
        break;
    }
    return indent;
}

Selection select_whole_indent(const Buffer& buffer, const Selection& selection, ObjectFlags flags)
{
    int tabstop = buffer.options()["tabstop"].get<int>();
    LineCount line = selection.last().line;
    auto indent = get_indent(buffer[line], tabstop);

    LineCount begin_line = line - 1;
    if (flags & ObjectFlags::ToBegin)
    {
        while (begin_line >= 0 and (buffer[begin_line] == "\n" or get_indent(buffer[begin_line], tabstop) >= indent))
            --begin_line;
    }
    ++begin_line;
    LineCount end_line = line + 1;
    if (flags & ObjectFlags::ToEnd)
    {
        LineCount end = buffer.line_count();
        while (end_line < end and (buffer[end_line] == "\n" or get_indent(buffer[end_line], tabstop) >= indent))
            ++end_line;
    }
    --end_line;
    BufferCoord first = begin_line;
    // keep the first line indent in inner mode
    if (flags & ObjectFlags::Inner)
    {
        CharCount i = 0;
        for (; i < indent; ++first.column)
        {
            auto c = buffer.byte_at(first);
            if (c == ' ')
                ++i;
            if (c == '\t')
                i = (i / tabstop + 1) * tabstop;
        }
    }
    return Selection{first, {end_line, buffer[end_line].length() - 1}};
}

Selection select_whole_lines(const Buffer& buffer, const Selection& selection)
{
    // no need to be utf8 aware for is_eol as we only use \n as line seperator
    BufferIterator first = buffer.iterator_at(selection.first());
    BufferIterator last  = buffer.iterator_at(selection.last());
    BufferIterator& to_line_start = first <= last ? first : last;
    BufferIterator& to_line_end = first <= last ? last : first;

    --to_line_start;
    skip_while_reverse(to_line_start, buffer.begin(), [](char cur) { return not is_eol(cur); });
    if (is_eol(*to_line_start))
        ++to_line_start;

    skip_while(to_line_end, buffer.end(), [](char cur) { return not is_eol(cur); });
    if (to_line_end == buffer.end())
        --to_line_end;

    return Selection(first.coord(), last.coord());
}

Selection trim_partial_lines(const Buffer& buffer, const Selection& selection)
{
    // same as select_whole_lines
    BufferIterator first = buffer.iterator_at(selection.first());
    BufferIterator last =  buffer.iterator_at(selection.last());
    BufferIterator& to_line_start = first <= last ? first : last;
    BufferIterator& to_line_end = first <= last ? last : first;

    while (to_line_start != buffer.begin() and *(to_line_start-1) != '\n')
        ++to_line_start;
    while (*(to_line_end+1) != '\n' and to_line_end != to_line_start)
        --to_line_end;

    return Selection(first.coord(), last.coord());
}

void select_whole_buffer(const Buffer& buffer, SelectionList& selections)
{
    selections = SelectionList{ Selection({0,0}, buffer.back_coord()) };
}

void select_all_matches(const Buffer& buffer, SelectionList& selections,
                        const Regex& regex)
{
    SelectionList result;
    for (auto& sel : selections)
    {
        auto sel_end = utf8::next(buffer.iterator_at(sel.max()));
        RegexIterator re_it(buffer.iterator_at(sel.min()), sel_end, regex);
        RegexIterator re_end;

        for (; re_it != re_end; ++re_it)
        {
            auto& begin = (*re_it)[0].first;
            auto& end   = (*re_it)[0].second;

            if (begin == sel_end)
                continue;

            CaptureList captures;
            for (auto& match : *re_it)
                captures.emplace_back(match.first, match.second);

            result.emplace_back(begin.coord(),
                                (begin == end ? end : utf8::previous(end)).coord(),
                                std::move(captures));
        }
    }
    if (result.empty())
        throw runtime_error("nothing selected");
    result.set_main_index(result.size() - 1);
    selections = std::move(result);
}

void split_selections(const Buffer& buffer, SelectionList& selections,
                      const Regex& regex)
{
    SelectionList result;
    for (auto& sel : selections)
    {
        auto begin = buffer.iterator_at(sel.min());
        auto sel_end = utf8::next(buffer.iterator_at(sel.max()));
        RegexIterator re_it(begin, sel_end, regex,
                            boost::regex_constants::match_nosubs);
        RegexIterator re_end;

        for (; re_it != re_end; ++re_it)
        {
            BufferIterator end = (*re_it)[0].first;

            result.emplace_back(begin.coord(), (begin == end) ? end.coord() : utf8::previous(end).coord());
            begin = (*re_it)[0].second;
        }
        result.emplace_back(begin.coord(), sel.max());
    }
    result.set_main_index(result.size() - 1);
    selections = std::move(result);
}

}
<commit_msg>Fix small misbehaviour in split_selections<commit_after>#include "selectors.hh"

#include "string.hh"

#include <algorithm>

#include <boost/optional.hpp>

namespace Kakoune
{

Selection select_line(const Buffer& buffer, const Selection& selection)
{
    Utf8Iterator first = buffer.iterator_at(selection.last());
    if (*first == '\n' and first + 1 != buffer.end())
        ++first;

    while (first != buffer.begin() and *(first - 1) != '\n')
        --first;

    Utf8Iterator last = first;
    while (last + 1 != buffer.end() and *last != '\n')
        ++last;
    return utf8_range(first, last);
}

Selection select_matching(const Buffer& buffer, const Selection& selection)
{
    std::vector<Codepoint> matching_pairs = { '(', ')', '{', '}', '[', ']', '<', '>' };
    Utf8Iterator it = buffer.iterator_at(selection.last());
    std::vector<Codepoint>::iterator match = matching_pairs.end();
    while (not is_eol(*it))
    {
        match = std::find(matching_pairs.begin(), matching_pairs.end(), *it);
        if (match != matching_pairs.end())
            break;
        ++it;
    }
    if (match == matching_pairs.end())
        return selection;

    Utf8Iterator begin = it;

    if (((match - matching_pairs.begin()) % 2) == 0)
    {
        int level = 0;
        const Codepoint opening = *match;
        const Codepoint closing = *(match+1);
        while (it != buffer.end())
        {
            if (*it == opening)
                ++level;
            else if (*it == closing and --level == 0)
                return utf8_range(begin, it);
            ++it;
        }
    }
    else
    {
        int level = 0;
        const Codepoint opening = *(match-1);
        const Codepoint closing = *match;
        while (true)
        {
            if (*it == closing)
                ++level;
            else if (*it == opening and --level == 0)
                return utf8_range(begin, it);
            if (it == buffer.begin())
                break;
            --it;
        }
    }
    return selection;
}

// c++14 will add std::optional, so we use boost::optional until then
using boost::optional;
static optional<Range> find_surrounding(const Buffer& buffer,
                                        BufferCoord coord,
                                        CodepointPair matching,
                                        ObjectFlags flags, int init_level)
{
    const bool to_begin = flags & ObjectFlags::ToBegin;
    const bool to_end   = flags & ObjectFlags::ToEnd;
    const bool nestable = matching.first != matching.second;
    auto pos = buffer.iterator_at(coord);
    Utf8Iterator first = pos;
    if (to_begin)
    {
        int level = nestable ? init_level : 0;
        while (first != buffer.begin())
        {
            if (nestable and first != pos and *first == matching.second)
                ++level;
            else if (*first == matching.first)
            {
                if (level == 0)
                    break;
                else
                    --level;
            }
            --first;
        }
        if (level != 0 or *first != matching.first)
            return optional<Range>{};
    }

    Utf8Iterator last = pos;
    if (to_end)
    {
        int level = nestable ? init_level : 0;
        while (last != buffer.end())
        {
            if (nestable and last != pos and *last == matching.first)
                ++level;
            else if (*last == matching.second)
            {
                if (level == 0)
                    break;
                else
                    --level;
            }
            ++last;
        }
        if (level != 0 or last == buffer.end())
            return optional<Range>{};
    }

    if (flags & ObjectFlags::Inner)
    {
        if (to_begin and first != last)
            ++first;
        if (to_end and first != last)
            --last;
    }
    return to_end ? utf8_range(first, last) : utf8_range(last, first);
}

Selection select_surrounding(const Buffer& buffer, const Selection& selection,
                             CodepointPair matching, int level,
                             ObjectFlags flags)
{
	const bool nestable = matching.first != matching.second;
	auto pos = selection.last();
	if (not nestable or flags & ObjectFlags::Inner)
	{
        if (auto res = find_surrounding(buffer, pos, matching, flags, level))
            return *res;
        return selection;
	}

	auto c = buffer.byte_at(pos);
    if ((flags == ObjectFlags::ToBegin and c == matching.first) or
        (flags == ObjectFlags::ToEnd and c == matching.second))
        ++level;

    auto res = find_surrounding(buffer, pos, matching, flags, level);
    if (not res)
        return selection;

    if (flags == (ObjectFlags::ToBegin | ObjectFlags::ToEnd) and
        res->min() == selection.min() and res->max() == selection.max())
    {
        if (auto res_parent = find_surrounding(buffer, pos, matching, flags, level+1))
            return Selection{*res_parent};
    }
    return *res;
}

Selection select_to(const Buffer& buffer, const Selection& selection,
                    Codepoint c, int count, bool inclusive)
{
    Utf8Iterator begin = buffer.iterator_at(selection.last());
    Utf8Iterator end = begin;
    do
    {
        ++end;
        skip_while(end, buffer.end(), [c](Codepoint cur) { return cur != c; });
        if (end == buffer.end())
            return selection;
    }
    while (--count > 0);

    return utf8_range(begin, inclusive ? end : end-1);
}

Selection select_to_reverse(const Buffer& buffer, const Selection& selection,
                            Codepoint c, int count, bool inclusive)
{
    Utf8Iterator begin = buffer.iterator_at(selection.last());
    Utf8Iterator end = begin;
    do
    {
        --end;
        skip_while_reverse(end, buffer.begin(), [c](Codepoint cur) { return cur != c; });
        if (end == buffer.begin())
            return selection;
    }
    while (--count > 0);

    return utf8_range(begin, inclusive ? end : end+1);
}

Selection select_to_eol(const Buffer& buffer, const Selection& selection)
{
    Utf8Iterator begin = buffer.iterator_at(selection.last());
    Utf8Iterator end = begin;
    skip_while(end, buffer.end(), [](Codepoint cur) { return not is_eol(cur); });
    return utf8_range(begin, end != begin ? end-1 : end);
}

Selection select_to_eol_reverse(const Buffer& buffer, const Selection& selection)
{
    Utf8Iterator begin = buffer.iterator_at(selection.last());
    Utf8Iterator end = begin - 1;
    skip_while_reverse(end, buffer.begin(), [](Codepoint cur) { return not is_eol(cur); });
    return utf8_range(begin, end == buffer.begin() ? end : end+1);
}

Selection select_whole_sentence(const Buffer& buffer, const Selection& selection, ObjectFlags flags)
{
    BufferIterator first = buffer.iterator_at(selection.last());
    BufferIterator last = first;

    if (flags & ObjectFlags::ToBegin)
    {
        bool saw_non_blank = false;
        while (first != buffer.begin())
        {
            char cur = *first;
            char prev = *(first-1);
            if (not is_blank(cur))
                saw_non_blank = true;
            if (is_eol(prev) and is_eol(cur))
            {
                ++first;
                break;
            }
            else if (prev == '.' or prev == ';' or prev == '!' or prev == '?')
            {
                if (saw_non_blank)
                    break;
                else if (flags & ObjectFlags::ToEnd)
                    last = first-1;
            }
            --first;
        }
        skip_while(first, buffer.end(), is_blank);
    }
    if (flags & ObjectFlags::ToEnd)
    {
        while (last != buffer.end())
        {
            char cur = *last;
            if (cur == '.' or cur == ';' or cur == '!' or cur == '?' or
                (is_eol(cur) and (last+1 == buffer.end() or is_eol(*(last+1)))))
                break;
            ++last;
        }
        if (not (flags & ObjectFlags::Inner) and last != buffer.end())
        {
            ++last;
            skip_while(last, buffer.end(), is_blank);
            --last;
        }
    }
    return (flags & ObjectFlags::ToEnd) ? Selection{first.coord(), last.coord()}
                                        : Selection{last.coord(), first.coord()};
}

Selection select_whole_paragraph(const Buffer& buffer, const Selection& selection, ObjectFlags flags)
{
    BufferIterator first = buffer.iterator_at(selection.last());
    BufferIterator last = first;

    if (flags & ObjectFlags::ToBegin and first != buffer.begin())
    {
        skip_while_reverse(first, buffer.begin(), is_eol);
        if (flags & ObjectFlags::ToEnd)
            last = first;
        while (first != buffer.begin())
        {
            char cur = *first;
            char prev = *(first-1);
            if (is_eol(prev) and is_eol(cur))
            {
                ++first;
                break;
            }
            --first;
        }
    }
    if (flags & ObjectFlags::ToEnd)
    {
        while (last != buffer.end())
        {
            char cur = *last;
            char prev = *(last-1);
            if (is_eol(cur) and is_eol(prev))
            {
                if (not (flags & ObjectFlags::Inner))
                    skip_while(last, buffer.end(), is_eol);
                break;
            }
            ++last;
        }
        --last;
    }
    return (flags & ObjectFlags::ToEnd) ? Selection{first.coord(), last.coord()}
                                        : Selection{last.coord(), first.coord()};
}

static CharCount get_indent(const String& str, int tabstop)
{
    CharCount indent = 0;
    for (auto& c : str)
    {
    if (c == ' ')
        ++indent;
    else if (c =='\t')
        indent = (indent / tabstop + 1) * tabstop;
    else
        break;
    }
    return indent;
}

Selection select_whole_indent(const Buffer& buffer, const Selection& selection, ObjectFlags flags)
{
    int tabstop = buffer.options()["tabstop"].get<int>();
    LineCount line = selection.last().line;
    auto indent = get_indent(buffer[line], tabstop);

    LineCount begin_line = line - 1;
    if (flags & ObjectFlags::ToBegin)
    {
        while (begin_line >= 0 and (buffer[begin_line] == "\n" or get_indent(buffer[begin_line], tabstop) >= indent))
            --begin_line;
    }
    ++begin_line;
    LineCount end_line = line + 1;
    if (flags & ObjectFlags::ToEnd)
    {
        LineCount end = buffer.line_count();
        while (end_line < end and (buffer[end_line] == "\n" or get_indent(buffer[end_line], tabstop) >= indent))
            ++end_line;
    }
    --end_line;
    BufferCoord first = begin_line;
    // keep the first line indent in inner mode
    if (flags & ObjectFlags::Inner)
    {
        CharCount i = 0;
        for (; i < indent; ++first.column)
        {
            auto c = buffer.byte_at(first);
            if (c == ' ')
                ++i;
            if (c == '\t')
                i = (i / tabstop + 1) * tabstop;
        }
    }
    return Selection{first, {end_line, buffer[end_line].length() - 1}};
}

Selection select_whole_lines(const Buffer& buffer, const Selection& selection)
{
    // no need to be utf8 aware for is_eol as we only use \n as line seperator
    BufferIterator first = buffer.iterator_at(selection.first());
    BufferIterator last  = buffer.iterator_at(selection.last());
    BufferIterator& to_line_start = first <= last ? first : last;
    BufferIterator& to_line_end = first <= last ? last : first;

    --to_line_start;
    skip_while_reverse(to_line_start, buffer.begin(), [](char cur) { return not is_eol(cur); });
    if (is_eol(*to_line_start))
        ++to_line_start;

    skip_while(to_line_end, buffer.end(), [](char cur) { return not is_eol(cur); });
    if (to_line_end == buffer.end())
        --to_line_end;

    return Selection(first.coord(), last.coord());
}

Selection trim_partial_lines(const Buffer& buffer, const Selection& selection)
{
    // same as select_whole_lines
    BufferIterator first = buffer.iterator_at(selection.first());
    BufferIterator last =  buffer.iterator_at(selection.last());
    BufferIterator& to_line_start = first <= last ? first : last;
    BufferIterator& to_line_end = first <= last ? last : first;

    while (to_line_start != buffer.begin() and *(to_line_start-1) != '\n')
        ++to_line_start;
    while (*(to_line_end+1) != '\n' and to_line_end != to_line_start)
        --to_line_end;

    return Selection(first.coord(), last.coord());
}

void select_whole_buffer(const Buffer& buffer, SelectionList& selections)
{
    selections = SelectionList{ Selection({0,0}, buffer.back_coord()) };
}

void select_all_matches(const Buffer& buffer, SelectionList& selections,
                        const Regex& regex)
{
    SelectionList result;
    for (auto& sel : selections)
    {
        auto sel_end = utf8::next(buffer.iterator_at(sel.max()));
        RegexIterator re_it(buffer.iterator_at(sel.min()), sel_end, regex);
        RegexIterator re_end;

        for (; re_it != re_end; ++re_it)
        {
            auto& begin = (*re_it)[0].first;
            auto& end   = (*re_it)[0].second;

            if (begin == sel_end)
                continue;

            CaptureList captures;
            for (auto& match : *re_it)
                captures.emplace_back(match.first, match.second);

            result.emplace_back(begin.coord(),
                                (begin == end ? end : utf8::previous(end)).coord(),
                                std::move(captures));
        }
    }
    if (result.empty())
        throw runtime_error("nothing selected");
    result.set_main_index(result.size() - 1);
    selections = std::move(result);
}

void split_selections(const Buffer& buffer, SelectionList& selections,
                      const Regex& regex)
{
    SelectionList result;
    for (auto& sel : selections)
    {
        auto begin = buffer.iterator_at(sel.min());
        auto sel_end = utf8::next(buffer.iterator_at(sel.max()));
        RegexIterator re_it(begin, sel_end, regex,
                            boost::regex_constants::match_nosubs);
        RegexIterator re_end;

        for (; re_it != re_end; ++re_it)
        {
            BufferIterator end = (*re_it)[0].first;

            result.emplace_back(begin.coord(), (begin == end) ? end.coord() : utf8::previous(end).coord());
            begin = (*re_it)[0].second;
        }
        if (begin.coord() <= sel.max())
            result.emplace_back(begin.coord(), sel.max());
    }
    result.set_main_index(result.size() - 1);
    selections = std::move(result);
}

}
<|endoftext|>