<commit_before>/******************************************************************************
** Copyright (c) 2014-2015, Intel Corporation                                **
** All rights reserved.                                                      **
**                                                                           **
** Redistribution and use in source and binary forms, with or without        **
** modification, are permitted provided that the following conditions        **
** are met:                                                                  **
** 1. Redistributions of source code must retain the above copyright         **
**    notice, this list of conditions and the following disclaimer.          **
** 2. 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.   **
** 3. Neither the name of the copyright holder nor the names of its          **
**    contributors may be used to endorse or promote products derived        **
**    from this software without specific prior written permission.          **
**                                                                           **
** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS       **
** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT         **
** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR     **
** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT      **
** HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,    **
** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED  **
** TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR    **
** PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF    **
** LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING      **
** NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS        **
** SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.              **
******************************************************************************/
/* Hans Pabst (Intel Corp.)
******************************************************************************/
#include "test.hpp"
#include <libxstream_begin.h>
#include <stdexcept>
#include <algorithm>
#include <cstdlib>
#include <cstring>
#include <cassert>
#include <cstdio>
#include <atomic>
#if defined(_OPENMP)
# include <omp.h>
#endif
#include <libxstream_end.h>

namespace test_internal {

LIBXSTREAM_TARGET(mic) void check(libxstream_bool* result, const void* buffer, size_t size, char pattern)
{
  const libxstream_argument* arg = 0;
  libxstream_get_argument(buffer, &arg);
  size_t shape = 0;
  libxstream_get_shape(arg, &shape);
  bool ok = shape == size;

  const char *const values = reinterpret_cast<const char*>(buffer);
  for (size_t i = 0; i < size && ok; ++i) {
    ok = pattern == values[i];
  }
  LIBXSTREAM_ASSERT(result);
  *result = ok;
}

} // namespace test_internal

test_type::test_type(int device)
  : m_device(device), m_stream(0), m_event(0)
  , m_host_mem(0), m_dev_mem(0)
{
  LIBXSTREAM_CHECK_CALL_RETURN(libxstream_get_active_device(&m_device));

  size_t mem_free = 0, mem_avail = 0;
  LIBXSTREAM_CHECK_CALL_RETURN(libxstream_mem_info(m_device, &mem_free, &mem_avail));
  LIBXSTREAM_CHECK_CALL_RETURN(libxstream_stream_create(&m_stream, m_device, 0, 0, 0));

  const size_t size = 4711u * 1024u;
  LIBXSTREAM_CHECK_CALL_RETURN(libxstream_mem_allocate(-1, &m_host_mem, size, 0));
  LIBXSTREAM_CHECK_CALL_RETURN(libxstream_mem_allocate(m_device, &m_dev_mem, size, 0));
  LIBXSTREAM_CHECK_CALL_RETURN(libxstream_mem_info(m_device, &mem_free, &mem_avail));

  const char pattern_a = 'a', pattern_b = 'b';
  LIBXSTREAM_ASSERT(pattern_a != pattern_b);
  std::fill_n(reinterpret_cast<char*>(m_host_mem), size, pattern_a);
  LIBXSTREAM_CHECK_CALL_RETURN(libxstream_memcpy_h2d(m_host_mem, m_dev_mem, size, m_stream));

  libxstream_bool ok = false;
  LIBXSTREAM_CHECK_CALL_RETURN(libxstream_fn_create_signature(&m_signature, 4));
  LIBXSTREAM_CHECK_CALL_RETURN(libxstream_fn_output(m_signature, 0, &ok, libxstream_type2value<libxstream_bool>::value, 0, 0));
  LIBXSTREAM_CHECK_CALL_RETURN(libxstream_fn_input (m_signature, 1, m_dev_mem, LIBXSTREAM_TYPE_BYTE, 1, &size));
  LIBXSTREAM_CHECK_CALL_RETURN(libxstream_fn_input (m_signature, 2, &size, libxstream_type2value<size_t>::value, 0, 0));
  LIBXSTREAM_CHECK_CALL_RETURN(libxstream_fn_input (m_signature, 3, &pattern_a, libxstream_type2value<char>::value, 0, 0));
  LIBXSTREAM_CHECK_CALL_RETURN(libxstream_fn_call(reinterpret_cast<libxstream_function>(test_internal::check), m_signature, m_stream, LIBXSTREAM_CALL_DEFAULT));

  LIBXSTREAM_CHECK_CALL_RETURN(libxstream_event_create(&m_event));
  LIBXSTREAM_CHECK_CALL_RETURN(libxstream_event_record(m_event, m_stream));
  LIBXSTREAM_CHECK_CALL_RETURN(libxstream_event_synchronize(m_event));
  LIBXSTREAM_CHECK_CONDITION_RETURN(ok);

  std::fill_n(reinterpret_cast<char*>(m_host_mem), size, pattern_b);
  LIBXSTREAM_CHECK_CALL_RETURN(libxstream_memcpy_d2h(m_dev_mem, m_host_mem, size, m_stream));

  const size_t size2 = size / 2;
  LIBXSTREAM_CHECK_CALL_RETURN(libxstream_memset_zero(m_dev_mem, size2, m_stream));
  LIBXSTREAM_CHECK_CALL_RETURN(libxstream_memset_zero(reinterpret_cast<char*>(m_dev_mem) + size2, size - size2, m_stream));
  LIBXSTREAM_CHECK_CALL_RETURN(libxstream_event_record(m_event, m_stream));

  int has_occured = 0;
  LIBXSTREAM_CHECK_CALL_RETURN(libxstream_event_query(m_event, &has_occured));
  if (0 == has_occured) {
    LIBXSTREAM_CHECK_CALL_RETURN(libxstream_event_synchronize(m_event));
  }

  test_internal::check(&ok, m_host_mem, size, pattern_a);
  LIBXSTREAM_CHECK_CONDITION_RETURN(ok);

  LIBXSTREAM_CHECK_CALL_RETURN(libxstream_memcpy_d2h(m_dev_mem, m_host_mem, size2, m_stream));
  LIBXSTREAM_CHECK_CALL_RETURN(libxstream_memcpy_d2h(reinterpret_cast<const char*>(m_dev_mem) + size2, reinterpret_cast<char*>(m_host_mem) + size2, size - size2, m_stream));
  LIBXSTREAM_CHECK_CALL_RETURN(libxstream_event_record(m_event, m_stream));
  LIBXSTREAM_CHECK_CALL_RETURN(libxstream_stream_sync(m_stream));

  LIBXSTREAM_CHECK_CALL_RETURN(libxstream_event_query(m_event, &has_occured));
  LIBXSTREAM_CHECK_CONDITION_RETURN(0 != has_occured);

  test_internal::check(&ok, m_host_mem, size, 0);
  LIBXSTREAM_CHECK_CONDITION_RETURN(ok);
}

test_type::~test_type()
{
  LIBXSTREAM_CHECK_CALL_RETURN(libxstream_event_destroy(m_event));
  LIBXSTREAM_CHECK_CALL_RETURN(libxstream_mem_deallocate(-1, m_host_mem));
  LIBXSTREAM_CHECK_CALL_RETURN(libxstream_mem_deallocate(m_device, m_dev_mem));
  LIBXSTREAM_CHECK_CALL_RETURN(libxstream_stream_destroy(m_stream));
  LIBXSTREAM_CHECK_CALL_RETURN(libxstream_fn_destroy_signature(m_signature));
  fprintf(stdout, "TST successfully completed.\n");
}

int main(int argc, char* argv[])
{
  try {
#if defined(_OPENMP)
    const int ntasks = std::max(1 < argc ? std::atoi(argv[1]) : omp_get_max_threads(), 1);
#else
    const int ntasks = 1;
#endif

    size_t ndevices = 0;
    if (LIBXSTREAM_ERROR_NONE != libxstream_get_ndevices(&ndevices) || 0 == ndevices) {
      throw std::runtime_error("no device found!");
    }

#if defined(_OPENMP)
#   pragma omp parallel for schedule(dynamic,1)
#endif
    for (int i = 0; i < ntasks; ++i) {
      const test_type test(i % ndevices);
    }
  }
  catch(const std::exception& e) {
    fprintf(stderr, "Error: %s\n", e.what());
    return EXIT_FAILURE;
  }
  catch(...) {
    fprintf(stderr, "Error: unknown exception caught!\n");
    return EXIT_FAILURE;
  }

  return EXIT_SUCCESS;
}
<commit_msg>Removed unnecessary header file from the list of included dependencies. Enables the code to compile with older tool chain (no <atomic>).<commit_after>/******************************************************************************
** Copyright (c) 2014-2015, Intel Corporation                                **
** All rights reserved.                                                      **
**                                                                           **
** Redistribution and use in source and binary forms, with or without        **
** modification, are permitted provided that the following conditions        **
** are met:                                                                  **
** 1. Redistributions of source code must retain the above copyright         **
**    notice, this list of conditions and the following disclaimer.          **
** 2. 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.   **
** 3. Neither the name of the copyright holder nor the names of its          **
**    contributors may be used to endorse or promote products derived        **
**    from this software without specific prior written permission.          **
**                                                                           **
** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS       **
** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT         **
** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR     **
** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT      **
** HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,    **
** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED  **
** TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR    **
** PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF    **
** LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING      **
** NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS        **
** SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.              **
******************************************************************************/
/* Hans Pabst (Intel Corp.)
******************************************************************************/
#include "test.hpp"
#include <libxstream_begin.h>
#include <stdexcept>
#include <algorithm>
#include <cstdlib>
#include <cstring>
#include <cassert>
#include <cstdio>
#if defined(_OPENMP)
# include <omp.h>
#endif
#include <libxstream_end.h>

namespace test_internal {

LIBXSTREAM_TARGET(mic) void check(libxstream_bool* result, const void* buffer, size_t size, char pattern)
{
  const libxstream_argument* arg = 0;
  libxstream_get_argument(buffer, &arg);
  size_t shape = 0;
  libxstream_get_shape(arg, &shape);
  bool ok = shape == size;

  const char *const values = reinterpret_cast<const char*>(buffer);
  for (size_t i = 0; i < size && ok; ++i) {
    ok = pattern == values[i];
  }
  LIBXSTREAM_ASSERT(result);
  *result = ok;
}

} // namespace test_internal

test_type::test_type(int device)
  : m_device(device), m_stream(0), m_event(0)
  , m_host_mem(0), m_dev_mem(0)
{
  LIBXSTREAM_CHECK_CALL_RETURN(libxstream_get_active_device(&m_device));

  size_t mem_free = 0, mem_avail = 0;
  LIBXSTREAM_CHECK_CALL_RETURN(libxstream_mem_info(m_device, &mem_free, &mem_avail));
  LIBXSTREAM_CHECK_CALL_RETURN(libxstream_stream_create(&m_stream, m_device, 0, 0, 0));

  const size_t size = 4711u * 1024u;
  LIBXSTREAM_CHECK_CALL_RETURN(libxstream_mem_allocate(-1, &m_host_mem, size, 0));
  LIBXSTREAM_CHECK_CALL_RETURN(libxstream_mem_allocate(m_device, &m_dev_mem, size, 0));
  LIBXSTREAM_CHECK_CALL_RETURN(libxstream_mem_info(m_device, &mem_free, &mem_avail));

  const char pattern_a = 'a', pattern_b = 'b';
  LIBXSTREAM_ASSERT(pattern_a != pattern_b);
  std::fill_n(reinterpret_cast<char*>(m_host_mem), size, pattern_a);
  LIBXSTREAM_CHECK_CALL_RETURN(libxstream_memcpy_h2d(m_host_mem, m_dev_mem, size, m_stream));

  libxstream_bool ok = false;
  LIBXSTREAM_CHECK_CALL_RETURN(libxstream_fn_create_signature(&m_signature, 4));
  LIBXSTREAM_CHECK_CALL_RETURN(libxstream_fn_output(m_signature, 0, &ok, libxstream_type2value<libxstream_bool>::value, 0, 0));
  LIBXSTREAM_CHECK_CALL_RETURN(libxstream_fn_input (m_signature, 1, m_dev_mem, LIBXSTREAM_TYPE_BYTE, 1, &size));
  LIBXSTREAM_CHECK_CALL_RETURN(libxstream_fn_input (m_signature, 2, &size, libxstream_type2value<size_t>::value, 0, 0));
  LIBXSTREAM_CHECK_CALL_RETURN(libxstream_fn_input (m_signature, 3, &pattern_a, libxstream_type2value<char>::value, 0, 0));
  LIBXSTREAM_CHECK_CALL_RETURN(libxstream_fn_call(reinterpret_cast<libxstream_function>(test_internal::check), m_signature, m_stream, LIBXSTREAM_CALL_DEFAULT));

  LIBXSTREAM_CHECK_CALL_RETURN(libxstream_event_create(&m_event));
  LIBXSTREAM_CHECK_CALL_RETURN(libxstream_event_record(m_event, m_stream));
  LIBXSTREAM_CHECK_CALL_RETURN(libxstream_event_synchronize(m_event));
  LIBXSTREAM_CHECK_CONDITION_RETURN(ok);

  std::fill_n(reinterpret_cast<char*>(m_host_mem), size, pattern_b);
  LIBXSTREAM_CHECK_CALL_RETURN(libxstream_memcpy_d2h(m_dev_mem, m_host_mem, size, m_stream));

  const size_t size2 = size / 2;
  LIBXSTREAM_CHECK_CALL_RETURN(libxstream_memset_zero(m_dev_mem, size2, m_stream));
  LIBXSTREAM_CHECK_CALL_RETURN(libxstream_memset_zero(reinterpret_cast<char*>(m_dev_mem) + size2, size - size2, m_stream));
  LIBXSTREAM_CHECK_CALL_RETURN(libxstream_event_record(m_event, m_stream));

  int has_occured = 0;
  LIBXSTREAM_CHECK_CALL_RETURN(libxstream_event_query(m_event, &has_occured));
  if (0 == has_occured) {
    LIBXSTREAM_CHECK_CALL_RETURN(libxstream_event_synchronize(m_event));
  }

  test_internal::check(&ok, m_host_mem, size, pattern_a);
  LIBXSTREAM_CHECK_CONDITION_RETURN(ok);

  LIBXSTREAM_CHECK_CALL_RETURN(libxstream_memcpy_d2h(m_dev_mem, m_host_mem, size2, m_stream));
  LIBXSTREAM_CHECK_CALL_RETURN(libxstream_memcpy_d2h(reinterpret_cast<const char*>(m_dev_mem) + size2, reinterpret_cast<char*>(m_host_mem) + size2, size - size2, m_stream));
  LIBXSTREAM_CHECK_CALL_RETURN(libxstream_event_record(m_event, m_stream));
  LIBXSTREAM_CHECK_CALL_RETURN(libxstream_stream_sync(m_stream));

  LIBXSTREAM_CHECK_CALL_RETURN(libxstream_event_query(m_event, &has_occured));
  LIBXSTREAM_CHECK_CONDITION_RETURN(0 != has_occured);

  test_internal::check(&ok, m_host_mem, size, 0);
  LIBXSTREAM_CHECK_CONDITION_RETURN(ok);
}

test_type::~test_type()
{
  LIBXSTREAM_CHECK_CALL_RETURN(libxstream_event_destroy(m_event));
  LIBXSTREAM_CHECK_CALL_RETURN(libxstream_mem_deallocate(-1, m_host_mem));
  LIBXSTREAM_CHECK_CALL_RETURN(libxstream_mem_deallocate(m_device, m_dev_mem));
  LIBXSTREAM_CHECK_CALL_RETURN(libxstream_stream_destroy(m_stream));
  LIBXSTREAM_CHECK_CALL_RETURN(libxstream_fn_destroy_signature(m_signature));
  fprintf(stdout, "TST successfully completed.\n");
}

int main(int argc, char* argv[])
{
  try {
#if defined(_OPENMP)
    const int ntasks = std::max(1 < argc ? std::atoi(argv[1]) : omp_get_max_threads(), 1);
#else
    const int ntasks = 1;
#endif

    size_t ndevices = 0;
    if (LIBXSTREAM_ERROR_NONE != libxstream_get_ndevices(&ndevices) || 0 == ndevices) {
      throw std::runtime_error("no device found!");
    }

#if defined(_OPENMP)
#   pragma omp parallel for schedule(dynamic,1)
#endif
    for (int i = 0; i < ntasks; ++i) {
      const test_type test(i % ndevices);
    }
  }
  catch(const std::exception& e) {
    fprintf(stderr, "Error: %s\n", e.what());
    return EXIT_FAILURE;
  }
  catch(...) {
    fprintf(stderr, "Error: unknown exception caught!\n");
    return EXIT_FAILURE;
  }

  return EXIT_SUCCESS;
}
<|endoftext|>


<commit_before>/*
  This file is part of the Grantlee template system.

  Copyright (c) 2009,2010 Stephen Kelly <steveire@gmail.com>

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

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

  You should have received a copy of the GNU Lesser General Public
  License along with this library.  If not, see <http://www.gnu.org/licenses/>.

*/

#include "filterexpression.h"

#include "exception.h"
#include "filter.h"
#include "grantlee_latin1literal_p.h"
#include "metatype.h"
#include "parser.h"
#include "util.h"

typedef QPair<Grantlee::Filter::Ptr, Grantlee::Variable> ArgFilter;

namespace Grantlee
{

class FilterExpressionPrivate
{
  FilterExpressionPrivate( FilterExpression *fe )
      : q_ptr( fe )
  {

  }

  Variable m_variable;
  QList<ArgFilter> m_filters;
  QStringList m_filterNames;

  Q_DECLARE_PUBLIC( FilterExpression )
  FilterExpression * const q_ptr;
};

}

using namespace Grantlee;

static const char FILTER_SEPARATOR = '|';
static const char FILTER_ARGUMENT_SEPARATOR = ':';

static QRegExp getFilterRegexp()
{
  const QString filterSep( QRegExp::escape( QChar::fromLatin1( FILTER_SEPARATOR ) ) );
  const QString argSep( QRegExp::escape( QChar::fromLatin1( FILTER_ARGUMENT_SEPARATOR ) ) );

  const QLatin1Literal varChars( "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_." );
  const QLatin1Literal numChars( "[-+\\.]?\\d[\\d\\.e]*" );
  const QString i18nOpen( QRegExp::escape( QLatin1String( "_(" ) ) );
  const QLatin1Literal doubleQuoteStringLiteral( "\"[^\"\\\\]*(?:\\\\.[^\"\\\\]*)*\"" );
  const QLatin1Literal singleQuoteStringLiteral( "\'[^\'\\\\]*(?:\\\\.[^\'\\\\]*)*\'" );
  const QString i18nClose( QRegExp::escape( QLatin1String( ")" ) ) );
  const QString variable = QLatin1Char( '[' ) + varChars + QLatin1Literal( "]+");

  const QString localizedExpression = QLatin1Literal( "(?:" ) + i18nOpen + doubleQuoteStringLiteral + i18nClose + QLatin1Char( '|' )
                                                              + i18nOpen + singleQuoteStringLiteral + i18nClose + QLatin1Char( '|' )
                                                              + i18nOpen + numChars                 + i18nClose + QLatin1Char( '|' )
                                                              + i18nOpen + variable                 + i18nClose + QLatin1Char( ')' );

  const QString constantString = QLatin1Literal( "(?:" ) + doubleQuoteStringLiteral + QLatin1Char( '|' )
                                                         + singleQuoteStringLiteral
                                   + QLatin1Char( ')' );

  const QString filterRawString = QLatin1Char( '^' ) + constantString + QLatin1Char( '|' )
                               + QLatin1Char( '^' ) + localizedExpression + QLatin1Char( '|' )
                               + QLatin1Char( '^' ) + variable + QLatin1Char( '|' )
                               + numChars + QLatin1Char( '|' )
                               + filterSep + QLatin1Literal( "\\w+|" )
                               + argSep
                               + QLatin1Literal( "(?:" )
                                 + constantString + QLatin1Char( '|' ) + localizedExpression
                                 + QLatin1Char( '|' )
                                 + variable
                                 + QLatin1Char( '|' )
                                 + numChars
                                 + QLatin1Char( '|' )
                                 + filterSep
                               + QLatin1Literal( "\\w+)" );

  return QRegExp( filterRawString );
}

FilterExpression::FilterExpression( const QString &varString, Parser *parser )
  : d_ptr( new FilterExpressionPrivate( this ) )
{
  Q_D( FilterExpression );

  int pos = 0;
  int lastPos = 0;
  int len;
  QString subString;

  QString vs = varString;

  static const QRegExp sFilterRe = getFilterRegexp();

  // This is one fo the few contstructors that can throw so we make sure to delete its d->pointer.
  try {
    while ( ( pos = sFilterRe.indexIn( vs, pos ) ) != -1 ) {
      len = sFilterRe.matchedLength();
      subString = vs.mid( pos, len );
      const int ssSize = subString.size();

      if ( pos != lastPos ) {
        throw Grantlee::Exception( TagSyntaxError,
            QString::fromLatin1( "Could not parse some characters: \"%1\"" ).arg( vs.mid( lastPos, pos ) ) );
      }

      if ( subString.startsWith( QLatin1Char( FILTER_SEPARATOR ) ) ) {
        subString = subString.right( ssSize - 1 );
        Filter::Ptr f = parser->getFilter( subString );

        Q_ASSERT( f );

        d->m_filterNames << subString;
        d->m_filters << qMakePair<Filter::Ptr, Variable>( f, Variable() );

      } else if ( subString.startsWith( QLatin1Char( FILTER_ARGUMENT_SEPARATOR ) ) ) {
        subString = subString.right( ssSize - 1 );
        const int lastFilter = d->m_filters.size();
        if ( subString.startsWith( QLatin1Char( FILTER_SEPARATOR ) ) )
          throw Grantlee::Exception( EmptyVariableError,
              QString::fromLatin1( "Missing argument to filter: %1" ).arg( d->m_filterNames[lastFilter -1] ) );

        d->m_filters[lastFilter -1].second = Variable( subString );
      } else {
        // Token is _("translated"), or "constant", or a variable;
        d->m_variable = Variable( subString );
      }

      pos += len;
      lastPos = pos;
    }

    const QString remainder = vs.right( vs.size() - lastPos );
    if ( !remainder.isEmpty() ) {
      throw Grantlee::Exception( TagSyntaxError,
          QString::fromLatin1( "Could not parse the remainder, %1 from %2" ).arg( remainder ).arg( varString ) );
    }
  } catch ( ... ) {
    delete d_ptr;
    throw;
  }
}

FilterExpression::FilterExpression( const FilterExpression &other )
    : d_ptr( new FilterExpressionPrivate( this ) )
{
  *this = other;
}

FilterExpression::FilterExpression()
    : d_ptr( new FilterExpressionPrivate( this ) )
{
}

bool FilterExpression::isValid() const
{
  Q_D( const FilterExpression );
  return d->m_variable.isValid();
}

FilterExpression::~FilterExpression()
{
  delete d_ptr;
}

Variable FilterExpression::variable() const
{
  Q_D( const FilterExpression );
  return d->m_variable;
}

FilterExpression &FilterExpression::operator=( const FilterExpression & other )
{
  if (&other == this)
    return *this;
  d_ptr->m_variable = other.d_ptr->m_variable;
  d_ptr->m_filters = other.d_ptr->m_filters;
  d_ptr->m_filterNames = other.d_ptr->m_filterNames;
  return *this;
}

QVariant FilterExpression::resolve( OutputStream *stream, Context *c ) const
{
  Q_D( const FilterExpression );
  QVariant var = d->m_variable.resolve( c );

  Q_FOREACH( const ArgFilter &argfilter, d->m_filters ) {
    Filter::Ptr filter = argfilter.first;
    filter->setStream( stream );
    const Variable argVar = argfilter.second;
    QVariant arg = argVar.resolve( c );

    if ( arg.isValid() ) {
      Grantlee::SafeString argString;
      if ( arg.userType() == qMetaTypeId<Grantlee::SafeString>() ) {
        argString = arg.value<Grantlee::SafeString>();
      } else if ( arg.type() == QVariant::String ) {
        argString = Grantlee::SafeString( arg.toString() );
      }
      if ( argVar.isConstant() ) {
        argString = markSafe( argString );
      }
      if ( !argString.get().isEmpty() ) {
        arg = argString;
      }
    }

    const SafeString varString = getSafeString( var );

    var = filter->doFilter( var, arg, c->autoEscape() );

    if ( var.userType() == qMetaTypeId<Grantlee::SafeString>() || var.type() == QVariant::String ) {
      if ( filter->isSafe() && varString.isSafe() ) {
        var = markSafe( getSafeString( var ) );
      } else if ( varString.needsEscape() ) {
        var = markForEscaping( getSafeString( var ) );
      } else {
        var = getSafeString( var );
      }
    }
  }
  ( *stream ) << getSafeString( var ).get();
  return var;
}

QVariant FilterExpression::resolve( Context *c ) const
{
  OutputStream _dummy;
  return resolve( &_dummy, c );
}

QVariantList FilterExpression::toList( Context *c ) const
{
  const QVariant var = resolve( c );
  return MetaType::toVariantList( var );
}

bool FilterExpression::isTrue( Context *c ) const
{
  return variantIsTrue( resolve( c ) );
}

QStringList FilterExpression::filters() const
{
  Q_D( const FilterExpression );
  return d->m_filterNames;
}

<commit_msg>Use iterators instead of Q_FOREACH<commit_after>/*
  This file is part of the Grantlee template system.

  Copyright (c) 2009,2010 Stephen Kelly <steveire@gmail.com>

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

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

  You should have received a copy of the GNU Lesser General Public
  License along with this library.  If not, see <http://www.gnu.org/licenses/>.

*/

#include "filterexpression.h"

#include "exception.h"
#include "filter.h"
#include "grantlee_latin1literal_p.h"
#include "metatype.h"
#include "parser.h"
#include "util.h"

typedef QPair<Grantlee::Filter::Ptr, Grantlee::Variable> ArgFilter;

namespace Grantlee
{

class FilterExpressionPrivate
{
  FilterExpressionPrivate( FilterExpression *fe )
      : q_ptr( fe )
  {

  }

  Variable m_variable;
  QList<ArgFilter> m_filters;
  QStringList m_filterNames;

  Q_DECLARE_PUBLIC( FilterExpression )
  FilterExpression * const q_ptr;
};

}

using namespace Grantlee;

static const char FILTER_SEPARATOR = '|';
static const char FILTER_ARGUMENT_SEPARATOR = ':';

static QRegExp getFilterRegexp()
{
  const QString filterSep( QRegExp::escape( QChar::fromLatin1( FILTER_SEPARATOR ) ) );
  const QString argSep( QRegExp::escape( QChar::fromLatin1( FILTER_ARGUMENT_SEPARATOR ) ) );

  const QLatin1Literal varChars( "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_." );
  const QLatin1Literal numChars( "[-+\\.]?\\d[\\d\\.e]*" );
  const QString i18nOpen( QRegExp::escape( QLatin1String( "_(" ) ) );
  const QLatin1Literal doubleQuoteStringLiteral( "\"[^\"\\\\]*(?:\\\\.[^\"\\\\]*)*\"" );
  const QLatin1Literal singleQuoteStringLiteral( "\'[^\'\\\\]*(?:\\\\.[^\'\\\\]*)*\'" );
  const QString i18nClose( QRegExp::escape( QLatin1String( ")" ) ) );
  const QString variable = QLatin1Char( '[' ) + varChars + QLatin1Literal( "]+");

  const QString localizedExpression = QLatin1Literal( "(?:" ) + i18nOpen + doubleQuoteStringLiteral + i18nClose + QLatin1Char( '|' )
                                                              + i18nOpen + singleQuoteStringLiteral + i18nClose + QLatin1Char( '|' )
                                                              + i18nOpen + numChars                 + i18nClose + QLatin1Char( '|' )
                                                              + i18nOpen + variable                 + i18nClose + QLatin1Char( ')' );

  const QString constantString = QLatin1Literal( "(?:" ) + doubleQuoteStringLiteral + QLatin1Char( '|' )
                                                         + singleQuoteStringLiteral
                                   + QLatin1Char( ')' );

  const QString filterRawString = QLatin1Char( '^' ) + constantString + QLatin1Char( '|' )
                               + QLatin1Char( '^' ) + localizedExpression + QLatin1Char( '|' )
                               + QLatin1Char( '^' ) + variable + QLatin1Char( '|' )
                               + numChars + QLatin1Char( '|' )
                               + filterSep + QLatin1Literal( "\\w+|" )
                               + argSep
                               + QLatin1Literal( "(?:" )
                                 + constantString + QLatin1Char( '|' ) + localizedExpression
                                 + QLatin1Char( '|' )
                                 + variable
                                 + QLatin1Char( '|' )
                                 + numChars
                                 + QLatin1Char( '|' )
                                 + filterSep
                               + QLatin1Literal( "\\w+)" );

  return QRegExp( filterRawString );
}

FilterExpression::FilterExpression( const QString &varString, Parser *parser )
  : d_ptr( new FilterExpressionPrivate( this ) )
{
  Q_D( FilterExpression );

  int pos = 0;
  int lastPos = 0;
  int len;
  QString subString;

  QString vs = varString;

  static const QRegExp sFilterRe = getFilterRegexp();

  // This is one fo the few contstructors that can throw so we make sure to delete its d->pointer.
  try {
    while ( ( pos = sFilterRe.indexIn( vs, pos ) ) != -1 ) {
      len = sFilterRe.matchedLength();
      subString = vs.mid( pos, len );
      const int ssSize = subString.size();

      if ( pos != lastPos ) {
        throw Grantlee::Exception( TagSyntaxError,
            QString::fromLatin1( "Could not parse some characters: \"%1\"" ).arg( vs.mid( lastPos, pos ) ) );
      }

      if ( subString.startsWith( QLatin1Char( FILTER_SEPARATOR ) ) ) {
        subString = subString.right( ssSize - 1 );
        Filter::Ptr f = parser->getFilter( subString );

        Q_ASSERT( f );

        d->m_filterNames << subString;
        d->m_filters << qMakePair<Filter::Ptr, Variable>( f, Variable() );

      } else if ( subString.startsWith( QLatin1Char( FILTER_ARGUMENT_SEPARATOR ) ) ) {
        subString = subString.right( ssSize - 1 );
        const int lastFilter = d->m_filters.size();
        if ( subString.startsWith( QLatin1Char( FILTER_SEPARATOR ) ) )
          throw Grantlee::Exception( EmptyVariableError,
              QString::fromLatin1( "Missing argument to filter: %1" ).arg( d->m_filterNames[lastFilter -1] ) );

        d->m_filters[lastFilter -1].second = Variable( subString );
      } else {
        // Token is _("translated"), or "constant", or a variable;
        d->m_variable = Variable( subString );
      }

      pos += len;
      lastPos = pos;
    }

    const QString remainder = vs.right( vs.size() - lastPos );
    if ( !remainder.isEmpty() ) {
      throw Grantlee::Exception( TagSyntaxError,
          QString::fromLatin1( "Could not parse the remainder, %1 from %2" ).arg( remainder ).arg( varString ) );
    }
  } catch ( ... ) {
    delete d_ptr;
    throw;
  }
}

FilterExpression::FilterExpression( const FilterExpression &other )
    : d_ptr( new FilterExpressionPrivate( this ) )
{
  *this = other;
}

FilterExpression::FilterExpression()
    : d_ptr( new FilterExpressionPrivate( this ) )
{
}

bool FilterExpression::isValid() const
{
  Q_D( const FilterExpression );
  return d->m_variable.isValid();
}

FilterExpression::~FilterExpression()
{
  delete d_ptr;
}

Variable FilterExpression::variable() const
{
  Q_D( const FilterExpression );
  return d->m_variable;
}

FilterExpression &FilterExpression::operator=( const FilterExpression & other )
{
  if (&other == this)
    return *this;
  d_ptr->m_variable = other.d_ptr->m_variable;
  d_ptr->m_filters = other.d_ptr->m_filters;
  d_ptr->m_filterNames = other.d_ptr->m_filterNames;
  return *this;
}

QVariant FilterExpression::resolve( OutputStream *stream, Context *c ) const
{
  Q_D( const FilterExpression );
  QVariant var = d->m_variable.resolve( c );

  QList<ArgFilter>::const_iterator it = d->m_filters.constBegin();
  const QList<ArgFilter>::const_iterator end = d->m_filters.constEnd();
  for ( ; it != end; ++it ) {
    Filter::Ptr filter = it->first;
    filter->setStream( stream );
    const Variable argVar = it->second;
    QVariant arg = argVar.resolve( c );

    if ( arg.isValid() ) {
      Grantlee::SafeString argString;
      if ( arg.userType() == qMetaTypeId<Grantlee::SafeString>() ) {
        argString = arg.value<Grantlee::SafeString>();
      } else if ( arg.type() == QVariant::String ) {
        argString = Grantlee::SafeString( arg.toString() );
      }
      if ( argVar.isConstant() ) {
        argString = markSafe( argString );
      }
      if ( !argString.get().isEmpty() ) {
        arg = argString;
      }
    }

    const SafeString varString = getSafeString( var );

    var = filter->doFilter( var, arg, c->autoEscape() );

    if ( var.userType() == qMetaTypeId<Grantlee::SafeString>() || var.type() == QVariant::String ) {
      if ( filter->isSafe() && varString.isSafe() ) {
        var = markSafe( getSafeString( var ) );
      } else if ( varString.needsEscape() ) {
        var = markForEscaping( getSafeString( var ) );
      } else {
        var = getSafeString( var );
      }
    }
  }
  ( *stream ) << getSafeString( var ).get();
  return var;
}

QVariant FilterExpression::resolve( Context *c ) const
{
  OutputStream _dummy;
  return resolve( &_dummy, c );
}

QVariantList FilterExpression::toList( Context *c ) const
{
  const QVariant var = resolve( c );
  return MetaType::toVariantList( var );
}

bool FilterExpression::isTrue( Context *c ) const
{
  return variantIsTrue( resolve( c ) );
}

QStringList FilterExpression::filters() const
{
  Q_D( const FilterExpression );
  return d->m_filterNames;
}

<|endoftext|>


<commit_before>/****************************************************************************
**
** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
** All rights reserved.
** Contact: Nokia Corporation (ivan.frade@nokia.com)
**
** This file is part of the QtSparql module (not yet part of the Qt Toolkit).
**
** $QT_BEGIN_LICENSE:LGPL$
** Commercial Usage
** Licensees holding valid Qt Commercial licenses may use this file in
** accordance with the Qt Commercial License Agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and Nokia.
**
** GNU Lesser General Public License Usage
** Alternatively, this file may be used under the terms of the GNU Lesser
** General Public License version 2.1 as published by the Free Software
** Foundation and appearing in the file LICENSE.LGPL included in the
** packaging of this file.  Please review the following information to
** ensure the GNU Lesser General Public License version 2.1 requirements
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
**
** In addition, as a special exception, Nokia gives you certain additional
** rights.  These rights are described in the Nokia Qt LGPL Exception
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 3.0 as published by the Free Software
** Foundation and appearing in the file LICENSE.GPL included in the
** packaging of this file.  Please review the following information to
** ensure the GNU General Public License version 3.0 requirements will be
** met: http://www.gnu.org/copyleft/gpl.html.
**
** If you have questions regarding the use of this file, please contact
** Nokia at ivan.frade@nokia.com.
** $QT_END_LICENSE$
**
****************************************************************************/

#include "qsparql_tracker_direct.h"
#include "qsparql_tracker_direct_p.h"

#include <qsparqlerror.h>
#include <qsparqlbinding.h>
#include <qsparqlquery.h>
#include <qsparqlresultrow.h>

#include <qcoreapplication.h>
#include <qvariant.h>
#include <qstringlist.h>
#include <qvector.h>

#include <qdebug.h>

QT_BEGIN_NAMESPACE

static void
async_cursor_next_callback( GObject *source_object,
                            GAsyncResult *result,
                            gpointer user_data)
{
    Q_UNUSED(source_object);
    QTrackerDirectResultPrivate *data = static_cast<QTrackerDirectResultPrivate*>(user_data);
    GError *error = 0;
    gboolean active = tracker_sparql_cursor_next_finish(data->cursor, result, &error);

    if (error != 0) {
        QSparqlError e(QString::fromLatin1(error ? error->message : "unknown error"));
        e.setType(QSparqlError::BackendError);
        data->setLastError(e);
        g_error_free(error);
        data->terminate();
        return;
    }

    if (!active) {
        if (data->q->isBool()) {
            data->setBoolValue( data->results.count() == 1
                                && data->results[0].count() == 1
                                && data->results[0].binding(0).value().toBool());
        }

        data->terminate();
        return;
    }

    QSparqlResultRow resultRow;
    gint n_columns = tracker_sparql_cursor_get_n_columns(data->cursor);

    if (data->columnNames.empty()) {
        for (int i = 0; i < n_columns; i++) {
            data->columnNames.append(QString::fromUtf8(tracker_sparql_cursor_get_variable_name(data->cursor, i)));
        }
    }

    for (int i = 0; i < n_columns; i++) {
        QString value = QString::fromUtf8(tracker_sparql_cursor_get_string(data->cursor, i, 0));
        QSparqlBinding binding;
        binding.setName(data->columnNames[i]);
        TrackerSparqlValueType type = tracker_sparql_cursor_get_value_type(data->cursor, i);

        switch (type) {
        case TRACKER_SPARQL_VALUE_TYPE_UNBOUND:
            break;
        case TRACKER_SPARQL_VALUE_TYPE_URI:
            binding.setValue(QUrl(value));
            break;
        case TRACKER_SPARQL_VALUE_TYPE_STRING:
            binding.setValue(value, QUrl::fromEncoded("http://www.w3.org/2001/XMLSchema#string"));
            break;
        case TRACKER_SPARQL_VALUE_TYPE_INTEGER:
            binding.setValue(value, QUrl::fromEncoded("http://www.w3.org/2001/XMLSchema#integer"));
            break;
        case TRACKER_SPARQL_VALUE_TYPE_DOUBLE:
            binding.setValue(value, QUrl::fromEncoded("http://www.w3.org/2001/XMLSchema#double"));
            break;
        case TRACKER_SPARQL_VALUE_TYPE_DATETIME:
            binding.setValue(value, QUrl::fromEncoded("http://www.w3.org/2001/XMLSchema#dateTime"));
            break;
        case TRACKER_SPARQL_VALUE_TYPE_BLANK_NODE:
            binding.setBlankNodeLabel(value);
            break;
        case TRACKER_SPARQL_VALUE_TYPE_BOOLEAN:
            binding.setValue(value == QLatin1String("1") ? QString::fromLatin1("true") : QString::fromLatin1("false"),
                             QUrl::fromEncoded("http://www.w3.org/2001/XMLSchema#boolean"));
            break;
        default:
            break;
        }

        resultRow.append(binding);
    }

    data->results.append(resultRow);
    data->dataReady(data->results.count());
    tracker_sparql_cursor_next_async(data->cursor, 0, async_cursor_next_callback, data);
}

static void
async_query_callback(   GObject *source_object,
                        GAsyncResult *result,
                        gpointer user_data)
{
    Q_UNUSED(source_object);
    QTrackerDirectResultPrivate *data = static_cast<QTrackerDirectResultPrivate*>(user_data);
    GError *error = 0;
    data->cursor = tracker_sparql_connection_query_finish(data->driverPrivate->connection, result, &error);

    if (error != 0 || data->cursor == 0) {
        QSparqlError e(QString::fromLatin1(error ? error->message : "unknown error"));
        e.setType(QSparqlError::StatementError);
        data->setLastError(e);
        g_error_free(error);
        data->terminate();
        return;
    }

    tracker_sparql_cursor_next_async(data->cursor, 0, async_cursor_next_callback, data);
}

static void
async_update_callback( GObject *source_object,
                       GAsyncResult *result,
                       gpointer user_data)
{
    Q_UNUSED(source_object);
    QTrackerDirectResultPrivate *data = static_cast<QTrackerDirectResultPrivate*>(user_data);
    GError *error = 0;
    tracker_sparql_connection_update_finish(data->driverPrivate->connection, result, &error);

    if (error != 0) {
        QSparqlError e(QString::fromLatin1(error ? error->message : "unknown error"));
        e.setType(QSparqlError::StatementError);
        data->setLastError(e);
        g_error_free(error);
        data->terminate();
        return;
    }

    data->terminate();
}

QTrackerDirectResultPrivate::QTrackerDirectResultPrivate(QTrackerDirectResult* result, QTrackerDirectDriverPrivate *dpp)
: cursor(0), isFinished(false), loop(0), q(result), driverPrivate(dpp)
{
}

QTrackerDirectResultPrivate::~QTrackerDirectResultPrivate()
{
    if (cursor != 0)
        g_object_unref(cursor);
}

void QTrackerDirectResultPrivate::terminate()
{
    isFinished = true;
    q->emit finished();

    if (loop != 0)
        loop->exit();
}

void QTrackerDirectResultPrivate::setLastError(const QSparqlError& e)
{
    q->setLastError(e);
}

void QTrackerDirectResultPrivate::setBoolValue(bool v)
{
    q->setBoolValue(v);
}

void QTrackerDirectResultPrivate::dataReady(int totalCount)
{
    emit q->dataReady(totalCount);
}

QTrackerDirectResult::QTrackerDirectResult(QTrackerDirectDriverPrivate* p)
{
    d = new QTrackerDirectResultPrivate(this, p);
}

QTrackerDirectResult::~QTrackerDirectResult()
{
    delete d;
}

QTrackerDirectResult* QTrackerDirectDriver::exec(const QString& query,
                          QSparqlQuery::StatementType type)
{
    QTrackerDirectResult* res = new QTrackerDirectResult(d);
    res->setStatementType(type);

    switch (type) {
    case QSparqlQuery::AskStatement:
    case QSparqlQuery::SelectStatement:
    {
        tracker_sparql_connection_query_async(  d->connection,
                                                query.toLatin1().constData(),
                                                0,
                                                async_query_callback,
                                                res->d);
        break;
    }
    case QSparqlQuery::InsertStatement:
    case QSparqlQuery::DeleteStatement:
        tracker_sparql_connection_update_async( d->connection,
                                                query.toLatin1().constData(),
                                                0,
                                                0,
                                                async_update_callback,
                                                res->d);
    {
        break;
    }
    default:
        qWarning() << "Tracker backend: unsupported statement type";
        res->setLastError(QSparqlError(
                              QLatin1String("Unsupported statement type"),
                              QSparqlError::BackendError));
        break;
    }
    return res;
}

void QTrackerDirectResult::cleanup()
{
    setPos(QSparql::BeforeFirstRow);
}

QSparqlBinding QTrackerDirectResult::binding(int field) const
{
    if (!isValid()) {
        return QSparqlBinding();
    }

    if (field >= d->results[pos()].count() || field < 0) {
        qWarning() << "QTrackerDirectResult::data[" << pos() << "]: column" << field << "out of range";
        return QSparqlBinding();
    }

    return d->results[pos()].binding(field);
}

QVariant QTrackerDirectResult::value(int field) const
{
    if (!isValid()) {
        return QVariant();
    }

    if (field >= d->results[pos()].count() || field < 0) {
        qWarning() << "QTrackerDirectResult::data[" << pos() << "]: column" << field << "out of range";
        return QVariant();
    }

    return d->results[pos()].value(field);
}

void QTrackerDirectResult::waitForFinished()
{
    if (d->isFinished)
        return;

    QEventLoop loop;
    d->loop = &loop;
    loop.exec();
    d->loop = 0;
}

bool QTrackerDirectResult::isFinished() const
{
    return d->isFinished;
}

int QTrackerDirectResult::size() const
{
    return d->results.count();
}

QSparqlResultRow QTrackerDirectResult::current() const
{
    if (!isValid()) {
        return QSparqlResultRow();
    }

    if (pos() < 0 || pos() >= d->results.count())
        return QSparqlResultRow();

    return d->results[pos()];
}

QTrackerDirectDriverPrivate::QTrackerDirectDriverPrivate()
{
}

QTrackerDirectDriverPrivate::~QTrackerDirectDriverPrivate()
{
}

QTrackerDirectDriver::QTrackerDirectDriver(QObject* parent)
    : QSparqlDriver(parent)
{
    d = new QTrackerDirectDriverPrivate();
    /* Initialize GLib type system */
    g_type_init();

}

QTrackerDirectDriver::~QTrackerDirectDriver()
{
    delete d;
}

bool QTrackerDirectDriver::hasFeature(QSparqlConnection::Feature f) const
{
    switch (f) {
    case QSparqlConnection::QuerySize:
        return true;
    case QSparqlConnection::AskQueries:
        return true;
    case QSparqlConnection::ConstructQueries:
        return false;
    case QSparqlConnection::UpdateQueries:
        return true;
    case QSparqlConnection::DefaultGraph:
        return true;
    }
    return false;
}

QAtomicInt connectionCounter = 0;

bool QTrackerDirectDriver::open(const QSparqlConnectionOptions& options)
{
    Q_UNUSED(options);

    if (isOpen())
        close();

    GError *error = 0;
    d->connection = tracker_sparql_connection_get(0, &error);
    // maybe-TODO: Add the GCancellable
    if (!d->connection) {
        qWarning("Couldn't obtain a direct connection to the Tracker store: %s",
                    error ? error->message : "unknown error");
        g_error_free(error);
        return false;
    }

    setOpen(true);
    setOpenError(false);

    return true;
}

void QTrackerDirectDriver::close()
{
    g_object_unref(d->connection);

    if (isOpen()) {
        setOpen(false);
        setOpenError(false);
    }
}

QT_END_NAMESPACE
<commit_msg>* The direct driver was not setting the query text in result. Probably it should be done in QSparqlConnection::exec() anyway<commit_after>/****************************************************************************
**
** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
** All rights reserved.
** Contact: Nokia Corporation (ivan.frade@nokia.com)
**
** This file is part of the QtSparql module (not yet part of the Qt Toolkit).
**
** $QT_BEGIN_LICENSE:LGPL$
** Commercial Usage
** Licensees holding valid Qt Commercial licenses may use this file in
** accordance with the Qt Commercial License Agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and Nokia.
**
** GNU Lesser General Public License Usage
** Alternatively, this file may be used under the terms of the GNU Lesser
** General Public License version 2.1 as published by the Free Software
** Foundation and appearing in the file LICENSE.LGPL included in the
** packaging of this file.  Please review the following information to
** ensure the GNU Lesser General Public License version 2.1 requirements
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
**
** In addition, as a special exception, Nokia gives you certain additional
** rights.  These rights are described in the Nokia Qt LGPL Exception
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 3.0 as published by the Free Software
** Foundation and appearing in the file LICENSE.GPL included in the
** packaging of this file.  Please review the following information to
** ensure the GNU General Public License version 3.0 requirements will be
** met: http://www.gnu.org/copyleft/gpl.html.
**
** If you have questions regarding the use of this file, please contact
** Nokia at ivan.frade@nokia.com.
** $QT_END_LICENSE$
**
****************************************************************************/

#include "qsparql_tracker_direct.h"
#include "qsparql_tracker_direct_p.h"

#include <qsparqlerror.h>
#include <qsparqlbinding.h>
#include <qsparqlquery.h>
#include <qsparqlresultrow.h>

#include <qcoreapplication.h>
#include <qvariant.h>
#include <qstringlist.h>
#include <qvector.h>

#include <qdebug.h>

QT_BEGIN_NAMESPACE

static void
async_cursor_next_callback( GObject *source_object,
                            GAsyncResult *result,
                            gpointer user_data)
{
    Q_UNUSED(source_object);
    QTrackerDirectResultPrivate *data = static_cast<QTrackerDirectResultPrivate*>(user_data);
    GError *error = 0;
    gboolean active = tracker_sparql_cursor_next_finish(data->cursor, result, &error);

    if (error != 0) {
        QSparqlError e(QString::fromLatin1(error ? error->message : "unknown error"));
        e.setType(QSparqlError::BackendError);
        data->setLastError(e);
        g_error_free(error);
        data->terminate();
        return;
    }

    if (!active) {
        if (data->q->isBool()) {
            data->setBoolValue( data->results.count() == 1
                                && data->results[0].count() == 1
                                && data->results[0].binding(0).value().toBool());
        }

        data->terminate();
        return;
    }

    QSparqlResultRow resultRow;
    gint n_columns = tracker_sparql_cursor_get_n_columns(data->cursor);

    if (data->columnNames.empty()) {
        for (int i = 0; i < n_columns; i++) {
            data->columnNames.append(QString::fromUtf8(tracker_sparql_cursor_get_variable_name(data->cursor, i)));
        }
    }

    for (int i = 0; i < n_columns; i++) {
        QString value = QString::fromUtf8(tracker_sparql_cursor_get_string(data->cursor, i, 0));
        QSparqlBinding binding;
        binding.setName(data->columnNames[i]);
        TrackerSparqlValueType type = tracker_sparql_cursor_get_value_type(data->cursor, i);

        switch (type) {
        case TRACKER_SPARQL_VALUE_TYPE_UNBOUND:
            break;
        case TRACKER_SPARQL_VALUE_TYPE_URI:
            binding.setValue(QUrl(value));
            break;
        case TRACKER_SPARQL_VALUE_TYPE_STRING:
            binding.setValue(value, QUrl::fromEncoded("http://www.w3.org/2001/XMLSchema#string"));
            break;
        case TRACKER_SPARQL_VALUE_TYPE_INTEGER:
            binding.setValue(value, QUrl::fromEncoded("http://www.w3.org/2001/XMLSchema#integer"));
            break;
        case TRACKER_SPARQL_VALUE_TYPE_DOUBLE:
            binding.setValue(value, QUrl::fromEncoded("http://www.w3.org/2001/XMLSchema#double"));
            break;
        case TRACKER_SPARQL_VALUE_TYPE_DATETIME:
            binding.setValue(value, QUrl::fromEncoded("http://www.w3.org/2001/XMLSchema#dateTime"));
            break;
        case TRACKER_SPARQL_VALUE_TYPE_BLANK_NODE:
            binding.setBlankNodeLabel(value);
            break;
        case TRACKER_SPARQL_VALUE_TYPE_BOOLEAN:
            binding.setValue(value == QLatin1String("1") ? QString::fromLatin1("true") : QString::fromLatin1("false"),
                             QUrl::fromEncoded("http://www.w3.org/2001/XMLSchema#boolean"));
            break;
        default:
            break;
        }

        resultRow.append(binding);
    }

    data->results.append(resultRow);
    data->dataReady(data->results.count());
    tracker_sparql_cursor_next_async(data->cursor, 0, async_cursor_next_callback, data);
}

static void
async_query_callback(   GObject *source_object,
                        GAsyncResult *result,
                        gpointer user_data)
{
    Q_UNUSED(source_object);
    QTrackerDirectResultPrivate *data = static_cast<QTrackerDirectResultPrivate*>(user_data);
    GError *error = 0;
    data->cursor = tracker_sparql_connection_query_finish(data->driverPrivate->connection, result, &error);

    if (error != 0 || data->cursor == 0) {
        QSparqlError e(QString::fromLatin1(error ? error->message : "unknown error"));
        e.setType(QSparqlError::StatementError);
        data->setLastError(e);
        g_error_free(error);
        data->terminate();
        return;
    }

    tracker_sparql_cursor_next_async(data->cursor, 0, async_cursor_next_callback, data);
}

static void
async_update_callback( GObject *source_object,
                       GAsyncResult *result,
                       gpointer user_data)
{
    Q_UNUSED(source_object);
    QTrackerDirectResultPrivate *data = static_cast<QTrackerDirectResultPrivate*>(user_data);
    GError *error = 0;
    tracker_sparql_connection_update_finish(data->driverPrivate->connection, result, &error);

    if (error != 0) {
        QSparqlError e(QString::fromLatin1(error ? error->message : "unknown error"));
        e.setType(QSparqlError::StatementError);
        data->setLastError(e);
        g_error_free(error);
        data->terminate();
        return;
    }

    data->terminate();
}

QTrackerDirectResultPrivate::QTrackerDirectResultPrivate(QTrackerDirectResult* result, QTrackerDirectDriverPrivate *dpp)
: cursor(0), isFinished(false), loop(0), q(result), driverPrivate(dpp)
{
}

QTrackerDirectResultPrivate::~QTrackerDirectResultPrivate()
{
    if (cursor != 0)
        g_object_unref(cursor);
}

void QTrackerDirectResultPrivate::terminate()
{
    isFinished = true;
    q->emit finished();

    if (loop != 0)
        loop->exit();
}

void QTrackerDirectResultPrivate::setLastError(const QSparqlError& e)
{
    q->setLastError(e);
}

void QTrackerDirectResultPrivate::setBoolValue(bool v)
{
    q->setBoolValue(v);
}

void QTrackerDirectResultPrivate::dataReady(int totalCount)
{
    emit q->dataReady(totalCount);
}

QTrackerDirectResult::QTrackerDirectResult(QTrackerDirectDriverPrivate* p)
{
    d = new QTrackerDirectResultPrivate(this, p);
}

QTrackerDirectResult::~QTrackerDirectResult()
{
    delete d;
}

QTrackerDirectResult* QTrackerDirectDriver::exec(const QString& query,
                          QSparqlQuery::StatementType type)
{
    QTrackerDirectResult* res = new QTrackerDirectResult(d);
    res->setStatementType(type);
    res->setQuery(query);

    switch (type) {
    case QSparqlQuery::AskStatement:
    case QSparqlQuery::SelectStatement:
    {
        tracker_sparql_connection_query_async(  d->connection,
                                                query.toLatin1().constData(),
                                                0,
                                                async_query_callback,
                                                res->d);
        break;
    }
    case QSparqlQuery::InsertStatement:
    case QSparqlQuery::DeleteStatement:
        tracker_sparql_connection_update_async( d->connection,
                                                query.toLatin1().constData(),
                                                0,
                                                0,
                                                async_update_callback,
                                                res->d);
    {
        break;
    }
    default:
        qWarning() << "Tracker backend: unsupported statement type";
        res->setLastError(QSparqlError(
                              QLatin1String("Unsupported statement type"),
                              QSparqlError::BackendError));
        break;
    }
    return res;
}

void QTrackerDirectResult::cleanup()
{
    setPos(QSparql::BeforeFirstRow);
}

QSparqlBinding QTrackerDirectResult::binding(int field) const
{
    if (!isValid()) {
        return QSparqlBinding();
    }

    if (field >= d->results[pos()].count() || field < 0) {
        qWarning() << "QTrackerDirectResult::data[" << pos() << "]: column" << field << "out of range";
        return QSparqlBinding();
    }

    return d->results[pos()].binding(field);
}

QVariant QTrackerDirectResult::value(int field) const
{
    if (!isValid()) {
        return QVariant();
    }

    if (field >= d->results[pos()].count() || field < 0) {
        qWarning() << "QTrackerDirectResult::data[" << pos() << "]: column" << field << "out of range";
        return QVariant();
    }

    return d->results[pos()].value(field);
}

void QTrackerDirectResult::waitForFinished()
{
    if (d->isFinished)
        return;

    QEventLoop loop;
    d->loop = &loop;
    loop.exec();
    d->loop = 0;
}

bool QTrackerDirectResult::isFinished() const
{
    return d->isFinished;
}

int QTrackerDirectResult::size() const
{
    return d->results.count();
}

QSparqlResultRow QTrackerDirectResult::current() const
{
    if (!isValid()) {
        return QSparqlResultRow();
    }

    if (pos() < 0 || pos() >= d->results.count())
        return QSparqlResultRow();

    return d->results[pos()];
}

QTrackerDirectDriverPrivate::QTrackerDirectDriverPrivate()
{
}

QTrackerDirectDriverPrivate::~QTrackerDirectDriverPrivate()
{
}

QTrackerDirectDriver::QTrackerDirectDriver(QObject* parent)
    : QSparqlDriver(parent)
{
    d = new QTrackerDirectDriverPrivate();
    /* Initialize GLib type system */
    g_type_init();

}

QTrackerDirectDriver::~QTrackerDirectDriver()
{
    delete d;
}

bool QTrackerDirectDriver::hasFeature(QSparqlConnection::Feature f) const
{
    switch (f) {
    case QSparqlConnection::QuerySize:
        return true;
    case QSparqlConnection::AskQueries:
        return true;
    case QSparqlConnection::ConstructQueries:
        return false;
    case QSparqlConnection::UpdateQueries:
        return true;
    case QSparqlConnection::DefaultGraph:
        return true;
    }
    return false;
}

QAtomicInt connectionCounter = 0;

bool QTrackerDirectDriver::open(const QSparqlConnectionOptions& options)
{
    Q_UNUSED(options);

    if (isOpen())
        close();

    GError *error = 0;
    d->connection = tracker_sparql_connection_get(0, &error);
    // maybe-TODO: Add the GCancellable
    if (!d->connection) {
        qWarning("Couldn't obtain a direct connection to the Tracker store: %s",
                    error ? error->message : "unknown error");
        g_error_free(error);
        return false;
    }

    setOpen(true);
    setOpenError(false);

    return true;
}

void QTrackerDirectDriver::close()
{
    g_object_unref(d->connection);

    if (isOpen()) {
        setOpen(false);
        setOpenError(false);
    }
}

QT_END_NAMESPACE
<|endoftext|>


<commit_before>/*  vim:expandtab:shiftwidth=2:tabstop=2:smarttab:
 *
 *  LibMemcached
 *
 *  Copyright (C) 2011 Data Differential, http://datadifferential.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.
 *
 *      * The names of its contributors may not be used to endorse or
 *  promote products derived from this software without specific prior
 *  written permission.
 *
 *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 *  "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 *  LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 *  A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 *  OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 *  SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 *  LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 *  DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 *  THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 *  (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 *  OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 */

#include <libmemcached/common.h>
#include <cassert>

#define MAX_ERROR_LENGTH 2048
struct memcached_error_t
{
  memcached_st *root;
  uint64_t query_id;
  struct memcached_error_t *next;
  memcached_return_t rc;
  int local_errno;
  size_t size;
  char message[MAX_ERROR_LENGTH];
};

static void _set(memcached_st& memc, memcached_string_t *str, memcached_return_t &rc, const char *at, int local_errno= 0)
{
  (void)at;
  if (memc.error_messages && memc.error_messages->query_id != memc.query_id)
  {
    memcached_error_free(&memc);
  }

  // For memory allocation we use our error since it is a bit more specific
  if (local_errno == ENOMEM and rc == MEMCACHED_ERRNO)
  {
    rc= MEMCACHED_MEMORY_ALLOCATION_FAILURE;
  }

  if (rc == MEMCACHED_MEMORY_ALLOCATION_FAILURE)
  {
    local_errno= ENOMEM;
  }

  if (rc == MEMCACHED_ERRNO and not local_errno)
  {
    local_errno= errno;
    rc= MEMCACHED_ERRNO;
  }

  if (rc == MEMCACHED_ERRNO and local_errno == ENOTCONN)
  {
    rc= MEMCACHED_CONNECTION_FAILURE;
  }

  if (local_errno == EINVAL)
  {
    rc= MEMCACHED_INVALID_ARGUMENTS;
  }

  if (local_errno == ECONNREFUSED)
  {
    rc= MEMCACHED_CONNECTION_FAILURE;
  }

  memcached_error_t *error= (struct memcached_error_t *)libmemcached_malloc(&memc, sizeof(struct memcached_error_t));
  if (not error) // Bad business if this happens
    return;

  error->root= &memc;
  error->query_id= memc.query_id;
  error->rc= rc;
  error->local_errno= local_errno;

  if (str and str->size and local_errno)
  {
    error->size= (int)snprintf(error->message, MAX_ERROR_LENGTH, "%s(%s), %.*s -> %s",
                               memcached_strerror(&memc, rc),
                               strerror(local_errno),
                               memcached_string_printf(*str), at);
  }
  else if (local_errno)
  {
    error->size= (int)snprintf(error->message, MAX_ERROR_LENGTH, "%s(%s) -> %s",
                               memcached_strerror(&memc, rc),
                               strerror(local_errno), at);
  }
  else if (str and str->size)
  {
    error->size= (int)snprintf(error->message, MAX_ERROR_LENGTH, "%s, %.*s -> %s",
                               memcached_strerror(&memc, rc),
                               int(str->size), str->c_str, at);
  }
  else
  {
    error->size= (int)snprintf(error->message, MAX_ERROR_LENGTH, "%s -> %s",
                               memcached_strerror(&memc, rc), at);
  }

  error->next= memc.error_messages;
  memc.error_messages= error;
}

memcached_return_t memcached_set_error(memcached_st& memc, memcached_return_t rc, const char *at, const char *str, size_t length)
{
  assert(rc != MEMCACHED_ERRNO);
  memcached_string_t tmp= { str, length };
  return memcached_set_error(memc, rc, at, tmp);
}

memcached_return_t memcached_set_error(memcached_server_st& self, memcached_return_t rc, const char *at, const char *str, size_t length)
{
  assert(rc != MEMCACHED_ERRNO);
  memcached_string_t tmp= { str, length };
  return memcached_set_error(self, rc, at, tmp);
}

memcached_return_t memcached_set_error(memcached_st& memc, memcached_return_t rc, const char *at, memcached_string_t& str)
{
  assert(rc != MEMCACHED_ERRNO);
  if (memcached_success(rc))
    return MEMCACHED_SUCCESS;

  _set(memc, &str, rc, at);

  return rc;
}

memcached_return_t memcached_set_error(memcached_server_st& self, memcached_return_t rc, const char *at, memcached_string_t& str)
{
  assert(rc != MEMCACHED_ERRNO);
  if (memcached_success(rc))
    return MEMCACHED_SUCCESS;

  char hostname_port_message[MAX_ERROR_LENGTH];
  int size;
  if (str.size)
  {
    size= snprintf(hostname_port_message, sizeof(hostname_port_message), "%.*s, host: %s:%d",
                   memcached_string_printf(str),
                   self.hostname, int(self.port));
  }
  else
  {
    size= snprintf(hostname_port_message, sizeof(hostname_port_message), "host: %s:%d",
                   self.hostname, int(self.port));
  }

  memcached_string_t error_host= { hostname_port_message, size };

  if (not self.root)
    return rc;

  _set(*self.root, &error_host, rc, at);

  return rc;
}

memcached_return_t memcached_set_error(memcached_server_st& self, memcached_return_t rc, const char *at)
{
  assert(rc != MEMCACHED_ERRNO);
  if (memcached_success(rc))
    return MEMCACHED_SUCCESS;

  char hostname_port[NI_MAXHOST +NI_MAXSERV + sizeof("host : ")];
  int size= snprintf(hostname_port, sizeof(hostname_port), "host: %s:%d", self.hostname, int(self.port));

  memcached_string_t error_host= { hostname_port, size};

  if (not self.root)
    return rc;

  _set(*self.root, &error_host, rc, at);

  return rc;
}

memcached_return_t memcached_set_error(memcached_st& self, memcached_return_t rc, const char *at)
{
  assert(rc != MEMCACHED_ERRNO);
  if (memcached_success(rc))
    return MEMCACHED_SUCCESS;

  _set(self, NULL, rc, at);

  return rc;
}

memcached_return_t memcached_set_errno(memcached_st& self, int local_errno, const char *at, const char *str, size_t length)
{
  memcached_string_t tmp= { str, length };
  return memcached_set_errno(self, local_errno, at, tmp);
}

memcached_return_t memcached_set_errno(memcached_server_st& self, int local_errno, const char *at, const char *str, size_t length)
{
  memcached_string_t tmp= { str, length };
  return memcached_set_errno(self, local_errno, at, tmp);
}

memcached_return_t memcached_set_errno(memcached_st& self, int local_errno, const char *at)
{
  if (not local_errno)
    return MEMCACHED_SUCCESS;

  memcached_return_t rc= MEMCACHED_ERRNO;
  _set(self, NULL, rc, at, local_errno);

  return rc;
}

memcached_return_t memcached_set_errno(memcached_st& memc, int local_errno, const char *at, memcached_string_t& str)
{
  if (not local_errno)
    return MEMCACHED_SUCCESS;

  memcached_return_t rc= MEMCACHED_ERRNO;
  _set(memc, &str, rc, at, local_errno);

  return rc;
}

memcached_return_t memcached_set_errno(memcached_server_st& self, int local_errno, const char *at, memcached_string_t& str)
{
  if (not local_errno)
    return MEMCACHED_SUCCESS;

  char hostname_port_message[MAX_ERROR_LENGTH];
  int size;
  if (str.size)
  {
    size= snprintf(hostname_port_message, sizeof(hostname_port_message), "%.*s, host: %s:%d",
                   memcached_string_printf(str),
                   self.hostname, int(self.port));
  }
  else
  {
    size= snprintf(hostname_port_message, sizeof(hostname_port_message), "host: %s:%d",
                   self.hostname, int(self.port));
  }

  memcached_string_t error_host= { hostname_port_message, size };

  self.cached_errno= local_errno; // Store in the actual server

  memcached_return_t rc= MEMCACHED_ERRNO;
  if (not self.root)
    return rc;

  _set(*self.root, &error_host, rc, at, local_errno);

  return rc;
}

memcached_return_t memcached_set_errno(memcached_server_st& self, int local_errno, const char *at)
{
  if (not local_errno)
    return MEMCACHED_SUCCESS;

  char hostname_port_message[MAX_ERROR_LENGTH];
  int size = snprintf(hostname_port_message, sizeof(hostname_port_message), "host: %s:%d",
                      self.hostname, int(self.port));

  memcached_string_t error_host= { hostname_port_message, size };

  self.cached_errno= local_errno; // Store in the actual server

  memcached_return_t rc= MEMCACHED_ERRNO;
  if (not self.root)
    return rc;

  _set(*self.root, &error_host, rc, at, local_errno);

  return rc;
}

static void _error_print(const memcached_error_t *error)
{
  if (not error)
    return;

  if (not error->size)
  {
    fprintf(stderr, "%s\n", memcached_strerror(NULL, error->rc) );
  }
  else
  {
    fprintf(stderr, "%s %s\n", memcached_strerror(NULL, error->rc), error->message);
  }

  _error_print(error->next);
}

void memcached_error_print(const memcached_st *self)
{
  if (not self)
    return;

  _error_print(self->error_messages);
}

static void _error_free(memcached_error_t *error)
{
  if (not error)
    return;

  _error_free(error->next);

  if (error && error->root)
  {
    libmemcached_free(error->root, error);
  }
  else if (error)
  {
    free(error);
  }
}

void memcached_error_free(memcached_st *self)
{
  if (not self)
    return;

  _error_free(self->error_messages);
  self->error_messages= NULL;
}

const char *memcached_last_error_message(memcached_st *memc)
{
  if (not memc)
    return memcached_strerror(memc, MEMCACHED_INVALID_ARGUMENTS);

  if (not memc->error_messages)
    return memcached_strerror(memc, MEMCACHED_SUCCESS);

  if (not memc->error_messages->size)
    return memcached_strerror(memc, memc->error_messages->rc);

  return memc->error_messages->message;
}

bool memcached_has_current_error(memcached_st &memc)
{
  if (memc.error_messages
      and memc.error_messages->query_id == memc.query_id
      and memcached_failed(memc.error_messages->rc))
  {
    return true;
  }

  return false;
}

memcached_return_t memcached_last_error(memcached_st *memc)
{
  if (not memc)
    return MEMCACHED_INVALID_ARGUMENTS;

  if (not memc->error_messages)
    return MEMCACHED_SUCCESS;

  return memc->error_messages->rc;
}

int memcached_last_error_errno(memcached_st *memc)
{
  if (not memc)
    return 0;

  if (not memc->error_messages)
    return 0;

  return memc->error_messages->local_errno;
}
<commit_msg>Make sure we use the correct strerror() in case someone is using threads.<commit_after>/*  vim:expandtab:shiftwidth=2:tabstop=2:smarttab:
 *
 *  LibMemcached
 *
 *  Copyright (C) 2011 Data Differential, http://datadifferential.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.
 *
 *      * The names of its contributors may not be used to endorse or
 *  promote products derived from this software without specific prior
 *  written permission.
 *
 *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 *  "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 *  LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 *  A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 *  OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 *  SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 *  LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 *  DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 *  THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 *  (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 *  OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 */

#include <libmemcached/common.h>
#include <cassert>

#define MAX_ERROR_LENGTH 2048
struct memcached_error_t
{
  memcached_st *root;
  uint64_t query_id;
  struct memcached_error_t *next;
  memcached_return_t rc;
  int local_errno;
  size_t size;
  char message[MAX_ERROR_LENGTH];
};

static void _set(memcached_st& memc, memcached_string_t *str, memcached_return_t &rc, const char *at, int local_errno= 0)
{
  (void)at;
  if (memc.error_messages && memc.error_messages->query_id != memc.query_id)
  {
    memcached_error_free(&memc);
  }

  // For memory allocation we use our error since it is a bit more specific
  if (local_errno == ENOMEM and rc == MEMCACHED_ERRNO)
  {
    rc= MEMCACHED_MEMORY_ALLOCATION_FAILURE;
  }

  if (rc == MEMCACHED_MEMORY_ALLOCATION_FAILURE)
  {
    local_errno= ENOMEM;
  }

  if (rc == MEMCACHED_ERRNO and not local_errno)
  {
    local_errno= errno;
    rc= MEMCACHED_ERRNO;
  }

  if (rc == MEMCACHED_ERRNO and local_errno == ENOTCONN)
  {
    rc= MEMCACHED_CONNECTION_FAILURE;
  }

  if (local_errno == EINVAL)
  {
    rc= MEMCACHED_INVALID_ARGUMENTS;
  }

  if (local_errno == ECONNREFUSED)
  {
    rc= MEMCACHED_CONNECTION_FAILURE;
  }

  memcached_error_t *error= (struct memcached_error_t *)libmemcached_malloc(&memc, sizeof(struct memcached_error_t));
  if (not error) // Bad business if this happens
    return;

  error->root= &memc;
  error->query_id= memc.query_id;
  error->rc= rc;
  error->local_errno= local_errno;

  const char *errmsg_ptr;
  char errmsg[MAX_ERROR_LENGTH];
  errmsg[0]= 0;
  errmsg_ptr= errmsg;

  if (local_errno)
  {
#ifdef STRERROR_R_CHAR_P
    errmsg_ptr= strerror_r(local_errno, errmsg, sizeof(errmsg));
#else
    strerror_r(local_errno, errmsg, sizeof(errmsg));
    errmsg_ptr= errmsg;
#endif
  }

  if (str and str->size and local_errno)
  {
    error->size= (int)snprintf(error->message, MAX_ERROR_LENGTH, "%s(%s), %.*s -> %s",
                               memcached_strerror(&memc, rc),
                               errmsg_ptr,
                               memcached_string_printf(*str), at);
  }
  else if (local_errno)
  {
    error->size= (int)snprintf(error->message, MAX_ERROR_LENGTH, "%s(%s) -> %s",
                               memcached_strerror(&memc, rc),
                               errmsg_ptr,
                               at);
  }
  else if (str and str->size)
  {
    error->size= (int)snprintf(error->message, MAX_ERROR_LENGTH, "%s, %.*s -> %s",
                               memcached_strerror(&memc, rc),
                               int(str->size), str->c_str, at);
  }
  else
  {
    error->size= (int)snprintf(error->message, MAX_ERROR_LENGTH, "%s -> %s",
                               memcached_strerror(&memc, rc), at);
  }

  error->next= memc.error_messages;
  memc.error_messages= error;
}

memcached_return_t memcached_set_error(memcached_st& memc, memcached_return_t rc, const char *at, const char *str, size_t length)
{
  assert(rc != MEMCACHED_ERRNO);
  memcached_string_t tmp= { str, length };
  return memcached_set_error(memc, rc, at, tmp);
}

memcached_return_t memcached_set_error(memcached_server_st& self, memcached_return_t rc, const char *at, const char *str, size_t length)
{
  assert(rc != MEMCACHED_ERRNO);
  memcached_string_t tmp= { str, length };
  return memcached_set_error(self, rc, at, tmp);
}

memcached_return_t memcached_set_error(memcached_st& memc, memcached_return_t rc, const char *at, memcached_string_t& str)
{
  assert(rc != MEMCACHED_ERRNO);
  if (memcached_success(rc))
    return MEMCACHED_SUCCESS;

  _set(memc, &str, rc, at);

  return rc;
}

memcached_return_t memcached_set_error(memcached_server_st& self, memcached_return_t rc, const char *at, memcached_string_t& str)
{
  assert(rc != MEMCACHED_ERRNO);
  if (memcached_success(rc))
    return MEMCACHED_SUCCESS;

  char hostname_port_message[MAX_ERROR_LENGTH];
  int size;
  if (str.size)
  {
    size= snprintf(hostname_port_message, sizeof(hostname_port_message), "%.*s, host: %s:%d",
                   memcached_string_printf(str),
                   self.hostname, int(self.port));
  }
  else
  {
    size= snprintf(hostname_port_message, sizeof(hostname_port_message), "host: %s:%d",
                   self.hostname, int(self.port));
  }

  memcached_string_t error_host= { hostname_port_message, size };

  if (not self.root)
    return rc;

  _set(*self.root, &error_host, rc, at);

  return rc;
}

memcached_return_t memcached_set_error(memcached_server_st& self, memcached_return_t rc, const char *at)
{
  assert(rc != MEMCACHED_ERRNO);
  if (memcached_success(rc))
    return MEMCACHED_SUCCESS;

  char hostname_port[NI_MAXHOST +NI_MAXSERV + sizeof("host : ")];
  int size= snprintf(hostname_port, sizeof(hostname_port), "host: %s:%d", self.hostname, int(self.port));

  memcached_string_t error_host= { hostname_port, size};

  if (not self.root)
    return rc;

  _set(*self.root, &error_host, rc, at);

  return rc;
}

memcached_return_t memcached_set_error(memcached_st& self, memcached_return_t rc, const char *at)
{
  assert(rc != MEMCACHED_ERRNO);
  if (memcached_success(rc))
    return MEMCACHED_SUCCESS;

  _set(self, NULL, rc, at);

  return rc;
}

memcached_return_t memcached_set_errno(memcached_st& self, int local_errno, const char *at, const char *str, size_t length)
{
  memcached_string_t tmp= { str, length };
  return memcached_set_errno(self, local_errno, at, tmp);
}

memcached_return_t memcached_set_errno(memcached_server_st& self, int local_errno, const char *at, const char *str, size_t length)
{
  memcached_string_t tmp= { str, length };
  return memcached_set_errno(self, local_errno, at, tmp);
}

memcached_return_t memcached_set_errno(memcached_st& self, int local_errno, const char *at)
{
  if (not local_errno)
    return MEMCACHED_SUCCESS;

  memcached_return_t rc= MEMCACHED_ERRNO;
  _set(self, NULL, rc, at, local_errno);

  return rc;
}

memcached_return_t memcached_set_errno(memcached_st& memc, int local_errno, const char *at, memcached_string_t& str)
{
  if (not local_errno)
    return MEMCACHED_SUCCESS;

  memcached_return_t rc= MEMCACHED_ERRNO;
  _set(memc, &str, rc, at, local_errno);

  return rc;
}

memcached_return_t memcached_set_errno(memcached_server_st& self, int local_errno, const char *at, memcached_string_t& str)
{
  if (not local_errno)
    return MEMCACHED_SUCCESS;

  char hostname_port_message[MAX_ERROR_LENGTH];
  int size;
  if (str.size)
  {
    size= snprintf(hostname_port_message, sizeof(hostname_port_message), "%.*s, host: %s:%d",
                   memcached_string_printf(str),
                   self.hostname, int(self.port));
  }
  else
  {
    size= snprintf(hostname_port_message, sizeof(hostname_port_message), "host: %s:%d",
                   self.hostname, int(self.port));
  }

  memcached_string_t error_host= { hostname_port_message, size };

  self.cached_errno= local_errno; // Store in the actual server

  memcached_return_t rc= MEMCACHED_ERRNO;
  if (not self.root)
    return rc;

  _set(*self.root, &error_host, rc, at, local_errno);

  return rc;
}

memcached_return_t memcached_set_errno(memcached_server_st& self, int local_errno, const char *at)
{
  if (not local_errno)
    return MEMCACHED_SUCCESS;

  char hostname_port_message[MAX_ERROR_LENGTH];
  int size = snprintf(hostname_port_message, sizeof(hostname_port_message), "host: %s:%d",
                      self.hostname, int(self.port));

  memcached_string_t error_host= { hostname_port_message, size };

  self.cached_errno= local_errno; // Store in the actual server

  memcached_return_t rc= MEMCACHED_ERRNO;
  if (not self.root)
    return rc;

  _set(*self.root, &error_host, rc, at, local_errno);

  return rc;
}

static void _error_print(const memcached_error_t *error)
{
  if (not error)
    return;

  if (not error->size)
  {
    fprintf(stderr, "%s\n", memcached_strerror(NULL, error->rc) );
  }
  else
  {
    fprintf(stderr, "%s %s\n", memcached_strerror(NULL, error->rc), error->message);
  }

  _error_print(error->next);
}

void memcached_error_print(const memcached_st *self)
{
  if (not self)
    return;

  _error_print(self->error_messages);
}

static void _error_free(memcached_error_t *error)
{
  if (not error)
    return;

  _error_free(error->next);

  if (error && error->root)
  {
    libmemcached_free(error->root, error);
  }
  else if (error)
  {
    free(error);
  }
}

void memcached_error_free(memcached_st *self)
{
  if (not self)
    return;

  _error_free(self->error_messages);
  self->error_messages= NULL;
}

const char *memcached_last_error_message(memcached_st *memc)
{
  if (not memc)
    return memcached_strerror(memc, MEMCACHED_INVALID_ARGUMENTS);

  if (not memc->error_messages)
    return memcached_strerror(memc, MEMCACHED_SUCCESS);

  if (not memc->error_messages->size)
    return memcached_strerror(memc, memc->error_messages->rc);

  return memc->error_messages->message;
}

bool memcached_has_current_error(memcached_st &memc)
{
  if (memc.error_messages
      and memc.error_messages->query_id == memc.query_id
      and memcached_failed(memc.error_messages->rc))
  {
    return true;
  }

  return false;
}

memcached_return_t memcached_last_error(memcached_st *memc)
{
  if (not memc)
    return MEMCACHED_INVALID_ARGUMENTS;

  if (not memc->error_messages)
    return MEMCACHED_SUCCESS;

  return memc->error_messages->rc;
}

int memcached_last_error_errno(memcached_st *memc)
{
  if (not memc)
    return 0;

  if (not memc->error_messages)
    return 0;

  return memc->error_messages->local_errno;
}
<|endoftext|>


<commit_before>#ifndef __penglei_mempool_hpp__
#define __penglei_mempool_hpp__

#include "valvec.hpp"

namespace terark {

/// mempool which alloc mem block identified by
/// integer offset(relative address), not pointers(absolute address)
/// integer offset could be 32bit even in 64bit hardware.
///
/// the returned offset is aligned to align_size, this allows 32bit
/// integer could address up to 4G*align_size memory
///
/// when memory exhausted, valvec can realloc memory without memcpy
/// @see valvec
template<int AlignSize>
class MemPool : private valvec<unsigned char> {
	BOOST_STATIC_ASSERT((AlignSize & (AlignSize-1)) == 0);
    typedef valvec<unsigned char> mem;
    struct link_t { // just for readable
        size_t next;
        explicit link_t(size_t l) : next(l) {}
    };
	struct HugeLink {
		size_t next;
		size_t size;
	};
    link_t* flarr; // free list array
    size_t  fllen; // free list length
	size_t  nFree; // number of free bytes
	size_t  hugelist;

    static const size_t list_tail = ~size_t(0);

	void destroy_and_clean() {
		mem::clear();
		if (flarr) {
			free(flarr);
			flarr = NULL;
		}
		fllen = 0;
		nFree = 0;
		hugelist = list_tail;
	}

public:
	      mem& get_data_byte_vec()       { return *this; }
	const mem& get_data_byte_vec() const { return *this; }

    static size_t align_to(size_t len) {
        return (len + align_size - 1) & ~size_t(align_size - 1);
    }
    enum { align_size = AlignSize };

    explicit MemPool(size_t maxBlockSize) {
        assert(maxBlockSize >= align_size);
        assert(maxBlockSize >= sizeof(HugeLink));
        maxBlockSize = align_to(maxBlockSize);
        fllen = maxBlockSize / align_size;
        flarr = (link_t*)malloc(sizeof(link_t) * fllen);
        if (NULL == flarr) {
            throw std::bad_alloc();
        }
		nFree = 0;
        std::uninitialized_fill_n(flarr, fllen, link_t(list_tail));
		hugelist = list_tail;
    }
    MemPool(const MemPool& y) : mem(y) {
        fllen = y.fllen;
        flarr = (link_t*)malloc(sizeof(link_t) * fllen);
        if (NULL == flarr) {
            throw std::bad_alloc();
        }
		nFree = y.nFree;
        memcpy(flarr, y.flarr, sizeof(link_t) * fllen);
		hugelist = y.hugelist;
    }
    MemPool& operator=(const MemPool& y) {
        if (&y == this)
            return *this;
		destroy_and_clean();
        MemPool(y).swap(*this);
        return *this;
    }
    ~MemPool() {
        if (flarr) {
            free(flarr);
			flarr = NULL;
		}
    }

#ifdef HSM_HAS_MOVE
    MemPool(MemPool&& y) noexcept : mem(y) {
		assert(y.data() == NULL);
		assert(y.size() == 0);
        fllen = y.fllen;
        flarr = y.flarr;
		nFree = y.nFree;
		hugelist = y.hugelist;
		y.fllen = 0;
		y.flarr = NULL;
		y.nFree = 0;
		y.hugelist = list_tail;
    }
    MemPool& operator=(MemPool&& y) noexcept {
        if (&y == this)
            return *this;
        this->~MemPool();
        new(this)MemPool(y);
        return *this;
    }
#endif

	using mem::data;
    using mem::size; // bring to public...
//  using mem::shrink_to_fit;
    using mem::reserve;
    using mem::capacity;

	unsigned char byte_at(size_t pos) const {
		assert(pos < n);
		return p[pos];
	}

	// keep flarr
    void clear() {
		hugelist = list_tail;
		nFree = 0;
        std::uninitialized_fill_n(flarr, fllen, link_t(list_tail));
		mem::clear();
	}

    void erase_all() {
		hugelist = list_tail;
		nFree = 0;
        std::uninitialized_fill_n(flarr, fllen, link_t(list_tail));
		mem::erase_all();
	}

    void resize_no_init(size_t newsize) {
        assert(newsize % align_size == 0);
		assert(newsize >= mem::size());
        mem::resize_no_init(newsize);
    }

	void shrink_to_fit() {
		mem::shrink_to_fit();
	}

    template<class U> const U& at(size_t pos) const {
        assert(pos < n);
    //  assert(pos + sizeof(U) < n);
        return *(U*)(p + pos);
    }
    template<class U> U& at(size_t pos) {
        assert(pos < n);
    //  assert(pos + sizeof(U) < n);
        return *(U*)(p + pos);
    }

    // param request must be aligned by align_size
    size_t alloc(size_t request) {
        assert(request % align_size == 0);
        assert(request > 0);
		request = std::max(sizeof(link_t), request);
        size_t res = list_tail;
        if (request <= fllen * align_size) {
			size_t idx = request / align_size - 1;
			res = flarr[idx].next;
			if (list_tail != res) {
				assert(nFree >= request);
				assert(res + request <= this->n);
				flarr[idx] = at<link_t>(res);
				nFree -= request;
			}
		}
		else { // find in freelist, use first match
			res = hugelist;
			size_t* prev = &hugelist;
			while (list_tail != res) {
			   	HugeLink* h = (HugeLink*)(p + res);
				assert(res + h->size <= this->n);
				if (h->size >= request) {
					size_t remain = h->size - request;
					if (remain) {
						size_t free_pos = res + request;
						if (res + h->size == this->n) {
							// this is the top most block, shrink the heap
							this->n = free_pos;
							*prev = h->next; // remove from hugelist
							nFree -= remain; // not in freelist
						} else if (remain <= fllen * align_size) {
							assert(remain >= sizeof(link_t));
							assert(remain >= align_size);
							size_t idx = remain / align_size - 1;
							at<link_t>(free_pos) = flarr[idx];
							flarr[idx].next = free_pos;
							*prev = h->next; // remove from hugelist
						} else {
							// replace h with h2, the 2nd part of h
							HugeLink* h2 = (HugeLink*)(p + free_pos);
							h2->next = h->next;
							h2->size = remain;
							*prev = free_pos; // replace linked in pointer
						}
					} else {
						*prev = h->next; // remove from hugelist
					}
					nFree -= request;
					break;
				}
				res = h->next;
				prev = &h->next;
			}
		}
		if (list_tail == res) {
			ensure_capacity(n + request);
			res = n;
			n += request;
		}
		return res;
	}

    size_t alloc3(size_t pos, size_t oldlen, size_t newlen) {
        assert(newlen % align_size == 0);
        assert(newlen > 0);
        assert(oldlen % align_size == 0);
        assert(oldlen > 0);
		oldlen = std::max(sizeof(link_t), oldlen);
		newlen = std::max(sizeof(link_t), newlen);
        assert(pos < n);
        assert(pos + oldlen <= n);
        if (pos + oldlen == n) {
            ensure_capacity(pos + newlen);
            n = pos + newlen;
            return pos;
        }
        else if (newlen < oldlen) {
			assert(oldlen - newlen >= sizeof(link_t));
			assert(oldlen - newlen >= align_size);
			sfree(pos + newlen, oldlen - newlen);
			return pos;
		}
		else if (newlen == oldlen) {
			// do nothing
			return pos;
		}
		else {
            size_t newpos = alloc(newlen);
            memcpy(p + newpos, p + pos, std::min(oldlen, newlen));
            sfree(pos, oldlen);
            return newpos;
        }
    }

    void sfree(size_t pos, size_t len) {
        assert(len % align_size == 0);
        assert(len > 0);
        assert(pos < n);
		len = std::max(sizeof(link_t), len);
        assert(pos + len <= n);
        if (pos + len == n) {
            n = pos;
        }
	   	else if (len <= fllen * align_size) {
            size_t idx = len / align_size - 1;
            at<link_t>(pos) = flarr[idx];
            flarr[idx].next = pos;
			nFree += len;
        }
		else {
			HugeLink* h = (HugeLink*)(p + pos);
			h->next = hugelist;
			h->size = len;
			hugelist = pos;
			nFree += len;
		}
    }

	size_t free_size() const { return nFree; }

    void swap(MemPool& y) {
        mem::swap(y);
        std::swap(flarr, y.flarr);
        std::swap(fllen, y.fllen);
		std::swap(nFree, y.nFree);
		std::swap(hugelist, y.hugelist);
    }

	template<class DataIO>
	friend void DataIO_loadObject(DataIO& dio, MemPool& self) {
		typename DataIO::my_var_size_t var;
		self.clear();
		if (self.flarr)
			::free(self.flarr);
		self.flarr = NULL;
		self.fllen = 0;
		self.nFree = 0;
		self.hugelist = list_tail;
		dio >> var;  self.hugelist = var.t;
		dio >> var;  self.nFree = var.t;
		dio >> var;  self.fllen = var.t;
		self.flarr = (link_t*)malloc(sizeof(link_t) * self.fllen);
		if (NULL == self.flarr) {
			self.flarr = NULL;
			self.fllen = 0;
			self.nFree = 0;
			self.hugelist = list_tail;
			throw std::bad_alloc();
		}
		for (size_t i = 0, n = self.fllen; i < n; ++i) {
			dio >> var;
			self.flarr[i].next = var.t;
		}
		dio >> static_cast<mem&>(self);
	}

	template<class DataIO>
	friend void DataIO_saveObject(DataIO& dio, const MemPool& self) {
		typename DataIO::my_var_size_t var;
		dio << typename DataIO::my_var_size_t(self.hugelist);
		dio << typename DataIO::my_var_size_t(self.nFree);
		dio << typename DataIO::my_var_size_t(self.fllen);
		for (size_t i = 0, n = self.fllen; i < n; ++i)
			dio << typename DataIO::my_var_size_t(self.flarr[i].next);
		dio << static_cast<const mem&>(self);
	}
};

} // namespace terark

#endif

<commit_msg>Fix MemPool<4> bug<commit_after>#ifndef __penglei_mempool_hpp__
#define __penglei_mempool_hpp__

#include "valvec.hpp"
#include <boost/integer/static_log2.hpp>

namespace terark {

/// mempool which alloc mem block identified by
/// integer offset(relative address), not pointers(absolute address)
/// integer offset could be 32bit even in 64bit hardware.
///
/// the returned offset is aligned to align_size, this allows 32bit
/// integer could address up to 4G*align_size memory
///
/// when memory exhausted, valvec can realloc memory without memcpy
/// @see valvec
template<int AlignSize>
class MemPool : private valvec<unsigned char> {
	BOOST_STATIC_ASSERT((AlignSize & (AlignSize-1)) == 0);
	BOOST_STATIC_ASSERT(AlignSize >= 4);
    typedef valvec<unsigned char> mem;
    typedef typename boost::mpl::if_c<AlignSize == 4, uint32_t, uint64_t>::type link_size_t;

    static const size_t huge_list_tail = ~size_t(0);
    static const link_size_t free_list_tail = ~link_size_t(0);
    static const size_t offset_shift = AlignSize == 4 ? boost::static_log2<AlignSize>::value : 0;

    struct link_t { // just for readable
        link_size_t next;
        explicit link_t(link_size_t l) : next(l) {}
    };
	struct HugeLink {
		size_t next;
		size_t size;
	};
    link_t* flarr; // free list array
    size_t  fllen; // free list length
	size_t  nFree; // number of free bytes
	size_t  hugelist;

	void destroy_and_clean() {
		mem::clear();
		if (flarr) {
			free(flarr);
			flarr = NULL;
		}
		fllen = 0;
		nFree = 0;
		hugelist = list_tail;
	}

public:
	      mem& get_data_byte_vec()       { return *this; }
	const mem& get_data_byte_vec() const { return *this; }

    static size_t align_to(size_t len) {
        return (len + align_size - 1) & ~size_t(align_size - 1);
    }
    enum { align_size = AlignSize };

    explicit MemPool(size_t maxBlockSize) {
        assert(maxBlockSize >= align_size);
        assert(maxBlockSize >= sizeof(HugeLink));
        maxBlockSize = align_to(maxBlockSize);
        fllen = maxBlockSize / align_size;
        flarr = (link_t*)malloc(sizeof(link_t) * fllen);
        if (NULL == flarr) {
            throw std::bad_alloc();
        }
		nFree = 0;
        std::uninitialized_fill_n(flarr, fllen, link_t(free_list_tail));
		hugelist = huge_list_tail;
    }
    MemPool(const MemPool& y) : mem(y) {
        fllen = y.fllen;
        flarr = (link_t*)malloc(sizeof(link_t) * fllen);
        if (NULL == flarr) {
            throw std::bad_alloc();
        }
		nFree = y.nFree;
        memcpy(flarr, y.flarr, sizeof(link_t) * fllen);
		hugelist = y.hugelist;
    }
    MemPool& operator=(const MemPool& y) {
        if (&y == this)
            return *this;
		destroy_and_clean();
        MemPool(y).swap(*this);
        return *this;
    }
    ~MemPool() {
        if (flarr) {
            free(flarr);
			flarr = NULL;
		}
    }

#ifdef HSM_HAS_MOVE
    MemPool(MemPool&& y) noexcept : mem(y) {
		assert(y.data() == NULL);
		assert(y.size() == 0);
        fllen = y.fllen;
        flarr = y.flarr;
		nFree = y.nFree;
		hugelist = y.hugelist;
		y.fllen = 0;
		y.flarr = NULL;
		y.nFree = 0;
		y.hugelist = list_tail;
    }
    MemPool& operator=(MemPool&& y) noexcept {
        if (&y == this)
            return *this;
        this->~MemPool();
        new(this)MemPool(y);
        return *this;
    }
#endif

	using mem::data;
    using mem::size; // bring to public...
//  using mem::shrink_to_fit;
    using mem::reserve;
    using mem::capacity;

	unsigned char byte_at(size_t pos) const {
		assert(pos < n);
		return p[pos];
	}

	// keep flarr
    void clear() {
		hugelist = huge_list_tail;
		nFree = 0;
        std::uninitialized_fill_n(flarr, fllen, link_t(free_list_tail));
		mem::clear();
	}

    void erase_all() {
		hugelist = huge_list_tail;
		nFree = 0;
        std::uninitialized_fill_n(flarr, fllen, link_t(free_list_tail));
		mem::erase_all();
	}

    void resize_no_init(size_t newsize) {
        assert(newsize % align_size == 0);
		assert(newsize >= mem::size());
        mem::resize_no_init(newsize);
    }

	void shrink_to_fit() {
		mem::shrink_to_fit();
	}

    template<class U> const U& at(size_t pos) const {
        assert((pos << offset_shift) < n);
    //  assert(pos + sizeof(U) < n);
        return *(U*)(p + (pos << offset_shift));
    }
    template<class U> U& at(size_t pos) {
        assert((pos << offset_shift) < n);
    //  assert(pos + sizeof(U) < n);
        return *(U*)(p + (pos << offset_shift));
    }

    // param request must be aligned by align_size
    size_t alloc(size_t request) {
        assert(request % align_size == 0);
        assert(request > 0);
		request = std::max(sizeof(link_t), request);
        size_t res = huge_list_tail;
        if (request <= fllen * align_size) {
			size_t idx = request / align_size - 1;
			if (free_list_tail != flarr[idx].next) {
				assert(nFree >= request);
			    res = size_t(flarr[idx].next) << offset_shift;
				assert(res + request <= this->n);
				flarr[idx] = at<link_t>(flarr[idx].next);
				nFree -= request;
			}
		}
		else { // find in freelist, use first match
			res = hugelist;
			size_t* prev = &hugelist;
			while (huge_list_tail != res) {
			   	HugeLink* h = (HugeLink*)(p + res);
				assert(res + h->size <= this->n);
				if (h->size >= request) {
					size_t remain = h->size - request;
					if (remain) {
						size_t free_pos = res + request;
						if (res + h->size == this->n) {
							// this is the top most block, shrink the heap
							this->n = free_pos;
							*prev = h->next; // remove from hugelist
							nFree -= remain; // not in freelist
						} else if (remain <= fllen * align_size) {
							assert(remain >= sizeof(link_t));
							assert(remain >= align_size);
							size_t idx = remain / align_size - 1;
                            free_pos >>= offset_shift;
							at<link_t>(free_pos) = flarr[idx];
							flarr[idx].next = link_size_t(free_pos);
							*prev = h->next; // remove from hugelist
						} else {
							// replace h with h2, the 2nd part of h
							HugeLink* h2 = (HugeLink*)(p + free_pos);
							h2->next = h->next;
							h2->size = remain;
							*prev = free_pos; // replace linked in pointer
						}
					} else {
						*prev = h->next; // remove from hugelist
					}
					nFree -= request;
					break;
				}
				res = h->next;
				prev = &h->next;
			}
		}
		if (huge_list_tail == res) {
			ensure_capacity(n + request);
			res = n;
			n += request;
		}
		return res >> offset_shift;
	}

    size_t alloc3(size_t pos, size_t oldlen, size_t newlen) {
        assert(newlen % align_size == 0);
        assert(newlen > 0);
        assert(oldlen % align_size == 0);
        assert(oldlen > 0);
        pos <<= offset_shift;
		oldlen = std::max(sizeof(link_t), oldlen);
		newlen = std::max(sizeof(link_t), newlen);
        assert(pos < n);
        assert(pos + oldlen <= n);
        if (pos + oldlen == n) {
            ensure_capacity(pos + newlen);
            n = pos + newlen;
            return pos >> offset_shift;
        }
        else if (newlen < oldlen) {
			assert(oldlen - newlen >= sizeof(link_t));
			assert(oldlen - newlen >= align_size);
			sfree(pos + newlen, oldlen - newlen);
            return pos >> offset_shift;
		}
		else if (newlen == oldlen) {
			// do nothing
            return pos >> offset_shift;
		}
		else {
            size_t newpos = alloc(newlen);
            memcpy(p + newpos, p + pos, std::min(oldlen, newlen));
            sfree(pos, oldlen);
            return newpos >> offset_shift;
        }
    }

    void sfree(size_t pos, size_t len) {
        assert(len % align_size == 0);
        assert(len > 0);
        pos <<= offset_shift;
        assert(pos < n);
		len = std::max(sizeof(link_t), len);
        assert(pos + len <= n);
        if (pos + len == n) {
            n = pos;
        }
	   	else if (len <= fllen * align_size) {
            size_t idx = len / align_size - 1;
            at<link_t>(pos >> offset_shift) = flarr[idx];
            flarr[idx].next = link_size_t(pos >> offset_shift);
			nFree += len;
        }
		else {
			HugeLink* h = (HugeLink*)(p + pos);
			h->next = hugelist;
			h->size = len;
			hugelist = pos;
			nFree += len;
		}
    }

	size_t free_size() const { return nFree; }

    void swap(MemPool& y) {
        mem::swap(y);
        std::swap(flarr, y.flarr);
        std::swap(fllen, y.fllen);
		std::swap(nFree, y.nFree);
		std::swap(hugelist, y.hugelist);
    }

	template<class DataIO>
	friend void DataIO_loadObject(DataIO& dio, MemPool& self) {
		typename DataIO::my_var_size_t var;
		self.clear();
		if (self.flarr)
			::free(self.flarr);
		self.flarr = NULL;
		self.fllen = 0;
		self.nFree = 0;
		self.hugelist = list_tail;
		dio >> var;  self.hugelist = var.t;
		dio >> var;  self.nFree = var.t;
		dio >> var;  self.fllen = var.t;
		self.flarr = (link_t*)malloc(sizeof(link_t) * self.fllen);
		if (NULL == self.flarr) {
			self.flarr = NULL;
			self.fllen = 0;
			self.nFree = 0;
			self.hugelist = list_tail;
			throw std::bad_alloc();
		}
		for (size_t i = 0, n = self.fllen; i < n; ++i) {
			dio >> var;
			self.flarr[i].next = var.t;
		}
		dio >> static_cast<mem&>(self);
	}

	template<class DataIO>
	friend void DataIO_saveObject(DataIO& dio, const MemPool& self) {
		typename DataIO::my_var_size_t var;
		dio << typename DataIO::my_var_size_t(self.hugelist);
		dio << typename DataIO::my_var_size_t(self.nFree);
		dio << typename DataIO::my_var_size_t(self.fllen);
		for (size_t i = 0, n = self.fllen; i < n; ++i)
			dio << typename DataIO::my_var_size_t(self.flarr[i].next);
		dio << static_cast<const mem&>(self);
	}
};

} // namespace terark

#endif

<|endoftext|>