<commit_before>//===-- BrainF.cpp - BrainF compiler example ----------------------------===//
//
//                     The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===--------------------------------------------------------------------===//
//
// This class compiles the BrainF language into LLVM assembly.
//
// The BrainF language has 8 commands:
// Command   Equivalent C    Action
// -------   ------------    ------
// ,         *h=getchar();   Read a character from stdin, 255 on EOF
// .         putchar(*h);    Write a character to stdout
// -         --*h;           Decrement tape
// +         ++*h;           Increment tape
// <         --h;            Move head left
// >         ++h;            Move head right
// [         while(*h) {     Start loop
// ]         }               End loop
//
//===--------------------------------------------------------------------===//

#include "BrainF.h"
#include "llvm/Constants.h"
#include "llvm/Instructions.h"
#include "llvm/Intrinsics.h"
#include "llvm/ADT/STLExtras.h"
#include <iostream>
using namespace llvm;

//Set the constants for naming
const char *BrainF::tapereg = "tape";
const char *BrainF::headreg = "head";
const char *BrainF::label   = "brainf";
const char *BrainF::testreg = "test";

Module *BrainF::parse(std::istream *in1, int mem, CompileFlags cf,
                      LLVMContext& Context) {
  in       = in1;
  memtotal = mem;
  comflag  = cf;

  header(Context);
  readloop(0, 0, 0, Context);
  delete builder;
  return module;
}

void BrainF::header(LLVMContext& C) {
  module = new Module("BrainF", C);

  //Function prototypes

  //declare void @llvm.memset.p0i8.i32(i8 *, i8, i32, i32, i1)
  Type *Tys[] = { Type::getInt8PtrTy(C), Type::getInt32Ty(C) };
  Function *memset_func = Intrinsic::getDeclaration(module, Intrinsic::memset,
                                                    Tys);

  //declare i32 @getchar()
  getchar_func = cast<Function>(module->
    getOrInsertFunction("getchar", IntegerType::getInt32Ty(C), NULL));

  //declare i32 @putchar(i32)
  putchar_func = cast<Function>(module->
    getOrInsertFunction("putchar", IntegerType::getInt32Ty(C),
                        IntegerType::getInt32Ty(C), NULL));

  //Function header

  //define void @brainf()
  brainf_func = cast<Function>(module->
    getOrInsertFunction("brainf", Type::getVoidTy(C), NULL));

  builder = new IRBuilder<>(BasicBlock::Create(C, label, brainf_func));

  //%arr = malloc i8, i32 %d
  ConstantInt *val_mem = ConstantInt::get(C, APInt(32, memtotal));
  BasicBlock* BB = builder->GetInsertBlock();
  const Type* IntPtrTy = IntegerType::getInt32Ty(C);
  const Type* Int8Ty = IntegerType::getInt8Ty(C);
  Constant* allocsize = ConstantExpr::getSizeOf(Int8Ty);
  allocsize = ConstantExpr::getTruncOrBitCast(allocsize, IntPtrTy);
  ptr_arr = CallInst::CreateMalloc(BB, IntPtrTy, Int8Ty, allocsize, val_mem,
                                   NULL, "arr");
  BB->getInstList().push_back(cast<Instruction>(ptr_arr));

  //call void @llvm.memset.p0i8.i32(i8 *%arr, i8 0, i32 %d, i32 1, i1 0)
  {
    Value *memset_params[] = {
      ptr_arr,
      ConstantInt::get(C, APInt(8, 0)),
      val_mem,
      ConstantInt::get(C, APInt(32, 1)),
      ConstantInt::get(C, APInt(1, 0))
    };

    CallInst *memset_call = builder->
      CreateCall(memset_func, memset_params);
    memset_call->setTailCall(false);
  }

  //%arrmax = getelementptr i8 *%arr, i32 %d
  if (comflag & flag_arraybounds) {
    ptr_arrmax = builder->
      CreateGEP(ptr_arr, ConstantInt::get(C, APInt(32, memtotal)), "arrmax");
  }

  //%head.%d = getelementptr i8 *%arr, i32 %d
  curhead = builder->CreateGEP(ptr_arr,
                               ConstantInt::get(C, APInt(32, memtotal/2)),
                               headreg);

  //Function footer

  //brainf.end:
  endbb = BasicBlock::Create(C, label, brainf_func);

  //call free(i8 *%arr)
  endbb->getInstList().push_back(CallInst::CreateFree(ptr_arr, endbb));

  //ret void
  ReturnInst::Create(C, endbb);

  //Error block for array out of bounds
  if (comflag & flag_arraybounds)
  {
    //@aberrormsg = internal constant [%d x i8] c"\00"
    Constant *msg_0 =
      ConstantArray::get(C, "Error: The head has left the tape.", true);

    GlobalVariable *aberrormsg = new GlobalVariable(
      *module,
      msg_0->getType(),
      true,
      GlobalValue::InternalLinkage,
      msg_0,
      "aberrormsg");

    //declare i32 @puts(i8 *)
    Function *puts_func = cast<Function>(module->
      getOrInsertFunction("puts", IntegerType::getInt32Ty(C),
                      PointerType::getUnqual(IntegerType::getInt8Ty(C)), NULL));

    //brainf.aberror:
    aberrorbb = BasicBlock::Create(C, label, brainf_func);

    //call i32 @puts(i8 *getelementptr([%d x i8] *@aberrormsg, i32 0, i32 0))
    {
      Constant *zero_32 = Constant::getNullValue(IntegerType::getInt32Ty(C));

      Constant *gep_params[] = {
        zero_32,
        zero_32
      };

      Constant *msgptr = ConstantExpr::
        getGetElementPtr(aberrormsg, gep_params,
                         array_lengthof(gep_params));

      Value *puts_params[] = {
        msgptr
      };

      CallInst *puts_call =
        CallInst::Create(puts_func,
                         puts_params,
                         "", aberrorbb);
      puts_call->setTailCall(false);
    }

    //br label %brainf.end
    BranchInst::Create(endbb, aberrorbb);
  }
}

void BrainF::readloop(PHINode *phi, BasicBlock *oldbb, BasicBlock *testbb,
                      LLVMContext &C) {
  Symbol cursym = SYM_NONE;
  int curvalue = 0;
  Symbol nextsym = SYM_NONE;
  int nextvalue = 0;
  char c;
  int loop;
  int direction;

  while(cursym != SYM_EOF && cursym != SYM_ENDLOOP) {
    // Write out commands
    switch(cursym) {
      case SYM_NONE:
        // Do nothing
        break;

      case SYM_READ:
        {
          //%tape.%d = call i32 @getchar()
          CallInst *getchar_call = builder->CreateCall(getchar_func, tapereg);
          getchar_call->setTailCall(false);
          Value *tape_0 = getchar_call;

          //%tape.%d = trunc i32 %tape.%d to i8
          Value *tape_1 = builder->
            CreateTrunc(tape_0, IntegerType::getInt8Ty(C), tapereg);

          //store i8 %tape.%d, i8 *%head.%d
          builder->CreateStore(tape_1, curhead);
        }
        break;

      case SYM_WRITE:
        {
          //%tape.%d = load i8 *%head.%d
          LoadInst *tape_0 = builder->CreateLoad(curhead, tapereg);

          //%tape.%d = sext i8 %tape.%d to i32
          Value *tape_1 = builder->
            CreateSExt(tape_0, IntegerType::getInt32Ty(C), tapereg);

          //call i32 @putchar(i32 %tape.%d)
          Value *putchar_params[] = {
            tape_1
          };
          CallInst *putchar_call = builder->
            CreateCall(putchar_func,
                       putchar_params);
          putchar_call->setTailCall(false);
        }
        break;

      case SYM_MOVE:
        {
          //%head.%d = getelementptr i8 *%head.%d, i32 %d
          curhead = builder->
            CreateGEP(curhead, ConstantInt::get(C, APInt(32, curvalue)),
                      headreg);

          //Error block for array out of bounds
          if (comflag & flag_arraybounds)
          {
            //%test.%d = icmp uge i8 *%head.%d, %arrmax
            Value *test_0 = builder->
              CreateICmpUGE(curhead, ptr_arrmax, testreg);

            //%test.%d = icmp ult i8 *%head.%d, %arr
            Value *test_1 = builder->
              CreateICmpULT(curhead, ptr_arr, testreg);

            //%test.%d = or i1 %test.%d, %test.%d
            Value *test_2 = builder->
              CreateOr(test_0, test_1, testreg);

            //br i1 %test.%d, label %main.%d, label %main.%d
            BasicBlock *nextbb = BasicBlock::Create(C, label, brainf_func);
            builder->CreateCondBr(test_2, aberrorbb, nextbb);

            //main.%d:
            builder->SetInsertPoint(nextbb);
          }
        }
        break;

      case SYM_CHANGE:
        {
          //%tape.%d = load i8 *%head.%d
          LoadInst *tape_0 = builder->CreateLoad(curhead, tapereg);

          //%tape.%d = add i8 %tape.%d, %d
          Value *tape_1 = builder->
            CreateAdd(tape_0, ConstantInt::get(C, APInt(8, curvalue)), tapereg);

          //store i8 %tape.%d, i8 *%head.%d\n"
          builder->CreateStore(tape_1, curhead);
        }
        break;

      case SYM_LOOP:
        {
          //br label %main.%d
          BasicBlock *testbb = BasicBlock::Create(C, label, brainf_func);
          builder->CreateBr(testbb);

          //main.%d:
          BasicBlock *bb_0 = builder->GetInsertBlock();
          BasicBlock *bb_1 = BasicBlock::Create(C, label, brainf_func);
          builder->SetInsertPoint(bb_1);

          // Make part of PHI instruction now, wait until end of loop to finish
          PHINode *phi_0 =
            PHINode::Create(PointerType::getUnqual(IntegerType::getInt8Ty(C)),
                            2, headreg, testbb);
          phi_0->addIncoming(curhead, bb_0);
          curhead = phi_0;

          readloop(phi_0, bb_1, testbb, C);
        }
        break;

      default:
        std::cerr << "Error: Unknown symbol.\n";
        abort();
        break;
    }

    cursym = nextsym;
    curvalue = nextvalue;
    nextsym = SYM_NONE;

    // Reading stdin loop
    loop = (cursym == SYM_NONE)
        || (cursym == SYM_MOVE)
        || (cursym == SYM_CHANGE);
    while(loop) {
      *in>>c;
      if (in->eof()) {
        if (cursym == SYM_NONE) {
          cursym = SYM_EOF;
        } else {
          nextsym = SYM_EOF;
        }
        loop = 0;
      } else {
        direction = 1;
        switch(c) {
          case '-':
            direction = -1;
            // Fall through

          case '+':
            if (cursym == SYM_CHANGE) {
              curvalue += direction;
              // loop = 1
            } else {
              if (cursym == SYM_NONE) {
                cursym = SYM_CHANGE;
                curvalue = direction;
                // loop = 1
              } else {
                nextsym = SYM_CHANGE;
                nextvalue = direction;
                loop = 0;
              }
            }
            break;

          case '<':
            direction = -1;
            // Fall through

          case '>':
            if (cursym == SYM_MOVE) {
              curvalue += direction;
              // loop = 1
            } else {
              if (cursym == SYM_NONE) {
                cursym = SYM_MOVE;
                curvalue = direction;
                // loop = 1
              } else {
                nextsym = SYM_MOVE;
                nextvalue = direction;
                loop = 0;
              }
            }
            break;

          case ',':
            if (cursym == SYM_NONE) {
              cursym = SYM_READ;
            } else {
              nextsym = SYM_READ;
            }
            loop = 0;
            break;

          case '.':
            if (cursym == SYM_NONE) {
              cursym = SYM_WRITE;
            } else {
              nextsym = SYM_WRITE;
            }
            loop = 0;
            break;

          case '[':
            if (cursym == SYM_NONE) {
              cursym = SYM_LOOP;
            } else {
              nextsym = SYM_LOOP;
            }
            loop = 0;
            break;

          case ']':
            if (cursym == SYM_NONE) {
              cursym = SYM_ENDLOOP;
            } else {
              nextsym = SYM_ENDLOOP;
            }
            loop = 0;
            break;

          // Ignore other characters
          default:
            break;
        }
      }
    }
  }

  if (cursym == SYM_ENDLOOP) {
    if (!phi) {
      std::cerr << "Error: Extra ']'\n";
      abort();
    }

    // Write loop test
    {
      //br label %main.%d
      builder->CreateBr(testbb);

      //main.%d:

      //%head.%d = phi i8 *[%head.%d, %main.%d], [%head.%d, %main.%d]
      //Finish phi made at beginning of loop
      phi->addIncoming(curhead, builder->GetInsertBlock());
      Value *head_0 = phi;

      //%tape.%d = load i8 *%head.%d
      LoadInst *tape_0 = new LoadInst(head_0, tapereg, testbb);

      //%test.%d = icmp eq i8 %tape.%d, 0
      ICmpInst *test_0 = new ICmpInst(*testbb, ICmpInst::ICMP_EQ, tape_0,
                                    ConstantInt::get(C, APInt(8, 0)), testreg);

      //br i1 %test.%d, label %main.%d, label %main.%d
      BasicBlock *bb_0 = BasicBlock::Create(C, label, brainf_func);
      BranchInst::Create(bb_0, oldbb, test_0, testbb);

      //main.%d:
      builder->SetInsertPoint(bb_0);

      //%head.%d = phi i8 *[%head.%d, %main.%d]
      PHINode *phi_1 = builder->
        CreatePHI(PointerType::getUnqual(IntegerType::getInt8Ty(C)), 1,
                  headreg);
      phi_1->addIncoming(head_0, testbb);
      curhead = phi_1;
    }

    return;
  }

  //End of the program, so go to return block
  builder->CreateBr(endbb);

  if (phi) {
    std::cerr << "Error: Missing ']'\n";
    abort();
  }
}
<commit_msg>update for recent api changes.  I have a hard time believing that this is actually a useful example.<commit_after>//===-- BrainF.cpp - BrainF compiler example ----------------------------===//
//
//                     The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===--------------------------------------------------------------------===//
//
// This class compiles the BrainF language into LLVM assembly.
//
// The BrainF language has 8 commands:
// Command   Equivalent C    Action
// -------   ------------    ------
// ,         *h=getchar();   Read a character from stdin, 255 on EOF
// .         putchar(*h);    Write a character to stdout
// -         --*h;           Decrement tape
// +         ++*h;           Increment tape
// <         --h;            Move head left
// >         ++h;            Move head right
// [         while(*h) {     Start loop
// ]         }               End loop
//
//===--------------------------------------------------------------------===//

#include "BrainF.h"
#include "llvm/Constants.h"
#include "llvm/Instructions.h"
#include "llvm/Intrinsics.h"
#include "llvm/ADT/STLExtras.h"
#include <iostream>
using namespace llvm;

//Set the constants for naming
const char *BrainF::tapereg = "tape";
const char *BrainF::headreg = "head";
const char *BrainF::label   = "brainf";
const char *BrainF::testreg = "test";

Module *BrainF::parse(std::istream *in1, int mem, CompileFlags cf,
                      LLVMContext& Context) {
  in       = in1;
  memtotal = mem;
  comflag  = cf;

  header(Context);
  readloop(0, 0, 0, Context);
  delete builder;
  return module;
}

void BrainF::header(LLVMContext& C) {
  module = new Module("BrainF", C);

  //Function prototypes

  //declare void @llvm.memset.p0i8.i32(i8 *, i8, i32, i32, i1)
  Type *Tys[] = { Type::getInt8PtrTy(C), Type::getInt32Ty(C) };
  Function *memset_func = Intrinsic::getDeclaration(module, Intrinsic::memset,
                                                    Tys);

  //declare i32 @getchar()
  getchar_func = cast<Function>(module->
    getOrInsertFunction("getchar", IntegerType::getInt32Ty(C), NULL));

  //declare i32 @putchar(i32)
  putchar_func = cast<Function>(module->
    getOrInsertFunction("putchar", IntegerType::getInt32Ty(C),
                        IntegerType::getInt32Ty(C), NULL));

  //Function header

  //define void @brainf()
  brainf_func = cast<Function>(module->
    getOrInsertFunction("brainf", Type::getVoidTy(C), NULL));

  builder = new IRBuilder<>(BasicBlock::Create(C, label, brainf_func));

  //%arr = malloc i8, i32 %d
  ConstantInt *val_mem = ConstantInt::get(C, APInt(32, memtotal));
  BasicBlock* BB = builder->GetInsertBlock();
  Type* IntPtrTy = IntegerType::getInt32Ty(C);
  Type* Int8Ty = IntegerType::getInt8Ty(C);
  Constant* allocsize = ConstantExpr::getSizeOf(Int8Ty);
  allocsize = ConstantExpr::getTruncOrBitCast(allocsize, IntPtrTy);
  ptr_arr = CallInst::CreateMalloc(BB, IntPtrTy, Int8Ty, allocsize, val_mem,
                                   NULL, "arr");
  BB->getInstList().push_back(cast<Instruction>(ptr_arr));

  //call void @llvm.memset.p0i8.i32(i8 *%arr, i8 0, i32 %d, i32 1, i1 0)
  {
    Value *memset_params[] = {
      ptr_arr,
      ConstantInt::get(C, APInt(8, 0)),
      val_mem,
      ConstantInt::get(C, APInt(32, 1)),
      ConstantInt::get(C, APInt(1, 0))
    };

    CallInst *memset_call = builder->
      CreateCall(memset_func, memset_params);
    memset_call->setTailCall(false);
  }

  //%arrmax = getelementptr i8 *%arr, i32 %d
  if (comflag & flag_arraybounds) {
    ptr_arrmax = builder->
      CreateGEP(ptr_arr, ConstantInt::get(C, APInt(32, memtotal)), "arrmax");
  }

  //%head.%d = getelementptr i8 *%arr, i32 %d
  curhead = builder->CreateGEP(ptr_arr,
                               ConstantInt::get(C, APInt(32, memtotal/2)),
                               headreg);

  //Function footer

  //brainf.end:
  endbb = BasicBlock::Create(C, label, brainf_func);

  //call free(i8 *%arr)
  endbb->getInstList().push_back(CallInst::CreateFree(ptr_arr, endbb));

  //ret void
  ReturnInst::Create(C, endbb);

  //Error block for array out of bounds
  if (comflag & flag_arraybounds)
  {
    //@aberrormsg = internal constant [%d x i8] c"\00"
    Constant *msg_0 =
      ConstantArray::get(C, "Error: The head has left the tape.", true);

    GlobalVariable *aberrormsg = new GlobalVariable(
      *module,
      msg_0->getType(),
      true,
      GlobalValue::InternalLinkage,
      msg_0,
      "aberrormsg");

    //declare i32 @puts(i8 *)
    Function *puts_func = cast<Function>(module->
      getOrInsertFunction("puts", IntegerType::getInt32Ty(C),
                      PointerType::getUnqual(IntegerType::getInt8Ty(C)), NULL));

    //brainf.aberror:
    aberrorbb = BasicBlock::Create(C, label, brainf_func);

    //call i32 @puts(i8 *getelementptr([%d x i8] *@aberrormsg, i32 0, i32 0))
    {
      Constant *zero_32 = Constant::getNullValue(IntegerType::getInt32Ty(C));

      Constant *gep_params[] = {
        zero_32,
        zero_32
      };

      Constant *msgptr = ConstantExpr::
        getGetElementPtr(aberrormsg, gep_params,
                         array_lengthof(gep_params));

      Value *puts_params[] = {
        msgptr
      };

      CallInst *puts_call =
        CallInst::Create(puts_func,
                         puts_params,
                         "", aberrorbb);
      puts_call->setTailCall(false);
    }

    //br label %brainf.end
    BranchInst::Create(endbb, aberrorbb);
  }
}

void BrainF::readloop(PHINode *phi, BasicBlock *oldbb, BasicBlock *testbb,
                      LLVMContext &C) {
  Symbol cursym = SYM_NONE;
  int curvalue = 0;
  Symbol nextsym = SYM_NONE;
  int nextvalue = 0;
  char c;
  int loop;
  int direction;

  while(cursym != SYM_EOF && cursym != SYM_ENDLOOP) {
    // Write out commands
    switch(cursym) {
      case SYM_NONE:
        // Do nothing
        break;

      case SYM_READ:
        {
          //%tape.%d = call i32 @getchar()
          CallInst *getchar_call = builder->CreateCall(getchar_func, tapereg);
          getchar_call->setTailCall(false);
          Value *tape_0 = getchar_call;

          //%tape.%d = trunc i32 %tape.%d to i8
          Value *tape_1 = builder->
            CreateTrunc(tape_0, IntegerType::getInt8Ty(C), tapereg);

          //store i8 %tape.%d, i8 *%head.%d
          builder->CreateStore(tape_1, curhead);
        }
        break;

      case SYM_WRITE:
        {
          //%tape.%d = load i8 *%head.%d
          LoadInst *tape_0 = builder->CreateLoad(curhead, tapereg);

          //%tape.%d = sext i8 %tape.%d to i32
          Value *tape_1 = builder->
            CreateSExt(tape_0, IntegerType::getInt32Ty(C), tapereg);

          //call i32 @putchar(i32 %tape.%d)
          Value *putchar_params[] = {
            tape_1
          };
          CallInst *putchar_call = builder->
            CreateCall(putchar_func,
                       putchar_params);
          putchar_call->setTailCall(false);
        }
        break;

      case SYM_MOVE:
        {
          //%head.%d = getelementptr i8 *%head.%d, i32 %d
          curhead = builder->
            CreateGEP(curhead, ConstantInt::get(C, APInt(32, curvalue)),
                      headreg);

          //Error block for array out of bounds
          if (comflag & flag_arraybounds)
          {
            //%test.%d = icmp uge i8 *%head.%d, %arrmax
            Value *test_0 = builder->
              CreateICmpUGE(curhead, ptr_arrmax, testreg);

            //%test.%d = icmp ult i8 *%head.%d, %arr
            Value *test_1 = builder->
              CreateICmpULT(curhead, ptr_arr, testreg);

            //%test.%d = or i1 %test.%d, %test.%d
            Value *test_2 = builder->
              CreateOr(test_0, test_1, testreg);

            //br i1 %test.%d, label %main.%d, label %main.%d
            BasicBlock *nextbb = BasicBlock::Create(C, label, brainf_func);
            builder->CreateCondBr(test_2, aberrorbb, nextbb);

            //main.%d:
            builder->SetInsertPoint(nextbb);
          }
        }
        break;

      case SYM_CHANGE:
        {
          //%tape.%d = load i8 *%head.%d
          LoadInst *tape_0 = builder->CreateLoad(curhead, tapereg);

          //%tape.%d = add i8 %tape.%d, %d
          Value *tape_1 = builder->
            CreateAdd(tape_0, ConstantInt::get(C, APInt(8, curvalue)), tapereg);

          //store i8 %tape.%d, i8 *%head.%d\n"
          builder->CreateStore(tape_1, curhead);
        }
        break;

      case SYM_LOOP:
        {
          //br label %main.%d
          BasicBlock *testbb = BasicBlock::Create(C, label, brainf_func);
          builder->CreateBr(testbb);

          //main.%d:
          BasicBlock *bb_0 = builder->GetInsertBlock();
          BasicBlock *bb_1 = BasicBlock::Create(C, label, brainf_func);
          builder->SetInsertPoint(bb_1);

          // Make part of PHI instruction now, wait until end of loop to finish
          PHINode *phi_0 =
            PHINode::Create(PointerType::getUnqual(IntegerType::getInt8Ty(C)),
                            2, headreg, testbb);
          phi_0->addIncoming(curhead, bb_0);
          curhead = phi_0;

          readloop(phi_0, bb_1, testbb, C);
        }
        break;

      default:
        std::cerr << "Error: Unknown symbol.\n";
        abort();
        break;
    }

    cursym = nextsym;
    curvalue = nextvalue;
    nextsym = SYM_NONE;

    // Reading stdin loop
    loop = (cursym == SYM_NONE)
        || (cursym == SYM_MOVE)
        || (cursym == SYM_CHANGE);
    while(loop) {
      *in>>c;
      if (in->eof()) {
        if (cursym == SYM_NONE) {
          cursym = SYM_EOF;
        } else {
          nextsym = SYM_EOF;
        }
        loop = 0;
      } else {
        direction = 1;
        switch(c) {
          case '-':
            direction = -1;
            // Fall through

          case '+':
            if (cursym == SYM_CHANGE) {
              curvalue += direction;
              // loop = 1
            } else {
              if (cursym == SYM_NONE) {
                cursym = SYM_CHANGE;
                curvalue = direction;
                // loop = 1
              } else {
                nextsym = SYM_CHANGE;
                nextvalue = direction;
                loop = 0;
              }
            }
            break;

          case '<':
            direction = -1;
            // Fall through

          case '>':
            if (cursym == SYM_MOVE) {
              curvalue += direction;
              // loop = 1
            } else {
              if (cursym == SYM_NONE) {
                cursym = SYM_MOVE;
                curvalue = direction;
                // loop = 1
              } else {
                nextsym = SYM_MOVE;
                nextvalue = direction;
                loop = 0;
              }
            }
            break;

          case ',':
            if (cursym == SYM_NONE) {
              cursym = SYM_READ;
            } else {
              nextsym = SYM_READ;
            }
            loop = 0;
            break;

          case '.':
            if (cursym == SYM_NONE) {
              cursym = SYM_WRITE;
            } else {
              nextsym = SYM_WRITE;
            }
            loop = 0;
            break;

          case '[':
            if (cursym == SYM_NONE) {
              cursym = SYM_LOOP;
            } else {
              nextsym = SYM_LOOP;
            }
            loop = 0;
            break;

          case ']':
            if (cursym == SYM_NONE) {
              cursym = SYM_ENDLOOP;
            } else {
              nextsym = SYM_ENDLOOP;
            }
            loop = 0;
            break;

          // Ignore other characters
          default:
            break;
        }
      }
    }
  }

  if (cursym == SYM_ENDLOOP) {
    if (!phi) {
      std::cerr << "Error: Extra ']'\n";
      abort();
    }

    // Write loop test
    {
      //br label %main.%d
      builder->CreateBr(testbb);

      //main.%d:

      //%head.%d = phi i8 *[%head.%d, %main.%d], [%head.%d, %main.%d]
      //Finish phi made at beginning of loop
      phi->addIncoming(curhead, builder->GetInsertBlock());
      Value *head_0 = phi;

      //%tape.%d = load i8 *%head.%d
      LoadInst *tape_0 = new LoadInst(head_0, tapereg, testbb);

      //%test.%d = icmp eq i8 %tape.%d, 0
      ICmpInst *test_0 = new ICmpInst(*testbb, ICmpInst::ICMP_EQ, tape_0,
                                    ConstantInt::get(C, APInt(8, 0)), testreg);

      //br i1 %test.%d, label %main.%d, label %main.%d
      BasicBlock *bb_0 = BasicBlock::Create(C, label, brainf_func);
      BranchInst::Create(bb_0, oldbb, test_0, testbb);

      //main.%d:
      builder->SetInsertPoint(bb_0);

      //%head.%d = phi i8 *[%head.%d, %main.%d]
      PHINode *phi_1 = builder->
        CreatePHI(PointerType::getUnqual(IntegerType::getInt8Ty(C)), 1,
                  headreg);
      phi_1->addIncoming(head_0, testbb);
      curhead = phi_1;
    }

    return;
  }

  //End of the program, so go to return block
  builder->CreateBr(endbb);

  if (phi) {
    std::cerr << "Error: Missing ']'\n";
    abort();
  }
}
<|endoftext|>


<commit_before>/**
 * @file lars.hpp
 * @author Nishant Mehta (niche)
 *
 * Definition of the LARS class, which performs Least Angle Regression and the
 * LASSO.
 */
#ifndef __MLPACK_METHODS_LARS_LARS_HPP
#define __MLPACK_METHODS_LARS_LARS_HPP

#include <armadillo>
#include <mlpack/core.hpp>

#define EPS 1e-16

namespace mlpack {
namespace lars {

// beta is the estimator
// yHat is the prediction from the current estimator

/**
 * An implementation of LARS, a stage-wise homotopy-based algorithm for
 * l1 regularized linear regression (LASSO) and l1+l2 regularized linear
 * regression (Elastic Net).
 * Let X be a matrix where each row is a point and each column is a dimension,
 * and let y be a vector of targets.
 * The Elastic Net problem is to solve
 * min_beta ||X beta - y||_2^2 + lambda_1 ||beta||_1 + 0.5 lambda_2 ||beta||_2^2
 * If lambda_1 > 0, lambda_2 = 0, the problem is the LASSO.
 * If lambda_1 > 0, lambda_2 > 0, the problem is the Elastic Net.
 * If lambda_1 = 0, lambda_2 > 0, the problem is Ridge Regression.
 * If lambda_1 = 0, lambda_2 = 0, the problem is unregularized linear regression.
 *
 * Note: This algorithm is not recommended for use (in terms of efficiency)
 * when lambda_1 = 0.
 *
 * Only minor modifications are necessary to handle the constrained version of
 * the problem:
 *   min_beta ||X beta - y||_2^2 + 0.5 lambda_2 ||beta||_2^2
 *   subject to ||beta||_1 <= tau
 * Although this option currently is not implemented, it will be implemented
 * very soon.
 *
 * For more details, see the following papers:
 *
 * @article{efron2004least,
 *   title={Least angle regression},
 *   author={Efron, B. and Hastie, T. and Johnstone, I. and Tibshirani, R.},
 *   journal={The Annals of statistics},
 *   volume={32},
 *   number={2},
 *   pages={407--499},
 *   year={2004},
 *   publisher={Institute of Mathematical Statistics}
 * }
 *
 * @article{zou2005regularization,
 *   title={Regularization and variable selection via the elastic net},
 *   author={Zou, H. and Hastie, T.},
 *   journal={Journal of the Royal Statistical Society Series B},
 *   volume={67},
 *   number={2},
 *   pages={301--320},
 *   year={2005},
 *   publisher={Royal Statistical Society}
 * }
 */
class LARS {

 public:

  /**
   * Set the parameters to LARS
   * Both lambda1 and lambda2 default to 0
   *
   * @param useCholesky Whether or not to use Cholesky decomposition when
   *    solving linear system. If no, compute full Gram matrix at beginning.
   * @param lambda1 Regularization parameter for l_1-norm penalty
   */
  LARS(const bool useCholesky);

  /**
   * Set the parameters to LARS
   * lambda2 defaults to 0
   *
   * @param useCholesky Whether or not to use Cholesky decomposition when
   *    solving linear system. If no, compute full Gram matrix at beginning.
   * @param lambda1 Regularization parameter for l_1-norm penalty
   */
  LARS(const bool useCholesky,
       const double lambda1);

  /**
   * Set the parameters to LARS
   *
   * @param useCholesky Whether or not to use Cholesky decomposition when
   *    solving linear system. If no, compute full Gram matrix at beginning.
   * @param lambda1 Regularization parameter for l_1-norm penalty
   * @param lambda2 Regularization parameter for l_2-norm penalty
   */
  LARS(const bool useCholesky,
       const double lambda1,
       const double lambda2);

  ~LARS() { }

  /*
   * Set the Gram matrix (done before calling DoLars)
   *
   * @param matGram Matrix to which to set Gram matrix
   */
  void SetGram(const arma::mat& matGram);

  /*
   * Compute Gram matrix. If elastic net, add lambda2 * identity to diagonal.
   *
   * @param matX Data matrix to use for computing Gram matrix
   */
  void ComputeGram(const arma::mat& matX);

  /*
   * Accessor for activeSet
   */
  const std::vector<arma::u32> ActiveSet();

  /*
   * Accessor for betaPath
   */
  const std::vector<arma::vec> BetaPath();

  /*
   * Accessor for lambdaPath
   */
  const std::vector<double> LambdaPath();

  /*
   * Accessor for matUtriCholFactor
   */
  const arma::mat MatUtriCholFactor();

  /*
   * Do LARS
   */
  void DoLARS(const arma::mat& matX, const arma::vec& y);

  /*
   * Load the solution vector, which is the last vector from the solution path
   */
  void Solution(arma::vec& beta);

private:
  arma::mat matGram;

  // Upper triangular cholesky factor; initially 0x0 arma::matrix.
  arma::mat matUtriCholFactor;

  bool useCholesky;

  bool lasso;
  double lambda1;

  bool elasticNet;
  double lambda2;

  std::vector<arma::vec> betaPath;
  std::vector<double> lambdaPath;

  arma::u32 nActive;
  std::vector<arma::u32> activeSet;
  std::vector<bool> isActive;

  void Deactivate(arma::u32 activeVarInd);

  void Activate(arma::u32 varInd);

  void ComputeYHatDirection(const arma::mat& matX,
			    const arma::vec& betaDirection,
                            arma::vec& yHatDirection);

  void InterpolateBeta();

  void CholeskyInsert(const arma::vec& newX, const arma::mat& X);

  void CholeskyInsert(const arma::vec& newX, const arma::vec& newGramCol);

  void GivensRotate(const arma::vec& x, arma::vec& rotatedX, arma::mat& G);

  void CholeskyDelete(arma::u32 colToKill);

};

}; // namespace lars
}; // namespace mlpack

#endif
<commit_msg>more comments for lars<commit_after>/**
 * @file lars.hpp
 * @author Nishant Mehta (niche)
 *
 * Definition of the LARS class, which performs Least Angle Regression and the
 * LASSO.
 */
#ifndef __MLPACK_METHODS_LARS_LARS_HPP
#define __MLPACK_METHODS_LARS_LARS_HPP

#include <armadillo>
#include <mlpack/core.hpp>

#define EPS 1e-16

namespace mlpack {
namespace lars {

// beta is the estimator
// yHat is the prediction from the current estimator

/**
 * An implementation of LARS, a stage-wise homotopy-based algorithm for
 * l1 regularized linear regression (LASSO) and l1+l2 regularized linear
 * regression (Elastic Net).
 * Let X be a matrix where each row is a point and each column is a dimension,
 * and let y be a vector of targets.
 * The Elastic Net problem is to solve
 * min_beta ||X beta - y||_2^2 + lambda_1 ||beta||_1 + 0.5 lambda_2 ||beta||_2^2
 * If lambda_1 > 0, lambda_2 = 0, the problem is the LASSO.
 * If lambda_1 > 0, lambda_2 > 0, the problem is the Elastic Net.
 * If lambda_1 = 0, lambda_2 > 0, the problem is Ridge Regression.
 * If lambda_1 = 0, lambda_2 = 0, the problem is unregularized linear regression.
 *
 * Note: This algorithm is not recommended for use (in terms of efficiency)
 * when lambda_1 = 0.
 *
 * Only minor modifications are necessary to handle the constrained version of
 * the problem:
 *   min_beta ||X beta - y||_2^2 + 0.5 lambda_2 ||beta||_2^2
 *   subject to ||beta||_1 <= tau
 * Although this option currently is not implemented, it will be implemented
 * very soon.
 *
 * For more details, see the following papers:
 *
 * @article{efron2004least,
 *   title={Least angle regression},
 *   author={Efron, B. and Hastie, T. and Johnstone, I. and Tibshirani, R.},
 *   journal={The Annals of statistics},
 *   volume={32},
 *   number={2},
 *   pages={407--499},
 *   year={2004},
 *   publisher={Institute of Mathematical Statistics}
 * }
 *
 * @article{zou2005regularization,
 *   title={Regularization and variable selection via the elastic net},
 *   author={Zou, H. and Hastie, T.},
 *   journal={Journal of the Royal Statistical Society Series B},
 *   volume={67},
 *   number={2},
 *   pages={301--320},
 *   year={2005},
 *   publisher={Royal Statistical Society}
 * }
 */
class LARS {

 public:

  /**
   * Set the parameters to LARS
   * Both lambda1 and lambda2 default to 0
   *
   * @param useCholesky Whether or not to use Cholesky decomposition when
   *    solving linear system. If no, compute full Gram matrix at beginning.
   * @param lambda1 Regularization parameter for l_1-norm penalty
   */
  LARS(const bool useCholesky);

  /**
   * Set the parameters to LARS
   * lambda2 defaults to 0
   *
   * @param useCholesky Whether or not to use Cholesky decomposition when
   *    solving linear system. If no, compute full Gram matrix at beginning.
   * @param lambda1 Regularization parameter for l_1-norm penalty
   */
  LARS(const bool useCholesky,
       const double lambda1);

  /**
   * Set the parameters to LARS
   *
   * @param useCholesky Whether or not to use Cholesky decomposition when
   *    solving linear system. If no, compute full Gram matrix at beginning.
   * @param lambda1 Regularization parameter for l_1-norm penalty
   * @param lambda2 Regularization parameter for l_2-norm penalty
   */
  LARS(const bool useCholesky,
       const double lambda1,
       const double lambda2);

  ~LARS() { }

  /*
   * Set the Gram matrix (done before calling DoLars)
   *
   * @param matGram Matrix to which to set Gram matrix
   */
  void SetGram(const arma::mat& matGram);

  /*
   * Compute Gram matrix. If elastic net, add lambda2 * identity to diagonal.
   *
   * @param matX Data matrix to use for computing Gram matrix
   */
  void ComputeGram(const arma::mat& matX);

  /*
   * Accessor for activeSet
   */
  const std::vector<arma::u32> ActiveSet();

  /*
   * Accessor for betaPath
   */
  const std::vector<arma::vec> BetaPath();

  /*
   * Accessor for lambdaPath
   */
  const std::vector<double> LambdaPath();

  /*
   * Accessor for matUtriCholFactor
   */
  const arma::mat MatUtriCholFactor();

  /* Run LARS
   *
   * @param matX Input data into the algorithm - a matrix where each row is a
   *    point and each column is a dimension
   * @param y A vector of targets
   */
  void DoLARS(const arma::mat& matX, const arma::vec& y);

  /*
   * Load the solution vector, which is the last vector from the solution path
   */
  void Solution(arma::vec& beta);

private:
  arma::mat matGram;

  // Upper triangular cholesky factor; initially 0x0 arma::matrix.
  arma::mat matUtriCholFactor;

  bool useCholesky;

  bool lasso;
  double lambda1;

  bool elasticNet;
  double lambda2;

  std::vector<arma::vec> betaPath;
  std::vector<double> lambdaPath;

  arma::u32 nActive;
  std::vector<arma::u32> activeSet;
  std::vector<bool> isActive;

  void Deactivate(arma::u32 activeVarInd);

  void Activate(arma::u32 varInd);

  void ComputeYHatDirection(const arma::mat& matX,
			    const arma::vec& betaDirection,
                            arma::vec& yHatDirection);

  void InterpolateBeta();

  void CholeskyInsert(const arma::vec& newX, const arma::mat& X);

  void CholeskyInsert(const arma::vec& newX, const arma::vec& newGramCol);

  void GivensRotate(const arma::vec& x, arma::vec& rotatedX, arma::mat& G);

  void CholeskyDelete(arma::u32 colToKill);

};

}; // namespace lars
}; // namespace mlpack

#endif
<|endoftext|>


<commit_before>// Copyright (c) 2013, Cloudera, inc.
// Confidential Cloudera Information: Covered by NDA.

#include "clang/AST/ASTContext.h"
#include "clang/AST/ASTTypeTraits.h"
#include "clang/ASTMatchers/ASTMatchers.h"
#include "clang/ASTMatchers/ASTMatchFinder.h"
#include "clang/AST/Stmt.h"
#include "clang/Driver/Options.h"
#include "clang/Frontend/FrontendActions.h"
#include "clang/Frontend/TextDiagnostic.h"
#include "clang/Tooling/CommonOptionsParser.h"
#include "clang/Tooling/Tooling.h"
#include "llvm/Option/OptTable.h"
#include "llvm/Support/CommandLine.h"

// Using clang without importing namespaces is damn near impossible.
using namespace llvm; // NOLINT
using namespace clang::ast_matchers; // NOLINT

using llvm::opt::OptTable;
using clang::ast_type_traits::DynTypedNode;
using clang::driver::createDriverOptTable;
using clang::driver::options::OPT_ast_dump;
using clang::tooling::CommonOptionsParser;
using clang::tooling::CommandLineArguments;
using clang::tooling::ClangTool;
using clang::tooling::newFrontendActionFactory;
using clang::ASTContext;
using clang::CharSourceRange;
using clang::ClassTemplateSpecializationDecl;
using clang::Decl;
using clang::DiagnosticsEngine;
using clang::Stmt;
using clang::FixItHint;
using clang::SourceLocation;
using clang::SourceRange;
using clang::Stmt;
using clang::TextDiagnostic;
using std::string;

static cl::extrahelp CommonHelp(CommonOptionsParser::HelpMessage);
static cl::extrahelp MoreHelp(
    "\tFor example, to run kudu-lint on all files in a subtree of the\n"
    "\tsource tree, use:\n"
    "\n"
    "\t  find path/in/subtree -name '*.cc'|xargs kudu-lint\n"
    "\n"
    "\tor using a specific build path:\n"
    "\n"
    "\t  find path/in/subtree -name '*.cc'|xargs kudu-lint -p build/path\n"
    "\n"
    "\tNote, that path/in/subtree and current directory should follow the\n"
    "\trules described above.\n"
    "\n"
    "\t Sometimes kudu-lint can't figure out the proper line number for an\n"
    "\t error, and reports it inside some standard library. the -ast-dump\n"
    "\t option can be useful for these circumstances.\n"
    "\n"
);

// Command line flags.
static OwningPtr<OptTable> gOptions(createDriverOptTable());
static cl::list<std::string> gArgsAfter(
  "extra-arg",
  cl::desc("Additional argument to append to the compiler command line"));
static cl::list<std::string> gArgsBefore(
  "extra-arg-before",
  cl::desc("Additional argument to prepend to the compiler command line"));
static cl::opt<bool> gASTDump("ast-dump",
                              cl::desc(gOptions->getOptionHelpText(OPT_ast_dump)));

namespace {

// Callback for unused statuses. Simply reports the error at the point where the
// expression was found.
template<class NODETYPE>
class ErrorPrinter : public MatchFinder::MatchCallback {
 public:
  ErrorPrinter(const std::string& error_msg,
               const std::string& bound_name,
               bool skip_system_headers)
    : error_msg_(error_msg),
      bound_name_(bound_name),
      skip_system_headers_(skip_system_headers) {
  }

  virtual void run(const MatchFinder::MatchResult& result) {
    const NODETYPE* node;
    if ((node = result.Nodes.getNodeAs<NODETYPE>(bound_name_))) {
      SourceRange r = node->getSourceRange();

      if (skip_system_headers_ && result.SourceManager->isInSystemHeader(r.getBegin())) {
        return;
      }

      if (gASTDump) {
        node->dump();
      }

      if (r.isValid()) {
        TextDiagnostic td(llvm::outs(), result.Context->getLangOpts(),
                          &result.Context->getDiagnostics().getDiagnosticOptions());
        td.emitDiagnostic(r.getBegin(), DiagnosticsEngine::Error,
                          error_msg_,
                          ArrayRef<CharSourceRange>(CharSourceRange::getTokenRange(r)),
                          ArrayRef<FixItHint>(), result.SourceManager);
      }

      SourceLocation instantiation_point;
      if (FindInstantiationPoint(result, node, &instantiation_point)) {
        TextDiagnostic td(llvm::outs(), result.Context->getLangOpts(),
                          &result.Context->getDiagnostics().getDiagnosticOptions());
        td.emitDiagnostic(instantiation_point, DiagnosticsEngine::Note,
                          "previous error instantiated at",
                          ArrayRef<CharSourceRange>(),
                          ArrayRef<FixItHint>(), result.SourceManager);
      }
    } else {
      llvm_unreachable("bound node missing");
    }
  }

 private:
  bool GetParent(ASTContext* ctx, const DynTypedNode& node, DynTypedNode* parent) {
    ASTContext::ParentVector parents = ctx->getParents(node);
    if (parents.empty()) return false;
    assert(parents.size() == 1);
    *parent = parents[0];
    return true;
  }

  // If the AST node 'node' has an ancestor which is a template instantiation,
  // fill the source location of that instantiation into 'loc'. Unfortunately,
  // Clang doesn't retain enough information in the AST nodes to recurse here --
  // so in many cases this is useless, since the instantiation point will simply
  // be inside another instantiated template.
  bool FindInstantiationPoint(const MatchFinder::MatchResult& result,
                              const NODETYPE* node,
                              SourceLocation* loc) {
    DynTypedNode dyn_node = DynTypedNode::create<NODETYPE>(*node);

    // Recurse up the tree.
    while (true) {
      const ClassTemplateSpecializationDecl* D =
          dyn_node.get<ClassTemplateSpecializationDecl>();
      if (D) {
        *loc = D->getPointOfInstantiation();
        return true;
      }
      // TODO: there are probably other types of specializations to handle, but this is the only
      // one seen so far.

      DynTypedNode parent;
      if (!GetParent(result.Context, dyn_node, &parent)) {
        return false;
      }
      dyn_node = parent;
    }
  }

  string error_msg_;
  string bound_name_;
  bool skip_system_headers_;
};

// Inserts arguments before or after the usual command line arguments.
class InsertAdjuster: public clang::tooling::ArgumentsAdjuster {
 public:
  enum Position { BEGIN, END };

  InsertAdjuster(const CommandLineArguments &extra, Position pos)
    : extra_(extra), pos_(pos) {
  }

  InsertAdjuster(const char *extra_, Position pos)
    : extra_(1, std::string(extra_)), pos_(pos) {
  }

  virtual CommandLineArguments Adjust(const CommandLineArguments &Args) LLVM_OVERRIDE {
    CommandLineArguments ret(Args);

    CommandLineArguments::iterator I;
    if (pos_ == END) {
      I = ret.end();
    } else {
      I = ret.begin();
      ++I; // To leave the program name in place
    }

    ret.insert(I, extra_.begin(), extra_.end());
    return ret;
  }

 private:
  const CommandLineArguments extra_;
  const Position pos_;
};

} // anonymous namespace

int main(int argc, const char **argv) {
  CommonOptionsParser options_parser(argc, argv);
  ClangTool Tool(options_parser.getCompilations(),
                 options_parser.getSourcePathList());
  if (gArgsAfter.size() > 0) {
    Tool.appendArgumentsAdjuster(new InsertAdjuster(gArgsAfter,
          InsertAdjuster::END));
  }
  if (gArgsBefore.size() > 0) {
    Tool.appendArgumentsAdjuster(new InsertAdjuster(gArgsBefore,
          InsertAdjuster::BEGIN));
  }

  // Match expressions of type 'Status' which are parented by a compound statement.
  // This implies that the expression is being thrown away, rather than assigned
  // to some variable or function call.
  //
  // For more information on AST matchers, refer to:
  // http://clang.llvm.org/docs/LibASTMatchersReference.html
  StatementMatcher ignored_status_matcher =
    expr(hasType(recordDecl(hasName("Status"))),
         hasParent(compoundStmt())).bind("expr");
  ErrorPrinter<Stmt> ignored_status_printer("Unused status result", "expr", false);

  // Match class members which are reference-typed. This is confusing since they
  // tend to "look like" copied values, but in fact often reference external
  // entities passed in in the constructor.
  DeclarationMatcher ref_member_matcher =
    fieldDecl(hasType(referenceType()))
    .bind("decl");
  ErrorPrinter<Decl> ref_member_printer("Reference-typed member", "decl", true);

  MatchFinder finder;
  finder.addMatcher(ignored_status_matcher, &ignored_status_printer);
  finder.addMatcher(ref_member_matcher, &ref_member_printer);
  return Tool.run(newFrontendActionFactory(&finder));
}
<commit_msg>KUDU-599. Add kudu-lint check to catch calls to sleep, usleep, nanosleep.<commit_after>// Copyright (c) 2013, Cloudera, inc.
// Confidential Cloudera Information: Covered by NDA.

#include "clang/AST/ASTContext.h"
#include "clang/AST/ASTTypeTraits.h"
#include "clang/ASTMatchers/ASTMatchers.h"
#include "clang/ASTMatchers/ASTMatchFinder.h"
#include "clang/AST/Stmt.h"
#include "clang/Driver/Options.h"
#include "clang/Frontend/FrontendActions.h"
#include "clang/Frontend/TextDiagnostic.h"
#include "clang/Tooling/CommonOptionsParser.h"
#include "clang/Tooling/Tooling.h"
#include "llvm/Option/OptTable.h"
#include "llvm/Support/CommandLine.h"

// Using clang without importing namespaces is damn near impossible.
using namespace llvm; // NOLINT
using namespace clang::ast_matchers; // NOLINT

using llvm::opt::OptTable;
using clang::ast_type_traits::DynTypedNode;
using clang::driver::createDriverOptTable;
using clang::driver::options::OPT_ast_dump;
using clang::tooling::CommonOptionsParser;
using clang::tooling::CommandLineArguments;
using clang::tooling::ClangTool;
using clang::tooling::newFrontendActionFactory;
using clang::ASTContext;
using clang::CharSourceRange;
using clang::ClassTemplateSpecializationDecl;
using clang::Decl;
using clang::DiagnosticsEngine;
using clang::Stmt;
using clang::FixItHint;
using clang::SourceLocation;
using clang::SourceRange;
using clang::Stmt;
using clang::TextDiagnostic;
using std::string;

static cl::extrahelp CommonHelp(CommonOptionsParser::HelpMessage);
static cl::extrahelp MoreHelp(
    "\tFor example, to run kudu-lint on all files in a subtree of the\n"
    "\tsource tree, use:\n"
    "\n"
    "\t  find path/in/subtree -name '*.cc'|xargs kudu-lint\n"
    "\n"
    "\tor using a specific build path:\n"
    "\n"
    "\t  find path/in/subtree -name '*.cc'|xargs kudu-lint -p build/path\n"
    "\n"
    "\tNote, that path/in/subtree and current directory should follow the\n"
    "\trules described above.\n"
    "\n"
    "\t Sometimes kudu-lint can't figure out the proper line number for an\n"
    "\t error, and reports it inside some standard library. the -ast-dump\n"
    "\t option can be useful for these circumstances.\n"
    "\n"
);

// Command line flags.
static OwningPtr<OptTable> gOptions(createDriverOptTable());
static cl::list<std::string> gArgsAfter(
  "extra-arg",
  cl::desc("Additional argument to append to the compiler command line"));
static cl::list<std::string> gArgsBefore(
  "extra-arg-before",
  cl::desc("Additional argument to prepend to the compiler command line"));
static cl::opt<bool> gASTDump("ast-dump",
                              cl::desc(gOptions->getOptionHelpText(OPT_ast_dump)));

namespace {

// Callback for unused statuses. Simply reports the error at the point where the
// expression was found.
template<class NODETYPE>
class ErrorPrinter : public MatchFinder::MatchCallback {
 public:
  ErrorPrinter(const std::string& error_msg,
               const std::string& bound_name,
               bool skip_system_headers)
    : error_msg_(error_msg),
      bound_name_(bound_name),
      skip_system_headers_(skip_system_headers) {
  }

  virtual void run(const MatchFinder::MatchResult& result) {
    const NODETYPE* node;
    if ((node = result.Nodes.getNodeAs<NODETYPE>(bound_name_))) {
      SourceRange r = node->getSourceRange();

      if (skip_system_headers_ && result.SourceManager->isInSystemHeader(r.getBegin())) {
        return;
      }

      if (gASTDump) {
        node->dump();
      }

      if (r.isValid()) {
        TextDiagnostic td(llvm::outs(), result.Context->getLangOpts(),
                          &result.Context->getDiagnostics().getDiagnosticOptions());
        td.emitDiagnostic(r.getBegin(), DiagnosticsEngine::Error,
                          error_msg_,
                          ArrayRef<CharSourceRange>(CharSourceRange::getTokenRange(r)),
                          ArrayRef<FixItHint>(), result.SourceManager);
      }

      SourceLocation instantiation_point;
      if (FindInstantiationPoint(result, node, &instantiation_point)) {
        TextDiagnostic td(llvm::outs(), result.Context->getLangOpts(),
                          &result.Context->getDiagnostics().getDiagnosticOptions());
        td.emitDiagnostic(instantiation_point, DiagnosticsEngine::Note,
                          "previous error instantiated at",
                          ArrayRef<CharSourceRange>(),
                          ArrayRef<FixItHint>(), result.SourceManager);
      }
    } else {
      llvm_unreachable("bound node missing");
    }
  }

 private:
  bool GetParent(ASTContext* ctx, const DynTypedNode& node, DynTypedNode* parent) {
    ASTContext::ParentVector parents = ctx->getParents(node);
    if (parents.empty()) return false;
    assert(parents.size() == 1);
    *parent = parents[0];
    return true;
  }

  // If the AST node 'node' has an ancestor which is a template instantiation,
  // fill the source location of that instantiation into 'loc'. Unfortunately,
  // Clang doesn't retain enough information in the AST nodes to recurse here --
  // so in many cases this is useless, since the instantiation point will simply
  // be inside another instantiated template.
  bool FindInstantiationPoint(const MatchFinder::MatchResult& result,
                              const NODETYPE* node,
                              SourceLocation* loc) {
    DynTypedNode dyn_node = DynTypedNode::create<NODETYPE>(*node);

    // Recurse up the tree.
    while (true) {
      const ClassTemplateSpecializationDecl* D =
          dyn_node.get<ClassTemplateSpecializationDecl>();
      if (D) {
        *loc = D->getPointOfInstantiation();
        return true;
      }
      // TODO: there are probably other types of specializations to handle, but this is the only
      // one seen so far.

      DynTypedNode parent;
      if (!GetParent(result.Context, dyn_node, &parent)) {
        return false;
      }
      dyn_node = parent;
    }
  }

  string error_msg_;
  string bound_name_;
  bool skip_system_headers_;
};

// Inserts arguments before or after the usual command line arguments.
class InsertAdjuster: public clang::tooling::ArgumentsAdjuster {
 public:
  enum Position { BEGIN, END };

  InsertAdjuster(const CommandLineArguments &extra, Position pos)
    : extra_(extra), pos_(pos) {
  }

  InsertAdjuster(const char *extra_, Position pos)
    : extra_(1, std::string(extra_)), pos_(pos) {
  }

  virtual CommandLineArguments Adjust(const CommandLineArguments &Args) LLVM_OVERRIDE {
    CommandLineArguments ret(Args);

    CommandLineArguments::iterator I;
    if (pos_ == END) {
      I = ret.end();
    } else {
      I = ret.begin();
      ++I; // To leave the program name in place
    }

    ret.insert(I, extra_.begin(), extra_.end());
    return ret;
  }

 private:
  const CommandLineArguments extra_;
  const Position pos_;
};

} // anonymous namespace

int main(int argc, const char **argv) {
  CommonOptionsParser options_parser(argc, argv);
  ClangTool Tool(options_parser.getCompilations(),
                 options_parser.getSourcePathList());
  if (gArgsAfter.size() > 0) {
    Tool.appendArgumentsAdjuster(new InsertAdjuster(gArgsAfter,
          InsertAdjuster::END));
  }
  if (gArgsBefore.size() > 0) {
    Tool.appendArgumentsAdjuster(new InsertAdjuster(gArgsBefore,
          InsertAdjuster::BEGIN));
  }

  // Match expressions of type 'Status' which are parented by a compound statement.
  // This implies that the expression is being thrown away, rather than assigned
  // to some variable or function call.
  //
  // For more information on AST matchers, refer to:
  // http://clang.llvm.org/docs/LibASTMatchersReference.html
  StatementMatcher ignored_status_matcher =
    expr(hasType(recordDecl(hasName("Status"))),
         hasParent(compoundStmt())).bind("expr");
  ErrorPrinter<Stmt> ignored_status_printer("Unused status result", "expr", false);

  // Match class members which are reference-typed. This is confusing since they
  // tend to "look like" copied values, but in fact often reference external
  // entities passed in in the constructor.
  DeclarationMatcher ref_member_matcher =
    fieldDecl(hasType(referenceType()))
    .bind("decl");
  ErrorPrinter<Decl> ref_member_printer("Reference-typed member", "decl", true);

  // Disallow calls to sleep, usleep, and nanosleep.
  // SleepFor(MonoDelta) should be used instead, as it is not prone to
  // unit conversion errors, and also ignores EINTR so will safely sleep
  // at least the requested duration.
  StatementMatcher sleep_matcher =
    callExpr(callee(namedDecl(matchesName("(nano|u)?sleep")))).bind("sleep_expr");
  ErrorPrinter<Stmt> sleep_printer("sleep, usleep or nanosleep call", "sleep_expr", true);

  MatchFinder finder;
  finder.addMatcher(ignored_status_matcher, &ignored_status_printer);
  finder.addMatcher(ref_member_matcher, &ref_member_printer);
  finder.addMatcher(sleep_matcher, &sleep_printer);

  return Tool.run(newFrontendActionFactory(&finder));
}
<|endoftext|>


<commit_before>/*
 *
 * Copyright 2015 gRPC authors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 */

#include <node.h>

#include "call.h"
#include "call_credentials.h"
#include "channel_credentials.h"
#include "util.h"
#include "grpc/grpc.h"
#include "grpc/grpc_security.h"
#include "grpc/support/log.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::Context;
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::Value;

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

ChannelCredentials::ChannelCredentials(grpc_channel_credentials *credentials)
    : wrapped_credentials(credentials) {}

ChannelCredentials::~ChannelCredentials() {
  grpc_channel_credentials_release(wrapped_credentials);
}

struct callback_md {
    Nan::Callback *callback;
};

static int verify_peer_callback_wrapper(const char* servername, const char* cert, void* userdata) {
  Nan::HandleScope scope;
  Nan::TryCatch try_catch;
  struct callback_md* md = (struct callback_md*)userdata;

  const unsigned argc = 2;
  Local<Value> argv[argc];
  if (servername == NULL) {
    argv[0] = Nan::Null();
  } else {
    argv[0] = Nan::New<v8::String>(servername).ToLocalChecked();
  }
  if (cert == NULL) {
    argv[1] = Nan::Null();
  } else {
    argv[1] = Nan::New<v8::String>(cert).ToLocalChecked();
  }

  md->callback->Call(argc, argv);

  if (try_catch.HasCaught()) {
    return 1;
  }

  return 0;
}

static void verify_peer_callback_destruct(void *userdata) {
  callback_md *md = (callback_md*)userdata;
  delete md->callback;
  delete md;
}

void ChannelCredentials::Init(Local<Object> exports) {
  HandleScope scope;
  Local<FunctionTemplate> tpl = Nan::New<FunctionTemplate>(New);
  tpl->SetClassName(Nan::New("ChannelCredentials").ToLocalChecked());
  tpl->InstanceTemplate()->SetInternalFieldCount(1);
  Nan::SetPrototypeMethod(tpl, "compose", Compose);
  fun_tpl.Reset(tpl);
  Local<Function> ctr = Nan::GetFunction(tpl).ToLocalChecked();
  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());
  Nan::Set(exports, Nan::New("ChannelCredentials").ToLocalChecked(), ctr);
  constructor = new Nan::Callback(ctr);
}

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

Local<Value> ChannelCredentials::WrapStruct(
    grpc_channel_credentials *credentials) {
  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_channel_credentials *ChannelCredentials::GetWrappedCredentials() {
  return wrapped_credentials;
}

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

NAN_METHOD(ChannelCredentials::CreateSsl) {
  StringOrNull root_certs;
  StringOrNull private_key;
  StringOrNull cert_chain;
  if (::node::Buffer::HasInstance(info[0])) {
    root_certs.assign(info[0]);
  } else if (!(info[0]->IsNull() || info[0]->IsUndefined())) {
    return Nan::ThrowTypeError("createSsl's first argument must be a Buffer");
  }
  if (::node::Buffer::HasInstance(info[1])) {
    private_key.assign(info[1]);
  } else if (!(info[1]->IsNull() || info[1]->IsUndefined())) {
    return Nan::ThrowTypeError(
        "createSSl's second argument must be a Buffer if provided");
  }
  if (::node::Buffer::HasInstance(info[2])) {
    cert_chain.assign(info[2]);
  } else if (!(info[2]->IsNull() || info[2]->IsUndefined())) {
    return Nan::ThrowTypeError(
        "createSSl's third argument must be a Buffer if provided");
  }
  grpc_ssl_pem_key_cert_pair key_cert_pair = {private_key.get(),
                                              cert_chain.get()};
  if (private_key.isAssigned() != cert_chain.isAssigned()) {
    return Nan::ThrowError(
        "createSsl's second and third arguments must be"
        " provided or omitted together");
  }

  verify_peer_options verify_options = {NULL, NULL, NULL};
  if (!info[3]->IsUndefined()) {
    if (!info[3]->IsObject()) {
      return Nan::ThrowTypeError("createSsl's fourth argument must be an "
          "object");
    }
    Local<Context> context = Nan::New<Context>();
    Local<Object> object = info[3]->ToObject();
    MaybeLocal<Array> maybe_props = object->GetOwnPropertyNames(context);
    if (!maybe_props.IsEmpty()) {
      Local<Array> props = maybe_props.ToLocalChecked();
      for(uint32_t i=0; i < props->Length(); i++) {
        Local<Value> key = props->Get(i);
        Local<Value> value = object->Get(key);

        if (key->IsString()) {
          Nan::Utf8String keyStr(key->ToString());

          if (strcmp("checkServerIdentity", (const char*)(*keyStr)) == 0) {

            if (!value->IsFunction()) {
                return Nan::ThrowError("Value of checkServerIdentity must be a function.");
            }
            struct callback_md *md = new struct callback_md;
            md->callback = new Callback(Local<Function>::Cast(value));
            verify_options.verify_peer_callback = verify_peer_callback_wrapper;
            verify_options.verify_peer_callback_userdata = (void*)md;
            verify_options.verify_peer_destruct = verify_peer_callback_destruct;

          }
        }
        // do stuff with key / value
      }
    }
  }

  grpc_channel_credentials *creds = grpc_ssl_credentials_create(
      root_certs.get(), private_key.isAssigned() ? &key_cert_pair : NULL,
      &verify_options, NULL);
  if (creds == NULL) {
    info.GetReturnValue().SetNull();
  } else {
    info.GetReturnValue().Set(WrapStruct(creds));
  }
}

NAN_METHOD(ChannelCredentials::Compose) {
  if (!ChannelCredentials::HasInstance(info.This())) {
    return Nan::ThrowTypeError(
        "compose can only be called on ChannelCredentials objects");
  }
  if (!CallCredentials::HasInstance(info[0])) {
    return Nan::ThrowTypeError(
        "compose's first argument must be a CallCredentials object");
  }
  ChannelCredentials *self =
      ObjectWrap::Unwrap<ChannelCredentials>(info.This());
  if (self->wrapped_credentials == NULL) {
    return Nan::ThrowTypeError("Cannot compose insecure credential");
  }
  CallCredentials *other = ObjectWrap::Unwrap<CallCredentials>(
      Nan::To<Object>(info[0]).ToLocalChecked());
  grpc_channel_credentials *creds = grpc_composite_channel_credentials_create(
      self->wrapped_credentials, other->GetWrappedCredentials(), NULL);
  if (creds == NULL) {
    info.GetReturnValue().SetNull();
  } else {
    info.GetReturnValue().Set(WrapStruct(creds));
  }
}

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

}  // namespace node
}  // namespace grpc
<commit_msg>Simplify userdata being passed to checkServerIdentity callback.<commit_after>/*
 *
 * Copyright 2015 gRPC authors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 */

#include <node.h>

#include "call.h"
#include "call_credentials.h"
#include "channel_credentials.h"
#include "util.h"
#include "grpc/grpc.h"
#include "grpc/grpc_security.h"
#include "grpc/support/log.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::Context;
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::Value;

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

ChannelCredentials::ChannelCredentials(grpc_channel_credentials *credentials)
    : wrapped_credentials(credentials) {}

ChannelCredentials::~ChannelCredentials() {
  grpc_channel_credentials_release(wrapped_credentials);
}

static int verify_peer_callback_wrapper(const char* servername, const char* cert, void* userdata) {
  Nan::HandleScope scope;
  Nan::TryCatch try_catch;
  Nan::Callback *callback = (Nan::Callback*)userdata;

  const unsigned argc = 2;
  Local<Value> argv[argc];
  if (servername == NULL) {
    argv[0] = Nan::Null();
  } else {
    argv[0] = Nan::New<v8::String>(servername).ToLocalChecked();
  }
  if (cert == NULL) {
    argv[1] = Nan::Null();
  } else {
    argv[1] = Nan::New<v8::String>(cert).ToLocalChecked();
  }

  callback->Call(argc, argv);

  if (try_catch.HasCaught()) {
    return 1;
  }

  return 0;
}

static void verify_peer_callback_destruct(void *userdata) {
  Nan::Callback *callback = (Nan::Callback*)userdata;
  delete callback;
}

void ChannelCredentials::Init(Local<Object> exports) {
  HandleScope scope;
  Local<FunctionTemplate> tpl = Nan::New<FunctionTemplate>(New);
  tpl->SetClassName(Nan::New("ChannelCredentials").ToLocalChecked());
  tpl->InstanceTemplate()->SetInternalFieldCount(1);
  Nan::SetPrototypeMethod(tpl, "compose", Compose);
  fun_tpl.Reset(tpl);
  Local<Function> ctr = Nan::GetFunction(tpl).ToLocalChecked();
  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());
  Nan::Set(exports, Nan::New("ChannelCredentials").ToLocalChecked(), ctr);
  constructor = new Nan::Callback(ctr);
}

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

Local<Value> ChannelCredentials::WrapStruct(
    grpc_channel_credentials *credentials) {
  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_channel_credentials *ChannelCredentials::GetWrappedCredentials() {
  return wrapped_credentials;
}

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

NAN_METHOD(ChannelCredentials::CreateSsl) {
  StringOrNull root_certs;
  StringOrNull private_key;
  StringOrNull cert_chain;
  if (::node::Buffer::HasInstance(info[0])) {
    root_certs.assign(info[0]);
  } else if (!(info[0]->IsNull() || info[0]->IsUndefined())) {
    return Nan::ThrowTypeError("createSsl's first argument must be a Buffer");
  }
  if (::node::Buffer::HasInstance(info[1])) {
    private_key.assign(info[1]);
  } else if (!(info[1]->IsNull() || info[1]->IsUndefined())) {
    return Nan::ThrowTypeError(
        "createSSl's second argument must be a Buffer if provided");
  }
  if (::node::Buffer::HasInstance(info[2])) {
    cert_chain.assign(info[2]);
  } else if (!(info[2]->IsNull() || info[2]->IsUndefined())) {
    return Nan::ThrowTypeError(
        "createSSl's third argument must be a Buffer if provided");
  }
  grpc_ssl_pem_key_cert_pair key_cert_pair = {private_key.get(),
                                              cert_chain.get()};
  if (private_key.isAssigned() != cert_chain.isAssigned()) {
    return Nan::ThrowError(
        "createSsl's second and third arguments must be"
        " provided or omitted together");
  }

  verify_peer_options verify_options = {NULL, NULL, NULL};
  if (!info[3]->IsUndefined()) {
    if (!info[3]->IsObject()) {
      return Nan::ThrowTypeError("createSsl's fourth argument must be an "
          "object");
    }
    Local<Context> context = Nan::New<Context>();
    Local<Object> object = info[3]->ToObject();
    MaybeLocal<Array> maybe_props = object->GetOwnPropertyNames(context);
    if (!maybe_props.IsEmpty()) {
      Local<Array> props = maybe_props.ToLocalChecked();
      for(uint32_t i=0; i < props->Length(); i++) {
        Local<Value> key = props->Get(i);
        Local<Value> value = object->Get(key);

        if (key->IsString()) {
          Nan::Utf8String keyStr(key->ToString());

          if (strcmp("checkServerIdentity", (const char*)(*keyStr)) == 0) {

            if (!value->IsFunction()) {
                return Nan::ThrowError("Value of checkServerIdentity must be a function.");
            }
            Nan::Callback *callback = new Callback(Local<Function>::Cast(value));
            verify_options.verify_peer_callback = verify_peer_callback_wrapper;
            verify_options.verify_peer_callback_userdata = (void*)callback;
            verify_options.verify_peer_destruct = verify_peer_callback_destruct;

          }
        }
        // do stuff with key / value
      }
    }
  }

  grpc_channel_credentials *creds = grpc_ssl_credentials_create(
      root_certs.get(), private_key.isAssigned() ? &key_cert_pair : NULL,
      &verify_options, NULL);
  if (creds == NULL) {
    info.GetReturnValue().SetNull();
  } else {
    info.GetReturnValue().Set(WrapStruct(creds));
  }
}

NAN_METHOD(ChannelCredentials::Compose) {
  if (!ChannelCredentials::HasInstance(info.This())) {
    return Nan::ThrowTypeError(
        "compose can only be called on ChannelCredentials objects");
  }
  if (!CallCredentials::HasInstance(info[0])) {
    return Nan::ThrowTypeError(
        "compose's first argument must be a CallCredentials object");
  }
  ChannelCredentials *self =
      ObjectWrap::Unwrap<ChannelCredentials>(info.This());
  if (self->wrapped_credentials == NULL) {
    return Nan::ThrowTypeError("Cannot compose insecure credential");
  }
  CallCredentials *other = ObjectWrap::Unwrap<CallCredentials>(
      Nan::To<Object>(info[0]).ToLocalChecked());
  grpc_channel_credentials *creds = grpc_composite_channel_credentials_create(
      self->wrapped_credentials, other->GetWrappedCredentials(), NULL);
  if (creds == NULL) {
    info.GetReturnValue().SetNull();
  } else {
    info.GetReturnValue().Set(WrapStruct(creds));
  }
}

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

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


<commit_before>/**
 * This file is part of the CernVM File System.
 *
 * Runs mount/umount commands and removes scratch space on behalf of a
 * repository owner.  Server-only utility.
 *
 * This binary does not use the cvmfs infrastructure code to stay lean.
 */

#include <sys/xattr.h>  // NOLINT

#include <dirent.h>
#include <errno.h>
#include <sys/stat.h>
#include <unistd.h>

#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <string>

#include "platform.h"
#include "sanitizer.h"

using namespace std;  // NOLINT

const char *kSpoolArea = "/var/spool/cvmfs";

enum RemountType {
  kRemountRdonly,
  kRemountRw
};

static void GetCredentials(uid_t *calling_uid, uid_t *effective_uid) {
  *calling_uid = getuid();
  *effective_uid = geteuid();
}

static void ExecAsRoot(const char *binary,
                       const char *arg1, const char *arg2, const char *arg3)
{
  char *argv[] = { strdup(binary),
                   arg1 ? strdup(arg1) : NULL,
                   arg2 ? strdup(arg2) : NULL,
                   arg3 ? strdup(arg3) : NULL,
                   NULL };
  char *environ[] = { NULL };

  int retval = setuid(0);
  if (retval != 0) {
    fprintf(stderr, "failed to gain root privileges (%d)\n", errno);
    exit(1);
  }

  execve(binary, argv, environ);
  fprintf(stderr, "failed to run %s... (%d)\n", binary, errno);
  exit(1);
}

static void Remount(const string &path, const RemountType how) {
  string remount_option = "remount,";
  switch (how) {
    case kRemountRw:
      remount_option += "rw";
      break;
    case kRemountRdonly:
      remount_option += "ro";
      break;
    default:
      fprintf(stderr, "internal error\n");
      exit(1);
  }

  ExecAsRoot("/bin/mount", "-o", remount_option.c_str(), path.c_str());
}

static void Mount(const string &path) {
  ExecAsRoot("/bin/mount", path.c_str(), NULL, NULL);
}

static void Umount(const string &path) {
  ExecAsRoot("/bin/umount", path.c_str(), NULL, NULL);
}

static void LazyUmount(const string &path) {
  ExecAsRoot("/bin/umount", "-l", path.c_str(), NULL);
}

static void KillCvmfs(const string &fqrn) {
  // prevent exploitation like:
  // fqrn = ../../../../usr/home/file_with_xattr_user.pid
  if (fqrn.find("/") != string::npos || fqrn.find("\\") != string::npos) {
    exit(1);
  }
  string pid;
  const string mountpoint = string(kSpoolArea) + "/" + fqrn + "/rdonly";
  const bool retval = platform_getxattr(mountpoint.c_str(), "user.pid", &pid);
  if (!retval || pid.empty())
    exit(1);
  sanitizer::PositiveIntegerSanitizer pid_sanitizer;
  if (!pid_sanitizer.IsValid(pid))
    exit(1);
  ExecAsRoot("/bin/kill", "-9", pid.c_str(), NULL);
}

static bool ClearWorkingDir() {
  int retval;
  DIR *dirp = opendir(".");
  if (dirp == NULL)
    return false;
  platform_dirent64 *dirent;
  while ((dirent = platform_readdir(dirp)) != NULL) {
    if ((strcmp(dirent->d_name, ".") == 0) ||
        (strcmp(dirent->d_name, "..") == 0))
    {
      continue;
    }

    platform_stat64 info;
    retval = platform_lstat(dirent->d_name, &info);
    if (retval != 0) {
      closedir(dirp);
      return false;
    }

    if (S_ISDIR(info.st_mode)) {
      // Recursion
      retval = chdir(dirent->d_name);
      if (retval != 0) {
        closedir(dirp);
        return false;
      }
      retval = ClearWorkingDir();
      if (!retval) {
        closedir(dirp);
        return false;
      }
      retval = chdir("..");
      if (retval != 0) {
        closedir(dirp);
        return false;
      }
      retval = rmdir(dirent->d_name);
      if (retval != 0) {
        closedir(dirp);
        return false;
      }
    } else {
      retval = unlink(dirent->d_name);
      if (retval != 0) {
        closedir(dirp);
        return false;
      }
    }
  }
  closedir(dirp);
  return true;
}

static void Usage(const string &exe, FILE *output) {
  fprintf(output,
    "Usage: %s lock|open|rw_mount|rw_umount|rdonly_mount|rdonly_umount|"
      "clear_scratch|kill_cvmfs <fqrn>\n"
    "Example: %s rw_umount atlas.cern.ch\n"
    "This binary is typically called by cvmfs_server.\n",
    exe.c_str(), exe.c_str());
}

int main(int argc, char *argv[]) {
  umask(077);
  int retval;

  // Figure out real and effective uid
  uid_t calling_uid, effective_uid, repository_uid;
  GetCredentials(&calling_uid, &effective_uid);
  if (effective_uid != 0) {
    fprintf(stderr, "Needs to run as root\n");
    return 1;
  }

  // Arguments
  if (argc != 3) {
    Usage(argv[0], stderr);
    return 1;
  }
  const string command = argv[1];
  const string fqrn = argv[2];

  // Verify if repository exists
  platform_stat64 info;
  retval = platform_lstat((string(kSpoolArea) + "/" + fqrn).c_str(), &info);
  if (retval != 0) {
    fprintf(stderr, "unknown repository: %s\n", fqrn.c_str());
    return 1;
  }
  repository_uid = info.st_uid;

  // Verify if caller uid matches
  if ((calling_uid != 0) && (calling_uid != repository_uid)) {
    fprintf(stderr, "called as %d, repository owned by %d\n",
            calling_uid, repository_uid);
    return 1;
  }

  if (command == "lock") {
    Remount("/cvmfs/" + fqrn, kRemountRdonly);
  } else if (command == "open") {
    Remount("/cvmfs/" + fqrn, kRemountRw);
  } else if (command == "kill_cvmfs") {
    KillCvmfs(fqrn);
  } else if (command == "rw_mount") {
    Mount("/cvmfs/" + fqrn);
  } else if (command == "rw_umount") {
    Umount("/cvmfs/" + fqrn);
  } else if (command == "rw_lazy_umount") {
    LazyUmount("/cvmfs/" + fqrn);
  } else if (command == "rdonly_mount") {
    Mount(string(kSpoolArea) + "/" + fqrn + "/rdonly");
  } else if (command == "rdonly_umount") {
    Umount(string(kSpoolArea) + "/" + fqrn + "/rdonly");
  } else if (command == "rdonly_lazy_umount") {
    LazyUmount(string(kSpoolArea) + "/" + fqrn + "/rdonly");
  } else if (command == "clear_scratch") {
    const string scratch_area = string(kSpoolArea) + "/" + fqrn + "/scratch";
    retval = chdir(scratch_area.c_str());
    if (retval != 0) {
      fprintf(stderr, "failed to chdir to %s\n", scratch_area.c_str());
      return 1;
    }
    retval = ClearWorkingDir();
    if (!retval) {
      fprintf(stderr, "failed to clear %s\n", scratch_area.c_str());
      return 1;
    }
  } else {
    Usage(argv[0], stderr);
    return 1;
  }

  return 0;
}
<commit_msg>Refactor: generalise directory cleaning<commit_after>/**
 * This file is part of the CernVM File System.
 *
 * Runs mount/umount commands and removes scratch space on behalf of a
 * repository owner.  Server-only utility.
 *
 * This binary does not use the cvmfs infrastructure code to stay lean.
 */

#include <sys/xattr.h>  // NOLINT

#include <dirent.h>
#include <errno.h>
#include <sys/stat.h>
#include <unistd.h>

#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <string>

#include "platform.h"
#include "sanitizer.h"

using namespace std;  // NOLINT

const char *kSpoolArea = "/var/spool/cvmfs";

enum RemountType {
  kRemountRdonly,
  kRemountRw
};

static void GetCredentials(uid_t *calling_uid, uid_t *effective_uid) {
  *calling_uid = getuid();
  *effective_uid = geteuid();
}

static void ExecAsRoot(const char *binary,
                       const char *arg1, const char *arg2, const char *arg3)
{
  char *argv[] = { strdup(binary),
                   arg1 ? strdup(arg1) : NULL,
                   arg2 ? strdup(arg2) : NULL,
                   arg3 ? strdup(arg3) : NULL,
                   NULL };
  char *environ[] = { NULL };

  int retval = setuid(0);
  if (retval != 0) {
    fprintf(stderr, "failed to gain root privileges (%d)\n", errno);
    exit(1);
  }

  execve(binary, argv, environ);
  fprintf(stderr, "failed to run %s... (%d)\n", binary, errno);
  exit(1);
}

static void Remount(const string &path, const RemountType how) {
  string remount_option = "remount,";
  switch (how) {
    case kRemountRw:
      remount_option += "rw";
      break;
    case kRemountRdonly:
      remount_option += "ro";
      break;
    default:
      fprintf(stderr, "internal error\n");
      exit(1);
  }

  ExecAsRoot("/bin/mount", "-o", remount_option.c_str(), path.c_str());
}

static void Mount(const string &path) {
  ExecAsRoot("/bin/mount", path.c_str(), NULL, NULL);
}

static void Umount(const string &path) {
  ExecAsRoot("/bin/umount", path.c_str(), NULL, NULL);
}

static void LazyUmount(const string &path) {
  ExecAsRoot("/bin/umount", "-l", path.c_str(), NULL);
}

static void KillCvmfs(const string &fqrn) {
  // prevent exploitation like:
  // fqrn = ../../../../usr/home/file_with_xattr_user.pid
  if (fqrn.find("/") != string::npos || fqrn.find("\\") != string::npos) {
    exit(1);
  }
  string pid;
  const string mountpoint = string(kSpoolArea) + "/" + fqrn + "/rdonly";
  const bool retval = platform_getxattr(mountpoint.c_str(), "user.pid", &pid);
  if (!retval || pid.empty())
    exit(1);
  sanitizer::PositiveIntegerSanitizer pid_sanitizer;
  if (!pid_sanitizer.IsValid(pid))
    exit(1);
  ExecAsRoot("/bin/kill", "-9", pid.c_str(), NULL);
}

class ScopedWorkingDirectory {
 public:
  ScopedWorkingDirectory(const string &path)
    : previous_path_(GetCurrentWorkingDirectory())
    , directory_handle_(NULL)
  {
    ChangeDirectory(path);
    directory_handle_ = opendir(".");
  }

  ~ScopedWorkingDirectory() {
    if (directory_handle_ != NULL) {
      closedir(directory_handle_);
    }
    ChangeDirectory(previous_path_);
  }

  operator bool() const { return directory_handle_ != NULL; }

  struct DirectoryEntry {
    bool   is_directory;
    string name;
  };

  bool NextDirectoryEntry(DirectoryEntry *entry) {
    platform_dirent64 *dirent;
    while ((dirent = platform_readdir(directory_handle_)) != NULL &&
           IsMetaEntry(dirent));
    if (dirent == NULL) {
      return false;
    }

    platform_stat64 info;
    if (platform_lstat(dirent->d_name, &info) != 0) {
      return false;
    }

    entry->is_directory = S_ISDIR(info.st_mode);
    entry->name         = dirent->d_name;
    return true;
  }

 protected:
  string GetCurrentWorkingDirectory() {
    char path[PATH_MAX];
    const char* cwd = getcwd(path, PATH_MAX);
    assert(cwd == path);
    return string(cwd);
  }

  void ChangeDirectory(const string &path) {
    const int retval = chdir(path.c_str());
    assert(retval == 0);
  }

  bool IsMetaEntry(const platform_dirent64 *dirent) {
    return (strcmp(dirent->d_name, ".")  == 0) ||
           (strcmp(dirent->d_name, "..") == 0);
  }

 private:
  const string  previous_path_;
        DIR    *directory_handle_;
};

static bool ClearDirectory(const string &path) {
  ScopedWorkingDirectory swd(path);
  if (!swd) {
    return false;
  }

  bool success = true;
  ScopedWorkingDirectory::DirectoryEntry dirent;
  while (success && swd.NextDirectoryEntry(&dirent)) {
    success = (dirent.is_directory)
      ? ClearDirectory(dirent.name) && (rmdir(dirent.name.c_str()) == 0)
      : (unlink(dirent.name.c_str()) == 0);
  }

  return success;
}

static int CleanupDirectory(const string &path) {
  if (!ClearDirectory(path)) {
    fprintf(stderr, "failed to clear %s\n", path.c_str());
    return 1;
  }
  return 0;
}

static int DoSynchronousScratchCleanup(const string &fqrn) {
  const string scratch = string(kSpoolArea) + "/" + fqrn + "/scratch/current";
  return CleanupDirectory(scratch);
}

static void Usage(const string &exe, FILE *output) {
  fprintf(output,
    "Usage: %s lock|open|rw_mount|rw_umount|rdonly_mount|rdonly_umount|"
      "clear_scratch|kill_cvmfs <fqrn>\n"
    "Example: %s rw_umount atlas.cern.ch\n"
    "This binary is typically called by cvmfs_server.\n",
    exe.c_str(), exe.c_str());
}

int main(int argc, char *argv[]) {
  umask(077);
  int retval;

  // Figure out real and effective uid
  uid_t calling_uid, effective_uid, repository_uid;
  GetCredentials(&calling_uid, &effective_uid);
  if (effective_uid != 0) {
    fprintf(stderr, "Needs to run as root\n");
    return 1;
  }

  // Arguments
  if (argc != 3) {
    Usage(argv[0], stderr);
    return 1;
  }
  const string command = argv[1];
  const string fqrn = argv[2];

  // Verify if repository exists
  platform_stat64 info;
  retval = platform_lstat((string(kSpoolArea) + "/" + fqrn).c_str(), &info);
  if (retval != 0) {
    fprintf(stderr, "unknown repository: %s\n", fqrn.c_str());
    return 1;
  }
  repository_uid = info.st_uid;

  // Verify if caller uid matches
  if ((calling_uid != 0) && (calling_uid != repository_uid)) {
    fprintf(stderr, "called as %d, repository owned by %d\n",
            calling_uid, repository_uid);
    return 1;
  }

  if (command == "lock") {
    Remount("/cvmfs/" + fqrn, kRemountRdonly);
  } else if (command == "open") {
    Remount("/cvmfs/" + fqrn, kRemountRw);
  } else if (command == "kill_cvmfs") {
    KillCvmfs(fqrn);
  } else if (command == "rw_mount") {
    Mount("/cvmfs/" + fqrn);
  } else if (command == "rw_umount") {
    Umount("/cvmfs/" + fqrn);
  } else if (command == "rw_lazy_umount") {
    LazyUmount("/cvmfs/" + fqrn);
  } else if (command == "rdonly_mount") {
    Mount(string(kSpoolArea) + "/" + fqrn + "/rdonly");
  } else if (command == "rdonly_umount") {
    Umount(string(kSpoolArea) + "/" + fqrn + "/rdonly");
  } else if (command == "rdonly_lazy_umount") {
    LazyUmount(string(kSpoolArea) + "/" + fqrn + "/rdonly");
  } else if (command == "clear_scratch") {
    return DoSynchronousScratchCleanup(fqrn);
  } else {
    Usage(argv[0], stderr);
    return 1;
  }

  return 0;
}
<|endoftext|>


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

namespace detail
{
    template<
        typename ForwardIterator,
        typename StrictWeakOrdering,
        typename Projection
    >
    auto bubble_sort(ForwardIterator first, StrictWeakOrdering compare,
                     Projection projection, std::size_t size)
        -> void
    {
        if (size < 2) return;

        auto&& proj = cppsort::utility::as_function(projection);

        while (--size)
        {
            ForwardIterator current = first;
            ForwardIterator next = std::next(current);
            for (std::size_t i = 0 ; i < size ; ++i)
            {
                if (compare(proj(*next), proj(*current)))
                {
                    std::iter_swap(current, next);
                }
                ++next;
                ++current;
            }
        }
    }

    struct bubble_sorter_impl
    {
        // Pair of iterators overload
        template<
            typename ForwardIterator,
            typename StrictWeakOrdering = std::less<>,
            typename Projection = cppsort::utility::identity,
            typename = std::enable_if_t<cppsort::is_projection_iterator<
                Projection, ForwardIterator, StrictWeakOrdering
            >>
        >
        auto operator()(ForwardIterator first, ForwardIterator last,
                        StrictWeakOrdering compare={}, Projection projection={}) const
            -> void
        {
            bubble_sort(first,
                        compare,
                        projection,
                        std::distance(first, last));
        }

        // Iterable overload
        template<
            typename ForwardIterable,
            typename StrictWeakOrdering = std::less<>,
            typename Projection = cppsort::utility::identity,
            typename = std::enable_if_t<cppsort::is_projection<
                Projection, ForwardIterable, StrictWeakOrdering
            >>
        >
        auto operator()(ForwardIterable& iterable, StrictWeakOrdering compare={},
                        Projection projection={}) const
            -> void
        {
            bubble_sort(std::begin(iterable),
                        compare,
                        projection,
                        cppsort::utility::size(iterable));
        }
    };
}

struct bubble_sorter:
    cppsort::sorter_facade<detail::bubble_sorter_impl>
{};

namespace cppsort
{
    template<>
    struct sorter_traits<::bubble_sorter>
    {
        using iterator_category = std::forward_iterator_tag;
        static constexpr bool is_stable = true;
    };
}

#include <array>
#include <cassert>
#include <numeric>
#include <cpp-sort/sort.h>

int main()
{
    // Check that the bubble_sorter can sort any permutation
    // of an array of 8 values

    // Fill the collection in sorted order
    std::array<int, 8> collection;
    std::iota(std::begin(collection), std::end(collection), 0);

    // Projection to sort in descending order
    auto projection = [](int n) { return -n; };

    // For each possible permutation of collection
    do
    {
        auto to_sort = collection;
        // Bubble sort the collection
        cppsort::sort(to_sort, bubble_sorter{}, projection);
        // Check that it is indeed sorted
        assert(std::is_sorted(std::begin(to_sort), std::end(to_sort), std::greater<>{}));
    } while (std::next_permutation(std::begin(collection), std::end(collection)));
}
<commit_msg>Enhance bubble_sorter with readable error messages.<commit_after>/*
 * The MIT License (MIT)
 *
 * Copyright (c) 2015 Morwenn
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 */
#include <algorithm>
#include <cstddef>
#include <functional>
#include <iterator>
#include <type_traits>
#include <cpp-sort/sorter_facade.h>
#include <cpp-sort/sorter_traits.h>
#include <cpp-sort/utility/as_function.h>
#include <cpp-sort/utility/identity.h>
#include <cpp-sort/utility/size.h>

namespace detail
{
    template<
        typename ForwardIterator,
        typename StrictWeakOrdering,
        typename Projection
    >
    auto bubble_sort(ForwardIterator first, StrictWeakOrdering compare,
                     Projection projection, std::size_t size)
        -> void
    {
        if (size < 2) return;

        auto&& proj = cppsort::utility::as_function(projection);

        while (--size)
        {
            ForwardIterator current = first;
            ForwardIterator next = std::next(current);
            for (std::size_t i = 0 ; i < size ; ++i)
            {
                if (compare(proj(*next), proj(*current)))
                {
                    std::iter_swap(current, next);
                }
                ++next;
                ++current;
            }
        }
    }

    struct bubble_sorter_impl
    {
        // Pair of iterators overload
        template<
            typename ForwardIterator,
            typename StrictWeakOrdering = std::less<>,
            typename Projection = cppsort::utility::identity,
            typename = std::enable_if_t<cppsort::is_projection_iterator<
                Projection, ForwardIterator, StrictWeakOrdering
            >>
        >
        auto operator()(ForwardIterator first, ForwardIterator last,
                        StrictWeakOrdering compare={}, Projection projection={}) const
            -> void
        {
            static_assert(
                std::is_base_of<
                    std::forward_iterator_tag,
                    typename std::iterator_traits<ForwardIterator>::iterator_category
                >::value,
                "bubble_sorter requires at least forward iterators"
            );

            bubble_sort(first,
                        compare, projection,
                        std::distance(first, last));
        }

        // Iterable overload
        template<
            typename ForwardIterable,
            typename StrictWeakOrdering = std::less<>,
            typename Projection = cppsort::utility::identity,
            typename = std::enable_if_t<cppsort::is_projection<
                Projection, ForwardIterable, StrictWeakOrdering
            >>
        >
        auto operator()(ForwardIterable& iterable, StrictWeakOrdering compare={},
                        Projection projection={}) const
            -> void
        {
            static_assert(
                std::is_base_of<
                    std::forward_iterator_tag,
                    typename std::iterator_traits<decltype(std::begin(iterable))>::iterator_category
                >::value,
                "bubble_sorter requires at least forward iterators"
            );

            bubble_sort(std::begin(iterable),
                        compare, projection,
                        cppsort::utility::size(iterable));
        }
    };
}

struct bubble_sorter:
    cppsort::sorter_facade<detail::bubble_sorter_impl>
{};

namespace cppsort
{
    template<>
    struct sorter_traits<::bubble_sorter>
    {
        using iterator_category = std::forward_iterator_tag;
        static constexpr bool is_stable = true;
    };
}

#include <array>
#include <cassert>
#include <numeric>
#include <cpp-sort/sort.h>

int main()
{
    // Check that the bubble_sorter can sort any permutation
    // of an array of 8 values

    // Fill the collection in sorted order
    std::array<int, 8> collection;
    std::iota(std::begin(collection), std::end(collection), 0);

    // Projection to sort in descending order
    auto projection = [](int n) { return -n; };

    // For each possible permutation of collection
    do
    {
        auto to_sort = collection;
        // Bubble sort the collection
        cppsort::sort(to_sort, bubble_sorter{}, projection);
        // Check that it is indeed sorted
        assert(std::is_sorted(std::begin(to_sort), std::end(to_sort), std::greater<>{}));
    } while (std::next_permutation(std::begin(collection), std::end(collection)));
}
<|endoftext|>