<commit_before>#include "DMWriteTask.h"

#include "DMUtil.h"
#include "SkColorPriv.h"
#include "SkCommonFlags.h"
#include "SkData.h"
#include "SkImageEncoder.h"
#include "SkMD5.h"
#include "SkMallocPixelRef.h"
#include "SkOSFile.h"
#include "SkStream.h"
#include "SkString.h"

DEFINE_bool(nameByHash, false, "If true, write .../hash.png instead of .../mode/config/name.png");

namespace DM {

// Splits off the last N suffixes of name (splitting on _) and appends them to out.
// Returns the total number of characters consumed.
static int split_suffixes(int N, const char* name, SkTArray<SkString>* out) {
    SkTArray<SkString> split;
    SkStrSplit(name, "_", &split);
    int consumed = 0;
    for (int i = 0; i < N; i++) {
        // We're splitting off suffixes from the back to front.
        out->push_back(split[split.count()-i-1]);
        consumed += out->back().size() + 1;  // Add one for the _.
    }
    return consumed;
}

inline static SkString find_base_name(const Task& parent, SkTArray<SkString>* suffixList) {
    const int suffixes = parent.depth() + 1;
    const SkString& name = parent.name();
    const int totalSuffixLength = split_suffixes(suffixes, name.c_str(), suffixList);
    return SkString(name.c_str(), name.size() - totalSuffixLength);
}

WriteTask::WriteTask(const Task& parent, const char* sourceType, SkBitmap bitmap)
    : CpuTask(parent)
    , fBaseName(find_base_name(parent, &fSuffixes))
    , fSourceType(sourceType)
    , fBitmap(bitmap)
    , fData(NULL)
    , fExtension(".png") {
}

WriteTask::WriteTask(const Task& parent,
                     const char* sourceType,
                     SkStreamAsset *data,
                     const char* ext)
    : CpuTask(parent)
    , fBaseName(find_base_name(parent, &fSuffixes))
    , fSourceType(sourceType)
    , fData(data)
    , fExtension(ext) {
    SkASSERT(fData.get());
    SkASSERT(fData->unique());
}

void WriteTask::makeDirOrFail(SkString dir) {
    if (!sk_mkdir(dir.c_str())) {
        this->fail();
    }
}

static SkString get_md5(const void* ptr, size_t len) {
    SkMD5 hasher;
    hasher.write(ptr, len);
    SkMD5::Digest digest;
    hasher.finish(digest);

    SkString md5;
    for (int i = 0; i < 16; i++) {
        md5.appendf("%02x", digest.data[i]);
    }
    return md5;
}

struct JsonData {
    SkString name;            // E.g. "ninepatch-stretch", "desk-gws_skp"
    SkString config;          //      "gpu", "8888"
    SkString mode;            //      "direct", "default-tilegrid", "pipe"
    SkString sourceType;      //      "GM", "SKP"
    SkString md5;             // In ASCII, so 32 bytes long.
};
SkTArray<JsonData> gJsonData;
SK_DECLARE_STATIC_MUTEX(gJsonDataLock);

void WriteTask::draw() {
    SkString md5;
    {
        SkAutoLockPixels lock(fBitmap);
        md5 = fData ? get_md5(fData->getMemoryBase(), fData->getLength())
                    : get_md5(fBitmap.getPixels(), fBitmap.getSize());
    }

    SkASSERT(fSuffixes.count() > 0);
    SkString config = fSuffixes.back();
    SkString mode("direct");
    if (fSuffixes.count() > 1) {
        mode = fSuffixes.fromBack(1);
    }

    JsonData entry = { fBaseName, config, mode, fSourceType, md5 };
    {
        SkAutoMutexAcquire lock(&gJsonDataLock);
        gJsonData.push_back(entry);
    }

    SkString dir(FLAGS_writePath[0]);
#if SK_BUILD_FOR_IOS
    if (dir.equals("@")) {
        dir.set(FLAGS_resourcePath[0]);
    }
#endif
    this->makeDirOrFail(dir);

    SkString path;
    if (FLAGS_nameByHash) {
        // Flat directory of hash-named files.
        path = SkOSPath::Join(dir.c_str(), md5.c_str());
        path.append(fExtension);
        // We're content-addressed, so it's possible two threads race to write
        // this file.  We let the first one win.  This also means we won't
        // overwrite identical files from previous runs.
        if (sk_exists(path.c_str())) {
            return;
        }
    } else {
        // Nested by mode, config, etc.
        for (int i = 0; i < fSuffixes.count(); i++) {
            dir = SkOSPath::Join(dir.c_str(), fSuffixes[i].c_str());
            this->makeDirOrFail(dir);
        }
        path = SkOSPath::Join(dir.c_str(), fBaseName.c_str());
        path.append(fExtension);
        // The path is unique, so two threads can't both write to the same file.
        // If already present we overwrite here, since the content may have changed.
    }

    SkFILEWStream file(path.c_str());
    if (!file.isValid()) {
        return this->fail("Can't open file.");
    }

    bool ok = fData ? file.writeStream(fData, fData->getLength())
                    : SkImageEncoder::EncodeStream(&file, fBitmap, SkImageEncoder::kPNG_Type, 100);
    if (!ok) {
        return this->fail("Can't write to file.");
    }
}

SkString WriteTask::name() const {
    SkString name("writing ");
    for (int i = 0; i < fSuffixes.count(); i++) {
        name.appendf("%s/", fSuffixes[i].c_str());
    }
    name.append(fBaseName.c_str());
    return name;
}

bool WriteTask::shouldSkip() const {
    return FLAGS_writePath.isEmpty();
}

void WriteTask::DumpJson() {
    if (FLAGS_writePath.isEmpty()) {
        return;
    }

    Json::Value root;

    for (int i = 1; i < FLAGS_properties.count(); i += 2) {
        root[FLAGS_properties[i-1]] = FLAGS_properties[i];
    }
    for (int i = 1; i < FLAGS_key.count(); i += 2) {
        root["key"][FLAGS_key[i-1]] = FLAGS_key[i];
    }

    {
        SkAutoMutexAcquire lock(&gJsonDataLock);
        for (int i = 0; i < gJsonData.count(); i++) {
            Json::Value result;
            result["key"]["name"]            = gJsonData[i].name.c_str();
            result["key"]["config"]          = gJsonData[i].config.c_str();
            result["key"]["mode"]            = gJsonData[i].mode.c_str();
            result["options"]["source_type"] = gJsonData[i].sourceType.c_str();
            result["md5"]                    = gJsonData[i].md5.c_str();

            root["results"].append(result);
        }
    }

    SkString path = SkOSPath::Join(FLAGS_writePath[0], "dm.json");
    SkFILEWStream stream(path.c_str());
    stream.writeText(Json::StyledWriter().write(root).c_str());
    stream.flush();
}

}  // namespace DM
<commit_msg>Handle getMemoryBase returning NULL in WriteTask.<commit_after>#include "DMWriteTask.h"

#include "DMUtil.h"
#include "SkColorPriv.h"
#include "SkCommonFlags.h"
#include "SkData.h"
#include "SkImageEncoder.h"
#include "SkMD5.h"
#include "SkMallocPixelRef.h"
#include "SkOSFile.h"
#include "SkStream.h"
#include "SkString.h"

DEFINE_bool(nameByHash, false, "If true, write .../hash.png instead of .../mode/config/name.png");

namespace DM {

// Splits off the last N suffixes of name (splitting on _) and appends them to out.
// Returns the total number of characters consumed.
static int split_suffixes(int N, const char* name, SkTArray<SkString>* out) {
    SkTArray<SkString> split;
    SkStrSplit(name, "_", &split);
    int consumed = 0;
    for (int i = 0; i < N; i++) {
        // We're splitting off suffixes from the back to front.
        out->push_back(split[split.count()-i-1]);
        consumed += out->back().size() + 1;  // Add one for the _.
    }
    return consumed;
}

inline static SkString find_base_name(const Task& parent, SkTArray<SkString>* suffixList) {
    const int suffixes = parent.depth() + 1;
    const SkString& name = parent.name();
    const int totalSuffixLength = split_suffixes(suffixes, name.c_str(), suffixList);
    return SkString(name.c_str(), name.size() - totalSuffixLength);
}

WriteTask::WriteTask(const Task& parent, const char* sourceType, SkBitmap bitmap)
    : CpuTask(parent)
    , fBaseName(find_base_name(parent, &fSuffixes))
    , fSourceType(sourceType)
    , fBitmap(bitmap)
    , fData(NULL)
    , fExtension(".png") {
}

WriteTask::WriteTask(const Task& parent,
                     const char* sourceType,
                     SkStreamAsset *data,
                     const char* ext)
    : CpuTask(parent)
    , fBaseName(find_base_name(parent, &fSuffixes))
    , fSourceType(sourceType)
    , fData(data)
    , fExtension(ext) {
    SkASSERT(fData.get());
    SkASSERT(fData->unique());
}

void WriteTask::makeDirOrFail(SkString dir) {
    if (!sk_mkdir(dir.c_str())) {
        this->fail();
    }
}

static SkString get_md5_string(SkMD5* hasher) {
    SkMD5::Digest digest;
    hasher->finish(digest);

    SkString md5;
    for (int i = 0; i < 16; i++) {
        md5.appendf("%02x", digest.data[i]);
    }
    return md5;
}

static SkString get_md5(const void* ptr, size_t len) {
    SkMD5 hasher;
    hasher.write(ptr, len);
    return get_md5_string(&hasher);
}

static SkString get_md5(SkStreamAsset* stream) {
    SkMD5 hasher;
    hasher.writeStream(stream, stream->getLength());
    return get_md5_string(&hasher);
}

struct JsonData {
    SkString name;            // E.g. "ninepatch-stretch", "desk-gws_skp"
    SkString config;          //      "gpu", "8888"
    SkString mode;            //      "direct", "default-tilegrid", "pipe"
    SkString sourceType;      //      "GM", "SKP"
    SkString md5;             // In ASCII, so 32 bytes long.
};
SkTArray<JsonData> gJsonData;
SK_DECLARE_STATIC_MUTEX(gJsonDataLock);

void WriteTask::draw() {
    SkString md5;
    {
        SkAutoLockPixels lock(fBitmap);
        md5 = fData ? get_md5(fData)
                    : get_md5(fBitmap.getPixels(), fBitmap.getSize());
    }

    SkASSERT(fSuffixes.count() > 0);
    SkString config = fSuffixes.back();
    SkString mode("direct");
    if (fSuffixes.count() > 1) {
        mode = fSuffixes.fromBack(1);
    }

    JsonData entry = { fBaseName, config, mode, fSourceType, md5 };
    {
        SkAutoMutexAcquire lock(&gJsonDataLock);
        gJsonData.push_back(entry);
    }

    SkString dir(FLAGS_writePath[0]);
#if SK_BUILD_FOR_IOS
    if (dir.equals("@")) {
        dir.set(FLAGS_resourcePath[0]);
    }
#endif
    this->makeDirOrFail(dir);

    SkString path;
    if (FLAGS_nameByHash) {
        // Flat directory of hash-named files.
        path = SkOSPath::Join(dir.c_str(), md5.c_str());
        path.append(fExtension);
        // We're content-addressed, so it's possible two threads race to write
        // this file.  We let the first one win.  This also means we won't
        // overwrite identical files from previous runs.
        if (sk_exists(path.c_str())) {
            return;
        }
    } else {
        // Nested by mode, config, etc.
        for (int i = 0; i < fSuffixes.count(); i++) {
            dir = SkOSPath::Join(dir.c_str(), fSuffixes[i].c_str());
            this->makeDirOrFail(dir);
        }
        path = SkOSPath::Join(dir.c_str(), fBaseName.c_str());
        path.append(fExtension);
        // The path is unique, so two threads can't both write to the same file.
        // If already present we overwrite here, since the content may have changed.
    }

    SkFILEWStream file(path.c_str());
    if (!file.isValid()) {
        return this->fail("Can't open file.");
    }

    bool ok = fData ? file.writeStream(fData, fData->getLength())
                    : SkImageEncoder::EncodeStream(&file, fBitmap, SkImageEncoder::kPNG_Type, 100);
    if (!ok) {
        return this->fail("Can't write to file.");
    }
}

SkString WriteTask::name() const {
    SkString name("writing ");
    for (int i = 0; i < fSuffixes.count(); i++) {
        name.appendf("%s/", fSuffixes[i].c_str());
    }
    name.append(fBaseName.c_str());
    return name;
}

bool WriteTask::shouldSkip() const {
    return FLAGS_writePath.isEmpty();
}

void WriteTask::DumpJson() {
    if (FLAGS_writePath.isEmpty()) {
        return;
    }

    Json::Value root;

    for (int i = 1; i < FLAGS_properties.count(); i += 2) {
        root[FLAGS_properties[i-1]] = FLAGS_properties[i];
    }
    for (int i = 1; i < FLAGS_key.count(); i += 2) {
        root["key"][FLAGS_key[i-1]] = FLAGS_key[i];
    }

    {
        SkAutoMutexAcquire lock(&gJsonDataLock);
        for (int i = 0; i < gJsonData.count(); i++) {
            Json::Value result;
            result["key"]["name"]            = gJsonData[i].name.c_str();
            result["key"]["config"]          = gJsonData[i].config.c_str();
            result["key"]["mode"]            = gJsonData[i].mode.c_str();
            result["options"]["source_type"] = gJsonData[i].sourceType.c_str();
            result["md5"]                    = gJsonData[i].md5.c_str();

            root["results"].append(result);
        }
    }

    SkString path = SkOSPath::Join(FLAGS_writePath[0], "dm.json");
    SkFILEWStream stream(path.c_str());
    stream.writeText(Json::StyledWriter().write(root).c_str());
    stream.flush();
}

}  // namespace DM
<|endoftext|>


<commit_before>#include <libmesh/equation_systems.h>
#include <libmesh/mesh.h>
#include <libmesh/mesh_communication.h>
#include <libmesh/mesh_generation.h>
#include <libmesh/replicated_mesh.h>
#include <libmesh/dyna_io.h>
#include <libmesh/exodusII_io.h>
#include <libmesh/dof_map.h>

#include "test_comm.h"
#include "libmesh_cppunit.h"

using namespace libMesh;

Number x_plus_y (const Point& p,
                 const Parameters&,
                 const std::string&,
                 const std::string&)
{
  const Real & x = p(0);
  const Real & y = p(1);

  return x + y;
}

class MeshInputTest : public CppUnit::TestCase {
public:
  CPPUNIT_TEST_SUITE( MeshInputTest );

#if LIBMESH_DIM > 1
#ifdef LIBMESH_HAVE_EXODUS_API
  CPPUNIT_TEST( testExodusCopyElementSolution );
#ifndef LIBMESH_USE_COMPLEX_NUMBERS
  // Eventually this will support complex numbers.
  CPPUNIT_TEST( testExodusWriteElementDataFromDiscontinuousNodalData );
#endif
#endif
  CPPUNIT_TEST( testDynaReadMesh );

  CPPUNIT_TEST( testMeshMoveConstructor );
#endif // LIBMESH_DIM > 1

  CPPUNIT_TEST_SUITE_END();

private:

public:
  void setUp()
  {}

  void tearDown()
  {}

#ifdef LIBMESH_HAVE_EXODUS_API
  void testExodusCopyElementSolution ()
  {
    {
      Mesh mesh(*TestCommWorld);

      EquationSystems es(mesh);
      System &sys = es.add_system<System> ("SimpleSystem");
      sys.add_variable("e", CONSTANT, MONOMIAL);

      MeshTools::Generation::build_square (mesh,
                                           3, 3,
                                           0., 1., 0., 1.);

      es.init();
      sys.project_solution(x_plus_y, nullptr, es.parameters);

      ExodusII_IO exii(mesh);

      // Don't try to write element data as nodal data
      std::set<std::string> sys_list;
      exii.write_equation_systems("mesh_with_soln.e", es, &sys_list);

      // Just write it as element data
      exii.write_element_data(es);
    }

    // copy_elemental_solution currently requires ReplicatedMesh
    {
      ReplicatedMesh mesh(*TestCommWorld);

      EquationSystems es(mesh);
      System &sys = es.add_system<System> ("SimpleSystem");
      sys.add_variable("teste", CONSTANT, MONOMIAL);

      ExodusII_IO exii(mesh);

      if (mesh.processor_id() == 0)
        exii.read("mesh_with_soln.e");
      MeshCommunication().broadcast(mesh);
      mesh.prepare_for_use();

      es.init();

      // Read the solution e into variable teste.
      //
      // With complex numbers, we'll only bother reading the real
      // part.
#ifdef LIBMESH_USE_COMPLEX_NUMBERS
      exii.copy_elemental_solution(sys, "teste", "r_e");
#else
      exii.copy_elemental_solution(sys, "teste", "e");
#endif

      // Exodus only handles double precision
      Real exotol = std::max(TOLERANCE*TOLERANCE, Real(1e-12));

      for (Real x = Real(1.L/6.L); x < 1; x += Real(1.L/3.L))
        for (Real y = Real(1.L/6.L); y < 1; y += Real(1.L/3.L))
          {
            Point p(x,y);
            LIBMESH_ASSERT_FP_EQUAL(libmesh_real(sys.point_value(0,p)),
                                    libmesh_real(x+y),
                                    exotol);
          }
    }
  }

#ifndef LIBMESH_USE_COMPLEX_NUMBERS
  void testExodusWriteElementDataFromDiscontinuousNodalData()
  {
    // first scope: write file
    {
      Mesh mesh(*TestCommWorld);

      EquationSystems es(mesh);
      System & sys = es.add_system<System> ("SimpleSystem");
      sys.add_variable("u", FIRST, L2_LAGRANGE);

      MeshTools::Generation::build_cube
        (mesh, 2, 2, 2, 0., 1., 0., 1., 0., 1., HEX8);

      es.init();

      // Set solution u^e_i = i, for the ith vertex of a given element e.
      const DofMap & dof_map = sys.get_dof_map();
      std::vector<dof_id_type> dof_indices;
      for (const auto & elem : mesh.element_ptr_range())
        {
          dof_map.dof_indices(elem, dof_indices, /*var_id=*/0);
          for (unsigned int i=0; i<dof_indices.size(); ++i)
            sys.solution->set(dof_indices[i], i);
        }
      sys.solution->close();

      // Now write to file.
      ExodusII_IO exii(mesh);

      // Don't try to write element data as averaged nodal data.
      std::set<std::string> sys_list;
      exii.write_equation_systems("elemental_from_nodal.e", es, &sys_list);

      // Write one elemental data field per vertex value.
      sys_list = {"SimpleSystem"};

      exii.write_element_data_from_discontinuous_nodal_data
        (es, &sys_list, /*var_suffix=*/"_elem_corner_");
    } // end first scope

    // second scope: read values back in, verify they are correct.
    {
      std::vector<std::string> file_var_names =
        {"u_elem_corner_0",
         "u_elem_corner_1",
         "u_elem_corner_2",
         "u_elem_corner_3"};
      std::vector<Real> expected_values = {0., 1., 2., 3.};

      // copy_elemental_solution currently requires ReplicatedMesh
      ReplicatedMesh mesh(*TestCommWorld);

      EquationSystems es(mesh);
      System & sys = es.add_system<System> ("SimpleSystem");
      for (auto i : index_range(file_var_names))
        sys.add_variable(file_var_names[i], CONSTANT, MONOMIAL);

      ExodusII_IO exii(mesh);

      if (mesh.processor_id() == 0)
        exii.read("elemental_from_nodal.e");
      MeshCommunication().broadcast(mesh);
      mesh.prepare_for_use();

      es.init();

      for (auto i : index_range(file_var_names))
        exii.copy_elemental_solution
          (sys, sys.variable_name(i), file_var_names[i]);

      // Check that the values we read back in are as expected.
      for (const auto & elem : mesh.active_element_ptr_range())
        for (auto i : index_range(file_var_names))
          {
            Real read_val = sys.point_value(i, elem->centroid());
            LIBMESH_ASSERT_FP_EQUAL
              (read_val, expected_values[i], TOLERANCE*TOLERANCE);
          }
    } // end second scope
  } // end testExodusWriteElementDataFromDiscontinuousNodalData

#endif // !LIBMESH_USE_COMPLEX_NUMBERS
#endif // LIBMESH_HAVE_EXODUS_API

  void testDynaReadMesh ()
  {
    Mesh mesh(*TestCommWorld);

    DynaIO dyna(mesh);
    if (mesh.processor_id() == 0)
      dyna.read("1_quad.dyn");
    MeshCommunication().broadcast (mesh);

    mesh.prepare_for_use();

    CPPUNIT_ASSERT_EQUAL(mesh.n_elem(), dof_id_type(1));
    CPPUNIT_ASSERT_EQUAL(mesh.n_nodes(), dof_id_type(9));

    CPPUNIT_ASSERT_EQUAL(mesh.default_mapping_type(),
                         RATIONAL_BERNSTEIN_MAP);

    unsigned char weight_index = mesh.default_mapping_data();

    if (mesh.query_elem_ptr(0))
      {
        const Elem & elem = mesh.elem_ref(0);

        CPPUNIT_ASSERT_EQUAL(elem.type(), QUAD9);
        for (unsigned int n=0; n != 9; ++n)
          CPPUNIT_ASSERT_EQUAL
            (elem.node_ref(n).get_extra_datum<Real>(weight_index),
             Real(0.75));

        CPPUNIT_ASSERT_EQUAL(elem.point(0)(0), Real(0.5));
        CPPUNIT_ASSERT_EQUAL(elem.point(0)(1), Real(0.5));
        CPPUNIT_ASSERT_EQUAL(elem.point(1)(0), Real(1.5));
        CPPUNIT_ASSERT_EQUAL(elem.point(1)(1), Real(0.5));
        CPPUNIT_ASSERT_EQUAL(elem.point(2)(0), Real(1.5));
        CPPUNIT_ASSERT_EQUAL(elem.point(2)(1), Real(1.5));
        CPPUNIT_ASSERT_EQUAL(elem.point(3)(0), Real(0.5));
        CPPUNIT_ASSERT_EQUAL(elem.point(3)(1), Real(1.5));
        CPPUNIT_ASSERT(elem.has_affine_map());
#if LIBMESH_DIM > 2
        for (unsigned int v=0; v != 4; ++v)
          CPPUNIT_ASSERT_EQUAL(elem.point(v)(2), Real(0));
#endif
      }
  }

  void testMeshMoveConstructor ()
  {
    Mesh mesh(*TestCommWorld);
    MeshTools::Generation::build_square (mesh,
                                         3, 3,
                                         0., 1., 0., 1.);

    // Construct mesh2, stealing the resources of the original.
    Mesh mesh2(std::move(mesh));

    // Make sure mesh2 now has the 9 elements.
    CPPUNIT_ASSERT_EQUAL(mesh2.n_elem(),
                         static_cast<dof_id_type>(9));

    // Verify that the moved-from mesh's Partitioner and BoundaryInfo
    // objects were successfully stolen.  Note: moved-from unique_ptrs
    // are guaranteed to compare equal to nullptr, see e.g. Section
    // 20.8.1/4 of the standard.
    // https://stackoverflow.com/questions/24061767/is-unique-ptr-guaranteed-to-store-nullptr-after-move
    CPPUNIT_ASSERT(!mesh.partitioner());
    CPPUNIT_ASSERT(!mesh.boundary_info);
  }
};

CPPUNIT_TEST_SUITE_REGISTRATION( MeshInputTest );
<commit_msg>Unit test for reading a whole .bxt patch<commit_after>#include <libmesh/equation_systems.h>
#include <libmesh/mesh.h>
#include <libmesh/mesh_communication.h>
#include <libmesh/mesh_generation.h>
#include <libmesh/replicated_mesh.h>
#include <libmesh/dyna_io.h>
#include <libmesh/exodusII_io.h>
#include <libmesh/dof_map.h>

#include "test_comm.h"
#include "libmesh_cppunit.h"

using namespace libMesh;

Number x_plus_y (const Point& p,
                 const Parameters&,
                 const std::string&,
                 const std::string&)
{
  const Real & x = p(0);
  const Real & y = p(1);

  return x + y;
}

class MeshInputTest : public CppUnit::TestCase {
public:
  CPPUNIT_TEST_SUITE( MeshInputTest );

#if LIBMESH_DIM > 1
#ifdef LIBMESH_HAVE_EXODUS_API
  CPPUNIT_TEST( testExodusCopyElementSolution );
#ifndef LIBMESH_USE_COMPLEX_NUMBERS
  // Eventually this will support complex numbers.
  CPPUNIT_TEST( testExodusWriteElementDataFromDiscontinuousNodalData );
#endif
#endif
  CPPUNIT_TEST( testDynaReadElem );
  CPPUNIT_TEST( testDynaReadPatch );

  CPPUNIT_TEST( testMeshMoveConstructor );
#endif // LIBMESH_DIM > 1

  CPPUNIT_TEST_SUITE_END();

private:

public:
  void setUp()
  {}

  void tearDown()
  {}

#ifdef LIBMESH_HAVE_EXODUS_API
  void testExodusCopyElementSolution ()
  {
    {
      Mesh mesh(*TestCommWorld);

      EquationSystems es(mesh);
      System &sys = es.add_system<System> ("SimpleSystem");
      sys.add_variable("e", CONSTANT, MONOMIAL);

      MeshTools::Generation::build_square (mesh,
                                           3, 3,
                                           0., 1., 0., 1.);

      es.init();
      sys.project_solution(x_plus_y, nullptr, es.parameters);

      ExodusII_IO exii(mesh);

      // Don't try to write element data as nodal data
      std::set<std::string> sys_list;
      exii.write_equation_systems("mesh_with_soln.e", es, &sys_list);

      // Just write it as element data
      exii.write_element_data(es);
    }

    // copy_elemental_solution currently requires ReplicatedMesh
    {
      ReplicatedMesh mesh(*TestCommWorld);

      EquationSystems es(mesh);
      System &sys = es.add_system<System> ("SimpleSystem");
      sys.add_variable("teste", CONSTANT, MONOMIAL);

      ExodusII_IO exii(mesh);

      if (mesh.processor_id() == 0)
        exii.read("mesh_with_soln.e");
      MeshCommunication().broadcast(mesh);
      mesh.prepare_for_use();

      es.init();

      // Read the solution e into variable teste.
      //
      // With complex numbers, we'll only bother reading the real
      // part.
#ifdef LIBMESH_USE_COMPLEX_NUMBERS
      exii.copy_elemental_solution(sys, "teste", "r_e");
#else
      exii.copy_elemental_solution(sys, "teste", "e");
#endif

      // Exodus only handles double precision
      Real exotol = std::max(TOLERANCE*TOLERANCE, Real(1e-12));

      for (Real x = Real(1.L/6.L); x < 1; x += Real(1.L/3.L))
        for (Real y = Real(1.L/6.L); y < 1; y += Real(1.L/3.L))
          {
            Point p(x,y);
            LIBMESH_ASSERT_FP_EQUAL(libmesh_real(sys.point_value(0,p)),
                                    libmesh_real(x+y),
                                    exotol);
          }
    }
  }

#ifndef LIBMESH_USE_COMPLEX_NUMBERS
  void testExodusWriteElementDataFromDiscontinuousNodalData()
  {
    // first scope: write file
    {
      Mesh mesh(*TestCommWorld);

      EquationSystems es(mesh);
      System & sys = es.add_system<System> ("SimpleSystem");
      sys.add_variable("u", FIRST, L2_LAGRANGE);

      MeshTools::Generation::build_cube
        (mesh, 2, 2, 2, 0., 1., 0., 1., 0., 1., HEX8);

      es.init();

      // Set solution u^e_i = i, for the ith vertex of a given element e.
      const DofMap & dof_map = sys.get_dof_map();
      std::vector<dof_id_type> dof_indices;
      for (const auto & elem : mesh.element_ptr_range())
        {
          dof_map.dof_indices(elem, dof_indices, /*var_id=*/0);
          for (unsigned int i=0; i<dof_indices.size(); ++i)
            sys.solution->set(dof_indices[i], i);
        }
      sys.solution->close();

      // Now write to file.
      ExodusII_IO exii(mesh);

      // Don't try to write element data as averaged nodal data.
      std::set<std::string> sys_list;
      exii.write_equation_systems("elemental_from_nodal.e", es, &sys_list);

      // Write one elemental data field per vertex value.
      sys_list = {"SimpleSystem"};

      exii.write_element_data_from_discontinuous_nodal_data
        (es, &sys_list, /*var_suffix=*/"_elem_corner_");
    } // end first scope

    // second scope: read values back in, verify they are correct.
    {
      std::vector<std::string> file_var_names =
        {"u_elem_corner_0",
         "u_elem_corner_1",
         "u_elem_corner_2",
         "u_elem_corner_3"};
      std::vector<Real> expected_values = {0., 1., 2., 3.};

      // copy_elemental_solution currently requires ReplicatedMesh
      ReplicatedMesh mesh(*TestCommWorld);

      EquationSystems es(mesh);
      System & sys = es.add_system<System> ("SimpleSystem");
      for (auto i : index_range(file_var_names))
        sys.add_variable(file_var_names[i], CONSTANT, MONOMIAL);

      ExodusII_IO exii(mesh);

      if (mesh.processor_id() == 0)
        exii.read("elemental_from_nodal.e");
      MeshCommunication().broadcast(mesh);
      mesh.prepare_for_use();

      es.init();

      for (auto i : index_range(file_var_names))
        exii.copy_elemental_solution
          (sys, sys.variable_name(i), file_var_names[i]);

      // Check that the values we read back in are as expected.
      for (const auto & elem : mesh.active_element_ptr_range())
        for (auto i : index_range(file_var_names))
          {
            Real read_val = sys.point_value(i, elem->centroid());
            LIBMESH_ASSERT_FP_EQUAL
              (read_val, expected_values[i], TOLERANCE*TOLERANCE);
          }
    } // end second scope
  } // end testExodusWriteElementDataFromDiscontinuousNodalData

#endif // !LIBMESH_USE_COMPLEX_NUMBERS
#endif // LIBMESH_HAVE_EXODUS_API

  void testDynaReadElem ()
  {
    Mesh mesh(*TestCommWorld);

    DynaIO dyna(mesh);
    if (mesh.processor_id() == 0)
      dyna.read("1_quad.dyn");
    MeshCommunication().broadcast (mesh);

    mesh.prepare_for_use();

    CPPUNIT_ASSERT_EQUAL(mesh.n_elem(), dof_id_type(1));
    CPPUNIT_ASSERT_EQUAL(mesh.n_nodes(), dof_id_type(9));

    CPPUNIT_ASSERT_EQUAL(mesh.default_mapping_type(),
                         RATIONAL_BERNSTEIN_MAP);

    unsigned char weight_index = mesh.default_mapping_data();

    if (mesh.query_elem_ptr(0))
      {
        const Elem & elem = mesh.elem_ref(0);

        CPPUNIT_ASSERT_EQUAL(elem.type(), QUAD9);
        for (unsigned int n=0; n != 9; ++n)
          CPPUNIT_ASSERT_EQUAL
            (elem.node_ref(n).get_extra_datum<Real>(weight_index),
             Real(0.75));

        CPPUNIT_ASSERT_EQUAL(elem.point(0)(0), Real(0.5));
        CPPUNIT_ASSERT_EQUAL(elem.point(0)(1), Real(0.5));
        CPPUNIT_ASSERT_EQUAL(elem.point(1)(0), Real(1.5));
        CPPUNIT_ASSERT_EQUAL(elem.point(1)(1), Real(0.5));
        CPPUNIT_ASSERT_EQUAL(elem.point(2)(0), Real(1.5));
        CPPUNIT_ASSERT_EQUAL(elem.point(2)(1), Real(1.5));
        CPPUNIT_ASSERT_EQUAL(elem.point(3)(0), Real(0.5));
        CPPUNIT_ASSERT_EQUAL(elem.point(3)(1), Real(1.5));
        CPPUNIT_ASSERT(elem.has_affine_map());
#if LIBMESH_DIM > 2
        for (unsigned int v=0; v != 4; ++v)
          CPPUNIT_ASSERT_EQUAL(elem.point(v)(2), Real(0));
#endif
      }
  }

  void testDynaReadPatch ()
  {
    Mesh mesh(*TestCommWorld);

    DynaIO dyna(mesh);
    if (mesh.processor_id() == 0)
      dyna.read("25_quad.bxt");
    MeshCommunication().broadcast (mesh);

    mesh.prepare_for_use();

    CPPUNIT_ASSERT_EQUAL(mesh.n_elem(), dof_id_type(25));
    CPPUNIT_ASSERT_EQUAL(mesh.n_nodes(), dof_id_type(121));

    CPPUNIT_ASSERT_EQUAL(mesh.default_mapping_type(),
                         RATIONAL_BERNSTEIN_MAP);

    unsigned char weight_index = mesh.default_mapping_data();

    for (const auto & elem : mesh.active_element_ptr_range())
      {
        LIBMESH_ASSERT_FP_EQUAL(libmesh_real(0.04), elem->volume(), TOLERANCE);

        for (unsigned int n=0; n != 9; ++n)
          CPPUNIT_ASSERT_EQUAL
            (elem->node_ref(n).get_extra_datum<Real>(weight_index),
             Real(1.0));

        unsigned int n_neighbors = 0, n_neighbors_expected = 2;
        for (unsigned int side=0; side != 4; ++side)
          if (elem->neighbor_ptr(side))
            n_neighbors++;
        Point c = elem->centroid();

        if (c(0) > 0.2 && c(0) < 0.8)
          n_neighbors_expected++;
        if (c(1) > 0.2 && c(1) < 0.8)
          n_neighbors_expected++;

        CPPUNIT_ASSERT_EQUAL(n_neighbors, n_neighbors_expected);
      }
  }

  void testMeshMoveConstructor ()
  {
    Mesh mesh(*TestCommWorld);
    MeshTools::Generation::build_square (mesh,
                                         3, 3,
                                         0., 1., 0., 1.);

    // Construct mesh2, stealing the resources of the original.
    Mesh mesh2(std::move(mesh));

    // Make sure mesh2 now has the 9 elements.
    CPPUNIT_ASSERT_EQUAL(mesh2.n_elem(),
                         static_cast<dof_id_type>(9));

    // Verify that the moved-from mesh's Partitioner and BoundaryInfo
    // objects were successfully stolen.  Note: moved-from unique_ptrs
    // are guaranteed to compare equal to nullptr, see e.g. Section
    // 20.8.1/4 of the standard.
    // https://stackoverflow.com/questions/24061767/is-unique-ptr-guaranteed-to-store-nullptr-after-move
    CPPUNIT_ASSERT(!mesh.partitioner());
    CPPUNIT_ASSERT(!mesh.boundary_info);
  }
};

CPPUNIT_TEST_SUITE_REGISTRATION( MeshInputTest );
<|endoftext|>


<commit_before>#include<cstdio>
#include<iostream>
#include<cmath>
#include<algorithm>
#include<cstring>
#include<vector>
#include<map>
#include<set>
#include<queue>
#include<stack>
#include<bitset>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
#define ls(x) tree[x].son[0]
#define rs(x) tree[x].son[1]
#define fa(x) tree[x].fa
struct node{
    int son[2],fa,val,siz;
    void clear()
    {
        son[0]=son[1]=fa=val=siz=0;
    }
}tree[3000010];
int root[6010],cnt,arr[301000];
void pushup(int x)
{
    fa(ls(x))=fa(rs(x))=x;
    tree[x].siz=tree[ls(x)].siz+tree[rs(x)].siz+1;
}
void rotate(int x,int row)
{
    int y=fa(x),z=fa(y);
    bool rsx=(rs(y)==x),rsy=(rs(z)==y);
    if(z)
        tree[z].son[rsy]=x,tree[x].fa=z;
    else tree[x].fa=0,root[row]=x;
    tree[y].son[rsx]=tree[x].son[!rsx];
    tree[x].son[!rsx]=y;
    pushup(y);
    pushup(x);
}
void splay(int x,int row)
{
    while(fa(x))
    {
        if(fa(fa(x)))
            rotate((ls(fa(x))==x)^(ls(fa(fa(x))))?fa(x):x,row);
        rotate(x,row);
    }
}
int kth(int x,int pos)
{
    //printf("x=%d,pos=%d,siz(ls)=%d,ls=%d,rs=%d\n",x,pos,tree[ls(x)].siz,ls(x),rs(x));
    if(tree[ls(x)].siz+1==pos)
        return x;
    if(tree[ls(x)].siz+1>pos)
        return kth(ls(x),pos);
    else return kth(rs(x),pos-1-tree[ls(x)].siz);
}
void append(int row,int pos,int val)
{
    int x=kth(root[row],pos);
    //printf("x=%d pos=%d\n",x,pos);
    splay(x,row);
    tree[++cnt].val=val;
    tree[cnt].son[1]=tree[x].son[1];
    tree[x].son[1]=cnt;
    pushup(cnt);
    pushup(x);
}
int query(int row,int pos)
{
    int x=kth(root[row],pos),y=kth(root[row],pos-1);
    int ret=tree[x].val;
    splay(x,row);
    root[row]=y;
    tree[y].fa=0;
    tree[y].son[1]=tree[x].son[1];
    tree[x].clear();
    pushup(y);
    return ret;
}
void build(int row,int n)
{
    int pre=0;
    for(int i=1;i<=n;i++)
    {
        tree[++cnt].val=arr[i];
        tree[cnt].son[0]=pre;
        pre=cnt;
        pushup(cnt);
    }
    root[row]=pre;
    splay(kth(root[row],n/2),row);
}
int n,m,q,x,y,ans,tmp;
void output(int x)
{
    if(ls(x)) output(ls(x));
    printf("id=%d,val=%d,ls=%d,rs=%d,fa=%d,siz=%d\n",x,tree[x].val,ls(x),rs(x),fa(x),tree[x].siz);
    if(rs(x)) output(rs(x));
}
int main()
{
    //freopen("phalanx.out","w",stdout);
    scanf("%d%d%d",&n,&m,&q);
    for(int i=1;i<=n+2;i++)
        arr[i]=(i-1)*m;
    build(0,n+2);
    for(int i=1;i<=q;i++)
    {
        //printf("%d:\n",i);
        scanf("%d%d",&x,&y);
        if(!root[x])
        {
            for(int j=1;j<=m+1;j++)
                arr[j]=(x-1)*m+j-1;
            build(x,m+1);
        }
        if(y!=m)
        {
            /*puts("1:");
            output(root[x]);
            puts("");*/
            ans=query(x,y+1);
            /*puts("2:");
            output(root[0]);
            puts("");*/
            tmp=query(0,x+1);
            //printf("%d %d\n",ans,tmp);
            //puts("3:");
            /*output(root[0]);
            puts("");*/
            append(0,n,ans);
            /*puts("4:");
            output(root[x]);
            puts("");*/
            append(x,m-1,tmp);
            /*puts("root[x]:");
            output(root[x]);
            puts("root[0]:");
            output(root[0]);*/
        }
        else
        {
            //output(root[0]);
            ans=query(0,x+1);
            printf("%d\n",ans);
            append(0,m,ans);
            /*puts("root[0]:");
            output(root[0]);*/
        }
        //puts("\n");
    }
}
<commit_msg>洛谷 3960<commit_after>#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<cctype>
#include<cmath>
#include<iostream>
#include<algorithm>
#include<vector>
#include<set>
#include<map>
#include<queue>
#include<stack>
#include<cassert>

typedef long long ll;
typedef unsigned long long ull;

using namespace std;

const int MAXN=3E5+10;

#define ls(x) t[x].son[0]
#define rs(x) t[x].son[1]
#define fa(x) t[x].fa
#define rson(x) (rs(fa(x))==x)

struct dat{
    ll v,siz;
    dat(){}
    dat(ll _,ll __){siz=__;v=_;}
    pair<dat,dat> split(ll k) const {return make_pair(dat(v,k),dat(v+k,siz-k));}
};

struct node{
    int son[2],fa;
    ll siz;
    dat v;
}t[MAXN<<5];

int rt[MAXN],cnt,n,m,q;

void pushup(int x) {if(x) t[x].siz=t[ls(x)].siz+t[rs(x)].siz+t[x].v.siz;}

void rotate(int x,int row)
{
    int y=fa(x),z=fa(y);
    bool rsx=rson(x),rsy=rson(y);
    if(z) t[z].son[rsy]=x;
    else rt[row]=x;
    t[y].son[rsx]=t[x].son[!rsx];
    t[x].son[!rsx]=y;
    fa(t[y].son[rsx])=y;
    fa(y)=x;fa(x)=z;
    pushup(y);pushup(x);
}

void splay(int x,int row)
{
    while(fa(x))
    {
        if(fa(fa(x))) rotate(rson(x)^rson(fa(x))?x:fa(x),row);
        rotate(x,row);
    }
    pushup(x);
}

int kth(int x,ll k)
{
    if(t[ls(x)].siz<k&&k<=t[ls(x)].siz+t[x].v.siz) return x;
    if(t[ls(x)].siz+t[x].v.siz>=k) return kth(ls(x),k);
    else return kth(rs(x),k-t[ls(x)].siz-t[x].v.siz);
}

void insert(int p,int row,dat v) //after p(rank)
{
    if(!rt[row])
    {
        rt[row]=++cnt;
        t[cnt].v=v;pushup(cnt);
        return;
    }
    if(p==0)
    {
        int x=rt[row];
        while(ls(x)) x=ls(x);
        splay(x,row);
        int y=++cnt;
        ls(x)=y;fa(y)=x;
        t[y].v=v;pushup(y);
        return;
    }
    int x=kth(rt[row],p);
    splay(x,row);
    if(rs(x))
    {
        x=rs(x);
        while(ls(x)) x=ls(x);
        int y=++cnt;
        fa(y)=x;t[y].v=v;ls(x)=y;
        while(fa(y)) pushup(y),y=fa(y);
        pushup(y);
    }
    else
    {
        int y=++cnt;
        rs(x)=y;
        fa(y)=x;t[y].v=v;
        pushup(y);pushup(x);
    }
}

dat split(int p,int row) //[l,p-1] ,[p,p] ,[p+1,r] return [p,p] p is rank
{
    int x=kth(rt[row],p);
    splay(x,row);
    pair<dat,dat> S=t[x].v.split(p-t[ls(x)].siz-1);
    if(S.first.siz==0)
    {
        if(!ls(x))
        {
            rt[row]=rs(x);
            fa(rs(x))=0;
        }
        else
        {
            int y=ls(x);fa(y)=0;
            while(rs(y)) y=rs(y);
            splay(y,row);
            rs(y)=rs(x);fa(rs(x))=y;
            pushup(y);
        }
    }
    else t[x].v=S.first,pushup(x);
    if(S.second.siz!=1)
    {
        pair<dat,dat> T=S.second.split(1);
        insert(p-1,row,T.second);
        return T.first;
    }
    return S.second;
}

int main()
{
    scanf("%d%d%d",&n,&m,&q);
    for(int i=1;i<=n;i++)
    {
        rt[i]=++cnt;
        t[cnt].v.v=1ll*(i-1)*m+1;
        t[cnt].v.siz=m-1;pushup(cnt);
    }
    for(int i=1;i<=n;i++)
    {
        ++cnt;
        fa(rt[0])=cnt;
        ls(cnt)=rt[0];
        t[cnt].v.v=1ll*i*m;
        t[cnt].v.siz=1;
        pushup(cnt);rt[0]=cnt;
    }
    for(int i=1,x,y;i<=q;i++)
    {
        scanf("%d%d",&x,&y);
        if(y!=m)
        {
            dat p=split(y,x);
            printf("%lld\n",p.v);
            dat q=split(x,0);
            insert(m-2,x,q);
            insert(n-1,0,p);
        }
        else
        {
            dat p=split(x,0);
            printf("%lld\n",p.v);
            insert(n-1,0,p);
        }
    }
}<|endoftext|>


<commit_before>#include "test_utils.h"
#include <QtTest/QtTest>

#include "../src/utils/utils.h"

void Utils::testReadableFileSize() {
    QVERIFY(::readableFileSize(0) == "0B");
    QVERIFY(::readableFileSize(1024) == "1024B");
    QVERIFY(::readableFileSize(1025) == "1KB");
    QVERIFY(::readableFileSize(1<<20) == "1024KB");
    QVERIFY(::readableFileSize(1L<<30) == "1024MB");
    QVERIFY(::readableFileSize(1L<<40) == "1024GB");
}

void Utils::testReadableFileSizeV2() {
    QVERIFY(::readableFileSizeV2(0) == "0 B");
    QVERIFY(::readableFileSizeV2(1) == "1B");
    QVERIFY(::readableFileSizeV2(1<<10) == "1.00K");
    QVERIFY(::readableFileSizeV2(1024 + 512) == "1.50K");
    QVERIFY(::readableFileSizeV2(1<<20) == "1.00M");
    QVERIFY(::readableFileSizeV2(1L<<30) == "1.00G");
    QVERIFY(::readableFileSizeV2(1L<<40) == "1.00T");
    QVERIFY(::readableFileSizeV2(1024 + (1L<<40)) == "1.00T");
    QVERIFY(::readableFileSizeV2(1L<<50) == "1.00P");
}

void Utils::testIncludeUrlParams() {
    QUrl urla(QString("http://example.com"));

    QHash<QString, QString> params;
    params.insert("simple", "c");
    params.insert("withspecial", "a?b");
    params.insert("withspace", "a b");
    // params.insert("withplus", "a+b");

    QUrl urlb = ::includeQueryParams(urla, params);

    QVERIFY(urla.scheme() == urlb.scheme());
    QVERIFY(urla.host() == urlb.host());

    foreach (const QString& key, params.keys()) {
#if (QT_VERSION >= QT_VERSION_CHECK(5, 0, 0))
        QUrlQuery query = QUrlQuery(urlb.query());
        QVERIFY(query.queryItemValue(key) == params[key]);
#else
        QVERIFY(urlb.queryItemValue(key) == params[key]);
#endif
    }
}

QTEST_APPLESS_MAIN(Utils)
<commit_msg>fix the broken test and add more cases for previous change<commit_after>#include "test_utils.h"
#include <QtTest/QtTest>

#include "../src/utils/utils.h"

void Utils::testReadableFileSize() {
    QVERIFY(::readableFileSize(0) == "0B");
    QVERIFY(::readableFileSize(1024) == "1024B");
    QVERIFY(::readableFileSize(1025) == "1KB");
    QVERIFY(::readableFileSize(1<<20) == "1024KB");
    QVERIFY(::readableFileSize(1L<<30) == "1024MB");
    QVERIFY(::readableFileSize(1L<<40) == "1024GB");
}

void Utils::testReadableFileSizeV2() {
    QVERIFY(::readableFileSizeV2(0) == "0 B");
    QVERIFY(::readableFileSizeV2(1) == "1B");
    QVERIFY(::readableFileSizeV2(1<<10) == "1.00K");
    QVERIFY(::readableFileSizeV2(1024 + 512) == "1.50K");
    QVERIFY(::readableFileSizeV2(1<<20) == "1.00M");
    QVERIFY(::readableFileSizeV2(1L<<30) == "1.00G");
    QVERIFY(::readableFileSizeV2(1L<<40) == "1.00T");
    QVERIFY(::readableFileSizeV2(1024 + (1L<<40)) == "1.00T");
    QVERIFY(::readableFileSizeV2(1L<<50) == "1.00P");
}

void Utils::testIncludeUrlParams() {
    QUrl urla(QString("http://example.com"));

    QHash<QString, QString> params;
    params.insert("simple", "c");
    params.insert("withspecial", "a?b");
    params.insert("withspace", "a b");
    params.insert("username", "a123fx b");
    params.insert("password", "!@#+-$%^12&*()qweqesaf\"';`~");
    params.insert("withplus", "a+b");

    QUrl urlb = ::includeQueryParams(urla, params);

    QVERIFY(urla.scheme() == urlb.scheme());
    QVERIFY(urla.host() == urlb.host());

    Q_FOREACH (const QString& key, params.keys()) {
#if (QT_VERSION >= QT_VERSION_CHECK(5, 0, 0))
        QString encoded_key = QUrl::toPercentEncoding(key);
        QString encoded_value = QUrl::toPercentEncoding(params[encoded_key]);
        QUrlQuery query = QUrlQuery(urlb.query());
        QVERIFY(query.queryItemValue(encoded_key, QUrl::FullyEncoded) == encoded_value);
#else
        QVERIFY(urlb.queryItemValue(key) == params[key]);
#endif
    }
}

QTEST_APPLESS_MAIN(Utils)
<|endoftext|>


<commit_before>#include "TrapezoidalPath.hpp"

namespace Planning {

TrapezoidalPath::TrapezoidalPath(Geometry2d::Point startPos, float startSpeed,
                                 Geometry2d::Point endPos, float endSpeed,
                                 const MotionConstraints& constraints)
    : _startPos(startPos),
      _startSpeed(std::min(startSpeed, constraints.maxSpeed)),
      _endPos(endPos),
      _endSpeed(endSpeed),
      _pathLength((startPos - endPos).mag()),
      _maxAcc(constraints.maxAcceleration),
      _maxSpeed(constraints.maxSpeed),
      _pathDirection((endPos - startPos).normalized()),
      // Precalculate the duration of the path
      _duration(Trapezoidal::getTime(_pathLength,  // distance
                                     _pathLength,  // pathLength
                                     _maxSpeed, _maxAcc, _startSpeed,
                                     _endSpeed)) {}

boost::optional<MotionInstant> TrapezoidalPath::evaluate(float time) const {
    float distance;
    float speedOut;
    bool valid = TrapezoidalMotion(_pathLength,  // PathLength
                                   _maxSpeed,    // maxSpeed
                                   _maxAcc,      // maxAcc
                                   time,         // time
                                   _startSpeed,  // startSpeed
                                   _endSpeed,    // endSpeed
                                   distance,     // posOut
                                   speedOut);    // speedOut
    if (!valid) return boost::none;

    return MotionInstant(_pathDirection * distance + _startPos,
                         _pathDirection * speedOut);
}

bool TrapezoidalPath::hit(const Geometry2d::ShapeSet& obstacles, float& hitTime,
                          float initialTime) const {
    for (RJ::Time t = RJ::TimestampToSecs(initialTime) + startTime();
         t < startTime() + RJ::SecsToTimestamp(_duration);
         t += RJ::SecsToTimestamp(0.25)) {
        auto instant = evaluate(RJ::TimestampToSecs(t - startTime()));
        if (instant) {
            for (auto& shape : obstacles.shapes()) {
                hitTime = RJ::TimestampToSecs(t);
                if (shape->hit(instant->pos)) return true;
            }
        }
    }

    return false;
}

std::unique_ptr<Path> TrapezoidalPath::subPath(float startTime,
                                               float endTime) const {
    debugThrow("This function is not implemented");
    return nullptr;
}

}  // namespace Planning
<commit_msg>Fix Trapezoidalmotion hit function<commit_after>#include "TrapezoidalPath.hpp"

namespace Planning {

TrapezoidalPath::TrapezoidalPath(Geometry2d::Point startPos, float startSpeed,
                                 Geometry2d::Point endPos, float endSpeed,
                                 const MotionConstraints& constraints)
    : _startPos(startPos),
      _startSpeed(std::min(startSpeed, constraints.maxSpeed)),
      _endPos(endPos),
      _endSpeed(endSpeed),
      _pathLength((startPos - endPos).mag()),
      _maxAcc(constraints.maxAcceleration),
      _maxSpeed(constraints.maxSpeed),
      _pathDirection((endPos - startPos).normalized()),
      // Precalculate the duration of the path
      _duration(Trapezoidal::getTime(_pathLength,  // distance
                                     _pathLength,  // pathLength
                                     _maxSpeed, _maxAcc, _startSpeed,
                                     _endSpeed)) {}

boost::optional<MotionInstant> TrapezoidalPath::evaluate(float time) const {
    float distance;
    float speedOut;
    bool valid = TrapezoidalMotion(_pathLength,  // PathLength
                                   _maxSpeed,    // maxSpeed
                                   _maxAcc,      // maxAcc
                                   time,         // time
                                   _startSpeed,  // startSpeed
                                   _endSpeed,    // endSpeed
                                   distance,     // posOut
                                   speedOut);    // speedOut
    if (!valid) return boost::none;

    return MotionInstant(_pathDirection * distance + _startPos,
                         _pathDirection * speedOut);
}

bool TrapezoidalPath::hit(const Geometry2d::ShapeSet& obstacles, float& hitTime,
                          float initialTime) const {
    for (float t = initialTime;
         t < _duration;
         t += 0.25) {
        auto instant = evaluate(t);
        if (instant) {
            for (auto& shape : obstacles.shapes()) {
                if (shape->hit(instant->pos)) {
                    hitTime = t;
                    return true;
                }
            }
        }
    }
    return false;
}

std::unique_ptr<Path> TrapezoidalPath::subPath(float startTime,
                                               float endTime) const {
    debugThrow("This function is not implemented");
    return nullptr;
}

}  // namespace Planning
<|endoftext|>


<commit_before>#include <QsLog.h>
#include "ac_sensor_settings.h"

AcSensorSettings::AcSensorSettings(int deviceType, const QString &serial,
								   QObject *parent) :
	QObject(parent),
	mDeviceType(deviceType),
	mSerial(serial),
	mIsMultiPhase(false),
	mHub4Mode(Hub4PhaseL1),
	mPosition(Input1),
	mDeviceInstance(-1),
	mL1Energy(0),
	mL2Energy(0),
	mL3Energy(0),
	mL2Position(Input1),
	mL2DeviceInstance(-1)
{
}

int AcSensorSettings::deviceType() const
{
	return mDeviceType;
}

QString AcSensorSettings::serial() const
{
	return mSerial;
}

QString AcSensorSettings::customName() const
{
	return mCustomName;
}

void AcSensorSettings::setCustomName(const QString &n)
{
	if (mCustomName == n)
		return;
	mCustomName	= n;
	emit customNameChanged();
	emit effectiveCustomNameChanged();
}

QString AcSensorSettings::productName() const
{
	return getProductName(mServiceType, mPosition);
}

QString AcSensorSettings::effectiveCustomName() const
{
	if (!mCustomName.isEmpty())
		return mCustomName;
	return productName();
}

void AcSensorSettings::setEffectiveCustomName(const QString &n)
{
	setCustomName(n == productName() ? QString() : n);
}

QString AcSensorSettings::serviceType() const
{
	return mServiceType;
}

void AcSensorSettings::setServiceType(const QString &t)
{
	if (mServiceType == t)
		return;
	mServiceType = t;
	if (t != "grid")
		setL2ServiceType(QString());
	emit serviceTypeChanged();
	emit productNameChanged();
	if (mCustomName.isEmpty())
		emit effectiveCustomNameChanged();
}

bool AcSensorSettings::isMultiPhase() const
{
	return mIsMultiPhase;
}

void AcSensorSettings::setIsMultiPhase(bool b)
{
	if (mIsMultiPhase == b)
		return;
	mIsMultiPhase = b;
	if (b)
		setL2ServiceType(QString());
	emit isMultiPhaseChanged();
}

QString AcSensorSettings::l2CustomName() const
{
	return mL2CustomName;
}

void AcSensorSettings::setL2CustomName(const QString &v)
{
	if (mL2CustomName == v)
		return;
	mL2CustomName = v;
	emit l2CustomNameChanged();
	emit l2EffectiveCustomNameChanged();
}

QString AcSensorSettings::l2ProductName() const
{
	return getProductName(mL2ServiceType, mL2Position);
}

QString AcSensorSettings::l2EffectiveCustomName() const
{
	if (!mL2CustomName.isEmpty())
		return mL2CustomName;
	return l2ProductName();
}

void AcSensorSettings::setL2EffectiveCustomName(const QString &n)
{
	setL2CustomName(n == l2ProductName() ? QString() : n);
}

QString AcSensorSettings::l2ServiceType() const
{
	return mL2ServiceType;
}

void AcSensorSettings::setL2ServiceType(const QString &v)
{
	if (mL2ServiceType == v)
		return;
	mL2ServiceType = v;
	if (!v.isEmpty())
		setIsMultiPhase(false);
	emit l2ServiceTypeChanged();
	emit l2ProductNameChanged();
	if (mL2CustomName.isEmpty())
		emit l2EffectiveCustomNameChanged();
}

Position AcSensorSettings::l2Position() const
{
	return mL2Position;
}

void AcSensorSettings::setL2Position(Position v)
{
	if (mL2Position == v)
		return;
	mL2Position = v;
	emit l2PositionChanged();
	emit l2ProductNameChanged();
	if (mL2CustomName.isEmpty())
		emit l2EffectiveCustomNameChanged();
}

Hub4Mode AcSensorSettings::hub4Mode() const
{
	return mHub4Mode;
}

void AcSensorSettings::setHub4Mode(Hub4Mode m)
{
	if (mHub4Mode == m)
		return;
	mHub4Mode = m;
	emit hub4ModeChanged();
}

Position AcSensorSettings::position()
{
	return mPosition;
}

void AcSensorSettings::setPosition(Position p)
{
	if (mPosition == p)
		return;
	mPosition = p;
	emit positionChanged();
	emit productNameChanged();
	if (mCustomName.isEmpty())
		emit effectiveCustomNameChanged();
}

int AcSensorSettings::deviceInstance() const
{
	return mDeviceInstance;
}

void AcSensorSettings::setDeviceInstance(int d)
{
	qDebug() << __FUNCTION__ << d << mSerial;
	if (mDeviceInstance == d)
		return;
	mDeviceInstance = d;
	emit deviceInstanceChanged();
}

int AcSensorSettings::l2DeviceInstance() const
{
	return mL2DeviceInstance;
}

void AcSensorSettings::setL2DeviceInstance(int d)
{
	qDebug() << __FUNCTION__ << d << mSerial;
	if (mL2DeviceInstance == d)
		return;
	mL2DeviceInstance = d;
	emit l2DeviceInstanceChanged();
}

double AcSensorSettings::l1ReverseEnergy() const
{
	return mL1Energy;
}

void AcSensorSettings::setL1ReverseEnergy(double e)
{
	if (mL1Energy == e)
		return;
	mL1Energy = e;
	emit l1ReverseEnergyChanged();
}

double AcSensorSettings::l2ReverseEnergy() const
{
	return mL2Energy;
}

void AcSensorSettings::setL2ReverseEnergy(double e)
{
	if (mL2Energy == e)
		return;
	mL2Energy = e;
	emit l2ReverseEnergyChanged();
}

double AcSensorSettings::l3ReverseEnergy() const
{
	return mL3Energy;
}

void AcSensorSettings::setL3ReverseEnergy(double e)
{
	if (mL3Energy == e)
		return;
	mL3Energy = e;
	emit l3ReverseEnergyChanged();
}

double AcSensorSettings::getReverseEnergy(Phase phase) const
{
	switch (phase) {
	case PhaseL1:
		return mL1Energy;
	case PhaseL2:
		return mL2Energy;
	case PhaseL3:
		return mL3Energy;
	default:
		QLOG_ERROR() << "Incorrect phase:" << phase;
		return 0;
	}
}

void AcSensorSettings::setReverseEnergy(Phase phase, double value)
{
	switch (phase) {
	case PhaseL1:
		setL1ReverseEnergy(value);
		break;
	case PhaseL2:
		setL2ReverseEnergy(value);
		break;
	case PhaseL3:
		setL3ReverseEnergy(value);
		break;
	default:
		QLOG_ERROR() << "Incorrect phase:" << phase;
		break;
	}
}

QString AcSensorSettings::getProductName(const QString &serviceType, Position position)
{
	if (serviceType == "grid")
		return "Grid meter";
	if (serviceType == "genset")
		return "Generator";
	if (serviceType == "pvinverter") {
		QString productName = "PV inverter";
		QString p;
		switch (position) {
		case Input1:
			p = "input 1";
			break;
		case Input2:
			p = "input 2";
			break;
		case Output:
			p = "output";
			break;
		default:
			break;
		}
		if (!p.isEmpty())
			productName.append(" on ").append(p);
		return productName;
	}
	return "AC sensor";
}
<commit_msg>Remove qDebug logging<commit_after>#include <QsLog.h>
#include "ac_sensor_settings.h"

AcSensorSettings::AcSensorSettings(int deviceType, const QString &serial,
								   QObject *parent) :
	QObject(parent),
	mDeviceType(deviceType),
	mSerial(serial),
	mIsMultiPhase(false),
	mHub4Mode(Hub4PhaseL1),
	mPosition(Input1),
	mDeviceInstance(-1),
	mL1Energy(0),
	mL2Energy(0),
	mL3Energy(0),
	mL2Position(Input1),
	mL2DeviceInstance(-1)
{
}

int AcSensorSettings::deviceType() const
{
	return mDeviceType;
}

QString AcSensorSettings::serial() const
{
	return mSerial;
}

QString AcSensorSettings::customName() const
{
	return mCustomName;
}

void AcSensorSettings::setCustomName(const QString &n)
{
	if (mCustomName == n)
		return;
	mCustomName	= n;
	emit customNameChanged();
	emit effectiveCustomNameChanged();
}

QString AcSensorSettings::productName() const
{
	return getProductName(mServiceType, mPosition);
}

QString AcSensorSettings::effectiveCustomName() const
{
	if (!mCustomName.isEmpty())
		return mCustomName;
	return productName();
}

void AcSensorSettings::setEffectiveCustomName(const QString &n)
{
	setCustomName(n == productName() ? QString() : n);
}

QString AcSensorSettings::serviceType() const
{
	return mServiceType;
}

void AcSensorSettings::setServiceType(const QString &t)
{
	if (mServiceType == t)
		return;
	mServiceType = t;
	if (t != "grid")
		setL2ServiceType(QString());
	emit serviceTypeChanged();
	emit productNameChanged();
	if (mCustomName.isEmpty())
		emit effectiveCustomNameChanged();
}

bool AcSensorSettings::isMultiPhase() const
{
	return mIsMultiPhase;
}

void AcSensorSettings::setIsMultiPhase(bool b)
{
	if (mIsMultiPhase == b)
		return;
	mIsMultiPhase = b;
	if (b)
		setL2ServiceType(QString());
	emit isMultiPhaseChanged();
}

QString AcSensorSettings::l2CustomName() const
{
	return mL2CustomName;
}

void AcSensorSettings::setL2CustomName(const QString &v)
{
	if (mL2CustomName == v)
		return;
	mL2CustomName = v;
	emit l2CustomNameChanged();
	emit l2EffectiveCustomNameChanged();
}

QString AcSensorSettings::l2ProductName() const
{
	return getProductName(mL2ServiceType, mL2Position);
}

QString AcSensorSettings::l2EffectiveCustomName() const
{
	if (!mL2CustomName.isEmpty())
		return mL2CustomName;
	return l2ProductName();
}

void AcSensorSettings::setL2EffectiveCustomName(const QString &n)
{
	setL2CustomName(n == l2ProductName() ? QString() : n);
}

QString AcSensorSettings::l2ServiceType() const
{
	return mL2ServiceType;
}

void AcSensorSettings::setL2ServiceType(const QString &v)
{
	if (mL2ServiceType == v)
		return;
	mL2ServiceType = v;
	if (!v.isEmpty())
		setIsMultiPhase(false);
	emit l2ServiceTypeChanged();
	emit l2ProductNameChanged();
	if (mL2CustomName.isEmpty())
		emit l2EffectiveCustomNameChanged();
}

Position AcSensorSettings::l2Position() const
{
	return mL2Position;
}

void AcSensorSettings::setL2Position(Position v)
{
	if (mL2Position == v)
		return;
	mL2Position = v;
	emit l2PositionChanged();
	emit l2ProductNameChanged();
	if (mL2CustomName.isEmpty())
		emit l2EffectiveCustomNameChanged();
}

Hub4Mode AcSensorSettings::hub4Mode() const
{
	return mHub4Mode;
}

void AcSensorSettings::setHub4Mode(Hub4Mode m)
{
	if (mHub4Mode == m)
		return;
	mHub4Mode = m;
	emit hub4ModeChanged();
}

Position AcSensorSettings::position()
{
	return mPosition;
}

void AcSensorSettings::setPosition(Position p)
{
	if (mPosition == p)
		return;
	mPosition = p;
	emit positionChanged();
	emit productNameChanged();
	if (mCustomName.isEmpty())
		emit effectiveCustomNameChanged();
}

int AcSensorSettings::deviceInstance() const
{
	return mDeviceInstance;
}

void AcSensorSettings::setDeviceInstance(int d)
{
	if (mDeviceInstance == d)
		return;
	mDeviceInstance = d;
	emit deviceInstanceChanged();
}

int AcSensorSettings::l2DeviceInstance() const
{
	return mL2DeviceInstance;
}

void AcSensorSettings::setL2DeviceInstance(int d)
{
	if (mL2DeviceInstance == d)
		return;
	mL2DeviceInstance = d;
	emit l2DeviceInstanceChanged();
}

double AcSensorSettings::l1ReverseEnergy() const
{
	return mL1Energy;
}

void AcSensorSettings::setL1ReverseEnergy(double e)
{
	if (mL1Energy == e)
		return;
	mL1Energy = e;
	emit l1ReverseEnergyChanged();
}

double AcSensorSettings::l2ReverseEnergy() const
{
	return mL2Energy;
}

void AcSensorSettings::setL2ReverseEnergy(double e)
{
	if (mL2Energy == e)
		return;
	mL2Energy = e;
	emit l2ReverseEnergyChanged();
}

double AcSensorSettings::l3ReverseEnergy() const
{
	return mL3Energy;
}

void AcSensorSettings::setL3ReverseEnergy(double e)
{
	if (mL3Energy == e)
		return;
	mL3Energy = e;
	emit l3ReverseEnergyChanged();
}

double AcSensorSettings::getReverseEnergy(Phase phase) const
{
	switch (phase) {
	case PhaseL1:
		return mL1Energy;
	case PhaseL2:
		return mL2Energy;
	case PhaseL3:
		return mL3Energy;
	default:
		QLOG_ERROR() << "Incorrect phase:" << phase;
		return 0;
	}
}

void AcSensorSettings::setReverseEnergy(Phase phase, double value)
{
	switch (phase) {
	case PhaseL1:
		setL1ReverseEnergy(value);
		break;
	case PhaseL2:
		setL2ReverseEnergy(value);
		break;
	case PhaseL3:
		setL3ReverseEnergy(value);
		break;
	default:
		QLOG_ERROR() << "Incorrect phase:" << phase;
		break;
	}
}

QString AcSensorSettings::getProductName(const QString &serviceType, Position position)
{
	if (serviceType == "grid")
		return "Grid meter";
	if (serviceType == "genset")
		return "Generator";
	if (serviceType == "pvinverter") {
		QString productName = "PV inverter";
		QString p;
		switch (position) {
		case Input1:
			p = "input 1";
			break;
		case Input2:
			p = "input 2";
			break;
		case Output:
			p = "output";
			break;
		default:
			break;
		}
		if (!p.isEmpty())
			productName.append(" on ").append(p);
		return productName;
	}
	return "AC sensor";
}
<|endoftext|>


<commit_before>//
// Aspia Project
// Copyright (C) 2020 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 "base/win/security_helpers.h"

#include "base/logging.h"
#include "base/win/scoped_local.h"
#include "base/win/scoped_object.h"

#include <ObjIdl.h>
#include <sddl.h>

namespace base::win {

namespace {

bool makeScopedAbsoluteSd(const ScopedSd& relative_sd,
                          ScopedSd* absolute_sd,
                          ScopedAcl* dacl,
                          ScopedSid* group,
                          ScopedSid* owner,
                          ScopedAcl* sacl)
{
    // Get buffer sizes.
    DWORD absolute_sd_size = 0;
    DWORD dacl_size = 0;
    DWORD group_size = 0;
    DWORD owner_size = 0;
    DWORD sacl_size = 0;

    if (MakeAbsoluteSD(relative_sd.get(),
                       nullptr,
                       &absolute_sd_size,
                       nullptr,
                       &dacl_size,
                       nullptr,
                       &sacl_size,
                       nullptr,
                       &owner_size,
                       nullptr,
                       &group_size) ||
        GetLastError() != ERROR_INSUFFICIENT_BUFFER)
    {
        PLOG(LS_WARNING) << "MakeAbsoluteSD failed";
        return false;
    }

    // Allocate buffers.
    ScopedSd local_absolute_sd(absolute_sd_size);
    ScopedAcl local_dacl(dacl_size);
    ScopedSid local_group(group_size);
    ScopedSid local_owner(owner_size);
    ScopedAcl local_sacl(sacl_size);

    // Do the conversion.
    if (!MakeAbsoluteSD(relative_sd.get(),
                        local_absolute_sd.get(),
                        &absolute_sd_size,
                        reinterpret_cast<PACL>(local_dacl.get()),
                        &dacl_size,
                        local_sacl.get(),
                        &sacl_size,
                        local_owner.get(),
                        &owner_size,
                        local_group.get(),
                        &group_size))
    {
        PLOG(LS_WARNING) << "MakeAbsoluteSD failed";
        return false;
    }

    absolute_sd->swap(local_absolute_sd);
    dacl->swap(local_dacl);
    group->swap(local_group);
    owner->swap(local_owner);
    sacl->swap(local_sacl);

    return true;
}

} // namespace

bool initializeComSecurity(const wchar_t* security_descriptor,
                           const wchar_t* mandatory_label,
                           bool activate_as_activator)
{
    std::wstring sddl;

    sddl.append(security_descriptor);
    sddl.append(mandatory_label);

    // Convert the SDDL description into a security descriptor in absolute format.
    ScopedSd relative_sd = convertSddlToSd(sddl);
    if (!relative_sd)
    {
        LOG(LS_WARNING) << "Failed to create a security descriptor";
        return false;
    }

    ScopedSd absolute_sd;
    ScopedAcl dacl;
    ScopedSid group;
    ScopedSid owner;
    ScopedAcl sacl;

    if (!makeScopedAbsoluteSd(relative_sd, &absolute_sd, &dacl,
                              &group, &owner, &sacl))
    {
        LOG(LS_WARNING) << "MakeScopedAbsoluteSd failed";
        return false;
    }

    DWORD capabilities = EOAC_DYNAMIC_CLOAKING;
    if (!activate_as_activator)
        capabilities |= EOAC_DISABLE_AAA;

    // Apply the security descriptor and default security settings. See
    // InitializeComSecurity's declaration for details.
    HRESULT result = CoInitializeSecurity(
        absolute_sd.get(),
        -1,        // Let COM choose which authentication services to register.
        nullptr,   // See above.
        nullptr,   // Reserved, must be nullptr.
        RPC_C_AUTHN_LEVEL_PKT_PRIVACY,
        RPC_C_IMP_LEVEL_IDENTIFY,
        nullptr,   // Default authentication information is not provided.
        capabilities,
        nullptr);  // Reserved, must be nullptr
    if (FAILED(result))
    {
        LOG(LS_WARNING) << "CoInitializeSecurity failed: " << SystemError::toString(result);
        return false;
    }

    return true;
}

ScopedSd convertSddlToSd(const std::wstring& sddl)
{
    ScopedLocal<PSECURITY_DESCRIPTOR> raw_sd;
    ULONG length = 0;

    if (!ConvertStringSecurityDescriptorToSecurityDescriptorW(sddl.c_str(), SDDL_REVISION_1,
        raw_sd.recieve(), &length))
    {
        PLOG(LS_WARNING) << "ConvertStringSecurityDescriptorToSecurityDescriptorW failed";
        return ScopedSd();
    }

    ScopedSd sd(length);
    memcpy(sd.get(), raw_sd, length);

    return sd;
}

bool userSidString(std::wstring* user_sid)
{
    // Get the current token.
    ScopedHandle token;
    if (!OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, token.recieve()))
        return false;

    DWORD size = sizeof(TOKEN_USER) + SECURITY_MAX_SID_SIZE;
    std::unique_ptr<BYTE[]> user_bytes = std::make_unique<BYTE[]>(size);

    TOKEN_USER* user = reinterpret_cast<TOKEN_USER*>(user_bytes.get());

    if (!GetTokenInformation(token, TokenUser, user, size, &size))
        return false;

    if (!user->User.Sid)
        return false;

    // Convert the data to a string.
    ScopedLocal<wchar_t*> sid_string;

    if (!ConvertSidToStringSidW(user->User.Sid, sid_string.recieve()))
        return false;

    user_sid->assign(sid_string);
    return true;
}

} // namespace base::win
<commit_msg>Fix warning.<commit_after>//
// Aspia Project
// Copyright (C) 2020 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 "base/win/security_helpers.h"

#include "base/logging.h"
#include "base/win/scoped_local.h"
#include "base/win/scoped_object.h"

#include <ObjIdl.h>
#include <sddl.h>

namespace base::win {

namespace {

bool makeScopedAbsoluteSd(const ScopedSd& relative_sd,
                          ScopedSd* absolute_sd,
                          ScopedAcl* dacl,
                          ScopedSid* group,
                          ScopedSid* owner,
                          ScopedAcl* sacl)
{
    // Get buffer sizes.
    DWORD absolute_sd_size = 0;
    DWORD dacl_size = 0;
    DWORD group_size = 0;
    DWORD owner_size = 0;
    DWORD sacl_size = 0;

    if (MakeAbsoluteSD(relative_sd.get(),
                       nullptr,
                       &absolute_sd_size,
                       nullptr,
                       &dacl_size,
                       nullptr,
                       &sacl_size,
                       nullptr,
                       &owner_size,
                       nullptr,
                       &group_size) ||
        GetLastError() != ERROR_INSUFFICIENT_BUFFER)
    {
        PLOG(LS_WARNING) << "MakeAbsoluteSD failed";
        return false;
    }

    // Allocate buffers.
    ScopedSd local_absolute_sd(absolute_sd_size);
    ScopedAcl local_dacl(dacl_size);
    ScopedSid local_group(group_size);
    ScopedSid local_owner(owner_size);
    ScopedAcl local_sacl(sacl_size);

    // Do the conversion.
    if (!MakeAbsoluteSD(relative_sd.get(),
                        local_absolute_sd.get(),
                        &absolute_sd_size,
                        reinterpret_cast<PACL>(local_dacl.get()),
                        &dacl_size,
                        local_sacl.get(),
                        &sacl_size,
                        local_owner.get(),
                        &owner_size,
                        local_group.get(),
                        &group_size))
    {
        PLOG(LS_WARNING) << "MakeAbsoluteSD failed";
        return false;
    }

    absolute_sd->swap(local_absolute_sd);
    dacl->swap(local_dacl);
    group->swap(local_group);
    owner->swap(local_owner);
    sacl->swap(local_sacl);

    return true;
}

} // namespace

bool initializeComSecurity(const wchar_t* security_descriptor,
                           const wchar_t* mandatory_label,
                           bool activate_as_activator)
{
    std::wstring sddl;

    sddl.append(security_descriptor);
    sddl.append(mandatory_label);

    // Convert the SDDL description into a security descriptor in absolute format.
    ScopedSd relative_sd = convertSddlToSd(sddl);
    if (!relative_sd)
    {
        LOG(LS_WARNING) << "Failed to create a security descriptor";
        return false;
    }

    ScopedSd absolute_sd;
    ScopedAcl dacl;
    ScopedSid group;
    ScopedSid owner;
    ScopedAcl sacl;

    if (!makeScopedAbsoluteSd(relative_sd, &absolute_sd, &dacl,
                              &group, &owner, &sacl))
    {
        LOG(LS_WARNING) << "MakeScopedAbsoluteSd failed";
        return false;
    }

    DWORD capabilities = EOAC_DYNAMIC_CLOAKING;
    if (!activate_as_activator)
        capabilities |= EOAC_DISABLE_AAA;

    // Apply the security descriptor and default security settings. See
    // InitializeComSecurity's declaration for details.
    HRESULT result = CoInitializeSecurity(
        absolute_sd.get(),
        -1,        // Let COM choose which authentication services to register.
        nullptr,   // See above.
        nullptr,   // Reserved, must be nullptr.
        RPC_C_AUTHN_LEVEL_PKT_PRIVACY,
        RPC_C_IMP_LEVEL_IDENTIFY,
        nullptr,   // Default authentication information is not provided.
        capabilities,
        nullptr);  // Reserved, must be nullptr
    if (FAILED(result))
    {
        LOG(LS_WARNING) << "CoInitializeSecurity failed: "
                        << SystemError::toString(static_cast<DWORD>(result));
        return false;
    }

    return true;
}

ScopedSd convertSddlToSd(const std::wstring& sddl)
{
    ScopedLocal<PSECURITY_DESCRIPTOR> raw_sd;
    ULONG length = 0;

    if (!ConvertStringSecurityDescriptorToSecurityDescriptorW(sddl.c_str(), SDDL_REVISION_1,
        raw_sd.recieve(), &length))
    {
        PLOG(LS_WARNING) << "ConvertStringSecurityDescriptorToSecurityDescriptorW failed";
        return ScopedSd();
    }

    ScopedSd sd(length);
    memcpy(sd.get(), raw_sd, length);

    return sd;
}

bool userSidString(std::wstring* user_sid)
{
    // Get the current token.
    ScopedHandle token;
    if (!OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, token.recieve()))
        return false;

    DWORD size = sizeof(TOKEN_USER) + SECURITY_MAX_SID_SIZE;
    std::unique_ptr<BYTE[]> user_bytes = std::make_unique<BYTE[]>(size);

    TOKEN_USER* user = reinterpret_cast<TOKEN_USER*>(user_bytes.get());

    if (!GetTokenInformation(token, TokenUser, user, size, &size))
        return false;

    if (!user->User.Sid)
        return false;

    // Convert the data to a string.
    ScopedLocal<wchar_t*> sid_string;

    if (!ConvertSidToStringSidW(user->User.Sid, sid_string.recieve()))
        return false;

    user_sid->assign(sid_string);
    return true;
}

} // namespace base::win
<|endoftext|>


<commit_before>/*
 *  Copyright (c) 2015, 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 "async.h"

#include <limits.h>

#ifndef IOV_MAX
/* POSIX says IOV_MAX is defined in limits.h, but glibc is broken */
#define __need_IOV_MAX /* must come before including stdio.h */
#endif
#include <stdio.h>

#include <fcntl.h>
#include <inttypes.h>
#include <sys/dir.h>
#include <sys/stat.h>
#include <sys/syscall.h>
#include <sys/types.h>
#include <sys/uio.h>
#include <time.h>
#include <unistd.h>

#include <folly/Conv.h>
#include <folly/File.h>
#include <folly/FileUtil.h>
#include <folly/json.h>
#include <folly/ThreadName.h>
#include <folly/experimental/fibers/EventBaseLoopController.h>

#include "mcrouter/awriter.h"
#include "mcrouter/lib/fbi/cpp/util.h"
#include "mcrouter/McrouterInstance.h"
#include "mcrouter/McrouterLogFailure.h"
#include "mcrouter/proxy.h"
#include "mcrouter/ProxyClientCommon.h"
#include "mcrouter/stats.h"

#define ASYNCLOG_MAGIC  "AS1.0"
#define ASYNCLOG_MAGIC2  "AS2.0"

using folly::dynamic;

namespace facebook { namespace memcache { namespace mcrouter {

AsyncWriter::AsyncWriter(size_t maxQueueSize)
    : maxQueueSize_(maxQueueSize),
      pid_(getpid()),
      fiberManager_(
          folly::make_unique<folly::fibers::EventBaseLoopController>()),
      eventBase_(/* enableTimeMeasurement */ false) {
  auto& c = fiberManager_.loopController();
  dynamic_cast<folly::fibers::EventBaseLoopController&>(c)
      .attachEventBase(eventBase_);
}

AsyncWriter::~AsyncWriter() {
  stop();
  assert(!fiberManager_.hasTasks());
}

void AsyncWriter::stop() {
  {
    std::lock_guard<SFRWriteLock> lock(runLock_.writeLock());
    if (stopped_) {
      return;
    }
    stopped_ = true;
  }

  if (thread_.joinable()) {
    eventBase_.terminateLoopSoon();
    if (pid_ == getpid()) {
      thread_.join();
    } else {
      // fork workaround
      thread_.detach();
    }
  } else {
    while (fiberManager_.hasTasks()) {
      fiberManager_.loopUntilNoReady();
    }
  }
}

bool AsyncWriter::start(folly::StringPiece threadName) {
  std::lock_guard<SFRWriteLock> lock(runLock_.writeLock());
  if (thread_.joinable() || stopped_) {
    return false;
  }

  try {
    thread_ = std::thread([this]() {
      // will return after terminateLoopSoon is called
      eventBase_.loopForever();

      while (fiberManager_.hasTasks()) {
        fiberManager_.loopUntilNoReady();
      }
    });
    folly::setThreadName(thread_.native_handle(), threadName);
  } catch (const std::system_error& e) {
    logFailure(memcache::failure::Category::kSystemError,
               "Can not start AsyncWriter thread {}: {}", threadName, e.what());
    return false;
  }

  return true;
}

bool AsyncWriter::run(std::function<void()> f) {
  std::lock_guard<SFRReadLock> lock(runLock_.readLock());
  if (stopped_) {
    return false;
  }

  if (maxQueueSize_ != 0) {
    auto size = queueSize_.load();
    do {
      if (maxQueueSize_ == size) {
        return false;
      }
    } while (!queueSize_.compare_exchange_weak(size, size + 1));
  }

#ifdef __clang__
#pragma clang diagnostic push // ignore generalized lambda capture warning
#pragma clang diagnostic ignored "-Wc++1y-extensions"
#endif
  fiberManager_.addTaskRemote([this, f_ = std::move(f)]() {
      fiberManager_.runInMainContext(std::move(f_));
      if (maxQueueSize_ != 0) {
        --queueSize_;
      }
  });
#ifdef __clang__
#pragma clang diagnostic pop
#endif
  return true;
}

bool awriter_queue(AsyncWriter* w, awriter_entry_t *e) {
  return w->run([e, w] () {
    if (!w->isActive()) {
      e->callbacks->completed(e, EPIPE);
      return;
    }
    int r = e->callbacks->perform_write(e);
    e->callbacks->completed(e, r);
  });
}

static std::shared_ptr<folly::File> countedfd_new(int fd) {
  if (fd < 0) {
    return nullptr;
  }
  return std::make_shared<folly::File>(fd, true);
}

/** Opens the asynchronous request store.  */
static std::shared_ptr<folly::File> asynclog_open(proxy_t *proxy) {
  char path[PATH_MAX + 1];
  time_t now = time(nullptr);
  pid_t tid = syscall(SYS_gettid);
  struct tm date;
  struct stat st;
  int success = 0;
  int fd = -1;

  if (proxy->async_fd &&
      now - proxy->async_spool_time <= DEFAULT_ASYNCLOG_LIFETIME) {
    return proxy->async_fd;
  }

  if (proxy->async_fd) {
    proxy->async_fd = nullptr;
  }

  localtime_r(&now, &date);
  char hour_path[PATH_MAX+1];
  time_t hour_time = now - (now % 3600);
  if (snprintf(hour_path, PATH_MAX, "%s/%04d%02d%02dT%02d-%lld",
               proxy->opts.async_spool.c_str(),
               date.tm_year + 1900,
               date.tm_mon + 1,
               date.tm_mday,
               date.tm_hour,
               (long long) hour_time) > PATH_MAX) {
    hour_path[PATH_MAX] = '\0';
    LOG(ERROR) << "async log hourly spool path is too long: " << hour_path;
    goto epilogue;
  }

  if (stat(hour_path, &st) != 0) {
    mode_t old_umask = umask(0);
    int ret = mkdir(hour_path, 0777);
    int mkdir_errno = 0;
    if (ret != 0) {
      mkdir_errno = errno;
    }
    if (old_umask != 0) {
      umask(old_umask);
    }
    /* EEXIST is possible due to a race. We don't care. */
    if (ret != 0 && mkdir_errno != EEXIST) {
      LOG(ERROR) << "couldn't create async log hour spool path: " <<
                    hour_path << ". reason: " << strerror(mkdir_errno);
      goto epilogue;
    }
  }

  if (snprintf(path, PATH_MAX, "%s/%04d%02d%02dT%02d%02d%02d-%lld-%s-%s-t%d-%p",
               hour_path,
               date.tm_year + 1900,
               date.tm_mon + 1,
               date.tm_mday,
               date.tm_hour,
               date.tm_min,
               date.tm_sec,
               (long long) now,
               (proxy->router ? proxy->router->opts().service_name.c_str() :
                "unknown"),
               (proxy->router ? proxy->router->opts().router_name.c_str() :
                "unknown"),
               tid,
               proxy) > PATH_MAX) {
    path[PATH_MAX] = '\0';
    LOG(ERROR) << "async log path is too long: " << path;
    goto epilogue;
  }

  /*
   * Just in case, append to the log if it exists
   */
  if (stat(path, &st) != 0) {
    fd = open(path, O_WRONLY | O_CREAT, 0666);
    if (fd < 0) {
      LOG(ERROR) << "Can't create and open async store " << path << ": " <<
                    strerror(errno);
      goto epilogue;
    }
  } else {
    fd = open(path, O_WRONLY | O_APPEND, 0666);
    if (fd < 0) {
      LOG(ERROR) << "Can't re-open async store " << path << ": " <<
                    strerror(errno);
      goto epilogue;
    }
  }

  if (fstat(fd, &st)) {
    LOG(ERROR) << "Can't stat async store " << path << ": " << strerror(errno);
    goto epilogue;
  }
  if (!S_ISREG(st.st_mode)) {
    LOG(ERROR) << "Async store exists but is not a file: " << path << ": " <<
                  strerror(errno);
    goto epilogue;
  }

  proxy->async_fd = countedfd_new(fd);
  if (!proxy->async_fd) {
    LOG(ERROR) << "Unable to allocate memory for async_fd: " << strerror(errno);
    goto epilogue;
  }

  /* Ownership of the descriptor has been passed to prox->async_fd. */
  fd = -1;

  proxy->async_spool_time = now;

  success = 1;

  VLOG(1) << "Opened async store for " << path;

epilogue:
  if (!success) {
    if (fd != -1) {
      close(fd);
    }
    if (proxy->async_fd) {
      proxy->async_fd = nullptr;
    }
  }
  return proxy->async_fd;
}

/** Adds an asynchronous request to the event log. */
void asynclog_delete(proxy_t* proxy,
                     std::shared_ptr<const ProxyClientCommon> pclient,
                     folly::StringPiece key,
                     folly::StringPiece poolName) {
  dynamic json = {};
  const auto& host = pclient->ap.getHost();
  const auto& port = pclient->ap.getPort();

  if (proxy->opts.use_asynclog_version2) {
    json = dynamic::object;
    json["f"] = proxy->opts.router_name;
    json["h"] = folly::sformat("[{}]:{}", host, port);
    json["p"] = poolName.str();
    json["k"] = key.str();
  } else {
    /* ["host", port, escaped_command] */
    json.push_back(host);
    json.push_back(port);
    json.push_back(folly::sformat("delete {}\r\n", key));
  }

  auto fd = asynclog_open(proxy);
  if (!fd) {
    logFailure(proxy->router, memcache::failure::Category::kSystemError,
               "asynclog_open() failed (key {}, pool {})",
               key, poolName);
    return;
  }

  // ["AS1.0", 1289416829.836, "C", ["10.0.0.1", 11302, "delete foo\r\n"]]
  // OR ["AS2.0", 1289416829.836, "C", {"f":"flavor","h":"[10.0.0.1]:11302",
  //                                    "p":"pool_name","k":"foo\r\n"}]
  dynamic jsonOut = {};
  if (proxy->opts.use_asynclog_version2) {
    jsonOut.push_back(ASYNCLOG_MAGIC2);
  } else {
    jsonOut.push_back(ASYNCLOG_MAGIC);
  }

  struct timeval timestamp;
  CHECK(gettimeofday(&timestamp, nullptr) == 0);

  auto timestamp_ms =
    facebook::memcache::to<std::chrono::milliseconds>(timestamp).count();

  jsonOut.push_back(1e-3 * timestamp_ms);
  jsonOut.push_back(std::string("C"));

  jsonOut.push_back(json);

  auto jstr = folly::toJson(jsonOut) + "\n";

  ssize_t size = folly::writeFull(fd->fd(), jstr.data(), jstr.size());
  if (size == -1 || size_t(size) < jstr.size()) {
    logFailure(proxy->router, memcache::failure::Category::kSystemError,
               "Error fully writing asynclog request (key {}, pool {})",
               key, poolName);
  }
}

}}} // facebook::memcache::mcrouter
<commit_msg>asynclog version 2 using flavor_name<commit_after>/*
 *  Copyright (c) 2015, 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 "async.h"

#include <limits.h>

#ifndef IOV_MAX
/* POSIX says IOV_MAX is defined in limits.h, but glibc is broken */
#define __need_IOV_MAX /* must come before including stdio.h */
#endif
#include <stdio.h>

#include <fcntl.h>
#include <inttypes.h>
#include <sys/dir.h>
#include <sys/stat.h>
#include <sys/syscall.h>
#include <sys/types.h>
#include <sys/uio.h>
#include <time.h>
#include <unistd.h>

#include <folly/Conv.h>
#include <folly/File.h>
#include <folly/FileUtil.h>
#include <folly/json.h>
#include <folly/ThreadName.h>
#include <folly/experimental/fibers/EventBaseLoopController.h>

#include "mcrouter/awriter.h"
#include "mcrouter/lib/fbi/cpp/util.h"
#include "mcrouter/McrouterInstance.h"
#include "mcrouter/McrouterLogFailure.h"
#include "mcrouter/proxy.h"
#include "mcrouter/ProxyClientCommon.h"
#include "mcrouter/stats.h"

#define ASYNCLOG_MAGIC  "AS1.0"
#define ASYNCLOG_MAGIC2  "AS2.0"

using folly::dynamic;

namespace facebook { namespace memcache { namespace mcrouter {

AsyncWriter::AsyncWriter(size_t maxQueueSize)
    : maxQueueSize_(maxQueueSize),
      pid_(getpid()),
      fiberManager_(
          folly::make_unique<folly::fibers::EventBaseLoopController>()),
      eventBase_(/* enableTimeMeasurement */ false) {
  auto& c = fiberManager_.loopController();
  dynamic_cast<folly::fibers::EventBaseLoopController&>(c)
      .attachEventBase(eventBase_);
}

AsyncWriter::~AsyncWriter() {
  stop();
  assert(!fiberManager_.hasTasks());
}

void AsyncWriter::stop() {
  {
    std::lock_guard<SFRWriteLock> lock(runLock_.writeLock());
    if (stopped_) {
      return;
    }
    stopped_ = true;
  }

  if (thread_.joinable()) {
    eventBase_.terminateLoopSoon();
    if (pid_ == getpid()) {
      thread_.join();
    } else {
      // fork workaround
      thread_.detach();
    }
  } else {
    while (fiberManager_.hasTasks()) {
      fiberManager_.loopUntilNoReady();
    }
  }
}

bool AsyncWriter::start(folly::StringPiece threadName) {
  std::lock_guard<SFRWriteLock> lock(runLock_.writeLock());
  if (thread_.joinable() || stopped_) {
    return false;
  }

  try {
    thread_ = std::thread([this]() {
      // will return after terminateLoopSoon is called
      eventBase_.loopForever();

      while (fiberManager_.hasTasks()) {
        fiberManager_.loopUntilNoReady();
      }
    });
    folly::setThreadName(thread_.native_handle(), threadName);
  } catch (const std::system_error& e) {
    logFailure(memcache::failure::Category::kSystemError,
               "Can not start AsyncWriter thread {}: {}", threadName, e.what());
    return false;
  }

  return true;
}

bool AsyncWriter::run(std::function<void()> f) {
  std::lock_guard<SFRReadLock> lock(runLock_.readLock());
  if (stopped_) {
    return false;
  }

  if (maxQueueSize_ != 0) {
    auto size = queueSize_.load();
    do {
      if (maxQueueSize_ == size) {
        return false;
      }
    } while (!queueSize_.compare_exchange_weak(size, size + 1));
  }

#ifdef __clang__
#pragma clang diagnostic push // ignore generalized lambda capture warning
#pragma clang diagnostic ignored "-Wc++1y-extensions"
#endif
  fiberManager_.addTaskRemote([this, f_ = std::move(f)]() {
      fiberManager_.runInMainContext(std::move(f_));
      if (maxQueueSize_ != 0) {
        --queueSize_;
      }
  });
#ifdef __clang__
#pragma clang diagnostic pop
#endif
  return true;
}

bool awriter_queue(AsyncWriter* w, awriter_entry_t *e) {
  return w->run([e, w] () {
    if (!w->isActive()) {
      e->callbacks->completed(e, EPIPE);
      return;
    }
    int r = e->callbacks->perform_write(e);
    e->callbacks->completed(e, r);
  });
}

static std::shared_ptr<folly::File> countedfd_new(int fd) {
  if (fd < 0) {
    return nullptr;
  }
  return std::make_shared<folly::File>(fd, true);
}

/** Opens the asynchronous request store.  */
static std::shared_ptr<folly::File> asynclog_open(proxy_t *proxy) {
  char path[PATH_MAX + 1];
  time_t now = time(nullptr);
  pid_t tid = syscall(SYS_gettid);
  struct tm date;
  struct stat st;
  int success = 0;
  int fd = -1;

  if (proxy->async_fd &&
      now - proxy->async_spool_time <= DEFAULT_ASYNCLOG_LIFETIME) {
    return proxy->async_fd;
  }

  if (proxy->async_fd) {
    proxy->async_fd = nullptr;
  }

  localtime_r(&now, &date);
  char hour_path[PATH_MAX+1];
  time_t hour_time = now - (now % 3600);
  if (snprintf(hour_path, PATH_MAX, "%s/%04d%02d%02dT%02d-%lld",
               proxy->opts.async_spool.c_str(),
               date.tm_year + 1900,
               date.tm_mon + 1,
               date.tm_mday,
               date.tm_hour,
               (long long) hour_time) > PATH_MAX) {
    hour_path[PATH_MAX] = '\0';
    LOG(ERROR) << "async log hourly spool path is too long: " << hour_path;
    goto epilogue;
  }

  if (stat(hour_path, &st) != 0) {
    mode_t old_umask = umask(0);
    int ret = mkdir(hour_path, 0777);
    int mkdir_errno = 0;
    if (ret != 0) {
      mkdir_errno = errno;
    }
    if (old_umask != 0) {
      umask(old_umask);
    }
    /* EEXIST is possible due to a race. We don't care. */
    if (ret != 0 && mkdir_errno != EEXIST) {
      LOG(ERROR) << "couldn't create async log hour spool path: " <<
                    hour_path << ". reason: " << strerror(mkdir_errno);
      goto epilogue;
    }
  }

  if (snprintf(path, PATH_MAX, "%s/%04d%02d%02dT%02d%02d%02d-%lld-%s-%s-t%d-%p",
               hour_path,
               date.tm_year + 1900,
               date.tm_mon + 1,
               date.tm_mday,
               date.tm_hour,
               date.tm_min,
               date.tm_sec,
               (long long) now,
               (proxy->router ? proxy->router->opts().service_name.c_str() :
                "unknown"),
               (proxy->router ? proxy->router->opts().router_name.c_str() :
                "unknown"),
               tid,
               proxy) > PATH_MAX) {
    path[PATH_MAX] = '\0';
    LOG(ERROR) << "async log path is too long: " << path;
    goto epilogue;
  }

  /*
   * Just in case, append to the log if it exists
   */
  if (stat(path, &st) != 0) {
    fd = open(path, O_WRONLY | O_CREAT, 0666);
    if (fd < 0) {
      LOG(ERROR) << "Can't create and open async store " << path << ": " <<
                    strerror(errno);
      goto epilogue;
    }
  } else {
    fd = open(path, O_WRONLY | O_APPEND, 0666);
    if (fd < 0) {
      LOG(ERROR) << "Can't re-open async store " << path << ": " <<
                    strerror(errno);
      goto epilogue;
    }
  }

  if (fstat(fd, &st)) {
    LOG(ERROR) << "Can't stat async store " << path << ": " << strerror(errno);
    goto epilogue;
  }
  if (!S_ISREG(st.st_mode)) {
    LOG(ERROR) << "Async store exists but is not a file: " << path << ": " <<
                  strerror(errno);
    goto epilogue;
  }

  proxy->async_fd = countedfd_new(fd);
  if (!proxy->async_fd) {
    LOG(ERROR) << "Unable to allocate memory for async_fd: " << strerror(errno);
    goto epilogue;
  }

  /* Ownership of the descriptor has been passed to prox->async_fd. */
  fd = -1;

  proxy->async_spool_time = now;

  success = 1;

  VLOG(1) << "Opened async store for " << path;

epilogue:
  if (!success) {
    if (fd != -1) {
      close(fd);
    }
    if (proxy->async_fd) {
      proxy->async_fd = nullptr;
    }
  }
  return proxy->async_fd;
}

/** Adds an asynchronous request to the event log. */
void asynclog_delete(proxy_t* proxy,
                     std::shared_ptr<const ProxyClientCommon> pclient,
                     folly::StringPiece key,
                     folly::StringPiece poolName) {
  dynamic json = {};
  const auto& host = pclient->ap.getHost();
  const auto& port = pclient->ap.getPort();

  if (proxy->opts.use_asynclog_version2) {
    json = dynamic::object;
    json["f"] = proxy->opts.flavor_name;
    json["h"] = folly::sformat("[{}]:{}", host, port);
    json["p"] = poolName.str();
    json["k"] = key.str();
  } else {
    /* ["host", port, escaped_command] */
    json.push_back(host);
    json.push_back(port);
    json.push_back(folly::sformat("delete {}\r\n", key));
  }

  auto fd = asynclog_open(proxy);
  if (!fd) {
    logFailure(proxy->router, memcache::failure::Category::kSystemError,
               "asynclog_open() failed (key {}, pool {})",
               key, poolName);
    return;
  }

  // ["AS1.0", 1289416829.836, "C", ["10.0.0.1", 11302, "delete foo\r\n"]]
  // OR ["AS2.0", 1289416829.836, "C", {"f":"flavor","h":"[10.0.0.1]:11302",
  //                                    "p":"pool_name","k":"foo\r\n"}]
  dynamic jsonOut = {};
  if (proxy->opts.use_asynclog_version2) {
    jsonOut.push_back(ASYNCLOG_MAGIC2);
  } else {
    jsonOut.push_back(ASYNCLOG_MAGIC);
  }

  struct timeval timestamp;
  CHECK(gettimeofday(&timestamp, nullptr) == 0);

  auto timestamp_ms =
    facebook::memcache::to<std::chrono::milliseconds>(timestamp).count();

  jsonOut.push_back(1e-3 * timestamp_ms);
  jsonOut.push_back(std::string("C"));

  jsonOut.push_back(json);

  auto jstr = folly::toJson(jsonOut) + "\n";

  ssize_t size = folly::writeFull(fd->fd(), jstr.data(), jstr.size());
  if (size == -1 || size_t(size) < jstr.size()) {
    logFailure(proxy->router, memcache::failure::Category::kSystemError,
               "Error fully writing asynclog request (key {}, pool {})",
               key, poolName);
  }
}

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