<commit_before>// Licensed to the Apache Software Foundation (ASF) under one
// or more contributor license agreements.  See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership.  The ASF licenses this file
// to you 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 "benchmark/benchmark.h"

#include <vector>

#include "arrow/builder.h"
#include "arrow/compute/api.h"
#include "arrow/memory_pool.h"
#include "arrow/testing/gtest_util.h"
#include "arrow/testing/random.h"
#include "arrow/util/benchmark_util.h"
#include "arrow/util/bit_util.h"

namespace arrow {
namespace compute {

#include <cassert>
#include <cmath>
#include <iostream>
#include <random>

#ifdef ARROW_WITH_BENCHMARKS_REFERENCE

namespace BitUtil = arrow::BitUtil;
using arrow::internal::BitmapReader;

template <typename T>
struct SumState {
  using ValueType = T;

  SumState() : total(0), valid_count(0) {}

  T total = 0;
  int64_t valid_count = 0;
};

template <typename T>
struct Traits {};

template <>
struct Traits<int64_t> {
  using ArrayType = typename CTypeTraits<int64_t>::ArrayType;
  static constexpr int64_t null_sentinel = std::numeric_limits<int64_t>::lowest();

  static void FixSentinel(std::shared_ptr<ArrayType>& array) {
    auto data = array->data();
    for (int64_t i = 0; i < array->length(); i++)
      if (array->IsNull(i)) {
        int64_t* val_ptr = data->GetMutableValues<int64_t>(1, i);
        *val_ptr = null_sentinel;
      }
  }

  static inline bool IsNull(int64_t val) { return val == null_sentinel; }

  static inline bool NotNull(int64_t val) { return val != null_sentinel; }
};

template <typename T>
struct Summer {
 public:
  using ValueType = T;
  using ArrowType = typename CTypeTraits<T>::ArrowType;
};

template <typename T>
struct SumNoNulls : public Summer<T> {
  using ArrayType = typename CTypeTraits<T>::ArrayType;

  static void Sum(const ArrayType& array, SumState<T>* state) {
    SumState<T> local;

    const auto values = array.raw_values();
    for (int64_t i = 0; i < array.length(); ++i) {
      local.total += values[i];
    }

    local.valid_count = array.length();
    *state = local;
  }
};

template <typename T>
struct SumNoNullsUnrolled : public Summer<T> {
  using ArrayType = typename CTypeTraits<T>::ArrayType;

  static void Sum(const ArrayType& array, SumState<T>* state) {
    SumState<T> local;

    const auto values = array.raw_values();
    const auto length = array.length();
    const int64_t length_rounded = BitUtil::RoundDown(length, 8);
    for (int64_t i = 0; i < length_rounded; i += 8) {
      local.total += values[i + 0] + values[i + 1] + values[i + 2] + values[i + 3] +
                     values[i + 4] + values[i + 5] + values[i + 6] + values[i + 7];
    }

    for (int64_t i = length_rounded; i < length; ++i) {
      local.total += values[i];
    }

    local.valid_count = length;

    *state = local;
  }
};

template <typename T>
struct SumSentinel : public Summer<T> {
  using ArrayType = typename CTypeTraits<T>::ArrayType;

  static void Sum(const ArrayType& array, SumState<T>* state) {
    SumState<T> local;

    const auto values = array.raw_values();
    const auto length = array.length();
    for (int64_t i = 0; i < length; i++) {
      // NaN is not equal to itself
      local.total += values[i] * Traits<T>::NotNull(values[i]);
      local.valid_count++;
    }

    *state = local;
  }
};

template <typename T>
struct SumSentinelUnrolled : public Summer<T> {
  using ArrayType = typename CTypeTraits<T>::ArrayType;

  static void Sum(const ArrayType& array, SumState<T>* state) {
    SumState<T> local;

#define SUM_NOT_NULL(ITEM)                                                  \
  do {                                                                      \
    local.total += values[i + ITEM] * Traits<T>::NotNull(values[i + ITEM]); \
    local.valid_count++;                                                    \
  } while (0)

    const auto values = array.raw_values();
    const auto length = array.length();
    const int64_t length_rounded = BitUtil::RoundDown(length, 8);
    for (int64_t i = 0; i < length_rounded; i += 8) {
      SUM_NOT_NULL(0);
      SUM_NOT_NULL(1);
      SUM_NOT_NULL(2);
      SUM_NOT_NULL(3);
      SUM_NOT_NULL(4);
      SUM_NOT_NULL(5);
      SUM_NOT_NULL(6);
      SUM_NOT_NULL(7);
    }

#undef SUM_NOT_NULL

    for (int64_t i = length_rounded * 8; i < length; ++i) {
      local.total += values[i] * Traits<T>::NotNull(values[i]);
      ++local.valid_count;
    }

    *state = local;
  }
};

template <typename T>
struct SumBitmapNaive : public Summer<T> {
  using ArrayType = typename CTypeTraits<T>::ArrayType;

  static void Sum(const ArrayType& array, SumState<T>* state) {
    SumState<T> local;

    const auto values = array.raw_values();
    const auto bitmap = array.null_bitmap_data();
    const auto length = array.length();

    for (int64_t i = 0; i < length; ++i) {
      if (BitUtil::GetBit(bitmap, i)) {
        local.total += values[i];
        ++local.valid_count;
      }
    }

    *state = local;
  }
};

template <typename T>
struct SumBitmapReader : public Summer<T> {
  using ArrayType = typename CTypeTraits<T>::ArrayType;

  static void Sum(const ArrayType& array, SumState<T>* state) {
    SumState<T> local;

    const auto values = array.raw_values();
    const auto bitmap = array.null_bitmap_data();
    const auto length = array.length();
    BitmapReader bit_reader(bitmap, 0, length);
    for (int64_t i = 0; i < length; ++i) {
      if (bit_reader.IsSet()) {
        local.total += values[i];
        ++local.valid_count;
      }

      bit_reader.Next();
    }

    *state = local;
  }
};

template <typename T>
struct SumBitmapVectorizeUnroll : public Summer<T> {
  using ArrayType = typename CTypeTraits<T>::ArrayType;

  static void Sum(const ArrayType& array, SumState<T>* state) {
    SumState<T> local;

    const auto values = array.raw_values();
    const auto bitmap = array.null_bitmap_data();
    const auto length = array.length();
    const int64_t length_rounded = BitUtil::RoundDown(length, 8);
    for (int64_t i = 0; i < length_rounded; i += 8) {
      const uint8_t valid_byte = bitmap[i / 8];

#define SUM_SHIFT(ITEM) (values[i + ITEM] * ((valid_byte >> ITEM) & 1))

      if (valid_byte < 0xFF) {
        // Some nulls
        local.total += SUM_SHIFT(0);
        local.total += SUM_SHIFT(1);
        local.total += SUM_SHIFT(2);
        local.total += SUM_SHIFT(3);
        local.total += SUM_SHIFT(4);
        local.total += SUM_SHIFT(5);
        local.total += SUM_SHIFT(6);
        local.total += SUM_SHIFT(7);
        local.valid_count += BitUtil::kBytePopcount[valid_byte];
      } else {
        // No nulls
        local.total += values[i + 0] + values[i + 1] + values[i + 2] + values[i + 3] +
                       values[i + 4] + values[i + 5] + values[i + 6] + values[i + 7];
        local.valid_count += 8;
      }
    }

#undef SUM_SHIFT

    for (int64_t i = length_rounded; i < length; ++i) {
      if (BitUtil::GetBit(bitmap, i)) {
        local.total = values[i];
        ++local.valid_count;
      }
    }

    *state = local;
  }
};

template <typename Functor>
void ReferenceSum(benchmark::State& state) {
  using T = typename Functor::ValueType;

  RegressionArgs args(state);
  const int64_t array_size = args.size / sizeof(int64_t);
  auto rand = random::RandomArrayGenerator(1923);
  auto array = std::static_pointer_cast<NumericArray<Int64Type>>(
      rand.Int64(array_size, -100, 100, args.null_proportion));

  Traits<T>::FixSentinel(array);

  for (auto _ : state) {
    SumState<T> sum_state;
    Functor::Sum(*array, &sum_state);
    benchmark::DoNotOptimize(sum_state);
  }
}

BENCHMARK_TEMPLATE(ReferenceSum, SumNoNulls<int64_t>)->Apply(BenchmarkSetArgs);
BENCHMARK_TEMPLATE(ReferenceSum, SumNoNullsUnrolled<int64_t>)->Apply(BenchmarkSetArgs);
BENCHMARK_TEMPLATE(ReferenceSum, SumSentinel<int64_t>)->Apply(BenchmarkSetArgs);
BENCHMARK_TEMPLATE(ReferenceSum, SumSentinelUnrolled<int64_t>)->Apply(BenchmarkSetArgs);
BENCHMARK_TEMPLATE(ReferenceSum, SumBitmapNaive<int64_t>)->Apply(BenchmarkSetArgs);
BENCHMARK_TEMPLATE(ReferenceSum, SumBitmapReader<int64_t>)->Apply(BenchmarkSetArgs);
BENCHMARK_TEMPLATE(ReferenceSum, SumBitmapVectorizeUnroll<int64_t>)
    ->Apply(BenchmarkSetArgs);
#endif  // ARROW_WITH_BENCHMARKS_REFERENCE

template <typename ArrowType>
static void SumKernel(benchmark::State& state) {
  using CType = typename TypeTraits<ArrowType>::CType;

  RegressionArgs args(state);
  const int64_t array_size = args.size / sizeof(CType);
  auto rand = random::RandomArrayGenerator(1923);
  auto array = rand.Numeric<ArrowType>(array_size, -100, 100, args.null_proportion);

  for (auto _ : state) {
    ABORT_NOT_OK(Sum(array).status());
  }
}

#define SUM_KERNEL_BENCHMARK(FuncName, Type)                                \
  static void FuncName(benchmark::State& state) { SumKernel<Type>(state); } \
  BENCHMARK(FuncName)->Apply(RegressionSetArgs)

SUM_KERNEL_BENCHMARK(SumKernelFloat, FloatType);
SUM_KERNEL_BENCHMARK(SumKernelDouble, DoubleType);
SUM_KERNEL_BENCHMARK(SumKernelInt8, Int8Type);
SUM_KERNEL_BENCHMARK(SumKernelInt16, Int16Type);
SUM_KERNEL_BENCHMARK(SumKernelInt32, Int32Type);
SUM_KERNEL_BENCHMARK(SumKernelInt64, Int64Type);

}  // namespace compute
}  // namespace arrow
<commit_msg>ARROW-9263: [C++] Promote compute aggregate benchmark size to 1M.<commit_after>// Licensed to the Apache Software Foundation (ASF) under one
// or more contributor license agreements.  See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership.  The ASF licenses this file
// to you 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 "benchmark/benchmark.h"

#include <vector>

#include "arrow/builder.h"
#include "arrow/compute/api.h"
#include "arrow/memory_pool.h"
#include "arrow/testing/gtest_util.h"
#include "arrow/testing/random.h"
#include "arrow/util/benchmark_util.h"
#include "arrow/util/bit_util.h"

namespace arrow {
namespace compute {

#include <cassert>
#include <cmath>
#include <iostream>
#include <random>

#ifdef ARROW_WITH_BENCHMARKS_REFERENCE

namespace BitUtil = arrow::BitUtil;
using arrow::internal::BitmapReader;

template <typename T>
struct SumState {
  using ValueType = T;

  SumState() : total(0), valid_count(0) {}

  T total = 0;
  int64_t valid_count = 0;
};

template <typename T>
struct Traits {};

template <>
struct Traits<int64_t> {
  using ArrayType = typename CTypeTraits<int64_t>::ArrayType;
  static constexpr int64_t null_sentinel = std::numeric_limits<int64_t>::lowest();

  static void FixSentinel(std::shared_ptr<ArrayType>& array) {
    auto data = array->data();
    for (int64_t i = 0; i < array->length(); i++)
      if (array->IsNull(i)) {
        int64_t* val_ptr = data->GetMutableValues<int64_t>(1, i);
        *val_ptr = null_sentinel;
      }
  }

  static inline bool IsNull(int64_t val) { return val == null_sentinel; }

  static inline bool NotNull(int64_t val) { return val != null_sentinel; }
};

template <typename T>
struct Summer {
 public:
  using ValueType = T;
  using ArrowType = typename CTypeTraits<T>::ArrowType;
};

template <typename T>
struct SumNoNulls : public Summer<T> {
  using ArrayType = typename CTypeTraits<T>::ArrayType;

  static void Sum(const ArrayType& array, SumState<T>* state) {
    SumState<T> local;

    const auto values = array.raw_values();
    for (int64_t i = 0; i < array.length(); ++i) {
      local.total += values[i];
    }

    local.valid_count = array.length();
    *state = local;
  }
};

template <typename T>
struct SumNoNullsUnrolled : public Summer<T> {
  using ArrayType = typename CTypeTraits<T>::ArrayType;

  static void Sum(const ArrayType& array, SumState<T>* state) {
    SumState<T> local;

    const auto values = array.raw_values();
    const auto length = array.length();
    const int64_t length_rounded = BitUtil::RoundDown(length, 8);
    for (int64_t i = 0; i < length_rounded; i += 8) {
      local.total += values[i + 0] + values[i + 1] + values[i + 2] + values[i + 3] +
                     values[i + 4] + values[i + 5] + values[i + 6] + values[i + 7];
    }

    for (int64_t i = length_rounded; i < length; ++i) {
      local.total += values[i];
    }

    local.valid_count = length;

    *state = local;
  }
};

template <typename T>
struct SumSentinel : public Summer<T> {
  using ArrayType = typename CTypeTraits<T>::ArrayType;

  static void Sum(const ArrayType& array, SumState<T>* state) {
    SumState<T> local;

    const auto values = array.raw_values();
    const auto length = array.length();
    for (int64_t i = 0; i < length; i++) {
      // NaN is not equal to itself
      local.total += values[i] * Traits<T>::NotNull(values[i]);
      local.valid_count++;
    }

    *state = local;
  }
};

template <typename T>
struct SumSentinelUnrolled : public Summer<T> {
  using ArrayType = typename CTypeTraits<T>::ArrayType;

  static void Sum(const ArrayType& array, SumState<T>* state) {
    SumState<T> local;

#define SUM_NOT_NULL(ITEM)                                                  \
  do {                                                                      \
    local.total += values[i + ITEM] * Traits<T>::NotNull(values[i + ITEM]); \
    local.valid_count++;                                                    \
  } while (0)

    const auto values = array.raw_values();
    const auto length = array.length();
    const int64_t length_rounded = BitUtil::RoundDown(length, 8);
    for (int64_t i = 0; i < length_rounded; i += 8) {
      SUM_NOT_NULL(0);
      SUM_NOT_NULL(1);
      SUM_NOT_NULL(2);
      SUM_NOT_NULL(3);
      SUM_NOT_NULL(4);
      SUM_NOT_NULL(5);
      SUM_NOT_NULL(6);
      SUM_NOT_NULL(7);
    }

#undef SUM_NOT_NULL

    for (int64_t i = length_rounded * 8; i < length; ++i) {
      local.total += values[i] * Traits<T>::NotNull(values[i]);
      ++local.valid_count;
    }

    *state = local;
  }
};

template <typename T>
struct SumBitmapNaive : public Summer<T> {
  using ArrayType = typename CTypeTraits<T>::ArrayType;

  static void Sum(const ArrayType& array, SumState<T>* state) {
    SumState<T> local;

    const auto values = array.raw_values();
    const auto bitmap = array.null_bitmap_data();
    const auto length = array.length();

    for (int64_t i = 0; i < length; ++i) {
      if (BitUtil::GetBit(bitmap, i)) {
        local.total += values[i];
        ++local.valid_count;
      }
    }

    *state = local;
  }
};

template <typename T>
struct SumBitmapReader : public Summer<T> {
  using ArrayType = typename CTypeTraits<T>::ArrayType;

  static void Sum(const ArrayType& array, SumState<T>* state) {
    SumState<T> local;

    const auto values = array.raw_values();
    const auto bitmap = array.null_bitmap_data();
    const auto length = array.length();
    BitmapReader bit_reader(bitmap, 0, length);
    for (int64_t i = 0; i < length; ++i) {
      if (bit_reader.IsSet()) {
        local.total += values[i];
        ++local.valid_count;
      }

      bit_reader.Next();
    }

    *state = local;
  }
};

template <typename T>
struct SumBitmapVectorizeUnroll : public Summer<T> {
  using ArrayType = typename CTypeTraits<T>::ArrayType;

  static void Sum(const ArrayType& array, SumState<T>* state) {
    SumState<T> local;

    const auto values = array.raw_values();
    const auto bitmap = array.null_bitmap_data();
    const auto length = array.length();
    const int64_t length_rounded = BitUtil::RoundDown(length, 8);
    for (int64_t i = 0; i < length_rounded; i += 8) {
      const uint8_t valid_byte = bitmap[i / 8];

#define SUM_SHIFT(ITEM) (values[i + ITEM] * ((valid_byte >> ITEM) & 1))

      if (valid_byte < 0xFF) {
        // Some nulls
        local.total += SUM_SHIFT(0);
        local.total += SUM_SHIFT(1);
        local.total += SUM_SHIFT(2);
        local.total += SUM_SHIFT(3);
        local.total += SUM_SHIFT(4);
        local.total += SUM_SHIFT(5);
        local.total += SUM_SHIFT(6);
        local.total += SUM_SHIFT(7);
        local.valid_count += BitUtil::kBytePopcount[valid_byte];
      } else {
        // No nulls
        local.total += values[i + 0] + values[i + 1] + values[i + 2] + values[i + 3] +
                       values[i + 4] + values[i + 5] + values[i + 6] + values[i + 7];
        local.valid_count += 8;
      }
    }

#undef SUM_SHIFT

    for (int64_t i = length_rounded; i < length; ++i) {
      if (BitUtil::GetBit(bitmap, i)) {
        local.total = values[i];
        ++local.valid_count;
      }
    }

    *state = local;
  }
};

template <typename Functor>
void ReferenceSum(benchmark::State& state) {
  using T = typename Functor::ValueType;

  RegressionArgs args(state);
  const int64_t array_size = args.size / sizeof(int64_t);
  auto rand = random::RandomArrayGenerator(1923);
  auto array = std::static_pointer_cast<NumericArray<Int64Type>>(
      rand.Int64(array_size, -100, 100, args.null_proportion));

  Traits<T>::FixSentinel(array);

  for (auto _ : state) {
    SumState<T> sum_state;
    Functor::Sum(*array, &sum_state);
    benchmark::DoNotOptimize(sum_state);
  }
}

BENCHMARK_TEMPLATE(ReferenceSum, SumNoNulls<int64_t>)->Apply(BenchmarkSetArgs);
BENCHMARK_TEMPLATE(ReferenceSum, SumNoNullsUnrolled<int64_t>)->Apply(BenchmarkSetArgs);
BENCHMARK_TEMPLATE(ReferenceSum, SumSentinel<int64_t>)->Apply(BenchmarkSetArgs);
BENCHMARK_TEMPLATE(ReferenceSum, SumSentinelUnrolled<int64_t>)->Apply(BenchmarkSetArgs);
BENCHMARK_TEMPLATE(ReferenceSum, SumBitmapNaive<int64_t>)->Apply(BenchmarkSetArgs);
BENCHMARK_TEMPLATE(ReferenceSum, SumBitmapReader<int64_t>)->Apply(BenchmarkSetArgs);
BENCHMARK_TEMPLATE(ReferenceSum, SumBitmapVectorizeUnroll<int64_t>)
    ->Apply(BenchmarkSetArgs);
#endif  // ARROW_WITH_BENCHMARKS_REFERENCE

template <typename ArrowType>
static void SumKernel(benchmark::State& state) {
  using CType = typename TypeTraits<ArrowType>::CType;

  RegressionArgs args(state);
  const int64_t array_size = args.size / sizeof(CType);
  auto rand = random::RandomArrayGenerator(1923);
  auto array = rand.Numeric<ArrowType>(array_size, -100, 100, args.null_proportion);

  for (auto _ : state) {
    ABORT_NOT_OK(Sum(array).status());
  }
}

static void SumKernelArgs(benchmark::internal::Benchmark* bench) {
  BenchmarkSetArgsWithSizes(bench, {1 * 1024 * 1024});  // 1M
}

#define SUM_KERNEL_BENCHMARK(FuncName, Type)                                \
  static void FuncName(benchmark::State& state) { SumKernel<Type>(state); } \
  BENCHMARK(FuncName)->Apply(SumKernelArgs)

SUM_KERNEL_BENCHMARK(SumKernelFloat, FloatType);
SUM_KERNEL_BENCHMARK(SumKernelDouble, DoubleType);
SUM_KERNEL_BENCHMARK(SumKernelInt8, Int8Type);
SUM_KERNEL_BENCHMARK(SumKernelInt16, Int16Type);
SUM_KERNEL_BENCHMARK(SumKernelInt32, Int32Type);
SUM_KERNEL_BENCHMARK(SumKernelInt64, Int64Type);

}  // namespace compute
}  // namespace arrow
<|endoftext|>


<commit_before>/*
 * Copyright 2014 Google Inc. All rights reserved.
 *
 * 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.
 */

// This file uses the Clang style conventions.

#include "GraphObserver.h"
#include "IndexerPPCallbacks.h"

#include "clang/Basic/SourceManager.h"
#include "clang/Basic/FileManager.h"
#include "clang/Lex/PPCallbacks.h"
#include "clang/Lex/Preprocessor.h"

#include "llvm/Support/raw_ostream.h"
#include "llvm/Support/Debug.h"

// TODO(zarko): IndexerASTHooks::RangeInCurrentContext should query the macro
// context. IndexerPPCallbacks doesn't need to query for the template context,
// since we'll always be in a state that looks like
//   /macro-expansion* template-inst*/,
// as template processing happens at a later stage.

// Embedding non-file source locations into vnames generates names that are
// not stable wrt include ordering (there appears to be a single scratch
// buffer with a line counter that is incrementally bumped).
// TODO(zarko): Fix this by using a hash of the scratch buffer + the file
// location into which the scratch buffer is being inlined. We might also be
// able to hook these locations to the macros that generate them, allowing us
// to insert edges for token pastes.

namespace kythe {

IndexerPPCallbacks::IndexerPPCallbacks(const clang::Preprocessor &PP,
                                       GraphObserver &GO)
    : Preprocessor(PP), Observer(GO) {}

IndexerPPCallbacks::~IndexerPPCallbacks() {}

void IndexerPPCallbacks::FileChanged(clang::SourceLocation Loc,
                                     PPCallbacks::FileChangeReason Reason,
                                     clang::SrcMgr::CharacteristicKind FileType,
                                     clang::FileID PrevFID) {
  switch (Reason) {
  case clang::PPCallbacks::EnterFile:
    Observer.pushFile(LastInclusionHash, Loc);
    break;
  case clang::PPCallbacks::ExitFile:
    Observer.popFile();
    break;
  case clang::PPCallbacks::SystemHeaderPragma:
    break;
  // RenameFile occurs when a #line directive is encountered, for example:
  // #line 10 "foo.cc"
  case clang::PPCallbacks::RenameFile:
    break;
  default:
    llvm::dbgs() << "Unknown FileChangeReason " << Reason << "\n";
  }
}

void IndexerPPCallbacks::FilterAndEmitDeferredRecords() {
  for (const auto &R : DeferredRecords) {
    if (R.WasDefined) {
      if (!R.Macro->getMacroInfo()->isUsedForHeaderGuard()) {
        Observer.recordBoundQueryRange(
            R.Range,
            BuildNodeIdForMacro(R.MacroName, *R.Macro->getMacroInfo()));
      }
    } else {
      Observer.recordUnboundQueryRange(R.Range,
                                       BuildNameIdForMacro(R.MacroName));
    }
  }
  DeferredRecords.clear();
}

void IndexerPPCallbacks::EndOfMainFile() {
  FilterAndEmitDeferredRecords();
  Observer.popFile();
}

GraphObserver::Range
IndexerPPCallbacks::RangeForTokenInCurrentContext(const clang::Token &Token) {
  const auto Start = Token.getLocation();
  const auto End = Start.getLocWithOffset(Token.getLength());
  return RangeInCurrentContext(clang::SourceRange(Start, End));
}

void IndexerPPCallbacks::MacroDefined(const clang::Token &Token,
                                      const clang::MacroDirective *Macro) {
  if (Macro == nullptr) {
    return;
  }
  const clang::MacroInfo &Info = *Macro->getMacroInfo();
  clang::FileID MacroFileID =
      Observer.getSourceManager()->getFileID(Info.getDefinitionLoc());
  const clang::FileEntry *MacroFileEntry =
      Observer.getSourceManager()->getFileEntryForID(MacroFileID);
  if (MacroFileEntry == nullptr) {
    // This is a builtin macro. Ignore it.
    return;
  }
  GraphObserver::NodeId MacroId = BuildNodeIdForMacro(Token, Info);
  if (Observer.claimNode(MacroId)) {
    GraphObserver::NameId MacroName = BuildNameIdForMacro(Token);
    Observer.recordDefinitionRange(RangeForTokenInCurrentContext(Token),
                                   MacroId);
    Observer.recordMacroNode(MacroId);
    Observer.recordNamedEdge(MacroId, MacroName);
  }
  // TODO(zarko): Record information about the definition (like other macro
  // references).
}

void IndexerPPCallbacks::MacroUndefined(const clang::Token &MacroName,
                                        const clang::MacroDirective *Macro) {
  if (Macro == nullptr) {
    return;
  }
  const clang::MacroInfo &Info = *Macro->getMacroInfo();
  GraphObserver::NodeId MacroId = BuildNodeIdForMacro(MacroName, Info);
  Observer.recordUndefinesRange(RangeForTokenInCurrentContext(MacroName),
                                MacroId);
}

void IndexerPPCallbacks::MacroExpands(const clang::Token &Token,
                                      const clang::MacroDirective *Macro,
                                      clang::SourceRange Range,
                                      const clang::MacroArgs *Args) {
  if (Macro == nullptr || Range.isInvalid()) {
    return;
  }

  const clang::MacroInfo &Info = *Macro->getMacroInfo();
  GraphObserver::NodeId MacroId = BuildNodeIdForMacro(Token, Info);
  if (!Range.getBegin().isFileID() || !Range.getEnd().isFileID()) {
    auto NewBegin =
        Observer.getSourceManager()->getExpansionLoc(Range.getBegin());
    if (!NewBegin.isFileID()) {
      return;
    }
    Range = clang::SourceRange(NewBegin,
                               clang::Lexer::getLocForEndOfToken(
                                   NewBegin, 0, /* offset from end of token */
                                   *Observer.getSourceManager(),
                                   *Observer.getLangOptions()));
    if (Range.isInvalid()) {
      return;
    }
    Observer.recordIndirectlyExpandsRange(RangeInCurrentContext(Range),
                                          MacroId);
  } else {
    Observer.recordExpandsRange(RangeForTokenInCurrentContext(Token), MacroId);
  }
  // TODO(zarko): Index macro arguments.
}

void IndexerPPCallbacks::Defined(const clang::Token &MacroName,
                                 const clang::MacroDirective *Macro,
                                 clang::SourceRange Range) {
  DeferredRecords.push_back(
      DeferredRecord{MacroName, Macro, Macro != nullptr && Macro->isDefined(),
                     RangeForTokenInCurrentContext(MacroName)});
}

void IndexerPPCallbacks::Ifdef(clang::SourceLocation Location,
                               const clang::Token &MacroName,
                               const clang::MacroDirective *Macro) {
  // Just delegate.
  Defined(MacroName, Macro, clang::SourceRange(Location));
}

void IndexerPPCallbacks::Ifndef(clang::SourceLocation Location,
                                const clang::Token &MacroName,
                                const clang::MacroDirective *Macro) {
  // Just delegate.
  Defined(MacroName, Macro, clang::SourceRange(Location));
}

void IndexerPPCallbacks::InclusionDirective(
    clang::SourceLocation HashLocation, const clang::Token &IncludeToken,
    llvm::StringRef Filename, bool IsAngled,
    clang::CharSourceRange FilenameRange, const clang::FileEntry *FileEntry,
    llvm::StringRef SearchPath, llvm::StringRef RelativePath,
    const clang::Module *Imported) {
  // TODO(zarko) (Modules): Check if `Imported` is non-null; if so, this
  // was transformed to a module import.
  if (FileEntry != nullptr) {
    Observer.recordIncludesRange(
        RangeInCurrentContext(FilenameRange.getAsRange()), FileEntry);
  }
  LastInclusionHash = HashLocation;
}

void IndexerPPCallbacks::AddMacroReferenceIfDefined(
    const clang::Token &MacroNameToken) {
  if (clang::IdentifierInfo *const NameII =
          MacroNameToken.getIdentifierInfo()) {
    if (NameII->hasMacroDefinition()) {
      if (auto *Macro = Preprocessor.getMacroDirective(NameII)) {
        AddReferenceToMacro(MacroNameToken, *Macro->getMacroInfo(),
                            Macro->isDefined());
      }
    } else if (NameII->hadMacroDefinition()) {
      if (auto *Macro = Preprocessor.getMacroDirectiveHistory(NameII)) {
        AddReferenceToMacro(MacroNameToken, *Macro->getMacroInfo(),
                            Macro->isDefined());
      }
    }
  } else {
    // This shouldn't happen; it's an error to do "defined TOKEN" unless
    // the preprocessor token is an identifier, in which case it should
    // have IdentifierInfo associated with it.  Note that Clang assigns
    // tok::tk_XXXX token types to preprocessing identifier tokens if
    // they have the names of tokens, rather than following the Standard's
    // formal phases of translation which would have them all be just
    // identifiers during preprocessing.
    assert(0 && "No IdentifierInfo in AddMacroReferenceIfDefined.");
  }
}

void IndexerPPCallbacks::AddReferenceToMacro(const clang::Token &MacroNameToken,
                                             clang::MacroInfo const &Info,
                                             bool IsDefined) {
  llvm::StringRef MacroName(MacroNameToken.getIdentifierInfo()->getName());
  Observer.recordExpandsRange(RangeForTokenInCurrentContext(MacroNameToken),
                              BuildNodeIdForMacro(MacroNameToken, Info));
}

GraphObserver::NameId
IndexerPPCallbacks::BuildNameIdForMacro(const clang::Token &Spelling) {
  assert(Spelling.getIdentifierInfo() && "Macro spelling lacks IdentifierInfo");
  GraphObserver::NameId Id;
  Id.EqClass = GraphObserver::NameId::NameEqClass::Macro;
  Id.Path = Spelling.getIdentifierInfo()->getName();
  return Id;
}

GraphObserver::NodeId
IndexerPPCallbacks::BuildNodeIdForMacro(const clang::Token &Spelling,
                                        clang::MacroInfo const &Info) {
  // Macro definitions always appear at the topmost level *and* always appear
  // in source text (or are implicit). For this reason, it's safe to use
  // location information to stably unique them.
  GraphObserver::NodeId Id(
      Observer.getClaimTokenForLocation(Info.getDefinitionLoc()));
  llvm::raw_string_ostream Ostream(Id.Identity);
  Ostream << BuildNameIdForMacro(Spelling);
  clang::SourceLocation Loc = Info.getDefinitionLoc();
  auto &SM = *Observer.getSourceManager();

  if (Loc.isInvalid()) {
    Ostream << "@invalid";
  } else if (SM.getFileID(Loc) ==
             Observer.getPreprocessor()->getPredefinesFileID()) {
    Ostream << "@builtin";
  } else {
    Loc.print(Ostream, SM);
  }
  return Id;
}

} // namespace kythe
<commit_msg>Address comment.<commit_after>/*
 * Copyright 2014 Google Inc. All rights reserved.
 *
 * 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.
 */

// This file uses the Clang style conventions.

#include "GraphObserver.h"
#include "IndexerPPCallbacks.h"

#include "clang/Basic/SourceManager.h"
#include "clang/Basic/FileManager.h"
#include "clang/Lex/PPCallbacks.h"
#include "clang/Lex/Preprocessor.h"

#include "llvm/Support/raw_ostream.h"
#include "llvm/Support/Debug.h"

// TODO(zarko): IndexerASTHooks::RangeInCurrentContext should query the macro
// context. IndexerPPCallbacks doesn't need to query for the template context,
// since we'll always be in a state that looks like
//   /macro-expansion* template-inst*/,
// as template processing happens at a later stage.

// Embedding non-file source locations into vnames generates names that are
// not stable wrt include ordering (there appears to be a single scratch
// buffer with a line counter that is incrementally bumped).
// TODO(zarko): Fix this by using a hash of the scratch buffer + the file
// location into which the scratch buffer is being inlined. We might also be
// able to hook these locations to the macros that generate them, allowing us
// to insert edges for token pastes.

namespace kythe {

IndexerPPCallbacks::IndexerPPCallbacks(const clang::Preprocessor &PP,
                                       GraphObserver &GO)
    : Preprocessor(PP), Observer(GO) {}

IndexerPPCallbacks::~IndexerPPCallbacks() {}

void IndexerPPCallbacks::FileChanged(clang::SourceLocation Loc,
                                     PPCallbacks::FileChangeReason Reason,
                                     clang::SrcMgr::CharacteristicKind FileType,
                                     clang::FileID PrevFID) {
  switch (Reason) {
  case clang::PPCallbacks::EnterFile:
    Observer.pushFile(LastInclusionHash, Loc);
    break;
  case clang::PPCallbacks::ExitFile:
    Observer.popFile();
    break;
  case clang::PPCallbacks::SystemHeaderPragma:
    break;
  // RenameFile occurs when a #line directive is encountered, for example:
  // #line 10 "foo.cc"
  case clang::PPCallbacks::RenameFile:
    break;
  default:
    llvm::dbgs() << "Unknown FileChangeReason " << Reason << "\n";
  }
}

void IndexerPPCallbacks::FilterAndEmitDeferredRecords() {
  for (const auto &R : DeferredRecords) {
    if (R.WasDefined) {
      if (!R.Macro->getMacroInfo()->isUsedForHeaderGuard()) {
        Observer.recordBoundQueryRange(
            R.Range,
            BuildNodeIdForMacro(R.MacroName, *R.Macro->getMacroInfo()));
      }
    } else {
      Observer.recordUnboundQueryRange(R.Range,
                                       BuildNameIdForMacro(R.MacroName));
    }
  }
  DeferredRecords.clear();
}

void IndexerPPCallbacks::EndOfMainFile() {
  FilterAndEmitDeferredRecords();
  Observer.popFile();
}

GraphObserver::Range
IndexerPPCallbacks::RangeForTokenInCurrentContext(const clang::Token &Token) {
  const auto Start = Token.getLocation();
  const auto End = Start.getLocWithOffset(Token.getLength());
  return RangeInCurrentContext(clang::SourceRange(Start, End));
}

void IndexerPPCallbacks::MacroDefined(const clang::Token &Token,
                                      const clang::MacroDirective *Macro) {
  if (Macro == nullptr) {
    return;
  }
  const clang::MacroInfo &Info = *Macro->getMacroInfo();
  clang::FileID MacroFileID =
      Observer.getSourceManager()->getFileID(Info.getDefinitionLoc());
  const clang::FileEntry *MacroFileEntry =
      Observer.getSourceManager()->getFileEntryForID(MacroFileID);
  if (MacroFileEntry == nullptr) {
    // This is a builtin macro. Ignore it.
    return;
  }
  GraphObserver::NodeId MacroId = BuildNodeIdForMacro(Token, Info);
  if (Observer.claimNode(MacroId)) {
    GraphObserver::NameId MacroName = BuildNameIdForMacro(Token);
    Observer.recordDefinitionRange(RangeForTokenInCurrentContext(Token),
                                   MacroId);
    Observer.recordMacroNode(MacroId);
    Observer.recordNamedEdge(MacroId, MacroName);
  }
  // TODO(zarko): Record information about the definition (like other macro
  // references).
}

void IndexerPPCallbacks::MacroUndefined(const clang::Token &MacroName,
                                        const clang::MacroDirective *Macro) {
  if (Macro == nullptr) {
    return;
  }
  const clang::MacroInfo &Info = *Macro->getMacroInfo();
  GraphObserver::NodeId MacroId = BuildNodeIdForMacro(MacroName, Info);
  Observer.recordUndefinesRange(RangeForTokenInCurrentContext(MacroName),
                                MacroId);
}

void IndexerPPCallbacks::MacroExpands(const clang::Token &Token,
                                      const clang::MacroDirective *Macro,
                                      clang::SourceRange Range,
                                      const clang::MacroArgs *Args) {
  if (Macro == nullptr || Range.isInvalid()) {
    return;
  }

  const clang::MacroInfo &Info = *Macro->getMacroInfo();
  GraphObserver::NodeId MacroId = BuildNodeIdForMacro(Token, Info);
  if (!Range.getBegin().isFileID() || !Range.getEnd().isFileID()) {
    auto NewBegin =
        Observer.getSourceManager()->getExpansionLoc(Range.getBegin());
    if (!NewBegin.isFileID()) {
      return;
    }
    Range = clang::SourceRange(NewBegin,
                               clang::Lexer::getLocForEndOfToken(
                                   NewBegin, 0, /* offset from end of token */
                                   *Observer.getSourceManager(),
                                   *Observer.getLangOptions()));
    if (Range.isInvalid()) {
      return;
    }
    Observer.recordIndirectlyExpandsRange(RangeInCurrentContext(Range),
                                          MacroId);
  } else {
    Observer.recordExpandsRange(RangeForTokenInCurrentContext(Token), MacroId);
  }
  // TODO(zarko): Index macro arguments.
}

void IndexerPPCallbacks::Defined(const clang::Token &MacroName,
                                 const clang::MacroDirective *Macro,
                                 clang::SourceRange Range) {
  DeferredRecords.push_back(
      DeferredRecord{MacroName, Macro, Macro != nullptr && Macro->isDefined(),
                     RangeForTokenInCurrentContext(MacroName)});
}

void IndexerPPCallbacks::Ifdef(clang::SourceLocation Location,
                               const clang::Token &MacroName,
                               const clang::MacroDirective *Macro) {
  // Just delegate.
  Defined(MacroName, Macro, clang::SourceRange(Location));
}

void IndexerPPCallbacks::Ifndef(clang::SourceLocation Location,
                                const clang::Token &MacroName,
                                const clang::MacroDirective *Macro) {
  // Just delegate.
  Defined(MacroName, Macro, clang::SourceRange(Location));
}

void IndexerPPCallbacks::InclusionDirective(
    clang::SourceLocation HashLocation, const clang::Token &IncludeToken,
    llvm::StringRef Filename, bool IsAngled,
    clang::CharSourceRange FilenameRange, const clang::FileEntry *FileEntry,
    llvm::StringRef SearchPath, llvm::StringRef RelativePath,
    const clang::Module *Imported) {
  // TODO(zarko) (Modules): Check if `Imported` is non-null; if so, this
  // was transformed to a module import.
  if (FileEntry != nullptr) {
    Observer.recordIncludesRange(
        RangeInCurrentContext(FilenameRange.getAsRange()), FileEntry);
  }
  LastInclusionHash = HashLocation;
}

void IndexerPPCallbacks::AddMacroReferenceIfDefined(
    const clang::Token &MacroNameToken) {
  if (clang::IdentifierInfo *const NameII =
          MacroNameToken.getIdentifierInfo()) {
    if (NameII->hasMacroDefinition()) {
      if (auto *Macro = Preprocessor.getMacroDirective(NameII)) {
        AddReferenceToMacro(MacroNameToken, *Macro->getMacroInfo(),
                            Macro->isDefined());
      }
    } else if (NameII->hadMacroDefinition()) {
      if (auto *Macro = Preprocessor.getMacroDirectiveHistory(NameII)) {
        AddReferenceToMacro(MacroNameToken, *Macro->getMacroInfo(),
                            Macro->isDefined());
      }
    }
  } else {
    // This shouldn't happen; it's an error to do "defined TOKEN" unless
    // the preprocessor token is an identifier, in which case it should
    // have IdentifierInfo associated with it.  Note that Clang assigns
    // tok::tk_XXXX token types to preprocessing identifier tokens if
    // they have the names of tokens, rather than following the Standard's
    // formal phases of translation which would have them all be just
    // identifiers during preprocessing.
    assert(0 && "No IdentifierInfo in AddMacroReferenceIfDefined.");
  }
}

void IndexerPPCallbacks::AddReferenceToMacro(const clang::Token &MacroNameToken,
                                             clang::MacroInfo const &Info,
                                             bool IsDefined) {
  llvm::StringRef MacroName(MacroNameToken.getIdentifierInfo()->getName());
  Observer.recordExpandsRange(RangeForTokenInCurrentContext(MacroNameToken),
                              BuildNodeIdForMacro(MacroNameToken, Info));
}

GraphObserver::NameId
IndexerPPCallbacks::BuildNameIdForMacro(const clang::Token &Spelling) {
  assert(Spelling.getIdentifierInfo() && "Macro spelling lacks IdentifierInfo");
  GraphObserver::NameId Id;
  Id.EqClass = GraphObserver::NameId::NameEqClass::Macro;
  Id.Path = Spelling.getIdentifierInfo()->getName();
  return Id;
}

GraphObserver::NodeId
IndexerPPCallbacks::BuildNodeIdForMacro(const clang::Token &Spelling,
                                        clang::MacroInfo const &Info) {
  // Macro definitions always appear at the topmost level *and* always appear
  // in source text (or are implicit). For this reason, it's safe to use
  // location information to stably unique them.
  GraphObserver::NodeId Id(
      Observer.getClaimTokenForLocation(Info.getDefinitionLoc()));
  llvm::raw_string_ostream Ostream(Id.Identity);
  Ostream << BuildNameIdForMacro(Spelling);
  clang::SourceLocation Loc = Info.getDefinitionLoc();
  auto &SM = *Observer.getSourceManager();

  if (Loc.isInvalid()) {
    Ostream << "@invalid";
  } else if (SM.getFileID(Loc) ==
             Observer.getPreprocessor()->getPredefinesFileID()) {
    // Locations of predefines in the predefine buffer can spuriously differ
    // from TU to TU, so we collapse them here.
    Ostream << "@builtin";
  } else {
    Loc.print(Ostream, SM);
  }
  return Id;
}

} // namespace kythe
<|endoftext|>


<commit_before>// Copyright (c) 2018 ASMlover. All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions
// are met:
//
//  * Redistributions of source code must retain the above copyright
//    notice, this list ofconditions and the following disclaimer.
//
//  * Redistributions in binary form must reproduce the above copyright
//    notice, this list of conditions and the following disclaimer in
//    the documentation and/or other materialsprovided with the
//    distribution.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
// FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
// COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
// ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
// POSSIBILITY OF SUCH DAMAGE.
#include <algorithm>
#include "ext_timer2.h"
#include "ext_timermgr2.h"

namespace ext {

Timer2Mgr::Timer2Mgr(void)
  : func_(boost::python::detail::borrowed_reference(Py_None)) {
}

Timer2Mgr::~Timer2Mgr(void) {
  if (!stoped_)
    stop_all_timers();
}

id_t Timer2Mgr::add_tiemr_proxy(bool is_repeat, double delay, PyObject* proxy) {
  if (func_.is_none())
    return 0;

  if (delay >= (double)std::numeric_limits<std::int64_t>::max() || delay < 0)
    return 0;

  std::int64_t delay_time =
    std::max(std::int64_t(delay * 1000000), std::int64_t(1));
  std::int64_t now = 0; // TODO: need function for this

  std::unique_lock<std::mutex> g(timer_mutex_);
  ++next_id_;
  if (BOOST_UNLIKELY(next_id_ == 0)) {
    next_id_ = 1;
    while (timers_.find(next_id_) != timers_.end())
      ++next_id_;
  }

  auto timer = std::make_shared<Timer2>(next_id_, delay_time, is_repeat);
  timer->set_proxy(proxy);
  timer->refresh_expire_time(now);
  timers_set_.insert(timer);
  timers_[next_id_] = timer;

  return next_id_;
}

void Timer2Mgr::del_timer(id_t timer_id) {
  std::unique_lock<std::mutex> g(timer_mutex_);
  auto it = timers_.find(timer_id);
  if (it != timers_.end()) {
    it->second->cancel();
    it->second->clear();
    timers_set_.erase(it->second);
    timers_.erase(it);
  }
}

void Timer2Mgr::stop_all_timers(void) {
  std::unique_lock<std::mutex> g(timer_mutex_);
  stoped_ = true;
  for (auto& t : timers_set_)
    t->cancel();
  timers_set_.clear();
  timers_.clear();
}

void Timer2Mgr::set_functor(boost::python::object& func) {
  func_ = func;
}

void Timer2Mgr::reset_functor(void) {
  func_ = boost::python::object();
}

std::size_t Timer2Mgr::call_expired_timers(void) {
  return 0;
}

}
<commit_msg>:construction: chore(timermgr): updated the implementation of timer manager<commit_after>// Copyright (c) 2018 ASMlover. All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions
// are met:
//
//  * Redistributions of source code must retain the above copyright
//    notice, this list ofconditions and the following disclaimer.
//
//  * Redistributions in binary form must reproduce the above copyright
//    notice, this list of conditions and the following disclaimer in
//    the documentation and/or other materialsprovided with the
//    distribution.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
// FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
// COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
// ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
// POSSIBILITY OF SUCH DAMAGE.
#include <algorithm>
#include "ext_timer2.h"
#include "ext_timermgr2.h"

namespace ext {

Timer2Mgr::Timer2Mgr(void)
  : func_(boost::python::detail::borrowed_reference(Py_None)) {
}

Timer2Mgr::~Timer2Mgr(void) {
  if (!stoped_)
    stop_all_timers();
}

id_t Timer2Mgr::add_tiemr_proxy(bool is_repeat, double delay, PyObject* proxy) {
  if (func_.is_none())
    return 0;

  if (delay >= (double)std::numeric_limits<std::int64_t>::max() || delay < 0)
    return 0;

  std::int64_t delay_time =
    std::max(std::int64_t(delay * 1000000), std::int64_t(1));
  std::int64_t now = 0; // TODO: need function for this

  std::unique_lock<std::mutex> g(timer_mutex_);
  ++next_id_;
  if (BOOST_UNLIKELY(next_id_ == 0)) {
    next_id_ = 1;
    while (timers_.find(next_id_) != timers_.end())
      ++next_id_;
  }

  auto timer = std::make_shared<Timer2>(next_id_, delay_time, is_repeat);
  timer->set_proxy(proxy);
  timer->refresh_expire_time(now);
  timers_set_.insert(timer);
  timers_[next_id_] = timer;

  return next_id_;
}

void Timer2Mgr::del_timer(id_t timer_id) {
  std::unique_lock<std::mutex> g(timer_mutex_);
  auto it = timers_.find(timer_id);
  if (it != timers_.end()) {
    it->second->cancel();
    it->second->clear();
    timers_set_.erase(it->second);
    timers_.erase(it);
  }
}

void Timer2Mgr::stop_all_timers(void) {
  std::unique_lock<std::mutex> g(timer_mutex_);
  stoped_ = true;
  for (auto& t : timers_set_)
    t->cancel();
  timers_set_.clear();
  timers_.clear();
}

void Timer2Mgr::set_functor(boost::python::object& func) {
  func_ = func;
}

void Timer2Mgr::reset_functor(void) {
  func_ = boost::python::object();
}

std::size_t Timer2Mgr::call_expired_timers(void) {
  std::size_t count{};
  std::int64_t now = 0; // TODO: get now millisec
  std::unique_lock<std::mutex> g(timer_mutex_);
  while (!timers_set_.empty()) {
    auto now_timer = *timers_set_.begin();
    if (!now_timer->is_expired(now))
      break;
    timers_set_.erase(now_timer);

    if (BOOST_UNLIKELY(now_timer->is_cancelled())) {
      timers_.erase(now_timer->get_id());
      now_timer->clear();
      continue;
    }
    now_timer->do_function();

    if (BOOST_UNLIKELY(!now_timer->is_repeat())) {
      timers_.erase(now_timer->get_id());
      now_timer->clear();
    }
    else {
      now_timer->refresh_expire_time(now);
      timers_set_.insert(now_timer);
    }

    if (BOOST_UNLIKELY(++count > kMaxPerTick))
      break;
  }

  return count;
}

}
<|endoftext|>


<commit_before>/*
 * LibCassandra
 * Copyright (C) 2010 Padraig O'Sullivan
 * All rights reserved.
 *
 * Use and distribution licensed under the BSD license. See
 * the COPYING file in the parent directory for full text.
 */

#include <time.h>

#include <iostream>
#include <string>
#include <map>

#include <protocol/TBinaryProtocol.h>
#include <transport/TSocket.h>
#include <transport/TTransportUtils.h>

#include "libgenthrift/Cassandra.h"

#include "libcassandra/cassandra.h"
#include "libcassandra/keyspace.h"
#include "libcassandra/exception.h"

using namespace libcassandra;
using namespace std;
using namespace apache::thrift;
using namespace apache::thrift::protocol;
using namespace apache::thrift::transport;
using namespace org::apache::cassandra;
using namespace boost;

Keyspace::Keyspace(Cassandra *in_client,
                   const string &in_name,
                   const map< string, map<string, string> > &in_desc,
                   ConsistencyLevel in_level)
  :
    client(in_client),
    name(in_name),
    keyspace_desc(in_desc),
    level(in_level)
{}

void Keyspace::insertColumn(const string &key,
                            const string &column_family,
                            const string &super_column_name,
                            const string &column_name,
                            const string &value)
{
  ColumnPath col_path;
  col_path.column_family.assign(column_family);
  if (! super_column_name.empty())
  {
    col_path.super_column.assign(super_column_name);
    col_path.__isset.super_column= true;
  }
  col_path.column.assign(column_name);
  col_path.__isset.column= true;
  /* validate the column path */
  validateColumnPath(col_path);
  /* actually perform the insert */
  client->getCassandra()->insert(name, key, col_path, value, createTimestamp(), level);
}

void Keyspace::insertColumn(const string &key,
                            const string &column_family,
                            const string &column_name,
                            const string &value)
{
  insertColumn(key, column_family, "", column_name, value);
}

void Keyspace::remove(const string &key,
                      const ColumnPath &col_path)
{
  /* validate the column path */
  validateColumnPath(col_path);
  client->getCassandra()->remove(name, key, col_path, createTimestamp(), level);
}

void Keyspace::remove(const string &key,
                      const string &column_family,
                      const string &super_column_name,
                      const string &column_name)
{
  ColumnPath col_path;
  col_path.column_family.assign(column_family);
  if (! super_column_name.empty())
  {
    col_path.super_column.assign(super_column_name);
    col_path.__isset.super_column= true;
  }
  if (! column_name.empty())
  {
    col_path.column.assign(column_name);
    col_path.__isset.column= true;
  }
  remove(key, col_path);
}

void Keyspace::removeColumn(const string &key,
                            const string &column_family,
                            const string &super_column_name,
                            const string &column_name)
{
  remove(key, column_family, super_column_name, column_name);
}

void Keyspace::removeSuperColumn(const string &key,
                                 const string &column_family,
                                 const string &super_column_name)
{
  remove(key, column_family, super_column_name, "");
}

Column Keyspace::getColumn(const string &key,
                           const string &column_family,
                           const string &super_column_name,
                           const string &column_name)
{
  ColumnPath col_path;
  col_path.column_family.assign(column_family);
  if (! super_column_name.empty())
  {
    col_path.super_column.assign(super_column_name);
    col_path.__isset.super_column= true;
  }
  col_path.column.assign(column_name);
  col_path.__isset.column= true;
  validateColumnPath(col_path);
  ColumnOrSuperColumn cosc;
  client->getCassandra()->get(cosc, name, key, col_path, level);
  if (cosc.column.name.empty())
  {
    /* throw an exception */
    throw(InvalidRequestException());
  }
  return cosc.column;
}

Column Keyspace::getColumn(const string &key,
                           const string &column_family,
                           const string &column_name)
{
  return getColumn(key, column_family, "", column_name);
}

string Keyspace::getColumnValue(const string &key,
                                const string &column_family,
                                const string &super_column_name,
                                const string &column_name)
{
  return getColumn(key, column_family, super_column_name, column_name).value;
}

string Keyspace::getColumnValue(const string &key,
                                const string &column_family,
                                const string &column_name)
{
	return getColumn(key, column_family, column_name).value;
}

SuperColumn Keyspace::getSuperColumn(const string &key,
                                     const string &column_family,
                                     const string &super_column_name)
{
  ColumnPath col_path;
  col_path.column_family.assign(column_family);
  col_path.super_column.assign(super_column_name);
  /* this is ugly but thanks to thrift is needed */
  col_path.__isset.super_column= true;
  validateSuperColumnPath(col_path);
  ColumnOrSuperColumn cosc;
  client->getCassandra()->get(cosc, name, key, col_path, level);
  if (cosc.super_column.name.empty())
  {
    /* throw an exception */
    throw(InvalidRequestException());
  }
  return cosc.super_column;
}

vector<Column> Keyspace::getSliceNames(const string &key,
                                       const ColumnParent &col_parent,
                                       SlicePredicate &pred)
{
  vector<ColumnOrSuperColumn> ret_cosc;
  vector<Column> result;
  /* damn you thrift! */
  pred.__isset.column_names= true;
  client->getCassandra()->get_slice(ret_cosc, name, key, col_parent, pred, level);
  for (vector<ColumnOrSuperColumn>::iterator it= ret_cosc.begin();
       it != ret_cosc.end();
       ++it)
  {
    if (! (*it).column.name.empty())
    {
      result.push_back((*it).column);
    }
  }
  return result;
}

vector<Column> Keyspace::getSliceRange(const string &key,
                                       const ColumnParent &col_parent,
                                       SlicePredicate &pred)
{
  vector<ColumnOrSuperColumn> ret_cosc;
  vector<Column> result;
  /* damn you thrift! */
  pred.__isset.slice_range= true;
  client->getCassandra()->get_slice(ret_cosc, name, key, col_parent, pred, level);
  for (vector<ColumnOrSuperColumn>::iterator it= ret_cosc.begin();
       it != ret_cosc.end();
       ++it)
  {
    if (! (*it).column.name.empty())
    {
      result.push_back((*it).column);
    }
  }
  return result;
}

map<string, vector<Column> > Keyspace::getRangeSlice(const ColumnParent &col_parent,
                                                     const SlicePredicate &pred,
                                                     const string &start,
                                                     const string &finish,
                                                     const int32_t row_count)
{
  map<string, vector<Column> > ret;
  vector<KeySlice> key_slices;
  client->getCassandra()->get_range_slice(key_slices,
                                          name,
                                          col_parent,
                                          pred,
                                          start,
                                          finish,
                                          row_count,
                                          level);
  if (! key_slices.empty())
  {
    for (vector<KeySlice>::iterator it= key_slices.begin();
         it != key_slices.end();
         ++it)
    {
      ret.insert(make_pair((*it).key, getColumnList((*it).columns)));
    }
  }
  return ret;
}

map<string, vector<SuperColumn> > Keyspace::getSuperRangeSlice(const ColumnParent &col_parent,
                                                               const SlicePredicate &pred,
                                                               const string &start,
                                                               const string &finish,
                                                               const int32_t row_count)
{
  map<string, vector<SuperColumn> > ret;
  vector<KeySlice> key_slices;
  client->getCassandra()->get_range_slice(key_slices,
                                          name,
                                          col_parent,
                                          pred,
                                          start,
                                          finish,
                                          row_count,
                                          level);
  if (! key_slices.empty())
  {
    for (vector<KeySlice>::iterator it= key_slices.begin();
         it != key_slices.end();
         ++it)
    {
      ret.insert(make_pair((*it).key, getSuperColumnList((*it).columns)));
    }
  }
  return ret;
}

vector<Column> Keyspace::getColumnList(vector<ColumnOrSuperColumn> &cols)
{
  vector<Column> ret(cols.size());
  for (vector<ColumnOrSuperColumn>::iterator it= cols.begin();
       it != cols.end();
       ++it)
  {
    ret.push_back((*it).column);
  }
  return ret;
}

vector<SuperColumn> Keyspace::getSuperColumnList(vector<ColumnOrSuperColumn> &cols)
{
  vector<SuperColumn> ret(cols.size());
  for (vector<ColumnOrSuperColumn>::iterator it= cols.begin();
       it != cols.end();
       ++it)
  {
    ret.push_back((*it).super_column);
  }
  return ret;
}

int32_t Keyspace::getCount(const string &key, const ColumnParent &col_parent)
{
  return (client->getCassandra()->get_count(name, key, col_parent, level));
}

string Keyspace::getName()
{
  return name;
}

ConsistencyLevel Keyspace::getConsistencyLevel() const
{
  return level;
}

map< string, map<string, string> > Keyspace::getDescription()
{
  return keyspace_desc;
}

int64_t Keyspace::createTimestamp()
{
  struct timeval tv;
  gettimeofday(&tv, NULL);
  return (int64_t) tv.tv_sec * 1000000 + (int64_t) tv.tv_usec;
}

void Keyspace::validateColumnPath(const ColumnPath &col_path)
{
  map<string, string> cf_define= keyspace_desc[col_path.column_family];
  if (cf_define.empty())
  {
	/* throw an exception */
    throw(InvalidRequestException());
  }
  string type= cf_define["Type"];
  if (! type.compare("Standard"))
  {
    if (! col_path.column.empty())
    {
      return;
    }
  }
  else if (! type.compare("Super"))
  {
    if (! col_path.super_column.empty())
    {
      return;
    }
  }
  /* if we get here, throw an exception */
  throw(InvalidRequestException());
}

void Keyspace::validateSuperColumnPath(const ColumnPath &col_path)
{
  map<string, string> cf_define= keyspace_desc[col_path.column_family];
  if (cf_define.empty())
  {
    /* throw an exception */
    throw(InvalidRequestException());
  }
  string type= cf_define["Type"];
  if (! type.compare("Super"))
  {
    if (! col_path.super_column.empty())
    {
      return;
    }
  }
  /* if we get here, throw an exception */
  throw(InvalidRequestException());
}
<commit_msg>Fix for issue 8.<commit_after>/*
 * LibCassandra
 * Copyright (C) 2010 Padraig O'Sullivan
 * All rights reserved.
 *
 * Use and distribution licensed under the BSD license. See
 * the COPYING file in the parent directory for full text.
 */

#include <time.h>

#include <iostream>
#include <string>
#include <map>

#include <protocol/TBinaryProtocol.h>
#include <transport/TSocket.h>
#include <transport/TTransportUtils.h>

#include "libgenthrift/Cassandra.h"

#include "libcassandra/cassandra.h"
#include "libcassandra/keyspace.h"
#include "libcassandra/exception.h"

using namespace libcassandra;
using namespace std;
using namespace apache::thrift;
using namespace apache::thrift::protocol;
using namespace apache::thrift::transport;
using namespace org::apache::cassandra;
using namespace boost;

Keyspace::Keyspace(Cassandra *in_client,
                   const string &in_name,
                   const map< string, map<string, string> > &in_desc,
                   ConsistencyLevel in_level)
  :
    client(in_client),
    name(in_name),
    keyspace_desc(in_desc),
    level(in_level)
{}

void Keyspace::insertColumn(const string &key,
                            const string &column_family,
                            const string &super_column_name,
                            const string &column_name,
                            const string &value)
{
  ColumnPath col_path;
  col_path.column_family.assign(column_family);
  if (! super_column_name.empty())
  {
    col_path.super_column.assign(super_column_name);
    col_path.__isset.super_column= true;
  }
  col_path.column.assign(column_name);
  col_path.__isset.column= true;
  /* validate the column path */
  validateColumnPath(col_path);
  /* actually perform the insert */
  client->getCassandra()->insert(name, key, col_path, value, createTimestamp(), level);
}

void Keyspace::insertColumn(const string &key,
                            const string &column_family,
                            const string &column_name,
                            const string &value)
{
  insertColumn(key, column_family, "", column_name, value);
}

void Keyspace::remove(const string &key,
                      const ColumnPath &col_path)
{
  client->getCassandra()->remove(name, key, col_path, createTimestamp(), level);
}

void Keyspace::remove(const string &key,
                      const string &column_family,
                      const string &super_column_name,
                      const string &column_name)
{
  ColumnPath col_path;
  col_path.column_family.assign(column_family);
  if (! super_column_name.empty())
  {
    col_path.super_column.assign(super_column_name);
    col_path.__isset.super_column= true;
  }
  if (! column_name.empty())
  {
    col_path.column.assign(column_name);
    col_path.__isset.column= true;
  }
  remove(key, col_path);
}

void Keyspace::removeColumn(const string &key,
                            const string &column_family,
                            const string &super_column_name,
                            const string &column_name)
{
  remove(key, column_family, super_column_name, column_name);
}

void Keyspace::removeSuperColumn(const string &key,
                                 const string &column_family,
                                 const string &super_column_name)
{
  remove(key, column_family, super_column_name, "");
}

Column Keyspace::getColumn(const string &key,
                           const string &column_family,
                           const string &super_column_name,
                           const string &column_name)
{
  ColumnPath col_path;
  col_path.column_family.assign(column_family);
  if (! super_column_name.empty())
  {
    col_path.super_column.assign(super_column_name);
    col_path.__isset.super_column= true;
  }
  col_path.column.assign(column_name);
  col_path.__isset.column= true;
  validateColumnPath(col_path);
  ColumnOrSuperColumn cosc;
  client->getCassandra()->get(cosc, name, key, col_path, level);
  if (cosc.column.name.empty())
  {
    /* throw an exception */
    throw(InvalidRequestException());
  }
  return cosc.column;
}

Column Keyspace::getColumn(const string &key,
                           const string &column_family,
                           const string &column_name)
{
  return getColumn(key, column_family, "", column_name);
}

string Keyspace::getColumnValue(const string &key,
                                const string &column_family,
                                const string &super_column_name,
                                const string &column_name)
{
  return getColumn(key, column_family, super_column_name, column_name).value;
}

string Keyspace::getColumnValue(const string &key,
                                const string &column_family,
                                const string &column_name)
{
	return getColumn(key, column_family, column_name).value;
}

SuperColumn Keyspace::getSuperColumn(const string &key,
                                     const string &column_family,
                                     const string &super_column_name)
{
  ColumnPath col_path;
  col_path.column_family.assign(column_family);
  col_path.super_column.assign(super_column_name);
  /* this is ugly but thanks to thrift is needed */
  col_path.__isset.super_column= true;
  validateSuperColumnPath(col_path);
  ColumnOrSuperColumn cosc;
  client->getCassandra()->get(cosc, name, key, col_path, level);
  if (cosc.super_column.name.empty())
  {
    /* throw an exception */
    throw(InvalidRequestException());
  }
  return cosc.super_column;
}

vector<Column> Keyspace::getSliceNames(const string &key,
                                       const ColumnParent &col_parent,
                                       SlicePredicate &pred)
{
  vector<ColumnOrSuperColumn> ret_cosc;
  vector<Column> result;
  /* damn you thrift! */
  pred.__isset.column_names= true;
  client->getCassandra()->get_slice(ret_cosc, name, key, col_parent, pred, level);
  for (vector<ColumnOrSuperColumn>::iterator it= ret_cosc.begin();
       it != ret_cosc.end();
       ++it)
  {
    if (! (*it).column.name.empty())
    {
      result.push_back((*it).column);
    }
  }
  return result;
}

vector<Column> Keyspace::getSliceRange(const string &key,
                                       const ColumnParent &col_parent,
                                       SlicePredicate &pred)
{
  vector<ColumnOrSuperColumn> ret_cosc;
  vector<Column> result;
  /* damn you thrift! */
  pred.__isset.slice_range= true;
  client->getCassandra()->get_slice(ret_cosc, name, key, col_parent, pred, level);
  for (vector<ColumnOrSuperColumn>::iterator it= ret_cosc.begin();
       it != ret_cosc.end();
       ++it)
  {
    if (! (*it).column.name.empty())
    {
      result.push_back((*it).column);
    }
  }
  return result;
}

map<string, vector<Column> > Keyspace::getRangeSlice(const ColumnParent &col_parent,
                                                     const SlicePredicate &pred,
                                                     const string &start,
                                                     const string &finish,
                                                     const int32_t row_count)
{
  map<string, vector<Column> > ret;
  vector<KeySlice> key_slices;
  client->getCassandra()->get_range_slice(key_slices,
                                          name,
                                          col_parent,
                                          pred,
                                          start,
                                          finish,
                                          row_count,
                                          level);
  if (! key_slices.empty())
  {
    for (vector<KeySlice>::iterator it= key_slices.begin();
         it != key_slices.end();
         ++it)
    {
      ret.insert(make_pair((*it).key, getColumnList((*it).columns)));
    }
  }
  return ret;
}

map<string, vector<SuperColumn> > Keyspace::getSuperRangeSlice(const ColumnParent &col_parent,
                                                               const SlicePredicate &pred,
                                                               const string &start,
                                                               const string &finish,
                                                               const int32_t row_count)
{
  map<string, vector<SuperColumn> > ret;
  vector<KeySlice> key_slices;
  client->getCassandra()->get_range_slice(key_slices,
                                          name,
                                          col_parent,
                                          pred,
                                          start,
                                          finish,
                                          row_count,
                                          level);
  if (! key_slices.empty())
  {
    for (vector<KeySlice>::iterator it= key_slices.begin();
         it != key_slices.end();
         ++it)
    {
      ret.insert(make_pair((*it).key, getSuperColumnList((*it).columns)));
    }
  }
  return ret;
}

vector<Column> Keyspace::getColumnList(vector<ColumnOrSuperColumn> &cols)
{
  vector<Column> ret(cols.size());
  for (vector<ColumnOrSuperColumn>::iterator it= cols.begin();
       it != cols.end();
       ++it)
  {
    ret.push_back((*it).column);
  }
  return ret;
}

vector<SuperColumn> Keyspace::getSuperColumnList(vector<ColumnOrSuperColumn> &cols)
{
  vector<SuperColumn> ret(cols.size());
  for (vector<ColumnOrSuperColumn>::iterator it= cols.begin();
       it != cols.end();
       ++it)
  {
    ret.push_back((*it).super_column);
  }
  return ret;
}

int32_t Keyspace::getCount(const string &key, const ColumnParent &col_parent)
{
  return (client->getCassandra()->get_count(name, key, col_parent, level));
}

string Keyspace::getName()
{
  return name;
}

ConsistencyLevel Keyspace::getConsistencyLevel() const
{
  return level;
}

map< string, map<string, string> > Keyspace::getDescription()
{
  return keyspace_desc;
}

int64_t Keyspace::createTimestamp()
{
  struct timeval tv;
  gettimeofday(&tv, NULL);
  return (int64_t) tv.tv_sec * 1000000 + (int64_t) tv.tv_usec;
}

void Keyspace::validateColumnPath(const ColumnPath &col_path)
{
  map<string, string> cf_define= keyspace_desc[col_path.column_family];
  if (cf_define.empty())
  {
	/* throw an exception */
    throw(InvalidRequestException());
  }
  string type= cf_define["Type"];
  if (! type.compare("Standard"))
  {
    if (! col_path.column.empty())
    {
      return;
    }
  }
  else if (! type.compare("Super"))
  {
    if (! col_path.super_column.empty())
    {
      return;
    }
  }
  /* if we get here, throw an exception */
  throw(InvalidRequestException());
}

void Keyspace::validateSuperColumnPath(const ColumnPath &col_path)
{
  map<string, string> cf_define= keyspace_desc[col_path.column_family];
  if (cf_define.empty())
  {
    /* throw an exception */
    throw(InvalidRequestException());
  }
  string type= cf_define["Type"];
  if (! type.compare("Super"))
  {
    if (! col_path.super_column.empty())
    {
      return;
    }
  }
  /* if we get here, throw an exception */
  throw(InvalidRequestException());
}
<|endoftext|>


<commit_before>#include <cstdio>
#include <iostream>
#include <stdexcept>

#include <pqxx/connection>
#include <pqxx/cursor.h>
#include <pqxx/transaction>
#include <pqxx/result>

using namespace PGSTD;
using namespace pqxx;

// Simple test program for libpqxx.  Read list of tables through a cursor,
// fetching <blocksize> rows at a time.  Default blocksize is 1; use 0 to
// read all rows at once.  Negative blocksizes read backwards.
//
// Usage: test003 [connect-string] [blocksize]
//
// Where connect-string is a set of connection options in Postgresql's
// PQconnectdb() format, eg. "dbname=template1" to select from a database
// called template1, of "host=foo.bar.net user=smith" to connect to a
// backend running on host foo.bar.net, logging in as user smith.
int main(int argc, char *argv[])
{
  try
  {
    int BlockSize = 1;
    if ((argc > 2) && argv[2] && (sscanf(argv[2],"%d",&BlockSize) != 1))
      throw invalid_argument("Expected number for second argument");
    if (BlockSize == 0) BlockSize = Cursor::ALL();

    // Set up a connection to the backend
    connection C(argv[1] ? argv[1] : "");

    // Enable all sorts of debug output
    C.trace(stdout);

    // Begin a transaction acting on our current connection
    transaction<serializable> T(C, "test3");

    // Declare a cursor for the list of database tables
    Cursor Cur(T, "SELECT * FROM pg_tables", "tablecur", BlockSize);

    // If we want to read backwards, move to the last tuple first
    if (BlockSize < 0) Cur.Move(Cursor::ALL());

    // Stop generating debug output
    C.trace(0);

    result R;
    while ((Cur >> R))
    {
      // Out of sheer curiosity, see if Cursor is consistent in the stream
      // status it reports with operator bool() and operator !() (1)
      if (!Cur) throw logic_error("Inconsistent cursor state!");

      // Received a block of rows.  Note that this may be less than the
      // blocksize we requested if we're at the end of our query.
      cout << "* Got " << R.size() << " row(s) *" << endl;

      // Another sanity check on Cursor: must never return too many rows,
      // even though returning too few is permitted
      if (R.size() > abs(BlockSize))
        throw logic_error("Cursor returned " + to_string(R.size()) + " rows, "
			  "when " + to_string(abs(BlockSize)) + " "
			  "was all I asked for!");

      // Process each successive result tuple
      for (result::const_iterator c = R.begin(); c != R.end(); ++c)
      {
        // Read value of column 0 into a string N
        string N;
        c[0].to(N);

        // Dump tuple number and column 0 value to cout
        cout << '\t' << to_string(c.num()) << '\t' << N << endl;
      }
    }

    // Out of sheer curiosity, see if Cursor is consistent in the stream
    // status it reports with operator bool() and operator !() (2)
    if (!!Cur) throw logic_error("Inconsistent cursor state!");

    // Tell the transaction that it has been successful
    T.commit();
  }
  catch (const sql_error &e)
  {
    // If we're interested in the text of a failed query, we can write separate
    // exception handling code for this type of exception
    cerr << "SQL error: " << e.what() << endl
         << "Query was: '" << e.query() << "'" << endl;
    return 1;
  }
  catch (const exception &e)
  {
    // All exceptions thrown by libpqxx are derived from std::exception
    cerr << "Exception: " << e.what() << endl;
    return 2;
  }
  catch (...)
  {
    // This is really unexpected (see above)
    cerr << "Unhandled exception" << endl;
    return 100;
  }

  return 0;
}

<commit_msg>Silenced warning about deprecated header<commit_after>#include <cstdio>
#include <iostream>
#include <stdexcept>

#define PQXXYES_I_KNOW_DEPRECATED_HEADER
#include <pqxx/connection>
#include <pqxx/cursor.h>
#include <pqxx/transaction>
#include <pqxx/result>

using namespace PGSTD;
using namespace pqxx;

// Simple test program for libpqxx.  Read list of tables through a cursor,
// fetching <blocksize> rows at a time.  Default blocksize is 1; use 0 to
// read all rows at once.  Negative blocksizes read backwards.
//
// Usage: test003 [connect-string] [blocksize]
//
// Where connect-string is a set of connection options in Postgresql's
// PQconnectdb() format, eg. "dbname=template1" to select from a database
// called template1, of "host=foo.bar.net user=smith" to connect to a
// backend running on host foo.bar.net, logging in as user smith.
int main(int argc, char *argv[])
{
  try
  {
    int BlockSize = 1;
    if ((argc > 2) && argv[2] && (sscanf(argv[2],"%d",&BlockSize) != 1))
      throw invalid_argument("Expected number for second argument");
    if (BlockSize == 0) BlockSize = Cursor::ALL();

    // Set up a connection to the backend
    connection C(argv[1] ? argv[1] : "");

    // Enable all sorts of debug output
    C.trace(stdout);

    // Begin a transaction acting on our current connection
    transaction<serializable> T(C, "test3");

    // Declare a cursor for the list of database tables
    Cursor Cur(T, "SELECT * FROM pg_tables", "tablecur", BlockSize);

    // If we want to read backwards, move to the last tuple first
    if (BlockSize < 0) Cur.Move(Cursor::ALL());

    // Stop generating debug output
    C.trace(0);

    result R;
    while ((Cur >> R))
    {
      // Out of sheer curiosity, see if Cursor is consistent in the stream
      // status it reports with operator bool() and operator !() (1)
      if (!Cur) throw logic_error("Inconsistent cursor state!");

      // Received a block of rows.  Note that this may be less than the
      // blocksize we requested if we're at the end of our query.
      cout << "* Got " << R.size() << " row(s) *" << endl;

      // Another sanity check on Cursor: must never return too many rows,
      // even though returning too few is permitted
      if (R.size() > abs(BlockSize))
        throw logic_error("Cursor returned " + to_string(R.size()) + " rows, "
			  "when " + to_string(abs(BlockSize)) + " "
			  "was all I asked for!");

      // Process each successive result tuple
      for (result::const_iterator c = R.begin(); c != R.end(); ++c)
      {
        // Read value of column 0 into a string N
        string N;
        c[0].to(N);

        // Dump tuple number and column 0 value to cout
        cout << '\t' << to_string(c.num()) << '\t' << N << endl;
      }
    }

    // Out of sheer curiosity, see if Cursor is consistent in the stream
    // status it reports with operator bool() and operator !() (2)
    if (!!Cur) throw logic_error("Inconsistent cursor state!");

    // Tell the transaction that it has been successful
    T.commit();
  }
  catch (const sql_error &e)
  {
    // If we're interested in the text of a failed query, we can write separate
    // exception handling code for this type of exception
    cerr << "SQL error: " << e.what() << endl
         << "Query was: '" << e.query() << "'" << endl;
    return 1;
  }
  catch (const exception &e)
  {
    // All exceptions thrown by libpqxx are derived from std::exception
    cerr << "Exception: " << e.what() << endl;
    return 2;
  }
  catch (...)
  {
    // This is really unexpected (see above)
    cerr << "Unhandled exception" << endl;
    return 100;
  }

  return 0;
}

<|endoftext|>


<commit_before>/*
 * Copyright (C) 2004-2011  See the AUTHORS file for details.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License version 2 as published
 * by the Free Software Foundation.
 */

#include "Modules.h"
#include "User.h"
#include "Chan.h"
#include "Nick.h"
#include "znc.h"
#include "IRCSock.h"

#include "modperl/module.h"
#include "modperl/swigperlrun.h"
#include <EXTERN.h>
#include <perl.h>
#include <XSUB.h>

#include "modperl/pstring.h"

// Allows perl to load .so files when needed by .pm
// For example, it needs to load ZNC.so
extern "C" {
	void boot_DynaLoader (pTHX_ CV* cv);
	static void xs_init(pTHX) {
		newXS("DynaLoader::boot_DynaLoader", boot_DynaLoader, __FILE__);
	}
}

class CModPerl: public CGlobalModule {
	PerlInterpreter *m_pPerl;
public:
	GLOBALMODCONSTRUCTOR(CModPerl) {
		m_pPerl = NULL;
	}

#define PSTART dSP; I32 ax; int ret = 0; ENTER; SAVETMPS; PUSHMARK(SP)
#define PCALL(name) PUTBACK; ret = call_pv(name, G_EVAL|G_ARRAY); SPAGAIN; SP -= ret; ax = (SP - PL_stack_base) + 1
#define PEND PUTBACK; FREETMPS; LEAVE
#define PUSH_STR(s) XPUSHs(PString(s).GetSV())
#define PUSH_PTR(type, p) XPUSHs(SWIG_NewInstanceObj(const_cast<type>(p), SWIG_TypeQuery(#type), SWIG_SHADOW))

	bool OnLoad(const CString& sArgsi, CString& sMessage) {
		CString sModPath, sTmp;
		if (!CModules::FindModPath("modperl/startup.pl", sModPath, sTmp)) {
			sMessage = "startup.pl not found.";
			return false;
		}
		sTmp = CDir::ChangeDir(sModPath, "..");
		int argc = 6;
		char *pArgv[] = {"", "-T", "-w",
			"-I", const_cast<char*>(sTmp.c_str()),
			const_cast<char*>(sModPath.c_str()), NULL};
		char **argv = pArgv;
		PERL_SYS_INIT3(&argc, &argv, &environ);
		m_pPerl = perl_alloc();
		perl_construct(m_pPerl);
		if (perl_parse(m_pPerl, xs_init, argc, argv, environ)) {
			perl_free(m_pPerl);
			PERL_SYS_TERM();
			m_pPerl = NULL;
			sMessage = "Can't initialize perl.";
			DEBUG(__PRETTY_FUNCTION__ << " can't init perl");
			return false;
		}
		PL_exit_flags |= PERL_EXIT_DESTRUCT_END;
		PSTART;
		PCALL("ZNC::Core::Init");
		PEND;
		return true;
	}

	ELoadPerlMod LoadPerlModule(const CString& sModule, const CString& sArgs, CUser* pUser, CString& sRetMsg) {
		ELoadPerlMod result = Perl_LoadError;
		PSTART;
		PUSH_STR(sModule);
		PUSH_STR(sArgs);
		PUSH_PTR(CUser*, pUser);
		PCALL("ZNC::Core::LoadModule");

		if (SvTRUE(ERRSV)) {
			sRetMsg = PString(ERRSV);
		} else if (2 == ret) {
			result = static_cast<ELoadPerlMod>(SvUV(ST(0)));
			sRetMsg = PString(ST(1));
		}
		DEBUG(__PRETTY_FUNCTION__ << " " << sRetMsg);

		PEND;
		return result;
	}

	virtual EModRet OnModuleLoading(const CString& sModName, const CString& sArgs,
			bool& bSuccess, CString& sRetMsg) {
		if (!GetUser()) {
			return CONTINUE;
		}
		switch (LoadPerlModule(sModName, sArgs, GetUser(), sRetMsg)) {
			case Perl_NotFound:
				return CONTINUE;
			case Perl_Loaded:
				bSuccess = true;
				return HALT;
			case Perl_LoadError:
				bSuccess = false;
				return HALT;
		}
		sRetMsg = "Something weird happened";
		bSuccess = false;
		return HALT;
	}

	virtual EModRet OnModuleUnloading(CModule* pModule, bool& bSuccess, CString& sRetMsg) {
		CPerlModule* pMod = AsPerlModule(pModule);
		if (pMod) {
			CString sModName = pMod->GetModName();
			PSTART;
			PUSH_PTR(CPerlModule*, pMod);
			PCALL("ZNC::Core::UnloadModule");
			if (SvTRUE(ERRSV)) {
				bSuccess = false;
				sRetMsg = PString(ERRSV);
			} else {
				bSuccess = true;
				sRetMsg = "Module [" + sModName + "] unloaded";
			}
			DEBUG(__PRETTY_FUNCTION__ << " " << sRetMsg);
			return HALT;
		}
		return CONTINUE;
	}

	virtual EModRet OnGetModInfo(CModInfo& ModInfo, const CString& sModule,
			bool& bSuccess, CString& sRetMsg) {
		PSTART;
		PUSH_STR(sModule);
		PCALL("ZNC::Core::GetModInfo");
		EModRet result = CONTINUE;
		if (SvTRUE(ERRSV)) {
			bSuccess = false;
			sRetMsg = PString(ERRSV);
		} else if (0 < ret) {
			switch(static_cast<ELoadPerlMod>(SvUV(ST(0)))) {
				case Perl_NotFound:
					result = CONTINUE;
					break;
				case Perl_Loaded:
					result = HALT;
					if (3 == ret) {
						ModInfo.SetGlobal(false);
						ModInfo.SetDescription(PString(ST(2)));
						ModInfo.SetName(sModule);
						ModInfo.SetPath(PString(ST(1)));
						bSuccess = true;
					} else {
						bSuccess = false;
						sRetMsg = "Something weird happened";
					}
					break;
				case Perl_LoadError:
					result = HALT;
					bSuccess = false;
					if (2 == ret) {
						sRetMsg = PString(ST(1));
					} else {
						sRetMsg = "Something weird happened";
					}
			}
		} else {
			result = HALT;
			bSuccess = false;
			sRetMsg = "Something weird happened";
		}
		PEND;
		DEBUG(__PRETTY_FUNCTION__ << " " << sRetMsg);
		return result;
	}

	virtual void OnGetAvailableMods(set<CModInfo>& ssMods, bool bGlobal) {
		if (bGlobal) {
			return;
		}

		unsigned int a = 0;
		CDir Dir;

		CModules::ModDirList dirs = CModules::GetModDirs();

		while (!dirs.empty()) {
			Dir.FillByWildcard(dirs.front().first, "*.pm");
			dirs.pop();

			for (a = 0; a < Dir.size(); a++) {
				CFile& File = *Dir[a];
				CString sName = File.GetShortName();
				CString sPath = File.GetLongName();
				CModInfo ModInfo;
				sName.RightChomp(3);
				PSTART;
				PUSH_STR(sPath);
				PUSH_STR(sName);
				PCALL("ZNC::Core::ModInfoByPath");
				if (!SvTRUE(ERRSV) && ret == 1) {
					ModInfo.SetGlobal(false);
					ModInfo.SetDescription(PString(ST(0)));
					ModInfo.SetName(sName);
					ModInfo.SetPath(sPath);
					ssMods.insert(ModInfo);
				}
				PEND;
			}
		}
	}

	virtual ~CModPerl() {
		if (m_pPerl) {
			PSTART;
			PCALL("ZNC::Core::UnloadAll");
			PEND;
			perl_destruct(m_pPerl);
			perl_free(m_pPerl);
			PERL_SYS_TERM();
		}
	}

};

#include "modperl/functions.cpp"

VWebSubPages& CPerlModule::GetSubPages() {
	VWebSubPages* result = _GetSubPages();
	if (!result) {
		return CModule::GetSubPages();
	}
	return *result;
}

void CPerlTimer::RunJob() {
	CPerlModule* pMod = AsPerlModule(GetModule());
	if (pMod) {
		PSTART;
		PUSH_STR(pMod->GetPerlID());
		PUSH_STR(GetPerlID());
		PCALL("ZNC::Core::CallTimer");
		PEND;
	}
}

CPerlTimer::~CPerlTimer() {
	CPerlModule* pMod = AsPerlModule(GetModule());
	if (pMod) {
		PSTART;
		PUSH_STR(pMod->GetPerlID());
		PUSH_STR(GetPerlID());
		PCALL("ZNC::Core::RemoveTimer");
		PEND;
	}
}

#define SOCKSTART PSTART; PUSH_STR(pMod->GetPerlID()); PUSH_STR(GetPerlID())
#define SOCKCBCHECK(OnSuccess) PCALL("ZNC::Core::CallSocket"); if (SvTRUE(ERRSV)) { Close(); DEBUG("Perl socket hook died with: " + PString(ERRSV)); } else { OnSuccess; } PEND
#define CBSOCK(Func) void CPerlSocket::Func() {\
	CPerlModule* pMod = AsPerlModule(GetModule());\
	if (pMod) {\
		SOCKSTART;\
		PUSH_STR("On" #Func);\
		SOCKCBCHECK();\
	}\
}
CBSOCK(Connected);
CBSOCK(Disconnected);
CBSOCK(Timeout);
CBSOCK(ConnectionRefused);

void CPerlSocket::ReadData(const char *data, size_t len) {
	CPerlModule* pMod = AsPerlModule(GetModule());
	if (pMod) {
		SOCKSTART;
		PUSH_STR("OnReadData");
		XPUSHs(sv_2mortal(newSVpvn(data, len)));
		mXPUSHi(len);
		SOCKCBCHECK();
	}
}
void CPerlSocket::ReadLine(const CString& sLine) {
	CPerlModule* pMod = AsPerlModule(GetModule());
	if (pMod) {
		SOCKSTART;
		PUSH_STR("OnReadLine");
		PUSH_STR(sLine);
		SOCKCBCHECK();
	}
}
Csock* CPerlSocket::GetSockObj(const CString& sHost, unsigned short uPort) {
	CPerlModule* pMod = AsPerlModule(GetModule());
	Csock* result = NULL;
	if (pMod) {
		SOCKSTART;
		PUSH_STR("_Accepted");
		PUSH_STR(sHost);
		mXPUSHi(uPort);
		SOCKCBCHECK(
				result = SvToPtr<CPerlSocket>("CPerlSocket*")(ST(0));
		);
	}
	return result;
}

CPerlSocket::~CPerlSocket() {
	CPerlModule* pMod = AsPerlModule(GetModule());
	if (pMod) {
		SOCKSTART;
		PCALL("ZNC::Core::RemoveSocket");
		PEND;
	}
}

GLOBALMODULEDEFS(CModPerl, "Loads perl scripts as ZNC modules")
<commit_msg>Fix warnings "variable 'ax' set but not used [-Wunused-but-set-variable]" of modperl. http://perldoc.perl.org/perlcall.html wants us to declare and set this variable, but it seems that not every perl actually uses it. So let me introduce you this small workaround which uses ax's value.<commit_after>/*
 * Copyright (C) 2004-2011  See the AUTHORS file for details.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License version 2 as published
 * by the Free Software Foundation.
 */

#include "Modules.h"
#include "User.h"
#include "Chan.h"
#include "Nick.h"
#include "znc.h"
#include "IRCSock.h"

#include "modperl/module.h"
#include "modperl/swigperlrun.h"
#include <EXTERN.h>
#include <perl.h>
#include <XSUB.h>

#include "modperl/pstring.h"

// Allows perl to load .so files when needed by .pm
// For example, it needs to load ZNC.so
extern "C" {
	void boot_DynaLoader (pTHX_ CV* cv);
	static void xs_init(pTHX) {
		newXS("DynaLoader::boot_DynaLoader", boot_DynaLoader, __FILE__);
	}
}

class CModPerl: public CGlobalModule {
	PerlInterpreter *m_pPerl;
public:
	GLOBALMODCONSTRUCTOR(CModPerl) {
		m_pPerl = NULL;
	}

#define PSTART dSP; I32 ax; int ret = 0; ENTER; SAVETMPS; PUSHMARK(SP)
#define PCALL(name) PUTBACK; ret = call_pv(name, G_EVAL|G_ARRAY); SPAGAIN; SP -= ret; ax = (SP - PL_stack_base) + 1
#define PEND ax += 0; PUTBACK; FREETMPS; LEAVE
#define PUSH_STR(s) XPUSHs(PString(s).GetSV())
#define PUSH_PTR(type, p) XPUSHs(SWIG_NewInstanceObj(const_cast<type>(p), SWIG_TypeQuery(#type), SWIG_SHADOW))

	bool OnLoad(const CString& sArgsi, CString& sMessage) {
		CString sModPath, sTmp;
		if (!CModules::FindModPath("modperl/startup.pl", sModPath, sTmp)) {
			sMessage = "startup.pl not found.";
			return false;
		}
		sTmp = CDir::ChangeDir(sModPath, "..");
		int argc = 6;
		char *pArgv[] = {"", "-T", "-w",
			"-I", const_cast<char*>(sTmp.c_str()),
			const_cast<char*>(sModPath.c_str()), NULL};
		char **argv = pArgv;
		PERL_SYS_INIT3(&argc, &argv, &environ);
		m_pPerl = perl_alloc();
		perl_construct(m_pPerl);
		if (perl_parse(m_pPerl, xs_init, argc, argv, environ)) {
			perl_free(m_pPerl);
			PERL_SYS_TERM();
			m_pPerl = NULL;
			sMessage = "Can't initialize perl.";
			DEBUG(__PRETTY_FUNCTION__ << " can't init perl");
			return false;
		}
		PL_exit_flags |= PERL_EXIT_DESTRUCT_END;
		PSTART;
		PCALL("ZNC::Core::Init");
		PEND;
		return true;
	}

	ELoadPerlMod LoadPerlModule(const CString& sModule, const CString& sArgs, CUser* pUser, CString& sRetMsg) {
		ELoadPerlMod result = Perl_LoadError;
		PSTART;
		PUSH_STR(sModule);
		PUSH_STR(sArgs);
		PUSH_PTR(CUser*, pUser);
		PCALL("ZNC::Core::LoadModule");

		if (SvTRUE(ERRSV)) {
			sRetMsg = PString(ERRSV);
		} else if (2 == ret) {
			result = static_cast<ELoadPerlMod>(SvUV(ST(0)));
			sRetMsg = PString(ST(1));
		}
		DEBUG(__PRETTY_FUNCTION__ << " " << sRetMsg);

		PEND;
		return result;
	}

	virtual EModRet OnModuleLoading(const CString& sModName, const CString& sArgs,
			bool& bSuccess, CString& sRetMsg) {
		if (!GetUser()) {
			return CONTINUE;
		}
		switch (LoadPerlModule(sModName, sArgs, GetUser(), sRetMsg)) {
			case Perl_NotFound:
				return CONTINUE;
			case Perl_Loaded:
				bSuccess = true;
				return HALT;
			case Perl_LoadError:
				bSuccess = false;
				return HALT;
		}
		sRetMsg = "Something weird happened";
		bSuccess = false;
		return HALT;
	}

	virtual EModRet OnModuleUnloading(CModule* pModule, bool& bSuccess, CString& sRetMsg) {
		CPerlModule* pMod = AsPerlModule(pModule);
		if (pMod) {
			CString sModName = pMod->GetModName();
			PSTART;
			PUSH_PTR(CPerlModule*, pMod);
			PCALL("ZNC::Core::UnloadModule");
			if (SvTRUE(ERRSV)) {
				bSuccess = false;
				sRetMsg = PString(ERRSV);
			} else {
				bSuccess = true;
				sRetMsg = "Module [" + sModName + "] unloaded";
			}
			DEBUG(__PRETTY_FUNCTION__ << " " << sRetMsg);
			return HALT;
		}
		return CONTINUE;
	}

	virtual EModRet OnGetModInfo(CModInfo& ModInfo, const CString& sModule,
			bool& bSuccess, CString& sRetMsg) {
		PSTART;
		PUSH_STR(sModule);
		PCALL("ZNC::Core::GetModInfo");
		EModRet result = CONTINUE;
		if (SvTRUE(ERRSV)) {
			bSuccess = false;
			sRetMsg = PString(ERRSV);
		} else if (0 < ret) {
			switch(static_cast<ELoadPerlMod>(SvUV(ST(0)))) {
				case Perl_NotFound:
					result = CONTINUE;
					break;
				case Perl_Loaded:
					result = HALT;
					if (3 == ret) {
						ModInfo.SetGlobal(false);
						ModInfo.SetDescription(PString(ST(2)));
						ModInfo.SetName(sModule);
						ModInfo.SetPath(PString(ST(1)));
						bSuccess = true;
					} else {
						bSuccess = false;
						sRetMsg = "Something weird happened";
					}
					break;
				case Perl_LoadError:
					result = HALT;
					bSuccess = false;
					if (2 == ret) {
						sRetMsg = PString(ST(1));
					} else {
						sRetMsg = "Something weird happened";
					}
			}
		} else {
			result = HALT;
			bSuccess = false;
			sRetMsg = "Something weird happened";
		}
		PEND;
		DEBUG(__PRETTY_FUNCTION__ << " " << sRetMsg);
		return result;
	}

	virtual void OnGetAvailableMods(set<CModInfo>& ssMods, bool bGlobal) {
		if (bGlobal) {
			return;
		}

		unsigned int a = 0;
		CDir Dir;

		CModules::ModDirList dirs = CModules::GetModDirs();

		while (!dirs.empty()) {
			Dir.FillByWildcard(dirs.front().first, "*.pm");
			dirs.pop();

			for (a = 0; a < Dir.size(); a++) {
				CFile& File = *Dir[a];
				CString sName = File.GetShortName();
				CString sPath = File.GetLongName();
				CModInfo ModInfo;
				sName.RightChomp(3);
				PSTART;
				PUSH_STR(sPath);
				PUSH_STR(sName);
				PCALL("ZNC::Core::ModInfoByPath");
				if (!SvTRUE(ERRSV) && ret == 1) {
					ModInfo.SetGlobal(false);
					ModInfo.SetDescription(PString(ST(0)));
					ModInfo.SetName(sName);
					ModInfo.SetPath(sPath);
					ssMods.insert(ModInfo);
				}
				PEND;
			}
		}
	}

	virtual ~CModPerl() {
		if (m_pPerl) {
			PSTART;
			PCALL("ZNC::Core::UnloadAll");
			PEND;
			perl_destruct(m_pPerl);
			perl_free(m_pPerl);
			PERL_SYS_TERM();
		}
	}

};

#include "modperl/functions.cpp"

VWebSubPages& CPerlModule::GetSubPages() {
	VWebSubPages* result = _GetSubPages();
	if (!result) {
		return CModule::GetSubPages();
	}
	return *result;
}

void CPerlTimer::RunJob() {
	CPerlModule* pMod = AsPerlModule(GetModule());
	if (pMod) {
		PSTART;
		PUSH_STR(pMod->GetPerlID());
		PUSH_STR(GetPerlID());
		PCALL("ZNC::Core::CallTimer");
		PEND;
	}
}

CPerlTimer::~CPerlTimer() {
	CPerlModule* pMod = AsPerlModule(GetModule());
	if (pMod) {
		PSTART;
		PUSH_STR(pMod->GetPerlID());
		PUSH_STR(GetPerlID());
		PCALL("ZNC::Core::RemoveTimer");
		PEND;
	}
}

#define SOCKSTART PSTART; PUSH_STR(pMod->GetPerlID()); PUSH_STR(GetPerlID())
#define SOCKCBCHECK(OnSuccess) PCALL("ZNC::Core::CallSocket"); if (SvTRUE(ERRSV)) { Close(); DEBUG("Perl socket hook died with: " + PString(ERRSV)); } else { OnSuccess; } PEND
#define CBSOCK(Func) void CPerlSocket::Func() {\
	CPerlModule* pMod = AsPerlModule(GetModule());\
	if (pMod) {\
		SOCKSTART;\
		PUSH_STR("On" #Func);\
		SOCKCBCHECK();\
	}\
}
CBSOCK(Connected);
CBSOCK(Disconnected);
CBSOCK(Timeout);
CBSOCK(ConnectionRefused);

void CPerlSocket::ReadData(const char *data, size_t len) {
	CPerlModule* pMod = AsPerlModule(GetModule());
	if (pMod) {
		SOCKSTART;
		PUSH_STR("OnReadData");
		XPUSHs(sv_2mortal(newSVpvn(data, len)));
		mXPUSHi(len);
		SOCKCBCHECK();
	}
}
void CPerlSocket::ReadLine(const CString& sLine) {
	CPerlModule* pMod = AsPerlModule(GetModule());
	if (pMod) {
		SOCKSTART;
		PUSH_STR("OnReadLine");
		PUSH_STR(sLine);
		SOCKCBCHECK();
	}
}
Csock* CPerlSocket::GetSockObj(const CString& sHost, unsigned short uPort) {
	CPerlModule* pMod = AsPerlModule(GetModule());
	Csock* result = NULL;
	if (pMod) {
		SOCKSTART;
		PUSH_STR("_Accepted");
		PUSH_STR(sHost);
		mXPUSHi(uPort);
		SOCKCBCHECK(
				result = SvToPtr<CPerlSocket>("CPerlSocket*")(ST(0));
		);
	}
	return result;
}

CPerlSocket::~CPerlSocket() {
	CPerlModule* pMod = AsPerlModule(GetModule());
	if (pMod) {
		SOCKSTART;
		PCALL("ZNC::Core::RemoveSocket");
		PEND;
	}
}

GLOBALMODULEDEFS(CModPerl, "Loads perl scripts as ZNC modules")
<|endoftext|>