<commit_before>#include <filterfalse.hpp>

#include "helpers.hpp"

#include <vector>
#include <string>
#include <iterator>

#include "catch.hpp"

using iter::filterfalse;

using Vec = const std::vector<int>;

namespace {
    bool less_than_five(int i) {
        return i < 5;
    }

    class LessThanValue {
        private:
            int compare_val;

        public:
            LessThanValue(int v) : compare_val(v) { }

            bool operator() (int i) {
                return i < this->compare_val;
            }
    };
}

TEST_CASE("filterfalse: handles different functor types", "[filterfalse]") {
    Vec ns = {1,2, 5,6, 3,1, 7, -1, 5};
    Vec vc = {5,6,7,5};
    SECTION("with function pointer") {
        auto f = filterfalse(less_than_five, ns);
        Vec v(std::begin(f), std::end(f));
        REQUIRE( v == vc );
    }

    SECTION("with callable object") {
        auto f = filterfalse(LessThanValue{5}, ns);
        Vec v(std::begin(f), std::end(f));
        REQUIRE( v == vc );
    }

    SECTION("with lambda") {
        auto ltf = [](int i) {return i < 5;};
        auto f = filterfalse(ltf, ns);
        Vec v(std::begin(f), std::end(f));
        REQUIRE( v == vc );
    }
}

TEST_CASE("filterfalse: using identity", "[filterfalse]") {
    Vec ns{0, 1, 2, 0, 3, 0, 0, 0, 4, 5, 0};
    auto f = filterfalse(ns);
    Vec v(std::begin(f), std::end(f));

    Vec vc = {0, 0, 0, 0, 0, 0};
    REQUIRE( v == vc );
}

TEST_CASE("filterfalse: binds to lvalues, moves rvales", "[filterfalse]") {
    itertest::BasicIterable<int> bi{1,2,3,4};

    SECTION("one-arg binds to lvalues") {
        filterfalse(bi);
        REQUIRE_FALSE(bi.was_moved_from());
    }

    SECTION("two-arg binds to lvalues") {
        filterfalse(less_than_five, bi);
        REQUIRE_FALSE(bi.was_moved_from());
    }

    SECTION("one-arg moves rvalues") {
        filterfalse(std::move(bi));
        REQUIRE(bi.was_moved_from());
    }

    SECTION("two-arg moves rvalues") {
        filterfalse(less_than_five, std::move(bi));
        REQUIRE(bi.was_moved_from());
    }
}

TEST_CASE("filterfalse: all elements pass predicate", "[filterfalse]") {
    Vec ns{0,1,2,3,4};
    auto f = filterfalse(less_than_five, ns);

    REQUIRE( std::begin(f) == std::end(f) );
}

TEST_CASE("filterfalse: iterator meets requirements", "[filterfalse]") {
    std::string s{};
    auto c = filterfalse([]{return true;}, s);
    REQUIRE( itertest::IsIterator<decltype(std::begin(c))>::value );
}
<commit_msg>tests filterfalse impl has move ctor only<commit_after>#include <filterfalse.hpp>

#include "helpers.hpp"

#include <vector>
#include <string>
#include <iterator>

#include "catch.hpp"

using iter::filterfalse;

using Vec = const std::vector<int>;

namespace {
  bool less_than_five(int i) {
    return i < 5;
  }

  class LessThanValue {
   private:
    int compare_val;

   public:
    LessThanValue(int v) : compare_val(v) {}

    bool operator()(int i) {
      return i < this->compare_val;
    }
  };
}

TEST_CASE("filterfalse: handles different functor types", "[filterfalse]") {
  Vec ns = {1, 2, 5, 6, 3, 1, 7, -1, 5};
  Vec vc = {5, 6, 7, 5};
  SECTION("with function pointer") {
    auto f = filterfalse(less_than_five, ns);
    Vec v(std::begin(f), std::end(f));
    REQUIRE(v == vc);
  }

  SECTION("with callable object") {
    auto f = filterfalse(LessThanValue{5}, ns);
    Vec v(std::begin(f), std::end(f));
    REQUIRE(v == vc);
  }

  SECTION("with lambda") {
    auto ltf = [](int i) { return i < 5; };
    auto f = filterfalse(ltf, ns);
    Vec v(std::begin(f), std::end(f));
    REQUIRE(v == vc);
  }
}

TEST_CASE("filterfalse: using identity", "[filterfalse]") {
  Vec ns{0, 1, 2, 0, 3, 0, 0, 0, 4, 5, 0};
  auto f = filterfalse(ns);
  Vec v(std::begin(f), std::end(f));

  Vec vc = {0, 0, 0, 0, 0, 0};
  REQUIRE(v == vc);
}

TEST_CASE("filterfalse: binds to lvalues, moves rvales", "[filterfalse]") {
  itertest::BasicIterable<int> bi{1, 2, 3, 4};

  SECTION("one-arg binds to lvalues") {
    filterfalse(bi);
    REQUIRE_FALSE(bi.was_moved_from());
  }

  SECTION("two-arg binds to lvalues") {
    filterfalse(less_than_five, bi);
    REQUIRE_FALSE(bi.was_moved_from());
  }

  SECTION("one-arg moves rvalues") {
    filterfalse(std::move(bi));
    REQUIRE(bi.was_moved_from());
  }

  SECTION("two-arg moves rvalues") {
    filterfalse(less_than_five, std::move(bi));
    REQUIRE(bi.was_moved_from());
  }
}

TEST_CASE("filterfalse: all elements pass predicate", "[filterfalse]") {
  Vec ns{0, 1, 2, 3, 4};
  auto f = filterfalse(less_than_five, ns);

  REQUIRE(std::begin(f) == std::end(f));
}

TEST_CASE("filterfalse: iterator meets requirements", "[filterfalse]") {
  std::string s{};
  auto c = filterfalse([] { return true; }, s);
  REQUIRE(itertest::IsIterator<decltype(std::begin(c))>::value);
}

template <typename T, typename U>
using ImpT = decltype(filterfalse(std::declval<T>(), std::declval<U>()));
TEST_CASE("filterfalse: has correct ctor and assign ops", "[filterfalse]") {
  using T1 = ImpT<bool (*)(char c), std::string&>;
  auto lam = [](char) { return false; };
  using T2 = ImpT<decltype(lam), std::string>;
  REQUIRE(itertest::IsMoveConstructibleOnly<T1>::value);
  REQUIRE(itertest::IsMoveConstructibleOnly<T2>::value);
}
<|endoftext|>


<commit_before>#include <map>
#include "simdjson/jsonstream.h"
#include "simdjson/isadetection.h"
#include "jsoncharutils.h"

using namespace simdjson;
void find_the_best_supported_implementation();

typedef int (*stage1_functype)(const char *buf, size_t len, ParsedJson &pj, bool streaming);
typedef int (*stage2_functype)(const char *buf, size_t len, ParsedJson &pj, size_t &next_json);

stage1_functype best_stage1;
stage2_functype best_stage2;

JsonStream::JsonStream(const char *buf, size_t len, size_t batchSize)
        : _buf(buf), _len(len), _batch_size(batchSize) {
    find_the_best_supported_implementation();
}

JsonStream::~JsonStream() {
#ifdef SIMDJSON_THREADS_ENABLED
    if(stage_1_thread.joinable()) {
      stage_1_thread.join();
    }
#endif
}

void JsonStream::set_new_buffer(const char *buf, size_t len) {
#ifdef SIMDJSON_THREADS_ENABLED
    if(stage_1_thread.joinable()) {
      stage_1_thread.join();
    }
#endif
    this->_buf = buf;
    this->_len = len;
    _batch_size = 0;
    _batch_size = 0;
    next_json = 0;
    current_buffer_loc = 0;
    n_parsed_docs = 0;
    error_on_last_attempt= false;
    load_next_batch = true;
}

// todo: this code is too complicated, it should be greatly simplified
int JsonStream::json_parse(ParsedJson &pj) {
    if (pj.byte_capacity == 0) {
        const bool allocok = pj.allocate_capacity(_batch_size);
        const bool allocok_thread = pj_thread.allocate_capacity(_batch_size);
        if (!allocok || !allocok_thread) {
            std::cerr << "can't allocate memory" << std::endl;
            return false;
        }
    }
    else if (pj.byte_capacity < _batch_size) {
        return simdjson::CAPACITY;
    }
#ifdef SIMDJSON_THREADS_ENABLED
    if(current_buffer_loc == last_json_buffer_loc) {
        load_next_batch = true;
    }
#endif

    if (load_next_batch) {
#ifdef SIMDJSON_THREADS_ENABLED
        //First time loading
        if(!stage_1_thread.joinable()) {
            _buf = _buf + current_buffer_loc;
            _len -= current_buffer_loc;
            n_bytes_parsed += current_buffer_loc;
            _batch_size = std::min(_batch_size, _len);
            _batch_size = trimmed_length_safe_utf8((const char*)_buf, _batch_size);
            if(_batch_size == 0) {
                pj.error_code = simdjson::UTF8_ERROR;
                return pj.error_code;
            }
            int stage1_is_ok = best_stage1(_buf, _batch_size, pj, true);
            if (stage1_is_ok != simdjson::SUCCESS) {
                pj.error_code = stage1_is_ok;
                return pj.error_code;
            }
            size_t last_index = find_last_json_buf_idx(_buf, _batch_size, pj);
            if(last_index == 0) {
              pj.error_code = simdjson::EMPTY;
              return pj.error_code;
            }
            pj.n_structural_indexes = last_index + 1;
        }
        // the second thread is running or done.
        else {
            stage_1_thread.join();
            if (stage1_is_ok_thread != simdjson::SUCCESS) {
                pj.error_code = stage1_is_ok_thread;
                return pj.error_code;
            }
            std::swap(pj.structural_indexes, pj_thread.structural_indexes);
            pj.n_structural_indexes = pj_thread.n_structural_indexes;
            _buf = _buf + last_json_buffer_loc;
            _len -= last_json_buffer_loc;
            n_bytes_parsed += last_json_buffer_loc;
            last_json_buffer_loc = 0; //because we want to use it in the if above.
        }
        if(_len - _batch_size > 0) {
            last_json_buffer_loc =  pj.structural_indexes[find_last_json_buf_idx(_buf,_batch_size,pj)];
            _batch_size = std::min(_batch_size, _len - last_json_buffer_loc);
            if(_batch_size > 0) {
                _batch_size = trimmed_length_safe_utf8((const char*)(_buf + last_json_buffer_loc), _batch_size);
                if(_batch_size == 0) {
                  pj.error_code = simdjson::UTF8_ERROR;
                  return pj.error_code;
                }
                // let us capture read-only variables
                const char * const b = _buf + last_json_buffer_loc;
                const size_t bs = _batch_size;
                // we call the thread on a lambda that will update  this->stage1_is_ok_thread
                // there is only one thread that may write to this value
                stage_1_thread = std::thread(
                   [this, b, bs] {
                     this->stage1_is_ok_thread = best_stage1(b, bs, this->pj_thread, true);
                });
            }
        }

        //If we loaded a perfect amount of documents last time, we need to skip the first element,
        // because it represents the end of the last document
        next_json = next_json == 1;
#else
        _buf = _buf + current_buffer_loc;
        _len -= current_buffer_loc;
        n_bytes_parsed += current_buffer_loc;

        _batch_size = std::min(_batch_size, _len);
        _batch_size = trimmed_length_safe_utf8((const char*)_buf, _batch_size);
        int stage1_is_ok = best_stage1(_buf, _batch_size, pj, true);
        if (stage1_is_ok != simdjson::SUCCESS) {
            pj.error_code = stage1_is_ok;
            return pj.error_code;
        }
        size_t last_index = find_last_json_buf_idx(_buf, _batch_size, pj);
        if(last_index == 0) {
            pj.error_code = simdjson::EMPTY;
            return pj.error_code;
        }
        pj.n_structural_indexes = last_index + 1;
#endif
        load_next_batch = false;

    }
//#define SIMDJSON_IREALLYNEEDHELP
#ifdef SIMDJSON_IREALLYNEEDHELP // for debugging
    size_t oldnext_json = next_json;
#endif
    int res = best_stage2(_buf, _len, pj, next_json);
#ifdef SIMDJSON_IREALLYNEEDHELP // for debugging
    int sizeofdoc = pj.structural_indexes[next_json]-pj.structural_indexes[oldnext_json];
    printf("size = %d\n", sizeofdoc);
    if(sizeofdoc > 0) {
      printf("%.*s\n",sizeofdoc, _buf + pj.structural_indexes[oldnext_json]);
    } else {
      printf("<empty>\n");
    }
#endif

    if (res == simdjson::SUCCESS_AND_HAS_MORE) {
        error_on_last_attempt = false;
        n_parsed_docs++;
        current_buffer_loc = pj.structural_indexes[next_json];
    } else if (res == simdjson::SUCCESS) {
        error_on_last_attempt = false;
        n_parsed_docs++;
        if(_len > _batch_size) {
            current_buffer_loc = pj.structural_indexes[next_json - 1];
#ifndef SIMDJSON_THREADS_ENABLED
            next_json = 1;
#endif
            load_next_batch = true;
            res = simdjson::SUCCESS_AND_HAS_MORE;
        }
    }
    // We assume the error is because the json was not loaded completely in this batch.
    // Load a new batch and if the error persists, it's a genuine error.
    else if(!error_on_last_attempt) {
                printf("failure?\n");

        load_next_batch = true;
        error_on_last_attempt = true;
        res = json_parse(pj);
    }
    return res;
}

size_t JsonStream::get_current_buffer_loc() const {
    return current_buffer_loc;
}

size_t JsonStream::get_n_parsed_docs() const {
    return n_parsed_docs;
}

size_t JsonStream::get_n_bytes_parsed() const {
    return n_bytes_parsed;
}

//// TODO: generalize this set of functions.  We don't want to have a copy in jsonparser.cpp
void find_the_best_supported_implementation() {
    uint32_t supports = detect_supported_architectures();
    // Order from best to worst (within architecture)
#ifdef IS_X86_64
    constexpr uint32_t haswell_flags =
            instruction_set::AVX2 | instruction_set::PCLMULQDQ |
            instruction_set::BMI1 | instruction_set::BMI2;
    constexpr uint32_t westmere_flags =
            instruction_set::SSE42 | instruction_set::PCLMULQDQ;
    if ((haswell_flags & supports) == haswell_flags) {
        best_stage1 = simdjson::find_structural_bits<Architecture ::HASWELL>;
        best_stage2 = simdjson::unified_machine<Architecture ::HASWELL>;
        return;
    }
    if ((westmere_flags & supports) == westmere_flags) {
        best_stage1 = simdjson::find_structural_bits<Architecture ::WESTMERE>;
        best_stage2 = simdjson::unified_machine<Architecture ::WESTMERE>;
        return;
    }
#endif
#ifdef IS_ARM64
    if (supports & instruction_set::NEON) {
        best_stage1 = simdjson::find_structural_bits<Architecture ::ARM64>;
        best_stage2 = simdjson::unified_machine<Architecture ::ARM64>;
        return;
    }
#endif
    std::cerr << "The processor is not supported by simdjson." << std::endl;
    // we throw an exception since this should not be recoverable
    throw new std::runtime_error("unsupported architecture");
}
<commit_msg>removing left over debug<commit_after>#include <map>
#include "simdjson/jsonstream.h"
#include "simdjson/isadetection.h"
#include "jsoncharutils.h"

using namespace simdjson;
void find_the_best_supported_implementation();

typedef int (*stage1_functype)(const char *buf, size_t len, ParsedJson &pj, bool streaming);
typedef int (*stage2_functype)(const char *buf, size_t len, ParsedJson &pj, size_t &next_json);

stage1_functype best_stage1;
stage2_functype best_stage2;

JsonStream::JsonStream(const char *buf, size_t len, size_t batchSize)
        : _buf(buf), _len(len), _batch_size(batchSize) {
    find_the_best_supported_implementation();
}

JsonStream::~JsonStream() {
#ifdef SIMDJSON_THREADS_ENABLED
    if(stage_1_thread.joinable()) {
      stage_1_thread.join();
    }
#endif
}

void JsonStream::set_new_buffer(const char *buf, size_t len) {
#ifdef SIMDJSON_THREADS_ENABLED
    if(stage_1_thread.joinable()) {
      stage_1_thread.join();
    }
#endif
    this->_buf = buf;
    this->_len = len;
    _batch_size = 0;
    _batch_size = 0;
    next_json = 0;
    current_buffer_loc = 0;
    n_parsed_docs = 0;
    error_on_last_attempt= false;
    load_next_batch = true;
}

// todo: this code is too complicated, it should be greatly simplified
int JsonStream::json_parse(ParsedJson &pj) {
    if (pj.byte_capacity == 0) {
        const bool allocok = pj.allocate_capacity(_batch_size);
        const bool allocok_thread = pj_thread.allocate_capacity(_batch_size);
        if (!allocok || !allocok_thread) {
            std::cerr << "can't allocate memory" << std::endl;
            return false;
        }
    }
    else if (pj.byte_capacity < _batch_size) {
        return simdjson::CAPACITY;
    }
#ifdef SIMDJSON_THREADS_ENABLED
    if(current_buffer_loc == last_json_buffer_loc) {
        load_next_batch = true;
    }
#endif

    if (load_next_batch) {
#ifdef SIMDJSON_THREADS_ENABLED
        //First time loading
        if(!stage_1_thread.joinable()) {
            _buf = _buf + current_buffer_loc;
            _len -= current_buffer_loc;
            n_bytes_parsed += current_buffer_loc;
            _batch_size = std::min(_batch_size, _len);
            _batch_size = trimmed_length_safe_utf8((const char*)_buf, _batch_size);
            if(_batch_size == 0) {
                pj.error_code = simdjson::UTF8_ERROR;
                return pj.error_code;
            }
            int stage1_is_ok = best_stage1(_buf, _batch_size, pj, true);
            if (stage1_is_ok != simdjson::SUCCESS) {
                pj.error_code = stage1_is_ok;
                return pj.error_code;
            }
            size_t last_index = find_last_json_buf_idx(_buf, _batch_size, pj);
            if(last_index == 0) {
              pj.error_code = simdjson::EMPTY;
              return pj.error_code;
            }
            pj.n_structural_indexes = last_index + 1;
        }
        // the second thread is running or done.
        else {
            stage_1_thread.join();
            if (stage1_is_ok_thread != simdjson::SUCCESS) {
                pj.error_code = stage1_is_ok_thread;
                return pj.error_code;
            }
            std::swap(pj.structural_indexes, pj_thread.structural_indexes);
            pj.n_structural_indexes = pj_thread.n_structural_indexes;
            _buf = _buf + last_json_buffer_loc;
            _len -= last_json_buffer_loc;
            n_bytes_parsed += last_json_buffer_loc;
            last_json_buffer_loc = 0; //because we want to use it in the if above.
        }
        if(_len - _batch_size > 0) {
            last_json_buffer_loc =  pj.structural_indexes[find_last_json_buf_idx(_buf,_batch_size,pj)];
            _batch_size = std::min(_batch_size, _len - last_json_buffer_loc);
            if(_batch_size > 0) {
                _batch_size = trimmed_length_safe_utf8((const char*)(_buf + last_json_buffer_loc), _batch_size);
                if(_batch_size == 0) {
                  pj.error_code = simdjson::UTF8_ERROR;
                  return pj.error_code;
                }
                // let us capture read-only variables
                const char * const b = _buf + last_json_buffer_loc;
                const size_t bs = _batch_size;
                // we call the thread on a lambda that will update  this->stage1_is_ok_thread
                // there is only one thread that may write to this value
                stage_1_thread = std::thread(
                   [this, b, bs] {
                     this->stage1_is_ok_thread = best_stage1(b, bs, this->pj_thread, true);
                });
            }
        }

        //If we loaded a perfect amount of documents last time, we need to skip the first element,
        // because it represents the end of the last document
        next_json = next_json == 1;
#else
        _buf = _buf + current_buffer_loc;
        _len -= current_buffer_loc;
        n_bytes_parsed += current_buffer_loc;

        _batch_size = std::min(_batch_size, _len);
        _batch_size = trimmed_length_safe_utf8((const char*)_buf, _batch_size);
        int stage1_is_ok = best_stage1(_buf, _batch_size, pj, true);
        if (stage1_is_ok != simdjson::SUCCESS) {
            pj.error_code = stage1_is_ok;
            return pj.error_code;
        }
        size_t last_index = find_last_json_buf_idx(_buf, _batch_size, pj);
        if(last_index == 0) {
            pj.error_code = simdjson::EMPTY;
            return pj.error_code;
        }
        pj.n_structural_indexes = last_index + 1;
#endif
        load_next_batch = false;

    }
//#define SIMDJSON_IREALLYNEEDHELP
#ifdef SIMDJSON_IREALLYNEEDHELP // for debugging
    size_t oldnext_json = next_json;
#endif
    int res = best_stage2(_buf, _len, pj, next_json);
#ifdef SIMDJSON_IREALLYNEEDHELP // for debugging
    int sizeofdoc = pj.structural_indexes[next_json]-pj.structural_indexes[oldnext_json];
    printf("size = %d\n", sizeofdoc);
    if(sizeofdoc > 0) {
      printf("%.*s\n",sizeofdoc, _buf + pj.structural_indexes[oldnext_json]);
    } else {
      printf("<empty>\n");
    }
#endif

    if (res == simdjson::SUCCESS_AND_HAS_MORE) {
        error_on_last_attempt = false;
        n_parsed_docs++;
        current_buffer_loc = pj.structural_indexes[next_json];
    } else if (res == simdjson::SUCCESS) {
        error_on_last_attempt = false;
        n_parsed_docs++;
        if(_len > _batch_size) {
            current_buffer_loc = pj.structural_indexes[next_json - 1];
#ifndef SIMDJSON_THREADS_ENABLED
            next_json = 1;
#endif
            load_next_batch = true;
            res = simdjson::SUCCESS_AND_HAS_MORE;
        }
    }
    // We assume the error is because the json was not loaded completely in this batch.
    // Load a new batch and if the error persists, it's a genuine error.
    else if(!error_on_last_attempt) {
        load_next_batch = true;
        error_on_last_attempt = true;
        res = json_parse(pj);
    }
    return res;
}

size_t JsonStream::get_current_buffer_loc() const {
    return current_buffer_loc;
}

size_t JsonStream::get_n_parsed_docs() const {
    return n_parsed_docs;
}

size_t JsonStream::get_n_bytes_parsed() const {
    return n_bytes_parsed;
}

//// TODO: generalize this set of functions.  We don't want to have a copy in jsonparser.cpp
void find_the_best_supported_implementation() {
    uint32_t supports = detect_supported_architectures();
    // Order from best to worst (within architecture)
#ifdef IS_X86_64
    constexpr uint32_t haswell_flags =
            instruction_set::AVX2 | instruction_set::PCLMULQDQ |
            instruction_set::BMI1 | instruction_set::BMI2;
    constexpr uint32_t westmere_flags =
            instruction_set::SSE42 | instruction_set::PCLMULQDQ;
    if ((haswell_flags & supports) == haswell_flags) {
        best_stage1 = simdjson::find_structural_bits<Architecture ::HASWELL>;
        best_stage2 = simdjson::unified_machine<Architecture ::HASWELL>;
        return;
    }
    if ((westmere_flags & supports) == westmere_flags) {
        best_stage1 = simdjson::find_structural_bits<Architecture ::WESTMERE>;
        best_stage2 = simdjson::unified_machine<Architecture ::WESTMERE>;
        return;
    }
#endif
#ifdef IS_ARM64
    if (supports & instruction_set::NEON) {
        best_stage1 = simdjson::find_structural_bits<Architecture ::ARM64>;
        best_stage2 = simdjson::unified_machine<Architecture ::ARM64>;
        return;
    }
#endif
    std::cerr << "The processor is not supported by simdjson." << std::endl;
    // we throw an exception since this should not be recoverable
    throw new std::runtime_error("unsupported architecture");
}
<|endoftext|>


<commit_before>/*
 * The MIT License (MIT)
 *
 * Copyright (c) 2016 Jan Kelling
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in all
 * copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE.
 */

///\file
///\brief Base classes for making types observable or observe the lifetime of objects.

#pragma once

#include <vector>
#include <algorithm>
#include <atomic>
#include <mutex>

namespace nytl
{

///XXX: at the moment not threadsafe. Discussion needed whether this would make sense.

class Observable;

///\ingroup utility
///Base class for classes that observe the lifetime of other Observeable objects.
class Observer
{
	friend class Observable;
	virtual void obsDestruction(Observable&) = 0;
};

///\ingroup utility
///\brief Utility class to make the objects lifetime observable.
///\details Base class that can be derived from if the lifetime of objects of this class should be
///observeable by others. Also see nytl::Observer and nytl::ObservingPtr.
class Observable
{
protected:
	std::vector<Observer*> observer_;
	//std::mutex mutex_;

public:
	~Observable()
	{
		//no lock guard needed. Undefined behavior if destructor and accesing run at the same time
		for(auto& obs : observer_)
			obs->obsDestruction(*this);
	}

	void addObserver(Observer& obs)
	{
		//std::lock_guard<std::mutex> lock(mutex_);
		observer_.push_back(&obs);
	}
	bool removeObserver(Observer& obs)
	{
		//std::lock_guard<std::mutex> lock(mutex_);
		return (observer_.cend() == std::remove(observer_.begin(), observer_.end(), &obs));
	}
	bool moveObserver(Observer& oldone, Observer& newone)
	{
		//std::lock_guard<std::mutex> lock(mutex_);
		auto it = std::find(observer_.begin(), observer_.end(), &oldone);
		if(it == observer_.cend()) return 0;
		*it = &newone;
		return 1;
	}
};

///\ingroup utility
///\brief Smart pointer class that observes the lifetime of its object.
///\details Basically a smart pointer that does always know, whether the object it points to is
//alive or not. Does only work with objects of classes that are derived from nytl::Observeable.
///Semantics are related to std::unique_ptr.
template <typename T>
class ObservingPtr : public Observer
{
private:
	T* object_ {nullptr};
	virtual void obsDestruction(Observable&) override { object_ = nullptr; }

public:
	ObservingPtr() = default;
	ObservingPtr(T* obj) : object_(obj) { if(object_) object_->addObserver(*this); }
	ObservingPtr(T& obj) : object_(&obj) { object_->addObserver(*this); }
	~ObservingPtr(){ if(object_) object_->removeObserver(*this); }

	ObservingPtr(const ObservingPtr& other) : object_(other.object_)
	{
		if(object_) object_->addObserver(*this);
	}
	ObservingPtr& operator=(const ObservingPtr& other)
	{
		reset(other.object_);
		return *this;
	}

	ObservingPtr(ObservingPtr&& other) noexcept : object_(other.object_)
	{
		if(object_) object_->moveObserver(other, *this);
		other.object_ = nullptr;
	}
	ObservingPtr& operator=(ObservingPtr&& other) noexcept
	{
		reset();
		object_ = other.object_;
		if(object_) object_->moveObserver(other, *this);
		other.object_ = nullptr;
		return *this;
	}

	void reset(T* obj = nullptr)
	{
		if(obj) obj->addObserver(*this);
		if(object_) object_->removeObserver(*this);
		object_ = obj;
	}
	void reset(T& obj)
	{
		obj.addObserver(*this);
		if(object_) object_->removeObserver(*this);
		object_ = &obj;
	}

	T* get() const { return object_; }

	T& operator*(){ return *object_; }
	const T& operator*() const { return *object_; }

	T* operator->(){ return object_; }
	const T* operator->() const { return object_; }

	operator bool() const { return (object_ != nullptr); }
	void swap(ObservingPtr& other) noexcept
	{
		if(object_) object_->moveObserver(*this, other);
		if(other.object_) other.object_->moveObserver(other, *this);
		std::swap(object_, other.object_);
	}
};

}
<commit_msg>Update observe.hpp<commit_after>/*
 * The MIT License (MIT)
 *
 * Copyright (c) 2016 Jan Kelling
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in all
 * copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE.
 */

///\file
///\brief Base classes for making types observable or observe the lifetime of objects.

#pragma once

#include <vector>
#include <algorithm>
#include <atomic>
#include <mutex>

namespace nytl
{

///XXX: at the moment not threadsafe. Discussion needed whether this would make sense.

class Observable;

///\ingroup utility
///Base class for classes that observe the lifetime of other Observeable objects.
class Observer
{
	friend class Observable;
	virtual void obsDestruction(Observable&) = 0;
};

///\ingroup utility
///\brief Utility class to make the objects lifetime observable.
///\details Base class that can be derived from if the lifetime of objects of this class should be
///observeable by others. Also see nytl::Observer and nytl::ObservingPtr.
class Observable
{
protected:
	std::vector<Observer*> observer_;
	//std::mutex mutex_;

public:
	~Observable()
	{
		//no lock guard needed. Undefined behavior if destructor and accesing run at the same time
		for(auto& obs : observer_)
			obs->obsDestruction(*this);
	}

	void addObserver(Observer& obs)
	{
		//std::lock_guard<std::mutex> lock(mutex_);
		observer_.push_back(&obs);
	}
	bool removeObserver(Observer& obs)
	{
		//std::lock_guard<std::mutex> lock(mutex_);
		return (observer_.cend() == std::remove(observer_.begin(), observer_.end(), &obs));
	}
	bool moveObserver(Observer& oldone, Observer& newone)
	{
		//std::lock_guard<std::mutex> lock(mutex_);
		auto it = std::find(observer_.begin(), observer_.end(), &oldone);
		if(it == observer_.cend()) return 0;
		*it = &newone;
		return 1;
	}
};

///\ingroup utility
///\brief Smart pointer class that observes the lifetime of its object.
///\details Basically a smart pointer that does always know, whether the object it points to is
//alive or not. Does only work with objects of classes that are derived from nytl::Observeable.
///Semantics are related to std::unique_ptr.
template <typename T>
class ObservingPtr : public Observer
{
private:
	T* object_ {nullptr};
	virtual void obsDestruction(Observable&) override { object_ = nullptr; }

public:
	ObservingPtr() = default;
	ObservingPtr(T* obj) : object_(obj) { if(object_) object_->addObserver(*this); }
	ObservingPtr(T& obj) : object_(&obj) { object_->addObserver(*this); }
	~ObservingPtr(){ if(object_) object_->removeObserver(*this); }

	ObservingPtr(const ObservingPtr& other) : object_(other.object_)
	{
		if(object_) object_->addObserver(*this);
	}
	ObservingPtr& operator=(const ObservingPtr& other)
	{
		reset(other.object_);
		return *this;
	}

	ObservingPtr(ObservingPtr&& other) noexcept : object_(other.object_)
	{
		if(object_) object_->moveObserver(other, *this);
		other.object_ = nullptr;
	}
	ObservingPtr& operator=(ObservingPtr&& other) noexcept
	{
		reset();
		object_ = other.object_;
		if(object_) object_->moveObserver(other, *this);
		other.object_ = nullptr;
		return *this;
	}

	void reset(T* obj = nullptr)
	{
		if(obj) obj->addObserver(*this);
		if(object_) object_->removeObserver(*this);
		object_ = obj;
	}
	void reset(T& obj)
	{
		obj.addObserver(*this);
		if(object_) object_->removeObserver(*this);
		object_ = &obj;
	}

	T* get() const { return object_; }
	T& operator*() const { return *object_; }
	T* operator->() const { return object_; }

	operator bool() const { return (object_ != nullptr); }
	void swap(ObservingPtr& other) noexcept
	{
		if(object_) object_->moveObserver(*this, other);
		if(other.object_) other.object_->moveObserver(other, *this);
		std::swap(object_, other.object_);
	}
};

}
<|endoftext|>


<commit_before>#include <agency/future.hpp>
#include <agency/new_executor_traits.hpp>
#include <iostream>
#include <cassert>

#include "test_executors.hpp"

template<class Executor>
void test()
{
  using executor_type = Executor;
  using traits = agency::new_executor_traits<executor_type>;

  {
    executor_type exec;

    auto void_future1 = traits::when_all(exec);
    auto void_future2 = traits::when_all(exec, void_future1);

    void_future2.get();
  }

  {
    executor_type exec;

    auto int_ready   = traits::template make_ready_future<int>(exec, 13);
    auto float_ready = traits::template make_ready_future<float>(exec, 7.f);

    auto int_float_fut = traits::when_all(exec, int_ready, float_ready);

    auto int_float = int_float_fut.get();

    assert(std::get<0>(int_float) == 13);
    assert(std::get<1>(int_float) == 7.f);
  }

  {
    executor_type exec;

    auto int_ready   = agency::detail::make_ready_future(13);
    auto void_ready  = agency::detail::make_ready_future();
    auto float_ready = agency::detail::make_ready_future(7.f);

    auto int_float_fut = traits::when_all(exec, int_ready, void_ready, float_ready);

    auto int_float = int_float_fut.get();

    assert(std::get<0>(int_float) == 13);
    assert(std::get<1>(int_float) == 7.f);
  }
}

int main()
{
  using namespace test_executors;

  test<empty_executor>();
  test<single_agent_when_all_execute_and_select_executor>();
  test<multi_agent_when_all_execute_and_select_executor>();
  test<single_agent_then_execute_executor>();
  test<when_all_executor>();

  std::cout << "OK" << std::endl;

  return 0;
}

<commit_msg>Test all existing test executors in test_when_all.cpp<commit_after>#include <agency/future.hpp>
#include <agency/new_executor_traits.hpp>
#include <iostream>
#include <cassert>

#include "test_executors.hpp"

template<class Executor>
void test()
{
  using executor_type = Executor;
  using traits = agency::new_executor_traits<executor_type>;

  {
    executor_type exec;

    auto void_future1 = traits::when_all(exec);
    auto void_future2 = traits::when_all(exec, void_future1);

    void_future2.get();
  }

  {
    executor_type exec;

    auto int_ready   = traits::template make_ready_future<int>(exec, 13);
    auto float_ready = traits::template make_ready_future<float>(exec, 7.f);

    auto int_float_fut = traits::when_all(exec, int_ready, float_ready);

    auto int_float = int_float_fut.get();

    assert(std::get<0>(int_float) == 13);
    assert(std::get<1>(int_float) == 7.f);
  }

  {
    executor_type exec;

    auto int_ready   = agency::detail::make_ready_future(13);
    auto void_ready  = agency::detail::make_ready_future();
    auto float_ready = agency::detail::make_ready_future(7.f);

    auto int_float_fut = traits::when_all(exec, int_ready, void_ready, float_ready);

    auto int_float = int_float_fut.get();

    assert(std::get<0>(int_float) == 13);
    assert(std::get<1>(int_float) == 7.f);
  }
}

int main()
{
  using namespace test_executors;

  test<empty_executor>();
  test<single_agent_when_all_execute_and_select_executor>();
  test<multi_agent_when_all_execute_and_select_executor>();
  test<single_agent_then_execute_executor>();
  test<when_all_executor>();
  test<multi_agent_execute_returning_user_defined_container_executor>();
  test<multi_agent_execute_returning_default_container_executor>();

  std::cout << "OK" << std::endl;

  return 0;
}

<|endoftext|>


<commit_before>#pragma once
#include "bits.hpp"

namespace spn {
	//! レイヤー分離したビット配列
	template <int N, class T=uint32_t>
	class LayerBitArray {
		const static int ElemWidth = NBits<sizeof(T)*8-1>::result,
						ElemBits = sizeof(T)*8,
						NElemLow = (N+ElemBits-1)/ElemBits,			// 最下層のレイヤーでElemを幾つ使うか
						NElem = NDivide<NElemLow, ElemBits>::sum+1,
						NLayer = NDivide<NElemLow,ElemBits>::result+1;	// 最上位を1Elemとした時に幾つレイヤーが必要か

		// 一次元配列で扱う
		T	_bit[NElem];
		// 各レイヤーのオフセット
		int	_offset[NLayer+1];
		// 総フラグ数
		int _nFlag;
		// NはBitsの整数倍でなければならない
		int _dummy[N%ElemBits == 0 ? 0 : -1];

		template <int Layer, int EndLayer, class Proc>
		struct Iter {
			static void proc(const LayerBitArray& src, Proc& p, int cur) {
				// フラグをシフトしながら巡回
				T bs = src._bit[src._offset[Layer] + cur];

				int bsLsb = Bit::LSB_N(bs);
				bs >>= bsLsb;
				int inCur = cur*ElemBits+bsLsb;
				while(bs != 0) {
					if(bs & 1)
						Iter< Layer+1, EndLayer, Proc>::proc(src, p, inCur);
					++inCur;
					bs >>= 1;
				}
			}
		};

		template <int Layer, class Proc>
		struct Iter<Layer, Layer, Proc> {
			static void proc(const LayerBitArray& src, Proc& p, int cur) {
				T bs = src._bit[src._offset[Layer] + cur];

				int bsLsb = Bit::LSB_N(bs);
				bs >>= bsLsb;
				int inCur = cur*ElemBits+bsLsb;
				while(bs != 0) {
					if(bs & 1)
						p(inCur);
					++inCur;
					bs >>= 1;
				}
			}
		};
		struct Setter {
			static void proc(T& val, const T mask, const T /*prev*/) { val |= mask; }
			static void count(int& cnt, const T e, const T mask) { cnt += ~Bit::ZeroOrFull(e & mask) & 1; }
		};
		struct Clearer {
			static void proc(T& val, const T mask, const T prev) {
				if(prev == 0)
					val &= ~mask;
			}
			static void count(int& cnt, const T e, const T mask) { cnt -= ~Bit::ZeroOrFull(e & mask)+1; }
		};
		template <class VAL>
		struct Collector {
			std::vector<VAL> _lst;
			VAL*			_ptr;
			Collector(int n): _lst(n), _ptr(&_lst[0]) {}

			void operator()(int idx) { *_ptr++ = idx; }
		};
		struct Callback {
			std::function<void (int)>&	_cb;
			Callback(std::function<void (int)>& cb): _cb(cb) {}
			void operator()(int idx) { _cb(idx); }
		};
		template <class Proc>
		void _iterate(Proc& p) const {
			Iter<0,NLayer,Proc>::proc(*this,p,0);
		}
		template <class Proc>
		void _proc(int idx) {
			int ofs = idx;
			T mask = 1<<(ofs % ElemBits);
			auto& e = _bit[_offset[NLayer] + ofs/ElemBits];
			Proc::count(_nFlag, e, mask);
			Proc::proc(e, mask, 0);
			auto prev = e;

			ofs >>= ElemWidth;
			// Elemの特定と上レイヤの巡回
			for(int i=NLayer-1 ; i>=0 ; i--) {
				int nofs = ofs >> ElemWidth;
				const T mask = 1<<(ofs % ElemBits);
				Proc::proc(_bit[_offset[i] + nofs], mask, prev);
				prev = _bit[_offset[i] + nofs];
				ofs = nofs;
			}
		}
		public:
			LayerBitArray() {
				// TODO: この処理は静的にできる筈
				int cur = NElem,
					sub = NElemLow;
				for(int i=NLayer ; i>=0 ; i--) {
					cur -= sub;
					sub /= ElemBits;
					_offset[i] = cur;
				}
				_offset[0] = 0;
				clear();
			}
			//! ビットを全て0にする
			void clear() {
				_nFlag = 0;
				memset(_bit, 0, sizeof(_bit));
			}
			//! 指定ビットを1にする
			void set(int idx) {
				_proc<Setter>(idx);
			}
			//! 指定ビットを0にする
			void reset(int idx) {
				_proc<Clearer>(idx);
			}
			//! 指定ビットが立っているかをチェック
			bool check(int idx) const {
				return _bit[_offset[NLayer] + idx/ElemBits] & (1<<(idx % ElemBits));
			}
			//! 1になっているビットの数
			int getNBit() const { return _nFlag; }

			//! 1になっているビットのインデックス配列を取得
			template <class VAL>
			std::vector<VAL> getIndexList() const {
				int nB = getNBit();
				if(nB > 0) {
					Collector<VAL> c(nB);
					_iterate(c);
					return std::move(c._lst);
				}
				return std::vector<VAL>();
			}
			//! 1になっているビットをコールバック関数で巡回
			void iterateBit(std::function<void (int)> cb) const {
				Callback c(cb);
				_iterate(c);
			}

			template <class ARDST, class AR0, class AR1, class OP>
			static ARDST& _operate (ARDST& arDst, const AR0& ar0, const AR1& ar1, OP op) {
				const int low = ar0._offset[countof(_offset)-1];
				// 上層
				int cur = 0;
				while(cur != low) {
					arDst._bit[cur] = op(ar0._bit[cur], ar1._bit[cur]);
					++cur;
				}

				// 最下層
				// ついでに総フラグ数の再カウント
				int count = 0;
				while(cur != countof(ar0._bit)) {
					arDst._bit[cur] = op(ar0._bit[cur], ar1._bit[cur]);
					count += Bit::Count(arDst._bit[cur]);
					++cur;
				}
				arDst._nFlag = count;
				return arDst;
			}
			template <class OP>
			LayerBitArray& _operate2 (const LayerBitArray& ar, OP op) {
				return _operate(*this, *this, ar, op); }
			template <class OP>
			LayerBitArray _operate3 (const LayerBitArray& ar, OP op) const {
				LayerBitArray ret;
				std::memcpy(ret._offset, _offset, sizeof(_offset));
				return _operate(ret, *this, ar, op);
			}

			#define FUNC_OR [](const T& v0, const T& v1) { return v0 | v1; }
			#define FUNC_AND [](const T& v0, const T& v1) { return v0 & v1; }
			#define FUNC_XOR [](const T& v0, const T& v1) { return v0 ^ v1; }

			//! LayerBit同士をOR演算
			LayerBitArray& operator |= (const LayerBitArray& ar) {
				return _operate2(ar, FUNC_OR); }
			LayerBitArray operator | (const LayerBitArray& ar) const {
				return _operate3(ar, FUNC_OR); }
			//! LayerBit同士をAND演算
			LayerBitArray& operator &= (const LayerBitArray& ar) {
				return _operate2(ar, FUNC_AND); }
			LayerBitArray operator & (const LayerBitArray& ar) const {
				return _operate3(ar, FUNC_AND); }
			//! LayerBit同士をXOR演算
			LayerBitArray& operator ^= (const LayerBitArray& ar) {
				return _operate2(ar, FUNC_XOR); }
			LayerBitArray operator ^ (const LayerBitArray& ar) const {
				return _operate3(ar, FUNC_XOR); }

			#undef FUNC_OR
			#undef FUNC_AND
			#undef FUNC_XOR

			bool operator == (const LayerBitArray& ar) const {
				// ビット数が違っていたらアウト
				if(getNBit() != ar.getNBit())
					return false;

				// 一旦上層レイヤーで比較した方が速いかもしれない
				T val(0);
				for(int cur = _offset[countof(_offset)-1] ; cur != countof(_bit) ; cur++)
					val |= _bit[cur] ^ ar._bit[cur];
				return val == 0;
			}
			bool operator != (const LayerBitArray& ar) const {
				return !(operator ==(ar));
			}
	};
}
<commit_msg>LayerBitArray: set(), reset()の引数を可変テンプレートに変更<commit_after>#pragma once
#include "bits.hpp"

namespace spn {
	//! レイヤー分離したビット配列
	template <int N, class T=uint32_t>
	class LayerBitArray {
		const static int ElemWidth = NBits<sizeof(T)*8-1>::result,
						ElemBits = sizeof(T)*8,
						NElemLow = (N+ElemBits-1)/ElemBits,			// 最下層のレイヤーでElemを幾つ使うか
						NElem = NDivide<NElemLow, ElemBits>::sum+1,
						NLayer = NDivide<NElemLow,ElemBits>::result+1;	// 最上位を1Elemとした時に幾つレイヤーが必要か

		// 一次元配列で扱う
		T	_bit[NElem];
		// 各レイヤーのオフセット
		int	_offset[NLayer+1];
		// 総フラグ数
		int _nFlag;
		// NはBitsの整数倍でなければならない
		int _dummy[N%ElemBits == 0 ? 0 : -1];

		template <int Layer, int EndLayer, class Proc>
		struct Iter {
			static void proc(const LayerBitArray& src, Proc& p, int cur) {
				// フラグをシフトしながら巡回
				T bs = src._bit[src._offset[Layer] + cur];

				int bsLsb = Bit::LSB_N(bs);
				bs >>= bsLsb;
				int inCur = cur*ElemBits+bsLsb;
				while(bs != 0) {
					if(bs & 1)
						Iter< Layer+1, EndLayer, Proc>::proc(src, p, inCur);
					++inCur;
					bs >>= 1;
				}
			}
		};

		template <int Layer, class Proc>
		struct Iter<Layer, Layer, Proc> {
			static void proc(const LayerBitArray& src, Proc& p, int cur) {
				T bs = src._bit[src._offset[Layer] + cur];

				int bsLsb = Bit::LSB_N(bs);
				bs >>= bsLsb;
				int inCur = cur*ElemBits+bsLsb;
				while(bs != 0) {
					if(bs & 1)
						p(inCur);
					++inCur;
					bs >>= 1;
				}
			}
		};
		struct Setter {
			static void proc(T& val, const T mask, const T /*prev*/) { val |= mask; }
			static void count(int& cnt, const T e, const T mask) { cnt += ~Bit::ZeroOrFull(e & mask) & 1; }
		};
		struct Clearer {
			static void proc(T& val, const T mask, const T prev) {
				if(prev == 0)
					val &= ~mask;
			}
			static void count(int& cnt, const T e, const T mask) { cnt -= ~Bit::ZeroOrFull(e & mask)+1; }
		};
		template <class VAL>
		struct Collector {
			std::vector<VAL> _lst;
			VAL*			_ptr;
			Collector(int n): _lst(n), _ptr(&_lst[0]) {}

			void operator()(int idx) { *_ptr++ = idx; }
		};
		struct Callback {
			std::function<void (int)>&	_cb;
			Callback(std::function<void (int)>& cb): _cb(cb) {}
			void operator()(int idx) { _cb(idx); }
		};
		template <class Proc>
		void _iterate(Proc& p) const {
			Iter<0,NLayer,Proc>::proc(*this,p,0);
		}
		template <class Proc>
		void _proc(int idx) {
			int ofs = idx;
			T mask = 1<<(ofs % ElemBits);
			auto& e = _bit[_offset[NLayer] + ofs/ElemBits];
			Proc::count(_nFlag, e, mask);
			Proc::proc(e, mask, 0);
			auto prev = e;

			ofs >>= ElemWidth;
			// Elemの特定と上レイヤの巡回
			for(int i=NLayer-1 ; i>=0 ; i--) {
				int nofs = ofs >> ElemWidth;
				const T mask = 1<<(ofs % ElemBits);
				Proc::proc(_bit[_offset[i] + nofs], mask, prev);
				prev = _bit[_offset[i] + nofs];
				ofs = nofs;
			}
		}
		public:
			LayerBitArray() {
				// TODO: この処理は静的にできる筈
				int cur = NElem,
					sub = NElemLow;
				for(int i=NLayer ; i>=0 ; i--) {
					cur -= sub;
					sub /= ElemBits;
					_offset[i] = cur;
				}
				_offset[0] = 0;
				clear();
			}
			//! ビットを全て0にする
			void clear() {
				_nFlag = 0;
				memset(_bit, 0, sizeof(_bit));
			}

			template <class OP>
			void _procIndex() {}
			template <class OP, class... Idx>
			void _procIndex(int idx0, Idx... idx) {
				_proc<OP>(idx0);
				_procIndex<OP>(idx...);
			}

			//! 指定ビットを1にする
			template <class... Idx>
			void set(Idx... idx) {
				_procIndex<Setter>(idx...);
			}
			//! 指定ビットを0にする
			template <class... Idx>
			void reset(Idx... idx) {
				_procIndex<Clearer>(idx...);
			}
			//! 指定ビットが立っているかをチェック
			bool check(int idx) const {
				return _bit[_offset[NLayer] + idx/ElemBits] & (1<<(idx % ElemBits));
			}
			//! 1になっているビットの数
			int getNBit() const { return _nFlag; }

			//! 1になっているビットのインデックス配列を取得
			template <class VAL>
			std::vector<VAL> getIndexList() const {
				int nB = getNBit();
				if(nB > 0) {
					Collector<VAL> c(nB);
					_iterate(c);
					return std::move(c._lst);
				}
				return std::vector<VAL>();
			}
			//! 1になっているビットをコールバック関数で巡回
			void iterateBit(std::function<void (int)> cb) const {
				Callback c(cb);
				_iterate(c);
			}

			template <class ARDST, class AR0, class AR1, class OP>
			static ARDST& _operate (ARDST& arDst, const AR0& ar0, const AR1& ar1, OP op) {
				const int low = ar0._offset[countof(_offset)-1];
				// 上層
				int cur = 0;
				while(cur != low) {
					arDst._bit[cur] = op(ar0._bit[cur], ar1._bit[cur]);
					++cur;
				}

				// 最下層
				// ついでに総フラグ数の再カウント
				int count = 0;
				while(cur != countof(ar0._bit)) {
					arDst._bit[cur] = op(ar0._bit[cur], ar1._bit[cur]);
					count += Bit::Count(arDst._bit[cur]);
					++cur;
				}
				arDst._nFlag = count;
				return arDst;
			}
			template <class OP>
			LayerBitArray& _operate2 (const LayerBitArray& ar, OP op) {
				return _operate(*this, *this, ar, op); }
			template <class OP>
			LayerBitArray _operate3 (const LayerBitArray& ar, OP op) const {
				LayerBitArray ret;
				std::memcpy(ret._offset, _offset, sizeof(_offset));
				return _operate(ret, *this, ar, op);
			}

			#define FUNC_OR [](const T& v0, const T& v1) { return v0 | v1; }
			#define FUNC_AND [](const T& v0, const T& v1) { return v0 & v1; }
			#define FUNC_XOR [](const T& v0, const T& v1) { return v0 ^ v1; }

			//! LayerBit同士をOR演算
			LayerBitArray& operator |= (const LayerBitArray& ar) {
				return _operate2(ar, FUNC_OR); }
			LayerBitArray operator | (const LayerBitArray& ar) const {
				return _operate3(ar, FUNC_OR); }
			//! LayerBit同士をAND演算
			LayerBitArray& operator &= (const LayerBitArray& ar) {
				return _operate2(ar, FUNC_AND); }
			LayerBitArray operator & (const LayerBitArray& ar) const {
				return _operate3(ar, FUNC_AND); }
			//! LayerBit同士をXOR演算
			LayerBitArray& operator ^= (const LayerBitArray& ar) {
				return _operate2(ar, FUNC_XOR); }
			LayerBitArray operator ^ (const LayerBitArray& ar) const {
				return _operate3(ar, FUNC_XOR); }

			#undef FUNC_OR
			#undef FUNC_AND
			#undef FUNC_XOR

			bool operator == (const LayerBitArray& ar) const {
				// ビット数が違っていたらアウト
				if(getNBit() != ar.getNBit())
					return false;

				// 一旦上層レイヤーで比較した方が速いかもしれない
				T val(0);
				for(int cur = _offset[countof(_offset)-1] ; cur != countof(_bit) ; cur++)
					val |= _bit[cur] ^ ar._bit[cur];
				return val == 0;
			}
			bool operator != (const LayerBitArray& ar) const {
				return !(operator ==(ar));
			}
	};
}
<|endoftext|>


<commit_before>//
// Physical page allocator.
// Slab allocator, for chunks larger than one page.
//

#include "types.h"
#include "mmu.h"
#include "kernel.hh"
#include "spinlock.h"
#include "kalloc.hh"
#include "mtrace.h"
#include "cpu.hh"
#include "multiboot.hh"
#include "wq.hh"

static struct Mbmem mem[128];
static u64 nmem;
static u64 membytes;
struct kmem kmems[NCPU];
struct kmem slabmem[slab_type_max][NCPU];

extern char end[]; // first address after kernel loaded from ELF file
char *newend;

static int kinited __mpalign__;

static struct Mbmem *
memsearch(paddr pa)
{
  struct Mbmem *e;
  struct Mbmem *q;

  q = mem+nmem;
  for (e = &mem[0]; e < q; e++)
    if ((e->base <= pa) && ((e->base+e->length) > pa))
      return e;
  return 0;
}

static u64
memsize(void *va)
{
  struct Mbmem *e;
  paddr pa = v2p(va);

  e = memsearch(pa);
  if (e == nullptr)
    return -1;
  return (e->base+e->length) - pa;
}

static void *
memnext(void *va, u64 inc)
{
  struct Mbmem *e, *q;
  paddr pa = v2p(va);

  e = memsearch(pa);
  if (e == nullptr)
    return (void *)-1;

  pa += inc;
  if (pa < (e->base+e->length))
    return p2v(pa);

  q = mem+nmem;
  for (e = e + 1; e < q; e++)
      return p2v(e->base);

  return (void *)-1;
}

static void
initmem(u64 mbaddr)
{
  struct Mbdata *mb;
  struct Mbmem *mbmem;
  u8 *p, *ep;

  mb = (Mbdata*) p2v(mbaddr);
  if(!(mb->flags & (1<<6)))
    panic("multiboot header has no memory map");

  p = (u8*) p2v(mb->mmap_addr);
  ep = p + mb->mmap_length;

  while (p < ep) {
    mbmem = (Mbmem *)(p+4);
    p += 4 + *(u32*)p;
    if (mbmem->type == 1 && mbmem->base >= 0x100000) {
      membytes += mbmem->length;
      mem[nmem] = *mbmem;
      nmem++;
    }
  }
}

// simple page allocator to get off the ground during boot
static char *
pgalloc(void)
{
  if (newend == 0)
    newend = end;

  void *p = (void*)PGROUNDUP((uptr)newend);
  memset(p, 0, PGSIZE);
  newend = newend + PGSIZE;
  return (char*) p;
}

//
// kmem
//
run*
kmem::alloc(const char* name)
{
  run* r;

  for (;;) {
    auto headval = freelist.load();
    r = headval.ptr();
    if (!r)
      return nullptr;

    run *nxt = r->next;
    if (freelist.compare_exchange(headval, nxt)) {
      if (r->next != nxt)
        panic("kmem:alloc: aba race %p %p %p\n",
              r, r->next, nxt);
      nfree--;
      if (name)
        mtlabel(mtrace_label_block, r, size, name, strlen(name));
      return r;
    }
  }
}

void
kmem::free(run* r)
{
  if (kinited)
    mtunlabel(mtrace_label_block, r);

  for (;;) {
    auto headval = freelist.load();
    r->next = headval.ptr();
    if (freelist.compare_exchange(headval, r))
      break;
  }
  nfree++;
}

// Free the page of physical memory pointed at by v,
// which normally should have been returned by a
// call to kalloc().  (The exception is when
// initializing the allocator; see kinit above.)
static void
kfree_pool(struct kmem *m, char *v)
{
  if ((uptr)v % PGSIZE)
    panic("kfree_pool: misaligned %p", v);
  if (memsize(v) == -1ull)
    panic("kfree_pool: unknown region %p", v);

  // Fill with junk to catch dangling refs.
  if (ALLOC_MEMSET && kinited && m->size <= 16384)
    memset(v, 1, m->size);

  m->free((run*)v);
}

static void
kmemprint_pool(struct kmem *km)
{
  cprintf("pool %s: [ ", &km[0].name[1]);
  for (u32 i = 0; i < NCPU; i++)
    if (i == mycpu()->id)
      cprintf("<%lu> ", km[i].nfree.load());
    else
      cprintf("%lu ", km[i].nfree.load());
  cprintf("]\n");
}

void
kmemprint()
{
  kmemprint_pool(kmems);
  for (int i = 0; i < slab_type_max; i++)
    kmemprint_pool(slabmem[i]);
}

static char*
kalloc_pool(struct kmem *km, const char *name)
{
  struct run *r = 0;
  struct kmem *m;

  u32 startcpu = mycpu()->id;
  for (u32 i = 0; r == 0 && i < NCPU; i++) {
    int cn = (i + startcpu) % NCPU;
    m = &km[cn];
    r = m->alloc(name);
  }

  if (r == 0) {
    cprintf("kalloc: out of memory in pool %s\n", km->name);
    // kmemprint();
    return 0;
  }

  if (ALLOC_MEMSET && m->size <= 16384)
    memset(r, 2, m->size);
  return (char*)r;
}

// Allocate one 4096-byte page of physical memory.
// Returns a pointer that the kernel can use.
// Returns 0 if the memory cannot be allocated.
char*
kalloc(const char *name)
{
  if (!kinited)
    return pgalloc();
  return kalloc_pool(kmems, name);
}

void *
ksalloc(int slab)
{
  return kalloc_pool(slabmem[slab], slabmem[slab]->name);
}

void
slabinit(struct kmem *k, char **p, u64 *off)
{
  for (int i = 0; i < k->ninit; i++) {
    if (*p == (void *)-1)
      panic("slabinit: memnext");
    // XXX(sbw) handle this condition
    if (memsize(p) < k->size)
      panic("slabinit: memsize");
    kfree_pool(k, *p);
    *p = (char*) memnext(*p, k->size);
    *off = *off+k->size;
  }
}

// Initialize free list of physical pages.
void
initkalloc(u64 mbaddr)
{
  char *p;
  u64 n;
  u64 k;

  initmem(mbaddr);

  for (int c = 0; c < NCPU; c++) {
    kmems[c].name[0] = (char) c + '0';
    safestrcpy(kmems[c].name+1, "kmem", MAXNAME-1);
    kmems[c].size = PGSIZE;
  }

  if (VERBOSE)
    cprintf("%lu mbytes\n", membytes / (1<<20));
  n = membytes / NCPU;
  if (n & (PGSIZE-1))
    n = PGROUNDDOWN(n);

  p = (char*)PGROUNDUP((uptr)newend);
  k = (((uptr)p) - KCODE);
  p = (char*) KBASE + k;
  for (int c = 0; c < NCPU; c++) {
    // Fill slab allocators
    strncpy(slabmem[slab_stack][c].name, " kstack", MAXNAME);
    slabmem[slab_stack][c].size = KSTACKSIZE;
    slabmem[slab_stack][c].ninit = CPUKSTACKS;

    strncpy(slabmem[slab_perf][c].name, " kperf", MAXNAME);
    slabmem[slab_perf][c].size = PERFSIZE;
    slabmem[slab_perf][c].ninit = 1;

    strncpy(slabmem[slab_kshared][c].name, " kshared", MAXNAME);
    slabmem[slab_kshared][c].size = KSHAREDSIZE;
    slabmem[slab_kshared][c].ninit = CPUKSTACKS;

    strncpy(slabmem[slab_wq][c].name, " wq", MAXNAME);
    slabmem[slab_wq][c].size = PGROUNDUP(wq_size());
    slabmem[slab_wq][c].ninit = 2;

    strncpy(slabmem[slab_userwq][c].name, " uwq", MAXNAME);
    slabmem[slab_userwq][c].size = USERWQSIZE;
    slabmem[slab_userwq][c].ninit = CPUKSTACKS;

    for (int i = 0; i < slab_type_max; i++) {
      slabmem[i][c].name[0] = (char) c + '0';
      slabinit(&slabmem[i][c], &p, &k);
    }

    // The rest goes to the page allocator
    for (; k != n; k += PGSIZE, p = (char*) memnext(p, PGSIZE)) {
      if (p == (void *)-1)
        panic("initkalloc: e820next");
      kfree_pool(&kmems[c], p);
    }

    k = 0;
  }

  kminit();
  kinited = 1;
}

void
kfree(void *v)
{
  kfree_pool(mykmem(), (char*) v);
}

void
ksfree(int slab, void *v)
{
  kfree_pool(&slabmem[slab][mycpuid()], (char*) v);
}
<commit_msg>Fix the same bug when getting a slab's name in ksalloc<commit_after>//
// Physical page allocator.
// Slab allocator, for chunks larger than one page.
//

#include "types.h"
#include "mmu.h"
#include "kernel.hh"
#include "spinlock.h"
#include "kalloc.hh"
#include "mtrace.h"
#include "cpu.hh"
#include "multiboot.hh"
#include "wq.hh"

static struct Mbmem mem[128];
static u64 nmem;
static u64 membytes;
struct kmem kmems[NCPU];
struct kmem slabmem[slab_type_max][NCPU];

extern char end[]; // first address after kernel loaded from ELF file
char *newend;

static int kinited __mpalign__;

static struct Mbmem *
memsearch(paddr pa)
{
  struct Mbmem *e;
  struct Mbmem *q;

  q = mem+nmem;
  for (e = &mem[0]; e < q; e++)
    if ((e->base <= pa) && ((e->base+e->length) > pa))
      return e;
  return 0;
}

static u64
memsize(void *va)
{
  struct Mbmem *e;
  paddr pa = v2p(va);

  e = memsearch(pa);
  if (e == nullptr)
    return -1;
  return (e->base+e->length) - pa;
}

static void *
memnext(void *va, u64 inc)
{
  struct Mbmem *e, *q;
  paddr pa = v2p(va);

  e = memsearch(pa);
  if (e == nullptr)
    return (void *)-1;

  pa += inc;
  if (pa < (e->base+e->length))
    return p2v(pa);

  q = mem+nmem;
  for (e = e + 1; e < q; e++)
      return p2v(e->base);

  return (void *)-1;
}

static void
initmem(u64 mbaddr)
{
  struct Mbdata *mb;
  struct Mbmem *mbmem;
  u8 *p, *ep;

  mb = (Mbdata*) p2v(mbaddr);
  if(!(mb->flags & (1<<6)))
    panic("multiboot header has no memory map");

  p = (u8*) p2v(mb->mmap_addr);
  ep = p + mb->mmap_length;

  while (p < ep) {
    mbmem = (Mbmem *)(p+4);
    p += 4 + *(u32*)p;
    if (mbmem->type == 1 && mbmem->base >= 0x100000) {
      membytes += mbmem->length;
      mem[nmem] = *mbmem;
      nmem++;
    }
  }
}

// simple page allocator to get off the ground during boot
static char *
pgalloc(void)
{
  if (newend == 0)
    newend = end;

  void *p = (void*)PGROUNDUP((uptr)newend);
  memset(p, 0, PGSIZE);
  newend = newend + PGSIZE;
  return (char*) p;
}

//
// kmem
//
run*
kmem::alloc(const char* name)
{
  run* r;

  for (;;) {
    auto headval = freelist.load();
    r = headval.ptr();
    if (!r)
      return nullptr;

    run *nxt = r->next;
    if (freelist.compare_exchange(headval, nxt)) {
      if (r->next != nxt)
        panic("kmem:alloc: aba race %p %p %p\n",
              r, r->next, nxt);
      nfree--;
      if (name)
        mtlabel(mtrace_label_block, r, size, name, strlen(name));
      return r;
    }
  }
}

void
kmem::free(run* r)
{
  if (kinited)
    mtunlabel(mtrace_label_block, r);

  for (;;) {
    auto headval = freelist.load();
    r->next = headval.ptr();
    if (freelist.compare_exchange(headval, r))
      break;
  }
  nfree++;
}

// Free the page of physical memory pointed at by v,
// which normally should have been returned by a
// call to kalloc().  (The exception is when
// initializing the allocator; see kinit above.)
static void
kfree_pool(struct kmem *m, char *v)
{
  if ((uptr)v % PGSIZE)
    panic("kfree_pool: misaligned %p", v);
  if (memsize(v) == -1ull)
    panic("kfree_pool: unknown region %p", v);

  // Fill with junk to catch dangling refs.
  if (ALLOC_MEMSET && kinited && m->size <= 16384)
    memset(v, 1, m->size);

  m->free((run*)v);
}

static void
kmemprint_pool(struct kmem *km)
{
  cprintf("pool %s: [ ", &km[0].name[1]);
  for (u32 i = 0; i < NCPU; i++)
    if (i == mycpu()->id)
      cprintf("<%lu> ", km[i].nfree.load());
    else
      cprintf("%lu ", km[i].nfree.load());
  cprintf("]\n");
}

void
kmemprint()
{
  kmemprint_pool(kmems);
  for (int i = 0; i < slab_type_max; i++)
    kmemprint_pool(slabmem[i]);
}

static char*
kalloc_pool(struct kmem *km, const char *name)
{
  struct run *r = 0;
  struct kmem *m;

  u32 startcpu = mycpu()->id;
  for (u32 i = 0; r == 0 && i < NCPU; i++) {
    int cn = (i + startcpu) % NCPU;
    m = &km[cn];
    r = m->alloc(name);
  }

  if (r == 0) {
    cprintf("kalloc: out of memory in pool %s\n", km->name);
    // kmemprint();
    return 0;
  }

  if (ALLOC_MEMSET && m->size <= 16384)
    memset(r, 2, m->size);
  return (char*)r;
}

// Allocate one 4096-byte page of physical memory.
// Returns a pointer that the kernel can use.
// Returns 0 if the memory cannot be allocated.
char*
kalloc(const char *name)
{
  if (!kinited)
    return pgalloc();
  return kalloc_pool(kmems, name);
}

void *
ksalloc(int slab)
{
  return kalloc_pool(slabmem[slab], slabmem[slab][mycpuid()].name);
}

void
slabinit(struct kmem *k, char **p, u64 *off)
{
  for (int i = 0; i < k->ninit; i++) {
    if (*p == (void *)-1)
      panic("slabinit: memnext");
    // XXX(sbw) handle this condition
    if (memsize(p) < k->size)
      panic("slabinit: memsize");
    kfree_pool(k, *p);
    *p = (char*) memnext(*p, k->size);
    *off = *off+k->size;
  }
}

// Initialize free list of physical pages.
void
initkalloc(u64 mbaddr)
{
  char *p;
  u64 n;
  u64 k;

  initmem(mbaddr);

  for (int c = 0; c < NCPU; c++) {
    kmems[c].name[0] = (char) c + '0';
    safestrcpy(kmems[c].name+1, "kmem", MAXNAME-1);
    kmems[c].size = PGSIZE;
  }

  if (VERBOSE)
    cprintf("%lu mbytes\n", membytes / (1<<20));
  n = membytes / NCPU;
  if (n & (PGSIZE-1))
    n = PGROUNDDOWN(n);

  p = (char*)PGROUNDUP((uptr)newend);
  k = (((uptr)p) - KCODE);
  p = (char*) KBASE + k;
  for (int c = 0; c < NCPU; c++) {
    // Fill slab allocators
    strncpy(slabmem[slab_stack][c].name, " kstack", MAXNAME);
    slabmem[slab_stack][c].size = KSTACKSIZE;
    slabmem[slab_stack][c].ninit = CPUKSTACKS;

    strncpy(slabmem[slab_perf][c].name, " kperf", MAXNAME);
    slabmem[slab_perf][c].size = PERFSIZE;
    slabmem[slab_perf][c].ninit = 1;

    strncpy(slabmem[slab_kshared][c].name, " kshared", MAXNAME);
    slabmem[slab_kshared][c].size = KSHAREDSIZE;
    slabmem[slab_kshared][c].ninit = CPUKSTACKS;

    strncpy(slabmem[slab_wq][c].name, " wq", MAXNAME);
    slabmem[slab_wq][c].size = PGROUNDUP(wq_size());
    slabmem[slab_wq][c].ninit = 2;

    strncpy(slabmem[slab_userwq][c].name, " uwq", MAXNAME);
    slabmem[slab_userwq][c].size = USERWQSIZE;
    slabmem[slab_userwq][c].ninit = CPUKSTACKS;

    for (int i = 0; i < slab_type_max; i++) {
      slabmem[i][c].name[0] = (char) c + '0';
      slabinit(&slabmem[i][c], &p, &k);
    }

    // The rest goes to the page allocator
    for (; k != n; k += PGSIZE, p = (char*) memnext(p, PGSIZE)) {
      if (p == (void *)-1)
        panic("initkalloc: e820next");
      kfree_pool(&kmems[c], p);
    }

    k = 0;
  }

  kminit();
  kinited = 1;
}

void
kfree(void *v)
{
  kfree_pool(mykmem(), (char*) v);
}

void
ksfree(int slab, void *v)
{
  kfree_pool(&slabmem[slab][mycpuid()], (char*) v);
}
<|endoftext|>


<commit_before>/*
 *  Copyright (c) 2016, Facebook, Inc.
 *  All rights reserved.
 *
 *  This source code is licensed under the BSD-style license found in the
 *  LICENSE file in the root directory of this source tree. An additional grant
 *  of patent rights can be found in the PATENTS file in the same directory.
 *
 */
#include "ConnectionFifo.h"

#include <chrono>

namespace facebook {
namespace memcache {

namespace {

uint64_t timeSinceEpoch() {
  using namespace std::chrono;
  return duration_cast<microseconds>(steady_clock::now().time_since_epoch())
      .count();
}

class PipeIov {
 public:
  const iovec* iov() const {
    return iov_;
  }
  size_t size() const {
    return index_;
  }
  bool full() const {
    return index_ >= kMaxLen || remSize_ == 0;
  }

  void reset() {
    index_ = 0;
    remSize_ = PIPE_BUF;
  }
  size_t append(void* buf, size_t len) {
    assert(!full());

    size_t numBytes = std::min(len, remSize_);
    iov_[index_].iov_base = buf;
    iov_[index_].iov_len = numBytes;
    remSize_ -= numBytes;
    ++index_;
    return numBytes;
  }

 private:
  static constexpr size_t kMaxLen = 16;
  iovec iov_[kMaxLen];
  size_t index_ = 0;
  size_t remSize_ = PIPE_BUF;

  static_assert(
      IOV_MAX >= kMaxLen,
      "IOV_MAX must be larger than PipeIov::kMaxLen.");
};

/**
 * Helper to keep state of iteration over const iovec.
 */
class IovecIterator {
 public:
  IovecIterator(const struct iovec* iov, size_t iovcnt)
      : iov_(iov), iovcnt_(iovcnt) {
    assert(iovcnt > 0);

    curBuf_ = reinterpret_cast<char*>(iov_[0].iov_base);
    curBufLen_ = iov_[0].iov_len;
  }

  char* currentBuffer() const {
    return curBuf_;
  }
  size_t currentBufferLength() const {
    return curBufLen_;
  }
  bool hasData() const {
    return index_ < iovcnt_;
  }

  void advance(size_t bytes) {
    assert(hasData());
    assert(bytes <= curBufLen_);

    if (bytes < curBufLen_) {
      curBuf_ += bytes;
      curBufLen_ -= bytes;
    } else { // (bytes == bufLen)
      ++index_;
      if (hasData()) {
        curBuf_ = reinterpret_cast<char*>(iov_[index_].iov_base);
        curBufLen_ = iov_[index_].iov_len;
      }
    }
  }

 private:
  const struct iovec* iov_;
  const size_t iovcnt_;

  size_t index_{0};
  char* curBuf_{nullptr};
  size_t curBufLen_{0};
};

MessageHeader buildMsgHeader(const folly::AsyncTransportWrapper* transport) {
  MessageHeader header;
  header.setConnectionId(reinterpret_cast<uintptr_t>(transport));

  if (!transport) {
    return header;
  }

  try {
    folly::SocketAddress address;

    transport->getPeerAddress(&address);
    if (address.getFamily() == AF_INET || address.getFamily() == AF_INET6) {
      address.getAddressStr(
          header.peerAddressModifiable(), MessageHeader::kAddressMaxSize);
      header.setPeerPort(address.getPort());
    } else if (address.getFamily() == AF_UNIX) {
      // For unix sockets, just localAddress has the path.
      transport->getLocalAddress(&address);
      std::snprintf(
          header.peerAddressModifiable(),
          MessageHeader::kAddressMaxSize,
          "%s%s",
          kUnixSocketPrefix.data(),
          address.getPath().c_str());
    }

    transport->getLocalAddress(&address);
    header.setLocalPort(address.getPort());
  } catch (const std::exception& e) {
    VLOG(2) << "Error getting host/port to write to debug fifo: " << e.what();
  }

  return header;
}

} // anonymous namespace

ConnectionFifo::ConnectionFifo() noexcept {}

ConnectionFifo::ConnectionFifo(
    std::shared_ptr<Fifo> debugFifo,
    const folly::AsyncTransportWrapper* transport) noexcept
    : debugFifo_(std::move(debugFifo)),
      currentMessageHeader_(buildMsgHeader(transport)) {}

bool ConnectionFifo::isConnected() const noexcept {
  return debugFifo_ && debugFifo_->isConnected();
}

bool ConnectionFifo::startMessage(
    MessageDirection direction,
    uint32_t typeId) noexcept {
  if (!isConnected()) {
    return false;
  }
  currentMessageHeader_.setDirection(direction);
  currentMessageHeader_.setTypeId(typeId);
  currentMessageHeader_.setTimeUs(timeSinceEpoch());
  nextPacketId_ = 0;
  return true;
}

bool ConnectionFifo::writeData(const void* buf, size_t len) noexcept {
  if (!isConnected()) {
    return false;
  }
  iovec iov[1];
  iov[0].iov_base = const_cast<void*>(buf);
  iov[0].iov_len = len;
  return writeData(iov, 1);
}
bool ConnectionFifo::writeData(
    const struct iovec* iov,
    size_t iovcnt) noexcept {
  // This method breaks data into packets and write them to the FIFO in a
  // specific format, described bellow:
  //  - First, the MessageHeader is written, with the following data formatted
  //    in little endian. The order is exactly as stated bellow:
  //      - magic           - Magic bytes to identify that this is a message
  //                          header. Value: 0xfaceb00c
  //      - version         - Version of the protocol.
  //      - peer ipAddress  - char[40] containing a 0-terminated string
  //                          representation of the peer's ip address.
  //      - peer port       - Peer port used for communication.
  //      - connection id   - Id of the connection that this message belongs to.
  //      - local port      - Local port used for communication.
  //      - direction       - Direction of the message (sent or received).
  //      - type id         - Id of the type of the request. For requests,
  //                          it's always odd, for responses it's reqTypeId + 1.
  //      - time            - Time in micros since epoch of when the message
  //                          originated.
  //  - After the message header, the data of the message is divided in
  //    packets of at most PIPE_BUF bytes.
  //  - Each packet is composed of two parts: header and body.
  //    - The packet header contains the following metadata about the packet:
  //      - connection id   - the id of the connection the packet belogs to.
  //                          8 bytes, little endian.
  //      - packet id       - Incremental id of the packet, starting at 0.
  //                          2 bytes, little endian.
  //      - packet size     - The size of the body of the packet.
  //                          2 bytes, little endian.
  //    - The body is the data itself (i.e. contents of iov).
  //  - Each packet is atomically written to the pipe. That means that
  //    the entire packet (header and body) will appear together in the
  //    pipe, as expected. The various packets of a message might be
  //    interleaved with packets from another message.
  //  - The entire message is NOT guaranteed to be written. Even though each
  //    packet is either completely written or not present at all, the message
  //    transmission might be interrupted - meaning that the last packets might
  //    be missing.
  //
  // Here is the layout of a message:
  // ---------------------------------------------------------
  // | MESSAGE HEADER | PACKET 1 | PACKET 2 | ... | PACKET N |
  // ---------------------------------------------------------
  // Where each packet has the following format:
  // -------------------------------
  // | PACKET HEADER | PACKET BODY |
  // -------------------------------

  if (!isConnected()) {
    return false;
  }

  PipeIov pipeIov;
  IovecIterator iovIter(iov, iovcnt);

  // Write header only if this is a brand new message.
  if (nextPacketId_ == 0) {
    pipeIov.append(&currentMessageHeader_, sizeof(MessageHeader));
  }

  // Create packet header
  PacketHeader pkgHeader;
  pkgHeader.setConnectionId(currentMessageHeader_.connectionId());

  // Add packet header
  pipeIov.append(&pkgHeader, sizeof(PacketHeader));

  // Send data
  uint32_t packetSize = 0;
  while (iovIter.hasData()) {
    // Build pipeIov
    while (iovIter.hasData() && !pipeIov.full()) {
      auto bytesAppended = pipeIov.append(iovIter.currentBuffer(),
                                          iovIter.currentBufferLength());
      iovIter.advance(bytesAppended);
      packetSize += bytesAppended;
    }
    pkgHeader.setPacketSize(packetSize);
    pkgHeader.setPacketId(nextPacketId_);

    // Write to pipe
    if (!debugFifo_->write(pipeIov.iov(), pipeIov.size())) {
      return false;
    }

    ++nextPacketId_;
    packetSize = 0;
    pipeIov.reset();
    pipeIov.append(&pkgHeader, sizeof(PacketHeader));
  }

  return true;
}

} // memcache
} // facebook
<commit_msg>Fix write to empty iovec<commit_after>/*
 *  Copyright (c) 2016, Facebook, Inc.
 *  All rights reserved.
 *
 *  This source code is licensed under the BSD-style license found in the
 *  LICENSE file in the root directory of this source tree. An additional grant
 *  of patent rights can be found in the PATENTS file in the same directory.
 *
 */
#include "ConnectionFifo.h"

#include <chrono>

namespace facebook {
namespace memcache {

namespace {

uint64_t timeSinceEpoch() {
  using namespace std::chrono;
  return duration_cast<microseconds>(steady_clock::now().time_since_epoch())
      .count();
}

class PipeIov {
 public:
  const iovec* iov() const {
    return iov_;
  }
  size_t size() const {
    return index_;
  }
  bool full() const {
    return index_ >= kMaxLen || remSize_ == 0;
  }

  void reset() {
    index_ = 0;
    remSize_ = PIPE_BUF;
  }
  size_t append(void* buf, size_t len) {
    assert(!full());

    size_t numBytes = std::min(len, remSize_);
    iov_[index_].iov_base = buf;
    iov_[index_].iov_len = numBytes;
    remSize_ -= numBytes;
    ++index_;
    return numBytes;
  }

 private:
  static constexpr size_t kMaxLen = 16;
  iovec iov_[kMaxLen];
  size_t index_ = 0;
  size_t remSize_ = PIPE_BUF;

  static_assert(
      IOV_MAX >= kMaxLen,
      "IOV_MAX must be larger than PipeIov::kMaxLen.");
};

/**
 * Helper to keep state of iteration over const iovec.
 */
class IovecIterator {
 public:
  IovecIterator(const struct iovec* iov, size_t iovcnt)
      : iov_(iov), iovcnt_(iovcnt) {
    assert(iovcnt > 0);

    curBuf_ = reinterpret_cast<char*>(iov_[0].iov_base);
    curBufLen_ = iov_[0].iov_len;
  }

  char* currentBuffer() const {
    return curBuf_;
  }
  size_t currentBufferLength() const {
    return curBufLen_;
  }
  bool hasData() const {
    return index_ < iovcnt_;
  }

  void advance(size_t bytes) {
    assert(hasData());
    assert(bytes <= curBufLen_);

    if (bytes < curBufLen_) {
      curBuf_ += bytes;
      curBufLen_ -= bytes;
    } else { // (bytes == bufLen)
      ++index_;
      if (hasData()) {
        curBuf_ = reinterpret_cast<char*>(iov_[index_].iov_base);
        curBufLen_ = iov_[index_].iov_len;
      }
    }
  }

 private:
  const struct iovec* iov_;
  const size_t iovcnt_;

  size_t index_{0};
  char* curBuf_{nullptr};
  size_t curBufLen_{0};
};

MessageHeader buildMsgHeader(const folly::AsyncTransportWrapper* transport) {
  MessageHeader header;
  header.setConnectionId(reinterpret_cast<uintptr_t>(transport));

  if (!transport) {
    return header;
  }

  try {
    folly::SocketAddress address;

    transport->getPeerAddress(&address);
    if (address.getFamily() == AF_INET || address.getFamily() == AF_INET6) {
      address.getAddressStr(
          header.peerAddressModifiable(), MessageHeader::kAddressMaxSize);
      header.setPeerPort(address.getPort());
    } else if (address.getFamily() == AF_UNIX) {
      // For unix sockets, just localAddress has the path.
      transport->getLocalAddress(&address);
      std::snprintf(
          header.peerAddressModifiable(),
          MessageHeader::kAddressMaxSize,
          "%s%s",
          kUnixSocketPrefix.data(),
          address.getPath().c_str());
    }

    transport->getLocalAddress(&address);
    header.setLocalPort(address.getPort());
  } catch (const std::exception& e) {
    VLOG(2) << "Error getting host/port to write to debug fifo: " << e.what();
  }

  return header;
}

} // anonymous namespace

ConnectionFifo::ConnectionFifo() noexcept {}

ConnectionFifo::ConnectionFifo(
    std::shared_ptr<Fifo> debugFifo,
    const folly::AsyncTransportWrapper* transport) noexcept
    : debugFifo_(std::move(debugFifo)),
      currentMessageHeader_(buildMsgHeader(transport)) {}

bool ConnectionFifo::isConnected() const noexcept {
  return debugFifo_ && debugFifo_->isConnected();
}

bool ConnectionFifo::startMessage(
    MessageDirection direction,
    uint32_t typeId) noexcept {
  if (!isConnected()) {
    return false;
  }
  currentMessageHeader_.setDirection(direction);
  currentMessageHeader_.setTypeId(typeId);
  currentMessageHeader_.setTimeUs(timeSinceEpoch());
  nextPacketId_ = 0;
  return true;
}

bool ConnectionFifo::writeData(const void* buf, size_t len) noexcept {
  if (!isConnected()) {
    return false;
  }
  iovec iov[1];
  iov[0].iov_base = const_cast<void*>(buf);
  iov[0].iov_len = len;
  return writeData(iov, 1);
}
bool ConnectionFifo::writeData(
    const struct iovec* iov,
    size_t iovcnt) noexcept {
  // This method breaks data into packets and write them to the FIFO in a
  // specific format, described bellow:
  //  - First, the MessageHeader is written, with the following data formatted
  //    in little endian. The order is exactly as stated bellow:
  //      - magic           - Magic bytes to identify that this is a message
  //                          header. Value: 0xfaceb00c
  //      - version         - Version of the protocol.
  //      - peer ipAddress  - char[40] containing a 0-terminated string
  //                          representation of the peer's ip address.
  //      - peer port       - Peer port used for communication.
  //      - connection id   - Id of the connection that this message belongs to.
  //      - local port      - Local port used for communication.
  //      - direction       - Direction of the message (sent or received).
  //      - type id         - Id of the type of the request. For requests,
  //                          it's always odd, for responses it's reqTypeId + 1.
  //      - time            - Time in micros since epoch of when the message
  //                          originated.
  //  - After the message header, the data of the message is divided in
  //    packets of at most PIPE_BUF bytes.
  //  - Each packet is composed of two parts: header and body.
  //    - The packet header contains the following metadata about the packet:
  //      - connection id   - the id of the connection the packet belogs to.
  //                          8 bytes, little endian.
  //      - packet id       - Incremental id of the packet, starting at 0.
  //                          2 bytes, little endian.
  //      - packet size     - The size of the body of the packet.
  //                          2 bytes, little endian.
  //    - The body is the data itself (i.e. contents of iov).
  //  - Each packet is atomically written to the pipe. That means that
  //    the entire packet (header and body) will appear together in the
  //    pipe, as expected. The various packets of a message might be
  //    interleaved with packets from another message.
  //  - The entire message is NOT guaranteed to be written. Even though each
  //    packet is either completely written or not present at all, the message
  //    transmission might be interrupted - meaning that the last packets might
  //    be missing.
  //
  // Here is the layout of a message:
  // ---------------------------------------------------------
  // | MESSAGE HEADER | PACKET 1 | PACKET 2 | ... | PACKET N |
  // ---------------------------------------------------------
  // Where each packet has the following format:
  // -------------------------------
  // | PACKET HEADER | PACKET BODY |
  // -------------------------------

  if (!isConnected() || iovcnt == 0) {
    return false;
  }

  PipeIov pipeIov;
  IovecIterator iovIter(iov, iovcnt);

  // Write header only if this is a brand new message.
  if (nextPacketId_ == 0) {
    pipeIov.append(&currentMessageHeader_, sizeof(MessageHeader));
  }

  // Create packet header
  PacketHeader pkgHeader;
  pkgHeader.setConnectionId(currentMessageHeader_.connectionId());

  // Add packet header
  pipeIov.append(&pkgHeader, sizeof(PacketHeader));

  // Send data
  uint32_t packetSize = 0;
  while (iovIter.hasData()) {
    // Build pipeIov
    while (iovIter.hasData() && !pipeIov.full()) {
      auto bytesAppended = pipeIov.append(iovIter.currentBuffer(),
                                          iovIter.currentBufferLength());
      iovIter.advance(bytesAppended);
      packetSize += bytesAppended;
    }
    pkgHeader.setPacketSize(packetSize);
    pkgHeader.setPacketId(nextPacketId_);

    // Write to pipe
    if (!debugFifo_->write(pipeIov.iov(), pipeIov.size())) {
      return false;
    }

    ++nextPacketId_;
    packetSize = 0;
    pipeIov.reset();
    pipeIov.append(&pkgHeader, sizeof(PacketHeader));
  }

  return true;
}

} // memcache
} // facebook
<|endoftext|>


<commit_before>// MSLParser.cpp : Defines the entry point for the console application.
//

#include "stdafx.h"
#define BOOST_RESULT_OF_USE_DECLTYPE
#define BOOST_SPIRIT_USE_PHOENIX_V3

#include <string>
#include <vector>
#include <map>
#include <memory>
#include <boost/config/warning_disable.hpp>
#include <boost/spirit/include/qi.hpp>
#include <boost/spirit/include/phoenix_core.hpp>
#include <boost/spirit/include/phoenix_operator.hpp>
#include <boost/make_shared.hpp>
#include <boost/fusion/include/std_pair.hpp>

/*
MSL Parser

Wbudowane typy:
- string (cudzys?owia, bez cudzys?owiu)
- float (1. 1 100%)
- boolean (true, false)
- null

- tablica [10 20 30]
- mapa {1:1 2:2 3:3}

- named value   name() value

-komentarze

[name() aaa
*/

namespace msl
{
	class Value
	{
	public:
		using pointer = std::shared_ptr<Value>;
		using ArrayType = std::vector<pointer>;
		using MapType = std::map<pointer, pointer>;

		enum class Type { String, Float, Boolean, Null, Array, Map };

		virtual float asFloat() { return 0.0f; }
		virtual const std::string& asString() { static std::string n = "Null"; return n; };
		virtual const ArrayType& asArray() { throw std::exception(); }
		virtual const MapType& asMap() { throw std::exception(); }

	protected:
		Type _type = Type::Null;
	};

	template<typename T, Value::Type type>
	class TemplatedValue : public Value
	{
	public:
		using base_type = TemplatedValue<T, type>;

		TemplatedValue() : { _type = type; }
		TemplatedValue(const T& t) : _value(t) { _type = type; }
		TemplatedValue(T&& t) : _value(std::move(t)) { _type = type; }

	protected:
		T _value;
	};

	class StringValue : public TemplatedValue<std::string, Value::Type::String>
	{
	public:
		using base_type::TemplatedValue;
		const std::string& asString() override { return _value; }
	};

	class FloatValue : public TemplatedValue<float, Value::Type::Float>
	{
	public:
		using base_type::TemplatedValue;
		float asFloat() override { return _value; }
	};

	class ArrayValue : public TemplatedValue<Value::ArrayType, Value::Type::Array>
	{
	public:
		using base_type::TemplatedValue;
		const ArrayType& asArray() override { return _value; }
	};

	class MapValue : public TemplatedValue<Value::MapType, Value::Type::Map>
	{
	public:
		using base_type::TemplatedValue;
		const MapType& asMap() override { return _value; }
	};
};

namespace client
{

	using namespace boost::spirit;

	template <typename Iterator>
	struct msl_grammar : qi::grammar<Iterator, msl::Value::pointer(), ascii::space_type>
	{
		using standard_rule = qi::rule<Iterator, msl::Value::pointer(), ascii::space_type>;

		template<typename Type>
		auto createAttrSynthesizer()
		{

			return [](auto&& f, auto &c)
			{
				using namespace boost::fusion;
				at_c<0>(c.attributes) = std::make_shared<Type>(f);
			};
		}

		msl_grammar() : msl_grammar::base_type(msl)
		{
			using namespace msl;
			using qi::lit;
			using qi::rule;
			using qi::float_;
			using qi::_1;
			using qi::phrase_parse;
			using ascii::space;
			using ascii::char_;
			using ascii::string;
			using namespace qi::labels;

			//float
			{
				rule_float = float_[createAttrSynthesizer<FloatValue>()];
			}

			//string
			{
				quoted_string %= lexeme['"' >> *(char_ - '"') >> '"'];
				rule_string = quoted_string[createAttrSynthesizer<StringValue>()];
			}

			//array
			{
				rule_varray %= '[' >> *rule_value >> ']';
				rule_array = rule_varray[createAttrSynthesizer<ArrayValue>()];
			}

			//map
			{
				rule_vmap %= qi::lit("{") >> *(rule_value >> qi::lit(":") >> rule_value) >> qi::lit("}");
				rule_map = rule_vmap[createAttrSynthesizer<MapValue>()];
			}

			rule_value = rule_float | rule_array | rule_map | rule_string;
			msl = rule_value;
		}

		standard_rule msl;

		standard_rule rule_value;
		standard_rule rule_float;
		standard_rule rule_string;
		standard_rule rule_array;
		standard_rule rule_map;

		qi::rule<Iterator, msl::Value::MapType(), ascii::space_type> rule_vmap;
		qi::rule<Iterator, msl::Value::ArrayType(), ascii::space_type> rule_varray;
		qi::rule<Iterator, std::string()> quoted_string;
	};

	template <typename Iterator>
	msl::Value::pointer parse_msl(Iterator first, Iterator last)
	{
		msl_grammar<Iterator> msl; // Our grammar
		msl::Value::pointer ast; // Our tree

		using boost::spirit::ascii::space;
		bool r = phrase_parse(first, last, msl, space, ast);

		return ast;
	}

}

int main()
{
	R"foo(

	{
		HP: 100
		Damage: 20

	}

	)foo";

#if 1
	{
		std::string str = "34.1";
		auto p = client::parse_msl(str.begin(), str.end());

		//std::string strArr = "[34.1 {1:1 \"Mateusz\":\"Borycki\"} \"Test\"]";

		std::string strArr = R"foo(

		[
			34.1
			{
				1:1
				"Mateusz":"Borycki"
			}
			"Test"
		]

		)foo";

		auto arr = client::parse_msl(strArr.begin(), strArr.end());
	}

#else
	{
		std::string str = "34.1";
		auto p = client::parse_msl(str.begin(), str.end());
	}

	{
		std::string str = "aaa sadas";
		auto p = client::parse_complex(str.begin(), str.end());
	}

	{
		std::string str = "\"aaa sa\"das";
		auto p = client::parse_complex(str.begin(), str.end());
	}
#endif

    return 0;
}

<commit_msg>added simple unquoted strings<commit_after>// MSLParser.cpp : Defines the entry point for the console application.
//

#include "stdafx.h"
#define BOOST_RESULT_OF_USE_DECLTYPE
#define BOOST_SPIRIT_USE_PHOENIX_V3

#include <string>
#include <vector>
#include <map>
#include <memory>
#include <boost/config/warning_disable.hpp>
#include <boost/spirit/include/qi.hpp>
#include <boost/spirit/include/phoenix_core.hpp>
#include <boost/spirit/include/phoenix_operator.hpp>
#include <boost/spirit/include/qi_char_class.hpp>
#include <boost/make_shared.hpp>
#include <boost/fusion/include/std_pair.hpp>

/*
MSL Parser

Wbudowane typy:
- string (cudzys?owia, bez cudzys?owiu)
- float (1. 1 100%)
- boolean (true, false)
- null

- tablica [10 20 30]
- mapa {1:1 2:2 3:3}

- named value   name() value

-komentarze

[name() aaa
*/

namespace msl
{
	class Value
	{
	public:
		using pointer = std::shared_ptr<Value>;
		using ArrayType = std::vector<pointer>;
		using MapType = std::map<pointer, pointer>;

		enum class Type { String, Float, Boolean, Null, Array, Map };

		virtual float asFloat() { return 0.0f; }
		virtual const std::string& asString() { static std::string n = "Null"; return n; };
		virtual const ArrayType& asArray() { throw std::exception(); }
		virtual const MapType& asMap() { throw std::exception(); }

	protected:
		Type _type = Type::Null;
	};

	template<typename T, Value::Type type>
	class TemplatedValue : public Value
	{
	public:
		using base_type = TemplatedValue<T, type>;

		TemplatedValue() : { _type = type; }
		TemplatedValue(const T& t) : _value(t) { _type = type; }
		TemplatedValue(T&& t) : _value(std::move(t)) { _type = type; }

	protected:
		T _value;
	};

	class StringValue : public TemplatedValue<std::string, Value::Type::String>
	{
	public:
		using base_type::TemplatedValue;
		const std::string& asString() override { return _value; }
	};

	class FloatValue : public TemplatedValue<float, Value::Type::Float>
	{
	public:
		using base_type::TemplatedValue;
		float asFloat() override { return _value; }
	};

	class ArrayValue : public TemplatedValue<Value::ArrayType, Value::Type::Array>
	{
	public:
		using base_type::TemplatedValue;
		const ArrayType& asArray() override { return _value; }
	};

	class MapValue : public TemplatedValue<Value::MapType, Value::Type::Map>
	{
	public:
		using base_type::TemplatedValue;
		const MapType& asMap() override { return _value; }
	};
};

namespace client
{

	using namespace boost::spirit;

	template <typename Iterator>
	struct msl_grammar : qi::grammar<Iterator, msl::Value::pointer(), ascii::space_type>
	{
		using standard_rule = qi::rule<Iterator, msl::Value::pointer(), ascii::space_type>;

		template<typename Type>
		auto createAttrSynthesizer()
		{

			return [](auto&& f, auto &c)
			{
				using namespace boost::fusion;
				at_c<0>(c.attributes) = std::make_shared<Type>(std::move(f));
			};
		}

		msl_grammar() : msl_grammar::base_type(msl)
		{
			using namespace msl;
			using qi::lit;
			using qi::rule;
			using qi::float_;
			using qi::_1;
			using qi::phrase_parse;
			using ascii::space;
			using ascii::char_;
			using ascii::alpha;
			using ascii::string;
			using namespace qi::labels;

			//float
			{
				rule_float = float_[createAttrSynthesizer<FloatValue>()];
			}

			//string
			{
				quoted_string %= lexeme['"' >> *(char_ - '"') >> '"'];
				simple_string %= alpha >> *(char_("a-zA-Z0-9\\.\\-"));

				rule_string = (quoted_string | simple_string)[createAttrSynthesizer<StringValue>()];
			}

			//array
			{
				rule_varray %= '[' >> *rule_value >> ']';
				rule_array = rule_varray[createAttrSynthesizer<ArrayValue>()];
			}

			//map
			{
				rule_vmap %= qi::lit("{") >> *(rule_value >> qi::lit(":") >> rule_value) >> qi::lit("}");
				rule_map = rule_vmap[createAttrSynthesizer<MapValue>()];
			}

			rule_value = rule_float | rule_array | rule_map | rule_string;
			msl = rule_value;
		}

		standard_rule msl;

		standard_rule rule_value;
		standard_rule rule_float;
		standard_rule rule_string;
		standard_rule rule_array;
		standard_rule rule_map;

		qi::rule<Iterator, msl::Value::MapType(), ascii::space_type> rule_vmap;
		qi::rule<Iterator, msl::Value::ArrayType(), ascii::space_type> rule_varray;
		qi::rule<Iterator, std::string()> quoted_string;

		qi::rule<Iterator, std::string()> simple_string;
	};

	template <typename Iterator>
	msl::Value::pointer parse_msl(Iterator first, Iterator last)
	{
		msl_grammar<Iterator> msl; // Our grammar
		msl::Value::pointer ast; // Our tree

		using boost::spirit::ascii::space;
		bool r = phrase_parse(first, last, msl, space, ast);

		return ast;
	}

}

int main()
{
	R"foo(

	{
		HP: 100
		Damage: 20

	}

	)foo";

#if 1
	{
		std::string str = "34.1";
		auto p = client::parse_msl(str.begin(), str.end());

		//std::string strArr = "[34.1 {1:1 \"Mateusz\":\"Borycki\"} \"Test\"]";

		std::string strArr = R"foo(

		[
			Test.1
			34.1
			{
				1:1
				"Mateusz":"Borycki"
			}
			"Test"
		]

		)foo";

		auto arr = client::parse_msl(strArr.begin(), strArr.end());
	}

#else
	{
		std::string str = "34.1";
		auto p = client::parse_msl(str.begin(), str.end());
	}

	{
		std::string str = "aaa sadas";
		auto p = client::parse_complex(str.begin(), str.end());
	}

	{
		std::string str = "\"aaa sa\"das";
		auto p = client::parse_complex(str.begin(), str.end());
	}
#endif

    return 0;
}

<|endoftext|>