<commit_before>//------------------------------------------------------------------------------
// CLING - the C++ LLVM-based InterpreterG :)
// author:  Baozeng Ding <sploving1@gmail.com>
// author:  Vassil Vassilev <vasil.georgiev.vasilev@cern.ch>
//
// This file is dual-licensed: you can choose to license it under the University
// of Illinois Open Source License or the GNU Lesser General Public License. See
// LICENSE.TXT for details.
//------------------------------------------------------------------------------

#include "NullDerefProtectionTransformer.h"

#include "cling/Interpreter/Interpreter.h"
#include "cling/Utils/AST.h"

#include "clang/AST/ASTContext.h"
#include "clang/AST/Decl.h"
#include "clang/AST/Expr.h"
#include "clang/AST/RecursiveASTVisitor.h"
#include "clang/Basic/SourceLocation.h"
#include "clang/Basic/SourceManager.h"
#include "clang/Frontend/CompilerInstance.h"
#include "clang/Sema/Lookup.h"

#include <bitset>

using namespace clang;

namespace {
using namespace cling;

class PointerCheckInjector : public RecursiveASTVisitor<PointerCheckInjector> {
  private:
    Interpreter& m_Interp;
    Sema& m_Sema;
    typedef llvm::DenseMap<clang::FunctionDecl*, std::bitset<32> > decl_map_t;
    llvm::DenseMap<clang::FunctionDecl*, std::bitset<32> > m_NonNullArgIndexs;

    ///\brief Needed for the AST transformations, owned by Sema.
    ///
    ASTContext& m_Context;

    ///\brief cling_runtime_internal_throwIfInvalidPointer cache.
    ///
    LookupResult* m_clingthrowIfInvalidPointerCache;

    bool IsTransparentThis(Expr* E) {
      if (llvm::isa<CXXThisExpr>(E))
        return true;
      if (auto ICE = dyn_cast<ImplicitCastExpr>(E))
        return IsTransparentThis(ICE->getSubExpr());
      return false;
    }

  public:
    PointerCheckInjector(Interpreter& I)
      : m_Interp(I), m_Sema(I.getCI()->getSema()),
        m_Context(I.getCI()->getASTContext()),
        m_clingthrowIfInvalidPointerCache(0) {}

    ~PointerCheckInjector() {
      delete m_clingthrowIfInvalidPointerCache;
    }

    bool VisitUnaryOperator(UnaryOperator* UnOp) {
      Expr* SubExpr = UnOp->getSubExpr();
      VisitStmt(SubExpr);
      if (UnOp->getOpcode() == UO_Deref
          && !IsTransparentThis(SubExpr)
          && SubExpr->getType().getTypePtr()->isPointerType())
          UnOp->setSubExpr(SynthesizeCheck(SubExpr));
      return true;
    }

    bool VisitMemberExpr(MemberExpr* ME) {
      Expr* Base = ME->getBase();
      VisitStmt(Base);
      if (ME->isArrow()
          && !IsTransparentThis(Base)
          && ME->getMemberDecl()->isCXXInstanceMember())
        ME->setBase(SynthesizeCheck(Base));
      return true;
    }

    bool VisitCallExpr(CallExpr* CE) {
      VisitStmt(CE->getCallee());
      FunctionDecl* FDecl = CE->getDirectCallee();
      if (FDecl && isDeclCandidate(FDecl)) {
        decl_map_t::const_iterator it = m_NonNullArgIndexs.find(FDecl);
        const std::bitset<32>& ArgIndexs = it->second;
        Sema::ContextRAII pushedDC(m_Sema, FDecl);
        for (int index = 0; index < 32; ++index) {
          if (ArgIndexs.test(index)) {
            // Get the argument with the nonnull attribute.
            Expr* Arg = CE->getArg(index);
            if (Arg->getType().getTypePtr()->isPointerType()
                && !IsTransparentThis(Arg))
              CE->setArg(index, SynthesizeCheck(Arg));
          }
        }
      }
      return true;
    }

    bool TraverseFunctionDecl(FunctionDecl* FD) {
      // We cannot synthesize when there is a const expr
      // and if it is a function template (we will do the transformation on
      // the instance).
      if (!FD->isConstexpr() && !FD->getDescribedFunctionTemplate())
         RecursiveASTVisitor::TraverseFunctionDecl(FD);
      return true;
    }

    bool TraverseCXXMethodDecl(CXXMethodDecl* CXXMD) {
      // We cannot synthesize when there is a const expr.
      if (!CXXMD->isConstexpr())
        RecursiveASTVisitor::TraverseCXXMethodDecl(CXXMD);
      return true;
    }

  private:
    Expr* SynthesizeCheck(Expr* Arg) {
      assert(Arg && "Cannot call with Arg=0");

      if(!m_clingthrowIfInvalidPointerCache)
        FindAndCacheRuntimeLookupResult();

      SourceLocation Loc = Arg->getBeginLoc();
      Expr* VoidSemaArg = utils::Synthesize::CStyleCastPtrExpr(&m_Sema,
                                                            m_Context.VoidPtrTy,
                                                            (uintptr_t)&m_Interp);
      Expr* VoidExprArg = utils::Synthesize::CStyleCastPtrExpr(&m_Sema,
                                                          m_Context.VoidPtrTy,
                                                          (uintptr_t)Arg);
      Scope* S = m_Sema.getScopeForContext(m_Sema.CurContext);
      CXXScopeSpec CSS;

      Expr* checkCall
        = m_Sema.BuildDeclarationNameExpr(CSS,
                                          *m_clingthrowIfInvalidPointerCache,
                                         /*ADL*/ false).get();
      const clang::FunctionProtoType* checkCallType
        = llvm::dyn_cast<const clang::FunctionProtoType>(
            checkCall->getType().getTypePtr());

      TypeSourceInfo* constVoidPtrTSI = m_Context.getTrivialTypeSourceInfo(
        checkCallType->getParamType(2), Loc);

      // It is unclear whether this is the correct cast if the type
      // is dependent.  Hence, For now, we do not expect SynthesizeCheck to
      // be run on a function template.  It should be run only on function
      // instances.
      // When this is actually insert in a function template, it seems that
      // clang r272382 when instantiating the templates drops one of the part
      // of the implicit cast chain.
      // Namely in:
/*
`-ImplicitCastExpr 0x1010cea90 <col:4> 'const void *' <BitCast>
 `-ImplicitCastExpr 0x1026e0bc0 <col:4> 'const class TAttMarker *'
                    <UncheckedDerivedToBase (TAttMarker)>
  `-ImplicitCastExpr 0x1026e0b48 <col:4> 'class TGraph *' <LValueToRValue>
   `-DeclRefExpr 0x1026e0b20 <col:4> 'class TGraph *' lvalue Var 0x1026e09c0
                   'g5' 'class TGraph *'
*/
      // It drops the 2nd lines (ImplicitCastExpr UncheckedDerivedToBase)
      // clang r227800 seems to actually keep that lines during instantiation.
      Expr* voidPtrArg
        = m_Sema.BuildCStyleCastExpr(Loc, constVoidPtrTSI, Loc, Arg).get();

      Expr *args[] = {VoidSemaArg, VoidExprArg, voidPtrArg};

      if (Expr* call = m_Sema.ActOnCallExpr(S, checkCall,
                                            Loc, args, Loc).get())
      {
        // It is unclear whether this is the correct cast if the type
        // is dependent.  Hence, For now, we do not expect SynthesizeCheck to
        // be run on a function template.  It should be run only on function
        // instances.
        clang::TypeSourceInfo* argTSI = m_Context.getTrivialTypeSourceInfo(
                                          Arg->getType(), Loc);
        Expr* castExpr = m_Sema.BuildCStyleCastExpr(Loc, argTSI,
                                                    Loc, call).get();
        return castExpr;
      }
      return voidPtrArg;
    }

    bool isDeclCandidate(FunctionDecl * FDecl) {
      if (m_NonNullArgIndexs.count(FDecl))
        return true;

      if (llvm::isa<CXXRecordDecl>(FDecl))
        return true;

      std::bitset<32> ArgIndexs;
      for (specific_attr_iterator<NonNullAttr>
             I = FDecl->specific_attr_begin<NonNullAttr>(),
             E = FDecl->specific_attr_end<NonNullAttr>(); I != E; ++I) {

        NonNullAttr *NonNull = *I;
        for (NonNullAttr::args_iterator i = NonNull->args_begin(),
               e = NonNull->args_end(); i != e; ++i) {
          ArgIndexs.set(*i);
        }
      }

      if (ArgIndexs.any()) {
        m_NonNullArgIndexs.insert(std::make_pair(FDecl, ArgIndexs));
        return true;
      }
      return false;
    }

    void FindAndCacheRuntimeLookupResult() {
      assert(!m_clingthrowIfInvalidPointerCache && "Called multiple times!?");

      DeclarationName Name
        = &m_Context.Idents.get("cling_runtime_internal_throwIfInvalidPointer");
      SourceLocation noLoc;
      m_clingthrowIfInvalidPointerCache = new LookupResult(m_Sema, Name, noLoc,
                                        Sema::LookupOrdinaryName,
                                        Sema::ForVisibleRedeclaration);
      m_Sema.LookupQualifiedName(*m_clingthrowIfInvalidPointerCache,
                                 m_Context.getTranslationUnitDecl());
      assert(!m_clingthrowIfInvalidPointerCache->empty() &&
              "Lookup of cling_runtime_internal_throwIfInvalidPointer failed!");
    }
  };

  static bool hasPtrCheckDisabledInContext(const Decl* D) {
    if (isa<TranslationUnitDecl>(D))
      return false;
    for (const auto *Ann : D->specific_attrs<AnnotateAttr>()) {
      if (Ann->getAnnotation() == "__cling__ptrcheck(off)")
        return true;
      else if (Ann->getAnnotation() == "__cling__ptrcheck(on)")
        return false;
    }
    const Decl* Parent = nullptr;
    for (auto DC = D->getDeclContext(); !Parent; DC = DC->getParent())
      Parent = dyn_cast<Decl>(DC);

    assert(Parent && "Decl without context!");

    return hasPtrCheckDisabledInContext(Parent);
  }

} // unnamed namespace

namespace cling {
  NullDerefProtectionTransformer::NullDerefProtectionTransformer(Interpreter* I)
    : ASTTransformer(&I->getCI()->getSema()), m_Interp(I) {
  }

  NullDerefProtectionTransformer::~NullDerefProtectionTransformer()
  { }

  bool NullDerefProtectionTransformer::shouldTransform(const clang::Decl* D) {
    if (D->isFromASTFile())
      return false;
    if (D->isTemplateDecl())
      return false;

    if (hasPtrCheckDisabledInContext(D))
      return false;

    auto Loc = D->getLocation();
    if (Loc.isInvalid())
      return false;

    SourceManager& SM = m_Interp->getSema().getSourceManager();
    auto Characteristic = SM.getFileCharacteristic(Loc);
    if (Characteristic != clang::SrcMgr::C_User)
      return false;

    auto FID = SM.getFileID(Loc);
    if (FID.isInvalid())
      return false;

    auto FE = SM.getFileEntryForID(FID);
    if (!FE)
      return false;

    auto Dir = FE->getDir();
    if (!Dir)
      return false;

    auto IterAndInserted = m_ShouldVisitDir.try_emplace(Dir, true);
    if (IterAndInserted.second == false)
      return IterAndInserted.first->second;

    if (llvm::sys::fs::can_write(Dir->getName()))
      return true; // `true` is already emplaced above.

    // Remember that this dir is not writable and should not be visited.
    IterAndInserted.first->second = false;
    return false;
  }

  ASTTransformer::Result
  NullDerefProtectionTransformer::Transform(clang::Decl* D) {
    if (getCompilationOpts().CheckPointerValidity && shouldTransform(D)) {
      PointerCheckInjector injector(*m_Interp);
      injector.TraverseDecl(D);
    }
    return Result(D, true);
  }
} // end namespace cling
<commit_msg>Index goes behind the getASTIndex interface.<commit_after>//------------------------------------------------------------------------------
// CLING - the C++ LLVM-based InterpreterG :)
// author:  Baozeng Ding <sploving1@gmail.com>
// author:  Vassil Vassilev <vasil.georgiev.vasilev@cern.ch>
//
// This file is dual-licensed: you can choose to license it under the University
// of Illinois Open Source License or the GNU Lesser General Public License. See
// LICENSE.TXT for details.
//------------------------------------------------------------------------------

#include "NullDerefProtectionTransformer.h"

#include "cling/Interpreter/Interpreter.h"
#include "cling/Utils/AST.h"

#include "clang/AST/ASTContext.h"
#include "clang/AST/Decl.h"
#include "clang/AST/Expr.h"
#include "clang/AST/RecursiveASTVisitor.h"
#include "clang/Basic/SourceLocation.h"
#include "clang/Basic/SourceManager.h"
#include "clang/Frontend/CompilerInstance.h"
#include "clang/Sema/Lookup.h"

#include <bitset>

using namespace clang;

namespace {
using namespace cling;

class PointerCheckInjector : public RecursiveASTVisitor<PointerCheckInjector> {
  private:
    Interpreter& m_Interp;
    Sema& m_Sema;
    typedef llvm::DenseMap<clang::FunctionDecl*, std::bitset<32> > decl_map_t;
    llvm::DenseMap<clang::FunctionDecl*, std::bitset<32> > m_NonNullArgIndexs;

    ///\brief Needed for the AST transformations, owned by Sema.
    ///
    ASTContext& m_Context;

    ///\brief cling_runtime_internal_throwIfInvalidPointer cache.
    ///
    LookupResult* m_clingthrowIfInvalidPointerCache;

    bool IsTransparentThis(Expr* E) {
      if (llvm::isa<CXXThisExpr>(E))
        return true;
      if (auto ICE = dyn_cast<ImplicitCastExpr>(E))
        return IsTransparentThis(ICE->getSubExpr());
      return false;
    }

  public:
    PointerCheckInjector(Interpreter& I)
      : m_Interp(I), m_Sema(I.getCI()->getSema()),
        m_Context(I.getCI()->getASTContext()),
        m_clingthrowIfInvalidPointerCache(0) {}

    ~PointerCheckInjector() {
      delete m_clingthrowIfInvalidPointerCache;
    }

    bool VisitUnaryOperator(UnaryOperator* UnOp) {
      Expr* SubExpr = UnOp->getSubExpr();
      VisitStmt(SubExpr);
      if (UnOp->getOpcode() == UO_Deref
          && !IsTransparentThis(SubExpr)
          && SubExpr->getType().getTypePtr()->isPointerType())
          UnOp->setSubExpr(SynthesizeCheck(SubExpr));
      return true;
    }

    bool VisitMemberExpr(MemberExpr* ME) {
      Expr* Base = ME->getBase();
      VisitStmt(Base);
      if (ME->isArrow()
          && !IsTransparentThis(Base)
          && ME->getMemberDecl()->isCXXInstanceMember())
        ME->setBase(SynthesizeCheck(Base));
      return true;
    }

    bool VisitCallExpr(CallExpr* CE) {
      VisitStmt(CE->getCallee());
      FunctionDecl* FDecl = CE->getDirectCallee();
      if (FDecl && isDeclCandidate(FDecl)) {
        decl_map_t::const_iterator it = m_NonNullArgIndexs.find(FDecl);
        const std::bitset<32>& ArgIndexs = it->second;
        Sema::ContextRAII pushedDC(m_Sema, FDecl);
        for (int index = 0; index < 32; ++index) {
          if (ArgIndexs.test(index)) {
            // Get the argument with the nonnull attribute.
            Expr* Arg = CE->getArg(index);
            if (Arg->getType().getTypePtr()->isPointerType()
                && !IsTransparentThis(Arg))
              CE->setArg(index, SynthesizeCheck(Arg));
          }
        }
      }
      return true;
    }

    bool TraverseFunctionDecl(FunctionDecl* FD) {
      // We cannot synthesize when there is a const expr
      // and if it is a function template (we will do the transformation on
      // the instance).
      if (!FD->isConstexpr() && !FD->getDescribedFunctionTemplate())
         RecursiveASTVisitor::TraverseFunctionDecl(FD);
      return true;
    }

    bool TraverseCXXMethodDecl(CXXMethodDecl* CXXMD) {
      // We cannot synthesize when there is a const expr.
      if (!CXXMD->isConstexpr())
        RecursiveASTVisitor::TraverseCXXMethodDecl(CXXMD);
      return true;
    }

  private:
    Expr* SynthesizeCheck(Expr* Arg) {
      assert(Arg && "Cannot call with Arg=0");

      if(!m_clingthrowIfInvalidPointerCache)
        FindAndCacheRuntimeLookupResult();

      SourceLocation Loc = Arg->getBeginLoc();
      Expr* VoidSemaArg = utils::Synthesize::CStyleCastPtrExpr(&m_Sema,
                                                            m_Context.VoidPtrTy,
                                                            (uintptr_t)&m_Interp);
      Expr* VoidExprArg = utils::Synthesize::CStyleCastPtrExpr(&m_Sema,
                                                          m_Context.VoidPtrTy,
                                                          (uintptr_t)Arg);
      Scope* S = m_Sema.getScopeForContext(m_Sema.CurContext);
      CXXScopeSpec CSS;

      Expr* checkCall
        = m_Sema.BuildDeclarationNameExpr(CSS,
                                          *m_clingthrowIfInvalidPointerCache,
                                         /*ADL*/ false).get();
      const clang::FunctionProtoType* checkCallType
        = llvm::dyn_cast<const clang::FunctionProtoType>(
            checkCall->getType().getTypePtr());

      TypeSourceInfo* constVoidPtrTSI = m_Context.getTrivialTypeSourceInfo(
        checkCallType->getParamType(2), Loc);

      // It is unclear whether this is the correct cast if the type
      // is dependent.  Hence, For now, we do not expect SynthesizeCheck to
      // be run on a function template.  It should be run only on function
      // instances.
      // When this is actually insert in a function template, it seems that
      // clang r272382 when instantiating the templates drops one of the part
      // of the implicit cast chain.
      // Namely in:
/*
`-ImplicitCastExpr 0x1010cea90 <col:4> 'const void *' <BitCast>
 `-ImplicitCastExpr 0x1026e0bc0 <col:4> 'const class TAttMarker *'
                    <UncheckedDerivedToBase (TAttMarker)>
  `-ImplicitCastExpr 0x1026e0b48 <col:4> 'class TGraph *' <LValueToRValue>
   `-DeclRefExpr 0x1026e0b20 <col:4> 'class TGraph *' lvalue Var 0x1026e09c0
                   'g5' 'class TGraph *'
*/
      // It drops the 2nd lines (ImplicitCastExpr UncheckedDerivedToBase)
      // clang r227800 seems to actually keep that lines during instantiation.
      Expr* voidPtrArg
        = m_Sema.BuildCStyleCastExpr(Loc, constVoidPtrTSI, Loc, Arg).get();

      Expr *args[] = {VoidSemaArg, VoidExprArg, voidPtrArg};

      if (Expr* call = m_Sema.ActOnCallExpr(S, checkCall,
                                            Loc, args, Loc).get())
      {
        // It is unclear whether this is the correct cast if the type
        // is dependent.  Hence, For now, we do not expect SynthesizeCheck to
        // be run on a function template.  It should be run only on function
        // instances.
        clang::TypeSourceInfo* argTSI = m_Context.getTrivialTypeSourceInfo(
                                          Arg->getType(), Loc);
        Expr* castExpr = m_Sema.BuildCStyleCastExpr(Loc, argTSI,
                                                    Loc, call).get();
        return castExpr;
      }
      return voidPtrArg;
    }

    bool isDeclCandidate(FunctionDecl * FDecl) {
      if (m_NonNullArgIndexs.count(FDecl))
        return true;

      if (llvm::isa<CXXRecordDecl>(FDecl))
        return true;

      std::bitset<32> ArgIndexs;
      for (specific_attr_iterator<NonNullAttr>
             I = FDecl->specific_attr_begin<NonNullAttr>(),
             E = FDecl->specific_attr_end<NonNullAttr>(); I != E; ++I) {

        NonNullAttr *NonNull = *I;
        for (const auto &Idx : NonNull->args()) {
          ArgIndexs.set(Idx.getASTIndex());
        }
      }

      if (ArgIndexs.any()) {
        m_NonNullArgIndexs.insert(std::make_pair(FDecl, ArgIndexs));
        return true;
      }
      return false;
    }

    void FindAndCacheRuntimeLookupResult() {
      assert(!m_clingthrowIfInvalidPointerCache && "Called multiple times!?");

      DeclarationName Name
        = &m_Context.Idents.get("cling_runtime_internal_throwIfInvalidPointer");
      SourceLocation noLoc;
      m_clingthrowIfInvalidPointerCache = new LookupResult(m_Sema, Name, noLoc,
                                        Sema::LookupOrdinaryName,
                                        Sema::ForVisibleRedeclaration);
      m_Sema.LookupQualifiedName(*m_clingthrowIfInvalidPointerCache,
                                 m_Context.getTranslationUnitDecl());
      assert(!m_clingthrowIfInvalidPointerCache->empty() &&
              "Lookup of cling_runtime_internal_throwIfInvalidPointer failed!");
    }
  };

  static bool hasPtrCheckDisabledInContext(const Decl* D) {
    if (isa<TranslationUnitDecl>(D))
      return false;
    for (const auto *Ann : D->specific_attrs<AnnotateAttr>()) {
      if (Ann->getAnnotation() == "__cling__ptrcheck(off)")
        return true;
      else if (Ann->getAnnotation() == "__cling__ptrcheck(on)")
        return false;
    }
    const Decl* Parent = nullptr;
    for (auto DC = D->getDeclContext(); !Parent; DC = DC->getParent())
      Parent = dyn_cast<Decl>(DC);

    assert(Parent && "Decl without context!");

    return hasPtrCheckDisabledInContext(Parent);
  }

} // unnamed namespace

namespace cling {
  NullDerefProtectionTransformer::NullDerefProtectionTransformer(Interpreter* I)
    : ASTTransformer(&I->getCI()->getSema()), m_Interp(I) {
  }

  NullDerefProtectionTransformer::~NullDerefProtectionTransformer()
  { }

  bool NullDerefProtectionTransformer::shouldTransform(const clang::Decl* D) {
    if (D->isFromASTFile())
      return false;
    if (D->isTemplateDecl())
      return false;

    if (hasPtrCheckDisabledInContext(D))
      return false;

    auto Loc = D->getLocation();
    if (Loc.isInvalid())
      return false;

    SourceManager& SM = m_Interp->getSema().getSourceManager();
    auto Characteristic = SM.getFileCharacteristic(Loc);
    if (Characteristic != clang::SrcMgr::C_User)
      return false;

    auto FID = SM.getFileID(Loc);
    if (FID.isInvalid())
      return false;

    auto FE = SM.getFileEntryForID(FID);
    if (!FE)
      return false;

    auto Dir = FE->getDir();
    if (!Dir)
      return false;

    auto IterAndInserted = m_ShouldVisitDir.try_emplace(Dir, true);
    if (IterAndInserted.second == false)
      return IterAndInserted.first->second;

    if (llvm::sys::fs::can_write(Dir->getName()))
      return true; // `true` is already emplaced above.

    // Remember that this dir is not writable and should not be visited.
    IterAndInserted.first->second = false;
    return false;
  }

  ASTTransformer::Result
  NullDerefProtectionTransformer::Transform(clang::Decl* D) {
    if (getCompilationOpts().CheckPointerValidity && shouldTransform(D)) {
      PointerCheckInjector injector(*m_Interp);
      injector.TraverseDecl(D);
    }
    return Result(D, true);
  }
} // end namespace cling
<|endoftext|>


<commit_before>#ifndef HTTP_CLIENT_HH
#define HTTP_CLIENT_HH

#include "http/http_message.hh"
#include "shared_pool.hh"
#include "buffer.hh"

namespace ten {

class http_client {
private:
    std::shared_ptr<netsock> s;
    buffer buf;

    void ensure_connection() {
        if (s && s->valid()) return;
        s.reset(new netsock(AF_INET, SOCK_STREAM));
        if (s->dial(host.c_str(), port) != 0) {
            throw errorx("dial %s:%d failed", host.c_str(), port);
        }
    }

public:
    const std::string host;
    const uint16_t port;
    size_t max_content_length;

    http_client(const std::string &host_, uint16_t port_=80)
        : buf(4*1024), host(host_), port(port_), max_content_length(-1) {}

    http_response perform(const std::string &method, const std::string &path, const std::string &data="") {
        try {
            ensure_connection();
            uri u;
            u.scheme = "http";
            u.host = host;
            u.port = port;
            u.path = path;
            u.normalize();

            http_request r(method, u.compose(true));
            // HTTP/1.1 requires host header
            r.append("Host", u.host);
            r.append("Content-Length", data.size());

            std::string hdata = r.data();
            ssize_t nw = s->send(hdata.c_str(), hdata.size());
            nw = s->send(data.c_str(), data.size());

            http_parser parser;
            http_response resp;
            resp.parser_init(&parser);

            buf.clear();

            while (!resp.complete) {
                buf.reserve(4*1024);
                ssize_t nr = s->recv(buf.back(), buf.available());
                if (nr <= 0) { throw errorx("http get error"); }
                buf.commit(nr);
                size_t len = buf.size();
                resp.parse(&parser, buf.front(), len);
                buf.remove(len);
                if (resp.body.size() >= max_content_length) {
                    s.reset(); // close this socket, we won't read anymore
                    return resp;
                }
            }
            // should not be any data left over in buf
            CHECK(buf.size() == 0);

            return resp;
        } catch (errorx &e) {
            s.reset();
            throw;
        }
    }

    http_response get(const std::string &path) {
        return perform("GET", path);
    }

    http_response post(const std::string &path, const std::string &data) {
        return perform("POST", path, data);
    }

};

class http_pool : public shared_pool<http_client> {
public:
    http_pool(const std::string &host_, uint16_t port_, ssize_t max_conn)
        : shared_pool<http_client>("http://" + host_,
            std::bind(&http_pool::new_resource, this),
            max_conn
        ),
        host(host_), port(port_) {}

protected:
    std::string host;
    uint16_t port;

    std::shared_ptr<http_client> new_resource() {
        VLOG(3) << "new http_client resource " << host;
        return std::make_shared<http_client>(host, port);
    }
};

} // end namespace ten
#endif // HTTP_CLIENT_HH

<commit_msg>custom error type<commit_after>#ifndef HTTP_CLIENT_HH
#define HTTP_CLIENT_HH

#include "http/http_message.hh"
#include "shared_pool.hh"
#include "buffer.hh"

namespace ten {

class http_error : public errorx {
public:
    http_error(const std::string &msg) : errorx(msg) {}
};

class http_client {
private:
    std::shared_ptr<netsock> s;
    buffer buf;

    void ensure_connection() {
        if (s && s->valid()) return;
        s.reset(new netsock(AF_INET, SOCK_STREAM));
        if (s->dial(host.c_str(), port) != 0) {
            throw http_error("dial");
        }
    }

public:
    const std::string host;
    const uint16_t port;
    size_t max_content_length;

    http_client(const std::string &host_, uint16_t port_=80)
        : buf(4*1024), host(host_), port(port_), max_content_length(-1) {}

    http_response perform(const std::string &method, const std::string &path, const std::string &data="") {
        try {
            ensure_connection();
            uri u;
            u.scheme = "http";
            u.host = host;
            u.port = port;
            u.path = path;
            u.normalize();

            http_request r(method, u.compose(true));
            // HTTP/1.1 requires host header
            r.append("Host", u.host);
            r.append("Content-Length", data.size());

            std::string hdata = r.data();
            ssize_t nw = s->send(hdata.c_str(), hdata.size());
            nw = s->send(data.c_str(), data.size());

            http_parser parser;
            http_response resp;
            resp.parser_init(&parser);

            buf.clear();

            while (!resp.complete) {
                buf.reserve(4*1024);
                ssize_t nr = s->recv(buf.back(), buf.available());
                if (nr <= 0) { throw http_error("recv"); }
                buf.commit(nr);
                size_t len = buf.size();
                resp.parse(&parser, buf.front(), len);
                buf.remove(len);
                if (resp.body.size() >= max_content_length) {
                    s.reset(); // close this socket, we won't read anymore
                    return resp;
                }
            }
            // should not be any data left over in buf
            CHECK(buf.size() == 0);

            return resp;
        } catch (errorx &e) {
            s.reset();
            throw;
        }
    }

    http_response get(const std::string &path) {
        return perform("GET", path);
    }

    http_response post(const std::string &path, const std::string &data) {
        return perform("POST", path, data);
    }

};

class http_pool : public shared_pool<http_client> {
public:
    http_pool(const std::string &host_, uint16_t port_, ssize_t max_conn)
        : shared_pool<http_client>("http://" + host_,
            std::bind(&http_pool::new_resource, this),
            max_conn
        ),
        host(host_), port(port_) {}

protected:
    std::string host;
    uint16_t port;

    std::shared_ptr<http_client> new_resource() {
        VLOG(3) << "new http_client resource " << host;
        return std::make_shared<http_client>(host, port);
    }
};

} // end namespace ten
#endif // HTTP_CLIENT_HH

<|endoftext|>


<commit_before>/******************************************************************************
 * $Id$
 *
 * Project:  libLAS - http://liblas.org - A BSD library for LAS format data.
 * Purpose:  LAS Dimension implementation for C++ libLAS
 * Author:   Howard Butler, hobu.inc@gmail.com
 *
 ******************************************************************************
 * Copyright (c) 2010, Howard Butler
 *
 * 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 the Martin Isenburg or Iowa Department
 *       of Natural Resources 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 <pdal/Dimension.hpp>

#include <pdal/GlobalEnvironment.hpp>

#include <boost/algorithm/string.hpp>
#include <boost/uuid/string_generator.hpp>
#include <boost/uuid/random_generator.hpp>
#include <boost/uuid/uuid_io.hpp>
#include <map>

#include <time.h>
#include <cstdlib>

namespace pdal
{

Dimension::Dimension(std::string const& name,
                     dimension::Interpretation interpretation,
                     dimension::size_type sizeInBytes,
                     std::string description)
    : m_name(name)
    , m_flags(0)
    , m_endian(pdal::Endian_Little)
    , m_byteSize(sizeInBytes)
    , m_description(description)
    , m_min(0.0)
    , m_max(0.0)
    , m_numericScale(1.0)
    , m_numericOffset(0.0)
    , m_byteOffset(0)
    , m_position(-1)
    , m_interpretation(interpretation)
    , m_uuid(boost::uuids::nil_uuid())
    , m_namespace(std::string(""))
    , m_parentDimensionID(boost::uuids::nil_uuid())

{

    if (!m_name.size())
    {
        // Generate a random name from the time
        ::time_t seconds;
        ::time(&seconds);

        std::ostringstream oss;
        srand(static_cast<unsigned int>(seconds));
        oss << "unnamed" << rand();
        m_name = oss.str();

    }

}

/// copy constructor
Dimension::Dimension(Dimension const& other)
    : m_name(other.m_name)
    , m_flags(other.m_flags)
    , m_endian(other.m_endian)
    , m_byteSize(other.m_byteSize)
    , m_description(other.m_description)
    , m_min(other.m_min)
    , m_max(other.m_max)
    , m_numericScale(other.m_numericScale)
    , m_numericOffset(other.m_numericOffset)
    , m_byteOffset(other.m_byteOffset)
    , m_position(other.m_position)
    , m_interpretation(other.m_interpretation)
    , m_uuid(other.m_uuid)
    , m_namespace(other.m_namespace)
    , m_parentDimensionID(other.m_parentDimensionID)
{
    return;
}

/// assignment operator
Dimension& Dimension::operator=(Dimension const& rhs)
{
    if (&rhs != this)
    {
        m_name = rhs.m_name;
        m_flags = rhs.m_flags;
        m_endian = rhs.m_endian;
        m_byteSize = rhs.m_byteSize;
        m_description = rhs.m_description;
        m_min = rhs.m_min;
        m_max = rhs.m_max;
        m_numericScale = rhs.m_numericScale;
        m_numericOffset = rhs.m_numericOffset;
        m_byteOffset = rhs.m_byteOffset;
        m_position = rhs.m_position;
        m_interpretation = rhs.m_interpretation;
        m_uuid = rhs.m_uuid;
        m_namespace = rhs.m_namespace;
        m_parentDimensionID = rhs.m_parentDimensionID;
    }

    return *this;
}

bool Dimension::operator==(const Dimension& other) const
{
    if (boost::iequals(m_name, other.m_name) &&
            m_flags == other.m_flags &&
            m_endian == other.m_endian &&
            m_byteSize == other.m_byteSize &&
            boost::iequals(m_description, other.m_description) &&
            Utils::compare_approx(m_min, other.m_min, (std::numeric_limits<double>::min)()) &&
            Utils::compare_approx(m_max, other.m_max, (std::numeric_limits<double>::min)()) &&
            Utils::compare_approx(m_numericScale, other.m_numericScale, (std::numeric_limits<double>::min)()) &&
            Utils::compare_approx(m_numericOffset, other.m_numericOffset, (std::numeric_limits<double>::min)()) &&
            m_byteOffset == other.m_byteOffset &&
            m_position == other.m_position &&
            m_interpretation == other.m_interpretation &&
            m_uuid == other.m_uuid &&
            m_parentDimensionID == other.m_parentDimensionID
       )
    {
        return true;
    }

    return false;
}

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

boost::property_tree::ptree Dimension::toPTree() const
{
    using boost::property_tree::ptree;
    ptree dim;
    dim.put("name", getName());
    dim.put("namespace", getNamespace());
    dim.put("parent", getParent());
    dim.put("description", getDescription());
    dim.put("bytesize", getByteSize());

    std::string e("little");
    if (getEndianness() == Endian_Big)
        e = std::string("big");
    dim.put("endianness", e);

    dim.put("minimum", getMinimum());
    dim.put("maximum", getMaximum());
    dim.put("scale", getNumericScale());
    dim.put("offset", getNumericOffset());

    dim.put("scale", getNumericScale());

    dim.put("position", getPosition());
    dim.put("byteoffset", getByteOffset());
    dim.put("isIgnored", isIgnored());

    std::stringstream oss;

    dimension::id t =  getUUID();
    oss << t;

    dim.put("uuid", oss.str());
    return dim;
}

void Dimension::setUUID(std::string const& id)
{
    boost::uuids::string_generator gen;
    m_uuid = gen(id);
}

void Dimension::createUUID()
{
    // Global RNG
    GlobalEnvironment& env = pdal::GlobalEnvironment::get();
    boost::uuids::basic_random_generator<boost::mt19937> gen(env.getRNG());
    m_uuid = gen();

    // Stack-allocated, uninitialized RNG
    // boost::mt19937 ran;
    // boost::uuids::basic_random_generator<boost::mt19937> gen(&ran);
    // m_uuid = gen();

    // Single call, uninitialized RNG
    // m_uuid = boost::uuids::random_generator()();
}

void Dimension::dump() const
{
    std::cout << *this;
}

std::string Dimension::getInterpretationName() const
{
    std::ostringstream type;
    dimension::Interpretation t = getInterpretation();
    boost::uint32_t bytesize = getByteSize();

    switch (t)
    {
        case dimension::SignedByte:
            if (bytesize == 1)
                type << "int8_t";
            break;
        case dimension::UnsignedByte:
            if (bytesize == 1)
                type << "uint8_t";
            break;

        case dimension::SignedInteger:
            if (bytesize == 1)
                type << "int8_t";
            else if (bytesize == 2)
                type << "int16_t";
            else if (bytesize == 4)
                type << "int32_t";
            else if (bytesize == 8)
                type << "int64_t";
            else
                type << "unknown";
            break;
        case dimension::UnsignedInteger:
            if (bytesize == 1)
                type << "uint8_t";
            else if (bytesize == 2)
                type << "uint16_t";
            else if (bytesize == 4)
                type << "uint32_t";
            else if (bytesize == 8)
                type << "uint64_t";
            else
                type << "unknown";
            break;

        case dimension::Float:
            if (bytesize == 4)
                type << "float";
            else if (bytesize == 8)
                type << "double";
            else
                type << "unknown";
            break;

        case dimension::Pointer:
            type << "pointer";
            break;
        case dimension::Undefined:
            type << "unknown";
            break;
    }

    return type.str();
}

dimension::Interpretation Dimension::getInterpretation(std::string const& interpretation)
{

    if (boost::iequals(interpretation, "int8_t") ||
            boost::iequals(interpretation, "int8"))
        return dimension::SignedInteger;

    if (boost::iequals(interpretation, "uint8_t") ||
            boost::iequals(interpretation, "uint8"))
        return dimension::UnsignedInteger;

    if (boost::iequals(interpretation, "int16_t") ||
            boost::iequals(interpretation, "int16"))
        return dimension::SignedInteger;

    if (boost::iequals(interpretation, "uint16_t") ||
            boost::iequals(interpretation, "uint16"))
        return dimension::UnsignedInteger;

    if (boost::iequals(interpretation, "int32_t") ||
            boost::iequals(interpretation, "int32"))
        return dimension::SignedInteger;

    if (boost::iequals(interpretation, "uint32_t") ||
            boost::iequals(interpretation, "uint32"))
        return dimension::UnsignedInteger;

    if (boost::iequals(interpretation, "int64_t") ||
            boost::iequals(interpretation, "int64"))
        return dimension::SignedInteger;

    if (boost::iequals(interpretation, "uint64_t") ||
            boost::iequals(interpretation, "uint64"))
        return dimension::UnsignedInteger;

    if (boost::iequals(interpretation, "float"))
        return dimension::Float;

    if (boost::iequals(interpretation, "double"))
        return dimension::Float;

    return dimension::Undefined;
}

std::ostream& operator<<(std::ostream& os, pdal::Dimension const& d)
{
    using boost::property_tree::ptree;
    ptree tree = d.toPTree();

    std::string const name = tree.get<std::string>("name");
    std::string const ns = tree.get<std::string>("namespace");

    std::ostringstream quoted_name;
    if (ns.size())
        quoted_name << "'" << ns << "." << name << "'";
    else
        quoted_name << "'" << name << "'";
    std::ostringstream pad;
    std::string const& cur = quoted_name.str();
    std::string::size_type size = cur.size();
    std::string::size_type buffer(24);
    std::string::size_type pad_size = buffer - size;
    if (size > buffer)
        pad_size = 4;
    for (std::string::size_type i=0; i != pad_size; i++)
    {
        pad << " ";
    }
    os << quoted_name.str() << pad.str() <<" -- "<< " size: " << tree.get<boost::uint32_t>("bytesize");

    double scale = tree.get<double>("scale");
    boost::uint32_t precision = Utils::getStreamPrecision(scale);
    os.setf(std::ios_base::fixed, std::ios_base::floatfield);
    os.precision(precision);
    os << " scale: " << scale;

    double offset = tree.get<double>("offset");
    os << " offset: " << offset;

    os << " ignored: " << tree.get<bool>("isIgnored");

    os << " uid: " << tree.get<std::string>("uuid");
    os << " parent: " << tree.get<std::string>("parent");

    os << std::endl;

    return os;
}

} // namespace pdal
<commit_msg>flip back to old RNG initialization<commit_after>/******************************************************************************
 * $Id$
 *
 * Project:  libLAS - http://liblas.org - A BSD library for LAS format data.
 * Purpose:  LAS Dimension implementation for C++ libLAS
 * Author:   Howard Butler, hobu.inc@gmail.com
 *
 ******************************************************************************
 * Copyright (c) 2010, Howard Butler
 *
 * 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 the Martin Isenburg or Iowa Department
 *       of Natural Resources 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 <pdal/Dimension.hpp>

#include <pdal/GlobalEnvironment.hpp>

#include <boost/algorithm/string.hpp>
#include <boost/uuid/string_generator.hpp>
#include <boost/uuid/random_generator.hpp>
#include <boost/uuid/uuid_io.hpp>
#include <map>

#include <time.h>
#include <cstdlib>

namespace pdal
{

Dimension::Dimension(std::string const& name,
                     dimension::Interpretation interpretation,
                     dimension::size_type sizeInBytes,
                     std::string description)
    : m_name(name)
    , m_flags(0)
    , m_endian(pdal::Endian_Little)
    , m_byteSize(sizeInBytes)
    , m_description(description)
    , m_min(0.0)
    , m_max(0.0)
    , m_numericScale(1.0)
    , m_numericOffset(0.0)
    , m_byteOffset(0)
    , m_position(-1)
    , m_interpretation(interpretation)
    , m_uuid(boost::uuids::nil_uuid())
    , m_namespace(std::string(""))
    , m_parentDimensionID(boost::uuids::nil_uuid())

{

    if (!m_name.size())
    {
        // Generate a random name from the time
        ::time_t seconds;
        ::time(&seconds);

        std::ostringstream oss;
        srand(static_cast<unsigned int>(seconds));
        oss << "unnamed" << rand();
        m_name = oss.str();

    }

}

/// copy constructor
Dimension::Dimension(Dimension const& other)
    : m_name(other.m_name)
    , m_flags(other.m_flags)
    , m_endian(other.m_endian)
    , m_byteSize(other.m_byteSize)
    , m_description(other.m_description)
    , m_min(other.m_min)
    , m_max(other.m_max)
    , m_numericScale(other.m_numericScale)
    , m_numericOffset(other.m_numericOffset)
    , m_byteOffset(other.m_byteOffset)
    , m_position(other.m_position)
    , m_interpretation(other.m_interpretation)
    , m_uuid(other.m_uuid)
    , m_namespace(other.m_namespace)
    , m_parentDimensionID(other.m_parentDimensionID)
{
    return;
}

/// assignment operator
Dimension& Dimension::operator=(Dimension const& rhs)
{
    if (&rhs != this)
    {
        m_name = rhs.m_name;
        m_flags = rhs.m_flags;
        m_endian = rhs.m_endian;
        m_byteSize = rhs.m_byteSize;
        m_description = rhs.m_description;
        m_min = rhs.m_min;
        m_max = rhs.m_max;
        m_numericScale = rhs.m_numericScale;
        m_numericOffset = rhs.m_numericOffset;
        m_byteOffset = rhs.m_byteOffset;
        m_position = rhs.m_position;
        m_interpretation = rhs.m_interpretation;
        m_uuid = rhs.m_uuid;
        m_namespace = rhs.m_namespace;
        m_parentDimensionID = rhs.m_parentDimensionID;
    }

    return *this;
}

bool Dimension::operator==(const Dimension& other) const
{
    if (boost::iequals(m_name, other.m_name) &&
            m_flags == other.m_flags &&
            m_endian == other.m_endian &&
            m_byteSize == other.m_byteSize &&
            boost::iequals(m_description, other.m_description) &&
            Utils::compare_approx(m_min, other.m_min, (std::numeric_limits<double>::min)()) &&
            Utils::compare_approx(m_max, other.m_max, (std::numeric_limits<double>::min)()) &&
            Utils::compare_approx(m_numericScale, other.m_numericScale, (std::numeric_limits<double>::min)()) &&
            Utils::compare_approx(m_numericOffset, other.m_numericOffset, (std::numeric_limits<double>::min)()) &&
            m_byteOffset == other.m_byteOffset &&
            m_position == other.m_position &&
            m_interpretation == other.m_interpretation &&
            m_uuid == other.m_uuid &&
            m_parentDimensionID == other.m_parentDimensionID
       )
    {
        return true;
    }

    return false;
}

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

boost::property_tree::ptree Dimension::toPTree() const
{
    using boost::property_tree::ptree;
    ptree dim;
    dim.put("name", getName());
    dim.put("namespace", getNamespace());
    dim.put("parent", getParent());
    dim.put("description", getDescription());
    dim.put("bytesize", getByteSize());

    std::string e("little");
    if (getEndianness() == Endian_Big)
        e = std::string("big");
    dim.put("endianness", e);

    dim.put("minimum", getMinimum());
    dim.put("maximum", getMaximum());
    dim.put("scale", getNumericScale());
    dim.put("offset", getNumericOffset());

    dim.put("scale", getNumericScale());

    dim.put("position", getPosition());
    dim.put("byteoffset", getByteOffset());
    dim.put("isIgnored", isIgnored());

    std::stringstream oss;

    dimension::id t =  getUUID();
    oss << t;

    dim.put("uuid", oss.str());
    return dim;
}

void Dimension::setUUID(std::string const& id)
{
    boost::uuids::string_generator gen;
    m_uuid = gen(id);
}

void Dimension::createUUID()
{
    // Global RNG
    // GlobalEnvironment& env = pdal::GlobalEnvironment::get();
    // boost::uuids::basic_random_generator<boost::mt19937> gen(env.getRNG());
    // m_uuid = gen();

    // Stack-allocated, uninitialized RNG
    // boost::mt19937 ran;
    // boost::uuids::basic_random_generator<boost::mt19937> gen(&ran);
    // m_uuid = gen();

    // Single call, uninitialized RNG
    m_uuid = boost::uuids::random_generator()();
}

void Dimension::dump() const
{
    std::cout << *this;
}

std::string Dimension::getInterpretationName() const
{
    std::ostringstream type;
    dimension::Interpretation t = getInterpretation();
    boost::uint32_t bytesize = getByteSize();

    switch (t)
    {
        case dimension::SignedByte:
            if (bytesize == 1)
                type << "int8_t";
            break;
        case dimension::UnsignedByte:
            if (bytesize == 1)
                type << "uint8_t";
            break;

        case dimension::SignedInteger:
            if (bytesize == 1)
                type << "int8_t";
            else if (bytesize == 2)
                type << "int16_t";
            else if (bytesize == 4)
                type << "int32_t";
            else if (bytesize == 8)
                type << "int64_t";
            else
                type << "unknown";
            break;
        case dimension::UnsignedInteger:
            if (bytesize == 1)
                type << "uint8_t";
            else if (bytesize == 2)
                type << "uint16_t";
            else if (bytesize == 4)
                type << "uint32_t";
            else if (bytesize == 8)
                type << "uint64_t";
            else
                type << "unknown";
            break;

        case dimension::Float:
            if (bytesize == 4)
                type << "float";
            else if (bytesize == 8)
                type << "double";
            else
                type << "unknown";
            break;

        case dimension::Pointer:
            type << "pointer";
            break;
        case dimension::Undefined:
            type << "unknown";
            break;
    }

    return type.str();
}

dimension::Interpretation Dimension::getInterpretation(std::string const& interpretation)
{

    if (boost::iequals(interpretation, "int8_t") ||
            boost::iequals(interpretation, "int8"))
        return dimension::SignedInteger;

    if (boost::iequals(interpretation, "uint8_t") ||
            boost::iequals(interpretation, "uint8"))
        return dimension::UnsignedInteger;

    if (boost::iequals(interpretation, "int16_t") ||
            boost::iequals(interpretation, "int16"))
        return dimension::SignedInteger;

    if (boost::iequals(interpretation, "uint16_t") ||
            boost::iequals(interpretation, "uint16"))
        return dimension::UnsignedInteger;

    if (boost::iequals(interpretation, "int32_t") ||
            boost::iequals(interpretation, "int32"))
        return dimension::SignedInteger;

    if (boost::iequals(interpretation, "uint32_t") ||
            boost::iequals(interpretation, "uint32"))
        return dimension::UnsignedInteger;

    if (boost::iequals(interpretation, "int64_t") ||
            boost::iequals(interpretation, "int64"))
        return dimension::SignedInteger;

    if (boost::iequals(interpretation, "uint64_t") ||
            boost::iequals(interpretation, "uint64"))
        return dimension::UnsignedInteger;

    if (boost::iequals(interpretation, "float"))
        return dimension::Float;

    if (boost::iequals(interpretation, "double"))
        return dimension::Float;

    return dimension::Undefined;
}

std::ostream& operator<<(std::ostream& os, pdal::Dimension const& d)
{
    using boost::property_tree::ptree;
    ptree tree = d.toPTree();

    std::string const name = tree.get<std::string>("name");
    std::string const ns = tree.get<std::string>("namespace");

    std::ostringstream quoted_name;
    if (ns.size())
        quoted_name << "'" << ns << "." << name << "'";
    else
        quoted_name << "'" << name << "'";
    std::ostringstream pad;
    std::string const& cur = quoted_name.str();
    std::string::size_type size = cur.size();
    std::string::size_type buffer(24);
    std::string::size_type pad_size = buffer - size;
    if (size > buffer)
        pad_size = 4;
    for (std::string::size_type i=0; i != pad_size; i++)
    {
        pad << " ";
    }
    os << quoted_name.str() << pad.str() <<" -- "<< " size: " << tree.get<boost::uint32_t>("bytesize");

    double scale = tree.get<double>("scale");
    boost::uint32_t precision = Utils::getStreamPrecision(scale);
    os.setf(std::ios_base::fixed, std::ios_base::floatfield);
    os.precision(precision);
    os << " scale: " << scale;

    double offset = tree.get<double>("offset");
    os << " offset: " << offset;

    os << " ignored: " << tree.get<bool>("isIgnored");

    os << " uid: " << tree.get<std::string>("uuid");
    os << " parent: " << tree.get<std::string>("parent");

    os << std::endl;

    return os;
}

} // namespace pdal
<|endoftext|>


<commit_before>/* vim: set sw=4 sts=4 et foldmethod=syntax : */

#include <max_clique/tbmcsabin_max_clique.hh>
#include <max_clique/colourise.hh>
#include <max_clique/print_incumbent.hh>
#include <threads/atomic_incumbent.hh>
#include <threads/queue.hh>
#include <graph/degree_sort.hh>

#include <algorithm>
#include <list>
#include <functional>
#include <vector>
#include <thread>

using namespace parasols;

namespace
{
    template <unsigned size_>
    struct QueueItem
    {
        FixedBitSet<size_> c;
        FixedBitSet<size_> p;
        std::array<unsigned, size_ * bits_per_word> p_order;
        std::array<unsigned, size_ * bits_per_word> colours;
        std::vector<int> position;
    };

    /**
     * We've possibly found a new best. Update best_anywhere and our local
     * result, and do any necessary printing.
     */
    template <unsigned size_>
    auto found_possible_new_best(const FixedBitGraph<size_> & graph, const std::vector<int> & o,
            const FixedBitSet<size_> & c, int c_popcount,
            const MaxCliqueParams & params, MaxCliqueResult & result, AtomicIncumbent & best_anywhere,
            const std::vector<int> & position) -> void
    {
        if (best_anywhere.update(c_popcount)) {
            result.size = c_popcount;
            result.members.clear();
            for (int i = 0 ; i < graph.size() ; ++i)
                if (c.test(i))
                    result.members.insert(o[i]);
            print_incumbent(params, result.size, position);
        }
    }

    /**
     * Bound function.
     */
    auto bound(unsigned c_popcount, unsigned cn, const MaxCliqueParams & params, AtomicIncumbent & best_anywhere) -> bool
    {
        unsigned best_anywhere_value = best_anywhere.get();
        return (c_popcount + cn <= best_anywhere_value || best_anywhere_value >= params.stop_after_finding);
    }

    template <unsigned size_>
    auto expand(
            const FixedBitGraph<size_> & graph,
            const std::vector<int> & o,                                  // static vertex ordering
            Queue<QueueItem<size_> > & queue,
            const std::array<unsigned, size_ * bits_per_word> & p_order,
            const std::array<unsigned, size_ * bits_per_word> & colours,
            FixedBitSet<size_> & c,                                      // current candidate clique
            FixedBitSet<size_> & p,                                      // potential additions
            MaxCliqueResult & result,
            const MaxCliqueParams & params,
            AtomicIncumbent & best_anywhere,
            std::vector<int> & position) -> void
    {
        auto c_popcount = c.popcount();

        // for each v in p... (v comes later)
        int n = p.popcount() - 1;

        // bound, timeout or early exit?
        if (bound(c_popcount, colours[n], params, best_anywhere) || params.abort.load())
            return;

        auto v = p_order[n];

        // queue empty? enqueue "not taking v"
        bool enqueued_not_v = false;
        if (n > 5 && queue.want_donations()) {
            enqueued_not_v = true;
            auto alt_p = p;
            alt_p.unset(v);
            auto alt_position = position;
            ++alt_position.back();
            queue.enqueue(QueueItem<size_>{ c, alt_p, p_order, colours, alt_position });
        }

        // consider taking v
        c.set(v);
        ++c_popcount;
        ++position.back();

        // filter p to contain vertices adjacent to v
        FixedBitSet<size_> new_p = p;
        new_p = p;
        graph.intersect_with_row(v, new_p);

        if (new_p.empty()) {
            found_possible_new_best(graph, o, c, c_popcount, params, result, best_anywhere, position);
        }
        else
        {
            std::array<unsigned, size_ * bits_per_word> new_p_order, new_colours;
            colourise<size_>(graph, new_p, new_p_order, new_colours);
            ++result.nodes; // rough consistency...

            position.push_back(0);
            expand<size_>(graph, o, queue, new_p_order, new_colours, c, new_p, result, params, best_anywhere, position);
            position.pop_back();
        }

        // now consider not taking v
        c.unset(v);
        p.unset(v);
        --c_popcount;

        if (n > 0 && ! enqueued_not_v) {
            expand<size_>(graph, o, queue, p_order, colours, c, p, result, params, best_anywhere, position);
        }
    }

    template <unsigned size_>
    auto max_clique(const FixedBitGraph<size_> & graph, const std::vector<int> & o, const MaxCliqueParams & params) -> MaxCliqueResult
    {
        Queue<QueueItem<size_> > queue{ params.n_threads, true }; // work queue

        MaxCliqueResult result; // global result
        std::mutex result_mutex;

        AtomicIncumbent best_anywhere; // global incumbent
        best_anywhere.update(params.initial_bound);

        std::list<std::thread> threads; // populating thread, and workers

        /* initial job */
        {
            FixedBitSet<size_> tc; // local candidate clique
            tc.resize(graph.size());

            FixedBitSet<size_> tp; // local potential additions
            tp.resize(graph.size());
            tp.set_all();

            std::vector<int> position;
            position.push_back(0);

            std::array<unsigned, size_ * bits_per_word> p_order, colours;
            colourise<size_>(graph, tp, p_order, colours);
            queue.enqueue(QueueItem<size_>{ tc, tp, p_order, colours, position });
            queue.initial_producer_done();
        }

        /* workers */
        for (unsigned i = 0 ; i < params.n_threads ; ++i) {
            threads.push_back(std::thread([&, i] {
                        auto start_time = std::chrono::steady_clock::now(); // local start time

                        MaxCliqueResult tr; // local result

                        while (true) {
                            // get some work to do
                            QueueItem<size_> args;
                            if (! queue.dequeue_blocking(args))
                                break;

                            // do some work
                            expand<size_>(graph, o, queue, args.p_order, args.colours, args.c, args.p, tr, params, best_anywhere, args.position);

                            // keep track of top nodes done
                            if (! params.abort.load())
                                ++tr.top_nodes_done;
                        }

                        auto overall_time = std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::steady_clock::now() - start_time);

                        // merge results
                        {
                            std::unique_lock<std::mutex> guard(result_mutex);
                            result.merge(tr);
                            result.times.push_back(overall_time);
                        }
                        }));
        }

        // wait until they're done, and clean up threads
        for (auto & t : threads)
            t.join();

        return result;
    }

    template <unsigned size_>
    auto tbmcsabin(const Graph & graph, const MaxCliqueParams & params) -> MaxCliqueResult
    {
        std::vector<int> o(graph.size()); // vertex ordering
        std::iota(o.begin(), o.end(), 0);
        degree_sort(graph, o, false);

        // re-encode graph as a bit graph
        FixedBitGraph<size_> bit_graph;
        bit_graph.resize(graph.size());

        for (int i = 0 ; i < graph.size() ; ++i)
            for (int j = 0 ; j < graph.size() ; ++j)
                if (graph.adjacent(o[i], o[j]))
                    bit_graph.add_edge(i, j);

        // go!
        return max_clique(bit_graph, o, params);
    }
}

auto parasols::tbmcsabin_max_clique(const Graph & graph, const MaxCliqueParams & params) -> MaxCliqueResult
{
    /* This is pretty horrible: in order to avoid dynamic allocation, select
     * the appropriate specialisation for our graph's size. */
    static_assert(max_graph_words == 256, "Need to update here if max_graph_size is changed.");
    if (graph.size() < bits_per_word)
        return tbmcsabin<1>(graph, params);
    else if (graph.size() < 2 * bits_per_word)
        return tbmcsabin<2>(graph, params);
    else if (graph.size() < 4 * bits_per_word)
        return tbmcsabin<4>(graph, params);
    else if (graph.size() < 8 * bits_per_word)
        return tbmcsabin<8>(graph, params);
    else if (graph.size() < 16 * bits_per_word)
        return tbmcsabin<16>(graph, params);
    else if (graph.size() < 32 * bits_per_word)
        return tbmcsabin<32>(graph, params);
    else if (graph.size() < 64 * bits_per_word)
        return tbmcsabin<64>(graph, params);
    else if (graph.size() < 128 * bits_per_word)
        return tbmcsabin<128>(graph, params);
    else if (graph.size() < 256 * bits_per_word)
        return tbmcsabin<256>(graph, params);
    else
        throw GraphTooBig();
}

<commit_msg>Don't queue things which will be eliminated<commit_after>/* vim: set sw=4 sts=4 et foldmethod=syntax : */

#include <max_clique/tbmcsabin_max_clique.hh>
#include <max_clique/colourise.hh>
#include <max_clique/print_incumbent.hh>
#include <threads/atomic_incumbent.hh>
#include <threads/queue.hh>
#include <graph/degree_sort.hh>

#include <algorithm>
#include <list>
#include <functional>
#include <vector>
#include <thread>

using namespace parasols;

namespace
{
    template <unsigned size_>
    struct QueueItem
    {
        FixedBitSet<size_> c;
        FixedBitSet<size_> p;
        std::array<unsigned, size_ * bits_per_word> p_order;
        std::array<unsigned, size_ * bits_per_word> colours;
        std::vector<int> position;
    };

    /**
     * We've possibly found a new best. Update best_anywhere and our local
     * result, and do any necessary printing.
     */
    template <unsigned size_>
    auto found_possible_new_best(const FixedBitGraph<size_> & graph, const std::vector<int> & o,
            const FixedBitSet<size_> & c, int c_popcount,
            const MaxCliqueParams & params, MaxCliqueResult & result, AtomicIncumbent & best_anywhere,
            const std::vector<int> & position) -> void
    {
        if (best_anywhere.update(c_popcount)) {
            result.size = c_popcount;
            result.members.clear();
            for (int i = 0 ; i < graph.size() ; ++i)
                if (c.test(i))
                    result.members.insert(o[i]);
            print_incumbent(params, result.size, position);
        }
    }

    /**
     * Bound function.
     */
    auto bound(unsigned c_popcount, unsigned cn, const MaxCliqueParams & params, AtomicIncumbent & best_anywhere) -> bool
    {
        unsigned best_anywhere_value = best_anywhere.get();
        return (c_popcount + cn <= best_anywhere_value || best_anywhere_value >= params.stop_after_finding);
    }

    template <unsigned size_>
    auto expand(
            const FixedBitGraph<size_> & graph,
            const std::vector<int> & o,                                  // static vertex ordering
            Queue<QueueItem<size_> > & queue,
            const std::array<unsigned, size_ * bits_per_word> & p_order,
            const std::array<unsigned, size_ * bits_per_word> & colours,
            FixedBitSet<size_> & c,                                      // current candidate clique
            FixedBitSet<size_> & p,                                      // potential additions
            MaxCliqueResult & result,
            const MaxCliqueParams & params,
            AtomicIncumbent & best_anywhere,
            std::vector<int> & position) -> void
    {
        auto c_popcount = c.popcount();

        // for each v in p... (v comes later)
        int n = p.popcount() - 1;

        // bound, timeout or early exit?
        if (bound(c_popcount, colours[n], params, best_anywhere) || params.abort.load())
            return;

        auto v = p_order[n];

        // queue empty? enqueue "not taking v"
        bool enqueued_not_v = false;
        if (n > 5 && queue.want_donations() && ! bound(c_popcount, colours[n - 1], params, best_anywhere)) {
            enqueued_not_v = true;
            auto alt_p = p;
            alt_p.unset(v);
            auto alt_position = position;
            ++alt_position.back();
            queue.enqueue(QueueItem<size_>{ c, alt_p, p_order, colours, alt_position });
        }

        // consider taking v
        c.set(v);
        ++c_popcount;
        ++position.back();

        // filter p to contain vertices adjacent to v
        FixedBitSet<size_> new_p = p;
        new_p = p;
        graph.intersect_with_row(v, new_p);

        if (new_p.empty()) {
            found_possible_new_best(graph, o, c, c_popcount, params, result, best_anywhere, position);
        }
        else
        {
            std::array<unsigned, size_ * bits_per_word> new_p_order, new_colours;
            colourise<size_>(graph, new_p, new_p_order, new_colours);
            ++result.nodes; // rough consistency...

            position.push_back(0);
            expand<size_>(graph, o, queue, new_p_order, new_colours, c, new_p, result, params, best_anywhere, position);
            position.pop_back();
        }

        // now consider not taking v
        c.unset(v);
        p.unset(v);
        --c_popcount;

        if (n > 0 && ! enqueued_not_v) {
            expand<size_>(graph, o, queue, p_order, colours, c, p, result, params, best_anywhere, position);
        }
    }

    template <unsigned size_>
    auto max_clique(const FixedBitGraph<size_> & graph, const std::vector<int> & o, const MaxCliqueParams & params) -> MaxCliqueResult
    {
        Queue<QueueItem<size_> > queue{ params.n_threads, true }; // work queue

        MaxCliqueResult result; // global result
        std::mutex result_mutex;

        AtomicIncumbent best_anywhere; // global incumbent
        best_anywhere.update(params.initial_bound);

        std::list<std::thread> threads; // populating thread, and workers

        /* initial job */
        {
            FixedBitSet<size_> tc; // local candidate clique
            tc.resize(graph.size());

            FixedBitSet<size_> tp; // local potential additions
            tp.resize(graph.size());
            tp.set_all();

            std::vector<int> position;
            position.push_back(0);

            std::array<unsigned, size_ * bits_per_word> p_order, colours;
            colourise<size_>(graph, tp, p_order, colours);
            queue.enqueue(QueueItem<size_>{ tc, tp, p_order, colours, position });
            queue.initial_producer_done();
        }

        /* workers */
        for (unsigned i = 0 ; i < params.n_threads ; ++i) {
            threads.push_back(std::thread([&, i] {
                        auto start_time = std::chrono::steady_clock::now(); // local start time

                        MaxCliqueResult tr; // local result

                        while (true) {
                            // get some work to do
                            QueueItem<size_> args;
                            if (! queue.dequeue_blocking(args))
                                break;

                            // do some work
                            expand<size_>(graph, o, queue, args.p_order, args.colours, args.c, args.p, tr, params, best_anywhere, args.position);

                            // keep track of top nodes done
                            if (! params.abort.load())
                                ++tr.top_nodes_done;
                        }

                        auto overall_time = std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::steady_clock::now() - start_time);

                        // merge results
                        {
                            std::unique_lock<std::mutex> guard(result_mutex);
                            result.merge(tr);
                            result.times.push_back(overall_time);
                        }
                        }));
        }

        // wait until they're done, and clean up threads
        for (auto & t : threads)
            t.join();

        return result;
    }

    template <unsigned size_>
    auto tbmcsabin(const Graph & graph, const MaxCliqueParams & params) -> MaxCliqueResult
    {
        std::vector<int> o(graph.size()); // vertex ordering
        std::iota(o.begin(), o.end(), 0);
        degree_sort(graph, o, false);

        // re-encode graph as a bit graph
        FixedBitGraph<size_> bit_graph;
        bit_graph.resize(graph.size());

        for (int i = 0 ; i < graph.size() ; ++i)
            for (int j = 0 ; j < graph.size() ; ++j)
                if (graph.adjacent(o[i], o[j]))
                    bit_graph.add_edge(i, j);

        // go!
        return max_clique(bit_graph, o, params);
    }
}

auto parasols::tbmcsabin_max_clique(const Graph & graph, const MaxCliqueParams & params) -> MaxCliqueResult
{
    /* This is pretty horrible: in order to avoid dynamic allocation, select
     * the appropriate specialisation for our graph's size. */
    static_assert(max_graph_words == 256, "Need to update here if max_graph_size is changed.");
    if (graph.size() < bits_per_word)
        return tbmcsabin<1>(graph, params);
    else if (graph.size() < 2 * bits_per_word)
        return tbmcsabin<2>(graph, params);
    else if (graph.size() < 4 * bits_per_word)
        return tbmcsabin<4>(graph, params);
    else if (graph.size() < 8 * bits_per_word)
        return tbmcsabin<8>(graph, params);
    else if (graph.size() < 16 * bits_per_word)
        return tbmcsabin<16>(graph, params);
    else if (graph.size() < 32 * bits_per_word)
        return tbmcsabin<32>(graph, params);
    else if (graph.size() < 64 * bits_per_word)
        return tbmcsabin<64>(graph, params);
    else if (graph.size() < 128 * bits_per_word)
        return tbmcsabin<128>(graph, params);
    else if (graph.size() < 256 * bits_per_word)
        return tbmcsabin<256>(graph, params);
    else
        throw GraphTooBig();
}

<|endoftext|>


<commit_before>/* The Next Great Finite Element Library. */
/* Copyright (C) 2003  Benjamin S. Kirk */

/* This library is free software; you can redistribute it and/or */
/* modify it under the terms of the GNU Lesser General Public */
/* License as published by the Free Software Foundation; either */
/* version 2.1 of the License, or (at your option) any later version. */

/* This library is distributed in the hope that it will be useful, */
/* but WITHOUT ANY WARRANTY; without even the implied warranty of */
/* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU */
/* Lesser General Public License for more details. */

/* You should have received a copy of the GNU Lesser General Public */
/* License along with this library; if not, write to the Free Software */
/* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA */

 //
Miscellaneous Example 6 - Meshing with LibMesh's TetGen and Triangle Interfaces

 //
 // LibMesh provides interfaces to both Triangle and TetGen for generating
 // Delaunay triangulations and tetrahedralizations in two and three dimensions
 // (respectively).

// Local header files
#include "elem.h"
#include "face_tri3.h"
#include "mesh.h"
#include "mesh_generation.h"
#include "mesh_tetgen_interface.h"
#include "mesh_triangle_holes.h"
#include "mesh_triangle_interface.h"
#include "node.h"
#include "serial_mesh.h"

// Bring in everything from the libMesh namespace
using namespace libMesh;

// Major functions called by main
void triangulate_domain();
void tetrahedralize_domain();

// Helper routine for tetrahedralize_domain().  Adds the points and elements
// of a convex hull generated by TetGen to the input mesh
void add_cube_convex_hull_to_mesh(MeshBase& mesh, Point lower_limit, Point upper_limit);

// Begin the main program.
int main (int argc, char** argv)
{
  // Initialize libMesh and any dependent libaries, like in example 2.
  LibMeshInit init (argc, argv);

  libmesh_example_assert(2 <= LIBMESH_DIM, "2D support");

  std::cout << "Triangulating an L-shaped domain with holes" << std::endl;

  // 1.) 2D triangulation of L-shaped domain with three holes of different shape
  triangulate_domain();

  libmesh_example_assert(3 <= LIBMESH_DIM, "3D support");

  std::cout << "Tetrahedralizing a prismatic domain with a hole" << std::endl;

  // 2.) 3D tetrahedralization of rectangular domain with hole.
  tetrahedralize_domain();

  return 0;
}

void triangulate_domain()
{
#ifdef LIBMESH_HAVE_TRIANGLE
  // Use typedefs for slightly less typing.
  typedef TriangleInterface::Hole Hole;
  typedef TriangleInterface::PolygonHole PolygonHole;
  typedef TriangleInterface::ArbitraryHole ArbitraryHole;

  // Libmesh mesh that will eventually be created.
  Mesh mesh(2);

  // The points which make up the L-shape:
  mesh.add_point(Point( 0. ,  0.));
  mesh.add_point(Point( 0. , -1.));
  mesh.add_point(Point(-1. , -1.));
  mesh.add_point(Point(-1. ,  1.));
  mesh.add_point(Point( 1. ,  1.));
  mesh.add_point(Point( 1. ,  0.));

  // Declare the TriangleInterface object.  This is where
  // we can set parameters of the triangulation and where the
  // actual triangulate function lives.
  TriangleInterface t(mesh);

  // Customize the variables for the triangulation
  t.desired_area()       = .01;

  // A Planar Straight Line Graph (PSLG) is essentially a list
  // of segments which have to exist in the final triangulation.
  // For an L-shaped domain, Triangle will compute the convex
  // hull of boundary points if we do not specify the PSLG.
  // The PSLG algorithm is also required for triangulating domains
  // containing holes
  t.triangulation_type() = TriangleInterface::PSLG;

  // Turn on/off Laplacian mesh smoothing after generation.
  // By default this is on.
  t.smooth_after_generating() = true;

  // Define holes...

  // hole_1 is a circle (discretized by 50 points)
  PolygonHole hole_1(Point(-0.5,  0.5), // center
		     0.25,              // radius
		     50);               // n. points

  // hole_2 is itself a triangle
  PolygonHole hole_2(Point(0.5, 0.5),   // center
		     0.1,               // radius
		     3);                // n. points

  // hole_3 is an ellipse of 100 points which we define here
  Point ellipse_center(-0.5,  -0.5);
  const unsigned int n_ellipse_points=100;
  std::vector<Point> ellipse_points(n_ellipse_points);
  const Real
    dtheta = 2*libMesh::pi / static_cast<Real>(n_ellipse_points),
    a = .1,
    b = .2;

  for (unsigned int i=0; i<n_ellipse_points; ++i)
    ellipse_points[i]= Point(ellipse_center(0)+a*cos(i*dtheta),
			     ellipse_center(1)+b*sin(i*dtheta));

  ArbitraryHole hole_3(ellipse_center, ellipse_points);

  // Create the vector of Hole*'s ...
  std::vector<Hole*> holes;
  holes.push_back(&hole_1);
  holes.push_back(&hole_2);
  holes.push_back(&hole_3);

  // ... and attach it to the triangulator object
  t.attach_hole_list(&holes);

  // Triangulate!
  t.triangulate();

  // Write the result to file
  mesh.write("delaunay_l_shaped_hole.e");

#endif // LIBMESH_HAVE_TRIANGLE
}

void tetrahedralize_domain()
{
#ifdef LIBMESH_HAVE_TETGEN
  // The algorithm is broken up into several steps:
  // 1.) A convex hull is constructed for a rectangular hole.
  // 2.) A convex hull is constructed for the domain exterior.
  // 3.) Neighbor information is updated so TetGen knows there is a convex hull
  // 4.) A vector of hole points is created.
  // 5.) The domain is tetrahedralized, the mesh is written out, etc.

  // The mesh we will eventually generate
  SerialMesh mesh(3);

  // Lower and Upper bounding box limits for a rectangular hole within the unit cube.
  Point hole_lower_limit(0.2, 0.2, 0.4);
  Point hole_upper_limit(0.8, 0.8, 0.6);

  // 1.) Construct a convex hull for the hole
  add_cube_convex_hull_to_mesh(mesh, hole_lower_limit, hole_upper_limit);

  // 2.) Generate elements comprising the outer boundary of the domain.
  add_cube_convex_hull_to_mesh(mesh, Point(0.,0.,0.), Point(1., 1., 1.));

  // 3.) Update neighbor information so that TetGen can verify there is a convex hull.
  mesh.find_neighbors();

  // 4.) Set up vector of hole points
  std::vector<Point> hole(1);
  hole[0] = Point( 0.5*(hole_lower_limit + hole_upper_limit) );

  // 5.) Set parameters and tetrahedralize the domain

  // 0 means "use TetGen default value"
  Real quality_constraint = 2.0;

  // The volume constraint determines the max-allowed tetrahedral
  // volume in the Mesh.  TetGen will split cells which are larger than
  // this size
  Real volume_constraint = 0.001;

  // Construct the Delaunay tetrahedralization
  TetGenMeshInterface t(mesh);
  t.triangulate_conformingDelaunayMesh_carvehole(hole,
						 quality_constraint,
						 volume_constraint);

  // Find neighbors, etc in preparation for writing out the Mesh
  mesh.prepare_for_use();

  // Finally, write out the result
  mesh.write("hole_3D.e");
#endif // LIBMESH_HAVE_TETGEN
}

void add_cube_convex_hull_to_mesh(MeshBase& mesh, Point lower_limit, Point upper_limit)
{
#ifdef LIBMESH_HAVE_TETGEN
  Mesh cube_mesh(3);

  unsigned n_elem = 1;

  MeshTools::Generation::build_cube(cube_mesh,
				    n_elem,n_elem,n_elem, // n. elements in each direction
				    lower_limit(0), upper_limit(0),
				    lower_limit(1), upper_limit(1),
				    lower_limit(2), upper_limit(2),
				    HEX8);

  // The pointset_convexhull() algorithm will ignore the Hex8s
  // in the Mesh, and just construct the triangulation
  // of the convex hull.
  TetGenMeshInterface t(cube_mesh);
  t.pointset_convexhull();

  // Now add all nodes from the boundary of the cube_mesh to the input mesh.

  // Map from "node id in cube_mesh" -> "node id in mesh".  Initially inserted
  // with a dummy value, later to be assigned a value by the input mesh.
  std::map<unsigned,unsigned> node_id_map;
  typedef std::map<unsigned,unsigned>::iterator iterator;

  {
    MeshBase::element_iterator it = cube_mesh.elements_begin();
    const MeshBase::element_iterator end = cube_mesh.elements_end();
    for ( ; it != end; ++it)
      {
	Elem* elem = *it;

	for (unsigned s=0; s<elem->n_sides(); ++s)
	  if (elem->neighbor(s) == NULL)
	    {
	      // Add the node IDs of this side to the set
	      AutoPtr<Elem> side = elem->side(s);

	      for (unsigned n=0; n<side->n_nodes(); ++n)
		node_id_map.insert( std::make_pair(side->node(n), /*dummy_value=*/0) );
	    }
      }
  }

  // For each node in the map, insert it into the input mesh and keep
  // track of the ID assigned.
  for (iterator it=node_id_map.begin(); it != node_id_map.end(); ++it)
    {
      // Id of the node in the cube mesh
      unsigned id = (*it).first;

      // Pointer to node in the cube mesh
      Node* old_node = cube_mesh.node_ptr(id);

      // Add geometric point to input mesh
      Node* new_node = mesh.add_point ( *old_node );

      // Track ID value of new_node in map
      (*it).second = new_node->id();
    }

  // With the points added and the map data structure in place, we are
  // ready to add each TRI3 element of the cube_mesh to the input Mesh
  // with proper node assignments
  {
    MeshBase::element_iterator       el     = cube_mesh.elements_begin();
    const MeshBase::element_iterator end_el = cube_mesh.elements_end();

    for (; el != end_el; ++el)
      {
	Elem* old_elem = *el;

	if (old_elem->type() == TRI3)
	  {
	    Elem* new_elem = mesh.add_elem(new Tri3);

	    // Assign nodes in new elements.  Since this is an example,
	    // we'll do it in several steps.
	    for (unsigned i=0; i<old_elem->n_nodes(); ++i)
	      {
		// Locate old node ID in the map
		iterator it = node_id_map.find(old_elem->node(i));

		// Check for not found
		if (it == node_id_map.end())
		  {
		    libMesh::err << "Node id " << old_elem->node(i) << " not found in map!" << std::endl;
		    libmesh_error();
		  }

		// Mapping to node ID in input mesh
		unsigned new_node_id = (*it).second;

		// Node pointer assigned from input mesh
		new_elem->set_node(i) = mesh.node_ptr(new_node_id);
	      }
	  }
      }
  }
#endif // LIBMESH_HAVE_TETGEN
}
<commit_msg>Using SerialMesh with TetGen for now<commit_after>/* The Next Great Finite Element Library. */
/* Copyright (C) 2003  Benjamin S. Kirk */

/* This library is free software; you can redistribute it and/or */
/* modify it under the terms of the GNU Lesser General Public */
/* License as published by the Free Software Foundation; either */
/* version 2.1 of the License, or (at your option) any later version. */

/* This library is distributed in the hope that it will be useful, */
/* but WITHOUT ANY WARRANTY; without even the implied warranty of */
/* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU */
/* Lesser General Public License for more details. */

/* You should have received a copy of the GNU Lesser General Public */
/* License along with this library; if not, write to the Free Software */
/* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA */

 //
Miscellaneous Example 6 - Meshing with LibMesh's TetGen and Triangle Interfaces

 //
 // LibMesh provides interfaces to both Triangle and TetGen for generating
 // Delaunay triangulations and tetrahedralizations in two and three dimensions
 // (respectively).

// Local header files
#include "elem.h"
#include "face_tri3.h"
#include "mesh.h"
#include "mesh_generation.h"
#include "mesh_tetgen_interface.h"
#include "mesh_triangle_holes.h"
#include "mesh_triangle_interface.h"
#include "node.h"
#include "serial_mesh.h"

// Bring in everything from the libMesh namespace
using namespace libMesh;

// Major functions called by main
void triangulate_domain();
void tetrahedralize_domain();

// Helper routine for tetrahedralize_domain().  Adds the points and elements
// of a convex hull generated by TetGen to the input mesh
void add_cube_convex_hull_to_mesh(MeshBase& mesh, Point lower_limit, Point upper_limit);

// Begin the main program.
int main (int argc, char** argv)
{
  // Initialize libMesh and any dependent libaries, like in example 2.
  LibMeshInit init (argc, argv);

  libmesh_example_assert(2 <= LIBMESH_DIM, "2D support");

  std::cout << "Triangulating an L-shaped domain with holes" << std::endl;

  // 1.) 2D triangulation of L-shaped domain with three holes of different shape
  triangulate_domain();

  libmesh_example_assert(3 <= LIBMESH_DIM, "3D support");

  std::cout << "Tetrahedralizing a prismatic domain with a hole" << std::endl;

  // 2.) 3D tetrahedralization of rectangular domain with hole.
  tetrahedralize_domain();

  return 0;
}

void triangulate_domain()
{
#ifdef LIBMESH_HAVE_TRIANGLE
  // Use typedefs for slightly less typing.
  typedef TriangleInterface::Hole Hole;
  typedef TriangleInterface::PolygonHole PolygonHole;
  typedef TriangleInterface::ArbitraryHole ArbitraryHole;

  // Libmesh mesh that will eventually be created.
  Mesh mesh(2);

  // The points which make up the L-shape:
  mesh.add_point(Point( 0. ,  0.));
  mesh.add_point(Point( 0. , -1.));
  mesh.add_point(Point(-1. , -1.));
  mesh.add_point(Point(-1. ,  1.));
  mesh.add_point(Point( 1. ,  1.));
  mesh.add_point(Point( 1. ,  0.));

  // Declare the TriangleInterface object.  This is where
  // we can set parameters of the triangulation and where the
  // actual triangulate function lives.
  TriangleInterface t(mesh);

  // Customize the variables for the triangulation
  t.desired_area()       = .01;

  // A Planar Straight Line Graph (PSLG) is essentially a list
  // of segments which have to exist in the final triangulation.
  // For an L-shaped domain, Triangle will compute the convex
  // hull of boundary points if we do not specify the PSLG.
  // The PSLG algorithm is also required for triangulating domains
  // containing holes
  t.triangulation_type() = TriangleInterface::PSLG;

  // Turn on/off Laplacian mesh smoothing after generation.
  // By default this is on.
  t.smooth_after_generating() = true;

  // Define holes...

  // hole_1 is a circle (discretized by 50 points)
  PolygonHole hole_1(Point(-0.5,  0.5), // center
		     0.25,              // radius
		     50);               // n. points

  // hole_2 is itself a triangle
  PolygonHole hole_2(Point(0.5, 0.5),   // center
		     0.1,               // radius
		     3);                // n. points

  // hole_3 is an ellipse of 100 points which we define here
  Point ellipse_center(-0.5,  -0.5);
  const unsigned int n_ellipse_points=100;
  std::vector<Point> ellipse_points(n_ellipse_points);
  const Real
    dtheta = 2*libMesh::pi / static_cast<Real>(n_ellipse_points),
    a = .1,
    b = .2;

  for (unsigned int i=0; i<n_ellipse_points; ++i)
    ellipse_points[i]= Point(ellipse_center(0)+a*cos(i*dtheta),
			     ellipse_center(1)+b*sin(i*dtheta));

  ArbitraryHole hole_3(ellipse_center, ellipse_points);

  // Create the vector of Hole*'s ...
  std::vector<Hole*> holes;
  holes.push_back(&hole_1);
  holes.push_back(&hole_2);
  holes.push_back(&hole_3);

  // ... and attach it to the triangulator object
  t.attach_hole_list(&holes);

  // Triangulate!
  t.triangulate();

  // Write the result to file
  mesh.write("delaunay_l_shaped_hole.e");

#endif // LIBMESH_HAVE_TRIANGLE
}

void tetrahedralize_domain()
{
#ifdef LIBMESH_HAVE_TETGEN
  // The algorithm is broken up into several steps:
  // 1.) A convex hull is constructed for a rectangular hole.
  // 2.) A convex hull is constructed for the domain exterior.
  // 3.) Neighbor information is updated so TetGen knows there is a convex hull
  // 4.) A vector of hole points is created.
  // 5.) The domain is tetrahedralized, the mesh is written out, etc.

  // The mesh we will eventually generate
  SerialMesh mesh(3);

  // Lower and Upper bounding box limits for a rectangular hole within the unit cube.
  Point hole_lower_limit(0.2, 0.2, 0.4);
  Point hole_upper_limit(0.8, 0.8, 0.6);

  // 1.) Construct a convex hull for the hole
  add_cube_convex_hull_to_mesh(mesh, hole_lower_limit, hole_upper_limit);

  // 2.) Generate elements comprising the outer boundary of the domain.
  add_cube_convex_hull_to_mesh(mesh, Point(0.,0.,0.), Point(1., 1., 1.));

  // 3.) Update neighbor information so that TetGen can verify there is a convex hull.
  mesh.find_neighbors();

  // 4.) Set up vector of hole points
  std::vector<Point> hole(1);
  hole[0] = Point( 0.5*(hole_lower_limit + hole_upper_limit) );

  // 5.) Set parameters and tetrahedralize the domain

  // 0 means "use TetGen default value"
  Real quality_constraint = 2.0;

  // The volume constraint determines the max-allowed tetrahedral
  // volume in the Mesh.  TetGen will split cells which are larger than
  // this size
  Real volume_constraint = 0.001;

  // Construct the Delaunay tetrahedralization
  TetGenMeshInterface t(mesh);
  t.triangulate_conformingDelaunayMesh_carvehole(hole,
						 quality_constraint,
						 volume_constraint);

  // Find neighbors, etc in preparation for writing out the Mesh
  mesh.prepare_for_use();

  // Finally, write out the result
  mesh.write("hole_3D.e");
#endif // LIBMESH_HAVE_TETGEN
}

void add_cube_convex_hull_to_mesh(MeshBase& mesh, Point lower_limit, Point upper_limit)
{
#ifdef LIBMESH_HAVE_TETGEN
  SerialMesh cube_mesh(3);

  unsigned n_elem = 1;

  MeshTools::Generation::build_cube(cube_mesh,
				    n_elem,n_elem,n_elem, // n. elements in each direction
				    lower_limit(0), upper_limit(0),
				    lower_limit(1), upper_limit(1),
				    lower_limit(2), upper_limit(2),
				    HEX8);

  // The pointset_convexhull() algorithm will ignore the Hex8s
  // in the Mesh, and just construct the triangulation
  // of the convex hull.
  TetGenMeshInterface t(cube_mesh);
  t.pointset_convexhull();

  // Now add all nodes from the boundary of the cube_mesh to the input mesh.

  // Map from "node id in cube_mesh" -> "node id in mesh".  Initially inserted
  // with a dummy value, later to be assigned a value by the input mesh.
  std::map<unsigned,unsigned> node_id_map;
  typedef std::map<unsigned,unsigned>::iterator iterator;

  {
    MeshBase::element_iterator it = cube_mesh.elements_begin();
    const MeshBase::element_iterator end = cube_mesh.elements_end();
    for ( ; it != end; ++it)
      {
	Elem* elem = *it;

	for (unsigned s=0; s<elem->n_sides(); ++s)
	  if (elem->neighbor(s) == NULL)
	    {
	      // Add the node IDs of this side to the set
	      AutoPtr<Elem> side = elem->side(s);

	      for (unsigned n=0; n<side->n_nodes(); ++n)
		node_id_map.insert( std::make_pair(side->node(n), /*dummy_value=*/0) );
	    }
      }
  }

  // For each node in the map, insert it into the input mesh and keep
  // track of the ID assigned.
  for (iterator it=node_id_map.begin(); it != node_id_map.end(); ++it)
    {
      // Id of the node in the cube mesh
      unsigned id = (*it).first;

      // Pointer to node in the cube mesh
      Node* old_node = cube_mesh.node_ptr(id);

      // Add geometric point to input mesh
      Node* new_node = mesh.add_point ( *old_node );

      // Track ID value of new_node in map
      (*it).second = new_node->id();
    }

  // With the points added and the map data structure in place, we are
  // ready to add each TRI3 element of the cube_mesh to the input Mesh
  // with proper node assignments
  {
    MeshBase::element_iterator       el     = cube_mesh.elements_begin();
    const MeshBase::element_iterator end_el = cube_mesh.elements_end();

    for (; el != end_el; ++el)
      {
	Elem* old_elem = *el;

	if (old_elem->type() == TRI3)
	  {
	    Elem* new_elem = mesh.add_elem(new Tri3);

	    // Assign nodes in new elements.  Since this is an example,
	    // we'll do it in several steps.
	    for (unsigned i=0; i<old_elem->n_nodes(); ++i)
	      {
		// Locate old node ID in the map
		iterator it = node_id_map.find(old_elem->node(i));

		// Check for not found
		if (it == node_id_map.end())
		  {
		    libMesh::err << "Node id " << old_elem->node(i) << " not found in map!" << std::endl;
		    libmesh_error();
		  }

		// Mapping to node ID in input mesh
		unsigned new_node_id = (*it).second;

		// Node pointer assigned from input mesh
		new_elem->set_node(i) = mesh.node_ptr(new_node_id);
	      }
	  }
      }
  }
#endif // LIBMESH_HAVE_TETGEN
}
<|endoftext|>