<commit_before>#ifndef COMMON_IMPL_HH
#define COMMON_IMPL_HH

#include <iterator>
#include <algorithm>
#include <ostream>
#include <fstream>
#include <stdexcept>
#include <sys/time.h>

template<typename T>
void
create_binary_tree(NHNode *root, Binary_tree<T> &tree,
                   typename Binary_tree<T>::vid_t ext)
{
    if (root->children == 0)
        return;

    char *left_label = root->children->node->label;
    char *right_label = root->children->next->node->label;
    double left_length = root->children->node->branchLength;
    double right_length = root->children->next->node->branchLength;
    tree.add_children(ext,
                      left_length <= 0 ? T(1) : T(left_length),
                      right_length <= 0 ? T(1) : T(right_length),
                      left_label == 0 ? "" : left_label,
                      right_label == 0 ? "" : right_label);

    create_binary_tree(root->children->node, tree, tree.left(ext));
    create_binary_tree(root->children->next->node, tree, tree.right(ext));
}

template<typename T>
void
create_gene_species_map(const Binary_tree<T> &species_tree,
                        const Binary_tree<T> &gene_tree,
                        std::string map_filename,
                        std::vector<typename Binary_tree<T>::vid_t> &sigma)
{
    using namespace std;
    typedef typename Binary_tree<T>::vid_t vid_t;

    vector<string> map_file_content;

    ifstream map_file(map_filename.c_str());
    copy(istream_iterator<string>(map_file), istream_iterator<string>(),
         back_inserter(map_file_content));

    /* Make sure there are even number of strings in map file. */
    if (map_file_content.size() == 0 || map_file_content.size() % 2 != 0)
        {
            throw logic_error("error reading map file.");
        }

    /* Create a map from gene name to species name. */
    map<string, string> str_sigma;
    for (unsigned i = 0; i < map_file_content.size(); i += 2)
        str_sigma[map_file_content[i]] = map_file_content[i+1];

    /* Create the final map called sigma mapping vid_t to vid_t */
    for (vid_t v = 0; v <= gene_tree.last(); ++v)
        {
            if (!gene_tree.is_leaf(v))
                continue;

            string gene_label = gene_tree.label(v);
            string species_label = str_sigma[gene_label];
            if (species_label == "")
                {
                    string message =
                        "gene label '" + gene_label + "' "
                        "is missing in map file.";
                    throw logic_error(message);
                }
            if (species_tree[species_label] == Binary_tree<T>::NONE)
                {
                    string message =
                        "species label '" + species_label + "' "
                        "which occurs in map file "
                        "does not exist in species tree.";
                    throw logic_error(message);
                }
            sigma[v] = species_tree[species_label];
        }
}

template<typename T>
void
output_subtree(std::ostream &out, const Binary_tree<T> &tree,
               typename Binary_tree<T>::vid_t subtree)
{
    if (tree.is_leaf(subtree))
        {
            out << subtree << "_" << tree.label(subtree);
            return;
        }

    out << "(";
    output_subtree(out, tree, tree.left(subtree));
    out << ",";
    output_subtree(out, tree, tree.right(subtree));
    out << ")" << subtree << "_" << tree.label(subtree);
}

template<typename T>
void
get_postorder_numbering(const Binary_tree<T> &tree,
                        std::vector<typename Binary_tree<T>::vid_t> &numbering)
{
    typedef typename Binary_tree<T>::vid_t vid_t;

    numbering.resize(tree.size());

    unsigned i = 0;
    for (vid_t u = tree.postorder_begin();
         u != tree.NONE;
         u = tree.postorder_next(u))
        {
            numbering[u] = i;

            ++i;
        }
}

template<typename T>
std::ostream &
operator<<(std::ostream &out, const Binary_tree<T> &tree)
{
    output_subtree(out, tree, 0);
    return out;
}

template <class RandomNumberGenerator>
void
init_rand(RandomNumberGenerator &engine)
{
    /*
     * Seed the random number generator.
     */
    timeval tv;
    gettimeofday(&tv, 0);
    unsigned seed = tv.tv_sec * 1000000 + tv.tv_usec;
    engine.seed(seed);
}

template <class RandomNumberGenerator>
void
init_rand(RandomNumberGenerator &engine, unsigned seed)
{
    /*
     * Seed the random number generator.
     */
    engine.seed(seed);
}

template<typename T>
void compute_lambda(const Binary_tree<T> &S,
                    const Binary_tree<T> &G,
                    const std::vector<typename Binary_tree<T>::vid_t> &sigma,
                    const boost::dynamic_bitset<> &transfer_edges,
                    std::vector<typename Binary_tree<T>::vid_t> &lambda)
{
    typedef Binary_tree<T> Tree_type;
    typedef typename Tree_type::vid_t vid_t;

    lambda.resize(G.size());
    for (vid_t u = G.postorder_begin();
         u != Tree_type::NONE;
         u = G.postorder_next(u))
        {
            /* Take care of gene tree leaves and continue. */
            if (G.is_leaf(u))
                {
                    lambda[u] = sigma[u];
                    continue;
                }

            vid_t v = G.left(u);
            vid_t w = G.right(u);

            if (transfer_edges[v])
                {
                    lambda[u] = lambda[w];
                }
            else if (transfer_edges[w])
                {
                    lambda[u] = lambda[v];
                }
            else
                {
                    lambda[u] = S.lca(lambda[w], lambda[v]);
                }
        }
}

template<typename T>
int count_losses(const Binary_tree<T> &S,
                 const Binary_tree<T> &G,
                 const std::vector<typename Binary_tree<T>::vid_t> &sigma,
                 const boost::dynamic_bitset<> &transfer_edges)
{
    typedef Binary_tree<T> Tree_type;
    typedef typename Binary_tree<T>::vid_t vid_t;

    // Compute lambda
    std::vector<vid_t> lambda;
    compute_lambda(S, G, sigma, transfer_edges, lambda);

    // For each non-transfer edge (u, v) in G, count the number of
    // speciations that we pass from lambda(u) to lambda(v).
    int losses = 0;
    for (vid_t u = 1; u < G.size(); ++u)
        {
            vid_t p = G.parent(u);
            if (transfer_edges[u] || lambda[p] == lambda[u])
                continue;

            vid_t x = S.parent(lambda[u]);
            while (x != lambda[p])
                {
                    losses += 1;
                    x = S.parent(x);
                }
        }

    return losses;
}

#endif /* COMMON_IMPL_HH */
<commit_msg>Fixed a bug related to the count of losses: Previously, the implicit loss that occurs when a child of a duplication is placed further below in the species tree was not counted.<commit_after>#ifndef COMMON_IMPL_HH
#define COMMON_IMPL_HH

#include <iterator>
#include <algorithm>
#include <ostream>
#include <fstream>
#include <stdexcept>
#include <sys/time.h>

template<typename T>
void
create_binary_tree(NHNode *root, Binary_tree<T> &tree,
                   typename Binary_tree<T>::vid_t ext)
{
    if (root->children == 0)
        return;

    char *left_label = root->children->node->label;
    char *right_label = root->children->next->node->label;
    double left_length = root->children->node->branchLength;
    double right_length = root->children->next->node->branchLength;
    tree.add_children(ext,
                      left_length <= 0 ? T(1) : T(left_length),
                      right_length <= 0 ? T(1) : T(right_length),
                      left_label == 0 ? "" : left_label,
                      right_label == 0 ? "" : right_label);

    create_binary_tree(root->children->node, tree, tree.left(ext));
    create_binary_tree(root->children->next->node, tree, tree.right(ext));
}

template<typename T>
void
create_gene_species_map(const Binary_tree<T> &species_tree,
                        const Binary_tree<T> &gene_tree,
                        std::string map_filename,
                        std::vector<typename Binary_tree<T>::vid_t> &sigma)
{
    using namespace std;
    typedef typename Binary_tree<T>::vid_t vid_t;

    vector<string> map_file_content;

    ifstream map_file(map_filename.c_str());
    copy(istream_iterator<string>(map_file), istream_iterator<string>(),
         back_inserter(map_file_content));

    /* Make sure there are even number of strings in map file. */
    if (map_file_content.size() == 0 || map_file_content.size() % 2 != 0)
        {
            throw logic_error("error reading map file.");
        }

    /* Create a map from gene name to species name. */
    map<string, string> str_sigma;
    for (unsigned i = 0; i < map_file_content.size(); i += 2)
        str_sigma[map_file_content[i]] = map_file_content[i+1];

    /* Create the final map called sigma mapping vid_t to vid_t */
    for (vid_t v = 0; v <= gene_tree.last(); ++v)
        {
            if (!gene_tree.is_leaf(v))
                continue;

            string gene_label = gene_tree.label(v);
            string species_label = str_sigma[gene_label];
            if (species_label == "")
                {
                    string message =
                        "gene label '" + gene_label + "' "
                        "is missing in map file.";
                    throw logic_error(message);
                }
            if (species_tree[species_label] == Binary_tree<T>::NONE)
                {
                    string message =
                        "species label '" + species_label + "' "
                        "which occurs in map file "
                        "does not exist in species tree.";
                    throw logic_error(message);
                }
            sigma[v] = species_tree[species_label];
        }
}

template<typename T>
void
output_subtree(std::ostream &out, const Binary_tree<T> &tree,
               typename Binary_tree<T>::vid_t subtree)
{
    if (tree.is_leaf(subtree))
        {
            out << subtree << "_" << tree.label(subtree);
            return;
        }

    out << "(";
    output_subtree(out, tree, tree.left(subtree));
    out << ",";
    output_subtree(out, tree, tree.right(subtree));
    out << ")" << subtree << "_" << tree.label(subtree);
}

template<typename T>
void
get_postorder_numbering(const Binary_tree<T> &tree,
                        std::vector<typename Binary_tree<T>::vid_t> &numbering)
{
    typedef typename Binary_tree<T>::vid_t vid_t;

    numbering.resize(tree.size());

    unsigned i = 0;
    for (vid_t u = tree.postorder_begin();
         u != tree.NONE;
         u = tree.postorder_next(u))
        {
            numbering[u] = i;

            ++i;
        }
}

template<typename T>
std::ostream &
operator<<(std::ostream &out, const Binary_tree<T> &tree)
{
    output_subtree(out, tree, 0);
    return out;
}

template <class RandomNumberGenerator>
void
init_rand(RandomNumberGenerator &engine)
{
    /*
     * Seed the random number generator.
     */
    timeval tv;
    gettimeofday(&tv, 0);
    unsigned seed = tv.tv_sec * 1000000 + tv.tv_usec;
    engine.seed(seed);
}

template <class RandomNumberGenerator>
void
init_rand(RandomNumberGenerator &engine, unsigned seed)
{
    /*
     * Seed the random number generator.
     */
    engine.seed(seed);
}

template<typename T>
void compute_lambda(const Binary_tree<T> &S,
                    const Binary_tree<T> &G,
                    const std::vector<typename Binary_tree<T>::vid_t> &sigma,
                    const boost::dynamic_bitset<> &transfer_edges,
                    std::vector<typename Binary_tree<T>::vid_t> &lambda)
{
    typedef Binary_tree<T> Tree_type;
    typedef typename Tree_type::vid_t vid_t;

    lambda.resize(G.size());
    for (vid_t u = G.postorder_begin();
         u != Tree_type::NONE;
         u = G.postorder_next(u))
        {
            /* Take care of gene tree leaves and continue. */
            if (G.is_leaf(u))
                {
                    lambda[u] = sigma[u];
                    continue;
                }

            vid_t v = G.left(u);
            vid_t w = G.right(u);

            if (transfer_edges[v])
                {
                    lambda[u] = lambda[w];
                }
            else if (transfer_edges[w])
                {
                    lambda[u] = lambda[v];
                }
            else
                {
                    lambda[u] = S.lca(lambda[w], lambda[v]);
                }
        }
}

template<typename T>
int count_losses(const Binary_tree<T> &S,
                 const Binary_tree<T> &G,
                 const std::vector<typename Binary_tree<T>::vid_t> &sigma,
                 const boost::dynamic_bitset<> &transfer_edges)
{
    typedef Binary_tree<T> Tree_type;
    typedef typename Binary_tree<T>::vid_t vid_t;

    // Compute lambda
    std::vector<vid_t> lambda;
    compute_lambda(S, G, sigma, transfer_edges, lambda);

    // For each non-transfer edge (u, v) in G, count the number of
    // speciations that we pass from lambda(u) to lambda(v).
    int losses = 0;
    for (vid_t u = 1; u < G.size(); ++u)
        {
            vid_t p = G.parent(u);
            if (transfer_edges[u] || lambda[p] == lambda[u])
                continue;

            vid_t x = S.parent(lambda[u]);

            vid_t u_sibling = G.left(p) == u ? G.right(p) : G.left(p);
            if (x != lambda[p] && lambda[u_sibling] == lambda[p])
                {
                    losses += 1;
                }

            while (x != lambda[p])
                {
                    losses += 1;
                    x = S.parent(x);
                }
        }

    return losses;
}

#endif /* COMMON_IMPL_HH */
<|endoftext|>


<commit_before>// Copyright (C) 2007-2015  CEA/DEN, EDF R&D, OPEN CASCADE
//
// Copyright (C) 2003-2007  OPEN CASCADE, EADS/CCR, LIP6, CEA/DEN,
// CEDRAT, EDF R&D, LEG, PRINCIPIA R&D, BUREAU VERITAS
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Lesser General Public
// License as published by the Free Software Foundation; either
// version 2.1 of the License, or (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
//
// See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com
//

#include "LightApp_ShowHideOp.h"
#include "LightApp_Application.h"
#include "LightApp_Module.h"
#include "LightApp_Study.h"
#include "LightApp_Displayer.h"

#include "LightApp_SelectionMgr.h"
#include "LightApp_Selection.h"

#ifndef DISABLE_SALOMEOBJECT
  #include <SALOME_ListIO.hxx>
#endif

/*!
  Constructor
*/
LightApp_ShowHideOp::LightApp_ShowHideOp( ActionType type )
: LightApp_Operation(),
  myActionType( type )
{
}

/*!
  Destructor
*/
LightApp_ShowHideOp::~LightApp_ShowHideOp()
{
}

/*!
  Makes show/hide operation
*/
void LightApp_ShowHideOp::startOperation()
{
  LightApp_Application* app = dynamic_cast<LightApp_Application*>( application() );
  LightApp_Study* study = app ? dynamic_cast<LightApp_Study*>( app->activeStudy() ) : 0;
  if( !app || !study )
  {
    abort();
    return;
  }

  LightApp_SelectionMgr* mgr = app->selectionMgr();
  LightApp_Module* mod = dynamic_cast<LightApp_Module*>( app->activeModule() );
  if( !mod )
    return;

  LightApp_Selection* sel = mod->createSelection();
  if( !sel )
    return;

  sel->init( "", mgr );
  if( sel->count()==0 && myActionType!=ERASE_ALL )
  {
    abort();
    return;
  }

  if( myActionType==DISPLAY_ONLY || myActionType==ERASE_ALL )
  {
    //ERASE ALL
    QStringList comps;
    study->components( comps );
    QStringList::const_iterator anIt = comps.begin(), aLast = comps.end();
    for( ; anIt!=aLast; anIt++ )
    {
      LightApp_Displayer* disp = LightApp_Displayer::FindDisplayer( app->moduleTitle( *anIt ), false );
      if( disp )
        disp->EraseAll( false, false, 0 );
    }
    if( myActionType==ERASE_ALL )
    {
      // Temporary displayer just to update viewer!
      LightApp_Displayer ld;
      ld.UpdateViewer();
      commit();
      return;
    }
  }

  QString mod_name;
  if( sel->count()>0 )
  {
    QString aStr =  sel->parameter( 0, "displayer" ).toString();
    mod_name = app->moduleTitle( aStr );
  }
  else if( app->activeModule() )
    mod_name = app->moduleTitle( app->activeModule()->name() );

  LightApp_Displayer* d = LightApp_Displayer::FindDisplayer( mod_name, true );
  if( !d )
  {
    abort();
    return;
  }

  QStringList entries;

#ifndef DISABLE_SALOMEOBJECT
  SALOME_ListIO selObjs;
  mgr->selectedObjects( selObjs );
  SALOME_ListIteratorOfListIO anIt( selObjs );
  for( ; anIt.More(); anIt.Next() )
    if( !anIt.Value().IsNull() )
#else
  QStringList selObjs;
  mgr->selectedObjects( selObjs );
  QStringList::const_iterator anIt = selObjs.begin(), aLast = selObjs.end();
  for( ; ; anIt!=aLast )
#endif
    {
      QString entry =
#ifndef DISABLE_SALOMEOBJECT
        anIt.Value()->getEntry();
#else
        *anIt;
#endif

      if( study->isComponent( entry ) )
        study->children( entry, entries );
      else
        entries.append( entry );
    }

  // be sure to use real object entries
  QStringList objEntries;
  QStringList::const_iterator it = entries.begin(), last = entries.end();
  for ( ; it!=last; ++it )
    objEntries.append( study->referencedToEntry( *it ) );

  if( myActionType==DISPLAY || myActionType==DISPLAY_ONLY ) {
    d->Display( objEntries, false, 0 );
    mgr->setSelectedObjects(selObjs);
  }
  else if( myActionType==ERASE ) {
    d->Erase( objEntries, false, false, 0 );
  }

  d->UpdateViewer();
  commit();
}
<commit_msg>"Wait cursor" during object showing<commit_after>// Copyright (C) 2007-2015  CEA/DEN, EDF R&D, OPEN CASCADE
//
// Copyright (C) 2003-2007  OPEN CASCADE, EADS/CCR, LIP6, CEA/DEN,
// CEDRAT, EDF R&D, LEG, PRINCIPIA R&D, BUREAU VERITAS
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Lesser General Public
// License as published by the Free Software Foundation; either
// version 2.1 of the License, or (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
//
// See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com
//

#include "LightApp_ShowHideOp.h"
#include "LightApp_Application.h"
#include "LightApp_Module.h"
#include "LightApp_Study.h"
#include "LightApp_Displayer.h"

#include "LightApp_SelectionMgr.h"
#include "LightApp_Selection.h"

#include "SUIT_OverrideCursor.h"

#ifndef DISABLE_SALOMEOBJECT
  #include <SALOME_ListIO.hxx>
#endif

/*!
  Constructor
*/
LightApp_ShowHideOp::LightApp_ShowHideOp( ActionType type )
: LightApp_Operation(),
  myActionType( type )
{
}

/*!
  Destructor
*/
LightApp_ShowHideOp::~LightApp_ShowHideOp()
{
}

/*!
  Makes show/hide operation
*/
void LightApp_ShowHideOp::startOperation()
{
  LightApp_Application* app = dynamic_cast<LightApp_Application*>( application() );
  LightApp_Study* study = app ? dynamic_cast<LightApp_Study*>( app->activeStudy() ) : 0;
  if( !app || !study )
  {
    abort();
    return;
  }

  LightApp_SelectionMgr* mgr = app->selectionMgr();
  LightApp_Module* mod = dynamic_cast<LightApp_Module*>( app->activeModule() );
  if( !mod )
    return;

  LightApp_Selection* sel = mod->createSelection();
  if( !sel )
    return;

  sel->init( "", mgr );
  if( sel->count()==0 && myActionType!=ERASE_ALL )
  {
    abort();
    return;
  }

  if( myActionType==DISPLAY_ONLY || myActionType==ERASE_ALL )
  {
    //ERASE ALL
    QStringList comps;
    study->components( comps );
    QStringList::const_iterator anIt = comps.begin(), aLast = comps.end();
    for( ; anIt!=aLast; anIt++ )
    {
      LightApp_Displayer* disp = LightApp_Displayer::FindDisplayer( app->moduleTitle( *anIt ), false );
      if( disp )
        disp->EraseAll( false, false, 0 );
    }
    if( myActionType==ERASE_ALL )
    {
      // Temporary displayer just to update viewer!
      LightApp_Displayer ld;
      ld.UpdateViewer();
      commit();
      return;
    }
  }

  QString mod_name;
  if( sel->count()>0 )
  {
    QString aStr =  sel->parameter( 0, "displayer" ).toString();
    mod_name = app->moduleTitle( aStr );
  }
  else if( app->activeModule() )
    mod_name = app->moduleTitle( app->activeModule()->name() );

  LightApp_Displayer* d = LightApp_Displayer::FindDisplayer( mod_name, true );
  if( !d )
  {
    abort();
    return;
  }

  QStringList entries;

#ifndef DISABLE_SALOMEOBJECT
  SALOME_ListIO selObjs;
  mgr->selectedObjects( selObjs );
  SALOME_ListIteratorOfListIO anIt( selObjs );
  for( ; anIt.More(); anIt.Next() )
    if( !anIt.Value().IsNull() )
#else
  QStringList selObjs;
  mgr->selectedObjects( selObjs );
  QStringList::const_iterator anIt = selObjs.begin(), aLast = selObjs.end();
  for( ; ; anIt!=aLast )
#endif
    {
      QString entry =
#ifndef DISABLE_SALOMEOBJECT
        anIt.Value()->getEntry();
#else
        *anIt;
#endif

      if( study->isComponent( entry ) )
        study->children( entry, entries );
      else
        entries.append( entry );
    }

  // be sure to use real object entries
  QStringList objEntries;
  QStringList::const_iterator it = entries.begin(), last = entries.end();
  for ( ; it!=last; ++it )
    objEntries.append( study->referencedToEntry( *it ) );

  if( myActionType==DISPLAY || myActionType==DISPLAY_ONLY ) {
    SUIT_OverrideCursor wc;
    d->Display( objEntries, false, 0 );
    mgr->setSelectedObjects(selObjs);
  }
  else if( myActionType==ERASE ) {
    d->Erase( objEntries, false, false, 0 );
  }

  d->UpdateViewer();
  commit();
}
<|endoftext|>


<commit_before>/**
 * This file is part of the SEEKS project.
 * Copyright (C) 2010 Emmanuel Benazera <ebenazer@seeks-project.info>
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License as
 * published by the Free Software Foundation, either version 3 of the
 * License, or (at your option) any later version.
 *
 * This program 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 Affero General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

#include "mutexes.h"
#include "errlog.h"

#include <stdlib.h>
#include <stdio.h>
#include <string.h>

using sp::errlog;

void mutex_lock(sp_mutex_t *mutex)
{
  int err = pthread_mutex_lock(mutex);
  if (err)
    {
      if (mutex != &errlog::_log_mutex)
        {
          errlog::log_error(LOG_LEVEL_FATAL,
                            "Mutex locking failed: %s.\n", strerror(err));
        }
      exit(1);
    }
}
int mutex_trylock(sp_mutex_t *mutex)
{
  return pthread_mutex_trylock(mutex);
}

void mutex_unlock(sp_mutex_t *mutex)
{
  int err = pthread_mutex_unlock(mutex);
  if (err)
    {
      if (mutex != &errlog::_log_mutex)
        {
          errlog::log_error(LOG_LEVEL_FATAL,
                            "Mutex unlocking failed: %s.\n", strerror(err));
        }
      exit(1);
    }
}

void mutex_init(sp_mutex_t *mutex)
{
  int err = pthread_mutex_init(mutex, 0);
  if (err)
    {
      printf("Fatal error. Mutex initialization failed: %s.\n",
             strerror(err));
      exit(1);
    }
}

void mutex_destroy(sp_mutex_t *mutex)
{
  int err = pthread_mutex_destroy(mutex);
  if (err)
    {
      printf("Fatal error. Mutex destruction failed: %s.\n",
             strerror(err));
      exit(1);
    }
}

void cond_init(sp_cond_t *cond)
{
  int err = pthread_cond_init(cond,0);
  if (err)
    {
      errlog::log_error(LOG_LEVEL_FATAL,
                        "Conditional variable initialization failed: %s.\n",strerror(err));
      exit(1); // in case fatal is turned into non exiting call.
    }
}

void cond_wait(sp_cond_t *cond, sp_mutex_t *mutex)
{
  int err = pthread_cond_wait(cond,mutex);
  if (err)
    {
      errlog::log_error(LOG_LEVEL_FATAL,
                        "Mutex conditional wait failed: %s.\n",strerror(err));
      exit(1); // in case fatal is turned into non exiting call.
    }
}

void cond_signal(sp_cond_t *cond)
{
  int err = pthread_cond_signal(cond);
  if (err)
    {
      errlog::log_error(LOG_LEVEL_FATAL,
                        "Conditional signalling failed: %s.\n",strerror(err));
      exit(1); // in case fatal is turned into non exiting call.
    }
}

void cond_broadcast(sp_cond_t *cond)
{
  int err = pthread_cond_broadcast(cond);
  if (err)
    {
      errlog::log_error(LOG_LEVEL_FATAL,
                        "Conditional broadcasting failed: %s.\n",strerror(err));
      exit(1); // in case fatal is turned into non exiting call.
    }
}
<commit_msg>no error checking on mutex destruction<commit_after>/**
 * This file is part of the SEEKS project.
 * Copyright (C) 2010 Emmanuel Benazera <ebenazer@seeks-project.info>
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License as
 * published by the Free Software Foundation, either version 3 of the
 * License, or (at your option) any later version.
 *
 * This program 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 Affero General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

#include "mutexes.h"
#include "errlog.h"

#include <stdlib.h>
#include <stdio.h>
#include <string.h>

using sp::errlog;

void mutex_lock(sp_mutex_t *mutex)
{
  int err = pthread_mutex_lock(mutex);
  if (err)
    {
      if (mutex != &errlog::_log_mutex)
        {
          errlog::log_error(LOG_LEVEL_FATAL,
                            "Mutex locking failed: %s.\n", strerror(err));
        }
      exit(1);
    }
}
int mutex_trylock(sp_mutex_t *mutex)
{
  return pthread_mutex_trylock(mutex);
}

void mutex_unlock(sp_mutex_t *mutex)
{
  int err = pthread_mutex_unlock(mutex);
  if (err)
    {
      if (mutex != &errlog::_log_mutex)
        {
          errlog::log_error(LOG_LEVEL_FATAL,
                            "Mutex unlocking failed: %s.\n", strerror(err));
        }
      exit(1);
    }
}

void mutex_init(sp_mutex_t *mutex)
{
  int err = pthread_mutex_init(mutex, 0);
  if (err)
    {
      printf("Fatal error. Mutex initialization failed: %s.\n",
             strerror(err));
      exit(1);
    }
}

void mutex_destroy(sp_mutex_t *mutex)
{
  pthread_mutex_destroy(mutex); // we don't track failures.
}

void cond_init(sp_cond_t *cond)
{
  int err = pthread_cond_init(cond,0);
  if (err)
    {
      errlog::log_error(LOG_LEVEL_FATAL,
                        "Conditional variable initialization failed: %s.\n",strerror(err));
      exit(1); // in case fatal is turned into non exiting call.
    }
}

void cond_wait(sp_cond_t *cond, sp_mutex_t *mutex)
{
  int err = pthread_cond_wait(cond,mutex);
  if (err)
    {
      errlog::log_error(LOG_LEVEL_FATAL,
                        "Mutex conditional wait failed: %s.\n",strerror(err));
      exit(1); // in case fatal is turned into non exiting call.
    }
}

void cond_signal(sp_cond_t *cond)
{
  int err = pthread_cond_signal(cond);
  if (err)
    {
      errlog::log_error(LOG_LEVEL_FATAL,
                        "Conditional signalling failed: %s.\n",strerror(err));
      exit(1); // in case fatal is turned into non exiting call.
    }
}

void cond_broadcast(sp_cond_t *cond)
{
  int err = pthread_cond_broadcast(cond);
  if (err)
    {
      errlog::log_error(LOG_LEVEL_FATAL,
                        "Conditional broadcasting failed: %s.\n",strerror(err));
      exit(1); // in case fatal is turned into non exiting call.
    }
}
<|endoftext|>


<commit_before>// Copyright (c) 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
//
// NOTE: These tests are run as part of "unit_tests" (in chrome/test/unit)
// rather than as part of test_shell_tests because they rely on being able
// to instantiate a MessageLoop of type TYPE_IO.  test_shell_tests uses
// TYPE_UI, which URLRequest doesn't allow.
//

#include "webkit/fileapi/file_system_url_request_job.h"

#include <string>

#include "base/bind.h"
#include "base/file_path.h"
#include "base/file_util.h"
#include "base/format_macros.h"
#include "base/memory/weak_ptr.h"
#include "base/message_loop.h"
#include "base/message_loop_proxy.h"
#include "base/platform_file.h"
#include "base/scoped_temp_dir.h"
#include "base/string_piece.h"
#include "base/stringprintf.h"
#include "base/utf_string_conversions.h"
#include "net/base/load_flags.h"
#include "net/base/mime_util.h"
#include "net/base/net_errors.h"
#include "net/base/net_util.h"
#include "net/http/http_request_headers.h"
#include "net/url_request/url_request.h"
#include "net/url_request/url_request_test_util.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "webkit/fileapi/file_system_context.h"
#include "webkit/fileapi/file_system_file_util.h"
#include "webkit/fileapi/file_system_operation_context.h"
#include "webkit/fileapi/mock_file_system_options.h"
#include "webkit/fileapi/sandbox_mount_point_provider.h"
#include "webkit/quota/mock_special_storage_policy.h"

namespace fileapi {
namespace {

// We always use the TEMPORARY FileSystem in this test.
const char kFileSystemURLPrefix[] = "filesystem:http://remote/temporary/";
const char kTestFileData[] = "0123456789";

void FillBuffer(char* buffer, size_t len) {
  static bool called = false;
  if (!called) {
    called = true;
    int seed = static_cast<int>(base::Time::Now().ToInternalValue());
    srand(seed);
  }

  for (size_t i = 0; i < len; i++) {
    buffer[i] = static_cast<char>(rand());
    if (!buffer[i])
      buffer[i] = 'g';
  }
}

}  // namespace

class FileSystemURLRequestJobTest : public testing::Test {
 protected:
  FileSystemURLRequestJobTest()
    : message_loop_(MessageLoop::TYPE_IO),  // simulate an IO thread
      ALLOW_THIS_IN_INITIALIZER_LIST(weak_factory_(this)) {
  }

  virtual void SetUp() {
    ASSERT_TRUE(temp_dir_.CreateUniqueTempDir());

    special_storage_policy_ = new quota::MockSpecialStoragePolicy;
    // We use the main thread so that we can get the root path synchronously.
    // TODO(adamk): Run this on the FILE thread we've created as well.
    file_system_context_ =
        new FileSystemContext(
            base::MessageLoopProxy::current(),
            base::MessageLoopProxy::current(),
            special_storage_policy_, NULL,
            temp_dir_.path(),
            CreateDisallowFileAccessOptions());

    file_system_context_->sandbox_provider()->ValidateFileSystemRoot(
        GURL("http://remote/"), kFileSystemTypeTemporary, true,  // create
        base::Bind(&FileSystemURLRequestJobTest::OnValidateFileSystem,
                   weak_factory_.GetWeakPtr()));
    MessageLoop::current()->RunAllPending();

    net::URLRequest::Deprecated::RegisterProtocolFactory(
        "filesystem", &FileSystemURLRequestJobFactory);
  }

  virtual void TearDown() {
    net::URLRequest::Deprecated::RegisterProtocolFactory("filesystem", NULL);
    ClearUnusedJob();
  }

  void OnValidateFileSystem(base::PlatformFileError result) {
    ASSERT_EQ(base::PLATFORM_FILE_OK, result);
  }

  void TestRequestHelper(const GURL& url,
                         const net::HttpRequestHeaders* headers,
                         bool run_to_completion) {
    delegate_.reset(new TestDelegate());
    // Make delegate_ exit the MessageLoop when the request is done.
    delegate_->set_quit_on_complete(true);
    delegate_->set_quit_on_redirect(true);
    request_.reset(new net::URLRequest(url, delegate_.get()));
    if (headers)
      request_->SetExtraRequestHeaders(*headers);
    job_ = new FileSystemURLRequestJob(
        request_.get(),
        file_system_context_.get(),
        base::MessageLoopProxy::current());

    request_->Start();
    ASSERT_TRUE(request_->is_pending());  // verify that we're starting async
    if (run_to_completion)
      MessageLoop::current()->Run();
  }

  void TestRequest(const GURL& url) {
    TestRequestHelper(url, NULL, true);
  }

  void TestRequestWithHeaders(const GURL& url,
                              const net::HttpRequestHeaders* headers) {
    TestRequestHelper(url, headers, true);
  }

  void TestRequestNoRun(const GURL& url) {
    TestRequestHelper(url, NULL, false);
  }

  void CreateDirectory(const base::StringPiece& dir_name) {
    FileSystemFileUtil* file_util = file_system_context_->
        sandbox_provider()->GetFileUtil();
    FileSystemPath path(GURL("http://remote"),
                        kFileSystemTypeTemporary,
                        FilePath().AppendASCII(dir_name));

    FileSystemOperationContext context(file_system_context_);
    context.set_allowed_bytes_growth(1024);

    ASSERT_EQ(base::PLATFORM_FILE_OK, file_util->CreateDirectory(
        &context,
        path,
        false /* exclusive */,
        false /* recursive */));
  }

  void WriteFile(const base::StringPiece& file_name,
                 const char* buf, int buf_size) {
    FileSystemFileUtil* file_util = file_system_context_->
        sandbox_provider()->GetFileUtil();
    FileSystemPath path(GURL("http://remote"),
                        kFileSystemTypeTemporary,
                        FilePath().AppendASCII(file_name));

    FileSystemOperationContext context(file_system_context_);
    context.set_allowed_bytes_growth(1024);

    base::PlatformFile handle = base::kInvalidPlatformFileValue;
    bool created = false;
    ASSERT_EQ(base::PLATFORM_FILE_OK, file_util->CreateOrOpen(
        &context,
        path,
        base::PLATFORM_FILE_CREATE | base::PLATFORM_FILE_WRITE,
        &handle,
        &created));
    EXPECT_TRUE(created);
    ASSERT_NE(base::kInvalidPlatformFileValue, handle);
    ASSERT_EQ(buf_size,
        base::WritePlatformFile(handle, 0 /* offset */, buf, buf_size));
    base::ClosePlatformFile(handle);
  }

  GURL CreateFileSystemURL(const std::string& path) {
    return GURL(kFileSystemURLPrefix + path);
  }

  static net::URLRequestJob* FileSystemURLRequestJobFactory(
      net::URLRequest* request,
      const std::string& scheme) {
    DCHECK(job_);
    net::URLRequestJob* temp = job_;
    job_ = NULL;
    return temp;
  }

  static void ClearUnusedJob() {
    if (job_) {
      scoped_refptr<net::URLRequestJob> deleter = job_;
      job_ = NULL;
    }
  }

  // Put the message loop at the top, so that it's the last thing deleted.
  MessageLoop message_loop_;

  ScopedTempDir temp_dir_;
  scoped_refptr<quota::MockSpecialStoragePolicy> special_storage_policy_;
  scoped_refptr<FileSystemContext> file_system_context_;
  base::WeakPtrFactory<FileSystemURLRequestJobTest> weak_factory_;

  // NOTE: order matters, request must die before delegate
  scoped_ptr<TestDelegate> delegate_;
  scoped_ptr<net::URLRequest> request_;

  static net::URLRequestJob* job_;
};

// static
net::URLRequestJob* FileSystemURLRequestJobTest::job_ = NULL;

namespace {

TEST_F(FileSystemURLRequestJobTest, FileTest) {
  WriteFile("file1.dat", kTestFileData, arraysize(kTestFileData) - 1);
  TestRequest(CreateFileSystemURL("file1.dat"));

  ASSERT_FALSE(request_->is_pending());
  EXPECT_EQ(1, delegate_->response_started_count());
  EXPECT_FALSE(delegate_->received_data_before_response());
  EXPECT_EQ(kTestFileData, delegate_->data_received());
  EXPECT_EQ(200, request_->GetResponseCode());
  std::string cache_control;
  request_->GetResponseHeaderByName("cache-control", &cache_control);
  EXPECT_EQ("no-cache", cache_control);
}

TEST_F(FileSystemURLRequestJobTest, FileTestFullSpecifiedRange) {
  const size_t buffer_size = 4000;
  scoped_array<char> buffer(new char[buffer_size]);
  FillBuffer(buffer.get(), buffer_size);
  WriteFile("bigfile", buffer.get(), buffer_size);

  const size_t first_byte_position = 500;
  const size_t last_byte_position = buffer_size - first_byte_position;
  std::string partial_buffer_string(buffer.get() + first_byte_position,
                                    buffer.get() + last_byte_position + 1);

  net::HttpRequestHeaders headers;
  headers.SetHeader(net::HttpRequestHeaders::kRange,
                    base::StringPrintf(
                         "bytes=%" PRIuS "-%" PRIuS,
                         first_byte_position, last_byte_position));
  TestRequestWithHeaders(CreateFileSystemURL("bigfile"), &headers);

  ASSERT_FALSE(request_->is_pending());
  EXPECT_EQ(1, delegate_->response_started_count());
  EXPECT_FALSE(delegate_->received_data_before_response());
  EXPECT_TRUE(partial_buffer_string == delegate_->data_received());
}

TEST_F(FileSystemURLRequestJobTest, FileTestHalfSpecifiedRange) {
  const size_t buffer_size = 4000;
  scoped_array<char> buffer(new char[buffer_size]);
  FillBuffer(buffer.get(), buffer_size);
  WriteFile("bigfile", buffer.get(), buffer_size);

  const size_t first_byte_position = 500;
  std::string partial_buffer_string(buffer.get() + first_byte_position,
                                    buffer.get() + buffer_size);

  net::HttpRequestHeaders headers;
  headers.SetHeader(net::HttpRequestHeaders::kRange,
                    base::StringPrintf("bytes=%" PRIuS "-",
                                       first_byte_position));
  TestRequestWithHeaders(CreateFileSystemURL("bigfile"), &headers);
  ASSERT_FALSE(request_->is_pending());
  EXPECT_EQ(1, delegate_->response_started_count());
  EXPECT_FALSE(delegate_->received_data_before_response());
  // Don't use EXPECT_EQ, it will print out a lot of garbage if check failed.
  EXPECT_TRUE(partial_buffer_string == delegate_->data_received());
}

TEST_F(FileSystemURLRequestJobTest, FileTestMultipleRangesNotSupported) {
  WriteFile("file1.dat", kTestFileData, arraysize(kTestFileData) - 1);
  net::HttpRequestHeaders headers;
  headers.SetHeader(net::HttpRequestHeaders::kRange,
                    "bytes=0-5,10-200,200-300");
  TestRequestWithHeaders(CreateFileSystemURL("file1.dat"), &headers);
  EXPECT_TRUE(delegate_->request_failed());
  EXPECT_EQ(net::ERR_REQUEST_RANGE_NOT_SATISFIABLE,
            request_->status().error());
}

TEST_F(FileSystemURLRequestJobTest, RangeOutOfBounds) {
  WriteFile("file1.dat", kTestFileData, arraysize(kTestFileData) - 1);
  net::HttpRequestHeaders headers;
  headers.SetHeader(net::HttpRequestHeaders::kRange, "bytes=500-1000");
  TestRequestWithHeaders(CreateFileSystemURL("file1.dat"), &headers);

  ASSERT_FALSE(request_->is_pending());
  EXPECT_TRUE(delegate_->request_failed());
  EXPECT_EQ(net::ERR_REQUEST_RANGE_NOT_SATISFIABLE,
            request_->status().error());
}

TEST_F(FileSystemURLRequestJobTest, FileDirRedirect) {
  CreateDirectory("dir");
  TestRequest(CreateFileSystemURL("dir"));

  EXPECT_EQ(1, delegate_->received_redirect_count());
  EXPECT_TRUE(request_->status().is_success());
  EXPECT_FALSE(delegate_->request_failed());

  // We've deferred the redirect; now cancel the request to avoid following it.
  request_->Cancel();
  MessageLoop::current()->Run();
}

TEST_F(FileSystemURLRequestJobTest, InvalidURL) {
  TestRequest(GURL("filesystem:/foo/bar/baz"));
  ASSERT_FALSE(request_->is_pending());
  EXPECT_TRUE(delegate_->request_failed());
  EXPECT_EQ(net::ERR_INVALID_URL, request_->status().error());
}

TEST_F(FileSystemURLRequestJobTest, NoSuchRoot) {
  TestRequest(GURL("filesystem:http://remote/persistent/somefile"));
  ASSERT_FALSE(request_->is_pending());
  EXPECT_TRUE(delegate_->request_failed());
  EXPECT_EQ(net::ERR_FILE_NOT_FOUND, request_->status().error());
}

TEST_F(FileSystemURLRequestJobTest, NoSuchFile) {
  TestRequest(CreateFileSystemURL("somefile"));
  ASSERT_FALSE(request_->is_pending());
  EXPECT_TRUE(delegate_->request_failed());
  EXPECT_EQ(net::ERR_FILE_NOT_FOUND, request_->status().error());
}

TEST_F(FileSystemURLRequestJobTest, Cancel) {
  WriteFile("file1.dat", kTestFileData, arraysize(kTestFileData) - 1);
  TestRequestNoRun(CreateFileSystemURL("file1.dat"));

  // Run StartAsync() and only StartAsync().
  MessageLoop::current()->DeleteSoon(FROM_HERE, request_.release());
  MessageLoop::current()->RunAllPending();
  // If we get here, success! we didn't crash!
}

TEST_F(FileSystemURLRequestJobTest, GetMimeType) {
  const char kFilename[] = "hoge.html";

  std::string mime_type_direct;
  FilePath::StringType extension =
      FilePath().AppendASCII(kFilename).Extension();
  if (!extension.empty())
    extension = extension.substr(1);
  EXPECT_TRUE(net::GetWellKnownMimeTypeFromExtension(
      extension, &mime_type_direct));

  TestRequest(CreateFileSystemURL(kFilename));

  std::string mime_type_from_job;
  request_->GetMimeType(&mime_type_from_job);
  EXPECT_EQ(mime_type_direct, mime_type_from_job);
}

}  // namespace
}  // namespace fileapi
<commit_msg>Disable FileSystemURLRequestJobTest.FileTest. It's crashing the Vista Canary bot.<commit_after>// Copyright (c) 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
//
// NOTE: These tests are run as part of "unit_tests" (in chrome/test/unit)
// rather than as part of test_shell_tests because they rely on being able
// to instantiate a MessageLoop of type TYPE_IO.  test_shell_tests uses
// TYPE_UI, which URLRequest doesn't allow.
//

#include "webkit/fileapi/file_system_url_request_job.h"

#include <string>

#include "base/bind.h"
#include "base/file_path.h"
#include "base/file_util.h"
#include "base/format_macros.h"
#include "base/memory/weak_ptr.h"
#include "base/message_loop.h"
#include "base/message_loop_proxy.h"
#include "base/platform_file.h"
#include "base/scoped_temp_dir.h"
#include "base/string_piece.h"
#include "base/stringprintf.h"
#include "base/utf_string_conversions.h"
#include "net/base/load_flags.h"
#include "net/base/mime_util.h"
#include "net/base/net_errors.h"
#include "net/base/net_util.h"
#include "net/http/http_request_headers.h"
#include "net/url_request/url_request.h"
#include "net/url_request/url_request_test_util.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "webkit/fileapi/file_system_context.h"
#include "webkit/fileapi/file_system_file_util.h"
#include "webkit/fileapi/file_system_operation_context.h"
#include "webkit/fileapi/mock_file_system_options.h"
#include "webkit/fileapi/sandbox_mount_point_provider.h"
#include "webkit/quota/mock_special_storage_policy.h"

namespace fileapi {
namespace {

// We always use the TEMPORARY FileSystem in this test.
const char kFileSystemURLPrefix[] = "filesystem:http://remote/temporary/";
const char kTestFileData[] = "0123456789";

void FillBuffer(char* buffer, size_t len) {
  static bool called = false;
  if (!called) {
    called = true;
    int seed = static_cast<int>(base::Time::Now().ToInternalValue());
    srand(seed);
  }

  for (size_t i = 0; i < len; i++) {
    buffer[i] = static_cast<char>(rand());
    if (!buffer[i])
      buffer[i] = 'g';
  }
}

}  // namespace

class FileSystemURLRequestJobTest : public testing::Test {
 protected:
  FileSystemURLRequestJobTest()
    : message_loop_(MessageLoop::TYPE_IO),  // simulate an IO thread
      ALLOW_THIS_IN_INITIALIZER_LIST(weak_factory_(this)) {
  }

  virtual void SetUp() {
    ASSERT_TRUE(temp_dir_.CreateUniqueTempDir());

    special_storage_policy_ = new quota::MockSpecialStoragePolicy;
    // We use the main thread so that we can get the root path synchronously.
    // TODO(adamk): Run this on the FILE thread we've created as well.
    file_system_context_ =
        new FileSystemContext(
            base::MessageLoopProxy::current(),
            base::MessageLoopProxy::current(),
            special_storage_policy_, NULL,
            temp_dir_.path(),
            CreateDisallowFileAccessOptions());

    file_system_context_->sandbox_provider()->ValidateFileSystemRoot(
        GURL("http://remote/"), kFileSystemTypeTemporary, true,  // create
        base::Bind(&FileSystemURLRequestJobTest::OnValidateFileSystem,
                   weak_factory_.GetWeakPtr()));
    MessageLoop::current()->RunAllPending();

    net::URLRequest::Deprecated::RegisterProtocolFactory(
        "filesystem", &FileSystemURLRequestJobFactory);
  }

  virtual void TearDown() {
    net::URLRequest::Deprecated::RegisterProtocolFactory("filesystem", NULL);
    ClearUnusedJob();
  }

  void OnValidateFileSystem(base::PlatformFileError result) {
    ASSERT_EQ(base::PLATFORM_FILE_OK, result);
  }

  void TestRequestHelper(const GURL& url,
                         const net::HttpRequestHeaders* headers,
                         bool run_to_completion) {
    delegate_.reset(new TestDelegate());
    // Make delegate_ exit the MessageLoop when the request is done.
    delegate_->set_quit_on_complete(true);
    delegate_->set_quit_on_redirect(true);
    request_.reset(new net::URLRequest(url, delegate_.get()));
    if (headers)
      request_->SetExtraRequestHeaders(*headers);
    job_ = new FileSystemURLRequestJob(
        request_.get(),
        file_system_context_.get(),
        base::MessageLoopProxy::current());

    request_->Start();
    ASSERT_TRUE(request_->is_pending());  // verify that we're starting async
    if (run_to_completion)
      MessageLoop::current()->Run();
  }

  void TestRequest(const GURL& url) {
    TestRequestHelper(url, NULL, true);
  }

  void TestRequestWithHeaders(const GURL& url,
                              const net::HttpRequestHeaders* headers) {
    TestRequestHelper(url, headers, true);
  }

  void TestRequestNoRun(const GURL& url) {
    TestRequestHelper(url, NULL, false);
  }

  void CreateDirectory(const base::StringPiece& dir_name) {
    FileSystemFileUtil* file_util = file_system_context_->
        sandbox_provider()->GetFileUtil();
    FileSystemPath path(GURL("http://remote"),
                        kFileSystemTypeTemporary,
                        FilePath().AppendASCII(dir_name));

    FileSystemOperationContext context(file_system_context_);
    context.set_allowed_bytes_growth(1024);

    ASSERT_EQ(base::PLATFORM_FILE_OK, file_util->CreateDirectory(
        &context,
        path,
        false /* exclusive */,
        false /* recursive */));
  }

  void WriteFile(const base::StringPiece& file_name,
                 const char* buf, int buf_size) {
    FileSystemFileUtil* file_util = file_system_context_->
        sandbox_provider()->GetFileUtil();
    FileSystemPath path(GURL("http://remote"),
                        kFileSystemTypeTemporary,
                        FilePath().AppendASCII(file_name));

    FileSystemOperationContext context(file_system_context_);
    context.set_allowed_bytes_growth(1024);

    base::PlatformFile handle = base::kInvalidPlatformFileValue;
    bool created = false;
    ASSERT_EQ(base::PLATFORM_FILE_OK, file_util->CreateOrOpen(
        &context,
        path,
        base::PLATFORM_FILE_CREATE | base::PLATFORM_FILE_WRITE,
        &handle,
        &created));
    EXPECT_TRUE(created);
    ASSERT_NE(base::kInvalidPlatformFileValue, handle);
    ASSERT_EQ(buf_size,
        base::WritePlatformFile(handle, 0 /* offset */, buf, buf_size));
    base::ClosePlatformFile(handle);
  }

  GURL CreateFileSystemURL(const std::string& path) {
    return GURL(kFileSystemURLPrefix + path);
  }

  static net::URLRequestJob* FileSystemURLRequestJobFactory(
      net::URLRequest* request,
      const std::string& scheme) {
    DCHECK(job_);
    net::URLRequestJob* temp = job_;
    job_ = NULL;
    return temp;
  }

  static void ClearUnusedJob() {
    if (job_) {
      scoped_refptr<net::URLRequestJob> deleter = job_;
      job_ = NULL;
    }
  }

  // Put the message loop at the top, so that it's the last thing deleted.
  MessageLoop message_loop_;

  ScopedTempDir temp_dir_;
  scoped_refptr<quota::MockSpecialStoragePolicy> special_storage_policy_;
  scoped_refptr<FileSystemContext> file_system_context_;
  base::WeakPtrFactory<FileSystemURLRequestJobTest> weak_factory_;

  // NOTE: order matters, request must die before delegate
  scoped_ptr<TestDelegate> delegate_;
  scoped_ptr<net::URLRequest> request_;

  static net::URLRequestJob* job_;
};

// static
net::URLRequestJob* FileSystemURLRequestJobTest::job_ = NULL;

namespace {

TEST_F(FileSystemURLRequestJobTest, DISABLED_FileTest) {
  WriteFile("file1.dat", kTestFileData, arraysize(kTestFileData) - 1);
  TestRequest(CreateFileSystemURL("file1.dat"));

  ASSERT_FALSE(request_->is_pending());
  EXPECT_EQ(1, delegate_->response_started_count());
  EXPECT_FALSE(delegate_->received_data_before_response());
  EXPECT_EQ(kTestFileData, delegate_->data_received());
  EXPECT_EQ(200, request_->GetResponseCode());
  std::string cache_control;
  request_->GetResponseHeaderByName("cache-control", &cache_control);
  EXPECT_EQ("no-cache", cache_control);
}

TEST_F(FileSystemURLRequestJobTest, FileTestFullSpecifiedRange) {
  const size_t buffer_size = 4000;
  scoped_array<char> buffer(new char[buffer_size]);
  FillBuffer(buffer.get(), buffer_size);
  WriteFile("bigfile", buffer.get(), buffer_size);

  const size_t first_byte_position = 500;
  const size_t last_byte_position = buffer_size - first_byte_position;
  std::string partial_buffer_string(buffer.get() + first_byte_position,
                                    buffer.get() + last_byte_position + 1);

  net::HttpRequestHeaders headers;
  headers.SetHeader(net::HttpRequestHeaders::kRange,
                    base::StringPrintf(
                         "bytes=%" PRIuS "-%" PRIuS,
                         first_byte_position, last_byte_position));
  TestRequestWithHeaders(CreateFileSystemURL("bigfile"), &headers);

  ASSERT_FALSE(request_->is_pending());
  EXPECT_EQ(1, delegate_->response_started_count());
  EXPECT_FALSE(delegate_->received_data_before_response());
  EXPECT_TRUE(partial_buffer_string == delegate_->data_received());
}

TEST_F(FileSystemURLRequestJobTest, FileTestHalfSpecifiedRange) {
  const size_t buffer_size = 4000;
  scoped_array<char> buffer(new char[buffer_size]);
  FillBuffer(buffer.get(), buffer_size);
  WriteFile("bigfile", buffer.get(), buffer_size);

  const size_t first_byte_position = 500;
  std::string partial_buffer_string(buffer.get() + first_byte_position,
                                    buffer.get() + buffer_size);

  net::HttpRequestHeaders headers;
  headers.SetHeader(net::HttpRequestHeaders::kRange,
                    base::StringPrintf("bytes=%" PRIuS "-",
                                       first_byte_position));
  TestRequestWithHeaders(CreateFileSystemURL("bigfile"), &headers);
  ASSERT_FALSE(request_->is_pending());
  EXPECT_EQ(1, delegate_->response_started_count());
  EXPECT_FALSE(delegate_->received_data_before_response());
  // Don't use EXPECT_EQ, it will print out a lot of garbage if check failed.
  EXPECT_TRUE(partial_buffer_string == delegate_->data_received());
}

TEST_F(FileSystemURLRequestJobTest, FileTestMultipleRangesNotSupported) {
  WriteFile("file1.dat", kTestFileData, arraysize(kTestFileData) - 1);
  net::HttpRequestHeaders headers;
  headers.SetHeader(net::HttpRequestHeaders::kRange,
                    "bytes=0-5,10-200,200-300");
  TestRequestWithHeaders(CreateFileSystemURL("file1.dat"), &headers);
  EXPECT_TRUE(delegate_->request_failed());
  EXPECT_EQ(net::ERR_REQUEST_RANGE_NOT_SATISFIABLE,
            request_->status().error());
}

TEST_F(FileSystemURLRequestJobTest, RangeOutOfBounds) {
  WriteFile("file1.dat", kTestFileData, arraysize(kTestFileData) - 1);
  net::HttpRequestHeaders headers;
  headers.SetHeader(net::HttpRequestHeaders::kRange, "bytes=500-1000");
  TestRequestWithHeaders(CreateFileSystemURL("file1.dat"), &headers);

  ASSERT_FALSE(request_->is_pending());
  EXPECT_TRUE(delegate_->request_failed());
  EXPECT_EQ(net::ERR_REQUEST_RANGE_NOT_SATISFIABLE,
            request_->status().error());
}

TEST_F(FileSystemURLRequestJobTest, FileDirRedirect) {
  CreateDirectory("dir");
  TestRequest(CreateFileSystemURL("dir"));

  EXPECT_EQ(1, delegate_->received_redirect_count());
  EXPECT_TRUE(request_->status().is_success());
  EXPECT_FALSE(delegate_->request_failed());

  // We've deferred the redirect; now cancel the request to avoid following it.
  request_->Cancel();
  MessageLoop::current()->Run();
}

TEST_F(FileSystemURLRequestJobTest, InvalidURL) {
  TestRequest(GURL("filesystem:/foo/bar/baz"));
  ASSERT_FALSE(request_->is_pending());
  EXPECT_TRUE(delegate_->request_failed());
  EXPECT_EQ(net::ERR_INVALID_URL, request_->status().error());
}

TEST_F(FileSystemURLRequestJobTest, NoSuchRoot) {
  TestRequest(GURL("filesystem:http://remote/persistent/somefile"));
  ASSERT_FALSE(request_->is_pending());
  EXPECT_TRUE(delegate_->request_failed());
  EXPECT_EQ(net::ERR_FILE_NOT_FOUND, request_->status().error());
}

TEST_F(FileSystemURLRequestJobTest, NoSuchFile) {
  TestRequest(CreateFileSystemURL("somefile"));
  ASSERT_FALSE(request_->is_pending());
  EXPECT_TRUE(delegate_->request_failed());
  EXPECT_EQ(net::ERR_FILE_NOT_FOUND, request_->status().error());
}

TEST_F(FileSystemURLRequestJobTest, Cancel) {
  WriteFile("file1.dat", kTestFileData, arraysize(kTestFileData) - 1);
  TestRequestNoRun(CreateFileSystemURL("file1.dat"));

  // Run StartAsync() and only StartAsync().
  MessageLoop::current()->DeleteSoon(FROM_HERE, request_.release());
  MessageLoop::current()->RunAllPending();
  // If we get here, success! we didn't crash!
}

TEST_F(FileSystemURLRequestJobTest, GetMimeType) {
  const char kFilename[] = "hoge.html";

  std::string mime_type_direct;
  FilePath::StringType extension =
      FilePath().AppendASCII(kFilename).Extension();
  if (!extension.empty())
    extension = extension.substr(1);
  EXPECT_TRUE(net::GetWellKnownMimeTypeFromExtension(
      extension, &mime_type_direct));

  TestRequest(CreateFileSystemURL(kFilename));

  std::string mime_type_from_job;
  request_->GetMimeType(&mime_type_from_job);
  EXPECT_EQ(mime_type_direct, mime_type_from_job);
}

}  // namespace
}  // namespace fileapi
<|endoftext|>


<commit_before>// Copyright (c) 2008 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "SkGraphicsContext.h"

#include "base/gfx/platform_canvas_mac.h"
#include "base/gfx/image_operations.h"
#include "base/gfx/skia_utils_mac.h"
#include "base/logging.h"
#include "GraphicsContextPrivate.h"
#include "SkBitmap.h"
#include "NativeImageSkia.h"
#include "SkiaUtils.h"

// These need to be moved
#include "ThemeData.h"

#define kSkiaOrientation  kHIThemeOrientationNormal

namespace {

// Draws the given bitmap to the given canvas. The subset of the source bitmap
// identified by src_rect is drawn to the given destination rect. The bitmap
// will be resampled to resample_width * resample_height (this is the size of
// the whole image, not the subset). See shouldResampleBitmap for more.
//
// This does a lot of computation to resample only the portion of the bitmap
// that will only be drawn. This is critical for performance since when we are
// scrolling, for example, we are only drawing a small strip of the image.
// Resampling the whole image every time is very slow, so this speeds up things
// dramatically.
void DrawResampledBitmap(SkCanvas& canvas,
                         SkPaint& paint,
                         const NativeImageSkia& bitmap,
                         const SkIRect& src_irect,
                         const SkRect& dest_rect) {
  // First get the subset we need. This is efficient and does not copy pixels.
  SkBitmap subset;
  bitmap.extractSubset(&subset, src_irect);
  SkRect src_rect;
  src_rect.set(src_irect);

  // Whether we're doing a subset or using the full source image.
  bool src_is_full = src_irect.fLeft == 0 && src_irect.fTop == 0 &&
      src_irect.width() == bitmap.width() &&
      src_irect.height() == bitmap.height();

  // We will always draw in integer sizes, so round the destination rect.
  SkIRect dest_rect_rounded;
  dest_rect.round(&dest_rect_rounded);
  SkIRect resized_image_rect;  // Represents the size of the resized image.
  resized_image_rect.set(0, 0,
                         dest_rect_rounded.width(), dest_rect_rounded.height());

  if (src_is_full &&
      bitmap.hasResizedBitmap(dest_rect_rounded.width(),
                              dest_rect_rounded.height())) {
    // Yay, this bitmap frame already has a resized version appropriate for us.
    SkBitmap resampled = bitmap.resizedBitmap(dest_rect_rounded.width(),
                                              dest_rect_rounded.height());
    canvas.drawBitmapRect(resampled, NULL, dest_rect, &paint);
    return;
  }

  // Compute the visible portion of our rect.
  SkRect dest_bitmap_subset_sk;
  ClipRectToCanvas(canvas, dest_rect, &dest_bitmap_subset_sk);
  dest_bitmap_subset_sk.offset(-dest_rect.fLeft, -dest_rect.fTop);

  // The matrix inverting, etc. could have introduced rounding error which
  // causes the bounds to be outside of the resized bitmap. We round outward so
  // we always lean toward it being larger rather than smaller than we need,
  // and then clamp to the bitmap bounds so we don't get any invalid data.
  SkIRect dest_bitmap_subset_sk_i;
  dest_bitmap_subset_sk.roundOut(&dest_bitmap_subset_sk_i);
  if (!dest_bitmap_subset_sk_i.intersect(resized_image_rect))
    return;  // Resized image does not intersect.

  if (src_is_full && bitmap.shouldCacheResampling(
          resized_image_rect.width(),
          resized_image_rect.height(),
          dest_bitmap_subset_sk_i.width(),
          dest_bitmap_subset_sk_i.height())) {
    // We're supposed to resize the entire image and cache it, even though we
    // don't need all of it.
    SkBitmap resampled = bitmap.resizedBitmap(dest_rect_rounded.width(),
                                              dest_rect_rounded.height());
    canvas.drawBitmapRect(resampled, NULL, dest_rect, &paint);
  } else {
    // We should only resize the exposed part of the bitmap to do the minimal
    // possible work.
    gfx::Rect dest_bitmap_subset(dest_bitmap_subset_sk_i.fLeft,
                                 dest_bitmap_subset_sk_i.fTop,
                                 dest_bitmap_subset_sk_i.width(),
                                 dest_bitmap_subset_sk_i.height());

    // Resample the needed part of the image.
    SkBitmap resampled = gfx::ImageOperations::Resize(subset,
        gfx::ImageOperations::RESIZE_LANCZOS3,
        gfx::Size(dest_rect_rounded.width(), dest_rect_rounded.height()),
        dest_bitmap_subset);

    // Compute where the new bitmap should be drawn. Since our new bitmap may be
    // smaller than the original, we have to shift it over by the same amount
    // that we cut off the top and left.
    SkRect offset_dest_rect = {
        dest_bitmap_subset.x() + dest_rect.fLeft,
        dest_bitmap_subset.y() + dest_rect.fTop,
        dest_bitmap_subset.right() + dest_rect.fLeft,
        dest_bitmap_subset.bottom() + dest_rect.fTop };

    canvas.drawBitmapRect(resampled, NULL, offset_dest_rect, &paint);
  }
}

}  // namespace

SkGraphicsContext::SkGraphicsContext(gfx::PlatformCanvas* canvas)
    : canvas_(canvas),
      paint_context_(NULL),
      own_canvas_(false) {
}

SkGraphicsContext::~SkGraphicsContext() {
  if (own_canvas_)
    delete canvas_;
}

const gfx::NativeTheme* SkGraphicsContext::nativeTheme() {
  // TODO(pinkerton): fix me
  return nil;
}

void SkGraphicsContext::paintIcon(CGImageRef icon, const SkIRect& rect) {
  CGContextRef context = canvas_->beginPlatformPaint();
  CGRect r = gfx::SkIRectToCGRect(rect);
  CGContextDrawImage(context, r, icon);
  canvas_->endPlatformPaint();
}

void SkGraphicsContext::paintButton(const SkIRect& widgetRect,
                                    const ThemeData& themeData) {
  CGContextRef context = canvas_->beginPlatformPaint();

  int state = themeData.m_state;
  CGRect rect(gfx::SkIRectToCGRect(widgetRect));
  HIThemeButtonDrawInfo button_draw_info = { 0 };
  button_draw_info.state = state;
  CGRect label_rect;

  HIThemeDrawButton(&rect, &button_draw_info, context,
                    kSkiaOrientation, &label_rect);

  canvas_->endPlatformPaint();
}

void SkGraphicsContext::paintTextField(const SkIRect& widgetRect,
                                       const ThemeData& themeData,
                                       SkColor c,
                                       bool drawEdges) {
  CGContextRef context = canvas_->beginPlatformPaint();

  int state = themeData.m_state;
  CGRect rect(gfx::SkIRectToCGRect(widgetRect));
  CGContextSaveGState(context);
  CGColorRef color = gfx::SkColorToCGColorRef(c);
  CGContextSetFillColorWithColor(context, color);
  CGContextFillRect(context, rect);
  CGContextSetRGBStrokeColor(context, 0.0, 0.0, 0.0, 1.0);  // black border for now
  CGContextStrokeRect(context, rect);
  CGContextRestoreGState(context);
  CGColorRelease(color);
  canvas_->endPlatformPaint();
}

void SkGraphicsContext::paintMenuListArrowButton(const SkIRect& widgetRect,
                                                 unsigned state,
                                                 unsigned classic_state) {
  CGContextRef context = canvas_->beginPlatformPaint();

  CGRect rect(gfx::SkIRectToCGRect(widgetRect));
  HIThemePopupArrowDrawInfo arrow_draw_info = { 0 };
  arrow_draw_info.state = state;
  CGRect label_rect;

  HIThemeDrawPopupArrow(&rect, &arrow_draw_info, context, kSkiaOrientation);

  canvas_->endPlatformPaint();
}

void SkGraphicsContext::paintComplexText(UniscribeStateTextRun& state,
                                         const SkPoint& point,
                                         int from,
                                         int to,
                                         int ascent) {
  SkColor sk_color(paint_context_->fillColor());
  uint8 alpha = SkColorGetA(sk_color);
  // Skip 100% transparent text; no need to draw anything.
  if (!alpha)
    return;

  CGContextRef context = canvas_->beginPlatformPaint();
  CGContextSaveGState(context);
  CGColorRef color = gfx::SkColorToCGColorRef(sk_color);
  CGContextSetFillColorWithColor(context, color);

  //TODO: remove use of UniscribeStateTextRun; show placeholder to test other
  // code for the moment
  CGContextShowTextAtPoint(context, point.fX, point.fY, "complex", 7);
  CGContextRestoreGState(context);
  CGColorRelease(color);
  canvas_->endPlatformPaint();
}

bool SkGraphicsContext::paintText(FontHandle font,
                                  int number_glyph,
                                  const uint16* chars,
                                  const int* advances,
                                  const SkPoint& origin) {
  SkColor sk_color(paint_context_->fillColor());
  uint8 alpha = SkColorGetA(sk_color);
  // Skip 100% transparent text; no need to draw anything.
  if (!alpha)
    return true;

  bool success = false;
  CGContextRef context = canvas_->beginPlatformPaint();
  CGContextSaveGState(context);
  CGColorRef color = gfx::SkColorToCGColorRef(sk_color);
  CGContextSetFillColorWithColor(context, color);
  CGContextMoveToPoint(context, origin.fX, origin.fY);
  CGFontRef cg_font = CTFontCopyGraphicsFont(font, NULL);
  CGContextSetFont(context, cg_font);

  CGSize cg_advances[number_glyph];
  CGGlyph cg_glyphs[number_glyph];

  CTFontGetGlyphsForCharacters(font, chars, cg_glyphs, number_glyph);
  CGContextShowGlyphsAtPoint(context, origin.fX, origin.fY,
                             cg_glyphs, number_glyph);
  CGContextRestoreGState(context);
  CGColorRelease(color);
  CGFontRelease(cg_font);
  canvas_->endPlatformPaint();
  return success;
}

void SkGraphicsContext::paintSkPaint(const SkRect& rect,
                                     const SkPaint& paint) {
  canvas_->drawRect(rect, paint);
}

// Returns smallest multiple of two of the dest size that is more than a small
// multiple larger than src_size.
//
// Used to determine the size that source should be high-quality upsampled to,
// after which we use linear interpolation. Making sure that the linear
// interpolation is a factor of two reduces artifacts, and doing the lowest
// level of resampling
static int GetResamplingThreshold(int src_size, int dest_size) {
  int lower_bound = src_size * 3 / 2;  // Minimum size we'll resample to (1.5x).
  int cur = dest_size;

  // Find the largest multiple of two of the destination size less than our
  // threshold
  while (cur > lower_bound)
    cur /= 2;

  // We want the next size above that, or just the destination size if it's
  // smaller.
  cur *= 2;
  if (cur > dest_size)
    return dest_size;
  return cur;
}

// static
SkGraphicsContext::ResamplingMode SkGraphicsContext::computeResamplingMode(
    const NativeImageSkia& bitmap,
    int src_width, int src_height,
    float dest_width, float dest_height) {
  int dest_iwidth = static_cast<int>(dest_width);
  int dest_iheight = static_cast<int>(dest_height);

  // The percent change below which we will not resample. This usually means
  // an off-by-one error on the web page, and just doing nearest neighbor
  // sampling is usually good enough.
  const float kFractionalChangeThreshold = 0.025f;

  // Images smaller than this in either direction are considered "small" and
  // are not resampled ever (see below).
  const int kSmallImageSizeThreshold = 8;

  // The amount an image can be stretched in a single direction before we
  // say that it is being stretched so much that it must be a line or
  // background that doesn't need resampling.
  const float kLargeStretch = 3.0f;

  // Figure out if we should resample this image. We try to prune out some
  // common cases where resampling won't give us anything, since it is much
  // slower than drawing stretched.
  if (src_width == dest_iwidth && src_height == dest_iheight) {
    // We don't need to resample if the source and destination are the same.
    return RESAMPLE_NONE;
  }

  if (src_width <= kSmallImageSizeThreshold ||
    src_height <= kSmallImageSizeThreshold ||
    dest_width <= kSmallImageSizeThreshold ||
    dest_height <= kSmallImageSizeThreshold) {
    // Never resample small images. These are often used for borders and
    // rules (think 1x1 images used to make lines).
    return RESAMPLE_NONE;
  }

  if (src_height * kLargeStretch <= dest_height ||
    src_width * kLargeStretch <= dest_width) {
    // Large image detected.

    // Don't resample if it is being stretched a lot in only one direction.
    // This is trying to catch cases where somebody has created a border
    // (which might be large) and then is stretching it to fill some part
    // of the page.
    if (src_width == dest_width || src_height == dest_height)
      return RESAMPLE_NONE;

    // The image is growing a lot and in more than one direction. Resampling
    // is slow and doesn't give us very much when growing a lot.
    return RESAMPLE_LINEAR;
  }

  if ((fabs(dest_width - src_width) / src_width <
       kFractionalChangeThreshold) &&
     (fabs(dest_height - src_height) / src_height <
      kFractionalChangeThreshold)) {
    // It is disappointingly common on the web for image sizes to be off by
    // one or two pixels. We don't bother resampling if the size difference
    // is a small fraction of the original size.
    return RESAMPLE_NONE;
  }

  // When the image is not yet done loading, use linear. We don't cache the
  // partially resampled images, and as they come in incrementally, it causes
  // us to have to resample the whole thing every time.
  if (!bitmap.isDataComplete())
    return RESAMPLE_LINEAR;

  // Everything else gets resampled.
  return RESAMPLE_AWESOME;
}

void SkGraphicsContext::paintSkBitmap(const NativeImageSkia& bitmap,
                                      const SkIRect& src_rect,
                                      const SkRect& dest_rect,
                                      const SkPorterDuff::Mode& comp_op) {
  SkPaint paint;
  paint.setPorterDuffXfermode(comp_op);

  ResamplingMode resampling = IsPrinting() ? RESAMPLE_NONE :
      computeResamplingMode(bitmap, src_rect.width(), src_rect.height(),
                            SkScalarToFloat(dest_rect.width()),
                            SkScalarToFloat(dest_rect.height()));
  if (resampling == RESAMPLE_AWESOME) {
    paint.setFilterBitmap(false);
    DrawResampledBitmap(*canvas_, paint, bitmap, src_rect, dest_rect);
  } else {
    // No resampling necessary, we can just draw the bitmap.
    // Note: for serialization, we will want to subset the bitmap first so
    // we don't send extra pixels.
    paint.setFilterBitmap(resampling == RESAMPLE_LINEAR);
    canvas_->drawBitmapRect(bitmap, &src_rect, dest_rect, &paint);
  }
}

void SkGraphicsContext::setDashPathEffect(SkDashPathEffect *dash) {
  paint_context_->setDashPathEffect(dash);
}

void SkGraphicsContext::setGradient(SkShader *gradient) {
  paint_context_->setGradient(gradient);
}

void SkGraphicsContext::setPattern(SkShader *pattern) {
  paint_context_->setPattern(pattern);
}

const SkBitmap* SkGraphicsContext::bitmap() const {
  return &canvas_->getDevice()->accessBitmap(false);
}

gfx::PlatformCanvas* SkGraphicsContext::canvas() const {
  return canvas_;
}

bool SkGraphicsContext::IsPrinting() {
  return canvas_->getTopPlatformDevice().IsVectorial();
}
<commit_msg>Update include headers for SkGraphicsContextMac.cpp<commit_after>// Copyright (c) 2008 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "config.h"
#include "SkGraphicsContext.h"

#include "base/gfx/platform_canvas_mac.h"
#include "base/gfx/image_operations.h"
#include "base/gfx/skia_utils_mac.h"
#include "base/logging.h"
#include "GraphicsContextPlatformPrivate.h"
#include "SkBitmap.h"
#include "NativeImageSkia.h"
#include "SkiaUtils.h"

// These need to be moved
#include "ThemeData.h"

#define kSkiaOrientation  kHIThemeOrientationNormal

namespace {

// Draws the given bitmap to the given canvas. The subset of the source bitmap
// identified by src_rect is drawn to the given destination rect. The bitmap
// will be resampled to resample_width * resample_height (this is the size of
// the whole image, not the subset). See shouldResampleBitmap for more.
//
// This does a lot of computation to resample only the portion of the bitmap
// that will only be drawn. This is critical for performance since when we are
// scrolling, for example, we are only drawing a small strip of the image.
// Resampling the whole image every time is very slow, so this speeds up things
// dramatically.
void DrawResampledBitmap(SkCanvas& canvas,
                         SkPaint& paint,
                         const NativeImageSkia& bitmap,
                         const SkIRect& src_irect,
                         const SkRect& dest_rect) {
  // First get the subset we need. This is efficient and does not copy pixels.
  SkBitmap subset;
  bitmap.extractSubset(&subset, src_irect);
  SkRect src_rect;
  src_rect.set(src_irect);

  // Whether we're doing a subset or using the full source image.
  bool src_is_full = src_irect.fLeft == 0 && src_irect.fTop == 0 &&
      src_irect.width() == bitmap.width() &&
      src_irect.height() == bitmap.height();

  // We will always draw in integer sizes, so round the destination rect.
  SkIRect dest_rect_rounded;
  dest_rect.round(&dest_rect_rounded);
  SkIRect resized_image_rect;  // Represents the size of the resized image.
  resized_image_rect.set(0, 0,
                         dest_rect_rounded.width(), dest_rect_rounded.height());

  if (src_is_full &&
      bitmap.hasResizedBitmap(dest_rect_rounded.width(),
                              dest_rect_rounded.height())) {
    // Yay, this bitmap frame already has a resized version appropriate for us.
    SkBitmap resampled = bitmap.resizedBitmap(dest_rect_rounded.width(),
                                              dest_rect_rounded.height());
    canvas.drawBitmapRect(resampled, NULL, dest_rect, &paint);
    return;
  }

  // Compute the visible portion of our rect.
  SkRect dest_bitmap_subset_sk;
  ClipRectToCanvas(canvas, dest_rect, &dest_bitmap_subset_sk);
  dest_bitmap_subset_sk.offset(-dest_rect.fLeft, -dest_rect.fTop);

  // The matrix inverting, etc. could have introduced rounding error which
  // causes the bounds to be outside of the resized bitmap. We round outward so
  // we always lean toward it being larger rather than smaller than we need,
  // and then clamp to the bitmap bounds so we don't get any invalid data.
  SkIRect dest_bitmap_subset_sk_i;
  dest_bitmap_subset_sk.roundOut(&dest_bitmap_subset_sk_i);
  if (!dest_bitmap_subset_sk_i.intersect(resized_image_rect))
    return;  // Resized image does not intersect.

  if (src_is_full && bitmap.shouldCacheResampling(
          resized_image_rect.width(),
          resized_image_rect.height(),
          dest_bitmap_subset_sk_i.width(),
          dest_bitmap_subset_sk_i.height())) {
    // We're supposed to resize the entire image and cache it, even though we
    // don't need all of it.
    SkBitmap resampled = bitmap.resizedBitmap(dest_rect_rounded.width(),
                                              dest_rect_rounded.height());
    canvas.drawBitmapRect(resampled, NULL, dest_rect, &paint);
  } else {
    // We should only resize the exposed part of the bitmap to do the minimal
    // possible work.
    gfx::Rect dest_bitmap_subset(dest_bitmap_subset_sk_i.fLeft,
                                 dest_bitmap_subset_sk_i.fTop,
                                 dest_bitmap_subset_sk_i.width(),
                                 dest_bitmap_subset_sk_i.height());

    // Resample the needed part of the image.
    SkBitmap resampled = gfx::ImageOperations::Resize(subset,
        gfx::ImageOperations::RESIZE_LANCZOS3,
        gfx::Size(dest_rect_rounded.width(), dest_rect_rounded.height()),
        dest_bitmap_subset);

    // Compute where the new bitmap should be drawn. Since our new bitmap may be
    // smaller than the original, we have to shift it over by the same amount
    // that we cut off the top and left.
    SkRect offset_dest_rect = {
        dest_bitmap_subset.x() + dest_rect.fLeft,
        dest_bitmap_subset.y() + dest_rect.fTop,
        dest_bitmap_subset.right() + dest_rect.fLeft,
        dest_bitmap_subset.bottom() + dest_rect.fTop };

    canvas.drawBitmapRect(resampled, NULL, offset_dest_rect, &paint);
  }
}

}  // namespace

SkGraphicsContext::SkGraphicsContext(gfx::PlatformCanvas* canvas)
    : canvas_(canvas),
      paint_context_(NULL),
      own_canvas_(false) {
}

SkGraphicsContext::~SkGraphicsContext() {
  if (own_canvas_)
    delete canvas_;
}

const gfx::NativeTheme* SkGraphicsContext::nativeTheme() {
  // TODO(pinkerton): fix me
  return nil;
}

void SkGraphicsContext::paintIcon(CGImageRef icon, const SkIRect& rect) {
  CGContextRef context = canvas_->beginPlatformPaint();
  CGRect r = gfx::SkIRectToCGRect(rect);
  CGContextDrawImage(context, r, icon);
  canvas_->endPlatformPaint();
}

void SkGraphicsContext::paintButton(const SkIRect& widgetRect,
                                    const ThemeData& themeData) {
  CGContextRef context = canvas_->beginPlatformPaint();

  int state = themeData.m_state;
  CGRect rect(gfx::SkIRectToCGRect(widgetRect));
  HIThemeButtonDrawInfo button_draw_info = { 0 };
  button_draw_info.state = state;
  CGRect label_rect;

  HIThemeDrawButton(&rect, &button_draw_info, context,
                    kSkiaOrientation, &label_rect);

  canvas_->endPlatformPaint();
}

void SkGraphicsContext::paintTextField(const SkIRect& widgetRect,
                                       const ThemeData& themeData,
                                       SkColor c,
                                       bool drawEdges) {
  CGContextRef context = canvas_->beginPlatformPaint();

  int state = themeData.m_state;
  CGRect rect(gfx::SkIRectToCGRect(widgetRect));
  CGContextSaveGState(context);
  CGColorRef color = gfx::SkColorToCGColorRef(c);
  CGContextSetFillColorWithColor(context, color);
  CGContextFillRect(context, rect);
  CGContextSetRGBStrokeColor(context, 0.0, 0.0, 0.0, 1.0);  // black border for now
  CGContextStrokeRect(context, rect);
  CGContextRestoreGState(context);
  CGColorRelease(color);
  canvas_->endPlatformPaint();
}

void SkGraphicsContext::paintMenuListArrowButton(const SkIRect& widgetRect,
                                                 unsigned state,
                                                 unsigned classic_state) {
  CGContextRef context = canvas_->beginPlatformPaint();

  CGRect rect(gfx::SkIRectToCGRect(widgetRect));
  HIThemePopupArrowDrawInfo arrow_draw_info = { 0 };
  arrow_draw_info.state = state;
  CGRect label_rect;

  HIThemeDrawPopupArrow(&rect, &arrow_draw_info, context, kSkiaOrientation);

  canvas_->endPlatformPaint();
}

void SkGraphicsContext::paintComplexText(UniscribeStateTextRun& state,
                                         const SkPoint& point,
                                         int from,
                                         int to,
                                         int ascent) {
  SkColor sk_color(paint_context_->fillColor());
  uint8 alpha = SkColorGetA(sk_color);
  // Skip 100% transparent text; no need to draw anything.
  if (!alpha)
    return;

  CGContextRef context = canvas_->beginPlatformPaint();
  CGContextSaveGState(context);
  CGColorRef color = gfx::SkColorToCGColorRef(sk_color);
  CGContextSetFillColorWithColor(context, color);

  //TODO: remove use of UniscribeStateTextRun; show placeholder to test other
  // code for the moment
  CGContextShowTextAtPoint(context, point.fX, point.fY, "complex", 7);
  CGContextRestoreGState(context);
  CGColorRelease(color);
  canvas_->endPlatformPaint();
}

bool SkGraphicsContext::paintText(FontHandle font,
                                  int number_glyph,
                                  const uint16* chars,
                                  const int* advances,
                                  const SkPoint& origin) {
  SkColor sk_color(paint_context_->fillColor());
  uint8 alpha = SkColorGetA(sk_color);
  // Skip 100% transparent text; no need to draw anything.
  if (!alpha)
    return true;

  bool success = false;
  CGContextRef context = canvas_->beginPlatformPaint();
  CGContextSaveGState(context);
  CGColorRef color = gfx::SkColorToCGColorRef(sk_color);
  CGContextSetFillColorWithColor(context, color);
  CGContextMoveToPoint(context, origin.fX, origin.fY);
  CGFontRef cg_font = CTFontCopyGraphicsFont(font, NULL);
  CGContextSetFont(context, cg_font);

  CGSize cg_advances[number_glyph];
  CGGlyph cg_glyphs[number_glyph];

  CTFontGetGlyphsForCharacters(font, chars, cg_glyphs, number_glyph);
  CGContextShowGlyphsAtPoint(context, origin.fX, origin.fY,
                             cg_glyphs, number_glyph);
  CGContextRestoreGState(context);
  CGColorRelease(color);
  CGFontRelease(cg_font);
  canvas_->endPlatformPaint();
  return success;
}

void SkGraphicsContext::paintSkPaint(const SkRect& rect,
                                     const SkPaint& paint) {
  canvas_->drawRect(rect, paint);
}

// Returns smallest multiple of two of the dest size that is more than a small
// multiple larger than src_size.
//
// Used to determine the size that source should be high-quality upsampled to,
// after which we use linear interpolation. Making sure that the linear
// interpolation is a factor of two reduces artifacts, and doing the lowest
// level of resampling
static int GetResamplingThreshold(int src_size, int dest_size) {
  int lower_bound = src_size * 3 / 2;  // Minimum size we'll resample to (1.5x).
  int cur = dest_size;

  // Find the largest multiple of two of the destination size less than our
  // threshold
  while (cur > lower_bound)
    cur /= 2;

  // We want the next size above that, or just the destination size if it's
  // smaller.
  cur *= 2;
  if (cur > dest_size)
    return dest_size;
  return cur;
}

// static
SkGraphicsContext::ResamplingMode SkGraphicsContext::computeResamplingMode(
    const NativeImageSkia& bitmap,
    int src_width, int src_height,
    float dest_width, float dest_height) {
  int dest_iwidth = static_cast<int>(dest_width);
  int dest_iheight = static_cast<int>(dest_height);

  // The percent change below which we will not resample. This usually means
  // an off-by-one error on the web page, and just doing nearest neighbor
  // sampling is usually good enough.
  const float kFractionalChangeThreshold = 0.025f;

  // Images smaller than this in either direction are considered "small" and
  // are not resampled ever (see below).
  const int kSmallImageSizeThreshold = 8;

  // The amount an image can be stretched in a single direction before we
  // say that it is being stretched so much that it must be a line or
  // background that doesn't need resampling.
  const float kLargeStretch = 3.0f;

  // Figure out if we should resample this image. We try to prune out some
  // common cases where resampling won't give us anything, since it is much
  // slower than drawing stretched.
  if (src_width == dest_iwidth && src_height == dest_iheight) {
    // We don't need to resample if the source and destination are the same.
    return RESAMPLE_NONE;
  }

  if (src_width <= kSmallImageSizeThreshold ||
    src_height <= kSmallImageSizeThreshold ||
    dest_width <= kSmallImageSizeThreshold ||
    dest_height <= kSmallImageSizeThreshold) {
    // Never resample small images. These are often used for borders and
    // rules (think 1x1 images used to make lines).
    return RESAMPLE_NONE;
  }

  if (src_height * kLargeStretch <= dest_height ||
    src_width * kLargeStretch <= dest_width) {
    // Large image detected.

    // Don't resample if it is being stretched a lot in only one direction.
    // This is trying to catch cases where somebody has created a border
    // (which might be large) and then is stretching it to fill some part
    // of the page.
    if (src_width == dest_width || src_height == dest_height)
      return RESAMPLE_NONE;

    // The image is growing a lot and in more than one direction. Resampling
    // is slow and doesn't give us very much when growing a lot.
    return RESAMPLE_LINEAR;
  }

  if ((fabs(dest_width - src_width) / src_width <
       kFractionalChangeThreshold) &&
     (fabs(dest_height - src_height) / src_height <
      kFractionalChangeThreshold)) {
    // It is disappointingly common on the web for image sizes to be off by
    // one or two pixels. We don't bother resampling if the size difference
    // is a small fraction of the original size.
    return RESAMPLE_NONE;
  }

  // When the image is not yet done loading, use linear. We don't cache the
  // partially resampled images, and as they come in incrementally, it causes
  // us to have to resample the whole thing every time.
  if (!bitmap.isDataComplete())
    return RESAMPLE_LINEAR;

  // Everything else gets resampled.
  return RESAMPLE_AWESOME;
}

void SkGraphicsContext::paintSkBitmap(const NativeImageSkia& bitmap,
                                      const SkIRect& src_rect,
                                      const SkRect& dest_rect,
                                      const SkPorterDuff::Mode& comp_op) {
  SkPaint paint;
  paint.setPorterDuffXfermode(comp_op);

  ResamplingMode resampling = IsPrinting() ? RESAMPLE_NONE :
      computeResamplingMode(bitmap, src_rect.width(), src_rect.height(),
                            SkScalarToFloat(dest_rect.width()),
                            SkScalarToFloat(dest_rect.height()));
  if (resampling == RESAMPLE_AWESOME) {
    paint.setFilterBitmap(false);
    DrawResampledBitmap(*canvas_, paint, bitmap, src_rect, dest_rect);
  } else {
    // No resampling necessary, we can just draw the bitmap.
    // Note: for serialization, we will want to subset the bitmap first so
    // we don't send extra pixels.
    paint.setFilterBitmap(resampling == RESAMPLE_LINEAR);
    canvas_->drawBitmapRect(bitmap, &src_rect, dest_rect, &paint);
  }
}

void SkGraphicsContext::setDashPathEffect(SkDashPathEffect *dash) {
  paint_context_->setDashPathEffect(dash);
}

void SkGraphicsContext::setGradient(SkShader *gradient) {
  paint_context_->setGradient(gradient);
}

void SkGraphicsContext::setPattern(SkShader *pattern) {
  paint_context_->setPattern(pattern);
}

const SkBitmap* SkGraphicsContext::bitmap() const {
  return &canvas_->getDevice()->accessBitmap(false);
}

gfx::PlatformCanvas* SkGraphicsContext::canvas() const {
  return canvas_;
}

bool SkGraphicsContext::IsPrinting() {
  return canvas_->getTopPlatformDevice().IsVectorial();
}
<|endoftext|>


<commit_before>
#include "ATDev.h"

ATDev::ATDev()
{
    m_hwSerial      = NULL;

    memset(m_endBuffer, 0x00, ATDEV_BUFF_END_SIZE + 1);
    memset(m_cmdBuffer, 0x00, ATDEV_BUFF_CMD_SIZE + 1);
    memset(m_msgBuffer, 0x00, ATDEV_BUFF_MSG_SIZE + 1);

    m_readPtr       ^= m_readPtr;
    m_timeOut       ^= m_timeOut;
}

void ATDev::flushInput()
{
    // UART initialize?
    if (m_hwSerial == NULL) {
        return;
    }

    // Clear input Serial buffer
    while (m_hwSerial->available() > 0) {
        while (m_hwSerial->read() >= 0);
        delay(ATDEV_FLUSH);
    }
}

uint8_t ATDev::sendATCmd(bool abruptEnd, bool streamBuf, char* readBuf, uint16_t readBufSize)
{
    uint32_t    isTimeOut;
    uint32_t    startTime;
    uint8_t     endSize;
    bool        over;

    // UART initialize?
    if (m_hwSerial == NULL) {
        return ATDEV_ERR_INITIALIZE;
    }

    ////
    // init buffers
    memset(readBuf, 0x00, readBufSize);

    // init Counter
    m_readPtr ^= m_readPtr;

    // is it the default AT end or had his own end?
    // set default end of AT communication
    if (!abruptEnd) {
        strncpy_P(m_endBuffer, ATDEV_END_OK, ATDEV_BUFF_END_SIZE);
    }
    endSize = strlen(m_endBuffer);

    ////
    // Send AT command
    if (m_cmdBuffer[0] != 0x00) {

        // Clear input Serial buffer
        this->flushInput();

        // send command
        m_hwSerial->println(m_cmdBuffer);

        // clean comand buffer for next use
        memset(m_cmdBuffer, 0x00, ATDEV_BUFF_CMD_SIZE);

        ////
        // wait until all data are send
        m_hwSerial->flush();
    }

    ////
    // Calc Timeout
    startTime = millis();
    isTimeOut = startTime + m_timeOut;

    // overloaded
    if (isTimeOut < startTime) {
        over = true;
    }
    else {
        over = false;
    }

    // reset timeout for next function
    m_timeOut = ATDEV_DEFAULT_TIMEOUT;

    ////
    // process answer
    do {

        // if data in serial input buffer
        while (m_hwSerial->available()) {

            // buffer is full (not circle)
            if (!streamBuf && m_readPtr >= readBufSize) {
                memset(m_endBuffer, 0x00, ATDEV_BUFF_END_SIZE +1);
                return ATDEV_ERR_BUFFER_FULL;
            }

            ////
            // Circle buffer / Line end
            if (streamBuf) {
                // Buffer full or Line end detect
                if (m_readPtr >= readBufSize || (m_readPtr > 0 && readBuf[m_readPtr -1] == ATDEV_CH_LF)) {
                    memset(readBuf, 0x00, readBufSize);
                    m_readPtr ^= m_readPtr;
                }
            }

            // read into buffer
            readBuf[m_readPtr++] = m_hwSerial->read();

            ////
            // if abrupt end of communication is set
            if (abruptEnd && m_readPtr >= endSize &&
                    strstr(&readBuf[m_readPtr - endSize], m_endBuffer) != 0) {
                memset(m_endBuffer, 0x00, ATDEV_BUFF_END_SIZE +1);
                return ATDEV_OK;
            }
        }

        ////
        // check is it the end of AT Command in answer buffer
        if (m_readPtr >= endSize && strstr(readBuf, m_endBuffer) != 0) {
            memset(m_endBuffer, 0x00, ATDEV_BUFF_END_SIZE +1);
            return ATDEV_OK;
        }
        // Error
        else if (m_readPtr >= ATDEV_END_ERROR_SIZE &&
                strstr_P(readBuf, ATDEV_END_ERROR) != 0) {
            memset(m_endBuffer, 0x00, ATDEV_BUFF_END_SIZE +1);
            return ATDEV_ERR_ERROR_RECEIVED;
        }

        // calc diff timeout
        if (over) {
            if (startTime > millis()) {
                over = false;
            }
        }

    } while ((isTimeOut > millis() && !over) || over); // timeout

    memset(m_endBuffer, 0x00, ATDEV_BUFF_END_SIZE +1);
    return ATDEV_ERR_TIMEOUT;
}

uint8_t ATDev::readLine(char* readBuf, uint16_t readBufSize)
{
    // CR LF to end buffer
    strncpy_P(m_endBuffer, ATDEV_END_LINE, ATDEV_BUFF_END_SIZE);

    return this->sendATCmdAbrupt(readBuf, readBufSize);
}

uint8_t ATDev::parseATCmdData(char* readBuf, uint16_t readBufSize)
{
    bool    isString    = false;
    uint8_t params      = 0;

    // search hole string
    for (uint16_t i = 0; i < readBufSize; i++) {

        // end
        if (readBuf[i] == 0x00) {
            return params;
        }
        // is string "xy zyx"
        else if (readBuf[i] == ATDEV_CH_IC) {
            readBuf[i] = 0x00;
            isString = !isString;
        }
        // ' ' or ',' or ':' replace with '\0'
        else if (!isString && (readBuf[i] == ATDEV_CH_SP ||
                readBuf[i] == ATDEV_CH_CO ||
                readBuf[i] == ATDEV_CH_DD)) {
            readBuf[i] = 0x00;
        }

        // count
        if (i > 0 && readBuf[i] == 0x00 && readBuf[i-1] != 0x00) {
            params++;
        }
    }

    return params;
}

char* ATDev::getParseElement(uint8_t indx, char* readBuf, uint16_t readBufSize)
{
    uint8_t count = 0;

    // search hole string
    for (uint16_t i = 0; i < readBufSize; i++) {

        // find next position
        if (readBuf[i] == 0x00 && i > 0 && readBuf[i-1] != 0x00) {
            count++;
        }

        // found indx with next character
        if (count == indx && readBuf[i] != 0x00) {
            return &readBuf[i];
        }
    }

    return NULL;
}

uint8_t ATDev::waitDevice(uint8_t ret)
{
    if (ret == ATDEV_OK) {
        delay(ATDEV_WAIT);
    }

    return ret;
}

void ATDev::trimATEnd(char* readBuf, uint16_t readBufSize, uint16_t dataSize)
{
    bool        foundAT = false;
    uint16_t    pos     = dataSize -2;

    // secure check
    if (pos < 0) {
        return;
    }

    // Search OK AT Answer String
    for ( ; pos > 0; --pos) {
        if (strstr_P(readBuf + pos, ATDEV_END_OK) != NULL) {
            --pos;
            foundAT = true;
            break;
        }
    }

    // NO AT CMD found!
    if (!foundAT) {
        return;
    }

    // find next character they not for AT communication
    for ( ; pos > 0; --pos) {
        if (readBuf[pos] != ATDEV_CH_CR && readBuf[pos] != ATDEV_CH_LF) {
            break;
        }
    }

    // clean AT communication controll characters
    memset(readBuf + pos +1, 0x00, readBufSize - pos -1);
}

uint8_t ATDev::isReady()
{
    strncpy_P(m_cmdBuffer, ATDEV_CMD_AT, ATDEV_BUFF_CMD_SIZE);

    return this->sendATCmd();
}

// vim: set sts=4 sw=4 ts=4 et:
<commit_msg>use macro for better readable code<commit_after>
#include "ATDev.h"

// return value for function sendATCmd
#define SENDATCMD_END(X) \
    memset(m_endBuffer, 0x00, ATDEV_BUFF_END_SIZE +1); \
    return X;

ATDev::ATDev()
{
    m_hwSerial      = NULL;

    memset(m_endBuffer, 0x00, ATDEV_BUFF_END_SIZE + 1);
    memset(m_cmdBuffer, 0x00, ATDEV_BUFF_CMD_SIZE + 1);
    memset(m_msgBuffer, 0x00, ATDEV_BUFF_MSG_SIZE + 1);

    m_readPtr       ^= m_readPtr;
    m_timeOut       ^= m_timeOut;
}

void ATDev::flushInput()
{
    // UART initialize?
    if (m_hwSerial == NULL) {
        return;
    }

    // Clear input Serial buffer
    while (m_hwSerial->available() > 0) {
        while (m_hwSerial->read() >= 0);
        delay(ATDEV_FLUSH);
    }
}

uint8_t ATDev::sendATCmd(bool abruptEnd, bool streamBuf, char* readBuf, uint16_t readBufSize)
{
    uint32_t    isTimeOut;
    uint32_t    startTime;
    uint8_t     endSize;
    bool        over;

    // UART initialize?
    if (m_hwSerial == NULL) {
        SENDATCMD_END(ATDEV_ERR_INITIALIZE);
    }

    ////
    // init buffers
    memset(readBuf, 0x00, readBufSize);

    // init Counter
    m_readPtr ^= m_readPtr;

    // is it the default AT end or had his own end?
    // set default end of AT communication
    if (!abruptEnd) {
        strncpy_P(m_endBuffer, ATDEV_END_OK, ATDEV_BUFF_END_SIZE);
    }
    endSize = strlen(m_endBuffer);

    ////
    // Send AT command
    if (m_cmdBuffer[0] != 0x00) {

        // Clear input Serial buffer
        this->flushInput();

        // send command
        m_hwSerial->println(m_cmdBuffer);

        // clean comand buffer for next use
        memset(m_cmdBuffer, 0x00, ATDEV_BUFF_CMD_SIZE);

        ////
        // wait until all data are send
        m_hwSerial->flush();
    }

    ////
    // Calc Timeout
    startTime = millis();
    isTimeOut = startTime + m_timeOut;

    // overloaded
    if (isTimeOut < startTime) {
        over = true;
    }
    else {
        over = false;
    }

    // reset timeout for next function
    m_timeOut = ATDEV_DEFAULT_TIMEOUT;

    ////
    // process answer
    do {

        // if data in serial input buffer
        while (m_hwSerial->available()) {

            // buffer is full (not circle)
            if (!streamBuf && m_readPtr >= readBufSize) {
                SENDATCMD_END(ATDEV_ERR_BUFFER_FULL);
            }

            ////
            // Circle buffer / Line end
            if (streamBuf) {
                // Buffer full or Line end detect
                if (m_readPtr >= readBufSize || (m_readPtr > 0 && readBuf[m_readPtr -1] == ATDEV_CH_LF)) {
                    memset(readBuf, 0x00, readBufSize);
                    m_readPtr ^= m_readPtr;
                }
            }

            // read into buffer
            readBuf[m_readPtr++] = m_hwSerial->read();

            ////
            // if abrupt end of communication is set
            if (abruptEnd && m_readPtr >= endSize &&
                    strstr(&readBuf[m_readPtr - endSize], m_endBuffer) != 0) {
                SENDATCMD_END(ATDEV_OK);
            }
        }

        ////
        // check is it the end of AT Command in answer buffer
        if (m_readPtr >= endSize && strstr(readBuf, m_endBuffer) != 0) {
            SENDATCMD_END(ATDEV_OK);
        }
        // Error
        else if (m_readPtr >= ATDEV_END_ERROR_SIZE &&
                strstr_P(readBuf, ATDEV_END_ERROR) != 0) {
            SENDATCMD_END(ATDEV_ERR_ERROR_RECEIVED);
        }

        // calc diff timeout
        if (over) {
            if (startTime > millis()) {
                over = false;
            }
        }

    } while ((isTimeOut > millis() && !over) || over); // timeout

    SENDATCMD_END(ATDEV_ERR_TIMEOUT);
}

uint8_t ATDev::readLine(char* readBuf, uint16_t readBufSize)
{
    // CR LF to end buffer
    strncpy_P(m_endBuffer, ATDEV_END_LINE, ATDEV_BUFF_END_SIZE);

    return this->sendATCmdAbrupt(readBuf, readBufSize);
}

uint8_t ATDev::parseATCmdData(char* readBuf, uint16_t readBufSize)
{
    bool    isString    = false;
    uint8_t params      = 0;

    // search hole string
    for (uint16_t i = 0; i < readBufSize; i++) {

        // end
        if (readBuf[i] == 0x00) {
            return params;
        }
        // is string "xy zyx"
        else if (readBuf[i] == ATDEV_CH_IC) {
            readBuf[i] = 0x00;
            isString = !isString;
        }
        // ' ' or ',' or ':' replace with '\0'
        else if (!isString && (readBuf[i] == ATDEV_CH_SP ||
                readBuf[i] == ATDEV_CH_CO ||
                readBuf[i] == ATDEV_CH_DD)) {
            readBuf[i] = 0x00;
        }

        // count
        if (i > 0 && readBuf[i] == 0x00 && readBuf[i-1] != 0x00) {
            params++;
        }
    }

    return params;
}

char* ATDev::getParseElement(uint8_t indx, char* readBuf, uint16_t readBufSize)
{
    uint8_t count = 0;

    // search hole string
    for (uint16_t i = 0; i < readBufSize; i++) {

        // find next position
        if (readBuf[i] == 0x00 && i > 0 && readBuf[i-1] != 0x00) {
            count++;
        }

        // found indx with next character
        if (count == indx && readBuf[i] != 0x00) {
            return &readBuf[i];
        }
    }

    return NULL;
}

uint8_t ATDev::waitDevice(uint8_t ret)
{
    if (ret == ATDEV_OK) {
        delay(ATDEV_WAIT);
    }

    return ret;
}

void ATDev::trimATEnd(char* readBuf, uint16_t readBufSize, uint16_t dataSize)
{
    bool        foundAT = false;
    uint16_t    pos     = dataSize -2;

    // secure check
    if (pos < 0) {
        return;
    }

    // Search OK AT Answer String
    for ( ; pos > 0; --pos) {
        if (strstr_P(readBuf + pos, ATDEV_END_OK) != NULL) {
            --pos;
            foundAT = true;
            break;
        }
    }

    // NO AT CMD found!
    if (!foundAT) {
        return;
    }

    // find next character they not for AT communication
    for ( ; pos > 0; --pos) {
        if (readBuf[pos] != ATDEV_CH_CR && readBuf[pos] != ATDEV_CH_LF) {
            break;
        }
    }

    // clean AT communication controll characters
    memset(readBuf + pos +1, 0x00, readBufSize - pos -1);
}

uint8_t ATDev::isReady()
{
    strncpy_P(m_cmdBuffer, ATDEV_CMD_AT, ATDEV_BUFF_CMD_SIZE);

    return this->sendATCmd();
}

// vim: set sts=4 sw=4 ts=4 et:
<|endoftext|>