<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 <iostream>

#include "parquet/arrow/reader.h"
#include "parquet/arrow/writer.h"
#include "parquet/column_reader.h"
#include "parquet/column_writer.h"
#include "parquet/file_reader.h"
#include "parquet/file_writer.h"
#include "parquet/platform.h"

#include "arrow/api.h"

using arrow::BooleanBuilder;
using arrow::NumericBuilder;

#define EXIT_NOT_OK(s)                                        \
  do {                                                        \
    ::arrow::Status _s = (s);                                 \
    if (ARROW_PREDICT_FALSE(!_s.ok())) {                      \
      std::cout << "Exiting: " << _s.ToString() << std::endl; \
      exit(EXIT_FAILURE);                                     \
    }                                                         \
  } while (0)

namespace parquet {

using arrow::FileReader;
using arrow::WriteTable;
using schema::PrimitiveNode;

namespace benchmark {

// This should result in multiple pages for most primitive types
constexpr int64_t BENCHMARK_SIZE = 10 * 1024 * 1024;

template <typename ParquetType>
struct benchmark_traits {};

template <>
struct benchmark_traits<Int32Type> {
  using arrow_type = ::arrow::Int32Type;
};

template <>
struct benchmark_traits<Int64Type> {
  using arrow_type = ::arrow::Int64Type;
};

template <>
struct benchmark_traits<DoubleType> {
  using arrow_type = ::arrow::DoubleType;
};

template <>
struct benchmark_traits<BooleanType> {
  using arrow_type = ::arrow::BooleanType;
};

template <typename ParquetType>
using ArrowType = typename benchmark_traits<ParquetType>::arrow_type;

template <typename ParquetType>
std::shared_ptr<ColumnDescriptor> MakeSchema(Repetition::type repetition) {
  auto node = PrimitiveNode::Make("int64", repetition, ParquetType::type_num);
  return std::make_shared<ColumnDescriptor>(node, repetition != Repetition::REQUIRED,
                                            repetition == Repetition::REPEATED);
}

template <bool nullable, typename ParquetType>
void SetBytesProcessed(::benchmark::State& state) {
  int64_t bytes_processed =
      state.iterations() * BENCHMARK_SIZE * sizeof(typename ParquetType::c_type);
  if (nullable) {
    bytes_processed += state.iterations() * BENCHMARK_SIZE * sizeof(int16_t);
  }
  state.SetBytesProcessed(bytes_processed);
}

template <typename ParquetType>
std::shared_ptr<::arrow::Table> TableFromVector(
    const std::vector<typename ParquetType::c_type>& vec, bool nullable) {
  std::shared_ptr<::arrow::DataType> type = std::make_shared<ArrowType<ParquetType>>();
  NumericBuilder<ArrowType<ParquetType>> builder;
  if (nullable) {
    std::vector<uint8_t> valid_bytes(BENCHMARK_SIZE, 0);
    int n = {0};
    std::generate(valid_bytes.begin(), valid_bytes.end(), [&n] { return n++ % 2; });
    EXIT_NOT_OK(builder.AppendValues(vec.data(), vec.size(), valid_bytes.data()));
  } else {
    EXIT_NOT_OK(builder.AppendValues(vec.data(), vec.size(), nullptr));
  }
  std::shared_ptr<::arrow::Array> array;
  EXIT_NOT_OK(builder.Finish(&array));

  auto field = ::arrow::field("column", type, nullable);
  auto schema = ::arrow::schema({field});
  return ::arrow::Table::Make(schema, {array});
}

template <>
std::shared_ptr<::arrow::Table> TableFromVector<BooleanType>(const std::vector<bool>& vec,
                                                             bool nullable) {
  BooleanBuilder builder;
  if (nullable) {
    std::vector<bool> valid_bytes(BENCHMARK_SIZE, 0);
    int n = {0};
    std::generate(valid_bytes.begin(), valid_bytes.end(),
                  [&n] { return (n++ % 2) != 0; });
    EXIT_NOT_OK(builder.AppendValues(vec, valid_bytes));
  } else {
    EXIT_NOT_OK(builder.AppendValues(vec));
  }
  std::shared_ptr<::arrow::Array> array;
  EXIT_NOT_OK(builder.Finish(&array));

  auto field = ::arrow::field("column", ::arrow::boolean(), nullable);
  auto schema = std::make_shared<::arrow::Schema>(
      std::vector<std::shared_ptr<::arrow::Field>>({field}));
  return ::arrow::Table::Make(schema, {array});
}

template <bool nullable, typename ParquetType>
static void BM_WriteColumn(::benchmark::State& state) {
  using T = typename ParquetType::c_type;
  std::vector<T> values(BENCHMARK_SIZE, static_cast<T>(128));
  std::shared_ptr<::arrow::Table> table = TableFromVector<ParquetType>(values, nullable);

  while (state.KeepRunning()) {
    auto output = CreateOutputStream();
    EXIT_NOT_OK(
        WriteTable(*table, ::arrow::default_memory_pool(), output, BENCHMARK_SIZE));
  }
  SetBytesProcessed<nullable, ParquetType>(state);
}

BENCHMARK_TEMPLATE2(BM_WriteColumn, false, Int32Type);
BENCHMARK_TEMPLATE2(BM_WriteColumn, true, Int32Type);

BENCHMARK_TEMPLATE2(BM_WriteColumn, false, Int64Type);
BENCHMARK_TEMPLATE2(BM_WriteColumn, true, Int64Type);

BENCHMARK_TEMPLATE2(BM_WriteColumn, false, DoubleType);
BENCHMARK_TEMPLATE2(BM_WriteColumn, true, DoubleType);

BENCHMARK_TEMPLATE2(BM_WriteColumn, false, BooleanType);
BENCHMARK_TEMPLATE2(BM_WriteColumn, true, BooleanType);

template <bool nullable, typename ParquetType>
static void BM_ReadColumn(::benchmark::State& state) {
  using T = typename ParquetType::c_type;

  std::vector<T> values(BENCHMARK_SIZE, static_cast<T>(128));
  std::shared_ptr<::arrow::Table> table = TableFromVector<ParquetType>(values, nullable);
  auto output = CreateOutputStream();
  EXIT_NOT_OK(WriteTable(*table, ::arrow::default_memory_pool(), output, BENCHMARK_SIZE));

  PARQUET_ASSIGN_OR_THROW(auto buffer, output->Finish());

  while (state.KeepRunning()) {
    auto reader =
        ParquetFileReader::Open(std::make_shared<::arrow::io::BufferReader>(buffer));
    std::unique_ptr<FileReader> arrow_reader;
    EXIT_NOT_OK(FileReader::Make(::arrow::default_memory_pool(), std::move(reader),
                                 &arrow_reader));
    std::shared_ptr<::arrow::Table> table;
    EXIT_NOT_OK(arrow_reader->ReadTable(&table));
  }
  SetBytesProcessed<nullable, ParquetType>(state);
}

BENCHMARK_TEMPLATE2(BM_ReadColumn, false, Int32Type);
BENCHMARK_TEMPLATE2(BM_ReadColumn, true, Int32Type);

BENCHMARK_TEMPLATE2(BM_ReadColumn, false, Int64Type);
BENCHMARK_TEMPLATE2(BM_ReadColumn, true, Int64Type);

BENCHMARK_TEMPLATE2(BM_ReadColumn, false, DoubleType);
BENCHMARK_TEMPLATE2(BM_ReadColumn, true, DoubleType);

BENCHMARK_TEMPLATE2(BM_ReadColumn, false, BooleanType);
BENCHMARK_TEMPLATE2(BM_ReadColumn, true, BooleanType);

static void BM_ReadIndividualRowGroups(::benchmark::State& state) {
  std::vector<int64_t> values(BENCHMARK_SIZE, 128);
  std::shared_ptr<::arrow::Table> table = TableFromVector<Int64Type>(values, true);
  auto output = CreateOutputStream();
  // This writes 10 RowGroups
  EXIT_NOT_OK(
      WriteTable(*table, ::arrow::default_memory_pool(), output, BENCHMARK_SIZE / 10));

  PARQUET_ASSIGN_OR_THROW(auto buffer, output->Finish());

  while (state.KeepRunning()) {
    auto reader =
        ParquetFileReader::Open(std::make_shared<::arrow::io::BufferReader>(buffer));
    std::unique_ptr<FileReader> arrow_reader;
    EXIT_NOT_OK(FileReader::Make(::arrow::default_memory_pool(), std::move(reader),
                                 &arrow_reader));

    std::vector<std::shared_ptr<::arrow::Table>> tables;
    for (int i = 0; i < arrow_reader->num_row_groups(); i++) {
      // Only read the even numbered RowGroups
      if ((i % 2) == 0) {
        std::shared_ptr<::arrow::Table> table;
        EXIT_NOT_OK(arrow_reader->RowGroup(i)->ReadTable(&table));
        tables.push_back(table);
      }
    }

    std::shared_ptr<::arrow::Table> final_table;
    PARQUET_ASSIGN_OR_THROW(final_table, ConcatenateTables(tables));
  }
  SetBytesProcessed<true, Int64Type>(state);
}

BENCHMARK(BM_ReadIndividualRowGroups);

static void BM_ReadMultipleRowGroups(::benchmark::State& state) {
  std::vector<int64_t> values(BENCHMARK_SIZE, 128);
  std::shared_ptr<::arrow::Table> table = TableFromVector<Int64Type>(values, true);
  auto output = CreateOutputStream();
  // This writes 10 RowGroups
  EXIT_NOT_OK(
      WriteTable(*table, ::arrow::default_memory_pool(), output, BENCHMARK_SIZE / 10));
  PARQUET_ASSIGN_OR_THROW(auto buffer, output->Finish());

  while (state.KeepRunning()) {
    auto reader =
        ParquetFileReader::Open(std::make_shared<::arrow::io::BufferReader>(buffer));
    std::unique_ptr<FileReader> arrow_reader;
    EXIT_NOT_OK(FileReader::Make(::arrow::default_memory_pool(), std::move(reader),
                                 &arrow_reader));

    std::vector<std::shared_ptr<::arrow::Table>> tables;
    std::vector<int> rgs;
    for (int i = 0; i < arrow_reader->num_row_groups(); i++) {
      // Only read the even numbered RowGroups
      if ((i % 2) == 0) {
        rgs.push_back(i);
      }
    }

    std::shared_ptr<::arrow::Table> table;
    EXIT_NOT_OK(arrow_reader->ReadRowGroups(rgs, &table));
  }
  SetBytesProcessed<true, Int64Type>(state);
}

BENCHMARK(BM_ReadMultipleRowGroups);

}  // namespace benchmark

}  // namespace parquet
<commit_msg>ARROW-8794: [C++] Expand performance coverage of parquet to arrow reading<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 <iostream>
#include <random>

#include "parquet/arrow/reader.h"
#include "parquet/arrow/writer.h"
#include "parquet/column_reader.h"
#include "parquet/column_writer.h"
#include "parquet/file_reader.h"
#include "parquet/file_writer.h"
#include "parquet/platform.h"

#include "arrow/api.h"
#include "arrow/util/logging.h"

using arrow::BooleanBuilder;
using arrow::NumericBuilder;

#define EXIT_NOT_OK(s)                                        \
  do {                                                        \
    ::arrow::Status _s = (s);                                 \
    if (ARROW_PREDICT_FALSE(!_s.ok())) {                      \
      std::cout << "Exiting: " << _s.ToString() << std::endl; \
      exit(EXIT_FAILURE);                                     \
    }                                                         \
  } while (0)

namespace parquet {

using arrow::FileReader;
using arrow::WriteTable;
using schema::PrimitiveNode;

namespace benchmark {

// This should result in multiple pages for most primitive types
constexpr int64_t BENCHMARK_SIZE = 10 * 1024 * 1024;

template <typename ParquetType>
struct benchmark_traits {};

template <>
struct benchmark_traits<Int32Type> {
  using arrow_type = ::arrow::Int32Type;
};

template <>
struct benchmark_traits<Int64Type> {
  using arrow_type = ::arrow::Int64Type;
};

template <>
struct benchmark_traits<DoubleType> {
  using arrow_type = ::arrow::DoubleType;
};

template <>
struct benchmark_traits<BooleanType> {
  using arrow_type = ::arrow::BooleanType;
};

template <typename ParquetType>
using ArrowType = typename benchmark_traits<ParquetType>::arrow_type;

template <typename ParquetType>
std::shared_ptr<ColumnDescriptor> MakeSchema(Repetition::type repetition) {
  auto node = PrimitiveNode::Make("int64", repetition, ParquetType::type_num);
  return std::make_shared<ColumnDescriptor>(node, repetition != Repetition::REQUIRED,
                                            repetition == Repetition::REPEATED);
}

template <bool nullable, typename ParquetType>
void SetBytesProcessed(::benchmark::State& state) {
  int64_t bytes_processed =
      state.iterations() * BENCHMARK_SIZE * sizeof(typename ParquetType::c_type);
  if (nullable) {
    bytes_processed += state.iterations() * BENCHMARK_SIZE * sizeof(int16_t);
  }
  state.SetBytesProcessed(bytes_processed);
}

constexpr int64_t kAlternatingOrNa = -1;

template <typename T>
std::vector<T> RandomVector(int64_t true_percentage, int64_t vector_size,
                            const std::array<T, 2>& sample_values) {
  std::vector<T> values(BENCHMARK_SIZE, {});
  if (true_percentage == kAlternatingOrNa) {
    int n = {0};
    std::generate(values.begin(), values.end(), [&n] { return n++ % 2; });
  } else {
    std::default_random_engine rng(500);
    double true_probability = static_cast<double>(true_percentage) / 100.0;
    std::bernoulli_distribution dist(true_probability);
    std::generate(values.begin(), values.end(), [&] { return sample_values[dist(rng)]; });
  }
  return values;
}

template <typename ParquetType>
std::shared_ptr<::arrow::Table> TableFromVector(
    const std::vector<typename ParquetType::c_type>& vec, bool nullable,
    int64_t null_percentage = kAlternatingOrNa) {
  if (!nullable) {
    ARROW_CHECK_EQ(null_percentage, kAlternatingOrNa);
  }
  std::shared_ptr<::arrow::DataType> type = std::make_shared<ArrowType<ParquetType>>();
  NumericBuilder<ArrowType<ParquetType>> builder;
  if (nullable) {
    // Note true values select index 1 of sample_values
    auto valid_bytes = RandomVector<uint8_t>(/*true_percentage=*/null_percentage,
                                             BENCHMARK_SIZE, /*sample_values=*/{1, 0});
    EXIT_NOT_OK(builder.AppendValues(vec.data(), vec.size(), valid_bytes.data()));
  } else {
    EXIT_NOT_OK(builder.AppendValues(vec.data(), vec.size(), nullptr));
  }
  std::shared_ptr<::arrow::Array> array;
  EXIT_NOT_OK(builder.Finish(&array));

  auto field = ::arrow::field("column", type, nullable);
  auto schema = ::arrow::schema({field});
  return ::arrow::Table::Make(schema, {array});
}

template <>
std::shared_ptr<::arrow::Table> TableFromVector<BooleanType>(const std::vector<bool>& vec,
                                                             bool nullable,
                                                             int64_t null_percentage) {
  BooleanBuilder builder;
  if (nullable) {
    auto valid_bytes = RandomVector<bool>(/*true_percentage=*/null_percentage,
                                          BENCHMARK_SIZE, {true, false});
    EXIT_NOT_OK(builder.AppendValues(vec, valid_bytes));
  } else {
    EXIT_NOT_OK(builder.AppendValues(vec));
  }
  std::shared_ptr<::arrow::Array> array;
  EXIT_NOT_OK(builder.Finish(&array));

  auto field = ::arrow::field("column", ::arrow::boolean(), nullable);
  auto schema = std::make_shared<::arrow::Schema>(
      std::vector<std::shared_ptr<::arrow::Field>>({field}));
  return ::arrow::Table::Make(schema, {array});
}

template <bool nullable, typename ParquetType>
static void BM_WriteColumn(::benchmark::State& state) {
  using T = typename ParquetType::c_type;
  std::vector<T> values(BENCHMARK_SIZE, 128);
  std::shared_ptr<::arrow::Table> table = TableFromVector<ParquetType>(values, nullable);

  while (state.KeepRunning()) {
    auto output = CreateOutputStream();
    EXIT_NOT_OK(
        WriteTable(*table, ::arrow::default_memory_pool(), output, BENCHMARK_SIZE));
  }
  SetBytesProcessed<nullable, ParquetType>(state);
}

BENCHMARK_TEMPLATE2(BM_WriteColumn, false, Int32Type);
BENCHMARK_TEMPLATE2(BM_WriteColumn, true, Int32Type);

BENCHMARK_TEMPLATE2(BM_WriteColumn, false, Int64Type);
BENCHMARK_TEMPLATE2(BM_WriteColumn, true, Int64Type);

BENCHMARK_TEMPLATE2(BM_WriteColumn, false, DoubleType);
BENCHMARK_TEMPLATE2(BM_WriteColumn, true, DoubleType);

BENCHMARK_TEMPLATE2(BM_WriteColumn, false, BooleanType);
BENCHMARK_TEMPLATE2(BM_WriteColumn, true, BooleanType);

template <typename T>
struct Examples {
  static constexpr std::array<T, 2> values() { return {127, 128}; }
};

template <>
struct Examples<bool> {
  static constexpr std::array<bool, 2> values() { return {false, true}; }
};

template <bool nullable, typename ParquetType>
static void BM_ReadColumn(::benchmark::State& state) {
  using T = typename ParquetType::c_type;

  auto values = RandomVector<T>(/*percentage=*/state.range(1), BENCHMARK_SIZE,
                                Examples<T>::values());

  std::shared_ptr<::arrow::Table> table =
      TableFromVector<ParquetType>(values, nullable, state.range(0));
  auto output = CreateOutputStream();
  EXIT_NOT_OK(WriteTable(*table, ::arrow::default_memory_pool(), output, BENCHMARK_SIZE));

  PARQUET_ASSIGN_OR_THROW(auto buffer, output->Finish());

  while (state.KeepRunning()) {
    auto reader =
        ParquetFileReader::Open(std::make_shared<::arrow::io::BufferReader>(buffer));
    std::unique_ptr<FileReader> arrow_reader;
    EXIT_NOT_OK(FileReader::Make(::arrow::default_memory_pool(), std::move(reader),
                                 &arrow_reader));
    std::shared_ptr<::arrow::Table> table;
    EXIT_NOT_OK(arrow_reader->ReadTable(&table));
  }
  SetBytesProcessed<nullable, ParquetType>(state);
}

// There are two parameters here that cover different data distributions.
// null_percentage governs distribution and therefore runs of null values.
// first_value_percentage governs distribution of values (we select from 1 of 2)
// so when 0 or 100 RLE is triggered all the time.  When a value in the range (0, 100)
// there will be some percentage of RLE encoded values and some percentage of literal
// encoded values (RLE is much less likely with percentages close to 50).
BENCHMARK_TEMPLATE2(BM_ReadColumn, false, Int32Type)
    ->Args({/*null_percentage=*/kAlternatingOrNa, 1})
    ->Args({/*null_percentage=*/kAlternatingOrNa, 10})
    ->Args({/*null_percentage=*/kAlternatingOrNa, 50});

BENCHMARK_TEMPLATE2(BM_ReadColumn, true, Int32Type)
    ->Args({/*null_percentage=*/kAlternatingOrNa, /*first_value_percentage=*/0})
    ->Args({/*null_percentage=*/1, /*first_value_percentage=*/1})
    ->Args({/*null_percentage=*/10, /*first_value_percentage=*/10})
    ->Args({/*null_percentage=*/25, /*first_value_percentage=*/5})
    ->Args({/*null_percentage=*/50, /*first_value_percentage=*/50})
    ->Args({/*null_percentage=*/50, /*first_value_percentage=*/0})
    ->Args({/*null_percentage=*/99, /*first_value_percentage=*/50})
    ->Args({/*null_percentage=*/99, /*first_value_percentage=*/0});

BENCHMARK_TEMPLATE2(BM_ReadColumn, false, Int64Type)
    ->Args({/*null_percentage=*/kAlternatingOrNa, 1})
    ->Args({/*null_percentage=*/kAlternatingOrNa, 10})
    ->Args({/*null_percentage=*/kAlternatingOrNa, 50});
BENCHMARK_TEMPLATE2(BM_ReadColumn, true, Int64Type)
    ->Args({/*null_percentage=*/kAlternatingOrNa, /*first_value_percentage=*/0})
    ->Args({/*null_percentage=*/1, /*first_value_percentage=*/1})
    ->Args({/*null_percentage=*/5, /*first_value_percentage=*/5})
    ->Args({/*null_percentage=*/10, /*first_value_percentage=*/5})
    ->Args({/*null_percentage=*/25, /*first_value_percentage=*/10})
    ->Args({/*null_percentage=*/30, /*first_value_percentage=*/10})
    ->Args({/*null_percentage=*/35, /*first_value_percentage=*/10})
    ->Args({/*null_percentage=*/45, /*first_value_percentage=*/25})
    ->Args({/*null_percentage=*/50, /*first_value_percentage=*/50})
    ->Args({/*null_percentage=*/50, /*first_value_percentage=*/1})
    ->Args({/*null_percentage=*/75, /*first_value_percentage=*/1})
    ->Args({/*null_percentage=*/99, /*first_value_percentage=*/50})
    ->Args({/*null_percentage=*/99, /*first_value_percentage=*/0});

BENCHMARK_TEMPLATE2(BM_ReadColumn, false, DoubleType)
    ->Args({kAlternatingOrNa, 0})
    ->Args({kAlternatingOrNa, 20});
// Less coverage because int64_t should be pretty good representation for nullability and
// repeating values.
BENCHMARK_TEMPLATE2(BM_ReadColumn, true, DoubleType)
    ->Args({/*null_percentage=*/kAlternatingOrNa, /*first_value_percentage=*/0})
    ->Args({/*null_percentage=*/10, /*first_value_percentage=*/50})
    ->Args({/*null_percentage=*/25, /*first_value_percentage=*/25});

BENCHMARK_TEMPLATE2(BM_ReadColumn, false, BooleanType)
    ->Args({kAlternatingOrNa, 0})
    ->Args({1, 20});
BENCHMARK_TEMPLATE2(BM_ReadColumn, true, BooleanType)
    ->Args({kAlternatingOrNa, 1})
    ->Args({5, 10});

static void BM_ReadIndividualRowGroups(::benchmark::State& state) {
  std::vector<int64_t> values(BENCHMARK_SIZE, 128);
  std::shared_ptr<::arrow::Table> table = TableFromVector<Int64Type>(values, true);
  auto output = CreateOutputStream();
  // This writes 10 RowGroups
  EXIT_NOT_OK(
      WriteTable(*table, ::arrow::default_memory_pool(), output, BENCHMARK_SIZE / 10));

  PARQUET_ASSIGN_OR_THROW(auto buffer, output->Finish());

  while (state.KeepRunning()) {
    auto reader =
        ParquetFileReader::Open(std::make_shared<::arrow::io::BufferReader>(buffer));
    std::unique_ptr<FileReader> arrow_reader;
    EXIT_NOT_OK(FileReader::Make(::arrow::default_memory_pool(), std::move(reader),
                                 &arrow_reader));

    std::vector<std::shared_ptr<::arrow::Table>> tables;
    for (int i = 0; i < arrow_reader->num_row_groups(); i++) {
      // Only read the even numbered RowGroups
      if ((i % 2) == 0) {
        std::shared_ptr<::arrow::Table> table;
        EXIT_NOT_OK(arrow_reader->RowGroup(i)->ReadTable(&table));
        tables.push_back(table);
      }
    }

    std::shared_ptr<::arrow::Table> final_table;
    PARQUET_ASSIGN_OR_THROW(final_table, ConcatenateTables(tables));
  }
  SetBytesProcessed<true, Int64Type>(state);
}

BENCHMARK(BM_ReadIndividualRowGroups);

static void BM_ReadMultipleRowGroups(::benchmark::State& state) {
  std::vector<int64_t> values(BENCHMARK_SIZE, 128);
  std::shared_ptr<::arrow::Table> table = TableFromVector<Int64Type>(values, true);
  auto output = CreateOutputStream();
  // This writes 10 RowGroups
  EXIT_NOT_OK(
      WriteTable(*table, ::arrow::default_memory_pool(), output, BENCHMARK_SIZE / 10));
  PARQUET_ASSIGN_OR_THROW(auto buffer, output->Finish());

  while (state.KeepRunning()) {
    auto reader =
        ParquetFileReader::Open(std::make_shared<::arrow::io::BufferReader>(buffer));
    std::unique_ptr<FileReader> arrow_reader;
    EXIT_NOT_OK(FileReader::Make(::arrow::default_memory_pool(), std::move(reader),
                                 &arrow_reader));

    std::vector<std::shared_ptr<::arrow::Table>> tables;
    std::vector<int> rgs;
    for (int i = 0; i < arrow_reader->num_row_groups(); i++) {
      // Only read the even numbered RowGroups
      if ((i % 2) == 0) {
        rgs.push_back(i);
      }
    }

    std::shared_ptr<::arrow::Table> table;
    EXIT_NOT_OK(arrow_reader->ReadRowGroups(rgs, &table));
  }
  SetBytesProcessed<true, Int64Type>(state);
}

BENCHMARK(BM_ReadMultipleRowGroups);

}  // namespace benchmark

}  // namespace parquet
<|endoftext|>


<commit_before>/***********************************************************************
 manip.cpp - Implements MySQL++'s various quoting/escaping stream
	manipulators.

 Copyright (c) 1998 by Kevin Atkinson, (c) 1999, 2000 and 2001 by
 MySQL AB, and (c) 2004, 2005 by Educational Technology Resources, Inc.
 Others may also hold copyrights on code in this file.  See the CREDITS
 file in the top directory of the distribution for details.

 This file is part of MySQL++.

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

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

 You should have received a copy of the GNU Lesser General Public
 License along with MySQL++; if not, write to the Free Software
 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301
 USA
***********************************************************************/

#include "manip.h"

#include "query.h"

using namespace std;

// Manipulator stuff is _always_ in namespace mysqlpp.
namespace mysqlpp {

/// \brief Set to true if you want to suppress automatic quoting
///
/// Works only for ColData inserted into C++ streams.

bool dont_quote_auto = false;

/// \brief Inserts a SQLString into a stream, quoted and escaped.
///
/// If in.is_string is set and in.dont_escape is \e not set, the string
/// is quoted and escaped.
///
/// If both in.is_string and in.dont_escape are set, the string is
/// quoted but not escaped.
///
/// If in.is_string is not set, the data is inserted as-is.  This is
/// the case when you initialize SQLString with one of the constructors
/// taking an integral type, for instance.

SQLQueryParms& operator <<(quote_type2 p, SQLString& in)
{
	if (in.is_string) {
		SQLString in2('\'');
		if (in.dont_escape) {
			in2 += in;
			in2 += '\'';
			in2.processed = true;
			return *p.qparms << in2;
		}
		else {
			char* s = new char[in.length() * 2 + 1];
			size_t len = mysql_escape_string(s, in.data(), in.length());
			in2.append(s, len);
			in2 += '\'';
			in2.processed = true;
			*p.qparms << in2;
			delete[] s;
			return *p.qparms;
		}
	}
	else {
		in.processed = true;
		return *p.qparms << in;
	}
}

/// \brief Inserts a C++ string into a stream, quoted and escaped
///
/// Because std::string lacks the type information we need, the string
/// is both quoted and escaped, always.

template <>
ostream& operator <<(quote_type1 o, const string& in)
{
	char* s = new char[in.length() * 2 + 1];
	size_t len = mysql_escape_string(s, in.data(), in.length());

	o.ostr->write("'", 1);
	o.ostr->write(s, len);
	o.ostr->write("'", 1);

	delete[] s;
	return *o.ostr;
}

/// \brief Inserts a C string into a stream, quoted and escaped
///
/// Because C strings lack the type information we need, the string
/// is both quoted and escaped, always.

template <>
ostream& operator <<(quote_type1 o, const char* const& in)
{
	size_t len = strlen(in);
	char* s = new char[len * 2 + 1];
	len = mysql_escape_string(s, in, len);

	o.ostr->write("'", 1);
	o.ostr->write(s, len);
	o.ostr->write("'", 1);

	delete[] s;
	return *o.ostr;
}

/// \brief Inserts a ColData with const string into a stream, quoted and
/// escaped
///
/// Because ColData was designed to contain MySQL type data, we may
/// choose not to actually quote or escape the data, if it is not
/// needed.

template <>
ostream& operator <<(quote_type1 o, const ColData& in)
{
	if (in.escape_q()) {
		char* s = new char[in.length() * 2 + 1];
		size_t len = mysql_escape_string(s, in.data(), in.length());

		if (in.quote_q()) o.ostr->write("'", 1);
		o.ostr->write(s, len);
		if (in.quote_q()) o.ostr->write("'", 1);

		delete[] s;
	}
	else {
		if (in.quote_q()) o.ostr->write("'", 1);
		o.ostr->write(in.data(), in.length());
		if (in.quote_q()) o.ostr->write("'", 1);
	}

	return *o.ostr;
}

/// \brief Inserts a ColData into a non-Query stream.
///
/// Although we know how to automatically quote and escape ColData
/// objects, we only do that when inserting them into Query streams
/// because this feature is only intended to make it easier to build
/// syntactically-correct SQL queries.  You can force the library to
/// give you quoting and escaping with the quote manipulator:
///
/// \code
/// mysqlpp::ColData cd("...");
/// cout << mysqlpp::quote << cd << endl;
/// \endcode

ostream& operator <<(ostream& o, const ColData& in)
{
	o.write(in.data(), in.length());
	return o;
}

/// \brief Insert a ColData with const string into a SQLQuery
///
/// This operator appears to be a workaround for a weakness in one
/// compiler's implementation of the C++ type system.  See Wishlist for
/// current plan on what to do about this.

Query& operator <<(Query& o, const ColData& in)
{
	if (dont_quote_auto) {
		o.write(in.data(), in.length());
	}
	else if (in.escape_q()) {
		char* s = new char[in.length() * 2 + 1];
		size_t len = mysql_escape_string(s, in.data(), in.length());

		if (in.quote_q()) o.write("'", 1);
		o.write(s, len);
		if (in.quote_q()) o.write("'", 1);

		delete[] s;
	}
	else {
		if (in.quote_q()) o.write("'", 1);
		o.write(in.data(), in.length());
		if (in.quote_q()) o.write("'", 1);
	}

	return o;
}

/// \brief Inserts a SQLString into a stream, quoting it unless it's
/// data that needs no quoting.
///
/// We make the decision to quote the data based on the in.is_string
/// flag.  You can set it yourself, but SQLString's ctors should set
/// it correctly for you.

SQLQueryParms& operator <<(quote_only_type2 p, SQLString& in)
{
	if (in.is_string) {
		SQLString in2;
		in2 = '\'' + in + '\'';
		in2.processed = true;
		return *p.qparms << in2;
	}
	else {
		in.processed = true;
		return *p.qparms << in;
	}
}

/// \brief Inserts a ColData with const string into a stream, quoted
///
/// Because ColData was designed to contain MySQL type data, we may
/// choose not to actually quote the data, if it is not needed.

template <>
ostream& operator <<(quote_only_type1 o, const ColData& in)
{
	if (in.quote_q()) o.ostr->write("'", 1);
	o.ostr->write(in.data(), in.length());
	if (in.quote_q()) o.ostr->write("'", 1);

	return *o.ostr;
}

/// \brief Inserts a SQLString into a stream, double-quoting it (")
/// unless it's data that needs no quoting.
///
/// We make the decision to quote the data based on the in.is_string
/// flag.  You can set it yourself, but SQLString's ctors should set
/// it correctly for you.

SQLQueryParms& operator <<(quote_double_only_type2 p, SQLString& in)
{
	if (in.is_string) {
		SQLString in2;
		in2 = "\"" + in + "\"";
		in2.processed = true;
		return *p.qparms << in2;
	}
	else {
		in.processed = true;
		return *p.qparms << in;
	}
}

/// \brief Inserts a ColData with const string into a stream,
/// double-quoted (")
///
/// Because ColData was designed to contain MySQL type data, we may
/// choose not to actually quote the data, if it is not needed.

template <>
ostream& operator <<(quote_double_only_type1 o, const ColData& in)
{
	if (in.quote_q()) o.ostr->write("'", 1);
	o.ostr->write(in.data(), in.length());
	if (in.quote_q()) o.ostr->write("'", 1);

	return *o.ostr;
}

SQLQueryParms& operator <<(escape_type2 p, SQLString& in)
{
	if (in.is_string && !in.dont_escape) {
		char* s = new char[in.length() * 2 + 1];
		size_t len = mysql_escape_string(s, in.data(), in.length());

		SQLString in2(s, len);
		in2.processed = true;
		*p.qparms << in2;

		delete[] s;
		return *p.qparms;
	}
	else {
		in.processed = true;
		return *p.qparms << in;
	}
}

/// \brief Inserts a C++ string into a stream, escaping special SQL
/// characters
///
/// Because std::string lacks the type information we need, the string
/// is always escaped, even if it doesn't need it.

template <>
std::ostream& operator <<(escape_type1 o, const std::string& in)
{
	char* s = new char[in.length() * 2 + 1];
	size_t len = mysql_escape_string(s, in.data(), in.length());
	o.ostr->write(s, len);
	delete[] s;
	return *o.ostr;
}

/// \brief Inserts a C string into a stream, escaping special SQL
/// characters
///
/// Because C's type system lacks the information we need to second-
/// guess this manipulator, we always run the escaping algorithm on
/// the data, even if it's not needed.

template <>
ostream& operator <<(escape_type1 o, const char* const& in)
{
	size_t len = strlen(in);
	char* s = new char[len * 2 + 1];
	len = mysql_escape_string(s, in, len);
	o.ostr->write(s, len);
	delete[] s;
	return *o.ostr;
}

/// \brief Inserts a ColData with const string into a stream, escaping
/// special SQL characters
///
/// Because ColData was designed to contain MySQL type data, we may
/// choose not to escape the data, if it is not needed.

std::ostream& operator <<(escape_type1 o, const ColData& in)
{
	if (in.escape_q()) {
		char* s = new char[in.length() * 2 + 1];
		size_t len = mysql_escape_string(s, in.data(), in.length());
		o.ostr->write(s, len);
		delete[] s;
	}
	else {
		o.ostr->write(in.data(), in.length());
	}

	return *o.ostr;
}

/// \brief Inserts a SQLString into a stream, with no escaping or
/// quoting.

SQLQueryParms& operator <<(do_nothing_type2 p, SQLString& in)
{
	in.processed = true;
	return *p.qparms << in;
}

/// \brief Inserts a SQLString into a stream, with no escaping or
/// quoting, and without marking the string as having been "processed".

SQLQueryParms& operator <<(ignore_type2 p, SQLString& in)
{
	return *p.qparms << in;
}

} // end namespace mysqlpp

<commit_msg>Fixed a bug in quote_double_only manipulator for ColData: was using single quotes by mistake.<commit_after>/***********************************************************************
 manip.cpp - Implements MySQL++'s various quoting/escaping stream
	manipulators.

 Copyright (c) 1998 by Kevin Atkinson, (c) 1999, 2000 and 2001 by
 MySQL AB, and (c) 2004, 2005 by Educational Technology Resources, Inc.
 Others may also hold copyrights on code in this file.  See the CREDITS
 file in the top directory of the distribution for details.

 This file is part of MySQL++.

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

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

 You should have received a copy of the GNU Lesser General Public
 License along with MySQL++; if not, write to the Free Software
 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301
 USA
***********************************************************************/

#include "manip.h"

#include "query.h"

using namespace std;

// Manipulator stuff is _always_ in namespace mysqlpp.
namespace mysqlpp {

/// \brief Set to true if you want to suppress automatic quoting
///
/// Works only for ColData inserted into C++ streams.

bool dont_quote_auto = false;

/// \brief Inserts a SQLString into a stream, quoted and escaped.
///
/// If in.is_string is set and in.dont_escape is \e not set, the string
/// is quoted and escaped.
///
/// If both in.is_string and in.dont_escape are set, the string is
/// quoted but not escaped.
///
/// If in.is_string is not set, the data is inserted as-is.  This is
/// the case when you initialize SQLString with one of the constructors
/// taking an integral type, for instance.

SQLQueryParms& operator <<(quote_type2 p, SQLString& in)
{
	if (in.is_string) {
		SQLString in2('\'');
		if (in.dont_escape) {
			in2 += in;
			in2 += '\'';
			in2.processed = true;
			return *p.qparms << in2;
		}
		else {
			char* s = new char[in.length() * 2 + 1];
			size_t len = mysql_escape_string(s, in.data(), in.length());
			in2.append(s, len);
			in2 += '\'';
			in2.processed = true;
			*p.qparms << in2;
			delete[] s;
			return *p.qparms;
		}
	}
	else {
		in.processed = true;
		return *p.qparms << in;
	}
}

/// \brief Inserts a C++ string into a stream, quoted and escaped
///
/// Because std::string lacks the type information we need, the string
/// is both quoted and escaped, always.

template <>
ostream& operator <<(quote_type1 o, const string& in)
{
	char* s = new char[in.length() * 2 + 1];
	size_t len = mysql_escape_string(s, in.data(), in.length());

	o.ostr->write("'", 1);
	o.ostr->write(s, len);
	o.ostr->write("'", 1);

	delete[] s;
	return *o.ostr;
}

/// \brief Inserts a C string into a stream, quoted and escaped
///
/// Because C strings lack the type information we need, the string
/// is both quoted and escaped, always.

template <>
ostream& operator <<(quote_type1 o, const char* const& in)
{
	size_t len = strlen(in);
	char* s = new char[len * 2 + 1];
	len = mysql_escape_string(s, in, len);

	o.ostr->write("'", 1);
	o.ostr->write(s, len);
	o.ostr->write("'", 1);

	delete[] s;
	return *o.ostr;
}

/// \brief Inserts a ColData with const string into a stream, quoted and
/// escaped
///
/// Because ColData was designed to contain MySQL type data, we may
/// choose not to actually quote or escape the data, if it is not
/// needed.

template <>
ostream& operator <<(quote_type1 o, const ColData& in)
{
	if (in.escape_q()) {
		char* s = new char[in.length() * 2 + 1];
		size_t len = mysql_escape_string(s, in.data(), in.length());

		if (in.quote_q()) o.ostr->write("'", 1);
		o.ostr->write(s, len);
		if (in.quote_q()) o.ostr->write("'", 1);

		delete[] s;
	}
	else {
		if (in.quote_q()) o.ostr->write("'", 1);
		o.ostr->write(in.data(), in.length());
		if (in.quote_q()) o.ostr->write("'", 1);
	}

	return *o.ostr;
}

/// \brief Inserts a ColData into a non-Query stream.
///
/// Although we know how to automatically quote and escape ColData
/// objects, we only do that when inserting them into Query streams
/// because this feature is only intended to make it easier to build
/// syntactically-correct SQL queries.  You can force the library to
/// give you quoting and escaping with the quote manipulator:
///
/// \code
/// mysqlpp::ColData cd("...");
/// cout << mysqlpp::quote << cd << endl;
/// \endcode

ostream& operator <<(ostream& o, const ColData& in)
{
	o.write(in.data(), in.length());
	return o;
}

/// \brief Insert a ColData with const string into a SQLQuery
///
/// This operator appears to be a workaround for a weakness in one
/// compiler's implementation of the C++ type system.  See Wishlist for
/// current plan on what to do about this.

Query& operator <<(Query& o, const ColData& in)
{
	if (dont_quote_auto) {
		o.write(in.data(), in.length());
	}
	else if (in.escape_q()) {
		char* s = new char[in.length() * 2 + 1];
		size_t len = mysql_escape_string(s, in.data(), in.length());

		if (in.quote_q()) o.write("'", 1);
		o.write(s, len);
		if (in.quote_q()) o.write("'", 1);

		delete[] s;
	}
	else {
		if (in.quote_q()) o.write("'", 1);
		o.write(in.data(), in.length());
		if (in.quote_q()) o.write("'", 1);
	}

	return o;
}

/// \brief Inserts a SQLString into a stream, quoting it unless it's
/// data that needs no quoting.
///
/// We make the decision to quote the data based on the in.is_string
/// flag.  You can set it yourself, but SQLString's ctors should set
/// it correctly for you.

SQLQueryParms& operator <<(quote_only_type2 p, SQLString& in)
{
	if (in.is_string) {
		SQLString in2;
		in2 = '\'' + in + '\'';
		in2.processed = true;
		return *p.qparms << in2;
	}
	else {
		in.processed = true;
		return *p.qparms << in;
	}
}

/// \brief Inserts a ColData with const string into a stream, quoted
///
/// Because ColData was designed to contain MySQL type data, we may
/// choose not to actually quote the data, if it is not needed.

template <>
ostream& operator <<(quote_only_type1 o, const ColData& in)
{
	if (in.quote_q()) o.ostr->write("'", 1);
	o.ostr->write(in.data(), in.length());
	if (in.quote_q()) o.ostr->write("'", 1);

	return *o.ostr;
}

/// \brief Inserts a SQLString into a stream, double-quoting it (")
/// unless it's data that needs no quoting.
///
/// We make the decision to quote the data based on the in.is_string
/// flag.  You can set it yourself, but SQLString's ctors should set
/// it correctly for you.

SQLQueryParms& operator <<(quote_double_only_type2 p, SQLString& in)
{
	if (in.is_string) {
		SQLString in2;
		in2 = "\"" + in + "\"";
		in2.processed = true;
		return *p.qparms << in2;
	}
	else {
		in.processed = true;
		return *p.qparms << in;
	}
}

/// \brief Inserts a ColData with const string into a stream,
/// double-quoted (")
///
/// Because ColData was designed to contain MySQL type data, we may
/// choose not to actually quote the data, if it is not needed.

template <>
ostream& operator <<(quote_double_only_type1 o, const ColData& in)
{
	if (in.quote_q()) o.ostr->write("\"", 1);
	o.ostr->write(in.data(), in.length());
	if (in.quote_q()) o.ostr->write("\"", 1);

	return *o.ostr;
}

SQLQueryParms& operator <<(escape_type2 p, SQLString& in)
{
	if (in.is_string && !in.dont_escape) {
		char* s = new char[in.length() * 2 + 1];
		size_t len = mysql_escape_string(s, in.data(), in.length());

		SQLString in2(s, len);
		in2.processed = true;
		*p.qparms << in2;

		delete[] s;
		return *p.qparms;
	}
	else {
		in.processed = true;
		return *p.qparms << in;
	}
}

/// \brief Inserts a C++ string into a stream, escaping special SQL
/// characters
///
/// Because std::string lacks the type information we need, the string
/// is always escaped, even if it doesn't need it.

template <>
std::ostream& operator <<(escape_type1 o, const std::string& in)
{
	char* s = new char[in.length() * 2 + 1];
	size_t len = mysql_escape_string(s, in.data(), in.length());
	o.ostr->write(s, len);
	delete[] s;
	return *o.ostr;
}

/// \brief Inserts a C string into a stream, escaping special SQL
/// characters
///
/// Because C's type system lacks the information we need to second-
/// guess this manipulator, we always run the escaping algorithm on
/// the data, even if it's not needed.

template <>
ostream& operator <<(escape_type1 o, const char* const& in)
{
	size_t len = strlen(in);
	char* s = new char[len * 2 + 1];
	len = mysql_escape_string(s, in, len);
	o.ostr->write(s, len);
	delete[] s;
	return *o.ostr;
}

/// \brief Inserts a ColData with const string into a stream, escaping
/// special SQL characters
///
/// Because ColData was designed to contain MySQL type data, we may
/// choose not to escape the data, if it is not needed.

std::ostream& operator <<(escape_type1 o, const ColData& in)
{
	if (in.escape_q()) {
		char* s = new char[in.length() * 2 + 1];
		size_t len = mysql_escape_string(s, in.data(), in.length());
		o.ostr->write(s, len);
		delete[] s;
	}
	else {
		o.ostr->write(in.data(), in.length());
	}

	return *o.ostr;
}

/// \brief Inserts a SQLString into a stream, with no escaping or
/// quoting.

SQLQueryParms& operator <<(do_nothing_type2 p, SQLString& in)
{
	in.processed = true;
	return *p.qparms << in;
}

/// \brief Inserts a SQLString into a stream, with no escaping or
/// quoting, and without marking the string as having been "processed".

SQLQueryParms& operator <<(ignore_type2 p, SQLString& in)
{
	return *p.qparms << in;
}

} // end namespace mysqlpp

<|endoftext|>


<commit_before>// sol2

// The MIT License (MIT)

// Copyright (c) 2013-2018 Rapptz, ThePhD and contributors

// Permission is hereby granted, free of charge, to any person obtaining a copy of
// this software and associated documentation files (the "Software"), to deal in
// the Software without restriction, including without limitation the rights to
// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
// the Software, and to permit persons to whom the Software is furnished to do so,
// subject to the following conditions:

// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.

// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
// FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
// IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

#ifndef SOL_PROXY_HPP
#define SOL_PROXY_HPP

#include "traits.hpp"
#include "function.hpp"
#include "protected_function.hpp"
#include "proxy_base.hpp"

namespace sol {
	template <typename Table, typename Key>
	struct proxy : public proxy_base<proxy<Table, Key>> {
	private:
		typedef meta::condition<meta::is_specialization_of<Key, std::tuple>, Key, std::tuple<meta::condition<std::is_array<meta::unqualified_t<Key>>, Key&, meta::unqualified_t<Key>>>> key_type;

		template <typename T, std::size_t... I>
		decltype(auto) tuple_get(std::index_sequence<I...>) const {
			return tbl.template traverse_get<T>(std::get<I>(key)...);
		}

		template <std::size_t... I, typename T>
		void tuple_set(std::index_sequence<I...>, T&& value) {
			tbl.traverse_set(std::get<I>(key)..., std::forward<T>(value));
		}

		auto setup_table(std::true_type) {
			auto p = stack::probe_get_field<std::is_same<meta::unqualified_t<Table>, global_table>::value>(lua_state(), key, tbl.stack_index());
			lua_pop(lua_state(), p.levels);
			return p;
		}

		bool is_valid(std::false_type) {
			auto pp = stack::push_pop(tbl);
			auto p = stack::probe_get_field<std::is_same<meta::unqualified_t<Table>, global_table>::value>(lua_state(), key, lua_gettop(lua_state()));
			lua_pop(lua_state(), p.levels);
			return p;
		}

	public:
		Table tbl;
		key_type key;

		template <typename T>
		proxy(Table table, T&& k)
		: tbl(table), key(std::forward<T>(k)) {
		}

		template <typename T>
		proxy& set(T&& item) {
			tuple_set(std::make_index_sequence<std::tuple_size<meta::unqualified_t<key_type>>::value>(), std::forward<T>(item));
			return *this;
		}

		template <typename... Args>
		proxy& set_function(Args&&... args) {
			tbl.set_function(key, std::forward<Args>(args)...);
			return *this;
		}

		template <typename U, meta::enable<meta::neg<is_lua_reference_or_proxy<meta::unwrap_unqualified_t<U>>>, meta::is_callable<meta::unwrap_unqualified_t<U>>> = meta::enabler>
		proxy& operator=(U&& other) {
			return set_function(std::forward<U>(other));
		}

		template <typename U, meta::disable<meta::neg<is_lua_reference_or_proxy<meta::unwrap_unqualified_t<U>>>, meta::is_callable<meta::unwrap_unqualified_t<U>>> = meta::enabler>
		proxy& operator=(U&& other) {
			return set(std::forward<U>(other));
		}

		template <typename T>
		proxy& operator=(std::initializer_list<T> other) {
			return set(std::move(other));
		}

		template <typename T>
		decltype(auto) get() const {
			return tuple_get<T>(std::make_index_sequence<std::tuple_size<meta::unqualified_t<key_type>>::value>());
		}

		template <typename T>
		decltype(auto) get_or(T&& otherwise) const {
			typedef decltype(get<T>()) U;
			optional<U> option = get<optional<U>>();
			if (option) {
				return static_cast<U>(option.value());
			}
			return static_cast<U>(std::forward<T>(otherwise));
		}

		template <typename T, typename D>
		decltype(auto) get_or(D&& otherwise) const {
			optional<T> option = get<optional<T>>();
			if (option) {
				return static_cast<T>(option.value());
			}
			return static_cast<T>(std::forward<D>(otherwise));
		}

		template <typename T>
		decltype(auto) get_or_create() {
			return get_or_create<T>(new_table());
		}

		template <typename T, typename Otherwise>
		decltype(auto) get_or_create(Otherwise&& other) {
			if (!this->valid()) {
				this->set(std::forward<Otherwise>(other));
			}
			return get<T>();
		}

		template <typename K>
		decltype(auto) operator[](K&& k) const {
			auto keys = meta::tuplefy(key, std::forward<K>(k));
			return proxy<Table, decltype(keys)>(tbl, std::move(keys));
		}

		template <typename... Ret, typename... Args>
		decltype(auto) call(Args&&... args) {
#if !defined(__clang__) && defined(_MSC_FULL_VER) && _MSC_FULL_VER >= 191200000
			// MSVC is ass sometimes
			return get<function>().call<Ret...>(std::forward<Args>(args)...);
#else
			return get<function>().template call<Ret...>(std::forward<Args>(args)...);
#endif
		}

		template <typename... Args>
		decltype(auto) operator()(Args&&... args) {
			return call<>(std::forward<Args>(args)...);
		}

		bool valid() const {
			auto pp = stack::push_pop(tbl);
			auto p = stack::probe_get_field<std::is_same<meta::unqualified_t<Table>, global_table>::value>(lua_state(), key, lua_gettop(lua_state()));
			lua_pop(lua_state(), p.levels);
			return p;
		}

		int push() const noexcept {
			return push(this->lua_state());
		}

		int push(lua_State* L) const noexcept {
			return get<reference>().push(L);
		}

		type get_type() const {
			type t = type::none;
			auto pp = stack::push_pop(tbl);
			auto p = stack::probe_get_field<std::is_same<meta::unqualified_t<Table>, global_table>::value>(lua_state(), key, lua_gettop(lua_state()));
			if (p) {
				t = type_of(lua_state(), -1);
			}
			lua_pop(lua_state(), p.levels);
			return t;
		}

		lua_State* lua_state() const {
			return tbl.lua_state();
		}

		proxy& force() {
			if (this->valid()) {
				this->set(new_table());
			}
			return *this;
		}
	};

	template <typename Table, typename Key, typename T>
	inline bool operator==(T&& left, const proxy<Table, Key>& right) {
		typedef decltype(stack::get<T>(nullptr, 0)) U;
		return right.template get<optional<U>>() == left;
	}

	template <typename Table, typename Key, typename T>
	inline bool operator==(const proxy<Table, Key>& right, T&& left) {
		typedef decltype(stack::get<T>(nullptr, 0)) U;
		return right.template get<optional<U>>() == left;
	}

	template <typename Table, typename Key, typename T>
	inline bool operator!=(T&& left, const proxy<Table, Key>& right) {
		typedef decltype(stack::get<T>(nullptr, 0)) U;
		return right.template get<optional<U>>() != left;
	}

	template <typename Table, typename Key, typename T>
	inline bool operator!=(const proxy<Table, Key>& right, T&& left) {
		typedef decltype(stack::get<T>(nullptr, 0)) U;
		return right.template get<optional<U>>() != left;
	}

	template <typename Table, typename Key>
	inline bool operator==(lua_nil_t, const proxy<Table, Key>& right) {
		return !right.valid();
	}

	template <typename Table, typename Key>
	inline bool operator==(const proxy<Table, Key>& right, lua_nil_t) {
		return !right.valid();
	}

	template <typename Table, typename Key>
	inline bool operator!=(lua_nil_t, const proxy<Table, Key>& right) {
		return right.valid();
	}

	template <typename Table, typename Key>
	inline bool operator!=(const proxy<Table, Key>& right, lua_nil_t) {
		return right.valid();
	}

	template <bool b>
	template <typename Super>
	basic_reference<b>& basic_reference<b>::operator=(proxy_base<Super>&& r) {
		basic_reference<b> v = r;
		this->operator=(std::move(v));
		return *this;
	}

	template <bool b>
	template <typename Super>
	basic_reference<b>& basic_reference<b>::operator=(const proxy_base<Super>& r) {
		basic_reference<b> v = r;
		this->operator=(std::move(v));
		return *this;
	}

	namespace stack {
		template <typename Table, typename Key>
		struct pusher<proxy<Table, Key>> {
			static int push(lua_State* L, const proxy<Table, Key>& p) {
				reference r = p;
				return r.push(L);
			}
		};
	} // namespace stack
} // namespace sol

#endif // SOL_PROXY_HPP
<commit_msg>Invert boolean check in proxy::force()<commit_after>// sol2

// The MIT License (MIT)

// Copyright (c) 2013-2018 Rapptz, ThePhD and contributors

// Permission is hereby granted, free of charge, to any person obtaining a copy of
// this software and associated documentation files (the "Software"), to deal in
// the Software without restriction, including without limitation the rights to
// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
// the Software, and to permit persons to whom the Software is furnished to do so,
// subject to the following conditions:

// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.

// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
// FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
// IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

#ifndef SOL_PROXY_HPP
#define SOL_PROXY_HPP

#include "traits.hpp"
#include "function.hpp"
#include "protected_function.hpp"
#include "proxy_base.hpp"

namespace sol {
	template <typename Table, typename Key>
	struct proxy : public proxy_base<proxy<Table, Key>> {
	private:
		typedef meta::condition<meta::is_specialization_of<Key, std::tuple>, Key, std::tuple<meta::condition<std::is_array<meta::unqualified_t<Key>>, Key&, meta::unqualified_t<Key>>>> key_type;

		template <typename T, std::size_t... I>
		decltype(auto) tuple_get(std::index_sequence<I...>) const {
			return tbl.template traverse_get<T>(std::get<I>(key)...);
		}

		template <std::size_t... I, typename T>
		void tuple_set(std::index_sequence<I...>, T&& value) {
			tbl.traverse_set(std::get<I>(key)..., std::forward<T>(value));
		}

		auto setup_table(std::true_type) {
			auto p = stack::probe_get_field<std::is_same<meta::unqualified_t<Table>, global_table>::value>(lua_state(), key, tbl.stack_index());
			lua_pop(lua_state(), p.levels);
			return p;
		}

		bool is_valid(std::false_type) {
			auto pp = stack::push_pop(tbl);
			auto p = stack::probe_get_field<std::is_same<meta::unqualified_t<Table>, global_table>::value>(lua_state(), key, lua_gettop(lua_state()));
			lua_pop(lua_state(), p.levels);
			return p;
		}

	public:
		Table tbl;
		key_type key;

		template <typename T>
		proxy(Table table, T&& k)
		: tbl(table), key(std::forward<T>(k)) {
		}

		template <typename T>
		proxy& set(T&& item) {
			tuple_set(std::make_index_sequence<std::tuple_size<meta::unqualified_t<key_type>>::value>(), std::forward<T>(item));
			return *this;
		}

		template <typename... Args>
		proxy& set_function(Args&&... args) {
			tbl.set_function(key, std::forward<Args>(args)...);
			return *this;
		}

		template <typename U, meta::enable<meta::neg<is_lua_reference_or_proxy<meta::unwrap_unqualified_t<U>>>, meta::is_callable<meta::unwrap_unqualified_t<U>>> = meta::enabler>
		proxy& operator=(U&& other) {
			return set_function(std::forward<U>(other));
		}

		template <typename U, meta::disable<meta::neg<is_lua_reference_or_proxy<meta::unwrap_unqualified_t<U>>>, meta::is_callable<meta::unwrap_unqualified_t<U>>> = meta::enabler>
		proxy& operator=(U&& other) {
			return set(std::forward<U>(other));
		}

		template <typename T>
		proxy& operator=(std::initializer_list<T> other) {
			return set(std::move(other));
		}

		template <typename T>
		decltype(auto) get() const {
			return tuple_get<T>(std::make_index_sequence<std::tuple_size<meta::unqualified_t<key_type>>::value>());
		}

		template <typename T>
		decltype(auto) get_or(T&& otherwise) const {
			typedef decltype(get<T>()) U;
			optional<U> option = get<optional<U>>();
			if (option) {
				return static_cast<U>(option.value());
			}
			return static_cast<U>(std::forward<T>(otherwise));
		}

		template <typename T, typename D>
		decltype(auto) get_or(D&& otherwise) const {
			optional<T> option = get<optional<T>>();
			if (option) {
				return static_cast<T>(option.value());
			}
			return static_cast<T>(std::forward<D>(otherwise));
		}

		template <typename T>
		decltype(auto) get_or_create() {
			return get_or_create<T>(new_table());
		}

		template <typename T, typename Otherwise>
		decltype(auto) get_or_create(Otherwise&& other) {
			if (!this->valid()) {
				this->set(std::forward<Otherwise>(other));
			}
			return get<T>();
		}

		template <typename K>
		decltype(auto) operator[](K&& k) const {
			auto keys = meta::tuplefy(key, std::forward<K>(k));
			return proxy<Table, decltype(keys)>(tbl, std::move(keys));
		}

		template <typename... Ret, typename... Args>
		decltype(auto) call(Args&&... args) {
#if !defined(__clang__) && defined(_MSC_FULL_VER) && _MSC_FULL_VER >= 191200000
			// MSVC is ass sometimes
			return get<function>().call<Ret...>(std::forward<Args>(args)...);
#else
			return get<function>().template call<Ret...>(std::forward<Args>(args)...);
#endif
		}

		template <typename... Args>
		decltype(auto) operator()(Args&&... args) {
			return call<>(std::forward<Args>(args)...);
		}

		bool valid() const {
			auto pp = stack::push_pop(tbl);
			auto p = stack::probe_get_field<std::is_same<meta::unqualified_t<Table>, global_table>::value>(lua_state(), key, lua_gettop(lua_state()));
			lua_pop(lua_state(), p.levels);
			return p;
		}

		int push() const noexcept {
			return push(this->lua_state());
		}

		int push(lua_State* L) const noexcept {
			return get<reference>().push(L);
		}

		type get_type() const {
			type t = type::none;
			auto pp = stack::push_pop(tbl);
			auto p = stack::probe_get_field<std::is_same<meta::unqualified_t<Table>, global_table>::value>(lua_state(), key, lua_gettop(lua_state()));
			if (p) {
				t = type_of(lua_state(), -1);
			}
			lua_pop(lua_state(), p.levels);
			return t;
		}

		lua_State* lua_state() const {
			return tbl.lua_state();
		}

		proxy& force() {
			if (!this->valid()) {
				this->set(new_table());
			}
			return *this;
		}
	};

	template <typename Table, typename Key, typename T>
	inline bool operator==(T&& left, const proxy<Table, Key>& right) {
		typedef decltype(stack::get<T>(nullptr, 0)) U;
		return right.template get<optional<U>>() == left;
	}

	template <typename Table, typename Key, typename T>
	inline bool operator==(const proxy<Table, Key>& right, T&& left) {
		typedef decltype(stack::get<T>(nullptr, 0)) U;
		return right.template get<optional<U>>() == left;
	}

	template <typename Table, typename Key, typename T>
	inline bool operator!=(T&& left, const proxy<Table, Key>& right) {
		typedef decltype(stack::get<T>(nullptr, 0)) U;
		return right.template get<optional<U>>() != left;
	}

	template <typename Table, typename Key, typename T>
	inline bool operator!=(const proxy<Table, Key>& right, T&& left) {
		typedef decltype(stack::get<T>(nullptr, 0)) U;
		return right.template get<optional<U>>() != left;
	}

	template <typename Table, typename Key>
	inline bool operator==(lua_nil_t, const proxy<Table, Key>& right) {
		return !right.valid();
	}

	template <typename Table, typename Key>
	inline bool operator==(const proxy<Table, Key>& right, lua_nil_t) {
		return !right.valid();
	}

	template <typename Table, typename Key>
	inline bool operator!=(lua_nil_t, const proxy<Table, Key>& right) {
		return right.valid();
	}

	template <typename Table, typename Key>
	inline bool operator!=(const proxy<Table, Key>& right, lua_nil_t) {
		return right.valid();
	}

	template <bool b>
	template <typename Super>
	basic_reference<b>& basic_reference<b>::operator=(proxy_base<Super>&& r) {
		basic_reference<b> v = r;
		this->operator=(std::move(v));
		return *this;
	}

	template <bool b>
	template <typename Super>
	basic_reference<b>& basic_reference<b>::operator=(const proxy_base<Super>& r) {
		basic_reference<b> v = r;
		this->operator=(std::move(v));
		return *this;
	}

	namespace stack {
		template <typename Table, typename Key>
		struct pusher<proxy<Table, Key>> {
			static int push(lua_State* L, const proxy<Table, Key>& p) {
				reference r = p;
				return r.push(L);
			}
		};
	} // namespace stack
} // namespace sol

#endif // SOL_PROXY_HPP
<|endoftext|>


<commit_before>/*
 * Copyright 2010, Nikhil Marathe <nsm.nikhil@gmail.com> All rights reserved.
 * See LICENSE for details.
*/
#include "sasljs.h"

#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <iostream>

using namespace v8;
using namespace node;

/*
 * Macro from the sqlite3 bindings
 * http://github.com/grumdrig/node-sqlite/blob/master/sqlite3_bindings.cc
 * by Eric Fredricksen
 */
#define REQ_STR_ARG(I, VAR)                                             \
      if (args.Length() <= (I) || !args[I]->IsString())                     \
    return ThrowException(Exception::TypeError(                         \
                                  String::New("Argument " #I " must be a string"))); \
  String::Utf8Value VAR(args[I]->ToString());

namespace sasljs {
void ServerSession::Initialize ( Handle<Object> target )
{
  v8::HandleScope scope;

  v8::Local<v8::FunctionTemplate> t = v8::FunctionTemplate::New(New);

  t->InstanceTemplate()->SetInternalFieldCount(1);

  // error codes
  NODE_DEFINE_CONSTANT( target, GSASL_OK  );
  NODE_DEFINE_CONSTANT( target, GSASL_NEEDS_MORE  );
  NODE_DEFINE_CONSTANT( target, GSASL_UNKNOWN_MECHANISM  );
  NODE_DEFINE_CONSTANT( target, GSASL_MECHANISM_CALLED_TOO_MANY_TIMES  );
  NODE_DEFINE_CONSTANT( target, GSASL_MALLOC_ERROR  );
  NODE_DEFINE_CONSTANT( target, GSASL_BASE64_ERROR  );
  NODE_DEFINE_CONSTANT( target, GSASL_CRYPTO_ERROR  );
  NODE_DEFINE_CONSTANT( target, GSASL_SASLPREP_ERROR  );
  NODE_DEFINE_CONSTANT( target, GSASL_MECHANISM_PARSE_ERROR  );
  NODE_DEFINE_CONSTANT( target, GSASL_AUTHENTICATION_ERROR  );
  NODE_DEFINE_CONSTANT( target, GSASL_INTEGRITY_ERROR  );
  NODE_DEFINE_CONSTANT( target, GSASL_NO_CLIENT_CODE  );
  NODE_DEFINE_CONSTANT( target, GSASL_NO_SERVER_CODE  );
  NODE_DEFINE_CONSTANT( target, GSASL_NO_CALLBACK  );
  NODE_DEFINE_CONSTANT( target, GSASL_NO_ANONYMOUS_TOKEN  );
  NODE_DEFINE_CONSTANT( target, GSASL_NO_AUTHID  );
  NODE_DEFINE_CONSTANT( target, GSASL_NO_AUTHZID  );
  NODE_DEFINE_CONSTANT( target, GSASL_NO_PASSWORD  );
  NODE_DEFINE_CONSTANT( target, GSASL_NO_PASSCODE  );
  NODE_DEFINE_CONSTANT( target, GSASL_NO_PIN  );
  NODE_DEFINE_CONSTANT( target, GSASL_NO_SERVICE  );
  NODE_DEFINE_CONSTANT( target, GSASL_NO_HOSTNAME  );
  NODE_DEFINE_CONSTANT( target, GSASL_GSSAPI_RELEASE_BUFFER_ERROR  );
  NODE_DEFINE_CONSTANT( target, GSASL_GSSAPI_IMPORT_NAME_ERROR  );
  NODE_DEFINE_CONSTANT( target, GSASL_GSSAPI_INIT_SEC_CONTEXT_ERROR  );
  NODE_DEFINE_CONSTANT( target, GSASL_GSSAPI_ACCEPT_SEC_CONTEXT_ERROR  );
  NODE_DEFINE_CONSTANT( target, GSASL_GSSAPI_UNWRAP_ERROR  );
  NODE_DEFINE_CONSTANT( target, GSASL_GSSAPI_WRAP_ERROR  );
  NODE_DEFINE_CONSTANT( target, GSASL_GSSAPI_ACQUIRE_CRED_ERROR  );
  NODE_DEFINE_CONSTANT( target, GSASL_GSSAPI_DISPLAY_NAME_ERROR  );
  NODE_DEFINE_CONSTANT( target, GSASL_GSSAPI_UNSUPPORTED_PROTECTION_ERROR  );
  NODE_DEFINE_CONSTANT( target, GSASL_KERBEROS_V5_INIT_ERROR  );
  NODE_DEFINE_CONSTANT( target, GSASL_KERBEROS_V5_INTERNAL_ERROR  );
  NODE_DEFINE_CONSTANT( target, GSASL_SHISHI_ERROR );
  NODE_DEFINE_CONSTANT( target, GSASL_SECURID_SERVER_NEED_ADDITIONAL_PASSCODE  );
  NODE_DEFINE_CONSTANT( target, GSASL_SECURID_SERVER_NEED_NEW_PIN  );

  // property constants
  NODE_DEFINE_CONSTANT( target, GSASL_AUTHID );
  NODE_DEFINE_CONSTANT( target, GSASL_AUTHZID );
  NODE_DEFINE_CONSTANT( target, GSASL_PASSWORD );
  NODE_DEFINE_CONSTANT( target, GSASL_ANONYMOUS_TOKEN );
  NODE_DEFINE_CONSTANT( target, GSASL_SERVICE );
  NODE_DEFINE_CONSTANT( target, GSASL_HOSTNAME );
  NODE_DEFINE_CONSTANT( target, GSASL_GSSAPI_DISPLAY_NAME );
  NODE_DEFINE_CONSTANT( target, GSASL_PASSCODE );
  NODE_DEFINE_CONSTANT( target, GSASL_SUGGESTED_PIN );
  NODE_DEFINE_CONSTANT( target, GSASL_PIN );
  NODE_DEFINE_CONSTANT( target, GSASL_REALM );
  NODE_DEFINE_CONSTANT( target, GSASL_DIGEST_MD5_HASHED_PASSWORD );
  NODE_DEFINE_CONSTANT( target, GSASL_QOPS );
  NODE_DEFINE_CONSTANT( target, GSASL_QOP );
  NODE_DEFINE_CONSTANT( target, GSASL_SCRAM_ITER );
  NODE_DEFINE_CONSTANT( target, GSASL_SCRAM_SALT );
  NODE_DEFINE_CONSTANT( target, GSASL_SCRAM_SALTED_PASSWORD );
  NODE_DEFINE_CONSTANT( target, GSASL_VALIDATE_SIMPLE );
  NODE_DEFINE_CONSTANT( target, GSASL_VALIDATE_EXTERNAL );
  NODE_DEFINE_CONSTANT( target, GSASL_VALIDATE_ANONYMOUS );
  NODE_DEFINE_CONSTANT( target, GSASL_VALIDATE_GSSAPI );
  NODE_DEFINE_CONSTANT( target, GSASL_VALIDATE_SECURID );

  NODE_SET_PROTOTYPE_METHOD( t, "_mechanisms", GetMechanisms );
  NODE_SET_PROTOTYPE_METHOD( t, "start", Start );
  NODE_SET_PROTOTYPE_METHOD( t, "step", Step );

  target->Set( v8::String::NewSymbol( "ServerSession"), t->GetFunction() );
}

/*
 * Call in JS
 * new ServerSession( "service name" );
 * All other options default to NULL for now
 */
v8::Handle<v8::Value>
ServerSession::New (const v8::Arguments& args)
{
  HandleScope scope;

  REQ_STR_ARG( 0, realm );

  if( args.Length() <= 1 || !args[1]->IsFunction() ) {
    return ThrowException(Exception::TypeError(
                                  String::New("Argument 1 must be a callback")));
  }

  ServerSession *server = new ServerSession( *realm, cb_persist( args[1] ) );
  server->Wrap( args.This() );
  return args.This();
}

ServerSession::ServerSession( const char *realm, Persistent<Function> *cb )
  : ObjectWrap()
  , m_session( NULL )
  , m_callback( cb )
{
}

ServerSession::~ServerSession()
{
}

Handle<Value>
ServerSession::GetMechanisms( const v8::Arguments &args )
{
  ServerSession *sc = Unwrap<ServerSession>( args.This() );

  char *result;

  int mechres = gsasl_server_mechlist( ctx, &result );
  if( mechres != GSASL_OK ) {
    return String::New( "" );
  }

  Handle<String> ret = String::New( result, strlen( result ) );
  free( result );
  return ret;
}

int
ServerSession::Callback( Gsasl *ctx, Gsasl_session *sctx, Gsasl_property prop )
{
  ServerSession *sc = static_cast<ServerSession*>(gsasl_session_hook_get( sctx ));
  assert( sc );

  Local<Value> argv[] = { Integer::New( prop ) };
  Local<Value> ret = (*sc->m_callback)->Call( Context::GetCurrent()->Global(), 1, argv );
  if( ret->IsString() ) {
    gsasl_property_set( sctx, prop, *String::Utf8Value(ret->ToString()) );
    return GSASL_OK;
  }
  return GSASL_NO_CALLBACK;
}

/**
 * Returns a map
 * { status: integer_error_code,
 *   data : data to send to client if error == GSASL_OK }
 */
v8::Handle<v8::Value>
ServerSession::Start( const v8::Arguments &args )
{
  REQ_STR_ARG( 0, mechanismString );

  int res;

  ServerSession *sc = Unwrap<ServerSession>( args.This() );
  if( sc->m_session != NULL ) {
    return ThrowException( Exception::Error( String::New( "sasljs: This session is already started!" ) ) );
  }

  res = gsasl_server_start( ctx, *mechanismString, &sc->m_session );
  gsasl_session_hook_set( sc->m_session, sc );
  gsasl_callback_set( ctx, sc->Callback );

  return Integer::New( res );
}

v8::Handle<v8::Value>
ServerSession::Step( const v8::Arguments &args )
{
  REQ_STR_ARG( 0, clientinString );

  ServerSession *sc = Unwrap<ServerSession>( args.This() );

  char *reply;
  size_t len;

  char *b64;
  size_t crap;
  gsasl_base64_from( *clientinString, strlen( *clientinString ), &b64, &crap );
  std::cerr << std::endl << "sasljs: step: " << b64 << std::endl;

  gsasl_base64_from( *clientinString, strlen(*clientinString), &reply, &len );
  int res = gsasl_step64( sc->m_session, *clientinString, &reply );

  Handle<Object> obj = Object::New();
  Local<String> status = String::New( "status" );

  char *d64;
  gsasl_base64_from( reply, strlen(reply), &d64, &len );
  std::cerr << std::endl << "sasljs: step OUT: " << d64 << std::endl;

  if( res == GSASL_OK || res == GSASL_NEEDS_MORE ) {
    obj->Set( status, Integer::New( res ) );
    obj->Set( String::New( "data" ), String::New( reply, strlen( reply ) ) );

    return obj;
  }
  else {
    obj->Set( status, Integer::New( res ) );
    obj->Set( String::New( "data" ), String::New( gsasl_strerror( res ) ) );
    return obj;
  }
}
}

extern "C" void
init (Handle<Object> target)
{
  HandleScope scope;

  sasljs::ctx = NULL;
  int initres = gsasl_init( &sasljs::ctx );

  if( initres != GSASL_OK ) {
      fprintf( stderr, "Could not initialize gsasl: %s\n", gsasl_strerror( initres ) );
      abort();
  }

  sasljs::ServerSession::Initialize(target);
}
<commit_msg>Set 'this' of callback function to session object<commit_after>/*
 * Copyright 2010, Nikhil Marathe <nsm.nikhil@gmail.com> All rights reserved.
 * See LICENSE for details.
*/
#include "sasljs.h"

#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <iostream>

using namespace v8;
using namespace node;

/*
 * Macro from the sqlite3 bindings
 * http://github.com/grumdrig/node-sqlite/blob/master/sqlite3_bindings.cc
 * by Eric Fredricksen
 */
#define REQ_STR_ARG(I, VAR)                                             \
      if (args.Length() <= (I) || !args[I]->IsString())                     \
    return ThrowException(Exception::TypeError(                         \
                                  String::New("Argument " #I " must be a string"))); \
  String::Utf8Value VAR(args[I]->ToString());

namespace sasljs {
void ServerSession::Initialize ( Handle<Object> target )
{
  v8::HandleScope scope;

  v8::Local<v8::FunctionTemplate> t = v8::FunctionTemplate::New(New);

  t->InstanceTemplate()->SetInternalFieldCount(1);

  // error codes
  NODE_DEFINE_CONSTANT( target, GSASL_OK  );
  NODE_DEFINE_CONSTANT( target, GSASL_NEEDS_MORE  );
  NODE_DEFINE_CONSTANT( target, GSASL_UNKNOWN_MECHANISM  );
  NODE_DEFINE_CONSTANT( target, GSASL_MECHANISM_CALLED_TOO_MANY_TIMES  );
  NODE_DEFINE_CONSTANT( target, GSASL_MALLOC_ERROR  );
  NODE_DEFINE_CONSTANT( target, GSASL_BASE64_ERROR  );
  NODE_DEFINE_CONSTANT( target, GSASL_CRYPTO_ERROR  );
  NODE_DEFINE_CONSTANT( target, GSASL_SASLPREP_ERROR  );
  NODE_DEFINE_CONSTANT( target, GSASL_MECHANISM_PARSE_ERROR  );
  NODE_DEFINE_CONSTANT( target, GSASL_AUTHENTICATION_ERROR  );
  NODE_DEFINE_CONSTANT( target, GSASL_INTEGRITY_ERROR  );
  NODE_DEFINE_CONSTANT( target, GSASL_NO_CLIENT_CODE  );
  NODE_DEFINE_CONSTANT( target, GSASL_NO_SERVER_CODE  );
  NODE_DEFINE_CONSTANT( target, GSASL_NO_CALLBACK  );
  NODE_DEFINE_CONSTANT( target, GSASL_NO_ANONYMOUS_TOKEN  );
  NODE_DEFINE_CONSTANT( target, GSASL_NO_AUTHID  );
  NODE_DEFINE_CONSTANT( target, GSASL_NO_AUTHZID  );
  NODE_DEFINE_CONSTANT( target, GSASL_NO_PASSWORD  );
  NODE_DEFINE_CONSTANT( target, GSASL_NO_PASSCODE  );
  NODE_DEFINE_CONSTANT( target, GSASL_NO_PIN  );
  NODE_DEFINE_CONSTANT( target, GSASL_NO_SERVICE  );
  NODE_DEFINE_CONSTANT( target, GSASL_NO_HOSTNAME  );
  NODE_DEFINE_CONSTANT( target, GSASL_GSSAPI_RELEASE_BUFFER_ERROR  );
  NODE_DEFINE_CONSTANT( target, GSASL_GSSAPI_IMPORT_NAME_ERROR  );
  NODE_DEFINE_CONSTANT( target, GSASL_GSSAPI_INIT_SEC_CONTEXT_ERROR  );
  NODE_DEFINE_CONSTANT( target, GSASL_GSSAPI_ACCEPT_SEC_CONTEXT_ERROR  );
  NODE_DEFINE_CONSTANT( target, GSASL_GSSAPI_UNWRAP_ERROR  );
  NODE_DEFINE_CONSTANT( target, GSASL_GSSAPI_WRAP_ERROR  );
  NODE_DEFINE_CONSTANT( target, GSASL_GSSAPI_ACQUIRE_CRED_ERROR  );
  NODE_DEFINE_CONSTANT( target, GSASL_GSSAPI_DISPLAY_NAME_ERROR  );
  NODE_DEFINE_CONSTANT( target, GSASL_GSSAPI_UNSUPPORTED_PROTECTION_ERROR  );
  NODE_DEFINE_CONSTANT( target, GSASL_KERBEROS_V5_INIT_ERROR  );
  NODE_DEFINE_CONSTANT( target, GSASL_KERBEROS_V5_INTERNAL_ERROR  );
  NODE_DEFINE_CONSTANT( target, GSASL_SHISHI_ERROR );
  NODE_DEFINE_CONSTANT( target, GSASL_SECURID_SERVER_NEED_ADDITIONAL_PASSCODE  );
  NODE_DEFINE_CONSTANT( target, GSASL_SECURID_SERVER_NEED_NEW_PIN  );

  // property constants
  NODE_DEFINE_CONSTANT( target, GSASL_AUTHID );
  NODE_DEFINE_CONSTANT( target, GSASL_AUTHZID );
  NODE_DEFINE_CONSTANT( target, GSASL_PASSWORD );
  NODE_DEFINE_CONSTANT( target, GSASL_ANONYMOUS_TOKEN );
  NODE_DEFINE_CONSTANT( target, GSASL_SERVICE );
  NODE_DEFINE_CONSTANT( target, GSASL_HOSTNAME );
  NODE_DEFINE_CONSTANT( target, GSASL_GSSAPI_DISPLAY_NAME );
  NODE_DEFINE_CONSTANT( target, GSASL_PASSCODE );
  NODE_DEFINE_CONSTANT( target, GSASL_SUGGESTED_PIN );
  NODE_DEFINE_CONSTANT( target, GSASL_PIN );
  NODE_DEFINE_CONSTANT( target, GSASL_REALM );
  NODE_DEFINE_CONSTANT( target, GSASL_DIGEST_MD5_HASHED_PASSWORD );
  NODE_DEFINE_CONSTANT( target, GSASL_QOPS );
  NODE_DEFINE_CONSTANT( target, GSASL_QOP );
  NODE_DEFINE_CONSTANT( target, GSASL_SCRAM_ITER );
  NODE_DEFINE_CONSTANT( target, GSASL_SCRAM_SALT );
  NODE_DEFINE_CONSTANT( target, GSASL_SCRAM_SALTED_PASSWORD );
  NODE_DEFINE_CONSTANT( target, GSASL_VALIDATE_SIMPLE );
  NODE_DEFINE_CONSTANT( target, GSASL_VALIDATE_EXTERNAL );
  NODE_DEFINE_CONSTANT( target, GSASL_VALIDATE_ANONYMOUS );
  NODE_DEFINE_CONSTANT( target, GSASL_VALIDATE_GSSAPI );
  NODE_DEFINE_CONSTANT( target, GSASL_VALIDATE_SECURID );

  NODE_SET_PROTOTYPE_METHOD( t, "_mechanisms", GetMechanisms );
  NODE_SET_PROTOTYPE_METHOD( t, "start", Start );
  NODE_SET_PROTOTYPE_METHOD( t, "step", Step );

  target->Set( v8::String::NewSymbol( "ServerSession"), t->GetFunction() );
}

/*
 * Call in JS
 * new ServerSession( "service name" );
 * All other options default to NULL for now
 */
v8::Handle<v8::Value>
ServerSession::New (const v8::Arguments& args)
{
  HandleScope scope;

  REQ_STR_ARG( 0, realm );

  if( args.Length() <= 1 || !args[1]->IsFunction() ) {
    return ThrowException(Exception::TypeError(
                                  String::New("Argument 1 must be a callback")));
  }

  ServerSession *server = new ServerSession( *realm, cb_persist( args[1] ) );
  server->Wrap( args.This() );
  return args.This();
}

ServerSession::ServerSession( const char *realm, Persistent<Function> *cb )
  : ObjectWrap()
  , m_session( NULL )
  , m_callback( cb )
{
}

ServerSession::~ServerSession()
{
}

Handle<Value>
ServerSession::GetMechanisms( const v8::Arguments &args )
{
  ServerSession *sc = Unwrap<ServerSession>( args.This() );

  char *result;

  int mechres = gsasl_server_mechlist( ctx, &result );
  if( mechres != GSASL_OK ) {
    return String::New( "" );
  }

  Handle<String> ret = String::New( result, strlen( result ) );
  free( result );
  return ret;
}

int
ServerSession::Callback( Gsasl *ctx, Gsasl_session *sctx, Gsasl_property prop )
{
  ServerSession *sc = static_cast<ServerSession*>(gsasl_session_hook_get( sctx ));
  assert( sc );

  Local<Value> argv[] = { Integer::New( prop ) };
  Local<Value> ret = (*sc->m_callback)->Call( sc->handle_, 1, argv );
  if( ret->IsString() ) {
    gsasl_property_set( sctx, prop, *String::Utf8Value(ret->ToString()) );
    return GSASL_OK;
  }
  return GSASL_NO_CALLBACK;
}

/**
 * Returns a map
 * { status: integer_error_code,
 *   data : data to send to client if error == GSASL_OK }
 */
v8::Handle<v8::Value>
ServerSession::Start( const v8::Arguments &args )
{
  REQ_STR_ARG( 0, mechanismString );

  int res;

  ServerSession *sc = Unwrap<ServerSession>( args.This() );
  if( sc->m_session != NULL ) {
    return ThrowException( Exception::Error( String::New( "sasljs: This session is already started!" ) ) );
  }

  res = gsasl_server_start( ctx, *mechanismString, &sc->m_session );
  gsasl_session_hook_set( sc->m_session, sc );
  gsasl_callback_set( ctx, sc->Callback );

  return Integer::New( res );
}

v8::Handle<v8::Value>
ServerSession::Step( const v8::Arguments &args )
{
  REQ_STR_ARG( 0, clientinString );

  ServerSession *sc = Unwrap<ServerSession>( args.This() );

  char *reply;
  size_t len;

  char *b64;
  size_t crap;
  gsasl_base64_from( *clientinString, strlen( *clientinString ), &b64, &crap );
  std::cerr << std::endl << "sasljs: step: " << b64 << std::endl;

  gsasl_base64_from( *clientinString, strlen(*clientinString), &reply, &len );
  int res = gsasl_step64( sc->m_session, *clientinString, &reply );

  Handle<Object> obj = Object::New();
  Local<String> status = String::New( "status" );

  char *d64;
  gsasl_base64_from( reply, strlen(reply), &d64, &len );
  std::cerr << std::endl << "sasljs: step OUT: " << d64 << std::endl;

  if( res == GSASL_OK || res == GSASL_NEEDS_MORE ) {
    obj->Set( status, Integer::New( res ) );
    obj->Set( String::New( "data" ), String::New( reply, strlen( reply ) ) );

    return obj;
  }
  else {
    obj->Set( status, Integer::New( res ) );
    obj->Set( String::New( "data" ), String::New( gsasl_strerror( res ) ) );
    return obj;
  }
}
}

extern "C" void
init (Handle<Object> target)
{
  HandleScope scope;

  sasljs::ctx = NULL;
  int initres = gsasl_init( &sasljs::ctx );

  if( initres != GSASL_OK ) {
      fprintf( stderr, "Could not initialize gsasl: %s\n", gsasl_strerror( initres ) );
      abort();
  }

  sasljs::ServerSession::Initialize(target);
}
<|endoftext|>


<commit_before>/*=========================================================================

  Program:   Visualization Library
  Module:    Filter.cc
  Language:  C++
  Date:      $Date$
  Version:   $Revision$

This file is part of the Visualization Library. No part of this file or its
contents may be copied, reproduced or altered in any way without the express
written consent of the authors.

Copyright (c) Ken Martin, Will Schroeder, Bill Lorensen 1993, 1994

=========================================================================*/
#include "Filter.hh"

// Description:
// Construct new filter without start or end methods.
vlFilter::vlFilter()
{
  this->Input = NULL;

  this->StartMethod = NULL;
  this->StartMethodArgDelete = NULL;
  this->StartMethodArg = NULL;
  this->EndMethod = NULL;
  this->EndMethodArgDelete = NULL;
  this->EndMethodArg = NULL;

  this->Updating = 0;
}

int vlFilter::GetDataReleased()
{
  vl_ErrorMacro(<<"Method should be implemented by subclass!");
  return 1;
}

void vlFilter::SetDataReleased(int flag)
{
  vl_ErrorMacro(<<"Method should be implemented by subclass!");
}

// Description:
// Update input to this filter and the filter itself.
void vlFilter::UpdateFilter()
{
  // make sure input is available
  if ( !this->Input )
    {
    vl_ErrorMacro(<< "No input!");
    return;
    }

  // prevent chasing our tail
  if (this->Updating) return;

  this->Updating = 1;
  this->Input->Update();
  this->Updating = 0;

  if (this->Input->GetMTime() > this->_GetMTime() ||
  this->_GetMTime() > this->ExecuteTime ||
  this->GetDataReleased() )
    {
    if ( this->StartMethod ) (*this->StartMethod)(this->StartMethodArg);
    this->Execute();
    this->ExecuteTime.Modified();
    this->SetDataReleased(0);
    if ( this->EndMethod ) (*this->EndMethod)(this->EndMethodArg);
    }

  if ( this->Input->ShouldIReleaseData() ) this->Input->ReleaseData();
}

// Description:
// Set the filter start method. The start method is invoked before the
// filter executes.
void vlFilter::SetStartMethod(void (*f)(void *), void *arg)
{
  if ( f != this->StartMethod || arg != this->StartMethodArg )
    {
    // delete the current arg if there is one and a delete meth
    if ((this->StartMethodArg)&&(this->StartMethodArgDelete))
      {
      (*this->StartMethodArgDelete)(this->StartMethodArg);
      }
    this->StartMethod = f;
    this->StartMethodArg = arg;
    this->_Modified();
    }
}

// Description:
// Set the filter end method. The end method is invoked after the
// filter executes.
void vlFilter::SetEndMethod(void (*f)(void *), void *arg)
{
  if ( f != this->EndMethod || arg != this->EndMethodArg )
    {
    // delete the current arg if there is one and a delete meth
    if ((this->EndMethodArg)&&(this->EndMethodArgDelete))
      {
      (*this->EndMethodArgDelete)(this->EndMethodArg);
      }
    this->EndMethod = f;
    this->EndMethodArg = arg;
    this->_Modified();
    }
}

// Description:
// Set the arg delete method. This is used to free user memory.
void vlFilter::SetStartMethodArgDelete(void (*f)(void *))
{
  if ( f != this->StartMethodArgDelete)
    {
    this->StartMethodArgDelete = f;
    this->_Modified();
    }
}

// Description:
// Set the arg delete method. This is used to free user memory.
void vlFilter::SetEndMethodArgDelete(void (*f)(void *))
{
  if ( f != this->EndMethodArgDelete)
    {
    this->EndMethodArgDelete = f;
    this->_Modified();
    }
}

void vlFilter::Execute()
{
  cerr << "Execution of filter should be in derived class" << "\n";
}

void vlFilter::_PrintSelf(ostream& os, vlIndent indent)
{
  vlLWObject::_PrintSelf(os,indent);

  if ( this->StartMethod )
    {
    os << indent << "Start Method: (" << (void *)this->StartMethod << ")\n";
    }
  else
    {
    os << indent << "Start Method: (none)\n";
    }

  if ( this->EndMethod )
    {
    os << indent << "End Method: (" << (void *)this->EndMethod << ")\n";
    }
  else
    {
    os << indent << "End Method: (none)\n";
    }

  os << indent << "Execute Time: " <<this->ExecuteTime.GetMTime() << "\n";

  if ( this->Input )
    {
    os << indent << "Input: (" << (void *)this->Input << ")\n";
    }
  else
    {
    os << indent << "Input: (none)\n";
    }
}

<commit_msg>ERR: Use appropriate error macro.<commit_after>/*=========================================================================

  Program:   Visualization Library
  Module:    Filter.cc
  Language:  C++
  Date:      $Date$
  Version:   $Revision$

This file is part of the Visualization Library. No part of this file or its
contents may be copied, reproduced or altered in any way without the express
written consent of the authors.

Copyright (c) Ken Martin, Will Schroeder, Bill Lorensen 1993, 1994

=========================================================================*/
#include "Filter.hh"

// Description:
// Construct new filter without start or end methods.
vlFilter::vlFilter()
{
  this->Input = NULL;

  this->StartMethod = NULL;
  this->StartMethodArgDelete = NULL;
  this->StartMethodArg = NULL;
  this->EndMethod = NULL;
  this->EndMethodArgDelete = NULL;
  this->EndMethodArg = NULL;

  this->Updating = 0;
}

int vlFilter::GetDataReleased()
{
  vl_ErrorMacro(<<"Method should be implemented by subclass!");
  return 1;
}

void vlFilter::SetDataReleased(int flag)
{
  vl_ErrorMacro(<<"Method should be implemented by subclass!");
}

// Description:
// Update input to this filter and the filter itself.
void vlFilter::UpdateFilter()
{
  // make sure input is available
  if ( !this->Input )
    {
    vl_ErrorMacro(<< "No input!");
    return;
    }

  // prevent chasing our tail
  if (this->Updating) return;

  this->Updating = 1;
  this->Input->Update();
  this->Updating = 0;

  if (this->Input->GetMTime() > this->_GetMTime() ||
  this->_GetMTime() > this->ExecuteTime ||
  this->GetDataReleased() )
    {
    if ( this->StartMethod ) (*this->StartMethod)(this->StartMethodArg);
    this->Execute();
    this->ExecuteTime.Modified();
    this->SetDataReleased(0);
    if ( this->EndMethod ) (*this->EndMethod)(this->EndMethodArg);
    }

  if ( this->Input->ShouldIReleaseData() ) this->Input->ReleaseData();
}

// Description:
// Set the filter start method. The start method is invoked before the
// filter executes.
void vlFilter::SetStartMethod(void (*f)(void *), void *arg)
{
  if ( f != this->StartMethod || arg != this->StartMethodArg )
    {
    // delete the current arg if there is one and a delete meth
    if ((this->StartMethodArg)&&(this->StartMethodArgDelete))
      {
      (*this->StartMethodArgDelete)(this->StartMethodArg);
      }
    this->StartMethod = f;
    this->StartMethodArg = arg;
    this->_Modified();
    }
}

// Description:
// Set the filter end method. The end method is invoked after the
// filter executes.
void vlFilter::SetEndMethod(void (*f)(void *), void *arg)
{
  if ( f != this->EndMethod || arg != this->EndMethodArg )
    {
    // delete the current arg if there is one and a delete meth
    if ((this->EndMethodArg)&&(this->EndMethodArgDelete))
      {
      (*this->EndMethodArgDelete)(this->EndMethodArg);
      }
    this->EndMethod = f;
    this->EndMethodArg = arg;
    this->_Modified();
    }
}

// Description:
// Set the arg delete method. This is used to free user memory.
void vlFilter::SetStartMethodArgDelete(void (*f)(void *))
{
  if ( f != this->StartMethodArgDelete)
    {
    this->StartMethodArgDelete = f;
    this->_Modified();
    }
}

// Description:
// Set the arg delete method. This is used to free user memory.
void vlFilter::SetEndMethodArgDelete(void (*f)(void *))
{
  if ( f != this->EndMethodArgDelete)
    {
    this->EndMethodArgDelete = f;
    this->_Modified();
    }
}

void vlFilter::Execute()
{
  vl_ErrorMacro(<< "Execution of filter should be in derived class");
}

void vlFilter::_PrintSelf(ostream& os, vlIndent indent)
{
  vlLWObject::_PrintSelf(os,indent);

  if ( this->StartMethod )
    {
    os << indent << "Start Method: (" << (void *)this->StartMethod << ")\n";
    }
  else
    {
    os << indent << "Start Method: (none)\n";
    }

  if ( this->EndMethod )
    {
    os << indent << "End Method: (" << (void *)this->EndMethod << ")\n";
    }
  else
    {
    os << indent << "End Method: (none)\n";
    }

  os << indent << "Execute Time: " <<this->ExecuteTime.GetMTime() << "\n";

  if ( this->Input )
    {
    os << indent << "Input: (" << (void *)this->Input << ")\n";
    }
  else
    {
    os << indent << "Input: (none)\n";
    }
}

<|endoftext|>


<commit_before>/* Copyright (C) 2005-2011, Thorvald Natvig <thorvald@natvig.com>

   All rights reserved.

   Redistribution and use in source and binary forms, with or without
   modification, are permitted provided that the following conditions
   are met:

   - Redistributions of source code must retain the above copyright notice,
     this list of conditions and the following disclaimer.
   - Redistributions in binary form must reproduce the above copyright notice,
     this list of conditions and the following disclaimer in the documentation
     and/or other materials provided with the distribution.
   - Neither the name of the Mumble Developers nor the names of its
     contributors may be used to endorse or promote products derived from this
     software without specific prior written permission.

   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
   ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
   LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
   A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION 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 "murmur_pch.h"

#include "Group.h"

#include "Channel.h"
#include "User.h"
#ifdef MURMUR
#include "ServerUser.h"
#endif

Group::Group(Channel *assoc, const QString &name) {
	c = assoc;
	bInherit = true;
	bInheritable = true;
	qsName = name;
	if (c)
		c->qhGroups[name] = this;
}

#ifdef MURMUR

QSet<int> Group::members() {
	QStack<Group *> s;
	QSet<int> m;
	Channel *p;
	Group *g;
	int i;

	p = c;
	while (p) {
		g = p->qhGroups.value(qsName);
		if (g) {
			if ((p != c) && ! g->bInheritable)
				break;
			s.push(g);
			if (! g->bInherit)
				break;
		}
		p = p->cParent;
	}

	while (! s.isEmpty()) {
		g = s.pop();
		foreach(i, g->qsAdd)
			m.insert(i);
		foreach(i, g->qsRemove)
			m.remove(i);
	}

	return m;
}

Group *Group::getGroup(Channel *chan, QString name) {
	Channel *p = chan;
	while (p) {
		Group *g = p->qhGroups.value(name);
		if (g) {
			if (chan == p)
				return g;
			else if (g->bInheritable)
				return g;
			else
				return NULL;
		}
		p = p->cParent;
	}
	return NULL;
}

QSet<QString> Group::groupNames(Channel *chan) {
	QStack<Channel *> s;
	QSet<QString> m;
	Channel *c = chan;
	Group *g;

	while (c) {
		s.push(c);
		c = c->cParent;
	}

	while (! s.isEmpty()) {
		c = s.pop();
		foreach(g, c->qhGroups) {
			if ((chan != c) && (! g->bInheritable))
				m.remove(g->qsName);
			else
				m.insert(g->qsName);
		}
	}

	return m;
}

#define RET_FALSE (invert ? true : false)
#define RET_TRUE (invert ? false : true)

bool Group::isMember(Channel *curChan, Channel *aclChan, QString name, ServerUser *pl) {
	Channel *p;
	Channel *c;
	Group *g;

	bool m = false;
	bool invert = false;
	bool token = false;
	bool hash = false;
	c = curChan;

	while (true) {
		if (name.isEmpty())
			return false;

		if (name.startsWith(QChar::fromLatin1('!'))) {
			invert = true;
			name = name.remove(0,1);
			continue;
		}

		if (name.startsWith(QChar::fromLatin1('~'))) {
			c = aclChan;
			name = name.remove(0,1);
			continue;
		}

		if (name.startsWith(QChar::fromLatin1('#'))) {
			token = true;
			name = name.remove(0,1);
			continue;
		}
		if (name.startsWith(QChar::fromLatin1('$'))) {
			hash = true;
			name = name.remove(0,1);
			continue;
		}

		break;
	}

	if (token)
		m = pl->qslAccessTokens.contains(name, Qt::CaseInsensitive);
	else if (hash)
		m = pl->qsHash == name;
	else if (name == QLatin1String("none"))
		m = false;
	else if (name == QLatin1String("all"))
		m = true;
	else if (name == QLatin1String("auth"))
		m = (pl->iId >= 0);
	else if (name == QLatin1String("strong"))
		m = pl->bVerified;
	else if (name == QLatin1String("in"))
		m = (pl->cChannel == c);
	else if (name == QLatin1String("out"))
		m = !(pl->cChannel == c);
	else if (name.startsWith(QLatin1String("sub"))) {
		name = name.remove(0,4);
		int mindesc = 1;
		int maxdesc = 1000;
		int minpath = 0;
		QStringList args = name.split(QLatin1String(","));
		switch (args.count()) {
			default:
			case 3:
				maxdesc = args[2].isEmpty() ? maxdesc : args[2].toInt();
			case 2:
				mindesc = args[1].isEmpty() ? mindesc : args[1].toInt();
			case 1:
				minpath = args[0].isEmpty() ? minpath : args[0].toInt();
			case 0:
				break;
		}

		Channel *home = pl->cChannel;
		QList<Channel *> playerChain;
		QList<Channel *> groupChain;

		p = home;
		while (p) {
			playerChain.prepend(p);
			p = p->cParent;
		}

		p = curChan;
		while (p) {
			groupChain.prepend(p);
			p = p->cParent;
		}

		int cofs = groupChain.indexOf(c);
		Q_ASSERT(cofs != -1);

		cofs += minpath;

		if (cofs >= groupChain.count()) {
			return RET_FALSE;
		} else if (cofs < 0) {
			cofs = 0;
		}

		Channel *needed = groupChain[cofs];
		if (playerChain.indexOf(needed) == -1) {
			return RET_FALSE;
		}

		int mindepth = cofs + mindesc;
		int maxdepth = cofs + maxdesc;

		int pdepth = playerChain.count() - 1;

		m = (pdepth >= mindepth) && (pdepth <= maxdepth);
	} else {
		QStack<Group *> s;

		p = c;

		while (p) {
			g = p->qhGroups.value(name);

			if (g) {
				if ((p != c) && ! g->bInheritable)
					break;
				s.push(g);
				if (! g->bInherit)
					break;
			}

			p = p->cParent;
		}

		while (! s.isEmpty()) {
			g = s.pop();
			if (g->qsAdd.contains(pl->iId) || g->qsTemporary.contains(pl->iId) || g->qsTemporary.contains(- static_cast<int>(pl->uiSession)))
				m = true;
			if (g->qsRemove.contains(pl->iId))
				m = false;
		}
	}
	return invert ? !m : m;
}

#endif
<commit_msg>Fix murmur handling all groups starting with "sub" as special.<commit_after>/* Copyright (C) 2005-2011, Thorvald Natvig <thorvald@natvig.com>

   All rights reserved.

   Redistribution and use in source and binary forms, with or without
   modification, are permitted provided that the following conditions
   are met:

   - Redistributions of source code must retain the above copyright notice,
     this list of conditions and the following disclaimer.
   - Redistributions in binary form must reproduce the above copyright notice,
     this list of conditions and the following disclaimer in the documentation
     and/or other materials provided with the distribution.
   - Neither the name of the Mumble Developers nor the names of its
     contributors may be used to endorse or promote products derived from this
     software without specific prior written permission.

   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
   ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
   LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
   A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION 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 "murmur_pch.h"

#include "Group.h"

#include "Channel.h"
#include "User.h"
#ifdef MURMUR
#include "ServerUser.h"
#endif

Group::Group(Channel *assoc, const QString &name) {
	c = assoc;
	bInherit = true;
	bInheritable = true;
	qsName = name;
	if (c)
		c->qhGroups[name] = this;
}

#ifdef MURMUR

QSet<int> Group::members() {
	QStack<Group *> s;
	QSet<int> m;
	Channel *p;
	Group *g;
	int i;

	p = c;
	while (p) {
		g = p->qhGroups.value(qsName);
		if (g) {
			if ((p != c) && ! g->bInheritable)
				break;
			s.push(g);
			if (! g->bInherit)
				break;
		}
		p = p->cParent;
	}

	while (! s.isEmpty()) {
		g = s.pop();
		foreach(i, g->qsAdd)
			m.insert(i);
		foreach(i, g->qsRemove)
			m.remove(i);
	}

	return m;
}

Group *Group::getGroup(Channel *chan, QString name) {
	Channel *p = chan;
	while (p) {
		Group *g = p->qhGroups.value(name);
		if (g) {
			if (chan == p)
				return g;
			else if (g->bInheritable)
				return g;
			else
				return NULL;
		}
		p = p->cParent;
	}
	return NULL;
}

QSet<QString> Group::groupNames(Channel *chan) {
	QStack<Channel *> s;
	QSet<QString> m;
	Channel *c = chan;
	Group *g;

	while (c) {
		s.push(c);
		c = c->cParent;
	}

	while (! s.isEmpty()) {
		c = s.pop();
		foreach(g, c->qhGroups) {
			if ((chan != c) && (! g->bInheritable))
				m.remove(g->qsName);
			else
				m.insert(g->qsName);
		}
	}

	return m;
}

#define RET_FALSE (invert ? true : false)
#define RET_TRUE (invert ? false : true)

bool Group::isMember(Channel *curChan, Channel *aclChan, QString name, ServerUser *pl) {
	Channel *p;
	Channel *c;
	Group *g;

	bool m = false;
	bool invert = false;
	bool token = false;
	bool hash = false;
	c = curChan;

	while (true) {
		if (name.isEmpty())
			return false;

		if (name.startsWith(QChar::fromLatin1('!'))) {
			invert = true;
			name = name.remove(0,1);
			continue;
		}

		if (name.startsWith(QChar::fromLatin1('~'))) {
			c = aclChan;
			name = name.remove(0,1);
			continue;
		}

		if (name.startsWith(QChar::fromLatin1('#'))) {
			token = true;
			name = name.remove(0,1);
			continue;
		}
		if (name.startsWith(QChar::fromLatin1('$'))) {
			hash = true;
			name = name.remove(0,1);
			continue;
		}

		break;
	}

	if (token)
		m = pl->qslAccessTokens.contains(name, Qt::CaseInsensitive);
	else if (hash)
		m = pl->qsHash == name;
	else if (name == QLatin1String("none"))
		m = false;
	else if (name == QLatin1String("all"))
		m = true;
	else if (name == QLatin1String("auth"))
		m = (pl->iId >= 0);
	else if (name == QLatin1String("strong"))
		m = pl->bVerified;
	else if (name == QLatin1String("in"))
		m = (pl->cChannel == c);
	else if (name == QLatin1String("out"))
		m = !(pl->cChannel == c);
	else if (name == QLatin1String("sub")
			|| name.startsWith(QLatin1String("sub,"))) {

		name = name.remove(0,4);
		int mindesc = 1;
		int maxdesc = 1000;
		int minpath = 0;
		QStringList args = name.split(QLatin1String(","));
		switch (args.count()) {
			default:
			case 3:
				maxdesc = args[2].isEmpty() ? maxdesc : args[2].toInt();
			case 2:
				mindesc = args[1].isEmpty() ? mindesc : args[1].toInt();
			case 1:
				minpath = args[0].isEmpty() ? minpath : args[0].toInt();
			case 0:
				break;
		}

		Channel *home = pl->cChannel;
		QList<Channel *> playerChain;
		QList<Channel *> groupChain;

		p = home;
		while (p) {
			playerChain.prepend(p);
			p = p->cParent;
		}

		p = curChan;
		while (p) {
			groupChain.prepend(p);
			p = p->cParent;
		}

		int cofs = groupChain.indexOf(c);
		Q_ASSERT(cofs != -1);

		cofs += minpath;

		if (cofs >= groupChain.count()) {
			return RET_FALSE;
		} else if (cofs < 0) {
			cofs = 0;
		}

		Channel *needed = groupChain[cofs];
		if (playerChain.indexOf(needed) == -1) {
			return RET_FALSE;
		}

		int mindepth = cofs + mindesc;
		int maxdepth = cofs + maxdesc;

		int pdepth = playerChain.count() - 1;

		m = (pdepth >= mindepth) && (pdepth <= maxdepth);
	} else {
		QStack<Group *> s;

		p = c;

		while (p) {
			g = p->qhGroups.value(name);

			if (g) {
				if ((p != c) && ! g->bInheritable)
					break;
				s.push(g);
				if (! g->bInherit)
					break;
			}

			p = p->cParent;
		}

		while (! s.isEmpty()) {
			g = s.pop();
			if (g->qsAdd.contains(pl->iId) || g->qsTemporary.contains(pl->iId) || g->qsTemporary.contains(- static_cast<int>(pl->uiSession)))
				m = true;
			if (g->qsRemove.contains(pl->iId))
				m = false;
		}
	}
	return invert ? !m : m;
}

#endif
<|endoftext|>