<commit_before>#define WIN32_LEAN_AND_MEAN

#include <iostream>
#include <map>
#include <memory>
#include <sstream>
#include <string>
#include <utility>
#include <uv.h>
#include <vector>
#include <windows.h>

#include "../../helper/windows/helper.h"
#include "../../lock.h"
#include "../../log.h"
#include "../../message.h"
#include "../../message_buffer.h"
#include "../recent_file_cache.h"
#include "../worker_platform.h"
#include "../worker_thread.h"
#include "subscription.h"

using std::default_delete;
using std::endl;
using std::make_pair;
using std::map;
using std::move;
using std::ostream;
using std::ostringstream;
using std::pair;
using std::shared_ptr;
using std::string;
using std::unique_ptr;
using std::vector;
using std::wostringstream;
using std::wstring;

const size_t DEFAULT_CACHE_SIZE = 4096;

const size_t DEFAULT_CACHE_PREPOPULATION = 1024;

void CALLBACK command_perform_helper(__in ULONG_PTR payload);

void CALLBACK event_helper(DWORD error_code, DWORD num_bytes, LPOVERLAPPED overlapped);

class WindowsWorkerPlatform : public WorkerPlatform
{
public:
  WindowsWorkerPlatform(WorkerThread *thread) : WorkerPlatform(thread), thread_handle{0}, cache{DEFAULT_CACHE_SIZE}
  {
    int err;

    err = uv_mutex_init(&thread_handle_mutex);
    if (err) {
      report_uv_error(err);
    }
    freeze();
  };

  ~WindowsWorkerPlatform() override { uv_mutex_destroy(&thread_handle_mutex); }

  Result<> wake() override
  {
    Lock lock(thread_handle_mutex);

    if (!thread_handle) {
      return ok_result();
    }

    BOOL success = QueueUserAPC(command_perform_helper, thread_handle, reinterpret_cast<ULONG_PTR>(this));
    if (!success) {
      return windows_error_result<>("Unable to queue APC");
    }

    return ok_result();
  }

  Result<> listen() override
  {
    {
      Lock lock(thread_handle_mutex);

      HANDLE pseudo_handle = GetCurrentThread();
      BOOL success = DuplicateHandle(GetCurrentProcess(),  // Source process
        pseudo_handle,  // Source handle
        GetCurrentProcess(),  // Destination process
        &thread_handle,  // Destination handle
        0,  // Desired access
        FALSE,  // Inheritable by new processes
        DUPLICATE_SAME_ACCESS  // options
      );
      if (!success) {
        return windows_error_result<>("Unable to duplicate thread handle");
      }
    }

    while (true) {
      SleepEx(INFINITE, true);
    }

    return error_result("listen loop ended unexpectedly");
  }

  Result<bool> handle_add_command(CommandID command,
    ChannelID channel,
    const string &root_path,
    bool recursive) override
  {
    // Convert the path to a wide-character string
    Result<wstring> convr = to_wchar(root_path);
    if (convr.is_error()) return convr.propagate<bool>();
    wstring &root_path_w = convr.get_value();

    // Open a directory handle
    HANDLE root = CreateFileW(root_path_w.c_str(),  // null-terminated wchar file name
      FILE_LIST_DIRECTORY,  // desired access
      FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,  // share mode
      NULL,  // security attributes
      OPEN_EXISTING,  // creation disposition
      FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OVERLAPPED,  // flags and attributes
      NULL  // template file
    );
    if (root == INVALID_HANDLE_VALUE) {
      ostringstream msg;
      msg << "Unable to open directory handle " << root_path;
      return windows_error_result<bool>(msg.str());
    }

    // Allocate and persist the subscription
    Subscription *sub = new Subscription(channel, root, root_path_w, recursive, this);
    auto insert_result = subscriptions.insert(make_pair(channel, sub));
    if (!insert_result.second) {
      delete sub;

      ostringstream msg("Channel collision: ");
      msg << channel;
      return Result<bool>::make_error(msg.str());
    }

    ostream &logline = LOGGER << "Added directory root " << root_path;
    if (!recursive) logline << " (non-recursive)";
    logline << " at channel " << channel << "." << endl;

    Result<bool> schedr = sub->schedule(&event_helper);
    if (schedr.is_error()) return schedr.propagate<bool>();
    if (!schedr.get_value()) {
      LOGGER << "Falling back to polling for watch root " << root_path << "." << endl;

      return emit(Message(CommandPayloadBuilder::add(channel, string(root_path), recursive, 1).build()))
        .propagate(false);
    }

    cache.prepopulate(root_path, DEFAULT_CACHE_PREPOPULATION, recursive);

    return ok_result(true);
  }

  Result<bool> handle_remove_command(CommandID command, ChannelID channel) override
  {
    auto it = subscriptions.find(channel);
    if (it == subscriptions.end()) {
      LOGGER << "Channel " << channel << " was already removed." << endl;
      return ok_result(true);
    }

    Result<> r = it->second->stop(command);
    if (r.is_error()) return r.propagate<bool>();

    LOGGER << "Subscription for channel " << channel << " stopped." << endl;
    return ok_result(false);
  }

  void handle_cache_size_command(size_t cache_size) override
  {
    LOGGER << "Changing cache size to " << cache_size << "." << endl;
    cache.resize(cache_size);
  }

  Result<> handle_fs_event(DWORD error_code, DWORD num_bytes, Subscription *sub)
  {
    Timer t;

    // Ensure that the subscription is valid.
    ChannelID channel = sub->get_channel();
    auto it = subscriptions.find(channel);
    if (it == subscriptions.end() || it->second != sub) {
      return ok_result();
    }

    // Subscription termination.
    bool terminate = false;
    if (error_code == ERROR_OPERATION_ABORTED) {
      LOGGER << "Completing termination of channel " << channel << "." << endl;
      terminate = true;
    } else if (sub->is_terminating()) {
      LOGGER << "Filesystem event encountered on terminating channel " << channel << "." << endl;
      terminate = true;
    }
    if (terminate) return remove(sub);

    // Handle errors.
    if (error_code == ERROR_INVALID_PARAMETER) {
      LOGGER << "Attempting to revert to a network-friendly buffer size." << endl;

      Result<> resize = sub->use_network_size();
      if (resize.is_error()) return emit_fatal_error(sub, move(resize));

      return reschedule(sub);
    }

    if (error_code == ERROR_NOTIFY_ENUM_DIR) {
      LOGGER << "Change buffer overflow. Some events may have been lost." << endl;
      return reschedule(sub);
    }

    if (error_code != ERROR_SUCCESS) {
      return emit_fatal_error(sub, windows_error_result<>("Completion callback error", error_code));
    }

    // Schedule the next completion callback.
    BYTE *base = sub->get_written(num_bytes);
    Result<> next = reschedule(sub);

    // Process received events.
    MessageBuffer buffer;
    ChannelMessageBuffer messages(buffer, channel);
    size_t num_events = 0;

    while (true) {
      PFILE_NOTIFY_INFORMATION info = reinterpret_cast<PFILE_NOTIFY_INFORMATION>(base);
      num_events++;

      Result<> pr = process_event_payload(info, sub, messages);
      if (pr.is_error()) {
        LOGGER << "Skipping entry " << pr << "." << endl;
      }

      if (info->NextEntryOffset == 0) {
        break;
      }
      base += info->NextEntryOffset;
    }

    cache.apply();
    cache.prune();

    if (!messages.empty()) {
      Result<> er = emit_all(messages.begin(), messages.end());
      if (er.is_error()) {
        LOGGER << "Unable to emit messages: " << er << "." << endl;
      } else {
        t.stop();
        LOGGER << "Filesystem event batch of size " << num_events << " completed in " << t << ". "
               << plural(messages.size(), "message") << " produced." << endl;
      }
    }

    return next.propagate_as_void();
  }

private:
  Result<> reschedule(Subscription *sub)
  {
    Result<bool> sch = sub->schedule(&event_helper);
    if (sch.is_error()) return emit_fatal_error(sub, sch.propagate_as_void());

    if (!sch.get_value()) {
      Result<string> root = sub->get_root_path();
      if (root.is_error()) return emit_fatal_error(sub, root.propagate_as_void());

      LOGGER << "Falling back to polling for path " << root.get_value() << " at channel " << sub->get_channel() << "."
             << endl;

      Result<> rem = remove(sub);
      rem &= emit(Message(
        CommandPayloadBuilder::add(sub->get_channel(), move(root.get_value()), sub->is_recursive(), 1).build()));
      return rem;
    }

    return ok_result();
  }

  Result<> remove(Subscription *sub)
  {
    Message response(AckPayload(sub->get_command_id(), sub->get_channel(), true, ""));

    // Ensure that the subscription is valid.
    ChannelID channel = sub->get_channel();
    auto it = subscriptions.find(channel);
    if (it == subscriptions.end() || it->second != sub) {
      return ok_result();
    }

    subscriptions.erase(it);
    delete sub;

    if (sub->get_command_id() != NULL_COMMAND_ID) {
      return emit(move(response));
    } else {
      return ok_result();
    }
  }

  Result<> emit_fatal_error(Subscription *sub, Result<> &&r)
  {
    assert(r.is_error());

    Result<> out = emit(Message(ErrorPayload(sub->get_channel(), string(r.get_error()), true)));
    out &= remove(sub);
    return out;
  }

  Result<> process_event_payload(PFILE_NOTIFY_INFORMATION info, Subscription *sub, ChannelMessageBuffer &messages)
  {
    ChannelID channel = sub->get_channel();
    wstring relpathw{info->FileName, info->FileNameLength / sizeof(WCHAR)};
    wstring shortpathw = sub->make_absolute(move(relpathw));
    Result<wstring> longpathr = to_long_path(shortpathw);
    if (longpathr.is_error()) return longpathr.propagate_as_void();
    wstring &pathw = longpathr.get_value();

    Result<string> u8r = to_utf8(pathw);
    if (u8r.is_error()) {
      LOGGER << "Unable to convert path to utf-8: " << u8r << "." << endl;
      return ok_result();
    }
    string &path = u8r.get_value();

    shared_ptr<StatResult> stat;
    if (info->Action == FILE_ACTION_REMOVED || info->Action == FILE_ACTION_RENAMED_OLD_NAME) {
      stat = cache.former_at_path(path, false, false);
    } else {
      stat = cache.current_at_path(path, false, false);
      if (stat->is_absent()) {
        stat = cache.former_at_path(path, false, false);
      }
    }
    EntryKind kind = stat->get_entry_kind();

    ostream &logline = LOGGER;
    logline << "Event at [" << path << "] ";

    switch (info->Action) {
      case FILE_ACTION_ADDED:
        logline << "FILE_ACTION_ADDED " << kind << "." << endl;
        messages.created(move(path), kind);
        break;
      case FILE_ACTION_MODIFIED:
        logline << "FILE_ACTION_MODIFIED " << kind;
        if (kind != KIND_DIRECTORY) {
          logline << "." << endl;
          messages.modified(move(path), kind);
        } else {
          logline << " (ignored)." << endl;
        }
        break;
      case FILE_ACTION_REMOVED:
        logline << "FILE_ACTION_REMOVED " << kind << "." << endl;
        messages.deleted(move(path), kind);
        break;
      case FILE_ACTION_RENAMED_OLD_NAME:
        logline << "FILE_ACTION_RENAMED_OLD_NAME " << kind << "." << endl;
        sub->remember_old_path(move(path), kind);
        break;
      case FILE_ACTION_RENAMED_NEW_NAME:
        logline << "FILE_ACTION_RENAMED_NEW_NAME ";
        if (sub->was_old_path_seen()) {
          // Old name received first
          if (kind == KIND_UNKNOWN) {
            kind = sub->get_old_path_kind();
          }

          logline << kind << "." << endl;
          cache.update_for_rename(sub->get_old_path(), path);
          messages.renamed(string(sub->get_old_path()), move(path), kind);
          sub->clear_old_path();
        } else {
          // No old name. Treat it as a creation
          logline << "(unpaired) " << kind << "." << endl;
          messages.created(move(path), kind);
        }
        break;
      default:
        logline << " with unexpected action " << info->Action << "." << endl;

        ostringstream out;
        out << "Unexpected action " << info->Action << " reported by ReadDirectoryChangesW for " << path;
        return error_result(out.str());
        break;
    }

    return ok_result();
  }

  uv_mutex_t thread_handle_mutex;
  HANDLE thread_handle;

  map<ChannelID, Subscription *> subscriptions;

  RecentFileCache cache;
};

unique_ptr<WorkerPlatform> WorkerPlatform::for_worker(WorkerThread *thread)
{
  return unique_ptr<WorkerPlatform>(new WindowsWorkerPlatform(thread));
}

void CALLBACK command_perform_helper(__in ULONG_PTR payload)
{
  WindowsWorkerPlatform *platform = reinterpret_cast<WindowsWorkerPlatform *>(payload);
  platform->handle_commands();
}

void CALLBACK event_helper(DWORD error_code, DWORD num_bytes, LPOVERLAPPED overlapped)
{
  Subscription *sub = static_cast<Subscription *>(overlapped->hEvent);
  Result<> r = sub->get_platform()->handle_fs_event(error_code, num_bytes, sub);
  if (r.is_error()) {
    LOGGER << "Unable to handle filesystem events: " << r << "." << endl;
  }
}
<commit_msg>Update calls to RecentFileCache to provide the additional hint parameter<commit_after>#define WIN32_LEAN_AND_MEAN

#include <iostream>
#include <map>
#include <memory>
#include <sstream>
#include <string>
#include <utility>
#include <uv.h>
#include <vector>
#include <windows.h>

#include "../../helper/windows/helper.h"
#include "../../lock.h"
#include "../../log.h"
#include "../../message.h"
#include "../../message_buffer.h"
#include "../recent_file_cache.h"
#include "../worker_platform.h"
#include "../worker_thread.h"
#include "subscription.h"

using std::default_delete;
using std::endl;
using std::make_pair;
using std::map;
using std::move;
using std::ostream;
using std::ostringstream;
using std::pair;
using std::shared_ptr;
using std::string;
using std::unique_ptr;
using std::vector;
using std::wostringstream;
using std::wstring;

const size_t DEFAULT_CACHE_SIZE = 4096;

const size_t DEFAULT_CACHE_PREPOPULATION = 1024;

void CALLBACK command_perform_helper(__in ULONG_PTR payload);

void CALLBACK event_helper(DWORD error_code, DWORD num_bytes, LPOVERLAPPED overlapped);

class WindowsWorkerPlatform : public WorkerPlatform
{
public:
  WindowsWorkerPlatform(WorkerThread *thread) : WorkerPlatform(thread), thread_handle{0}, cache{DEFAULT_CACHE_SIZE}
  {
    int err;

    err = uv_mutex_init(&thread_handle_mutex);
    if (err) {
      report_uv_error(err);
    }
    freeze();
  };

  ~WindowsWorkerPlatform() override { uv_mutex_destroy(&thread_handle_mutex); }

  Result<> wake() override
  {
    Lock lock(thread_handle_mutex);

    if (!thread_handle) {
      return ok_result();
    }

    BOOL success = QueueUserAPC(command_perform_helper, thread_handle, reinterpret_cast<ULONG_PTR>(this));
    if (!success) {
      return windows_error_result<>("Unable to queue APC");
    }

    return ok_result();
  }

  Result<> listen() override
  {
    {
      Lock lock(thread_handle_mutex);

      HANDLE pseudo_handle = GetCurrentThread();
      BOOL success = DuplicateHandle(GetCurrentProcess(),  // Source process
        pseudo_handle,  // Source handle
        GetCurrentProcess(),  // Destination process
        &thread_handle,  // Destination handle
        0,  // Desired access
        FALSE,  // Inheritable by new processes
        DUPLICATE_SAME_ACCESS  // options
      );
      if (!success) {
        return windows_error_result<>("Unable to duplicate thread handle");
      }
    }

    while (true) {
      SleepEx(INFINITE, true);
    }

    return error_result("listen loop ended unexpectedly");
  }

  Result<bool> handle_add_command(CommandID command,
    ChannelID channel,
    const string &root_path,
    bool recursive) override
  {
    // Convert the path to a wide-character string
    Result<wstring> convr = to_wchar(root_path);
    if (convr.is_error()) return convr.propagate<bool>();
    wstring &root_path_w = convr.get_value();

    // Open a directory handle
    HANDLE root = CreateFileW(root_path_w.c_str(),  // null-terminated wchar file name
      FILE_LIST_DIRECTORY,  // desired access
      FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,  // share mode
      NULL,  // security attributes
      OPEN_EXISTING,  // creation disposition
      FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OVERLAPPED,  // flags and attributes
      NULL  // template file
    );
    if (root == INVALID_HANDLE_VALUE) {
      ostringstream msg;
      msg << "Unable to open directory handle " << root_path;
      return windows_error_result<bool>(msg.str());
    }

    // Allocate and persist the subscription
    Subscription *sub = new Subscription(channel, root, root_path_w, recursive, this);
    auto insert_result = subscriptions.insert(make_pair(channel, sub));
    if (!insert_result.second) {
      delete sub;

      ostringstream msg("Channel collision: ");
      msg << channel;
      return Result<bool>::make_error(msg.str());
    }

    ostream &logline = LOGGER << "Added directory root " << root_path;
    if (!recursive) logline << " (non-recursive)";
    logline << " at channel " << channel << "." << endl;

    Result<bool> schedr = sub->schedule(&event_helper);
    if (schedr.is_error()) return schedr.propagate<bool>();
    if (!schedr.get_value()) {
      LOGGER << "Falling back to polling for watch root " << root_path << "." << endl;

      return emit(Message(CommandPayloadBuilder::add(channel, string(root_path), recursive, 1).build()))
        .propagate(false);
    }

    cache.prepopulate(root_path, DEFAULT_CACHE_PREPOPULATION, recursive);

    return ok_result(true);
  }

  Result<bool> handle_remove_command(CommandID command, ChannelID channel) override
  {
    auto it = subscriptions.find(channel);
    if (it == subscriptions.end()) {
      LOGGER << "Channel " << channel << " was already removed." << endl;
      return ok_result(true);
    }

    Result<> r = it->second->stop(command);
    if (r.is_error()) return r.propagate<bool>();

    LOGGER << "Subscription for channel " << channel << " stopped." << endl;
    return ok_result(false);
  }

  void handle_cache_size_command(size_t cache_size) override
  {
    LOGGER << "Changing cache size to " << cache_size << "." << endl;
    cache.resize(cache_size);
  }

  Result<> handle_fs_event(DWORD error_code, DWORD num_bytes, Subscription *sub)
  {
    Timer t;

    // Ensure that the subscription is valid.
    ChannelID channel = sub->get_channel();
    auto it = subscriptions.find(channel);
    if (it == subscriptions.end() || it->second != sub) {
      return ok_result();
    }

    // Subscription termination.
    bool terminate = false;
    if (error_code == ERROR_OPERATION_ABORTED) {
      LOGGER << "Completing termination of channel " << channel << "." << endl;
      terminate = true;
    } else if (sub->is_terminating()) {
      LOGGER << "Filesystem event encountered on terminating channel " << channel << "." << endl;
      terminate = true;
    }
    if (terminate) return remove(sub);

    // Handle errors.
    if (error_code == ERROR_INVALID_PARAMETER) {
      LOGGER << "Attempting to revert to a network-friendly buffer size." << endl;

      Result<> resize = sub->use_network_size();
      if (resize.is_error()) return emit_fatal_error(sub, move(resize));

      return reschedule(sub);
    }

    if (error_code == ERROR_NOTIFY_ENUM_DIR) {
      LOGGER << "Change buffer overflow. Some events may have been lost." << endl;
      return reschedule(sub);
    }

    if (error_code != ERROR_SUCCESS) {
      return emit_fatal_error(sub, windows_error_result<>("Completion callback error", error_code));
    }

    // Schedule the next completion callback.
    BYTE *base = sub->get_written(num_bytes);
    Result<> next = reschedule(sub);

    // Process received events.
    MessageBuffer buffer;
    ChannelMessageBuffer messages(buffer, channel);
    size_t num_events = 0;

    while (true) {
      PFILE_NOTIFY_INFORMATION info = reinterpret_cast<PFILE_NOTIFY_INFORMATION>(base);
      num_events++;

      Result<> pr = process_event_payload(info, sub, messages);
      if (pr.is_error()) {
        LOGGER << "Skipping entry " << pr << "." << endl;
      }

      if (info->NextEntryOffset == 0) {
        break;
      }
      base += info->NextEntryOffset;
    }

    cache.apply();
    cache.prune();

    if (!messages.empty()) {
      Result<> er = emit_all(messages.begin(), messages.end());
      if (er.is_error()) {
        LOGGER << "Unable to emit messages: " << er << "." << endl;
      } else {
        t.stop();
        LOGGER << "Filesystem event batch of size " << num_events << " completed in " << t << ". "
               << plural(messages.size(), "message") << " produced." << endl;
      }
    }

    return next.propagate_as_void();
  }

private:
  Result<> reschedule(Subscription *sub)
  {
    Result<bool> sch = sub->schedule(&event_helper);
    if (sch.is_error()) return emit_fatal_error(sub, sch.propagate_as_void());

    if (!sch.get_value()) {
      Result<string> root = sub->get_root_path();
      if (root.is_error()) return emit_fatal_error(sub, root.propagate_as_void());

      LOGGER << "Falling back to polling for path " << root.get_value() << " at channel " << sub->get_channel() << "."
             << endl;

      Result<> rem = remove(sub);
      rem &= emit(Message(
        CommandPayloadBuilder::add(sub->get_channel(), move(root.get_value()), sub->is_recursive(), 1).build()));
      return rem;
    }

    return ok_result();
  }

  Result<> remove(Subscription *sub)
  {
    Message response(AckPayload(sub->get_command_id(), sub->get_channel(), true, ""));

    // Ensure that the subscription is valid.
    ChannelID channel = sub->get_channel();
    auto it = subscriptions.find(channel);
    if (it == subscriptions.end() || it->second != sub) {
      return ok_result();
    }

    subscriptions.erase(it);
    delete sub;

    if (sub->get_command_id() != NULL_COMMAND_ID) {
      return emit(move(response));
    } else {
      return ok_result();
    }
  }

  Result<> emit_fatal_error(Subscription *sub, Result<> &&r)
  {
    assert(r.is_error());

    Result<> out = emit(Message(ErrorPayload(sub->get_channel(), string(r.get_error()), true)));
    out &= remove(sub);
    return out;
  }

  Result<> process_event_payload(PFILE_NOTIFY_INFORMATION info, Subscription *sub, ChannelMessageBuffer &messages)
  {
    ChannelID channel = sub->get_channel();
    wstring relpathw{info->FileName, info->FileNameLength / sizeof(WCHAR)};
    wstring shortpathw = sub->make_absolute(move(relpathw));
    Result<wstring> longpathr = to_long_path(shortpathw);
    if (longpathr.is_error()) return longpathr.propagate_as_void();
    wstring &pathw = longpathr.get_value();

    Result<string> u8r = to_utf8(pathw);
    if (u8r.is_error()) {
      LOGGER << "Unable to convert path to utf-8: " << u8r << "." << endl;
      return ok_result();
    }
    string &path = u8r.get_value();

    shared_ptr<StatResult> stat;
    if (info->Action == FILE_ACTION_REMOVED || info->Action == FILE_ACTION_RENAMED_OLD_NAME) {
      stat = cache.former_at_path(path, false, false, false);
    } else {
      stat = cache.current_at_path(path, false, false, false);
      if (stat->is_absent()) {
        stat = cache.former_at_path(path, false, false, false);
      }
    }
    EntryKind kind = stat->get_entry_kind();

    ostream &logline = LOGGER;
    logline << "Event at [" << path << "] ";

    switch (info->Action) {
      case FILE_ACTION_ADDED:
        logline << "FILE_ACTION_ADDED " << kind << "." << endl;
        messages.created(move(path), kind);
        break;
      case FILE_ACTION_MODIFIED:
        logline << "FILE_ACTION_MODIFIED " << kind;
        if (kind != KIND_DIRECTORY) {
          logline << "." << endl;
          messages.modified(move(path), kind);
        } else {
          logline << " (ignored)." << endl;
        }
        break;
      case FILE_ACTION_REMOVED:
        logline << "FILE_ACTION_REMOVED " << kind << "." << endl;
        messages.deleted(move(path), kind);
        break;
      case FILE_ACTION_RENAMED_OLD_NAME:
        logline << "FILE_ACTION_RENAMED_OLD_NAME " << kind << "." << endl;
        sub->remember_old_path(move(path), kind);
        break;
      case FILE_ACTION_RENAMED_NEW_NAME:
        logline << "FILE_ACTION_RENAMED_NEW_NAME ";
        if (sub->was_old_path_seen()) {
          // Old name received first
          if (kind == KIND_UNKNOWN) {
            kind = sub->get_old_path_kind();
          }

          logline << kind << "." << endl;
          cache.update_for_rename(sub->get_old_path(), path);
          messages.renamed(string(sub->get_old_path()), move(path), kind);
          sub->clear_old_path();
        } else {
          // No old name. Treat it as a creation
          logline << "(unpaired) " << kind << "." << endl;
          messages.created(move(path), kind);
        }
        break;
      default:
        logline << " with unexpected action " << info->Action << "." << endl;

        ostringstream out;
        out << "Unexpected action " << info->Action << " reported by ReadDirectoryChangesW for " << path;
        return error_result(out.str());
        break;
    }

    return ok_result();
  }

  uv_mutex_t thread_handle_mutex;
  HANDLE thread_handle;

  map<ChannelID, Subscription *> subscriptions;

  RecentFileCache cache;
};

unique_ptr<WorkerPlatform> WorkerPlatform::for_worker(WorkerThread *thread)
{
  return unique_ptr<WorkerPlatform>(new WindowsWorkerPlatform(thread));
}

void CALLBACK command_perform_helper(__in ULONG_PTR payload)
{
  WindowsWorkerPlatform *platform = reinterpret_cast<WindowsWorkerPlatform *>(payload);
  platform->handle_commands();
}

void CALLBACK event_helper(DWORD error_code, DWORD num_bytes, LPOVERLAPPED overlapped)
{
  Subscription *sub = static_cast<Subscription *>(overlapped->hEvent);
  Result<> r = sub->get_platform()->handle_fs_event(error_code, num_bytes, sub);
  if (r.is_error()) {
    LOGGER << "Unable to handle filesystem events: " << r << "." << endl;
  }
}
<|endoftext|>


<commit_before>#include "queryview.h"

ulong QueryView::queryViewObjectId = 0;

QueryView::QueryView(QWidget *parent, QSqlQueryModel* model, QString const name,
                     int const time, int const rows, int const cols, Qt::WidgetAttribute f)
{
    //Identify this object with thisTableViewId for constructing database connection
    //specific to this object and this object alone.
    thisQueryViewId = queryViewObjectId++;

    //Thread busy indicator to avoid crashing of app when
    //QueryView receives a close signal before data retrieval.
    threadBusy = false;

    qview = new QTableView(this);
    qview->resizeColumnsToContents();

    setCentralWidget(qview);
    QString timeel = QApplication::translate("QueryView", "Time elapsed:", 0, QApplication::UnicodeUTF8);
    QString rowsStr = QApplication::translate("QueryView", "Rows:", 0, QApplication::UnicodeUTF8);
    QString colsStr = QApplication::translate("QueryView", "Columns:", 0, QApplication::UnicodeUTF8);
    statusBar()->showMessage(QApplication::translate("QueryView", "Fetching data...", 0, QApplication::UnicodeUTF8));
    //qview->setModel(model);
    setWindowTitle(name);
    qview->setStyleSheet("QTableView {font-weight: 400;}");
    qview->setAlternatingRowColors(true);
    setGeometry(100,100,640,480);

    //Create Ctrl+Shift+C key combo to copy selected table contents without headers.
    QShortcut* shortcut_ctrl_c = new QShortcut(QKeySequence::Copy, this);
    connect(shortcut_ctrl_c, SIGNAL(activated()), this, SLOT(copyc()));

    //Create Ctrl+Shift+C key combo to copy selected table contents with headers.
    QShortcut* shortcut_ctrl_shft_c = new QShortcut(QKeySequence("Ctrl+Shift+C"), this);
    connect(shortcut_ctrl_shft_c, SIGNAL(activated()), this, SLOT(copych()));

    //Create key-sequences for fullscreen and restore.
    QShortcut* shortcut_fs_win = new QShortcut(QKeySequence(Qt::Key_F11), this);
    connect(shortcut_fs_win, SIGNAL(activated()), this, SLOT(fullscreen()));
    QShortcut* shortcut_restore_win = new QShortcut(QKeySequence(Qt::Key_Escape), this);
    connect(shortcut_restore_win, SIGNAL(activated()), this, SLOT(restore()));
    show();
}

void QueryView::contextMenuEvent(QGraphicsSceneContextMenuEvent *event)
{
    QMenu menu;

    QPalette palette;
    palette.setColor(menu.backgroundRole(), QColor(205,205,205));
    menu.setPalette(palette);

    menu.addAction("Remove");
    QAction *a = menu.exec(event->screenPos());
    if(a && QString::compare(a->text(),"Remove")==0) {

    }
    if(a && QString::compare(a->text(),"Expand")==0) {

    }
    if(a && QString::compare(a->text(),"Collapse")==0) {

    }
}

void QueryView::closeEvent(QCloseEvent *event)
{
    if(!threadBusy)
    {
        emit cleanSignal();
        delete qview;
        close();
    }
}

void QueryView::copyc()
{
    QItemSelectionModel* s = qview->selectionModel();
    QModelIndexList indices = s->selectedIndexes();
    if(indices.isEmpty()) {
        return;
    }
    qSort(indices);
    QModelIndex prev = indices.first();
    QModelIndex last = indices.last();
    indices.removeFirst();
    QModelIndex current;
    QString selectedText;

    foreach(current, indices) {
        QVariant data = qview->model()->data(prev);
        selectedText.append(data.toString());
        if(current.row() != prev.row())
            selectedText.append(QLatin1Char('\n'));
        else
            selectedText.append(QLatin1Char('\t'));
        prev = current;
    }
    selectedText.append(qview->model()->data(last).toString());
    selectedText.append(QLatin1Char('\n'));
    qApp->clipboard()->setText(selectedText);
}

void QueryView::copych()
{
    QAbstractItemModel* atm = qview->model();
    QItemSelectionModel* s = qview->selectionModel();
    QModelIndexList indices = s->selectedIndexes();
    if(indices.isEmpty())
        return;
    qSort(indices);
    QString headerText;
    QModelIndex current;
    int prevRow = indices.at(0).row();
    foreach(current, indices) {
        if(current.row() == prevRow) {
            QVariant data = atm->headerData(current.column(), Qt::Horizontal);
            headerText.append(data.toString());
            headerText.append(QLatin1Char('\t'));
        }
        else {
            headerText.append(QLatin1Char('\n'));
            break;
        }
        prevRow = current.row();
    }
    if(!headerText.endsWith("\n"))
        headerText.append(QLatin1Char('\n'));
    QString selectedText;
    QModelIndex prev = indices.first();
    QModelIndex last = indices.last();
    indices.removeFirst();
    foreach(current, indices) {
        QVariant data = atm->data(prev);
        selectedText.append(data.toString());
        if(current.row() != prev.row())
            selectedText.append(QLatin1Char('\n'));
        else
            selectedText.append(QLatin1Char('\t'));
        prev = current;
    }
    selectedText.append(atm->data(last).toString());
    selectedText.append(QLatin1Char('\n'));
    qApp->clipboard()->setText(headerText + selectedText);
}

void QueryView::fetchDataSlot(SqlMdl* smdl, int time, qint32 rows, qint32 cols)
{
    if(smdl->lastError().isValid()) {
        //emit errMesg(smdl->lastError().databaseText(), 1);
        //return;

        QStandardItemModel* model = new QStandardItemModel(0,1);
        QStringList mesgs = smdl->lastError().databaseText().split("\n");
        QStandardItem *item = new QStandardItem(mesgs[0]);
        model->appendRow(item);
        item->appendRow(new QStandardItem(mesgs[1]));
        model->appendRow(new QStandardItem(mesgs[1]));
        item->appendRow(new QStandardItem(mesgs[2]));
        model->appendRow(new QStandardItem(mesgs[2]));
        QFont serifFont("Courier", 10, QFont::Bold);
        qview->setFont(serifFont);
        qview->setModel(model);
        qview->resizeColumnToContents(0);
        QStringList hdr;
        hdr << "Error messages";
        model->setHorizontalHeaderLabels(hdr);
        QString timeel = QApplication::translate("QueryView", "Time elapsed:", 0, QApplication::UnicodeUTF8);
        QString rowsStr = QApplication::translate("QueryView", "Rows:", 0, QApplication::UnicodeUTF8);
        QString colsStr = QApplication::translate("QueryView", "Columns:", 0, QApplication::UnicodeUTF8);
        statusBar()->showMessage(timeel + QString::number((double)time/1000) +
                                 " s \t " + rowsStr + "3" +
                                 " \t " + colsStr + "1");
    }
    else
    {
        QString timeel = QApplication::translate("QueryView", "Time elapsed:", 0, QApplication::UnicodeUTF8);
        QString rowsStr = QApplication::translate("QueryView", "Rows:", 0, QApplication::UnicodeUTF8);
        QString colsStr = QApplication::translate("QueryView", "Columns:", 0, QApplication::UnicodeUTF8);
        statusBar()->showMessage(timeel + QString::number((double)time/1000) +
                                 " s \t " + rowsStr + QString::number(rows) +
                                 " \t " + colsStr + QString::number(cols));
        qview->setModel(smdl);
        if(smdl->query().executedQuery().startsWith("explain",Qt::CaseInsensitive))
        {
            qview->resizeColumnToContents(0);
            this->setGeometry(100,100,qview->columnWidth(0),480);
        }
    }
    setCursor(Qt::ArrowCursor);
    threadBusy = false;
    emit finished();
}

void QueryView::busySlot()
{
    threadBusy = true;
    setCursor(Qt::WaitCursor);
}

void QueryView::fullscreen()
{
    this->showFullScreen();
}

void QueryView::restore()
{
    this->showNormal();
}

<commit_msg>Don't assume errors are always 3 lines. Change to display as many as necessary.<commit_after>#include "queryview.h"

ulong QueryView::queryViewObjectId = 0;

QueryView::QueryView(QWidget *parent, QSqlQueryModel* model, QString const name,
                     int const time, int const rows, int const cols, Qt::WidgetAttribute f)
{
    //Identify this object with thisTableViewId for constructing database connection
    //specific to this object and this object alone.
    thisQueryViewId = queryViewObjectId++;

    //Thread busy indicator to avoid crashing of app when
    //QueryView receives a close signal before data retrieval.
    threadBusy = false;

    qview = new QTableView(this);
    qview->resizeColumnsToContents();

    setCentralWidget(qview);
    QString timeel = QApplication::translate("QueryView", "Time elapsed:", 0, QApplication::UnicodeUTF8);
    QString rowsStr = QApplication::translate("QueryView", "Rows:", 0, QApplication::UnicodeUTF8);
    QString colsStr = QApplication::translate("QueryView", "Columns:", 0, QApplication::UnicodeUTF8);
    statusBar()->showMessage(QApplication::translate("QueryView", "Fetching data...", 0, QApplication::UnicodeUTF8));
    //qview->setModel(model);
    setWindowTitle(name);
    qview->setStyleSheet("QTableView {font-weight: 400;}");
    qview->setAlternatingRowColors(true);
    setGeometry(100,100,640,480);

    //Create Ctrl+Shift+C key combo to copy selected table contents without headers.
    QShortcut* shortcut_ctrl_c = new QShortcut(QKeySequence::Copy, this);
    connect(shortcut_ctrl_c, SIGNAL(activated()), this, SLOT(copyc()));

    //Create Ctrl+Shift+C key combo to copy selected table contents with headers.
    QShortcut* shortcut_ctrl_shft_c = new QShortcut(QKeySequence("Ctrl+Shift+C"), this);
    connect(shortcut_ctrl_shft_c, SIGNAL(activated()), this, SLOT(copych()));

    //Create key-sequences for fullscreen and restore.
    QShortcut* shortcut_fs_win = new QShortcut(QKeySequence(Qt::Key_F11), this);
    connect(shortcut_fs_win, SIGNAL(activated()), this, SLOT(fullscreen()));
    QShortcut* shortcut_restore_win = new QShortcut(QKeySequence(Qt::Key_Escape), this);
    connect(shortcut_restore_win, SIGNAL(activated()), this, SLOT(restore()));
    show();
}

void QueryView::contextMenuEvent(QGraphicsSceneContextMenuEvent *event)
{
    QMenu menu;

    QPalette palette;
    palette.setColor(menu.backgroundRole(), QColor(205,205,205));
    menu.setPalette(palette);

    menu.addAction("Remove");
    QAction *a = menu.exec(event->screenPos());
    if(a && QString::compare(a->text(),"Remove")==0) {

    }
    if(a && QString::compare(a->text(),"Expand")==0) {

    }
    if(a && QString::compare(a->text(),"Collapse")==0) {

    }
}

void QueryView::closeEvent(QCloseEvent *event)
{
    if(!threadBusy)
    {
        emit cleanSignal();
        delete qview;
        close();
    }
}

void QueryView::copyc()
{
    QItemSelectionModel* s = qview->selectionModel();
    QModelIndexList indices = s->selectedIndexes();
    if(indices.isEmpty()) {
        return;
    }
    qSort(indices);
    QModelIndex prev = indices.first();
    QModelIndex last = indices.last();
    indices.removeFirst();
    QModelIndex current;
    QString selectedText;

    foreach(current, indices) {
        QVariant data = qview->model()->data(prev);
        selectedText.append(data.toString());
        if(current.row() != prev.row())
            selectedText.append(QLatin1Char('\n'));
        else
            selectedText.append(QLatin1Char('\t'));
        prev = current;
    }
    selectedText.append(qview->model()->data(last).toString());
    selectedText.append(QLatin1Char('\n'));
    qApp->clipboard()->setText(selectedText);
}

void QueryView::copych()
{
    QAbstractItemModel* atm = qview->model();
    QItemSelectionModel* s = qview->selectionModel();
    QModelIndexList indices = s->selectedIndexes();
    if(indices.isEmpty())
        return;
    qSort(indices);
    QString headerText;
    QModelIndex current;
    int prevRow = indices.at(0).row();
    foreach(current, indices) {
        if(current.row() == prevRow) {
            QVariant data = atm->headerData(current.column(), Qt::Horizontal);
            headerText.append(data.toString());
            headerText.append(QLatin1Char('\t'));
        }
        else {
            headerText.append(QLatin1Char('\n'));
            break;
        }
        prevRow = current.row();
    }
    if(!headerText.endsWith("\n"))
        headerText.append(QLatin1Char('\n'));
    QString selectedText;
    QModelIndex prev = indices.first();
    QModelIndex last = indices.last();
    indices.removeFirst();
    foreach(current, indices) {
        QVariant data = atm->data(prev);
        selectedText.append(data.toString());
        if(current.row() != prev.row())
            selectedText.append(QLatin1Char('\n'));
        else
            selectedText.append(QLatin1Char('\t'));
        prev = current;
    }
    selectedText.append(atm->data(last).toString());
    selectedText.append(QLatin1Char('\n'));
    qApp->clipboard()->setText(headerText + selectedText);
}

void QueryView::fetchDataSlot(SqlMdl* smdl, int time, qint32 rows, qint32 cols)
{
    if(smdl->lastError().isValid()) {
        //emit errMesg(smdl->lastError().databaseText(), 1);
        //return;

        QStandardItemModel* model = new QStandardItemModel(0,1);
        QStringList mesgs = smdl->lastError().databaseText().split("\n");
        mesgs.removeLast();
        foreach(const QString m , mesgs)
        {
            model->appendRow(new QStandardItem(m));
        }

        QFont serifFont("Courier", 10, QFont::Bold);
        qview->setFont(serifFont);
        qview->setModel(model);
        qview->resizeColumnToContents(0);
        QStringList hdr;
        hdr << "Error messages";
        model->setHorizontalHeaderLabels(hdr);
        QString timeel = QApplication::translate("QueryView", "Time elapsed:", 0, QApplication::UnicodeUTF8);
        QString rowsStr = QApplication::translate("QueryView", "Rows:", 0, QApplication::UnicodeUTF8);
        QString colsStr = QApplication::translate("QueryView", "Columns:", 0, QApplication::UnicodeUTF8);
        statusBar()->showMessage(timeel + QString::number((double)time/1000) +
                                 " s \t " + rowsStr + "3" +
                                 " \t " + colsStr + "1");
    }
    else
    {
        QString timeel = QApplication::translate("QueryView", "Time elapsed:", 0, QApplication::UnicodeUTF8);
        QString rowsStr = QApplication::translate("QueryView", "Rows:", 0, QApplication::UnicodeUTF8);
        QString colsStr = QApplication::translate("QueryView", "Columns:", 0, QApplication::UnicodeUTF8);
        statusBar()->showMessage(timeel + QString::number((double)time/1000) +
                                 " s \t " + rowsStr + QString::number(rows) +
                                 " \t " + colsStr + QString::number(cols));
        qview->setModel(smdl);
        if(smdl->query().executedQuery().startsWith("explain",Qt::CaseInsensitive))
        {
            qview->resizeColumnToContents(0);
            this->setGeometry(100,100,qview->columnWidth(0),480);
        }
    }
    setCursor(Qt::ArrowCursor);
    threadBusy = false;
    emit finished();
}

void QueryView::busySlot()
{
    threadBusy = true;
    setCursor(Qt::WaitCursor);
}

void QueryView::fullscreen()
{
    this->showFullScreen();
}

void QueryView::restore()
{
    this->showNormal();
}

<|endoftext|>


<commit_before>//
// Aspia Project
// Copyright (C) 2019 Dmitry Chapyshev <dmitry@aspia.ru>
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
//

#include "client/client_desktop.h"

#include "base/logging.h"
#include "client/desktop_control_proxy.h"
#include "client/desktop_window.h"
#include "client/desktop_window_proxy.h"
#include "codec/cursor_decoder.h"
#include "codec/video_decoder.h"
#include "codec/video_util.h"
#include "common/desktop_session_constants.h"
#include "desktop/desktop_frame.h"
#include "desktop/mouse_cursor.h"

namespace client {

ClientDesktop::ClientDesktop(std::shared_ptr<base::TaskRunner>& ui_task_runner)
    : Client(ui_task_runner)
{
    // Nothing
}

ClientDesktop::~ClientDesktop() = default;

void ClientDesktop::setDesktopWindow(DesktopWindow* desktop_window)
{
    DCHECK(uiTaskRunner()->belongsToCurrentThread());

    desktop_window_proxy_ = DesktopWindowProxy::create(uiTaskRunner(), desktop_window);
}

void ClientDesktop::onSessionStarted(const base::Version& peer_version)
{
    desktop_control_proxy_ = std::make_shared<DesktopControlProxy>(ioTaskRunner(), this);
    started_ = true;

    desktop_window_proxy_->showWindow(desktop_control_proxy_, peer_version);
}

void ClientDesktop::onSessionStopped()
{
    started_ = false;
    desktop_control_proxy_->dettach();
}

void ClientDesktop::onMessageReceived(const base::ByteArray& buffer)
{
    incoming_message_.Clear();

    if (!incoming_message_.ParseFromArray(buffer.data(), buffer.size()))
    {
        LOG(LS_ERROR) << "Invalid message from host";
        return;
    }

    if (incoming_message_.has_video_packet() || incoming_message_.has_cursor_shape())
    {
        if (incoming_message_.has_video_packet())
            readVideoPacket(incoming_message_.video_packet());

        if (incoming_message_.has_cursor_shape())
            readCursorShape(incoming_message_.cursor_shape());
    }
    else if (incoming_message_.has_clipboard_event())
    {
        readClipboardEvent(incoming_message_.clipboard_event());
    }
    else if (incoming_message_.has_config_request())
    {
        readConfigRequest(incoming_message_.config_request());
    }
    else if (incoming_message_.has_extension())
    {
        readExtension(incoming_message_.extension());
    }
    else
    {
        // Unknown messages are ignored.
        LOG(LS_WARNING) << "Unhandled message from host";
    }
}

void ClientDesktop::onMessageWritten()
{
    // Nothing
}

void ClientDesktop::setDesktopConfig(const proto::DesktopConfig& desktop_config)
{
    desktop_config_ = desktop_config;

    // If the session is not already running, then we do not need to send the configuration.
    if (!started_)
        return;

    if (!(desktop_config_.flags() & proto::ENABLE_CURSOR_SHAPE))
        cursor_decoder_.reset();

    outgoing_message_.Clear();
    outgoing_message_.mutable_config()->CopyFrom(desktop_config_);
    sendMessage(outgoing_message_);
}

void ClientDesktop::setCurrentScreen(const proto::Screen& screen)
{
    outgoing_message_.Clear();

    proto::DesktopExtension* extension = outgoing_message_.mutable_extension();

    extension->set_name(common::kSelectScreenExtension);
    extension->set_data(screen.SerializeAsString());

    sendMessage(outgoing_message_);
}

void ClientDesktop::onKeyEvent(const proto::KeyEvent& event)
{
    if (sessionType() != proto::SESSION_TYPE_DESKTOP_MANAGE)
        return;

    outgoing_message_.Clear();
    outgoing_message_.mutable_key_event()->CopyFrom(event);
    sendMessage(outgoing_message_);
}

void ClientDesktop::onPointerEvent(const proto::PointerEvent& event)
{
    if (sessionType() != proto::SESSION_TYPE_DESKTOP_MANAGE)
        return;

    outgoing_message_.Clear();
    outgoing_message_.mutable_pointer_event()->CopyFrom(event);
    sendMessage(outgoing_message_);
}

void ClientDesktop::onClipboardEvent(const proto::ClipboardEvent& event)
{
    if (sessionType() != proto::SESSION_TYPE_DESKTOP_MANAGE)
        return;

    if (!(desktop_config_.flags() & proto::ENABLE_CLIPBOARD))
        return;

    outgoing_message_.Clear();
    outgoing_message_.mutable_clipboard_event()->CopyFrom(event);
    sendMessage(outgoing_message_);
}

void ClientDesktop::onPowerControl(proto::PowerControl::Action action)
{
    if (sessionType() != proto::SESSION_TYPE_DESKTOP_MANAGE)
        return;

    outgoing_message_.Clear();

    proto::DesktopExtension* extension = outgoing_message_.mutable_extension();

    proto::PowerControl power_control;
    power_control.set_action(action);

    extension->set_name(common::kPowerControlExtension);
    extension->set_data(power_control.SerializeAsString());

    sendMessage(outgoing_message_);
}

void ClientDesktop::onRemoteUpdate()
{
    outgoing_message_.Clear();
    outgoing_message_.mutable_extension()->set_name(common::kRemoteUpdateExtension);
    sendMessage(outgoing_message_);
}

void ClientDesktop::onSystemInfoRequest()
{
    outgoing_message_.Clear();
    outgoing_message_.mutable_extension()->set_name(common::kSystemInfoExtension);
    sendMessage(outgoing_message_);
}

void ClientDesktop::readConfigRequest(const proto::DesktopConfigRequest& config_request)
{
    // We notify the window about changes in the list of extensions and video encodings.
    // A window can disable/enable some of its capabilities in accordance with this information.
    desktop_window_proxy_->setCapabilities(
        config_request.extensions(), config_request.video_encodings());

    // If current video encoding not supported.
    if (!(config_request.video_encodings() & desktop_config_.video_encoding()))
    {
        // We tell the window about the need to change the encoding.
        desktop_window_proxy_->configRequired();
    }
    else
    {
        // Everything is fine, we send the current configuration.
        setDesktopConfig(desktop_config_);
    }
}

void ClientDesktop::readVideoPacket(const proto::VideoPacket& packet)
{
    if (video_encoding_ != packet.encoding())
    {
        video_decoder_ = codec::VideoDecoder::create(packet.encoding());
        video_encoding_ = packet.encoding();
    }

    if (!video_decoder_)
    {
        LOG(LS_ERROR) << "Video decoder not initialized";
        return;
    }

    if (packet.has_format())
    {
        desktop::Rect screen_rect = codec::parseRect(packet.format().screen_rect());

        static const int kMaxValue = std::numeric_limits<uint16_t>::max();
        static const int kMinValue = -std::numeric_limits<uint16_t>::max();

        if (screen_rect.width()  <= 0 || screen_rect.width()  >= kMaxValue ||
            screen_rect.height() <= 0 || screen_rect.height() >= kMaxValue)
        {
            LOG(LS_ERROR) << "Wrong video frame size";
            return;
        }

        if (screen_rect.x() < kMinValue || screen_rect.x() >= kMaxValue ||
            screen_rect.y() < kMinValue || screen_rect.y() >= kMaxValue)
        {
            LOG(LS_ERROR) << "Wrong video frame position";
            return;
        }

        desktop_frame_ = desktop_window_proxy_->allocateFrame(screen_rect.size());
        desktop_frame_->setTopLeft(screen_rect.topLeft());
    }

    if (!desktop_frame_)
    {
        LOG(LS_ERROR) << "The desktop frame is not initialized";
        return;
    }

    if (!video_decoder_->decode(packet, desktop_frame_.get()))
    {
        LOG(LS_ERROR) << "The video packet could not be decoded";
        return;
    }

    desktop_window_proxy_->drawFrame(desktop_frame_);
}

void ClientDesktop::readCursorShape(const proto::CursorShape& cursor_shape)
{
    if (sessionType() != proto::SESSION_TYPE_DESKTOP_MANAGE)
        return;

    if (!(desktop_config_.flags() & proto::ENABLE_CURSOR_SHAPE))
        return;

    if (!cursor_decoder_)
        cursor_decoder_ = std::make_unique<codec::CursorDecoder>();

    std::shared_ptr<desktop::MouseCursor> mouse_cursor = cursor_decoder_->decode(cursor_shape);
    if (!mouse_cursor)
        return;

    desktop_window_proxy_->drawMouseCursor(mouse_cursor);
}

void ClientDesktop::readClipboardEvent(const proto::ClipboardEvent& clipboard_event)
{
    if (sessionType() != proto::SESSION_TYPE_DESKTOP_MANAGE)
        return;

    if (!(desktop_config_.flags() & proto::ENABLE_CLIPBOARD))
        return;

    desktop_window_proxy_->injectClipboardEvent(clipboard_event);
}

void ClientDesktop::readExtension(const proto::DesktopExtension& extension)
{
    if (extension.name() == common::kSelectScreenExtension)
    {
        proto::ScreenList screen_list;

        if (!screen_list.ParseFromString(extension.data()))
        {
            LOG(LS_ERROR) << "Unable to parse select screen extension data";
            return;
        }

        desktop_window_proxy_->setScreenList(screen_list);
    }
    else if (extension.name() == common::kSystemInfoExtension)
    {
        proto::SystemInfo system_info;

        if (!system_info.ParseFromString(extension.data()))
        {
            LOG(LS_ERROR) << "Unable to parse system info extension data";
            return;
        }

        desktop_window_proxy_->setSystemInfo(system_info);
    }
    else
    {
        LOG(LS_WARNING) << "Unknown extension: " << extension.name();
    }
}

} // namespace client
<commit_msg>- Added missed fixupDesktopConfig call.<commit_after>//
// Aspia Project
// Copyright (C) 2019 Dmitry Chapyshev <dmitry@aspia.ru>
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
//

#include "client/client_desktop.h"

#include "base/logging.h"
#include "client/desktop_control_proxy.h"
#include "client/desktop_window.h"
#include "client/desktop_window_proxy.h"
#include "client/config_factory.h"
#include "codec/cursor_decoder.h"
#include "codec/video_decoder.h"
#include "codec/video_util.h"
#include "common/desktop_session_constants.h"
#include "desktop/desktop_frame.h"
#include "desktop/mouse_cursor.h"

namespace client {

ClientDesktop::ClientDesktop(std::shared_ptr<base::TaskRunner>& ui_task_runner)
    : Client(ui_task_runner)
{
    // Nothing
}

ClientDesktop::~ClientDesktop() = default;

void ClientDesktop::setDesktopWindow(DesktopWindow* desktop_window)
{
    DCHECK(uiTaskRunner()->belongsToCurrentThread());

    desktop_window_proxy_ = DesktopWindowProxy::create(uiTaskRunner(), desktop_window);
}

void ClientDesktop::onSessionStarted(const base::Version& peer_version)
{
    desktop_control_proxy_ = std::make_shared<DesktopControlProxy>(ioTaskRunner(), this);
    started_ = true;

    desktop_window_proxy_->showWindow(desktop_control_proxy_, peer_version);
}

void ClientDesktop::onSessionStopped()
{
    started_ = false;
    desktop_control_proxy_->dettach();
}

void ClientDesktop::onMessageReceived(const base::ByteArray& buffer)
{
    incoming_message_.Clear();

    if (!incoming_message_.ParseFromArray(buffer.data(), buffer.size()))
    {
        LOG(LS_ERROR) << "Invalid message from host";
        return;
    }

    if (incoming_message_.has_video_packet() || incoming_message_.has_cursor_shape())
    {
        if (incoming_message_.has_video_packet())
            readVideoPacket(incoming_message_.video_packet());

        if (incoming_message_.has_cursor_shape())
            readCursorShape(incoming_message_.cursor_shape());
    }
    else if (incoming_message_.has_clipboard_event())
    {
        readClipboardEvent(incoming_message_.clipboard_event());
    }
    else if (incoming_message_.has_config_request())
    {
        readConfigRequest(incoming_message_.config_request());
    }
    else if (incoming_message_.has_extension())
    {
        readExtension(incoming_message_.extension());
    }
    else
    {
        // Unknown messages are ignored.
        LOG(LS_WARNING) << "Unhandled message from host";
    }
}

void ClientDesktop::onMessageWritten()
{
    // Nothing
}

void ClientDesktop::setDesktopConfig(const proto::DesktopConfig& desktop_config)
{
    desktop_config_ = desktop_config;

    ConfigFactory::fixupDesktopConfig(&desktop_config_);

    // If the session is not already running, then we do not need to send the configuration.
    if (!started_)
        return;

    if (!(desktop_config_.flags() & proto::ENABLE_CURSOR_SHAPE))
        cursor_decoder_.reset();

    outgoing_message_.Clear();
    outgoing_message_.mutable_config()->CopyFrom(desktop_config_);
    sendMessage(outgoing_message_);
}

void ClientDesktop::setCurrentScreen(const proto::Screen& screen)
{
    outgoing_message_.Clear();

    proto::DesktopExtension* extension = outgoing_message_.mutable_extension();

    extension->set_name(common::kSelectScreenExtension);
    extension->set_data(screen.SerializeAsString());

    sendMessage(outgoing_message_);
}

void ClientDesktop::onKeyEvent(const proto::KeyEvent& event)
{
    if (sessionType() != proto::SESSION_TYPE_DESKTOP_MANAGE)
        return;

    outgoing_message_.Clear();
    outgoing_message_.mutable_key_event()->CopyFrom(event);
    sendMessage(outgoing_message_);
}

void ClientDesktop::onPointerEvent(const proto::PointerEvent& event)
{
    if (sessionType() != proto::SESSION_TYPE_DESKTOP_MANAGE)
        return;

    outgoing_message_.Clear();
    outgoing_message_.mutable_pointer_event()->CopyFrom(event);
    sendMessage(outgoing_message_);
}

void ClientDesktop::onClipboardEvent(const proto::ClipboardEvent& event)
{
    if (sessionType() != proto::SESSION_TYPE_DESKTOP_MANAGE)
        return;

    if (!(desktop_config_.flags() & proto::ENABLE_CLIPBOARD))
        return;

    outgoing_message_.Clear();
    outgoing_message_.mutable_clipboard_event()->CopyFrom(event);
    sendMessage(outgoing_message_);
}

void ClientDesktop::onPowerControl(proto::PowerControl::Action action)
{
    if (sessionType() != proto::SESSION_TYPE_DESKTOP_MANAGE)
        return;

    outgoing_message_.Clear();

    proto::DesktopExtension* extension = outgoing_message_.mutable_extension();

    proto::PowerControl power_control;
    power_control.set_action(action);

    extension->set_name(common::kPowerControlExtension);
    extension->set_data(power_control.SerializeAsString());

    sendMessage(outgoing_message_);
}

void ClientDesktop::onRemoteUpdate()
{
    outgoing_message_.Clear();
    outgoing_message_.mutable_extension()->set_name(common::kRemoteUpdateExtension);
    sendMessage(outgoing_message_);
}

void ClientDesktop::onSystemInfoRequest()
{
    outgoing_message_.Clear();
    outgoing_message_.mutable_extension()->set_name(common::kSystemInfoExtension);
    sendMessage(outgoing_message_);
}

void ClientDesktop::readConfigRequest(const proto::DesktopConfigRequest& config_request)
{
    // We notify the window about changes in the list of extensions and video encodings.
    // A window can disable/enable some of its capabilities in accordance with this information.
    desktop_window_proxy_->setCapabilities(
        config_request.extensions(), config_request.video_encodings());

    // If current video encoding not supported.
    if (!(config_request.video_encodings() & desktop_config_.video_encoding()))
    {
        // We tell the window about the need to change the encoding.
        desktop_window_proxy_->configRequired();
    }
    else
    {
        // Everything is fine, we send the current configuration.
        setDesktopConfig(desktop_config_);
    }
}

void ClientDesktop::readVideoPacket(const proto::VideoPacket& packet)
{
    if (video_encoding_ != packet.encoding())
    {
        video_decoder_ = codec::VideoDecoder::create(packet.encoding());
        video_encoding_ = packet.encoding();
    }

    if (!video_decoder_)
    {
        LOG(LS_ERROR) << "Video decoder not initialized";
        return;
    }

    if (packet.has_format())
    {
        const proto::Rect& screen_rect = packet.format().screen_rect();

        static const int kMaxValue = std::numeric_limits<uint16_t>::max();
        static const int kMinValue = -std::numeric_limits<uint16_t>::max();

        if (screen_rect.width()  <= 0 || screen_rect.width()  >= kMaxValue ||
            screen_rect.height() <= 0 || screen_rect.height() >= kMaxValue)
        {
            LOG(LS_ERROR) << "Wrong video frame size";
            return;
        }

        if (screen_rect.x() < kMinValue || screen_rect.x() >= kMaxValue ||
            screen_rect.y() < kMinValue || screen_rect.y() >= kMaxValue)
        {
            LOG(LS_ERROR) << "Wrong video frame position";
            return;
        }

        desktop_frame_ = desktop_window_proxy_->allocateFrame(
            desktop::Size(screen_rect.width(), screen_rect.height()));

        desktop_frame_->setTopLeft(desktop::Point(screen_rect.x(), screen_rect.y()));
    }

    if (!desktop_frame_)
    {
        LOG(LS_ERROR) << "The desktop frame is not initialized";
        return;
    }

    if (!video_decoder_->decode(packet, desktop_frame_.get()))
    {
        LOG(LS_ERROR) << "The video packet could not be decoded";
        return;
    }

    desktop_window_proxy_->drawFrame(desktop_frame_);
}

void ClientDesktop::readCursorShape(const proto::CursorShape& cursor_shape)
{
    if (sessionType() != proto::SESSION_TYPE_DESKTOP_MANAGE)
        return;

    if (!(desktop_config_.flags() & proto::ENABLE_CURSOR_SHAPE))
        return;

    if (!cursor_decoder_)
        cursor_decoder_ = std::make_unique<codec::CursorDecoder>();

    std::shared_ptr<desktop::MouseCursor> mouse_cursor = cursor_decoder_->decode(cursor_shape);
    if (!mouse_cursor)
        return;

    desktop_window_proxy_->drawMouseCursor(mouse_cursor);
}

void ClientDesktop::readClipboardEvent(const proto::ClipboardEvent& clipboard_event)
{
    if (sessionType() != proto::SESSION_TYPE_DESKTOP_MANAGE)
        return;

    if (!(desktop_config_.flags() & proto::ENABLE_CLIPBOARD))
        return;

    desktop_window_proxy_->injectClipboardEvent(clipboard_event);
}

void ClientDesktop::readExtension(const proto::DesktopExtension& extension)
{
    if (extension.name() == common::kSelectScreenExtension)
    {
        proto::ScreenList screen_list;

        if (!screen_list.ParseFromString(extension.data()))
        {
            LOG(LS_ERROR) << "Unable to parse select screen extension data";
            return;
        }

        desktop_window_proxy_->setScreenList(screen_list);
    }
    else if (extension.name() == common::kSystemInfoExtension)
    {
        proto::SystemInfo system_info;

        if (!system_info.ParseFromString(extension.data()))
        {
            LOG(LS_ERROR) << "Unable to parse system info extension data";
            return;
        }

        desktop_window_proxy_->setSystemInfo(system_info);
    }
    else
    {
        LOG(LS_WARNING) << "Unknown extension: " << extension.name();
    }
}

} // namespace client
<|endoftext|>


<commit_before>#include <iostream>
#include <immintrin.h>
#include <new>
#include <cassert>
#include <sstream>
#include "simd_util.h"
#include "pauli_string.h"
#include <cstring>
#include <cstdlib>
#include <bit>

PauliStringPtr::PauliStringPtr(
            size_t init_size,
            BitPtr init_sign,
            uint64_t *init_x,
            uint64_t *init_z,
            size_t init_stride256) :
        size(init_size),
        bit_ptr_sign(init_sign),
        _x(init_x),
        _z(init_z),
        stride256(init_stride256) {
}

PauliStringVal::PauliStringVal(size_t init_size) :
        val_sign(false),
        x_data(init_size),
        z_data(init_size) {
}

PauliStringVal::PauliStringVal(const PauliStringPtr &other) :
        val_sign(other.bit_ptr_sign.get()),
        x_data(other.size, other._x),
        z_data(other.size, other._z) {
}

PauliStringPtr PauliStringVal::ptr() const {
    return PauliStringPtr(*this);
}

std::string PauliStringVal::str() const {
    return ptr().str();
}

void PauliStringPtr::swap_with(PauliStringPtr &other) {
    assert(size == other.size);
    bit_ptr_sign.swap(other.bit_ptr_sign);
    auto x256 = (__m256i *)_x;
    auto z256 = (__m256i *)_z;
    auto ox256 = (__m256i *)other._x;
    auto oz256 = (__m256i *)other._z;
    auto end = &x256[num_words256() * stride256];
    while (x256 != end) {
        std::swap(*x256, *ox256);
        std::swap(*z256, *oz256);
        x256 += stride256;
        z256 += stride256;
        ox256 += other.stride256;
        oz256 += other.stride256;
    }
}

void PauliStringPtr::overwrite_with(const PauliStringPtr &other) {
    assert(size == other.size);
    bit_ptr_sign.set(other.bit_ptr_sign.get());
    auto x256 = (__m256i *)_x;
    auto z256 = (__m256i *)_z;
    auto ox256 = (__m256i *)other._x;
    auto oz256 = (__m256i *)other._z;
    auto end = &x256[num_words256() * stride256];
    while (x256 != end) {
        *x256 = *ox256;
        *z256 = *oz256;
        x256 += stride256;
        z256 += stride256;
        ox256 += other.stride256;
        oz256 += other.stride256;
    }
}

PauliStringVal& PauliStringVal::operator=(const PauliStringPtr &other) noexcept {
    (*this).~PauliStringVal();
    new(this) PauliStringVal(other);
    return *this;
}

PauliStringPtr::PauliStringPtr(const PauliStringVal &other) :
        size(other.x_data.num_bits),
        bit_ptr_sign(BitPtr((void *)&other.val_sign, 0)),
        _x(other.x_data.u64),
        _z(other.z_data.u64),
        stride256(1) {
}

uint8_t PauliStringPtr::log_i_scalar_byproduct(const PauliStringPtr &other) const {
    assert(size == other.size);
    union {__m256i u256; uint64_t u64[4]; } cnt1 {};
    union {__m256i u256; uint64_t u64[4]; } cnt2 {};
    auto x256 = (__m256i *)_x;
    auto z256 = (__m256i *)_z;
    auto ox256 = (__m256i *)other._x;
    auto oz256 = (__m256i *)other._z;
    auto end = &x256[num_words256() * stride256];
    while (x256 != end) {
        // Load into registers.
        auto x1 = *x256;
        auto z2 = *oz256;
        auto z1 = *z256;
        auto x2 = *ox256;

        auto t1 = x1 & z2;
        auto t2 = x2 & z1;
        // At each bit position: do the Paulis anti-commute?
        auto a = t1 ^ t2;
        // At each bit position: do the Paulis anti-commute and produce a -i instead of a +i?
        auto b = ((x1 ^ z2) & t2) ^ _mm256_andnot_si256(z1, _mm256_andnot_si256(x2, t1));
        // At each bit position: `count += forward - backward` where `backward=b`, `forward=a^b`, `count=cnt1 + 2*cnt2`.
        cnt1.u256 ^= b;
        cnt2.u256 ^= cnt1.u256 & a;
        cnt1.u256 ^= a ^ b;

        // Move along.
        x256 += stride256;
        z256 += stride256;
        ox256 += other.stride256;
        oz256 += other.stride256;
    }

    // Combine final anti-commutation phase tally (mod 4).
    size_t s = 0;
    for (size_t k = 0; k < 4; k++) {
        s += (uint8_t) std::popcount(cnt1.u64[k]);
        s ^= (uint8_t) std::popcount(cnt2.u64[k]) << 1;
    }
    return s & 3;
}

std::string PauliStringPtr::str() const {
    std::stringstream ss;
    ss << *this;
    return ss.str();
}

bool PauliStringPtr::operator==(const PauliStringPtr &other) const {
    if (size != other.size || bit_ptr_sign.get() != other.bit_ptr_sign.get()) {
        return false;
    }
    __m256i acc {};
    auto x256 = (__m256i *)_x;
    auto z256 = (__m256i *)_z;
    auto ox256 = (__m256i *)other._x;
    auto oz256 = (__m256i *)other._z;
    auto end = &x256[num_words256() * stride256];
    while (x256 != end) {
        acc |= *x256 ^ *ox256;
        acc |= *z256 ^ *oz256;
        x256 += stride256;
        z256 += stride256;
        ox256 += other.stride256;
        oz256 += other.stride256;
    }

    auto acc64 = (uint64_t *)&acc;
    for (size_t k = 0; k < 4; k++) {
        if (acc64[k]) {
            return false;
        }
    }

    return true;
}

bool PauliStringPtr::operator!=(const PauliStringPtr &other) const {
    return !(*this == other);
}

std::ostream &operator<<(std::ostream &out, const PauliStringVal &ps) {
    return out << ps.ptr();
}

size_t PauliStringPtr::num_words256() const {
    return ceil256(size) >> 8;
}

std::ostream &operator<<(std::ostream &out, const PauliStringPtr &ps) {
    out << (ps.bit_ptr_sign.get() ? '-' : '+');
    auto x256 = (__m256i *)ps._x;
    auto z256 = (__m256i *)ps._z;
    auto end = &x256[ps.num_words256() * ps.stride256];
    size_t remaining = ps.size;
    while (x256 != end) {
        auto xs = m256i_to_bits(*x256);
        auto zs = m256i_to_bits(*z256);
        for (int j = 0; j < 256 && remaining; j++) {
            out << "_XZY"[xs[j] + 2 * zs[j]];
            remaining--;
        }
        x256 += ps.stride256;
        z256 += ps.stride256;
    }
    return out;
}

PauliStringVal PauliStringVal::from_pattern(bool sign, size_t size, const std::function<char(size_t)> &func) {
    PauliStringVal result(size);
    result.val_sign = sign;
    for (size_t i = 0; i < size; i++) {
        char c = func(i);
        bool x;
        bool z;
        if (c == 'X') {
            x = true;
            z = false;
        } else if (c == 'Y') {
            x = true;
            z = true;
        } else if (c == 'Z') {
            x = false;
            z = true;
        } else if (c == '_' || c == 'I') {
            x = false;
            z = false;
        } else {
            throw std::runtime_error("Unrecognized pauli character. " + std::to_string(c));
        }
        result.x_data.u64[i / 64] ^= (uint64_t)x << (i & 63);
        result.z_data.u64[i / 64] ^= (uint64_t)z << (i & 63);
    }
    return result;
}

PauliStringVal PauliStringVal::from_str(const char *text) {
    auto sign = text[0] == '-';
    if (text[0] == '+' || text[0] == '-') {
        text++;
    }
    return PauliStringVal::from_pattern(sign, strlen(text), [&](size_t i){ return text[i]; });
}

PauliStringVal PauliStringVal::identity(size_t size) {
    return PauliStringVal(size);
}

PauliStringPtr& PauliStringPtr::operator*=(const PauliStringPtr& rhs) {
    uint8_t log_i = inplace_right_mul_with_scalar_output(rhs);
    assert((log_i & 1) == 0);
    bit_ptr_sign.toggle_if(log_i & 2);
    return *this;
}

uint8_t PauliStringPtr::inplace_right_mul_with_scalar_output(const PauliStringPtr& rhs) {
    uint8_t result = log_i_scalar_byproduct(rhs);
    result ^= (uint8_t)rhs.bit_ptr_sign.get() << 1;
    auto x256 = (__m256i *)_x;
    auto z256 = (__m256i *)_z;
    auto ox256 = (__m256i *)rhs._x;
    auto oz256 = (__m256i *)rhs._z;
    auto end = &x256[num_words256() * stride256];
    while (x256 != end) {
        *x256 ^= *ox256;
        x256 += stride256;
        ox256 += rhs.stride256;
    }
    end = &z256[num_words256() * stride256];
    while (z256 != end) {
        *z256 ^= *oz256;
        z256 += stride256;
        oz256 += rhs.stride256;
    }
    return result;
}

bool PauliStringPtr::get_x_bit(size_t k) const {
    k = (k & ~0xFF)*stride256 | (k & 0xFF);
    size_t i0 = k >> 6;
    size_t i1 = k & 63;
    return ((_x[i0] >> i1) & 1) != 0;
}

bool PauliStringPtr::get_z_bit(size_t k) const {
    k = (k & ~0xFF)*stride256 | (k & 0xFF);
    size_t i0 = k >> 6;
    size_t i1 = k & 63;
    return ((_z[i0] >> i1) & 1) != 0;
}

void PauliStringPtr::toggle_x_bit(size_t k) {
    k = (k & ~0xFF)*stride256 | (k & 0xFF);
    size_t i0 = k >> 6;
    size_t i1 = k & 63;
    _x[i0] ^= 1ull << i1;
}

void PauliStringPtr::toggle_z_bit(size_t k) {
    k = (k & ~0xFF)*stride256 | (k & 0xFF);
    size_t i0 = k >> 6;
    size_t i1 = k & 63;
    _z[i0] ^= 1ull << i1;
}

void PauliStringPtr::set_x_bit(size_t k, bool val) {
    k = (k & ~0xFF)*stride256 | (k & 0xFF);
    size_t i0 = k >> 6;
    size_t i1 = k & 63;
    _x[i0] &= ~(1ull << i1);
    _x[i0] ^= (uint64_t)val << i1;
}

void PauliStringPtr::set_z_bit(size_t k, bool val) {
    k = (k & ~0xFF)*stride256 | (k & 0xFF);
    size_t i0 = k >> 6;
    size_t i1 = k & 63;
    _z[i0] &= ~(1ull << i1);
    _z[i0] ^= (uint64_t)val << i1;
}

void PauliStringPtr::gather_into(PauliStringPtr &out, const std::vector<size_t> &in_indices) const {
    assert(in_indices.size() == out.size);
    for (size_t k_out = 0; k_out < out.size; k_out++) {
        size_t k_in = in_indices[k_out];
        out.set_x_bit(k_out, get_x_bit(k_in));
        out.set_z_bit(k_out, get_z_bit(k_in));
    }
}

void PauliStringPtr::scatter_into(PauliStringPtr &out, const std::vector<size_t> &out_indices) const {
    assert(size == out_indices.size());
    for (size_t k_in = 0; k_in < size; k_in++) {
        size_t k_out = out_indices[k_in];
        out.set_x_bit(k_out, get_x_bit(k_in));
        out.set_z_bit(k_out, get_z_bit(k_in));
    }
    out.bit_ptr_sign.toggle_if(bit_ptr_sign.get());
}

bool PauliStringVal::operator==(const PauliStringPtr &other) const {
    return ptr() == other;
}
bool PauliStringVal::operator!=(const PauliStringPtr &other) const {
    return ptr() != other;
}
<commit_msg>Saved one operation<commit_after>#include <iostream>
#include <immintrin.h>
#include <new>
#include <cassert>
#include <sstream>
#include "simd_util.h"
#include "pauli_string.h"
#include <cstring>
#include <cstdlib>
#include <bit>

PauliStringPtr::PauliStringPtr(
            size_t init_size,
            BitPtr init_sign,
            uint64_t *init_x,
            uint64_t *init_z,
            size_t init_stride256) :
        size(init_size),
        bit_ptr_sign(init_sign),
        _x(init_x),
        _z(init_z),
        stride256(init_stride256) {
}

PauliStringVal::PauliStringVal(size_t init_size) :
        val_sign(false),
        x_data(init_size),
        z_data(init_size) {
}

PauliStringVal::PauliStringVal(const PauliStringPtr &other) :
        val_sign(other.bit_ptr_sign.get()),
        x_data(other.size, other._x),
        z_data(other.size, other._z) {
}

PauliStringPtr PauliStringVal::ptr() const {
    return PauliStringPtr(*this);
}

std::string PauliStringVal::str() const {
    return ptr().str();
}

void PauliStringPtr::swap_with(PauliStringPtr &other) {
    assert(size == other.size);
    bit_ptr_sign.swap(other.bit_ptr_sign);
    auto x256 = (__m256i *)_x;
    auto z256 = (__m256i *)_z;
    auto ox256 = (__m256i *)other._x;
    auto oz256 = (__m256i *)other._z;
    auto end = &x256[num_words256() * stride256];
    while (x256 != end) {
        std::swap(*x256, *ox256);
        std::swap(*z256, *oz256);
        x256 += stride256;
        z256 += stride256;
        ox256 += other.stride256;
        oz256 += other.stride256;
    }
}

void PauliStringPtr::overwrite_with(const PauliStringPtr &other) {
    assert(size == other.size);
    bit_ptr_sign.set(other.bit_ptr_sign.get());
    auto x256 = (__m256i *)_x;
    auto z256 = (__m256i *)_z;
    auto ox256 = (__m256i *)other._x;
    auto oz256 = (__m256i *)other._z;
    auto end = &x256[num_words256() * stride256];
    while (x256 != end) {
        *x256 = *ox256;
        *z256 = *oz256;
        x256 += stride256;
        z256 += stride256;
        ox256 += other.stride256;
        oz256 += other.stride256;
    }
}

PauliStringVal& PauliStringVal::operator=(const PauliStringPtr &other) noexcept {
    (*this).~PauliStringVal();
    new(this) PauliStringVal(other);
    return *this;
}

PauliStringPtr::PauliStringPtr(const PauliStringVal &other) :
        size(other.x_data.num_bits),
        bit_ptr_sign(BitPtr((void *)&other.val_sign, 0)),
        _x(other.x_data.u64),
        _z(other.z_data.u64),
        stride256(1) {
}

uint8_t PauliStringPtr::log_i_scalar_byproduct(const PauliStringPtr &other) const {
    assert(size == other.size);
    union {__m256i u256; uint64_t u64[4]; } cnt1 {};
    union {__m256i u256; uint64_t u64[4]; } cnt2 {};
    auto x256 = (__m256i *)_x;
    auto z256 = (__m256i *)_z;
    auto ox256 = (__m256i *)other._x;
    auto oz256 = (__m256i *)other._z;
    auto end = &x256[num_words256() * stride256];
    while (x256 != end) {
        // Load into registers.
        auto x1 = *x256;
        auto z2 = *oz256;
        auto z1 = *z256;
        auto x2 = *ox256;

        auto t1 = x1 & z2;
        auto t2 = x2 & z1;
        // At each bit position: do the Paulis anti-commute?
        auto a = t1 ^ t2;
        // At each bit position: do the Paulis anti-commute and produce a -i instead of a +i?
        auto b = ((x1 ^ z2) & t2) ^ _mm256_andnot_si256(z1, _mm256_andnot_si256(x2, t1));
        // At each bit position: `count += forward - backward` where `backward=b`, `forward=a^b`, `count=cnt1 + 2*cnt2`.
        cnt2.u256 ^= (cnt1.u256 ^ b) & a;
        cnt1.u256 ^= a;

        // Move along.
        x256 += stride256;
        z256 += stride256;
        ox256 += other.stride256;
        oz256 += other.stride256;
    }

    // Combine final anti-commutation phase tally (mod 4).
    size_t s = 0;
    for (size_t k = 0; k < 4; k++) {
        s += (uint8_t) std::popcount(cnt1.u64[k]);
        s ^= (uint8_t) std::popcount(cnt2.u64[k]) << 1;
    }
    return s & 3;
}

std::string PauliStringPtr::str() const {
    std::stringstream ss;
    ss << *this;
    return ss.str();
}

bool PauliStringPtr::operator==(const PauliStringPtr &other) const {
    if (size != other.size || bit_ptr_sign.get() != other.bit_ptr_sign.get()) {
        return false;
    }
    __m256i acc {};
    auto x256 = (__m256i *)_x;
    auto z256 = (__m256i *)_z;
    auto ox256 = (__m256i *)other._x;
    auto oz256 = (__m256i *)other._z;
    auto end = &x256[num_words256() * stride256];
    while (x256 != end) {
        acc |= *x256 ^ *ox256;
        acc |= *z256 ^ *oz256;
        x256 += stride256;
        z256 += stride256;
        ox256 += other.stride256;
        oz256 += other.stride256;
    }

    auto acc64 = (uint64_t *)&acc;
    for (size_t k = 0; k < 4; k++) {
        if (acc64[k]) {
            return false;
        }
    }

    return true;
}

bool PauliStringPtr::operator!=(const PauliStringPtr &other) const {
    return !(*this == other);
}

std::ostream &operator<<(std::ostream &out, const PauliStringVal &ps) {
    return out << ps.ptr();
}

size_t PauliStringPtr::num_words256() const {
    return ceil256(size) >> 8;
}

std::ostream &operator<<(std::ostream &out, const PauliStringPtr &ps) {
    out << (ps.bit_ptr_sign.get() ? '-' : '+');
    auto x256 = (__m256i *)ps._x;
    auto z256 = (__m256i *)ps._z;
    auto end = &x256[ps.num_words256() * ps.stride256];
    size_t remaining = ps.size;
    while (x256 != end) {
        auto xs = m256i_to_bits(*x256);
        auto zs = m256i_to_bits(*z256);
        for (int j = 0; j < 256 && remaining; j++) {
            out << "_XZY"[xs[j] + 2 * zs[j]];
            remaining--;
        }
        x256 += ps.stride256;
        z256 += ps.stride256;
    }
    return out;
}

PauliStringVal PauliStringVal::from_pattern(bool sign, size_t size, const std::function<char(size_t)> &func) {
    PauliStringVal result(size);
    result.val_sign = sign;
    for (size_t i = 0; i < size; i++) {
        char c = func(i);
        bool x;
        bool z;
        if (c == 'X') {
            x = true;
            z = false;
        } else if (c == 'Y') {
            x = true;
            z = true;
        } else if (c == 'Z') {
            x = false;
            z = true;
        } else if (c == '_' || c == 'I') {
            x = false;
            z = false;
        } else {
            throw std::runtime_error("Unrecognized pauli character. " + std::to_string(c));
        }
        result.x_data.u64[i / 64] ^= (uint64_t)x << (i & 63);
        result.z_data.u64[i / 64] ^= (uint64_t)z << (i & 63);
    }
    return result;
}

PauliStringVal PauliStringVal::from_str(const char *text) {
    auto sign = text[0] == '-';
    if (text[0] == '+' || text[0] == '-') {
        text++;
    }
    return PauliStringVal::from_pattern(sign, strlen(text), [&](size_t i){ return text[i]; });
}

PauliStringVal PauliStringVal::identity(size_t size) {
    return PauliStringVal(size);
}

PauliStringPtr& PauliStringPtr::operator*=(const PauliStringPtr& rhs) {
    uint8_t log_i = inplace_right_mul_with_scalar_output(rhs);
    assert((log_i & 1) == 0);
    bit_ptr_sign.toggle_if(log_i & 2);
    return *this;
}

uint8_t PauliStringPtr::inplace_right_mul_with_scalar_output(const PauliStringPtr& rhs) {
    uint8_t result = log_i_scalar_byproduct(rhs);
    result ^= (uint8_t)rhs.bit_ptr_sign.get() << 1;
    auto x256 = (__m256i *)_x;
    auto z256 = (__m256i *)_z;
    auto ox256 = (__m256i *)rhs._x;
    auto oz256 = (__m256i *)rhs._z;
    auto end = &x256[num_words256() * stride256];
    while (x256 != end) {
        *x256 ^= *ox256;
        x256 += stride256;
        ox256 += rhs.stride256;
    }
    end = &z256[num_words256() * stride256];
    while (z256 != end) {
        *z256 ^= *oz256;
        z256 += stride256;
        oz256 += rhs.stride256;
    }
    return result;
}

bool PauliStringPtr::get_x_bit(size_t k) const {
    k = (k & ~0xFF)*stride256 | (k & 0xFF);
    size_t i0 = k >> 6;
    size_t i1 = k & 63;
    return ((_x[i0] >> i1) & 1) != 0;
}

bool PauliStringPtr::get_z_bit(size_t k) const {
    k = (k & ~0xFF)*stride256 | (k & 0xFF);
    size_t i0 = k >> 6;
    size_t i1 = k & 63;
    return ((_z[i0] >> i1) & 1) != 0;
}

void PauliStringPtr::toggle_x_bit(size_t k) {
    k = (k & ~0xFF)*stride256 | (k & 0xFF);
    size_t i0 = k >> 6;
    size_t i1 = k & 63;
    _x[i0] ^= 1ull << i1;
}

void PauliStringPtr::toggle_z_bit(size_t k) {
    k = (k & ~0xFF)*stride256 | (k & 0xFF);
    size_t i0 = k >> 6;
    size_t i1 = k & 63;
    _z[i0] ^= 1ull << i1;
}

void PauliStringPtr::set_x_bit(size_t k, bool val) {
    k = (k & ~0xFF)*stride256 | (k & 0xFF);
    size_t i0 = k >> 6;
    size_t i1 = k & 63;
    _x[i0] &= ~(1ull << i1);
    _x[i0] ^= (uint64_t)val << i1;
}

void PauliStringPtr::set_z_bit(size_t k, bool val) {
    k = (k & ~0xFF)*stride256 | (k & 0xFF);
    size_t i0 = k >> 6;
    size_t i1 = k & 63;
    _z[i0] &= ~(1ull << i1);
    _z[i0] ^= (uint64_t)val << i1;
}

void PauliStringPtr::gather_into(PauliStringPtr &out, const std::vector<size_t> &in_indices) const {
    assert(in_indices.size() == out.size);
    for (size_t k_out = 0; k_out < out.size; k_out++) {
        size_t k_in = in_indices[k_out];
        out.set_x_bit(k_out, get_x_bit(k_in));
        out.set_z_bit(k_out, get_z_bit(k_in));
    }
}

void PauliStringPtr::scatter_into(PauliStringPtr &out, const std::vector<size_t> &out_indices) const {
    assert(size == out_indices.size());
    for (size_t k_in = 0; k_in < size; k_in++) {
        size_t k_out = out_indices[k_in];
        out.set_x_bit(k_out, get_x_bit(k_in));
        out.set_z_bit(k_out, get_z_bit(k_in));
    }
    out.bit_ptr_sign.toggle_if(bit_ptr_sign.get());
}

bool PauliStringVal::operator==(const PauliStringPtr &other) const {
    return ptr() == other;
}
bool PauliStringVal::operator!=(const PauliStringPtr &other) const {
    return ptr() != other;
}
<|endoftext|>


<commit_before>// Copyright (c) 2019 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 <chainparams.h>
#include <compressor.h>
#include <core_io.h>
#include <core_memusage.h>
#include <policy/policy.h>
#include <pubkey.h>
#include <script/descriptor.h>
#include <script/script.h>
#include <script/sign.h>
#include <script/signingprovider.h>
#include <script/standard.h>
#include <streams.h>
#include <test/fuzz/fuzz.h>
#include <univalue.h>
#include <util/memory.h>

void initialize()
{
    // Fuzzers using pubkey must hold an ECCVerifyHandle.
    static const auto verify_handle = MakeUnique<ECCVerifyHandle>();

    SelectParams(CBaseChainParams::REGTEST);
}

void test_one_input(const std::vector<uint8_t>& buffer)
{
    const CScript script(buffer.begin(), buffer.end());

    std::vector<unsigned char> compressed;
    if (CompressScript(script, compressed)) {
        const unsigned int size = compressed[0];
        compressed.erase(compressed.begin());
        assert(size >= 0 && size <= 5);
        CScript decompressed_script;
        const bool ok = DecompressScript(decompressed_script, size, compressed);
        assert(ok);
        assert(script == decompressed_script);
    }

    CTxDestination address;
    (void)ExtractDestination(script, address);

    txnouttype type_ret;
    std::vector<CTxDestination> addresses;
    int required_ret;
    (void)ExtractDestinations(script, type_ret, addresses, required_ret);

    (void)GetScriptForWitness(script);

    const FlatSigningProvider signing_provider;
    (void)InferDescriptor(script, signing_provider);

    (void)IsSegWitOutput(signing_provider, script);

    (void)IsSolvable(signing_provider, script);

    txnouttype which_type;
    (void)IsStandard(script, which_type);

    (void)RecursiveDynamicUsage(script);

    std::vector<std::vector<unsigned char>> solutions;
    (void)Solver(script, solutions);

    (void)script.HasValidOps();
    (void)script.IsPayToScriptHash();
    (void)script.IsPayToWitnessScriptHash();
    (void)script.IsPushOnly();
    (void)script.IsUnspendable();
    (void)script.GetSigOpCount(/* fAccurate= */ false);

    (void)FormatScript(script);
    (void)ScriptToAsmStr(script, false);
    (void)ScriptToAsmStr(script, true);

    UniValue o1(UniValue::VOBJ);
    ScriptPubKeyToUniv(script, o1, true);
    UniValue o2(UniValue::VOBJ);
    ScriptPubKeyToUniv(script, o2, false);
    UniValue o3(UniValue::VOBJ);
    ScriptToUniv(script, o3, true);
    UniValue o4(UniValue::VOBJ);
    ScriptToUniv(script, o4, false);
}
<commit_msg>tests: Increase fuzzing coverage of DecompressScript(...)<commit_after>// Copyright (c) 2019 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 <chainparams.h>
#include <compressor.h>
#include <core_io.h>
#include <core_memusage.h>
#include <policy/policy.h>
#include <pubkey.h>
#include <script/descriptor.h>
#include <script/script.h>
#include <script/sign.h>
#include <script/signingprovider.h>
#include <script/standard.h>
#include <streams.h>
#include <test/fuzz/FuzzedDataProvider.h>
#include <test/fuzz/fuzz.h>
#include <test/fuzz/util.h>
#include <univalue.h>
#include <util/memory.h>

void initialize()
{
    // Fuzzers using pubkey must hold an ECCVerifyHandle.
    static const auto verify_handle = MakeUnique<ECCVerifyHandle>();

    SelectParams(CBaseChainParams::REGTEST);
}

void test_one_input(const std::vector<uint8_t>& buffer)
{
    const CScript script(buffer.begin(), buffer.end());

    std::vector<unsigned char> compressed;
    if (CompressScript(script, compressed)) {
        const unsigned int size = compressed[0];
        compressed.erase(compressed.begin());
        assert(size >= 0 && size <= 5);
        CScript decompressed_script;
        const bool ok = DecompressScript(decompressed_script, size, compressed);
        assert(ok);
        assert(script == decompressed_script);
    }

    CTxDestination address;
    (void)ExtractDestination(script, address);

    txnouttype type_ret;
    std::vector<CTxDestination> addresses;
    int required_ret;
    (void)ExtractDestinations(script, type_ret, addresses, required_ret);

    (void)GetScriptForWitness(script);

    const FlatSigningProvider signing_provider;
    (void)InferDescriptor(script, signing_provider);

    (void)IsSegWitOutput(signing_provider, script);

    (void)IsSolvable(signing_provider, script);

    txnouttype which_type;
    (void)IsStandard(script, which_type);

    (void)RecursiveDynamicUsage(script);

    std::vector<std::vector<unsigned char>> solutions;
    (void)Solver(script, solutions);

    (void)script.HasValidOps();
    (void)script.IsPayToScriptHash();
    (void)script.IsPayToWitnessScriptHash();
    (void)script.IsPushOnly();
    (void)script.IsUnspendable();
    (void)script.GetSigOpCount(/* fAccurate= */ false);

    (void)FormatScript(script);
    (void)ScriptToAsmStr(script, false);
    (void)ScriptToAsmStr(script, true);

    UniValue o1(UniValue::VOBJ);
    ScriptPubKeyToUniv(script, o1, true);
    UniValue o2(UniValue::VOBJ);
    ScriptPubKeyToUniv(script, o2, false);
    UniValue o3(UniValue::VOBJ);
    ScriptToUniv(script, o3, true);
    UniValue o4(UniValue::VOBJ);
    ScriptToUniv(script, o4, false);

    {
        FuzzedDataProvider fuzzed_data_provider(buffer.data(), buffer.size());
        const std::vector<uint8_t> bytes = ConsumeRandomLengthByteVector(fuzzed_data_provider);
        // DecompressScript(..., ..., bytes) is not guaranteed to be defined if bytes.size() <= 23.
        if (bytes.size() >= 24) {
            CScript decompressed_script;
            DecompressScript(decompressed_script, fuzzed_data_provider.ConsumeIntegral<unsigned int>(), bytes);
        }
    }
}
<|endoftext|>


<commit_before>#include "include/rados/librados.h"
#include "test/rados-api/test.h"

#include <errno.h>
#include "gtest/gtest.h"

TEST(LibRadosIo, SimpleWrite) {
  char buf[128];
  rados_t cluster;
  rados_ioctx_t ioctx;
  std::string pool_name = get_temp_pool_name();
  ASSERT_EQ(0, create_one_pool(pool_name, &cluster));
  rados_ioctx_create(cluster, pool_name.c_str(), &ioctx);
  memset(buf, 0xcc, sizeof(buf));
  ASSERT_EQ((int)sizeof(buf), rados_write(ioctx, "foo", buf, sizeof(buf), 0));
  rados_ioctx_destroy(ioctx);
  ASSERT_EQ(0, destroy_one_pool(pool_name, &cluster));
}

TEST(LibRadosIo, RoundTrip) {
  char buf[128];
  char buf2[128];
  rados_t cluster;
  rados_ioctx_t ioctx;
  std::string pool_name = get_temp_pool_name();
  ASSERT_EQ(0, create_one_pool(pool_name, &cluster));
  rados_ioctx_create(cluster, pool_name.c_str(), &ioctx);
  memset(buf, 0xcc, sizeof(buf));
  ASSERT_EQ((int)sizeof(buf), rados_write(ioctx, "foo", buf, sizeof(buf), 0));
  memset(buf2, 0, sizeof(buf2));
  ASSERT_EQ((int)sizeof(buf2), rados_read(ioctx, "foo", buf2, sizeof(buf2), 0));
  ASSERT_EQ(0, memcmp(buf, buf2, sizeof(buf)));
  rados_ioctx_destroy(ioctx);
  ASSERT_EQ(0, destroy_one_pool(pool_name, &cluster));
}

TEST(LibRadosIo, OverlappingWriteRoundTrip) {
  char buf[128];
  char buf2[64];
  char buf3[128];
  rados_t cluster;
  rados_ioctx_t ioctx;
  std::string pool_name = get_temp_pool_name();
  ASSERT_EQ(0, create_one_pool(pool_name, &cluster));
  rados_ioctx_create(cluster, pool_name.c_str(), &ioctx);
  memset(buf, 0xcc, sizeof(buf));
  ASSERT_EQ((int)sizeof(buf), rados_write(ioctx, "foo", buf, sizeof(buf), 0));
  memset(buf2, 0xdd, sizeof(buf2));
  ASSERT_EQ((int)sizeof(buf2), rados_write(ioctx, "foo", buf2, sizeof(buf2), 0));
  memset(buf3, 0xdd, sizeof(buf3));
  ASSERT_EQ((int)sizeof(buf3), rados_read(ioctx, "foo", buf3, sizeof(buf3), 0));
  ASSERT_EQ(0, memcmp(buf3, buf2, sizeof(buf2)));
  ASSERT_EQ(0, memcmp(buf3 + sizeof(buf2), buf, sizeof(buf) - sizeof(buf2)));
  rados_ioctx_destroy(ioctx);
  ASSERT_EQ(0, destroy_one_pool(pool_name, &cluster));
}

TEST(LibRadosIo, WriteFullRoundTrip) {
  char buf[128];
  char buf2[64];
  char buf3[128];
  rados_t cluster;
  rados_ioctx_t ioctx;
  std::string pool_name = get_temp_pool_name();
  ASSERT_EQ(0, create_one_pool(pool_name, &cluster));
  rados_ioctx_create(cluster, pool_name.c_str(), &ioctx);
  memset(buf, 0xcc, sizeof(buf));
  ASSERT_EQ((int)sizeof(buf), rados_write(ioctx, "foo", buf, sizeof(buf), 0));
  memset(buf2, 0xdd, sizeof(buf2));
  ASSERT_EQ(0, rados_write_full(ioctx, "foo", buf2, sizeof(buf2), 0));
  memset(buf3, 0xdd, sizeof(buf3));
  ASSERT_EQ((int)sizeof(buf2), rados_read(ioctx, "foo", buf3, sizeof(buf3), 0));
  ASSERT_EQ(0, memcmp(buf2, buf2, sizeof(buf2)));
  rados_ioctx_destroy(ioctx);
  ASSERT_EQ(0, destroy_one_pool(pool_name, &cluster));
}

TEST(LibRadosIo, AppendRoundTrip) {
  char buf[64];
  char buf2[64];
  char buf3[sizeof(buf) + sizeof(buf2)];
  rados_t cluster;
  rados_ioctx_t ioctx;
  std::string pool_name = get_temp_pool_name();
  ASSERT_EQ(0, create_one_pool(pool_name, &cluster));
  rados_ioctx_create(cluster, pool_name.c_str(), &ioctx);
  memset(buf, 0xde, sizeof(buf));
  ASSERT_EQ((int)sizeof(buf), rados_append(ioctx, "foo", buf, sizeof(buf)));
  memset(buf2, 0xad, sizeof(buf2));
  ASSERT_EQ((int)sizeof(buf2), rados_append(ioctx, "foo", buf2, sizeof(buf2)));
  memset(buf3, 0, sizeof(buf3));
  ASSERT_EQ((int)sizeof(buf3), rados_read(ioctx, "foo", buf3, sizeof(buf3), 0));
  ASSERT_EQ(0, memcmp(buf3, buf, sizeof(buf)));
  ASSERT_EQ(0, memcmp(buf3 + sizeof(buf), buf2, sizeof(buf2)));
  rados_ioctx_destroy(ioctx);
  ASSERT_EQ(0, destroy_one_pool(pool_name, &cluster));
}

TEST(LibRadosIo, TruncTest) {
  char buf[128];
  char buf2[sizeof(buf)];
  rados_t cluster;
  rados_ioctx_t ioctx;
  std::string pool_name = get_temp_pool_name();
  ASSERT_EQ(0, create_one_pool(pool_name, &cluster));
  rados_ioctx_create(cluster, pool_name.c_str(), &ioctx);
  memset(buf, 0xaa, sizeof(buf));
  ASSERT_EQ((int)sizeof(buf), rados_append(ioctx, "foo", buf, sizeof(buf)));
  ASSERT_EQ(0, rados_trunc(ioctx, "foo", sizeof(buf) / 2));
  memset(buf2, 0, sizeof(buf2));
  ASSERT_EQ((int)(sizeof(buf)/2), rados_read(ioctx, "foo", buf2, sizeof(buf2), 0));
  ASSERT_EQ(0, memcmp(buf, buf2, sizeof(buf)/2));
  rados_ioctx_destroy(ioctx);
  ASSERT_EQ(0, destroy_one_pool(pool_name, &cluster));
}

TEST(LibRadosIo, RemoveTest) {
  char buf[128];
  char buf2[sizeof(buf)];
  rados_t cluster;
  rados_ioctx_t ioctx;
  std::string pool_name = get_temp_pool_name();
  ASSERT_EQ(0, create_one_pool(pool_name, &cluster));
  rados_ioctx_create(cluster, pool_name.c_str(), &ioctx);
  memset(buf, 0xaa, sizeof(buf));
  ASSERT_EQ((int)sizeof(buf), rados_append(ioctx, "foo", buf, sizeof(buf)));
  ASSERT_EQ(0, rados_remove(ioctx, "foo"));
  memset(buf2, 0, sizeof(buf2));
  ASSERT_EQ(-ENOENT, rados_read(ioctx, "foo", buf2, sizeof(buf2), 0));
  rados_ioctx_destroy(ioctx);
  ASSERT_EQ(0, destroy_one_pool(pool_name, &cluster));
}
<commit_msg>test/rados-api/io: add XattrsRoundTrip<commit_after>#include "include/rados/librados.h"
#include "test/rados-api/test.h"

#include <errno.h>
#include "gtest/gtest.h"

TEST(LibRadosIo, SimpleWrite) {
  char buf[128];
  rados_t cluster;
  rados_ioctx_t ioctx;
  std::string pool_name = get_temp_pool_name();
  ASSERT_EQ(0, create_one_pool(pool_name, &cluster));
  rados_ioctx_create(cluster, pool_name.c_str(), &ioctx);
  memset(buf, 0xcc, sizeof(buf));
  ASSERT_EQ((int)sizeof(buf), rados_write(ioctx, "foo", buf, sizeof(buf), 0));
  rados_ioctx_destroy(ioctx);
  ASSERT_EQ(0, destroy_one_pool(pool_name, &cluster));
}

TEST(LibRadosIo, RoundTrip) {
  char buf[128];
  char buf2[128];
  rados_t cluster;
  rados_ioctx_t ioctx;
  std::string pool_name = get_temp_pool_name();
  ASSERT_EQ(0, create_one_pool(pool_name, &cluster));
  rados_ioctx_create(cluster, pool_name.c_str(), &ioctx);
  memset(buf, 0xcc, sizeof(buf));
  ASSERT_EQ((int)sizeof(buf), rados_write(ioctx, "foo", buf, sizeof(buf), 0));
  memset(buf2, 0, sizeof(buf2));
  ASSERT_EQ((int)sizeof(buf2), rados_read(ioctx, "foo", buf2, sizeof(buf2), 0));
  ASSERT_EQ(0, memcmp(buf, buf2, sizeof(buf)));
  rados_ioctx_destroy(ioctx);
  ASSERT_EQ(0, destroy_one_pool(pool_name, &cluster));
}

TEST(LibRadosIo, OverlappingWriteRoundTrip) {
  char buf[128];
  char buf2[64];
  char buf3[128];
  rados_t cluster;
  rados_ioctx_t ioctx;
  std::string pool_name = get_temp_pool_name();
  ASSERT_EQ(0, create_one_pool(pool_name, &cluster));
  rados_ioctx_create(cluster, pool_name.c_str(), &ioctx);
  memset(buf, 0xcc, sizeof(buf));
  ASSERT_EQ((int)sizeof(buf), rados_write(ioctx, "foo", buf, sizeof(buf), 0));
  memset(buf2, 0xdd, sizeof(buf2));
  ASSERT_EQ((int)sizeof(buf2), rados_write(ioctx, "foo", buf2, sizeof(buf2), 0));
  memset(buf3, 0xdd, sizeof(buf3));
  ASSERT_EQ((int)sizeof(buf3), rados_read(ioctx, "foo", buf3, sizeof(buf3), 0));
  ASSERT_EQ(0, memcmp(buf3, buf2, sizeof(buf2)));
  ASSERT_EQ(0, memcmp(buf3 + sizeof(buf2), buf, sizeof(buf) - sizeof(buf2)));
  rados_ioctx_destroy(ioctx);
  ASSERT_EQ(0, destroy_one_pool(pool_name, &cluster));
}

TEST(LibRadosIo, WriteFullRoundTrip) {
  char buf[128];
  char buf2[64];
  char buf3[128];
  rados_t cluster;
  rados_ioctx_t ioctx;
  std::string pool_name = get_temp_pool_name();
  ASSERT_EQ(0, create_one_pool(pool_name, &cluster));
  rados_ioctx_create(cluster, pool_name.c_str(), &ioctx);
  memset(buf, 0xcc, sizeof(buf));
  ASSERT_EQ((int)sizeof(buf), rados_write(ioctx, "foo", buf, sizeof(buf), 0));
  memset(buf2, 0xdd, sizeof(buf2));
  ASSERT_EQ(0, rados_write_full(ioctx, "foo", buf2, sizeof(buf2), 0));
  memset(buf3, 0xdd, sizeof(buf3));
  ASSERT_EQ((int)sizeof(buf2), rados_read(ioctx, "foo", buf3, sizeof(buf3), 0));
  ASSERT_EQ(0, memcmp(buf2, buf2, sizeof(buf2)));
  rados_ioctx_destroy(ioctx);
  ASSERT_EQ(0, destroy_one_pool(pool_name, &cluster));
}

TEST(LibRadosIo, AppendRoundTrip) {
  char buf[64];
  char buf2[64];
  char buf3[sizeof(buf) + sizeof(buf2)];
  rados_t cluster;
  rados_ioctx_t ioctx;
  std::string pool_name = get_temp_pool_name();
  ASSERT_EQ(0, create_one_pool(pool_name, &cluster));
  rados_ioctx_create(cluster, pool_name.c_str(), &ioctx);
  memset(buf, 0xde, sizeof(buf));
  ASSERT_EQ((int)sizeof(buf), rados_append(ioctx, "foo", buf, sizeof(buf)));
  memset(buf2, 0xad, sizeof(buf2));
  ASSERT_EQ((int)sizeof(buf2), rados_append(ioctx, "foo", buf2, sizeof(buf2)));
  memset(buf3, 0, sizeof(buf3));
  ASSERT_EQ((int)sizeof(buf3), rados_read(ioctx, "foo", buf3, sizeof(buf3), 0));
  ASSERT_EQ(0, memcmp(buf3, buf, sizeof(buf)));
  ASSERT_EQ(0, memcmp(buf3 + sizeof(buf), buf2, sizeof(buf2)));
  rados_ioctx_destroy(ioctx);
  ASSERT_EQ(0, destroy_one_pool(pool_name, &cluster));
}

TEST(LibRadosIo, TruncTest) {
  char buf[128];
  char buf2[sizeof(buf)];
  rados_t cluster;
  rados_ioctx_t ioctx;
  std::string pool_name = get_temp_pool_name();
  ASSERT_EQ(0, create_one_pool(pool_name, &cluster));
  rados_ioctx_create(cluster, pool_name.c_str(), &ioctx);
  memset(buf, 0xaa, sizeof(buf));
  ASSERT_EQ((int)sizeof(buf), rados_append(ioctx, "foo", buf, sizeof(buf)));
  ASSERT_EQ(0, rados_trunc(ioctx, "foo", sizeof(buf) / 2));
  memset(buf2, 0, sizeof(buf2));
  ASSERT_EQ((int)(sizeof(buf)/2), rados_read(ioctx, "foo", buf2, sizeof(buf2), 0));
  ASSERT_EQ(0, memcmp(buf, buf2, sizeof(buf)/2));
  rados_ioctx_destroy(ioctx);
  ASSERT_EQ(0, destroy_one_pool(pool_name, &cluster));
}

TEST(LibRadosIo, RemoveTest) {
  char buf[128];
  char buf2[sizeof(buf)];
  rados_t cluster;
  rados_ioctx_t ioctx;
  std::string pool_name = get_temp_pool_name();
  ASSERT_EQ(0, create_one_pool(pool_name, &cluster));
  rados_ioctx_create(cluster, pool_name.c_str(), &ioctx);
  memset(buf, 0xaa, sizeof(buf));
  ASSERT_EQ((int)sizeof(buf), rados_append(ioctx, "foo", buf, sizeof(buf)));
  ASSERT_EQ(0, rados_remove(ioctx, "foo"));
  memset(buf2, 0, sizeof(buf2));
  ASSERT_EQ(-ENOENT, rados_read(ioctx, "foo", buf2, sizeof(buf2), 0));
  rados_ioctx_destroy(ioctx);
  ASSERT_EQ(0, destroy_one_pool(pool_name, &cluster));
}

TEST(LibRadosIo, XattrsRoundTrip) {
  char buf[128];
  char attr1[] = "attr1";
  char attr1_buf[] = "foo bar baz";
  rados_t cluster;
  rados_ioctx_t ioctx;
  std::string pool_name = get_temp_pool_name();
  ASSERT_EQ(0, create_one_pool(pool_name, &cluster));
  rados_ioctx_create(cluster, pool_name.c_str(), &ioctx);
  memset(buf, 0xaa, sizeof(buf));
  ASSERT_EQ((int)sizeof(buf), rados_append(ioctx, "foo", buf, sizeof(buf)));
  ASSERT_EQ(-ENODATA, rados_getxattr(ioctx, "foo", attr1, buf, sizeof(buf)));
  ASSERT_EQ((int)sizeof(attr1_buf),
      rados_setxattr(ioctx, "foo", attr1, attr1_buf, sizeof(attr1_buf)));
  ASSERT_EQ((int)sizeof(attr1_buf),
	    rados_getxattr(ioctx, "foo", attr1, buf, sizeof(buf)));
  ASSERT_EQ(0, memcmp(attr1_buf, buf, sizeof(attr1_buf)));
  rados_ioctx_destroy(ioctx);
  ASSERT_EQ(0, destroy_one_pool(pool_name, &cluster));
}
<|endoftext|>