<commit_before>/*
 *
 * Copyright 2015, Google Inc.
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are
 * met:
 *
 *     * Redistributions of source code must retain the above copyright
 * notice, this list of conditions and the following disclaimer.
 *     * Redistributions in binary form must reproduce the above
 * copyright notice, this list of conditions and the following disclaimer
 * in the documentation and/or other materials provided with the
 * distribution.
 *     * Neither the name of Google Inc. nor the names of its
 * contributors may be used to endorse or promote products derived from
 * this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 */

#include <node.h>

#include "grpc/grpc.h"
#include "grpc/grpc_security.h"
#include "grpc/support/log.h"
#include "server_credentials.h"

namespace grpc {
namespace node {

using Nan::Callback;
using Nan::EscapableHandleScope;
using Nan::HandleScope;
using Nan::Maybe;
using Nan::MaybeLocal;
using Nan::ObjectWrap;
using Nan::Persistent;
using Nan::Utf8String;

using v8::Array;
using v8::Exception;
using v8::External;
using v8::Function;
using v8::FunctionTemplate;
using v8::Integer;
using v8::Local;
using v8::Object;
using v8::ObjectTemplate;
using v8::String;
using v8::Value;

Nan::Callback *ServerCredentials::constructor;
Persistent<FunctionTemplate> ServerCredentials::fun_tpl;

ServerCredentials::ServerCredentials(grpc_server_credentials *credentials)
    : wrapped_credentials(credentials) {}

ServerCredentials::~ServerCredentials() {
  grpc_server_credentials_release(wrapped_credentials);
}

void ServerCredentials::Init(Local<Object> exports) {
  Nan::HandleScope scope;
  Local<FunctionTemplate> tpl = Nan::New<FunctionTemplate>(New);
  tpl->SetClassName(Nan::New("ServerCredentials").ToLocalChecked());
  tpl->InstanceTemplate()->SetInternalFieldCount(1);
  Local<Function> ctr = tpl->GetFunction();
  Nan::Set(ctr, Nan::New("createSsl").ToLocalChecked(),
           Nan::GetFunction(
               Nan::New<FunctionTemplate>(CreateSsl)).ToLocalChecked());
  Nan::Set(ctr, Nan::New("createInsecure").ToLocalChecked(),
           Nan::GetFunction(
               Nan::New<FunctionTemplate>(CreateInsecure)).ToLocalChecked());
  fun_tpl.Reset(tpl);
  constructor = new Nan::Callback(ctr);
  Nan::Set(exports, Nan::New("ServerCredentials").ToLocalChecked(), ctr);
}

bool ServerCredentials::HasInstance(Local<Value> val) {
  Nan::HandleScope scope;
  return Nan::New(fun_tpl)->HasInstance(val);
}

Local<Value> ServerCredentials::WrapStruct(
    grpc_server_credentials *credentials) {
  Nan::EscapableHandleScope scope;
  const int argc = 1;
  Local<Value> argv[argc] = {
    Nan::New<External>(reinterpret_cast<void *>(credentials))};
  MaybeLocal<Object> maybe_instance = Nan::NewInstance(
      constructor->GetFunction(), argc, argv);
  if (maybe_instance.IsEmpty()) {
    return scope.Escape(Nan::Null());
  } else {
    return scope.Escape(maybe_instance.ToLocalChecked());
  }
}

grpc_server_credentials *ServerCredentials::GetWrappedServerCredentials() {
  return wrapped_credentials;
}

NAN_METHOD(ServerCredentials::New) {
  if (info.IsConstructCall()) {
    if (!info[0]->IsExternal()) {
      return Nan::ThrowTypeError(
          "ServerCredentials can only be created with the provided functions");
    }
    Local<External> ext = info[0].As<External>();
    grpc_server_credentials *creds_value =
        reinterpret_cast<grpc_server_credentials *>(ext->Value());
    ServerCredentials *credentials = new ServerCredentials(creds_value);
    credentials->Wrap(info.This());
    info.GetReturnValue().Set(info.This());
  } else {
    // This should never be called directly
    return Nan::ThrowTypeError(
        "ServerCredentials can only be created with the provided functions");
  }
}

NAN_METHOD(ServerCredentials::CreateSsl) {
  Nan::HandleScope scope;
  char *root_certs = NULL;
  if (::node::Buffer::HasInstance(info[0])) {
    root_certs = ::node::Buffer::Data(info[0]);
  } else if (!(info[0]->IsNull() || info[0]->IsUndefined())) {
    return Nan::ThrowTypeError(
        "createSSl's first argument must be a Buffer if provided");
  }
  if (!info[1]->IsArray()) {
    return Nan::ThrowTypeError(
        "createSsl's second argument must be a list of objects");
  }

  // Default to not requesting the client certificate
  grpc_ssl_client_certificate_request_type client_certificate_request =
      GRPC_SSL_DONT_REQUEST_CLIENT_CERTIFICATE;
  if (info[2]->IsBoolean()) {
    client_certificate_request =
        Nan::To<bool>(info[2]).FromJust()
            ? GRPC_SSL_REQUEST_AND_REQUIRE_CLIENT_CERTIFICATE_AND_VERIFY
            : GRPC_SSL_DONT_REQUEST_CLIENT_CERTIFICATE;
  } else if (!(info[2]->IsUndefined() || info[2]->IsNull())) {
    return Nan::ThrowTypeError(
        "createSsl's third argument must be a boolean if provided");
  }
  Local<Array> pair_list = Local<Array>::Cast(info[1]);
  uint32_t key_cert_pair_count = pair_list->Length();
  grpc_ssl_pem_key_cert_pair *key_cert_pairs = new grpc_ssl_pem_key_cert_pair[
      key_cert_pair_count];

  Local<String> key_key = Nan::New("private_key").ToLocalChecked();
  Local<String> cert_key = Nan::New("cert_chain").ToLocalChecked();

  for(uint32_t i = 0; i < key_cert_pair_count; i++) {
    Local<Value> pair_val = Nan::Get(pair_list, i).ToLocalChecked();
    if (!pair_val->IsObject()) {
      delete key_cert_pairs;
      return Nan::ThrowTypeError("Key/cert pairs must be objects");
    }
    Local<Object> pair_obj = Nan::To<Object>(pair_val).ToLocalChecked();
    Local<Value> maybe_key = Nan::Get(pair_obj, key_key).ToLocalChecked();
    Local<Value> maybe_cert = Nan::Get(pair_obj, cert_key).ToLocalChecked();
    if (!::node::Buffer::HasInstance(maybe_key)) {
      delete key_cert_pairs;
      return Nan::ThrowTypeError("private_key must be a Buffer");
    }
    if (!::node::Buffer::HasInstance(maybe_cert)) {
      delete key_cert_pairs;
      return Nan::ThrowTypeError("cert_chain must be a Buffer");
    }
    key_cert_pairs[i].private_key = ::node::Buffer::Data(maybe_key);
    key_cert_pairs[i].cert_chain = ::node::Buffer::Data(maybe_cert);
  }
  grpc_server_credentials *creds = grpc_ssl_server_credentials_create_ex(
      root_certs, key_cert_pairs, key_cert_pair_count,
      client_certificate_request, NULL);
  delete key_cert_pairs;
  if (creds == NULL) {
    info.GetReturnValue().SetNull();
  } else {
    info.GetReturnValue().Set(WrapStruct(creds));
  }
}

NAN_METHOD(ServerCredentials::CreateInsecure) {
  info.GetReturnValue().Set(WrapStruct(NULL));
}

}  // namespace node
}  // namespace grpc
<commit_msg>Fix mismatched new[] and delete in Node extension code<commit_after>/*
 *
 * Copyright 2015, Google Inc.
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are
 * met:
 *
 *     * Redistributions of source code must retain the above copyright
 * notice, this list of conditions and the following disclaimer.
 *     * Redistributions in binary form must reproduce the above
 * copyright notice, this list of conditions and the following disclaimer
 * in the documentation and/or other materials provided with the
 * distribution.
 *     * Neither the name of Google Inc. nor the names of its
 * contributors may be used to endorse or promote products derived from
 * this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 */

#include <node.h>

#include "grpc/grpc.h"
#include "grpc/grpc_security.h"
#include "grpc/support/log.h"
#include "server_credentials.h"

namespace grpc {
namespace node {

using Nan::Callback;
using Nan::EscapableHandleScope;
using Nan::HandleScope;
using Nan::Maybe;
using Nan::MaybeLocal;
using Nan::ObjectWrap;
using Nan::Persistent;
using Nan::Utf8String;

using v8::Array;
using v8::Exception;
using v8::External;
using v8::Function;
using v8::FunctionTemplate;
using v8::Integer;
using v8::Local;
using v8::Object;
using v8::ObjectTemplate;
using v8::String;
using v8::Value;

Nan::Callback *ServerCredentials::constructor;
Persistent<FunctionTemplate> ServerCredentials::fun_tpl;

ServerCredentials::ServerCredentials(grpc_server_credentials *credentials)
    : wrapped_credentials(credentials) {}

ServerCredentials::~ServerCredentials() {
  grpc_server_credentials_release(wrapped_credentials);
}

void ServerCredentials::Init(Local<Object> exports) {
  Nan::HandleScope scope;
  Local<FunctionTemplate> tpl = Nan::New<FunctionTemplate>(New);
  tpl->SetClassName(Nan::New("ServerCredentials").ToLocalChecked());
  tpl->InstanceTemplate()->SetInternalFieldCount(1);
  Local<Function> ctr = tpl->GetFunction();
  Nan::Set(ctr, Nan::New("createSsl").ToLocalChecked(),
           Nan::GetFunction(
               Nan::New<FunctionTemplate>(CreateSsl)).ToLocalChecked());
  Nan::Set(ctr, Nan::New("createInsecure").ToLocalChecked(),
           Nan::GetFunction(
               Nan::New<FunctionTemplate>(CreateInsecure)).ToLocalChecked());
  fun_tpl.Reset(tpl);
  constructor = new Nan::Callback(ctr);
  Nan::Set(exports, Nan::New("ServerCredentials").ToLocalChecked(), ctr);
}

bool ServerCredentials::HasInstance(Local<Value> val) {
  Nan::HandleScope scope;
  return Nan::New(fun_tpl)->HasInstance(val);
}

Local<Value> ServerCredentials::WrapStruct(
    grpc_server_credentials *credentials) {
  Nan::EscapableHandleScope scope;
  const int argc = 1;
  Local<Value> argv[argc] = {
    Nan::New<External>(reinterpret_cast<void *>(credentials))};
  MaybeLocal<Object> maybe_instance = Nan::NewInstance(
      constructor->GetFunction(), argc, argv);
  if (maybe_instance.IsEmpty()) {
    return scope.Escape(Nan::Null());
  } else {
    return scope.Escape(maybe_instance.ToLocalChecked());
  }
}

grpc_server_credentials *ServerCredentials::GetWrappedServerCredentials() {
  return wrapped_credentials;
}

NAN_METHOD(ServerCredentials::New) {
  if (info.IsConstructCall()) {
    if (!info[0]->IsExternal()) {
      return Nan::ThrowTypeError(
          "ServerCredentials can only be created with the provided functions");
    }
    Local<External> ext = info[0].As<External>();
    grpc_server_credentials *creds_value =
        reinterpret_cast<grpc_server_credentials *>(ext->Value());
    ServerCredentials *credentials = new ServerCredentials(creds_value);
    credentials->Wrap(info.This());
    info.GetReturnValue().Set(info.This());
  } else {
    // This should never be called directly
    return Nan::ThrowTypeError(
        "ServerCredentials can only be created with the provided functions");
  }
}

NAN_METHOD(ServerCredentials::CreateSsl) {
  Nan::HandleScope scope;
  char *root_certs = NULL;
  if (::node::Buffer::HasInstance(info[0])) {
    root_certs = ::node::Buffer::Data(info[0]);
  } else if (!(info[0]->IsNull() || info[0]->IsUndefined())) {
    return Nan::ThrowTypeError(
        "createSSl's first argument must be a Buffer if provided");
  }
  if (!info[1]->IsArray()) {
    return Nan::ThrowTypeError(
        "createSsl's second argument must be a list of objects");
  }

  // Default to not requesting the client certificate
  grpc_ssl_client_certificate_request_type client_certificate_request =
      GRPC_SSL_DONT_REQUEST_CLIENT_CERTIFICATE;
  if (info[2]->IsBoolean()) {
    client_certificate_request =
        Nan::To<bool>(info[2]).FromJust()
            ? GRPC_SSL_REQUEST_AND_REQUIRE_CLIENT_CERTIFICATE_AND_VERIFY
            : GRPC_SSL_DONT_REQUEST_CLIENT_CERTIFICATE;
  } else if (!(info[2]->IsUndefined() || info[2]->IsNull())) {
    return Nan::ThrowTypeError(
        "createSsl's third argument must be a boolean if provided");
  }
  Local<Array> pair_list = Local<Array>::Cast(info[1]);
  uint32_t key_cert_pair_count = pair_list->Length();
  grpc_ssl_pem_key_cert_pair *key_cert_pairs = new grpc_ssl_pem_key_cert_pair[
      key_cert_pair_count];

  Local<String> key_key = Nan::New("private_key").ToLocalChecked();
  Local<String> cert_key = Nan::New("cert_chain").ToLocalChecked();

  for(uint32_t i = 0; i < key_cert_pair_count; i++) {
    Local<Value> pair_val = Nan::Get(pair_list, i).ToLocalChecked();
    if (!pair_val->IsObject()) {
      delete[] key_cert_pairs;
      return Nan::ThrowTypeError("Key/cert pairs must be objects");
    }
    Local<Object> pair_obj = Nan::To<Object>(pair_val).ToLocalChecked();
    Local<Value> maybe_key = Nan::Get(pair_obj, key_key).ToLocalChecked();
    Local<Value> maybe_cert = Nan::Get(pair_obj, cert_key).ToLocalChecked();
    if (!::node::Buffer::HasInstance(maybe_key)) {
      delete[] key_cert_pairs;
      return Nan::ThrowTypeError("private_key must be a Buffer");
    }
    if (!::node::Buffer::HasInstance(maybe_cert)) {
      delete[] key_cert_pairs;
      return Nan::ThrowTypeError("cert_chain must be a Buffer");
    }
    key_cert_pairs[i].private_key = ::node::Buffer::Data(maybe_key);
    key_cert_pairs[i].cert_chain = ::node::Buffer::Data(maybe_cert);
  }
  grpc_server_credentials *creds = grpc_ssl_server_credentials_create_ex(
      root_certs, key_cert_pairs, key_cert_pair_count,
      client_certificate_request, NULL);
  delete[] key_cert_pairs;
  if (creds == NULL) {
    info.GetReturnValue().SetNull();
  } else {
    info.GetReturnValue().Set(WrapStruct(creds));
  }
}

NAN_METHOD(ServerCredentials::CreateInsecure) {
  info.GetReturnValue().Set(WrapStruct(NULL));
}

}  // namespace node
}  // namespace grpc
<|endoftext|>


<commit_before>#include <functional>

#include "SLABasePool.hpp"
#include "ExPolygon.hpp"
#include "TriangleMesh.hpp"
#include <numeric>
#include "ClipperUtils.hpp"
#include "boost/log/trivial.hpp"

//#include "SVG.hpp"

namespace Slic3r { namespace sla {

namespace {

using coord_t = Point::coord_type;
inline coord_t mm(double v) { return coord_t(v/SCALING_FACTOR); }

inline coord_t x(const Point& p) { return p.x; }
inline coord_t y(const Point& p) { return p.y; }

struct Contour3D {
    Pointf3s points;
    std::vector<Point3> indices;

    void merge(const Contour3D& ctr) {
        auto s3 = coord_t(points.size());
        auto s = coord_t(indices.size());

        points.insert(points.end(), ctr.points.begin(), ctr.points.end());
        indices.insert(indices.end(), ctr.indices.begin(), ctr.indices.end());

        for(auto n = s; n < indices.size(); n++) {
            auto& idx = indices[n]; idx.x += s3; idx.y += s3; idx.z += s3;
        }
    }
};

inline Contour3D convert(const Polygons& triangles, coord_t z, bool dir) {

    Pointf3s points;
    points.reserve(3*triangles.size());
    std::vector<Point3> indices;
    indices.reserve(points.size());

    for(auto& tr : triangles) {
        auto c = coord_t(points.size()), b = c++, a = c++;
        if(dir) indices.emplace_back(a, b, c);
        else indices.emplace_back(c, b, a);
        for(auto& p : tr.points) {
            points.emplace_back(Pointf3::new_unscale(x(p), y(p), z));
        }
    }

    return {points, indices};
}

inline Contour3D roofs(const ExPolygon& poly, coord_t z_distance) {
    Polygons triangles;
    poly.triangulate_pp(&triangles);

    auto lower = convert(triangles, 0, false);
    auto upper = convert(triangles, z_distance, true);
    lower.merge(upper);
    return lower;
}

inline Contour3D inner_bed(const ExPolygon& poly, coord_t depth) {
    Polygons triangles;
    poly.triangulate_p2t(&triangles);

    auto bottom = convert(triangles, -depth, false);
    auto lines = poly.lines();

    // Generate outer walls
    auto fp = [](const Point& p, Point::coord_type z) {
        return Pointf3::new_unscale(x(p), y(p), z);
    };

    for(auto& l : lines) {
        auto s = coord_t(bottom.points.size());

        bottom.points.emplace_back(fp(l.a, -depth));
        bottom.points.emplace_back(fp(l.b, -depth));
        bottom.points.emplace_back(fp(l.a, 0));
        bottom.points.emplace_back(fp(l.b, 0));

        bottom.indices.emplace_back(s, s + 1, s + 3);
        bottom.indices.emplace_back(s, s + 3, s + 2);
    }

    return bottom;
}

inline TriangleMesh mesh(const Contour3D& ctour) {
    return {ctour.points, ctour.indices};
}

inline TriangleMesh mesh(Contour3D&& ctour) {
    return {std::move(ctour.points), std::move(ctour.indices)};
}

inline void offset(ExPolygon& sh, coord_t distance) {
    using ClipperLib::ClipperOffset;
    using ClipperLib::jtRound;
    using ClipperLib::etClosedPolygon;
    using ClipperLib::Paths;
    using ClipperLib::Path;

    auto&& ctour = Slic3rMultiPoint_to_ClipperPath(sh.contour);
    auto&& holes = Slic3rMultiPoints_to_ClipperPaths(sh.holes);

    // If the input is not at least a triangle, we can not do this algorithm
    if(ctour.size() < 3 ||
       std::any_of(holes.begin(), holes.end(),
                   [](const Path& p) { return p.size() < 3; })
            ) {
        BOOST_LOG_TRIVIAL(error) << "Invalid geometry for offsetting!";
        return;
    }

    ClipperOffset offs;
    offs.ArcTolerance = 0.05*mm(1);
    Paths result;
    offs.AddPath(ctour, jtRound, etClosedPolygon);
    offs.AddPaths(holes, jtRound, etClosedPolygon);
    offs.Execute(result, static_cast<double>(distance));

    // Offsetting reverts the orientation and also removes the last vertex
    // so boost will not have a closed polygon.

    bool found_the_contour = false;
    sh.holes.clear();
    for(auto& r : result) {
        if(ClipperLib::Orientation(r)) {
            // We don't like if the offsetting generates more than one contour
            // but throwing would be an overkill. Instead, we should warn the
            // caller about the inability to create correct geometries
            if(!found_the_contour) {
                auto rr = ClipperPath_to_Slic3rPolygon(r);
                sh.contour.points.swap(rr.points);
                found_the_contour = true;
            } else {
                BOOST_LOG_TRIVIAL(warning)
                        << "Warning: offsetting result is invalid!";
            }
        } else {
            // TODO If there are multiple contours we can't be sure which hole
            // belongs to the first contour. (But in this case the situation is
            // bad enough to let it go...)
            sh.holes.emplace_back(ClipperPath_to_Slic3rPolygon(r));
        }
    }
}

inline ExPolygons unify(const ExPolygons& shapes) {
    ExPolygons retv;

    bool closed = true;
    bool valid = true;

    ClipperLib::Clipper clipper;

    for(auto& path : shapes) {
        auto clipperpath = Slic3rMultiPoint_to_ClipperPath(path.contour);
        valid &= clipper.AddPath(clipperpath, ClipperLib::ptSubject, closed);

        auto clipperholes = Slic3rMultiPoints_to_ClipperPaths(path.holes);
        for(auto& hole : clipperholes) {
            valid &= clipper.AddPath(hole, ClipperLib::ptSubject, closed);
        }
    }

    if(!valid) BOOST_LOG_TRIVIAL(warning) << "Unification of invalid shapes!";

    ClipperLib::PolyTree result;
    clipper.Execute(ClipperLib::ctUnion, result, ClipperLib::pftNonZero);

    retv.reserve(static_cast<size_t>(result.Total()));

    // Now we will recursively traverse the polygon tree and serialize it
    // into an ExPolygon with holes. The polygon tree has the clipper-ish
    // PolyTree structure which alternates its nodes as contours and holes

    // A "declaration" of function for traversing leafs which are holes
    std::function<void(ClipperLib::PolyNode*, ExPolygon&)> processHole;

    // Process polygon which calls processHoles which than calls processPoly
    // again until no leafs are left.
    auto processPoly = [&retv, &processHole](ClipperLib::PolyNode *pptr) {
        ExPolygon poly;
        poly.contour.points = ClipperPath_to_Slic3rPolygon(pptr->Contour);
        for(auto h : pptr->Childs) { processHole(h, poly); }
        retv.push_back(poly);
    };

    // Body of the processHole function
    processHole = [&processPoly](ClipperLib::PolyNode *pptr, ExPolygon& poly)
    {
        poly.holes.emplace_back();
        poly.holes.back().points = ClipperPath_to_Slic3rPolygon(pptr->Contour);
        for(auto c : pptr->Childs) processPoly(c);
    };

    // Wrapper for traversing.
    auto traverse = [&processPoly] (ClipperLib::PolyNode *node)
    {
        for(auto ch : node->Childs) {
            processPoly(ch);
        }
    };

    // Here is the actual traverse
    traverse(&result);

    return retv;
}

inline Point centroid(Points& pp) {
    Polygon p;
    p.points.swap(pp);
    Point c = p.centroid();
    pp.swap(p.points);
    return c;
}

inline Point centroid(const ExPolygon& poly) {
    return poly.contour.centroid();
}

inline ExPolygon concave_hull(const ExPolygons& polys) {
    if(polys.empty()) return ExPolygon();

    ExPolygons punion = unify(polys);

    ExPolygon ret;

    if(punion.size() == 1) return punion.front();

    // We get the centroids of all the islands in the 2D slice
    Points centroids; centroids.reserve(punion.size());
    std::transform(punion.begin(), punion.end(), std::back_inserter(centroids),
                   [](const ExPolygon& poly) { return centroid(poly); });

    // Centroid of the centroids of islands. This is where the additional
    // connector sticks are routed.
    Point cc = centroid(centroids);

    punion.reserve(punion.size() + centroids.size());

    std::transform(centroids.begin(), centroids.end(),
                   std::back_inserter(punion),
                   [cc](const Point& c) {

        double dx = x(c) - x(cc), dy = y(c) - y(cc);
        double l = std::sqrt(dx * dx + dy * dy);
        double nx = dx / l, ny = dy / l;

        ExPolygon r;
        auto& ctour = r.contour.points;

        ctour.reserve(3);
        ctour.emplace_back(cc);

        Point d(coord_t(mm(1)*nx), coord_t(mm(1)*ny));
        ctour.emplace_back(c + Point( -y(d),  x(d) ));
        ctour.emplace_back(c + Point(  y(d), -x(d) ));
        offset(r, mm(1));

        return r;
    });

    punion = unify(punion);

    if(punion.size() != 1)
        BOOST_LOG_TRIVIAL(error) << "Cannot generate correct SLA base pool!";

    if(!punion.empty()) ret = punion.front();

    return ret;
}

}

void ground_layer(const TriangleMesh &mesh, ExPolygons &output, float h)
{
    TriangleMesh m = mesh;
    TriangleMeshSlicer slicer(&m);

    std::vector<ExPolygons> tmp;

    slicer.slice({h}, &tmp);

    output = tmp.front();
}

void create_base_pool(const ExPolygons &ground_layer, TriangleMesh& out,
                      double min_wall_thickness_mm,
                      double min_wall_height_mm)
{
    // 1: Offset the ground layer
    auto concaveh = concave_hull(ground_layer);
    if(concaveh.contour.points.empty()) return;
    concaveh.holes.clear();

    BoundingBox bb(concaveh);
    coord_t w = bb.max.x - bb.min.x;
    coord_t h = bb.max.y - bb.min.y;

    auto wall_thickness = coord_t(std::pow((w+h)*0.1, 0.8));

    const coord_t WALL_THICKNESS = mm(min_wall_thickness_mm) + wall_thickness;
    const coord_t WALL_DISTANCE = coord_t(0.3*WALL_THICKNESS);
    const coord_t HEIGHT = mm(min_wall_height_mm);

    auto outer_base = concaveh;
    offset(outer_base, WALL_THICKNESS+WALL_DISTANCE);
    auto inner_base = outer_base;
    offset(inner_base, -WALL_THICKNESS);
    inner_base.holes.clear(); outer_base.holes.clear();

    ExPolygon top_poly;
    top_poly.contour = outer_base.contour;
    top_poly.holes.emplace_back(inner_base.contour);
    auto& tph = top_poly.holes.back().points;
    std::reverse(tph.begin(), tph.end());

    Contour3D pool;

    Polygons top_triangles, bottom_triangles;
    top_poly.triangulate_p2t(&top_triangles);
    outer_base.triangulate_p2t(&bottom_triangles);
    auto top_plate = convert(top_triangles, 0, false);
    auto bottom_plate = convert(bottom_triangles, -HEIGHT, true);
    auto innerbed = inner_bed(inner_base, HEIGHT/2);

    // Generate outer walls
    auto fp = [](const Point& p, coord_t z) {
        return Pointf3::new_unscale(x(p), y(p), z);
    };

    auto lines = outer_base.lines();
    for(auto& l : lines) {
        auto s = coord_t(pool.points.size());

        pool.points.emplace_back(fp(l.a, -HEIGHT));
        pool.points.emplace_back(fp(l.b, -HEIGHT));
        pool.points.emplace_back(fp(l.a, 0));
        pool.points.emplace_back(fp(l.b, 0));

        pool.indices.emplace_back(s, s + 3, s + 1);
        pool.indices.emplace_back(s, s + 2, s + 3);
    }

    pool.merge(top_plate);
    pool.merge(bottom_plate);
    pool.merge(innerbed);

    out = mesh(pool);
}

}
}
<commit_msg>Comments mostly<commit_after>#include <functional>
#include <numeric>

#include "SLABasePool.hpp"
#include "ExPolygon.hpp"
#include "TriangleMesh.hpp"
#include "ClipperUtils.hpp"
#include "boost/log/trivial.hpp"

//#include "SVG.hpp"

namespace Slic3r { namespace sla {

namespace {

using coord_t = Point::coord_type;

/// get the scaled clipper units for a millimeter value
inline coord_t mm(double v) { return coord_t(v/SCALING_FACTOR); }

/// Get x and y coordinates (because we are eigenizing...)
inline coord_t x(const Point& p) { return p.x; }
inline coord_t y(const Point& p) { return p.y; }

/// Intermediate struct for a 3D mesh
struct Contour3D {
    Pointf3s points;
    std::vector<Point3> indices;

    void merge(const Contour3D& ctr) {
        auto s3 = coord_t(points.size());
        auto s = coord_t(indices.size());

        points.insert(points.end(), ctr.points.begin(), ctr.points.end());
        indices.insert(indices.end(), ctr.indices.begin(), ctr.indices.end());

        for(auto n = s; n < indices.size(); n++) {
            auto& idx = indices[n]; idx.x += s3; idx.y += s3; idx.z += s3;
        }
    }
};

/// Convert the triangulation output to an intermediate mesh.
inline Contour3D convert(const Polygons& triangles, coord_t z, bool dir) {

    Pointf3s points;
    points.reserve(3*triangles.size());
    std::vector<Point3> indices;
    indices.reserve(points.size());

    for(auto& tr : triangles) {
        auto c = coord_t(points.size()), b = c++, a = c++;
        if(dir) indices.emplace_back(a, b, c);
        else indices.emplace_back(c, b, a);
        for(auto& p : tr.points) {
            points.emplace_back(Pointf3::new_unscale(x(p), y(p), z));
        }
    }

    return {points, indices};
}

/// Only a debug function to generate top and bottom plates from a 2D shape.
/// It is not used in the algorithm directly.
inline Contour3D roofs(const ExPolygon& poly, coord_t z_distance) {
    Polygons triangles;
    poly.triangulate_pp(&triangles);

    auto lower = convert(triangles, 0, false);
    auto upper = convert(triangles, z_distance, true);
    lower.merge(upper);
    return lower;
}

/// Generating the concave part of the 3D pool with the bottom plate and the
/// side walls.
inline Contour3D inner_bed(const ExPolygon& poly, coord_t depth) {
    Polygons triangles;
    poly.triangulate_p2t(&triangles);

    auto bottom = convert(triangles, -depth, false);
    auto lines = poly.lines();

    // Generate outer walls
    auto fp = [](const Point& p, Point::coord_type z) {
        return Pointf3::new_unscale(x(p), y(p), z);
    };

    for(auto& l : lines) {
        auto s = coord_t(bottom.points.size());

        bottom.points.emplace_back(fp(l.a, -depth));
        bottom.points.emplace_back(fp(l.b, -depth));
        bottom.points.emplace_back(fp(l.a, 0));
        bottom.points.emplace_back(fp(l.b, 0));

        bottom.indices.emplace_back(s, s + 1, s + 3);
        bottom.indices.emplace_back(s, s + 3, s + 2);
    }

    return bottom;
}

/// Mesh from an existing contour.
inline TriangleMesh mesh(const Contour3D& ctour) {
    return {ctour.points, ctour.indices};
}

/// Mesh from an evaporating 3D contour
inline TriangleMesh mesh(Contour3D&& ctour) {
    return {std::move(ctour.points), std::move(ctour.indices)};
}

/// Offsetting with clipper and smoothing the edges into a curvature.
inline void offset(ExPolygon& sh, coord_t distance) {
    using ClipperLib::ClipperOffset;
    using ClipperLib::jtRound;
    using ClipperLib::etClosedPolygon;
    using ClipperLib::Paths;
    using ClipperLib::Path;

    auto&& ctour = Slic3rMultiPoint_to_ClipperPath(sh.contour);
    auto&& holes = Slic3rMultiPoints_to_ClipperPaths(sh.holes);

    // If the input is not at least a triangle, we can not do this algorithm
    if(ctour.size() < 3 ||
       std::any_of(holes.begin(), holes.end(),
                   [](const Path& p) { return p.size() < 3; })
            ) {
        BOOST_LOG_TRIVIAL(error) << "Invalid geometry for offsetting!";
        return;
    }

    ClipperOffset offs;
    offs.ArcTolerance = 0.05*mm(1);
    Paths result;
    offs.AddPath(ctour, jtRound, etClosedPolygon);
    offs.AddPaths(holes, jtRound, etClosedPolygon);
    offs.Execute(result, static_cast<double>(distance));

    // Offsetting reverts the orientation and also removes the last vertex
    // so boost will not have a closed polygon.

    bool found_the_contour = false;
    sh.holes.clear();
    for(auto& r : result) {
        if(ClipperLib::Orientation(r)) {
            // We don't like if the offsetting generates more than one contour
            // but throwing would be an overkill. Instead, we should warn the
            // caller about the inability to create correct geometries
            if(!found_the_contour) {
                auto rr = ClipperPath_to_Slic3rPolygon(r);
                sh.contour.points.swap(rr.points);
                found_the_contour = true;
            } else {
                BOOST_LOG_TRIVIAL(warning)
                        << "Warning: offsetting result is invalid!";
            }
        } else {
            // TODO If there are multiple contours we can't be sure which hole
            // belongs to the first contour. (But in this case the situation is
            // bad enough to let it go...)
            sh.holes.emplace_back(ClipperPath_to_Slic3rPolygon(r));
        }
    }
}

/// Unification of polygons (with clipper) preserving holes as well.
inline ExPolygons unify(const ExPolygons& shapes) {
    ExPolygons retv;

    bool closed = true;
    bool valid = true;

    ClipperLib::Clipper clipper;

    for(auto& path : shapes) {
        auto clipperpath = Slic3rMultiPoint_to_ClipperPath(path.contour);
        valid &= clipper.AddPath(clipperpath, ClipperLib::ptSubject, closed);

        auto clipperholes = Slic3rMultiPoints_to_ClipperPaths(path.holes);
        for(auto& hole : clipperholes) {
            valid &= clipper.AddPath(hole, ClipperLib::ptSubject, closed);
        }
    }

    if(!valid) BOOST_LOG_TRIVIAL(warning) << "Unification of invalid shapes!";

    ClipperLib::PolyTree result;
    clipper.Execute(ClipperLib::ctUnion, result, ClipperLib::pftNonZero);

    retv.reserve(static_cast<size_t>(result.Total()));

    // Now we will recursively traverse the polygon tree and serialize it
    // into an ExPolygon with holes. The polygon tree has the clipper-ish
    // PolyTree structure which alternates its nodes as contours and holes

    // A "declaration" of function for traversing leafs which are holes
    std::function<void(ClipperLib::PolyNode*, ExPolygon&)> processHole;

    // Process polygon which calls processHoles which than calls processPoly
    // again until no leafs are left.
    auto processPoly = [&retv, &processHole](ClipperLib::PolyNode *pptr) {
        ExPolygon poly;
        poly.contour.points = ClipperPath_to_Slic3rPolygon(pptr->Contour);
        for(auto h : pptr->Childs) { processHole(h, poly); }
        retv.push_back(poly);
    };

    // Body of the processHole function
    processHole = [&processPoly](ClipperLib::PolyNode *pptr, ExPolygon& poly)
    {
        poly.holes.emplace_back();
        poly.holes.back().points = ClipperPath_to_Slic3rPolygon(pptr->Contour);
        for(auto c : pptr->Childs) processPoly(c);
    };

    // Wrapper for traversing.
    auto traverse = [&processPoly] (ClipperLib::PolyNode *node)
    {
        for(auto ch : node->Childs) {
            processPoly(ch);
        }
    };

    // Here is the actual traverse
    traverse(&result);

    return retv;
}

inline Point centroid(Points& pp) {
    Polygon p;
    p.points.swap(pp);
    Point c = p.centroid();
    pp.swap(p.points);
    return c;
}

inline Point centroid(const ExPolygon& poly) {
    return poly.contour.centroid();
}

/// A fake concave hull that is constructed by connecting separate shapes
/// with explicit bridges. Bridges are generated from each shape's centroid
/// to the center of the "scene" which is the centroid calculated from the shape
/// centroids (a star is created...)
inline ExPolygon concave_hull(const ExPolygons& polys) {
    if(polys.empty()) return ExPolygon();

    ExPolygons punion = unify(polys);

    ExPolygon ret;

    if(punion.size() == 1) return punion.front();

    // We get the centroids of all the islands in the 2D slice
    Points centroids; centroids.reserve(punion.size());
    std::transform(punion.begin(), punion.end(), std::back_inserter(centroids),
                   [](const ExPolygon& poly) { return centroid(poly); });

    // Centroid of the centroids of islands. This is where the additional
    // connector sticks are routed.
    Point cc = centroid(centroids);

    punion.reserve(punion.size() + centroids.size());

    std::transform(centroids.begin(), centroids.end(),
                   std::back_inserter(punion),
                   [cc](const Point& c) {

        double dx = x(c) - x(cc), dy = y(c) - y(cc);
        double l = std::sqrt(dx * dx + dy * dy);
        double nx = dx / l, ny = dy / l;

        ExPolygon r;
        auto& ctour = r.contour.points;

        ctour.reserve(3);
        ctour.emplace_back(cc);

        Point d(coord_t(mm(1)*nx), coord_t(mm(1)*ny));
        ctour.emplace_back(c + Point( -y(d),  x(d) ));
        ctour.emplace_back(c + Point(  y(d), -x(d) ));
        offset(r, mm(1));

        return r;
    });

    punion = unify(punion);

    if(punion.size() != 1)
        BOOST_LOG_TRIVIAL(error) << "Cannot generate correct SLA base pool!";

    if(!punion.empty()) ret = punion.front();

    return ret;
}

}

void ground_layer(const TriangleMesh &mesh, ExPolygons &output, float h)
{
    TriangleMesh m = mesh;
    TriangleMeshSlicer slicer(&m);

    std::vector<ExPolygons> tmp;

    slicer.slice({h}, &tmp);

    output = tmp.front();
}

void create_base_pool(const ExPolygons &ground_layer, TriangleMesh& out,
                      double min_wall_thickness_mm,
                      double min_wall_height_mm)
{
    // 1: Offset the ground layer
    auto concaveh = concave_hull(ground_layer);
    if(concaveh.contour.points.empty()) return;
    concaveh.holes.clear();

    BoundingBox bb(concaveh);
    coord_t w = bb.max.x - bb.min.x;
    coord_t h = bb.max.y - bb.min.y;

    auto wall_thickness = coord_t(std::pow((w+h)*0.1, 0.8));

    const coord_t WALL_THICKNESS = mm(min_wall_thickness_mm) + wall_thickness;
    const coord_t WALL_DISTANCE = coord_t(0.3*WALL_THICKNESS);
    const coord_t HEIGHT = mm(min_wall_height_mm);

    auto outer_base = concaveh;
    offset(outer_base, WALL_THICKNESS+WALL_DISTANCE);
    auto inner_base = outer_base;
    offset(inner_base, -WALL_THICKNESS);
    inner_base.holes.clear(); outer_base.holes.clear();

    ExPolygon top_poly;
    top_poly.contour = outer_base.contour;
    top_poly.holes.emplace_back(inner_base.contour);
    auto& tph = top_poly.holes.back().points;
    std::reverse(tph.begin(), tph.end());

    Contour3D pool;

    Polygons top_triangles, bottom_triangles;
    top_poly.triangulate_p2t(&top_triangles);
    outer_base.triangulate_p2t(&bottom_triangles);
    auto top_plate = convert(top_triangles, 0, false);
    auto bottom_plate = convert(bottom_triangles, -HEIGHT, true);
    auto innerbed = inner_bed(inner_base, HEIGHT/2);

    // Generate outer walls
    auto fp = [](const Point& p, coord_t z) {
        return Pointf3::new_unscale(x(p), y(p), z);
    };

    auto lines = outer_base.lines();
    for(auto& l : lines) {
        auto s = coord_t(pool.points.size());

        pool.points.emplace_back(fp(l.a, -HEIGHT));
        pool.points.emplace_back(fp(l.b, -HEIGHT));
        pool.points.emplace_back(fp(l.a, 0));
        pool.points.emplace_back(fp(l.b, 0));

        pool.indices.emplace_back(s, s + 3, s + 1);
        pool.indices.emplace_back(s, s + 2, s + 3);
    }

    pool.merge(top_plate);
    pool.merge(bottom_plate);
    pool.merge(innerbed);

    out = mesh(pool);
}

}
}
<|endoftext|>


<commit_before>#include <iostream>
#include <cstdio>
#include <algorithm>
#include <queue>
#include <map>
#include <set>
#include <stack>
#include <cstring>
#include <string>
#include <vector>
#include <iomanip>
#include <cmath>
#include <list>
#include <bitset>

using namespace std;

#define ll long long
#define lson l,mid,id<<1
#define rson mid+1,r,id<<1|1

typedef pair<int, int>pii;
typedef pair<ll, ll>pll;
typedef pair<double, double>pdd;

const double eps = 1e-6;
const ll  LINF = 0x3f3f3f3f3f3f3f3fLL;
const int INF = 0x3f3f3f3f;
const double FINF = 1e18;

#define x first
#define y second

#define REP(i,j,k) for(int i =(j);i<=(k);i++)
#define REPD(i,j,k) for(int i =(j);i>=(k);i--)
#define print(x) cout<<(x)<<endl;
#define IOS ios::sync_with_stdio(0);cin.tie(0);

const int maxn = 222222;
int MAX[maxn<<2];

void PushUP(int id) {
    MAX[id] = max(MAX[id<<1] , MAX[id<<1|1]);
}

void build(int l,int r,int id) {
    if (l == r) {
        scanf("%d",&MAX[id]);
        return ;
    }
    int mid = (l + r) >> 1;
    build(lson);
    build(rson);
    PushUP(id);
}
void update(int p,int sc,int l,int r,int id) {
    if (l == r) {
        MAX[id] = sc;
        return ;
    }
    int mid = (l + r) >> 1;
    if (p <= m) update(p , sc , lson);
    else update(p , sc , rson);
    PushUP(id);
}
int query(int L,int R,int l,int r,int id) {
    if (L <= l && r <= R) {
        return MAX[id];
    }
    int mid = (l + r) >> 1;
    int ret = 0;
    if (L <= m) ret = max(ret , query(L , R , lson));
    if (R > m) ret = max(ret , query(L , R , rson));
    return ret;
}
int n , m;

void Init(){

	build(1 , n , 1);
	return ;
}

void Solve(){

	while (m --) {
            char op[2];
            int a , b;
            scanf("%s%d%d",op,&a,&b);
            if (op[0] == 'Q') printf("%d\n",query(a , b , 1 , n , 1));
            else update(a , b , 1 , n , 1);
    }
	return ;
}

int main(){
#ifdef LOCAL
	freopen("hdu1754.in","r",stdin);
#endif

	while (~scanf("%d%d",&n,&m))
	Init(),Solve();
	return 0;
}hdu1754.cp<commit_msg>update hdu1754<commit_after>#include <iostream>
#include <cstdio>
#include <algorithm>
#include <queue>
#include <map>
#include <set>
#include <stack>
#include <cstring>
#include <string>
#include <vector>
#include <iomanip>
#include <cmath>
#include <list>
#include <bitset>

using namespace std;

#define ll long long
#define lson l,mid,id<<1
#define rson mid+1,r,id<<1|1

typedef pair<int, int>pii;
typedef pair<ll, ll>pll;
typedef pair<double, double>pdd;

const double eps = 1e-6;
const ll  LINF = 0x3f3f3f3f3f3f3f3fLL;
const int INF = 0x3f3f3f3f;
const double FINF = 1e18;

#define x first
#define y second

#define REP(i,j,k) for(int i =(j);i<=(k);i++)
#define REPD(i,j,k) for(int i =(j);i>=(k);i--)
#define print(x) cout<<(x)<<endl;
#define IOS ios::sync_with_stdio(0);cin.tie(0);

const int maxn = 222222;
int MAX[maxn<<2];

void PushUP(int id) {
    MAX[id] = max(MAX[id<<1] , MAX[id<<1|1]);
}

void build(int l,int r,int id) {
    if (l == r) {
        scanf("%d",&MAX[id]);
        return ;
    }
    int mid = (l + r) >> 1;
    build(lson);
    build(rson);
    PushUP(id);
}
void update(int p,int sc,int l,int r,int id) {
    if (l == r) {
        MAX[id] = sc;
        return ;
    }
    int mid = (l + r) >> 1;
    if (p <= mid) update(p , sc , lson);
    else update(p , sc , rson);
    PushUP(id);
}
int query(int L,int R,int l,int r,int id) {
    if (L <= l && r <= R) {
        return MAX[id];
    }
    int mid = (l + r) >> 1;
    int ret = 0;
    if (L <= mid) ret = max(ret , query(L , R , lson));
    if (R > mid) ret = max(ret , query(L , R , rson));
    return ret;
}
int n , m;

void Init(){

	build(1 , n , 1);
	return ;
}

void Solve(){

	while (m --) {
            char op[2];
            int a , b;
            scanf("%s%d%d",op,&a,&b);
            if (op[0] == 'Q') printf("%d\n",query(a , b , 1 , n , 1));
            else update(a , b , 1 , n , 1);
    }
	return ;
}

int main(){
#ifdef LOCAL
	freopen("hdu1754.in","r",stdin);
#endif

	while (~scanf("%d%d",&n,&m))
	Init(),Solve();
	return 0;
}<|endoftext|>


<commit_before>#define _USE_MATH_DEFINES
#include <cmath>
#include <stdio.h>
#include <stdlib.h>
#include <iostream>
#include <time.h>

#include "edflib.h"
#include "eeg_spectrogram.h"

#define max(a,b) \
   ({ __typeof__ (a) _a = (a); \
       __typeof__ (b) _b = (b); \
     _a > _b ? _a : _b; })

#define min(a,b) \
   ({ __typeof__ (a) _a = (a); \
       __typeof__ (b) _b = (b); \
     _a < _b ? _a : _b; })

#include <time.h>
#include <sys/time.h>

#define N 10

// should use a hashmap + linked list implementation if the
// list traversal becomes a bottleneck
void print_hdr_cache()
{
  printf("[\n");
  for (int i = 0; i < EDFLIB_MAXFILES; i++)
  {
    if (EDF_HDR_CACHE[i] == NULL)
    {
      printf("-,");
    }
    else
    {
      printf("%s,\n", EDF_HDR_CACHE[i]->path);
    }
  }
  printf("]\n");
}

edf_hdr_struct* get_hdr_cache(const char *filename)
{
  for (int i = 0; i < EDFLIB_MAXFILES; i++)
  {
    if (EDF_HDR_CACHE[i] != NULL)
    {
      if (!(strcmp(filename, EDF_HDR_CACHE[i]->path)))
      {
        edf_hdr_struct* hdr = EDF_HDR_CACHE[i];
        // TODO(joshblum): probably remove this once
        // windowing is fully implemented
        for (int signal = 0; signal < hdr->edfsignals; signal++)
        {
          edfrewind(hdr->handle, signal);
        }
        return hdr;
      }
    }
  }

  return NULL;
}

void set_hdr_cache(edf_hdr_struct* hdr)
{
  for (int i = 0; i < EDFLIB_MAXFILES; i++)
  {
    if (EDF_HDR_CACHE[i] == NULL)
    {
      EDF_HDR_CACHE[i] = hdr;
      break;
    }
  }
}

void pop_hdr_cache(const char* filename)
{
  for (int i = 0; i < EDFLIB_MAXFILES; i++)
  {
    if (EDF_HDR_CACHE[i] != NULL)
    {
      if (!(strcmp(filename, EDF_HDR_CACHE[i]->path)))
      {
        free(EDF_HDR_CACHE[i]);
        EDF_HDR_CACHE[i] = NULL;
      }
    }
  }
}

unsigned long long getticks()
{
  struct timeval t;
  gettimeofday(&t, 0);
  return t.tv_sec * 1000000ULL + t.tv_usec;
}

double ticks_to_seconds(unsigned long long ticks)
{
  return ticks * 1.0e-6;
}

void log_time_diff(unsigned long long ticks)
{
  double diff = ticks_to_seconds(ticks);
  printf("Time taken %.2f seconds\n", diff);
}

void print_spec_params_t(spec_params_t* spec_params)
{
  printf("spec_params: {\n");
  printf("\tfilename: %s\n", spec_params->filename);
  printf("\tduration: %.2f\n", spec_params->duration);
  printf("\thdl: %d\n", spec_params->hdl);
  printf("\tnfft: %d\n", spec_params->nfft);
  printf("\tnstep: %d\n", spec_params->nstep);
  printf("\tshift: %d\n", spec_params->shift);
  printf("\tnsamples: %d\n", spec_params->nsamples);
  printf("\tnblocks: %d\n", spec_params->nblocks);
  printf("\tnfreqs: %d\n", spec_params->nfreqs);
  printf("\tspec_len: %d\n", spec_params->spec_len);
  printf("\tfs: %d\n", spec_params->fs);
  printf("}\n");
}

int get_data_len(edf_hdr_struct* hdr)
{
  // assume all signals have a uniform sample rate
  return hdr->signalparam[0].smp_in_file;
}

int get_nfft(int shift, int pad)
{
  return max(get_next_pow_2(shift) + pad, shift);
}

int get_nsamples(int data_len, int fs, float duration)
{
  return min(data_len, fs * 60 * 60 * duration);
}

int get_nblocks(int nsamples, int nfft, int shift)
{
  return (nsamples - nfft) / shift + 1;
}

int get_nfreqs(int nfft)
{
  return nfft / 2 + 1;
}

int get_next_pow_2(unsigned int v)
{
  v--;
  v |= v >> 1;
  v |= v >> 2;
  v |= v >> 4;
  v |= v >> 8;
  v |= v >> 16;
  v++;
  return v;
}

int get_fs(edf_hdr_struct* hdr)
{
  return ((double)hdr->signalparam[0].smp_in_datarecord / (double)hdr->datarecord_duration) * EDFLIB_TIME_DIMENSION;
}

void get_eeg_spectrogram_params(spec_params_t* spec_params,
                                char* filename, float duration)
{
  // TODO(joshblum): implement full multitaper method
  // and remove hard coding
  spec_params->filename = filename;
  spec_params->duration = duration;

  edf_hdr_struct hdr;
  load_edf(&hdr, filename);
  spec_params->hdl = hdr.handle;

  spec_params->fs = get_fs(&hdr);

  int data_len = get_data_len(&hdr);
  int pad = 0;
  spec_params->shift = spec_params->fs * 4;
  spec_params->nstep = spec_params->fs * 1;
  spec_params->nfft = get_nfft(spec_params->shift, pad);
  spec_params->nsamples = get_nsamples(data_len, spec_params->fs, duration);
  spec_params->nblocks = get_nblocks(spec_params->nsamples,
                                     spec_params->shift, spec_params->nstep);
  spec_params->nfreqs = get_nfreqs(spec_params->nfft);
  spec_params->spec_len = spec_params->nsamples / spec_params->fs;
}

void load_edf(edf_hdr_struct* hdr, char* filename)
{
  edf_hdr_struct* cached_hdr = get_hdr_cache(filename);
  if (cached_hdr != NULL)
  {
    // get the file from the cache
    hdr = cached_hdr;
    return;
  }
  if (edfopen_file_readonly(filename, hdr, EDFLIB_READ_ALL_ANNOTATIONS))
  {
    switch (hdr->filetype)
    {
    case EDFLIB_MALLOC_ERROR                :
      printf("\nmalloc error\n\n");
      break;
    case EDFLIB_NO_SUCH_FILE_OR_DIRECTORY   :
      printf("\ncannot open file, no such file or directory: %s\n\n", filename);
      break;
    case EDFLIB_FILE_CONTAINS_FORMAT_ERRORS :
      printf("\nthe file is not EDF(+) or BDF(+) compliant\n"
             "(it contains format errors)\n\n");
      break;
    case EDFLIB_MAXFILES_REACHED            :
      printf("\nto many files opened\n\n");
      break;
    case EDFLIB_FILE_READ_ERROR             :
      printf("\na read error occurred\n\n");
      break;
    case EDFLIB_FILE_ALREADY_OPENED         :
      printf("\nfile has already been opened\n\n");
      break;
    default                                 :
      printf("\nunknown error\n\n");
      break;
    }
    exit(1);
  }
  // set the file in the cache
  set_hdr_cache(hdr);
}

// TODO(joshblum): implement a call to this on websocket close
void close_edf(char* filename)
{
  edf_hdr_struct* hdr = get_hdr_cache(filename);
  edfclose_file(hdr->handle);
  pop_hdr_cache(filename);
}

double* create_buffer(int n, int hdl)
{
  double* buf = (double * ) malloc(sizeof(double[n]));
  if (buf == NULL)
  {
    printf("\nmalloc error\n");
    edfclose_file(hdl);
    exit(1);
  }
  return buf;
}

int read_samples(int hdl, int ch, int n, double *buf)
{
  int bytes_read = edfread_physical_samples(hdl, ch, n, buf);

  if (bytes_read == -1)
  {
    printf("\nerror: edf_read_physical_samples()\n");
    edfclose_file(hdl);
    free(buf);
    exit(1);
  }
  // clear buffer in case we didn't read as much as we expected to
  for (int i = bytes_read; i < n; i++) {
    buf[i] = 0.0;
  }
  return bytes_read;
}

// Create a hamming window of windowLength samples in buffer
void hamming(int windowLength, double* buffer)
{
  for (int i = 0; i < windowLength; i++)
  {
    buffer[i] = 0.54 - (0.46 * cos( 2 * M_PI * (i / ((windowLength - 1) * 1.0))));
  }
}

static inline double abs(fftw_complex* arr, int i)
{
  return sqrt(arr[i][0] * arr[i][0] + arr[i][1] * arr[i][1]);
}

// copy pasta http://ofdsp.blogspot.co.il/2011/08/short-time-fourier-transform-with-fftw3.html
/*
 * Fill the `specs` matrix with values for the spectrogram for the given diff.
 * `specs` is expected to be initialized and the results are added to allow averaging
 */
void STFT(arma::rowvec& diff, spec_params_t* spec_params, arma::mat& specs)
{
  fftw_complex    *data, *fft_result;
  fftw_plan       plan_forward;
  int nfft = spec_params->nfft;

  int nstep = spec_params->nstep;

  int nblocks = spec_params->nblocks;
  int nfreqs = spec_params->nfreqs;
  int nsamples = spec_params->nsamples;

  data = ( fftw_complex* ) fftw_malloc( sizeof( fftw_complex ) * nfft);
  fft_result = ( fftw_complex* ) fftw_malloc( sizeof( fftw_complex ) * nfft);
  // TODO keep plans in memory until end, create plans once and cache?
  plan_forward = fftw_plan_dft_1d(nfft, data, fft_result,
                                  FFTW_FORWARD, FFTW_ESTIMATE);

  // Create a hamming window of appropriate length
  double window[nfft];
  hamming(nfft, window);

  for (int idx = 0; idx < nblocks; idx++)
  {
    // get the last chunk
    if (idx * nstep + nfft > nsamples)
    {
      int upper_bound = nsamples - idx * nstep;
      for (int i = 0; i < upper_bound; i++)
      {
        data[i][0] = diff(idx * nstep + i) * window[i];
        data[i][1] = 0.0;

      }
      for (int i = upper_bound; i < nfft; i++)
      {
        data[i][0] = 0.0;
        data[i][1] = 0.0;
      }
      break;
    }
    else
    {
      for (int i = 0; i < nfft; i++)
      {
        // TODO vector multiplication?
        data[i][0] = diff(idx * nstep  + i) * window[i];
        data[i][1] = 0.0;
      }
    }

    // Perform the FFT on our chunk
    fftw_execute( plan_forward );

    // TODO: change maybe?
    // http://www.fftw.org/fftw2_doc/fftw_2.html
    for (int i = 0; i < nfreqs; i++)
    {
      specs(i, idx) += abs(fft_result, i) / nfft;
    }

    // Uncomment to see the raw-data output from the FFT calculation
    // printf("[ ");
    // for (int i = 0 ; i < 10 ; i++ )
    // {
    //   printf("%2.2f ", specs(i, idx));
    // }
    // printf("]\n");
  }

  fftw_destroy_plan( plan_forward );

  fftw_free( data );
  fftw_free( fft_result );
}

void eeg_file_spectrogram_handler(char* filename, float duration, int ch, float* out)
{
  spec_params_t spec_params;
  get_eeg_spectrogram_params(&spec_params, filename, duration);
  print_spec_params_t(&spec_params);
  eeg_spectrogram_handler(&spec_params, ch, out);
}

void eeg_spectrogram_handler(spec_params_t* spec_params, int ch, float* out)
{
  if (out == NULL)
  {
    out = (float *) malloc(sizeof(float) * spec_params->nblocks * spec_params->nfreqs);
  }
  eeg_spectrogram(spec_params, ch, out);
}

void eeg_spectrogram(spec_params_t* spec_params, int ch, float* out)
{
  // TODO reuse buffers
  // TODO chunking?
  // write edf method to do diff on the fly?
  int nsamples = spec_params->nsamples;
  double* buf1 = create_buffer(nsamples, spec_params->hdl);
  double* buf2 = create_buffer(nsamples, spec_params->hdl);

  // nfreqs x nblocks matrix
  arma::mat specs(spec_params->nfreqs, spec_params->nblocks);
  specs.fill(0);

  int ch_idx1, ch_idx2;
  ch_idx1 = DIFFERENCE_PAIRS[ch].ch_idx[0];
  read_samples(spec_params->hdl, ch_idx1, nsamples, buf1);
  for (int i = 1; i < NUM_DIFFS; i++)
  {
    ch_idx2 = DIFFERENCE_PAIRS[ch].ch_idx[i];
    read_samples(spec_params->hdl, ch_idx2, nsamples, buf2);

    // TODO use arma::rowvec::fixed with fixed size chunks
    arma::rowvec v1 = arma::rowvec(buf1, nsamples);
    arma::rowvec v2 = arma::rowvec(buf2, nsamples);
    arma::rowvec diff = v2 - v1;

    // fill in the spec matrix with fft values
    STFT(diff, spec_params, specs);
    std::swap(buf1, buf2);
  }
  // TODO serialize specs output for each channel
  specs /=  (NUM_DIFFS - 1); // average diff spectrograms

  // tranpose the output
  for (int i = 0; i < spec_params->nfreqs; i++)
  {
    for (int j = 0; j < spec_params->nblocks; j++)
    {
      *(out + i + j*spec_params->nfreqs) = (float) specs(i, j);
    }
  }

}
<commit_msg>dont exit!<commit_after>#define _USE_MATH_DEFINES
#include <cmath>
#include <stdio.h>
#include <stdlib.h>
#include <iostream>
#include <time.h>

#include "edflib.h"
#include "eeg_spectrogram.h"

#define max(a,b) \
   ({ __typeof__ (a) _a = (a); \
       __typeof__ (b) _b = (b); \
     _a > _b ? _a : _b; })

#define min(a,b) \
   ({ __typeof__ (a) _a = (a); \
       __typeof__ (b) _b = (b); \
     _a < _b ? _a : _b; })

#include <time.h>
#include <sys/time.h>

#define N 10

// should use a hashmap + linked list implementation if the
// list traversal becomes a bottleneck
void print_hdr_cache()
{
  printf("[\n");
  for (int i = 0; i < EDFLIB_MAXFILES; i++)
  {
    if (EDF_HDR_CACHE[i] == NULL)
    {
      printf("-,");
    }
    else
    {
      printf("%s,\n", EDF_HDR_CACHE[i]->path);
    }
  }
  printf("]\n");
}

edf_hdr_struct* get_hdr_cache(const char *filename)
{
  for (int i = 0; i < EDFLIB_MAXFILES; i++)
  {
    if (EDF_HDR_CACHE[i] != NULL)
    {
      if (!(strcmp(filename, EDF_HDR_CACHE[i]->path)))
      {
        edf_hdr_struct* hdr = EDF_HDR_CACHE[i];
        // TODO(joshblum): probably remove this once
        // windowing is fully implemented
        for (int signal = 0; signal < hdr->edfsignals; signal++)
        {
          edfrewind(hdr->handle, signal);
        }
        return hdr;
      }
    }
  }

  return NULL;
}

void set_hdr_cache(edf_hdr_struct* hdr)
{
  for (int i = 0; i < EDFLIB_MAXFILES; i++)
  {
    if (EDF_HDR_CACHE[i] == NULL)
    {
      EDF_HDR_CACHE[i] = hdr;
      break;
    }
  }
}

void pop_hdr_cache(const char* filename)
{
  for (int i = 0; i < EDFLIB_MAXFILES; i++)
  {
    if (EDF_HDR_CACHE[i] != NULL)
    {
      if (!(strcmp(filename, EDF_HDR_CACHE[i]->path)))
      {
        free(EDF_HDR_CACHE[i]);
        EDF_HDR_CACHE[i] = NULL;
      }
    }
  }
}

unsigned long long getticks()
{
  struct timeval t;
  gettimeofday(&t, 0);
  return t.tv_sec * 1000000ULL + t.tv_usec;
}

double ticks_to_seconds(unsigned long long ticks)
{
  return ticks * 1.0e-6;
}

void log_time_diff(unsigned long long ticks)
{
  double diff = ticks_to_seconds(ticks);
  printf("Time taken %.2f seconds\n", diff);
}

void print_spec_params_t(spec_params_t* spec_params)
{
  printf("spec_params: {\n");
  printf("\tfilename: %s\n", spec_params->filename);
  printf("\tduration: %.2f\n", spec_params->duration);
  printf("\thdl: %d\n", spec_params->hdl);
  printf("\tnfft: %d\n", spec_params->nfft);
  printf("\tnstep: %d\n", spec_params->nstep);
  printf("\tshift: %d\n", spec_params->shift);
  printf("\tnsamples: %d\n", spec_params->nsamples);
  printf("\tnblocks: %d\n", spec_params->nblocks);
  printf("\tnfreqs: %d\n", spec_params->nfreqs);
  printf("\tspec_len: %d\n", spec_params->spec_len);
  printf("\tfs: %d\n", spec_params->fs);
  printf("}\n");
}

int get_data_len(edf_hdr_struct* hdr)
{
  // assume all signals have a uniform sample rate
  return hdr->signalparam[0].smp_in_file;
}

int get_nfft(int shift, int pad)
{
  return max(get_next_pow_2(shift) + pad, shift);
}

int get_nsamples(int data_len, int fs, float duration)
{
  return min(data_len, fs * 60 * 60 * duration);
}

int get_nblocks(int nsamples, int nfft, int shift)
{
  return (nsamples - nfft) / shift + 1;
}

int get_nfreqs(int nfft)
{
  return nfft / 2 + 1;
}

int get_next_pow_2(unsigned int v)
{
  v--;
  v |= v >> 1;
  v |= v >> 2;
  v |= v >> 4;
  v |= v >> 8;
  v |= v >> 16;
  v++;
  return v;
}

int get_fs(edf_hdr_struct* hdr)
{
  return ((double)hdr->signalparam[0].smp_in_datarecord / (double)hdr->datarecord_duration) * EDFLIB_TIME_DIMENSION;
}

void get_eeg_spectrogram_params(spec_params_t* spec_params,
                                char* filename, float duration)
{
  // TODO(joshblum): implement full multitaper method
  // and remove hard coding
  spec_params->filename = filename;
  spec_params->duration = duration;

  edf_hdr_struct hdr;
  load_edf(&hdr, filename);
  spec_params->hdl = hdr.handle;

  spec_params->fs = get_fs(&hdr);

  int data_len = get_data_len(&hdr);
  int pad = 0;
  spec_params->shift = spec_params->fs * 4;
  spec_params->nstep = spec_params->fs * 1;
  spec_params->nfft = get_nfft(spec_params->shift, pad);
  spec_params->nsamples = get_nsamples(data_len, spec_params->fs, duration);
  spec_params->nblocks = get_nblocks(spec_params->nsamples,
                                     spec_params->shift, spec_params->nstep);
  spec_params->nfreqs = get_nfreqs(spec_params->nfft);
  spec_params->spec_len = spec_params->nsamples / spec_params->fs;
}

void load_edf(edf_hdr_struct* hdr, char* filename)
{
  edf_hdr_struct* cached_hdr = get_hdr_cache(filename);
  if (cached_hdr != NULL)
  {
    // get the file from the cache
    hdr = cached_hdr;
    return;
  }
  if (edfopen_file_readonly(filename, hdr, EDFLIB_READ_ALL_ANNOTATIONS))
  {
    switch (hdr->filetype)
    {
    case EDFLIB_MALLOC_ERROR                :
      printf("\nmalloc error\n\n");
      break;
    case EDFLIB_NO_SUCH_FILE_OR_DIRECTORY   :
      printf("\ncannot open file, no such file or directory: %s\n\n", filename);
      break;
    case EDFLIB_FILE_CONTAINS_FORMAT_ERRORS :
      printf("\nthe file is not EDF(+) or BDF(+) compliant\n"
             "(it contains format errors)\n\n");
      break;
    case EDFLIB_MAXFILES_REACHED            :
      printf("\nto many files opened\n\n");
      break;
    case EDFLIB_FILE_READ_ERROR             :
      printf("\na read error occurred\n\n");
      break;
    case EDFLIB_FILE_ALREADY_OPENED         :
      printf("\nfile has already been opened\n\n");
      break;
    default                                 :
      printf("\nunknown error\n\n");
      break;
    }
  }
  // set the file in the cache
  set_hdr_cache(hdr);
}

// TODO(joshblum): implement a call to this on websocket close
void close_edf(char* filename)
{
  edf_hdr_struct* hdr = get_hdr_cache(filename);
  edfclose_file(hdr->handle);
  pop_hdr_cache(filename);
}

double* create_buffer(int n, int hdl)
{
  double* buf = (double * ) malloc(sizeof(double[n]));
  if (buf == NULL)
  {
    printf("\nmalloc error\n");
    edfclose_file(hdl);
  }
  return buf;
}

int read_samples(int hdl, int ch, int n, double *buf)
{
  int bytes_read = edfread_physical_samples(hdl, ch, n, buf);

  if (bytes_read == -1)
  {
    printf("\nerror: edf_read_physical_samples()\n");
    edfclose_file(hdl);
    free(buf);
    exit(1);
  }
  // clear buffer in case we didn't read as much as we expected to
  for (int i = bytes_read; i < n; i++) {
    buf[i] = 0.0;
  }
  return bytes_read;
}

// Create a hamming window of windowLength samples in buffer
void hamming(int windowLength, double* buffer)
{
  for (int i = 0; i < windowLength; i++)
  {
    buffer[i] = 0.54 - (0.46 * cos( 2 * M_PI * (i / ((windowLength - 1) * 1.0))));
  }
}

static inline double abs(fftw_complex* arr, int i)
{
  return sqrt(arr[i][0] * arr[i][0] + arr[i][1] * arr[i][1]);
}

// copy pasta http://ofdsp.blogspot.co.il/2011/08/short-time-fourier-transform-with-fftw3.html
/*
 * Fill the `specs` matrix with values for the spectrogram for the given diff.
 * `specs` is expected to be initialized and the results are added to allow averaging
 */
void STFT(arma::rowvec& diff, spec_params_t* spec_params, arma::mat& specs)
{
  fftw_complex    *data, *fft_result;
  fftw_plan       plan_forward;
  int nfft = spec_params->nfft;

  int nstep = spec_params->nstep;

  int nblocks = spec_params->nblocks;
  int nfreqs = spec_params->nfreqs;
  int nsamples = spec_params->nsamples;

  data = ( fftw_complex* ) fftw_malloc( sizeof( fftw_complex ) * nfft);
  fft_result = ( fftw_complex* ) fftw_malloc( sizeof( fftw_complex ) * nfft);
  // TODO keep plans in memory until end, create plans once and cache?
  plan_forward = fftw_plan_dft_1d(nfft, data, fft_result,
                                  FFTW_FORWARD, FFTW_ESTIMATE);

  // Create a hamming window of appropriate length
  double window[nfft];
  hamming(nfft, window);

  for (int idx = 0; idx < nblocks; idx++)
  {
    // get the last chunk
    if (idx * nstep + nfft > nsamples)
    {
      int upper_bound = nsamples - idx * nstep;
      for (int i = 0; i < upper_bound; i++)
      {
        data[i][0] = diff(idx * nstep + i) * window[i];
        data[i][1] = 0.0;

      }
      for (int i = upper_bound; i < nfft; i++)
      {
        data[i][0] = 0.0;
        data[i][1] = 0.0;
      }
      break;
    }
    else
    {
      for (int i = 0; i < nfft; i++)
      {
        // TODO vector multiplication?
        data[i][0] = diff(idx * nstep  + i) * window[i];
        data[i][1] = 0.0;
      }
    }

    // Perform the FFT on our chunk
    fftw_execute( plan_forward );

    // TODO: change maybe?
    // http://www.fftw.org/fftw2_doc/fftw_2.html
    for (int i = 0; i < nfreqs; i++)
    {
      specs(i, idx) += abs(fft_result, i) / nfft;
    }

    // Uncomment to see the raw-data output from the FFT calculation
    // printf("[ ");
    // for (int i = 0 ; i < 10 ; i++ )
    // {
    //   printf("%2.2f ", specs(i, idx));
    // }
    // printf("]\n");
  }

  fftw_destroy_plan( plan_forward );

  fftw_free( data );
  fftw_free( fft_result );
}

void eeg_file_spectrogram_handler(char* filename, float duration, int ch, float* out)
{
  spec_params_t spec_params;
  get_eeg_spectrogram_params(&spec_params, filename, duration);
  print_spec_params_t(&spec_params);
  eeg_spectrogram_handler(&spec_params, ch, out);
}

void eeg_spectrogram_handler(spec_params_t* spec_params, int ch, float* out)
{
  if (out == NULL)
  {
    out = (float *) malloc(sizeof(float) * spec_params->nblocks * spec_params->nfreqs);
  }
  eeg_spectrogram(spec_params, ch, out);
}

void eeg_spectrogram(spec_params_t* spec_params, int ch, float* out)
{
  // TODO reuse buffers
  // TODO chunking?
  // write edf method to do diff on the fly?
  int nsamples = spec_params->nsamples;
  double* buf1 = create_buffer(nsamples, spec_params->hdl);
  double* buf2 = create_buffer(nsamples, spec_params->hdl);

  // nfreqs x nblocks matrix
  arma::mat specs(spec_params->nfreqs, spec_params->nblocks);
  specs.fill(0);

  int ch_idx1, ch_idx2;
  ch_idx1 = DIFFERENCE_PAIRS[ch].ch_idx[0];
  read_samples(spec_params->hdl, ch_idx1, nsamples, buf1);
  for (int i = 1; i < NUM_DIFFS; i++)
  {
    ch_idx2 = DIFFERENCE_PAIRS[ch].ch_idx[i];
    read_samples(spec_params->hdl, ch_idx2, nsamples, buf2);

    // TODO use arma::rowvec::fixed with fixed size chunks
    arma::rowvec v1 = arma::rowvec(buf1, nsamples);
    arma::rowvec v2 = arma::rowvec(buf2, nsamples);
    arma::rowvec diff = v2 - v1;

    // fill in the spec matrix with fft values
    STFT(diff, spec_params, specs);
    std::swap(buf1, buf2);
  }
  // TODO serialize specs output for each channel
  specs /=  (NUM_DIFFS - 1); // average diff spectrograms

  // tranpose the output
  for (int i = 0; i < spec_params->nfreqs; i++)
  {
    for (int j = 0; j < spec_params->nblocks; j++)
    {
      *(out + i + j*spec_params->nfreqs) = (float) specs(i, j);
    }
  }

}
<|endoftext|>


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

#include "httprpc.h"

#include "base58.h"
#include "chainparams.h"
#include "httpserver.h"
#include "rpc/protocol.h"
#include "rpc/server.h"
#include "random.h"
#include "sync.h"
#include "util.h"
#include "utilstrencodings.h"
#include "ui_interface.h"
#include "crypto/hmac_sha256.h"
#include <stdio.h>
#include "utilstrencodings.h"

#include <boost/algorithm/string.hpp> // boost::trim
#include <boost/foreach.hpp> //BOOST_FOREACH

/** WWW-Authenticate to present with 401 Unauthorized response */
static const char* WWW_AUTH_HEADER_DATA = "Basic realm=\"jsonrpc\"";

/** Simple one-shot callback timer to be used by the RPC mechanism to e.g.
 * re-lock the wallet.
 */
class HTTPRPCTimer : public RPCTimerBase
{
public:
    HTTPRPCTimer(struct event_base* eventBase, boost::function<void(void)>& func, int64_t millis) :
        ev(eventBase, false, func)
    {
        struct timeval tv;
        tv.tv_sec = millis/1000;
        tv.tv_usec = (millis%1000)*1000;
        ev.trigger(&tv);
    }
private:
    HTTPEvent ev;
};

class HTTPRPCTimerInterface : public RPCTimerInterface
{
public:
    HTTPRPCTimerInterface(struct event_base* _base) : base(_base)
    {
    }
    const char* Name()
    {
        return "HTTP";
    }
    RPCTimerBase* NewTimer(boost::function<void(void)>& func, int64_t millis)
    {
        return new HTTPRPCTimer(base, func, millis);
    }
private:
    struct event_base* base;
};

/* Pre-base64-encoded authentication token */
static std::string strRPCUserColonPass;
/* Stored RPC timer interface (for unregistration) */
static HTTPRPCTimerInterface* httpRPCTimerInterface = 0;

static void JSONErrorReply(HTTPRequest* req, const UniValue& objError, const UniValue& id)
{
    // Send error reply from json-rpc error object
    int nStatus = HTTP_INTERNAL_SERVER_ERROR;
    int code = find_value(objError, "code").get_int();

    if (code == RPC_INVALID_REQUEST)
        nStatus = HTTP_BAD_REQUEST;
    else if (code == RPC_METHOD_NOT_FOUND)
        nStatus = HTTP_NOT_FOUND;

    std::string strReply = JSONRPCReply(NullUniValue, objError, id);

    req->WriteHeader("Content-Type", "application/json");
    req->WriteReply(nStatus, strReply);
}

//This function checks username and password against -rpcauth
//entries from config file.
static bool multiUserAuthorized(std::string strUserPass)
{
    if (strUserPass.find(":") == std::string::npos) {
        return false;
    }
    std::string strUser = strUserPass.substr(0, strUserPass.find(":"));
    std::string strPass = strUserPass.substr(strUserPass.find(":") + 1);

    if (mapMultiArgs.count("-rpcauth") > 0) {
        //Search for multi-user login/pass "rpcauth" from config
        BOOST_FOREACH(std::string strRPCAuth, mapMultiArgs.at("-rpcauth"))
        {
            std::vector<std::string> vFields;
            boost::split(vFields, strRPCAuth, boost::is_any_of(":$"));
            if (vFields.size() != 3) {
                //Incorrect formatting in config file
                continue;
            }

            std::string strName = vFields[0];
            if (!TimingResistantEqual(strName, strUser)) {
                continue;
            }

            std::string strSalt = vFields[1];
            std::string strHash = vFields[2];

            unsigned int KEY_SIZE = 32;
            unsigned char out[KEY_SIZE];

            CHMAC_SHA256(reinterpret_cast<const unsigned char*>(strSalt.c_str()), strSalt.size()).Write(reinterpret_cast<const unsigned char*>(strPass.c_str()), strPass.size()).Finalize(out);
            std::vector<unsigned char> hexvec(out, out+KEY_SIZE);
            std::string strHashFromPass = HexStr(hexvec);

            if (TimingResistantEqual(strHashFromPass, strHash)) {
                return true;
            }
        }
    }
    return false;
}

static bool RPCAuthorized(const std::string& strAuth, std::string& strAuthUsernameOut)
{
    if (strRPCUserColonPass.empty()) // Belt-and-suspenders measure if InitRPCAuthentication was not called
        return false;
    if (strAuth.substr(0, 6) != "Basic ")
        return false;
    std::string strUserPass64 = strAuth.substr(6);
    boost::trim(strUserPass64);
    std::string strUserPass = DecodeBase64(strUserPass64);

    if (strUserPass.find(":") != std::string::npos)
        strAuthUsernameOut = strUserPass.substr(0, strUserPass.find(":"));

    //Check if authorized under single-user field
    if (TimingResistantEqual(strUserPass, strRPCUserColonPass)) {
        return true;
    }
    return multiUserAuthorized(strUserPass);
}

static bool HTTPReq_JSONRPC(HTTPRequest* req, const std::string &)
{
    // JSONRPC handles only POST
    if (req->GetRequestMethod() != HTTPRequest::POST) {
        req->WriteReply(HTTP_BAD_METHOD, "JSONRPC server handles only POST requests");
        return false;
    }
    // Check authorization
    std::pair<bool, std::string> authHeader = req->GetHeader("authorization");
    if (!authHeader.first) {
        req->WriteHeader("WWW-Authenticate", WWW_AUTH_HEADER_DATA);
        req->WriteReply(HTTP_UNAUTHORIZED);
        return false;
    }

    JSONRPCRequest jreq;
    if (!RPCAuthorized(authHeader.second, jreq.authUser)) {
        LogPrintf("ThreadRPCServer incorrect password attempt from %s\n", req->GetPeer().ToString());

        /* Deter brute-forcing
           If this results in a DoS the user really
           shouldn't have their RPC port exposed. */
        MilliSleep(250);

        req->WriteHeader("WWW-Authenticate", WWW_AUTH_HEADER_DATA);
        req->WriteReply(HTTP_UNAUTHORIZED);
        return false;
    }

    try {
        // Parse request
        UniValue valRequest;
        if (!valRequest.read(req->ReadBody()))
            throw JSONRPCError(RPC_PARSE_ERROR, "Parse error");

        // Set the URI
        jreq.URI = req->GetURI();

        std::string strReply;
        // singleton request
        if (valRequest.isObject()) {
            jreq.parse(valRequest);

            UniValue result = tableRPC.execute(jreq);

            // Send reply
            strReply = JSONRPCReply(result, NullUniValue, jreq.id);

        // array of requests
        } else if (valRequest.isArray())
            strReply = JSONRPCExecBatch(valRequest.get_array());
        else
            throw JSONRPCError(RPC_PARSE_ERROR, "Top-level object parse error");

        req->WriteHeader("Content-Type", "application/json");
        req->WriteReply(HTTP_OK, strReply);
    } catch (const UniValue& objError) {
        JSONErrorReply(req, objError, jreq.id);
        return false;
    } catch (const std::exception& e) {
        JSONErrorReply(req, JSONRPCError(RPC_PARSE_ERROR, e.what()), jreq.id);
        return false;
    }
    return true;
}

static bool InitRPCAuthentication()
{
    if (GetArg("-rpcpassword", "") == "")
    {
        LogPrintf("No rpcpassword set - using random cookie authentication\n");
        if (!GenerateAuthCookie(&strRPCUserColonPass)) {
            uiInterface.ThreadSafeMessageBox(
                _("Error: A fatal internal error occurred, see debug.log for details"), // Same message as AbortNode
                "", CClientUIInterface::MSG_ERROR);
            return false;
        }
    } else {
        LogPrintf("Config options rpcuser and rpcpassword will soon be deprecated. Locally-run instances may remove rpcuser to use cookie-based auth, or may be replaced with rpcauth. Please see share/rpcuser for rpcauth auth generation.\n");
        strRPCUserColonPass = GetArg("-rpcuser", "") + ":" + GetArg("-rpcpassword", "");
    }
    return true;
}

bool StartHTTPRPC()
{
    LogPrint("rpc", "Starting HTTP RPC server\n");
    if (!InitRPCAuthentication())
        return false;

    RegisterHTTPHandler("/", true, HTTPReq_JSONRPC);

    assert(EventBase());
    httpRPCTimerInterface = new HTTPRPCTimerInterface(EventBase());
    RPCSetTimerInterface(httpRPCTimerInterface);
    return true;
}

void InterruptHTTPRPC()
{
    LogPrint("rpc", "Interrupting HTTP RPC server\n");
}

void StopHTTPRPC()
{
    LogPrint("rpc", "Stopping HTTP RPC server\n");
    UnregisterHTTPHandler("/", true);
    if (httpRPCTimerInterface) {
        RPCUnsetTimerInterface(httpRPCTimerInterface);
        delete httpRPCTimerInterface;
        httpRPCTimerInterface = 0;
    }
}
<commit_msg>Make KEY_SIZE a compile-time constant<commit_after>// Copyright (c) 2015-2016 The Bitcoin Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.

#include "httprpc.h"

#include "base58.h"
#include "chainparams.h"
#include "httpserver.h"
#include "rpc/protocol.h"
#include "rpc/server.h"
#include "random.h"
#include "sync.h"
#include "util.h"
#include "utilstrencodings.h"
#include "ui_interface.h"
#include "crypto/hmac_sha256.h"
#include <stdio.h>
#include "utilstrencodings.h"

#include <boost/algorithm/string.hpp> // boost::trim
#include <boost/foreach.hpp> //BOOST_FOREACH

/** WWW-Authenticate to present with 401 Unauthorized response */
static const char* WWW_AUTH_HEADER_DATA = "Basic realm=\"jsonrpc\"";

/** Simple one-shot callback timer to be used by the RPC mechanism to e.g.
 * re-lock the wallet.
 */
class HTTPRPCTimer : public RPCTimerBase
{
public:
    HTTPRPCTimer(struct event_base* eventBase, boost::function<void(void)>& func, int64_t millis) :
        ev(eventBase, false, func)
    {
        struct timeval tv;
        tv.tv_sec = millis/1000;
        tv.tv_usec = (millis%1000)*1000;
        ev.trigger(&tv);
    }
private:
    HTTPEvent ev;
};

class HTTPRPCTimerInterface : public RPCTimerInterface
{
public:
    HTTPRPCTimerInterface(struct event_base* _base) : base(_base)
    {
    }
    const char* Name()
    {
        return "HTTP";
    }
    RPCTimerBase* NewTimer(boost::function<void(void)>& func, int64_t millis)
    {
        return new HTTPRPCTimer(base, func, millis);
    }
private:
    struct event_base* base;
};

/* Pre-base64-encoded authentication token */
static std::string strRPCUserColonPass;
/* Stored RPC timer interface (for unregistration) */
static HTTPRPCTimerInterface* httpRPCTimerInterface = 0;

static void JSONErrorReply(HTTPRequest* req, const UniValue& objError, const UniValue& id)
{
    // Send error reply from json-rpc error object
    int nStatus = HTTP_INTERNAL_SERVER_ERROR;
    int code = find_value(objError, "code").get_int();

    if (code == RPC_INVALID_REQUEST)
        nStatus = HTTP_BAD_REQUEST;
    else if (code == RPC_METHOD_NOT_FOUND)
        nStatus = HTTP_NOT_FOUND;

    std::string strReply = JSONRPCReply(NullUniValue, objError, id);

    req->WriteHeader("Content-Type", "application/json");
    req->WriteReply(nStatus, strReply);
}

//This function checks username and password against -rpcauth
//entries from config file.
static bool multiUserAuthorized(std::string strUserPass)
{
    if (strUserPass.find(":") == std::string::npos) {
        return false;
    }
    std::string strUser = strUserPass.substr(0, strUserPass.find(":"));
    std::string strPass = strUserPass.substr(strUserPass.find(":") + 1);

    if (mapMultiArgs.count("-rpcauth") > 0) {
        //Search for multi-user login/pass "rpcauth" from config
        BOOST_FOREACH(std::string strRPCAuth, mapMultiArgs.at("-rpcauth"))
        {
            std::vector<std::string> vFields;
            boost::split(vFields, strRPCAuth, boost::is_any_of(":$"));
            if (vFields.size() != 3) {
                //Incorrect formatting in config file
                continue;
            }

            std::string strName = vFields[0];
            if (!TimingResistantEqual(strName, strUser)) {
                continue;
            }

            std::string strSalt = vFields[1];
            std::string strHash = vFields[2];

            static const unsigned int KEY_SIZE = 32;
            unsigned char out[KEY_SIZE];

            CHMAC_SHA256(reinterpret_cast<const unsigned char*>(strSalt.c_str()), strSalt.size()).Write(reinterpret_cast<const unsigned char*>(strPass.c_str()), strPass.size()).Finalize(out);
            std::vector<unsigned char> hexvec(out, out+KEY_SIZE);
            std::string strHashFromPass = HexStr(hexvec);

            if (TimingResistantEqual(strHashFromPass, strHash)) {
                return true;
            }
        }
    }
    return false;
}

static bool RPCAuthorized(const std::string& strAuth, std::string& strAuthUsernameOut)
{
    if (strRPCUserColonPass.empty()) // Belt-and-suspenders measure if InitRPCAuthentication was not called
        return false;
    if (strAuth.substr(0, 6) != "Basic ")
        return false;
    std::string strUserPass64 = strAuth.substr(6);
    boost::trim(strUserPass64);
    std::string strUserPass = DecodeBase64(strUserPass64);

    if (strUserPass.find(":") != std::string::npos)
        strAuthUsernameOut = strUserPass.substr(0, strUserPass.find(":"));

    //Check if authorized under single-user field
    if (TimingResistantEqual(strUserPass, strRPCUserColonPass)) {
        return true;
    }
    return multiUserAuthorized(strUserPass);
}

static bool HTTPReq_JSONRPC(HTTPRequest* req, const std::string &)
{
    // JSONRPC handles only POST
    if (req->GetRequestMethod() != HTTPRequest::POST) {
        req->WriteReply(HTTP_BAD_METHOD, "JSONRPC server handles only POST requests");
        return false;
    }
    // Check authorization
    std::pair<bool, std::string> authHeader = req->GetHeader("authorization");
    if (!authHeader.first) {
        req->WriteHeader("WWW-Authenticate", WWW_AUTH_HEADER_DATA);
        req->WriteReply(HTTP_UNAUTHORIZED);
        return false;
    }

    JSONRPCRequest jreq;
    if (!RPCAuthorized(authHeader.second, jreq.authUser)) {
        LogPrintf("ThreadRPCServer incorrect password attempt from %s\n", req->GetPeer().ToString());

        /* Deter brute-forcing
           If this results in a DoS the user really
           shouldn't have their RPC port exposed. */
        MilliSleep(250);

        req->WriteHeader("WWW-Authenticate", WWW_AUTH_HEADER_DATA);
        req->WriteReply(HTTP_UNAUTHORIZED);
        return false;
    }

    try {
        // Parse request
        UniValue valRequest;
        if (!valRequest.read(req->ReadBody()))
            throw JSONRPCError(RPC_PARSE_ERROR, "Parse error");

        // Set the URI
        jreq.URI = req->GetURI();

        std::string strReply;
        // singleton request
        if (valRequest.isObject()) {
            jreq.parse(valRequest);

            UniValue result = tableRPC.execute(jreq);

            // Send reply
            strReply = JSONRPCReply(result, NullUniValue, jreq.id);

        // array of requests
        } else if (valRequest.isArray())
            strReply = JSONRPCExecBatch(valRequest.get_array());
        else
            throw JSONRPCError(RPC_PARSE_ERROR, "Top-level object parse error");

        req->WriteHeader("Content-Type", "application/json");
        req->WriteReply(HTTP_OK, strReply);
    } catch (const UniValue& objError) {
        JSONErrorReply(req, objError, jreq.id);
        return false;
    } catch (const std::exception& e) {
        JSONErrorReply(req, JSONRPCError(RPC_PARSE_ERROR, e.what()), jreq.id);
        return false;
    }
    return true;
}

static bool InitRPCAuthentication()
{
    if (GetArg("-rpcpassword", "") == "")
    {
        LogPrintf("No rpcpassword set - using random cookie authentication\n");
        if (!GenerateAuthCookie(&strRPCUserColonPass)) {
            uiInterface.ThreadSafeMessageBox(
                _("Error: A fatal internal error occurred, see debug.log for details"), // Same message as AbortNode
                "", CClientUIInterface::MSG_ERROR);
            return false;
        }
    } else {
        LogPrintf("Config options rpcuser and rpcpassword will soon be deprecated. Locally-run instances may remove rpcuser to use cookie-based auth, or may be replaced with rpcauth. Please see share/rpcuser for rpcauth auth generation.\n");
        strRPCUserColonPass = GetArg("-rpcuser", "") + ":" + GetArg("-rpcpassword", "");
    }
    return true;
}

bool StartHTTPRPC()
{
    LogPrint("rpc", "Starting HTTP RPC server\n");
    if (!InitRPCAuthentication())
        return false;

    RegisterHTTPHandler("/", true, HTTPReq_JSONRPC);

    assert(EventBase());
    httpRPCTimerInterface = new HTTPRPCTimerInterface(EventBase());
    RPCSetTimerInterface(httpRPCTimerInterface);
    return true;
}

void InterruptHTTPRPC()
{
    LogPrint("rpc", "Interrupting HTTP RPC server\n");
}

void StopHTTPRPC()
{
    LogPrint("rpc", "Stopping HTTP RPC server\n");
    UnregisterHTTPHandler("/", true);
    if (httpRPCTimerInterface) {
        RPCUnsetTimerInterface(httpRPCTimerInterface);
        delete httpRPCTimerInterface;
        httpRPCTimerInterface = 0;
    }
}
<|endoftext|>


<commit_before>// TODO: handle --stop specially in ibb.cpp.  it should wait for the server to shut down.

#include <stdio.h>
#include <string.h>
#include <stdarg.h>

#ifdef _WIN32

#define WIN32_LEAN_AND_MEAN
#define STRICT
#define UNICODE
#include <winsock2.h>
#include <shellapi.h>
#include <shlwapi.h>

// UTF-16

#define PSTR(x) L ## x

typedef wchar_t pchar;
size_t pstrlen(const pchar* p) {
    return wcslen(p);
}
int pprintf(const pchar* format, ...) {
    va_list arg;
    va_start(arg, format);
    int result = vwprintf(format, arg);
    va_end(arg);
    return result;
}

#else

#include <unistd.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
typedef int SOCKET; // old-school
const SOCKET INVALID_SOCKET = -1;
const ssize_t SOCKET_ERROR = -1;
int closesocket(SOCKET socket) {
    return close(socket);
}

// UTF-8

#define PSTR(x) x
typedef char pchar;
size_t pstrlen(const pchar* p) {
    return strlen(p);
}
int pprintf(const pchar* format, ...) {
    va_list arg;
    va_start(arg, format);
    int result = vprintf(format, arg);
    va_end(arg);
    return result;
}

#endif

#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <wchar.h>

// int(md5.md5('ibb').hexdigest()[-4:], 16)
const int IBB_PORT = 26830;

int error(const char* msg) {
    fprintf(stderr, "ibb *** error: %s\n", msg);
    return 1;
}

bool openServerConnection(SOCKET* s) {
    *s = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
    if (*s == INVALID_SOCKET) {
        // TODO: print error code
        error("Failed to create socket");
        return false;
    }

    sockaddr_in address;
    address.sin_family = AF_INET;
    address.sin_addr.s_addr = inet_addr("127.0.0.1");
    address.sin_port = htons(IBB_PORT);

    int result = connect(*s, reinterpret_cast<sockaddr*>(&address), sizeof(address));
    if (result) {
        return false;
    }

    return true;
}

#ifdef _WIN32

bool startServer() {
    WCHAR executable_path[MAX_PATH * 2];
    DWORD length = GetModuleFileNameW(GetModuleHandle(NULL), executable_path, MAX_PATH);
    if (length == 0 || length == MAX_PATH) {
        fprintf(stderr, "ibb *** failed to get executable path\n");
        return false;
    }

    wchar_t* last_slash = wcsrchr(executable_path, '\\');
    if (last_slash) {
        *last_slash = 0;
    } else {
        last_slash = executable_path;
    }
    wcsncpy(last_slash, L"\\ibb_server.exe", MAX_PATH);

    if (PathFileExists(executable_path)) {
        // Launch server executable directly.

        // TODO: use CreateProcess instead of ShellExecute
        // TODO: print error code
        HINSTANCE result = ShellExecuteW(0, L"open", executable_path, executable_path, NULL, SW_SHOW);
        return result > reinterpret_cast<HINSTANCE>(32);
    } else {
        // Launch server from Python.

        WCHAR python_path[MAX_PATH + 1] = {0};
        LONG size = sizeof(python_path);
        LONG success = RegQueryValueW(
            HKEY_LOCAL_MACHINE,
            L"SOFTWARE\\Python\\PythonCore\\3.1\\InstallPath",
            python_path,
            &size);
        if (!success) {
            // TODO: print error
            fprintf(stderr, "ibb *** failed to locate Python 3.1\n");
            return false;
        }

        // TODO: use safe strings
        wcsncat(python_path, L"\\python.exe", MAX_PATH);

        wcsncpy(last_slash, L"\\src\\ibb.py", MAX_PATH);

        // TODO: use CreateProcess instead of ShellExecute
        // TODO: print error code
        HINSTANCE result = ShellExecuteW(0, L"open", python_path, executable_path, NULL, SW_SHOW);
        return result > reinterpret_cast<HINSTANCE>(32);
    }
}

#else

// Mac
#include <mach-o/dyld.h>

bool startServer() {
    uint32_t pathSize = 0;
    // should return -1
    _NSGetExecutablePath(0, &pathSize);

    char executablePath[pathSize];
    if (0 != _NSGetExecutablePath(executablePath, &pathSize)) {
        fprintf(stderr, "ibb *** failed to get executable path\n");
        return false;
    }

    // TODO: Mac deployment (port to Python 2?)
    /*
    wchar_t* last_slash = wcsrchr(executable_path, '\\');
    if (last_slash) {
        *last_slash = 0;
    } else {
        last_slash = executable_path;
    }
    wcsncpy(last_slash, L"\\ibb_server.exe", MAX_PATH);

    if (PathFileExists(executable_path)) {
        // Launch server executable directly.

        // TODO: use CreateProcess instead of ShellExecute
        // TODO: print error code
        HINSTANCE result = ShellExecuteW(0, L"open", executable_path, executable_path, NULL, SW_SHOW);
        return result > reinterpret_cast<HINSTANCE>(32);
    } else {
    */

    // Launch server from Python.
    // TODO: check for python3

    if (0 != system("python3 src/ibb.py &")) {
        fprintf(stderr, "ibb *** failed to run ibb.py\n");
        return false;
    }

    // TODO: how do you wait for a process to be ready to accept connections?

    return true;
}

#endif

void sendString(SOCKET connection, const pchar* begin, const pchar* end = 0) {
    if (!end) {
        end = begin + pstrlen(begin);
    }

    // UTF-16 over the wire on Windows
    // UTF-32 on Linux and Mac
    send(
        connection,
        reinterpret_cast<const char*>(begin),
        (end - begin) * sizeof(pchar),
        0);

    // TODO: error checking
}

struct ScopedFree {
    explicit ScopedFree(char* p)
        : p(p)
    {}

    ~ScopedFree() {
        free(p);
    }

private:
    char* p;
};

bool sendBuild(SOCKET connection, int argc, const pchar* argv[], clock_t start) {
    sendString(connection, PSTR("version: 1\n"));

    sendString(connection, PSTR("cwd: "));

    #ifdef _WIN32
    WCHAR current_directory[MAX_PATH] = {0};
    GetCurrentDirectoryW(MAX_PATH, current_directory);
    #else
    char* current_directory = getcwd(0, 0);
    ScopedFree scoped_free(current_directory);
    #endif

    sendString(connection, current_directory);
    sendString(connection, PSTR("\n"));

    for (int i = 0; i < argc; ++i) {
        sendString(connection, PSTR("arg: "));
        sendString(connection, argv[i]);
        sendString(connection, PSTR("\n"));
    }

    sendString(connection, PSTR("build\n"));

    //printf("Build sent in %g seconds\n", float(clock() - start) / CLOCKS_PER_SEC);
    //fflush(stdout);

    for (;;) {
        const int BUFFER_LENGTH = 1024;
        pchar buffer[BUFFER_LENGTH + 1];
        const int bytes = recv(
            connection,
            reinterpret_cast<char*>(buffer),
            sizeof(pchar) * BUFFER_LENGTH,
            0);
        if (0 == bytes) {
            break;
        }
        if (SOCKET_ERROR == bytes) {
            error("Broken connection");
            break;
        }
        const int chars = bytes / sizeof(pchar); // TODO: handle odd bytes values
        buffer[chars] = 0;
        pprintf(PSTR("%s"), buffer); // %*s sometimes wrote extra crap at the end
    }

    //printf("Result recieved in %g seconds\n", float(clock() - start) / CLOCKS_PER_SEC);
    //fflush(stdout);

    return true;
}

int pmain(int argc, const pchar* argv[]) {
    clock_t start = clock();

#ifdef _WIN32
    WSADATA wsadata;
    if (0 != WSAStartup(2, &wsadata)) {
        return error("Failed to initialize winsock");
    }

    struct cleanup_t {
        cleanup_t() {}
        ~cleanup_t() { WSACleanup(); }
    } cleanup;
#endif

    //printf("Started winsock in %g seconds\n", float(clock() - start) / CLOCKS_PER_SEC);
    //fflush(stdout);

    SOCKET connection;
    if (!openServerConnection(&connection)) {
        if (!startServer()) {
            return error("Failed to start server");
        }
        if (!openServerConnection(&connection)) {
            return error("Failed to connect to server");
        }
    }

    //printf("Opened connection in %g seconds\n", float(clock() - start) / CLOCKS_PER_SEC);
    //fflush(stdout);

    if (!sendBuild(connection, argc, argv, start)) {
        return error("Failed to submit build");
    }

    closesocket(connection);

    //printf("Socket closed in %g seconds\n", float(clock() - start) / CLOCKS_PER_SEC);
    //fflush(stdout);
    return 0;
}

// hack around mingw's lack of wmain support
#ifdef _WIN32

int main() {
    int argc;
    WCHAR** argv = CommandLineToArgvW(
        GetCommandLineW(),
        &argc);
    return pmain(argc, const_cast<const wchar_t**>(argv));
}

#else

int main(int argc, const char* argv[]) {
    return pmain(argc, argv);
}

#endif
<commit_msg>make server startup a bit more reliable<commit_after>// TODO: handle --stop specially in ibb.cpp.  it should wait for the server to shut down.

#include <algorithm>
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <wchar.h>

#ifdef _WIN32

#define WIN32_LEAN_AND_MEAN
#define STRICT
#define UNICODE
#include <winsock2.h>
#include <shellapi.h>
#include <shlwapi.h>

// UTF-16

#define PSTR(x) L ## x

typedef wchar_t pchar;
size_t pstrlen(const pchar* p) {
    return wcslen(p);
}
int pprintf(const pchar* format, ...) {
    va_list arg;
    va_start(arg, format);
    int result = vwprintf(format, arg);
    va_end(arg);
    return result;
}

#else

#include <unistd.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
typedef int SOCKET; // old-school
const SOCKET INVALID_SOCKET = -1;
const ssize_t SOCKET_ERROR = -1;
int closesocket(SOCKET socket) {
    return close(socket);
}

// UTF-8

#define PSTR(x) x
typedef char pchar;
size_t pstrlen(const pchar* p) {
    return strlen(p);
}
int pprintf(const pchar* format, ...) {
    va_list arg;
    va_start(arg, format);
    int result = vprintf(format, arg);
    va_end(arg);
    return result;
}

#endif

// int(md5.md5('ibb').hexdigest()[-4:], 16)
const int IBB_PORT = 26830;

int error(const char* msg) {
    fprintf(stderr, "ibb *** error: %s\n", msg);
    return 1;
}

enum ConnectionRetry {
    NO_RETRY,
    RETRY
};

int MAX_RETRY_TIME = 30; // seconds

bool openServerConnection(SOCKET* s, ConnectionRetry retry) {
    *s = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
    if (*s == INVALID_SOCKET) {
        // TODO: print error code
        error("Failed to create socket");
        return false;
    }

    sockaddr_in address;
    address.sin_family = AF_INET;
    address.sin_addr.s_addr = inet_addr("127.0.0.1");
    address.sin_port = htons(IBB_PORT);

    time_t now = time(0);
    unsigned sleep = 10; // milliseconds
    for (;;) {
        int result = connect(*s, reinterpret_cast<sockaddr*>(&address), sizeof(address));
        if (result) {
            return false;
        }
        if (retry == RETRY && (time(0) - now) < MAX_RETRY_TIME) {
            usleep(sleep * 1000);
            sleep = std::min(100u, sleep + sleep / 2);
        } else {
            break;
        }
    }

    return true;
}

#ifdef _WIN32

bool startServer() {
    WCHAR executable_path[MAX_PATH * 2];
    DWORD length = GetModuleFileNameW(GetModuleHandle(NULL), executable_path, MAX_PATH);
    if (length == 0 || length == MAX_PATH) {
        fprintf(stderr, "ibb *** failed to get executable path\n");
        return false;
    }

    wchar_t* last_slash = wcsrchr(executable_path, '\\');
    if (last_slash) {
        *last_slash = 0;
    } else {
        last_slash = executable_path;
    }
    wcsncpy(last_slash, L"\\ibb_server.exe", MAX_PATH);

    if (PathFileExists(executable_path)) {
        // Launch server executable directly.

        // TODO: use CreateProcess instead of ShellExecute
        // TODO: print error code
        HINSTANCE result = ShellExecuteW(0, L"open", executable_path, executable_path, NULL, SW_SHOW);
        return result > reinterpret_cast<HINSTANCE>(32);
    } else {
        // Launch server from Python.

        WCHAR python_path[MAX_PATH + 1] = {0};
        LONG size = sizeof(python_path);
        LONG success = RegQueryValueW(
            HKEY_LOCAL_MACHINE,
            L"SOFTWARE\\Python\\PythonCore\\3.1\\InstallPath",
            python_path,
            &size);
        if (!success) {
            // TODO: print error
            fprintf(stderr, "ibb *** failed to locate Python 3.1\n");
            return false;
        }

        // TODO: use safe strings
        wcsncat(python_path, L"\\python.exe", MAX_PATH);

        wcsncpy(last_slash, L"\\src\\ibb.py", MAX_PATH);

        // TODO: use CreateProcess instead of ShellExecute
        // TODO: print error code
        HINSTANCE result = ShellExecuteW(0, L"open", python_path, executable_path, NULL, SW_SHOW);
        return result > reinterpret_cast<HINSTANCE>(32);
    }
}

#else

// Mac
#include <mach-o/dyld.h>

bool startServer() {
    uint32_t pathSize = 0;
    // should return -1
    _NSGetExecutablePath(0, &pathSize);

    char executablePath[pathSize];
    if (0 != _NSGetExecutablePath(executablePath, &pathSize)) {
        fprintf(stderr, "ibb *** failed to get executable path\n");
        return false;
    }

    // TODO: Mac deployment (port to Python 2?)
    /*
    wchar_t* last_slash = wcsrchr(executable_path, '\\');
    if (last_slash) {
        *last_slash = 0;
    } else {
        last_slash = executable_path;
    }
    wcsncpy(last_slash, L"\\ibb_server.exe", MAX_PATH);

    if (PathFileExists(executable_path)) {
        // Launch server executable directly.

        // TODO: use CreateProcess instead of ShellExecute
        // TODO: print error code
        HINSTANCE result = ShellExecuteW(0, L"open", executable_path, executable_path, NULL, SW_SHOW);
        return result > reinterpret_cast<HINSTANCE>(32);
    } else {
    */

    // Launch server from Python.
    // TODO: check for python3

    // TODO: figure out how to spawn python3 directory
    // without going through system()
    /*
    pid_t child = fork();
    if (child == 0) {
        // TODO: src/ibb.py relative to this directory
        execl("python3", "src/ibb.py", static_cast<char*>(0));
        abort(); // unreachable under success
    } else if (child == -1) {
        return false;
    } else {
        return true;
    }
    */

    system("python3 src/ibb.py &");

    // TODO: how do you wait for a process to be ready to accept connections?
    // TODO: the internet seems to think "just try to connect for a while"

    return true;
}

#endif

void sendString(SOCKET connection, const pchar* begin, const pchar* end = 0) {
    if (!end) {
        end = begin + pstrlen(begin);
    }

    // UTF-16 over the wire on Windows
    // UTF-32 on Linux and Mac
    send(
        connection,
        reinterpret_cast<const char*>(begin),
        (end - begin) * sizeof(pchar),
        0);

    // TODO: error checking
}

struct ScopedFree {
    explicit ScopedFree(char* p)
        : p(p)
    {}

    ~ScopedFree() {
        free(p);
    }

private:
    char* p;
};

bool sendBuild(SOCKET connection, int argc, const pchar* argv[], clock_t start) {
    sendString(connection, PSTR("version: 1\n"));

    sendString(connection, PSTR("cwd: "));

    #ifdef _WIN32
    WCHAR current_directory[MAX_PATH] = {0};
    GetCurrentDirectoryW(MAX_PATH, current_directory);
    #else
    char* current_directory = getcwd(0, 0);
    ScopedFree scoped_free(current_directory);
    #endif

    sendString(connection, current_directory);
    sendString(connection, PSTR("\n"));

    for (int i = 0; i < argc; ++i) {
        sendString(connection, PSTR("arg: "));
        sendString(connection, argv[i]);
        sendString(connection, PSTR("\n"));
    }

    sendString(connection, PSTR("build\n"));

    //printf("Build sent in %g seconds\n", float(clock() - start) / CLOCKS_PER_SEC);
    //fflush(stdout);

    for (;;) {
        const int BUFFER_LENGTH = 1024;
        pchar buffer[BUFFER_LENGTH + 1];
        const int bytes = recv(
            connection,
            reinterpret_cast<char*>(buffer),
            sizeof(pchar) * BUFFER_LENGTH,
            0);
        if (0 == bytes) {
            break;
        }
        if (SOCKET_ERROR == bytes) {
            error("Broken connection");
            break;
        }
        const int chars = bytes / sizeof(pchar); // TODO: handle odd bytes values
        buffer[chars] = 0;
        pprintf(PSTR("%s"), buffer); // %*s sometimes wrote extra crap at the end
    }

    //printf("Result recieved in %g seconds\n", float(clock() - start) / CLOCKS_PER_SEC);
    //fflush(stdout);

    return true;
}

int pmain(int argc, const pchar* argv[]) {
    clock_t start = clock();

#ifdef _WIN32
    WSADATA wsadata;
    if (0 != WSAStartup(2, &wsadata)) {
        return error("Failed to initialize winsock");
    }

    struct cleanup_t {
        cleanup_t() {}
        ~cleanup_t() { WSACleanup(); }
    } cleanup;
#endif

    //printf("Started winsock in %g seconds\n", float(clock() - start) / CLOCKS_PER_SEC);
    //fflush(stdout);

    SOCKET connection;
    if (!openServerConnection(&connection, NO_RETRY)) {
        if (!startServer()) {
            return error("Failed to start server");
        }
        if (!openServerConnection(&connection, RETRY)) {
            return error("Failed to connect to server");
        }
    }

    //printf("Opened connection in %g seconds\n", float(clock() - start) / CLOCKS_PER_SEC);
    //fflush(stdout);

    if (!sendBuild(connection, argc, argv, start)) {
        return error("Failed to submit build");
    }

    closesocket(connection);

    //printf("Socket closed in %g seconds\n", float(clock() - start) / CLOCKS_PER_SEC);
    //fflush(stdout);
    return 0;
}

// hack around mingw's lack of wmain support
#ifdef _WIN32

int main() {
    int argc;
    WCHAR** argv = CommandLineToArgvW(
        GetCommandLineW(),
        &argc);
    return pmain(argc, const_cast<const wchar_t**>(argv));
}

#else

int main(int argc, const char* argv[]) {
    return pmain(argc, argv);
}

#endif
<|endoftext|>