<commit_before>/*!
 * \file glyph_cache.cpp
 * \brief file glyph_cache.cpp
 *
 * Copyright 2016 by Intel.
 *
 * Contact: kevin.rogovin@intel.com
 *
 * This Source Code Form is subject to the
 * terms of the Mozilla Public License, v. 2.0.
 * If a copy of the MPL was not distributed with
 * this file, You can obtain one at
 * http://mozilla.org/MPL/2.0/.
 *
 * \author Kevin Rogovin <kevin.rogovin@intel.com>
 *
 */

#include <map>
#include <vector>
#include <fastuidraw/text/glyph_cache.hpp>
#include <fastuidraw/text/glyph_render_data.hpp>
#include "../private/util_private.hpp"

namespace
{

  class GlyphCachePrivate;

  class GlyphDataPrivate
  {
  public:
    GlyphDataPrivate(GlyphCachePrivate *c, unsigned int I):
      m_cache(c),
      m_cache_location(I),
      m_geometry_offset(-1),
      m_geometry_length(0),
      m_uploaded_to_atlas(false),
      m_glyph_data(NULL)
    {}

    void
    clear(void);

    enum fastuidraw::return_code
    upload_to_atlas(void);

    /* owner
     */
    GlyphCachePrivate *m_cache;

    /* location into m_cache->m_glyphs
     */
    unsigned int m_cache_location;

    /* layout magicks
     */
    fastuidraw::GlyphLayoutData m_layout;
    fastuidraw::GlyphRender m_render;

    /* Location in atlas
     */
    fastuidraw::vecN<fastuidraw::GlyphLocation, 2> m_atlas_location;
    int m_geometry_offset, m_geometry_length;
    bool m_uploaded_to_atlas;

    /* data to generate glyph data
     */
    fastuidraw::GlyphRenderData *m_glyph_data;
  };

  class GlyphSource
  {
  public:
    GlyphSource(void):
      m_glyph_code(0)
    {}

    GlyphSource(fastuidraw::FontBase::const_handle f,
                uint32_t gc, fastuidraw::GlyphRender r):
      m_font(f),
      m_glyph_code(gc),
      m_render(r)
    {}

    bool
    operator<(const GlyphSource &rhs) const
    {
      return (m_font != rhs.m_font) ? m_font < rhs.m_font :
        (m_glyph_code != rhs.m_glyph_code) ? m_glyph_code < rhs.m_glyph_code :
        m_render < rhs.m_render;
    }

    fastuidraw::FontBase::const_handle m_font;
    uint32_t m_glyph_code;
    fastuidraw::GlyphRender m_render;
  };

  class GlyphCachePrivate
  {
  public:
    explicit
    GlyphCachePrivate(fastuidraw::GlyphAtlas::handle patlas, fastuidraw::GlyphCache *p);

    ~GlyphCachePrivate();

    /*  When the atlas is full, we will clear the atlas, but save
        the values in m_glyphs but mark them as not having been
        uploaded, this way returned values are safe and we do
        not have to regenerate data either.
     */

    GlyphDataPrivate*
    fetch_or_allocate_glyph(GlyphSource src);

    fastuidraw::GlyphAtlas::handle m_atlas;
    std::map<GlyphSource, GlyphDataPrivate*> m_glyph_map;
    std::vector<GlyphDataPrivate*> m_glyphs;
    std::vector<unsigned int> m_free_slots;
    fastuidraw::GlyphCache *m_p;
  };
}

/////////////////////////////////////////////////////////
// GlyphDataPrivate methods
void
GlyphDataPrivate::
clear(void)
{
  m_render = fastuidraw::GlyphRender();
  assert(!m_render.valid());

  if(m_atlas_location[0].valid())
    {
      m_cache->m_atlas->deallocate(m_atlas_location[0]);
      m_atlas_location[0] = fastuidraw::GlyphLocation();
    }

  if(m_atlas_location[1].valid())
    {
      m_cache->m_atlas->deallocate(m_atlas_location[1]);
      m_atlas_location[1] = fastuidraw::GlyphLocation();
    }

  if(m_geometry_offset != -1)
    {
      m_cache->m_atlas->deallocate_geometry_data(m_geometry_offset, m_geometry_length);
      m_geometry_offset = -1;
      m_geometry_length = 0;
    }

  m_uploaded_to_atlas = false;
  if(m_glyph_data)
    {
      FASTUIDRAWdelete(m_glyph_data);
      m_glyph_data = NULL;
    }
}

enum fastuidraw::return_code
GlyphDataPrivate::
upload_to_atlas(void)
{
  /* TODO:
     1. this method is not thread safe if different threads
        attempt to access the same glyph (or if different
        threads call this routine and clear_atlas()
        at the same time).

     2. this routine is hideous to read due to the handling
        of allocation failures. Should likely bunch the
        allocation behind objects whose ctors and dtors
        do the right thing.
   */
  enum fastuidraw::return_code return_value;

  if(m_uploaded_to_atlas)
    {
      return fastuidraw::routine_success;
    }

  assert(m_glyph_data);
  return_value = m_glyph_data->upload_to_atlas(m_cache->m_atlas,
                                               m_atlas_location[0],
                                               m_atlas_location[1],
                                               m_geometry_offset,
                                               m_geometry_length);
  if(return_value == fastuidraw::routine_success)
    {
      m_uploaded_to_atlas = true;
    }

  return return_value;
}

/////////////////////////////////////////////////
// GlyphCachePrivate methods
GlyphCachePrivate::
GlyphCachePrivate(fastuidraw::GlyphAtlas::handle patlas,
                  fastuidraw::GlyphCache *p):
  m_atlas(patlas),
  m_p(p)
{}

GlyphCachePrivate::
~GlyphCachePrivate()
{
  for(unsigned int i = 0, endi = m_glyphs.size(); i < endi; ++i)
    {
      m_glyphs[i]->clear();
      FASTUIDRAWdelete(m_glyphs[i]);
    }
}

GlyphDataPrivate*
GlyphCachePrivate::
fetch_or_allocate_glyph(GlyphSource src)
{
  std::map<GlyphSource, GlyphDataPrivate*>::iterator iter;
  GlyphDataPrivate *G;

  iter = m_glyph_map.find(src);
  if(iter != m_glyph_map.end())
    {
      return iter->second;
    }

  if(m_free_slots.empty())
    {
      G = FASTUIDRAWnew GlyphDataPrivate(this, m_glyphs.size());
      m_glyphs.push_back(G);
      assert(!G->m_render.valid());
    }
  else
    {
      unsigned int v(m_free_slots.back());
      m_free_slots.pop_back();
      G = m_glyphs[v];
      assert(!G->m_render.valid());
    }
  m_glyph_map[src] = G;
  return G;
}

///////////////////////////////////////////////////////
// fastuidraw::Glyph methods
enum fastuidraw::glyph_type
fastuidraw::Glyph::
type(void) const
{
  GlyphDataPrivate *p;
  p = reinterpret_cast<GlyphDataPrivate*>(m_opaque);
  assert(p != NULL);
  return p->m_render.m_type;
}

const fastuidraw::GlyphLayoutData&
fastuidraw::Glyph::
layout(void) const
{
  GlyphDataPrivate *p;
  p = reinterpret_cast<GlyphDataPrivate*>(m_opaque);
  assert(p != NULL && p->m_render.valid());
  return p->m_layout;
}

fastuidraw::reference_counted_ptr<fastuidraw::GlyphCache>
fastuidraw::Glyph::
cache(void) const
{
  GlyphDataPrivate *p;
  p = reinterpret_cast<GlyphDataPrivate*>(m_opaque);
  assert(p != NULL && p->m_render.valid());
  return p->m_cache->m_p;
}

unsigned int
fastuidraw::Glyph::
cache_location(void) const
{
  GlyphDataPrivate *p;
  p = reinterpret_cast<GlyphDataPrivate*>(m_opaque);
  assert(p != NULL && p->m_render.valid());
  return p->m_cache_location;
}

fastuidraw::GlyphLocation
fastuidraw::Glyph::
atlas_location(void) const
{
  GlyphDataPrivate *p;
  p = reinterpret_cast<GlyphDataPrivate*>(m_opaque);
  assert(p != NULL && p->m_render.valid());
  return p->m_atlas_location[0];
}

fastuidraw::GlyphLocation
fastuidraw::Glyph::
secondary_atlas_location(void) const
{
  GlyphDataPrivate *p;
  p = reinterpret_cast<GlyphDataPrivate*>(m_opaque);
  assert(p != NULL && p->m_render.valid());
  return p->m_atlas_location[1];
}

int
fastuidraw::Glyph::
geometry_offset(void) const
{
  GlyphDataPrivate *p;
  p = reinterpret_cast<GlyphDataPrivate*>(m_opaque);
  assert(p != NULL && p->m_render.valid());
  return p->m_geometry_offset;
}

enum fastuidraw::return_code
fastuidraw::Glyph::
upload_to_atlas(void) const
{
  GlyphDataPrivate *p;
  p = reinterpret_cast<GlyphDataPrivate*>(m_opaque);
  assert(p != NULL && p->m_render.valid());
  return p->upload_to_atlas();
}

//////////////////////////////////////////////////////////
// fastuidraw::GlyphCache methods
fastuidraw::GlyphCache::
GlyphCache(GlyphAtlas::handle patlas)
{
  m_d = FASTUIDRAWnew GlyphCachePrivate(patlas, this);
}

fastuidraw::GlyphCache::
~GlyphCache()
{
  GlyphCachePrivate *d;
  d = reinterpret_cast<GlyphCachePrivate*>(m_d);
  FASTUIDRAWdelete(d);
  m_d = NULL;
}

fastuidraw::Glyph
fastuidraw::GlyphCache::
fetch_glyph(GlyphRender render, FontBase::const_handle font, uint32_t glyph_code)
{
  if(!font || !font->can_create_rendering_data(render.m_type))
    {
      return Glyph();
    }

  GlyphCachePrivate *d;
  d = reinterpret_cast<GlyphCachePrivate*>(m_d);

  GlyphDataPrivate *q;
  GlyphSource src(font, glyph_code, render);

  q = d->fetch_or_allocate_glyph(src);

  if(!q->m_render.valid())
    {
      q->m_render = render;
      assert(!q->m_glyph_data);
      q->m_glyph_data = font->compute_rendering_data(q->m_render, glyph_code, q->m_layout);
    }

  return Glyph(q);
}

void
fastuidraw::GlyphCache::
delete_glyph(Glyph G)
{
  GlyphCachePrivate *d;
  d = reinterpret_cast<GlyphCachePrivate*>(m_d);

  GlyphDataPrivate *p;
  p = reinterpret_cast<GlyphDataPrivate*>(G.m_opaque);
  assert(p != NULL);
  assert(p->m_cache == d);
  assert(p->m_render.valid());

  GlyphSource src(p->m_layout.m_font, p->m_layout.m_glyph_code, p->m_render);
  d->m_glyph_map.erase(src);
  p->clear();
  d->m_free_slots.push_back(p->m_cache_location);
}

void
fastuidraw::GlyphCache::
clear_atlas(void)
{
  GlyphCachePrivate *d;
  d = reinterpret_cast<GlyphCachePrivate*>(m_d);

  d->m_atlas->clear();
  for(unsigned int i = 0, endi = d->m_glyphs.size(); i < endi; ++i)
    {
      d->m_glyphs[i]->m_uploaded_to_atlas = false;
      d->m_glyphs[i]->m_atlas_location[0] = fastuidraw::GlyphLocation();
      d->m_glyphs[i]->m_atlas_location[1] = fastuidraw::GlyphLocation();
      d->m_glyphs[i]->m_geometry_offset = -1;
      d->m_glyphs[i]->m_geometry_length = 0;
    }
}

void
fastuidraw::GlyphCache::
clear_cache(void)
{
  GlyphCachePrivate *d;
  d = reinterpret_cast<GlyphCachePrivate*>(m_d);

  clear_atlas();
  d->m_glyphs.clear();
  d->m_free_slots.clear();
}
<commit_msg>fastuidraw/text: fix to GlyphCache::clear_cache() implementation<commit_after>/*!
 * \file glyph_cache.cpp
 * \brief file glyph_cache.cpp
 *
 * Copyright 2016 by Intel.
 *
 * Contact: kevin.rogovin@intel.com
 *
 * This Source Code Form is subject to the
 * terms of the Mozilla Public License, v. 2.0.
 * If a copy of the MPL was not distributed with
 * this file, You can obtain one at
 * http://mozilla.org/MPL/2.0/.
 *
 * \author Kevin Rogovin <kevin.rogovin@intel.com>
 *
 */

#include <map>
#include <vector>
#include <fastuidraw/text/glyph_cache.hpp>
#include <fastuidraw/text/glyph_render_data.hpp>
#include "../private/util_private.hpp"

namespace
{

  class GlyphCachePrivate;

  class GlyphDataPrivate
  {
  public:
    GlyphDataPrivate(GlyphCachePrivate *c, unsigned int I):
      m_cache(c),
      m_cache_location(I),
      m_geometry_offset(-1),
      m_geometry_length(0),
      m_uploaded_to_atlas(false),
      m_glyph_data(NULL)
    {}

    void
    clear(void);

    enum fastuidraw::return_code
    upload_to_atlas(void);

    /* owner
     */
    GlyphCachePrivate *m_cache;

    /* location into m_cache->m_glyphs
     */
    unsigned int m_cache_location;

    /* layout magicks
     */
    fastuidraw::GlyphLayoutData m_layout;
    fastuidraw::GlyphRender m_render;

    /* Location in atlas
     */
    fastuidraw::vecN<fastuidraw::GlyphLocation, 2> m_atlas_location;
    int m_geometry_offset, m_geometry_length;
    bool m_uploaded_to_atlas;

    /* data to generate glyph data
     */
    fastuidraw::GlyphRenderData *m_glyph_data;
  };

  class GlyphSource
  {
  public:
    GlyphSource(void):
      m_glyph_code(0)
    {}

    GlyphSource(fastuidraw::FontBase::const_handle f,
                uint32_t gc, fastuidraw::GlyphRender r):
      m_font(f),
      m_glyph_code(gc),
      m_render(r)
    {}

    bool
    operator<(const GlyphSource &rhs) const
    {
      return (m_font != rhs.m_font) ? m_font < rhs.m_font :
        (m_glyph_code != rhs.m_glyph_code) ? m_glyph_code < rhs.m_glyph_code :
        m_render < rhs.m_render;
    }

    fastuidraw::FontBase::const_handle m_font;
    uint32_t m_glyph_code;
    fastuidraw::GlyphRender m_render;
  };

  class GlyphCachePrivate
  {
  public:
    explicit
    GlyphCachePrivate(fastuidraw::GlyphAtlas::handle patlas, fastuidraw::GlyphCache *p);

    ~GlyphCachePrivate();

    /*  When the atlas is full, we will clear the atlas, but save
        the values in m_glyphs but mark them as not having been
        uploaded, this way returned values are safe and we do
        not have to regenerate data either.
     */

    GlyphDataPrivate*
    fetch_or_allocate_glyph(GlyphSource src);

    fastuidraw::GlyphAtlas::handle m_atlas;
    std::map<GlyphSource, GlyphDataPrivate*> m_glyph_map;
    std::vector<GlyphDataPrivate*> m_glyphs;
    std::vector<unsigned int> m_free_slots;
    fastuidraw::GlyphCache *m_p;
  };
}

/////////////////////////////////////////////////////////
// GlyphDataPrivate methods
void
GlyphDataPrivate::
clear(void)
{
  m_render = fastuidraw::GlyphRender();
  assert(!m_render.valid());

  if(m_atlas_location[0].valid())
    {
      m_cache->m_atlas->deallocate(m_atlas_location[0]);
      m_atlas_location[0] = fastuidraw::GlyphLocation();
    }

  if(m_atlas_location[1].valid())
    {
      m_cache->m_atlas->deallocate(m_atlas_location[1]);
      m_atlas_location[1] = fastuidraw::GlyphLocation();
    }

  if(m_geometry_offset != -1)
    {
      m_cache->m_atlas->deallocate_geometry_data(m_geometry_offset, m_geometry_length);
      m_geometry_offset = -1;
      m_geometry_length = 0;
    }

  m_uploaded_to_atlas = false;
  if(m_glyph_data)
    {
      FASTUIDRAWdelete(m_glyph_data);
      m_glyph_data = NULL;
    }
}

enum fastuidraw::return_code
GlyphDataPrivate::
upload_to_atlas(void)
{
  /* TODO:
     1. this method is not thread safe if different threads
        attempt to access the same glyph (or if different
        threads call this routine and clear_atlas()
        at the same time).

     2. this routine is hideous to read due to the handling
        of allocation failures. Should likely bunch the
        allocation behind objects whose ctors and dtors
        do the right thing.
   */
  enum fastuidraw::return_code return_value;

  if(m_uploaded_to_atlas)
    {
      return fastuidraw::routine_success;
    }

  assert(m_glyph_data);
  return_value = m_glyph_data->upload_to_atlas(m_cache->m_atlas,
                                               m_atlas_location[0],
                                               m_atlas_location[1],
                                               m_geometry_offset,
                                               m_geometry_length);
  if(return_value == fastuidraw::routine_success)
    {
      m_uploaded_to_atlas = true;
    }

  return return_value;
}

/////////////////////////////////////////////////
// GlyphCachePrivate methods
GlyphCachePrivate::
GlyphCachePrivate(fastuidraw::GlyphAtlas::handle patlas,
                  fastuidraw::GlyphCache *p):
  m_atlas(patlas),
  m_p(p)
{}

GlyphCachePrivate::
~GlyphCachePrivate()
{
  for(unsigned int i = 0, endi = m_glyphs.size(); i < endi; ++i)
    {
      m_glyphs[i]->clear();
      FASTUIDRAWdelete(m_glyphs[i]);
    }
}

GlyphDataPrivate*
GlyphCachePrivate::
fetch_or_allocate_glyph(GlyphSource src)
{
  std::map<GlyphSource, GlyphDataPrivate*>::iterator iter;
  GlyphDataPrivate *G;

  iter = m_glyph_map.find(src);
  if(iter != m_glyph_map.end())
    {
      return iter->second;
    }

  if(m_free_slots.empty())
    {
      G = FASTUIDRAWnew GlyphDataPrivate(this, m_glyphs.size());
      m_glyphs.push_back(G);
      assert(!G->m_render.valid());
    }
  else
    {
      unsigned int v(m_free_slots.back());
      m_free_slots.pop_back();
      G = m_glyphs[v];
      assert(!G->m_render.valid());
    }
  m_glyph_map[src] = G;
  return G;
}

///////////////////////////////////////////////////////
// fastuidraw::Glyph methods
enum fastuidraw::glyph_type
fastuidraw::Glyph::
type(void) const
{
  GlyphDataPrivate *p;
  p = reinterpret_cast<GlyphDataPrivate*>(m_opaque);
  assert(p != NULL);
  return p->m_render.m_type;
}

const fastuidraw::GlyphLayoutData&
fastuidraw::Glyph::
layout(void) const
{
  GlyphDataPrivate *p;
  p = reinterpret_cast<GlyphDataPrivate*>(m_opaque);
  assert(p != NULL && p->m_render.valid());
  return p->m_layout;
}

fastuidraw::reference_counted_ptr<fastuidraw::GlyphCache>
fastuidraw::Glyph::
cache(void) const
{
  GlyphDataPrivate *p;
  p = reinterpret_cast<GlyphDataPrivate*>(m_opaque);
  assert(p != NULL && p->m_render.valid());
  return p->m_cache->m_p;
}

unsigned int
fastuidraw::Glyph::
cache_location(void) const
{
  GlyphDataPrivate *p;
  p = reinterpret_cast<GlyphDataPrivate*>(m_opaque);
  assert(p != NULL && p->m_render.valid());
  return p->m_cache_location;
}

fastuidraw::GlyphLocation
fastuidraw::Glyph::
atlas_location(void) const
{
  GlyphDataPrivate *p;
  p = reinterpret_cast<GlyphDataPrivate*>(m_opaque);
  assert(p != NULL && p->m_render.valid());
  return p->m_atlas_location[0];
}

fastuidraw::GlyphLocation
fastuidraw::Glyph::
secondary_atlas_location(void) const
{
  GlyphDataPrivate *p;
  p = reinterpret_cast<GlyphDataPrivate*>(m_opaque);
  assert(p != NULL && p->m_render.valid());
  return p->m_atlas_location[1];
}

int
fastuidraw::Glyph::
geometry_offset(void) const
{
  GlyphDataPrivate *p;
  p = reinterpret_cast<GlyphDataPrivate*>(m_opaque);
  assert(p != NULL && p->m_render.valid());
  return p->m_geometry_offset;
}

enum fastuidraw::return_code
fastuidraw::Glyph::
upload_to_atlas(void) const
{
  GlyphDataPrivate *p;
  p = reinterpret_cast<GlyphDataPrivate*>(m_opaque);
  assert(p != NULL && p->m_render.valid());
  return p->upload_to_atlas();
}

//////////////////////////////////////////////////////////
// fastuidraw::GlyphCache methods
fastuidraw::GlyphCache::
GlyphCache(GlyphAtlas::handle patlas)
{
  m_d = FASTUIDRAWnew GlyphCachePrivate(patlas, this);
}

fastuidraw::GlyphCache::
~GlyphCache()
{
  GlyphCachePrivate *d;
  d = reinterpret_cast<GlyphCachePrivate*>(m_d);
  FASTUIDRAWdelete(d);
  m_d = NULL;
}

fastuidraw::Glyph
fastuidraw::GlyphCache::
fetch_glyph(GlyphRender render, FontBase::const_handle font, uint32_t glyph_code)
{
  if(!font || !font->can_create_rendering_data(render.m_type))
    {
      return Glyph();
    }

  GlyphCachePrivate *d;
  d = reinterpret_cast<GlyphCachePrivate*>(m_d);

  GlyphDataPrivate *q;
  GlyphSource src(font, glyph_code, render);

  q = d->fetch_or_allocate_glyph(src);

  if(!q->m_render.valid())
    {
      q->m_render = render;
      assert(!q->m_glyph_data);
      q->m_glyph_data = font->compute_rendering_data(q->m_render, glyph_code, q->m_layout);
    }

  return Glyph(q);
}

void
fastuidraw::GlyphCache::
delete_glyph(Glyph G)
{
  GlyphCachePrivate *d;
  d = reinterpret_cast<GlyphCachePrivate*>(m_d);

  GlyphDataPrivate *p;
  p = reinterpret_cast<GlyphDataPrivate*>(G.m_opaque);
  assert(p != NULL);
  assert(p->m_cache == d);
  assert(p->m_render.valid());

  GlyphSource src(p->m_layout.m_font, p->m_layout.m_glyph_code, p->m_render);
  d->m_glyph_map.erase(src);
  p->clear();
  d->m_free_slots.push_back(p->m_cache_location);
}

void
fastuidraw::GlyphCache::
clear_atlas(void)
{
  GlyphCachePrivate *d;
  d = reinterpret_cast<GlyphCachePrivate*>(m_d);

  d->m_atlas->clear();
  for(unsigned int i = 0, endi = d->m_glyphs.size(); i < endi; ++i)
    {
      d->m_glyphs[i]->m_uploaded_to_atlas = false;
      d->m_glyphs[i]->m_atlas_location[0] = fastuidraw::GlyphLocation();
      d->m_glyphs[i]->m_atlas_location[1] = fastuidraw::GlyphLocation();
      d->m_glyphs[i]->m_geometry_offset = -1;
      d->m_glyphs[i]->m_geometry_length = 0;
    }
}

void
fastuidraw::GlyphCache::
clear_cache(void)
{
  GlyphCachePrivate *d;
  d = reinterpret_cast<GlyphCachePrivate*>(m_d);

  d->m_atlas->clear();
  d->m_glyph_map.clear();

  for(unsigned int i = 0, endi = d->m_glyphs.size(); i < endi; ++i)
    {
      GlyphDataPrivate *p;
      p = d->m_glyphs[i];
      if(p->m_render.valid())
	{
	  p->clear();
	  d->m_free_slots.push_back(p->m_cache_location);
	}
    }
}
<|endoftext|>


<commit_before>#include "StdAfx.h"
#include "FileMap.h"
/*
    RawSpeed - RAW file decoder.

    Copyright (C) 2009-2014 Klaus Post

    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 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA

    http://www.klauspost.com
*/

namespace RawSpeed {

FileMap::FileMap(uint32 _size) : size(_size) {
  if (!size)
    throw FileIOException("Filemap of 0 bytes not possible");
  data = (uchar8*)_aligned_malloc(size + FILEMAP_MARGIN, 16);
  if (!data) {
    throw FileIOException("Not enough memory to open file.");
  }
  mOwnAlloc = true;
}

FileMap::FileMap(uchar8* _data, uint32 _size): data(_data), size(_size) {
  mOwnAlloc = false;
}

FileMap::FileMap(FileMap *f, uint32 offset) {
  size = f->getSize()-offset;
  data = f->getDataWrt(offset, size+FILEMAP_MARGIN);
  mOwnAlloc = false;
}

FileMap::FileMap(FileMap *f, uint32 offset, uint32 size) {
  data = f->getDataWrt(offset, size+FILEMAP_MARGIN);
  mOwnAlloc = false;
}

FileMap::~FileMap(void) {
  if (data && mOwnAlloc) {
    _aligned_free(data);
  }
  data = 0;
  size = 0;
}

FileMap* FileMap::clone() {
  FileMap *new_map = new FileMap(size);
  memcpy(new_map->data, data, size);
  return new_map;
}

FileMap* FileMap::cloneRandomSize() {
  uint32 new_size = (rand() | (rand() << 15)) % size;
  FileMap *new_map = new FileMap(new_size);
  memcpy(new_map->data, data, new_size);
  return new_map;
}

void FileMap::corrupt(int errors) {
  for (int i = 0; i < errors; i++) {
    uint32 pos = (rand() | (rand() << 15)) % size;
    data[pos] = rand() & 0xff;
  }
}

bool FileMap::isValid(uint32 offset, uint32 count)
{
  uint64 totaloffset = (uint64)offset + (uint64)count - 1;
  return (isValid(offset) && totaloffset < size);
}

const uchar8* FileMap::getData( uint32 offset, uint32 count )
{
  if (count == 0)
    throw IOException("FileMap: Trying to get a zero sized buffer?!");

  uint64 totaloffset = (uint64)offset + (uint64)count - 1;
  uint64 totalsize = (uint64)size + FILEMAP_MARGIN;

  // Give out data up to FILEMAP_MARGIN more bytes than are really in the
  // file as that is useful for some of the BitPump code
  if (!isValid(offset) || totaloffset > totalsize)
    throw IOException("FileMap: Attempting to read file out of bounds.");
  return &data[offset];
}

} // namespace RawSpeed
<commit_msg>off-by-one in file bounds checking<commit_after>#include "StdAfx.h"
#include "FileMap.h"
/*
    RawSpeed - RAW file decoder.

    Copyright (C) 2009-2014 Klaus Post

    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 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA

    http://www.klauspost.com
*/

namespace RawSpeed {

FileMap::FileMap(uint32 _size) : size(_size) {
  if (!size)
    throw FileIOException("Filemap of 0 bytes not possible");
  data = (uchar8*)_aligned_malloc(size + FILEMAP_MARGIN, 16);
  if (!data) {
    throw FileIOException("Not enough memory to open file.");
  }
  mOwnAlloc = true;
}

FileMap::FileMap(uchar8* _data, uint32 _size): data(_data), size(_size) {
  mOwnAlloc = false;
}

FileMap::FileMap(FileMap *f, uint32 offset) {
  size = f->getSize()-offset;
  data = f->getDataWrt(offset, size+FILEMAP_MARGIN);
  mOwnAlloc = false;
}

FileMap::FileMap(FileMap *f, uint32 offset, uint32 size) {
  data = f->getDataWrt(offset, size+FILEMAP_MARGIN);
  mOwnAlloc = false;
}

FileMap::~FileMap(void) {
  if (data && mOwnAlloc) {
    _aligned_free(data);
  }
  data = 0;
  size = 0;
}

FileMap* FileMap::clone() {
  FileMap *new_map = new FileMap(size);
  memcpy(new_map->data, data, size);
  return new_map;
}

FileMap* FileMap::cloneRandomSize() {
  uint32 new_size = (rand() | (rand() << 15)) % size;
  FileMap *new_map = new FileMap(new_size);
  memcpy(new_map->data, data, new_size);
  return new_map;
}

void FileMap::corrupt(int errors) {
  for (int i = 0; i < errors; i++) {
    uint32 pos = (rand() | (rand() << 15)) % size;
    data[pos] = rand() & 0xff;
  }
}

bool FileMap::isValid(uint32 offset, uint32 count)
{
  uint64 totaloffset = (uint64)offset + (uint64)count - 1;
  return (isValid(offset) && totaloffset < size);
}

const uchar8* FileMap::getData( uint32 offset, uint32 count )
{
  if (count == 0)
    throw IOException("FileMap: Trying to get a zero sized buffer?!");

  uint64 totaloffset = (uint64)offset + (uint64)count - 1;
  uint64 totalsize = (uint64)size + FILEMAP_MARGIN;

  // Give out data up to FILEMAP_MARGIN more bytes than are really in the
  // file as that is useful for some of the BitPump code
  if (!isValid(offset) || totaloffset >= totalsize)
    throw IOException("FileMap: Attempting to read file out of bounds.");
  return &data[offset];
}

} // namespace RawSpeed
<|endoftext|>


<commit_before>/////////////////////////////////////////////////////////////////////////////
//
// Copyright (c) 2022, The Regents of the University of California
// All rights reserved.
//
// BSD 3-Clause License
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are met:
//
// * Redistributions of source code must retain the above copyright notice, this
//   list of conditions and the following disclaimer.
//
// * Redistributions in binary form must reproduce the above copyright notice,
//   this list of conditions and the following disclaimer in the documentation
//   and/or other materials provided with the distribution.
//
// * Neither the name of the copyright holder nor the names of its
//   contributors may be used to endorse or promote products derived from
//   this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
// POSSIBILITY OF SUCH DAMAGE.
//
///////////////////////////////////////////////////////////////////////////////

#include "globalConnectDialog.h"
#include "utl/Logger.h"

#include <QApplication>
#include <QHBoxLayout>
#include <QLabel>
#include <QMessageBox>
#include <QVBoxLayout>

Q_DECLARE_METATYPE(odb::dbNet*);
Q_DECLARE_METATYPE(odb::dbRegion*);

namespace gui {

enum class GlobalConnectField
{
  Instance,
  Pin,
  Net,
  Region,
  Run,
  Remove
};

static int toValue(GlobalConnectField f)
{
  return static_cast<int>(f);
}

/////////

GlobalConnectDialog::GlobalConnectDialog(odb::dbBlock* block, QWidget* parent)
    : QDialog(parent),
      block_(block),
      layout_(new QGridLayout),
      add_(new QPushButton(this)),
      clear_(new QPushButton(this)),
      run_(new QPushButton(this)),
      rules_({}),
      inst_pattern_(new QLineEdit(this)),
      pin_pattern_(new QLineEdit(this)),
      net_(new QComboBox(this)),
      region_(new QComboBox(this)),
      connections_(new QLabel(this))
{
  setWindowTitle("Global Connect Rules");

  QVBoxLayout* layout = new QVBoxLayout;
  layout->addLayout(layout_);
  QHBoxLayout* button_layout = new QHBoxLayout;
  button_layout->addWidget(clear_);
  button_layout->addWidget(run_);
  layout->addLayout(button_layout);
  layout->addWidget(connections_);

  setLayout(layout);

  add_->setIcon(QIcon(":/add.png"));
  add_->setToolTip(tr("Run"));
  add_->setAutoDefault(false);
  add_->setDefault(false);
  add_->setEnabled(false);

  run_->setIcon(QIcon(":/play.png"));
  run_->setToolTip(tr("Run"));
  run_->setAutoDefault(false);
  run_->setDefault(false);

  clear_->setIcon(QIcon(":/delete.png"));
  clear_->setToolTip(tr("Clear"));
  clear_->setAutoDefault(false);
  clear_->setDefault(false);

  connect(add_, SIGNAL(pressed()), this, SLOT(makeRule()));
  connect(run_, SIGNAL(pressed()), this, SLOT(runRules()));
  connect(clear_, SIGNAL(pressed()), this, SLOT(clearRules()));

  layout_->addWidget(new QLabel("Instance pattern", this),
                     0,
                     toValue(GlobalConnectField::Instance),
                     Qt::AlignCenter);
  layout_->addWidget(new QLabel("Pin pattern", this),
                     0,
                     toValue(GlobalConnectField::Pin),
                     Qt::AlignCenter);
  layout_->addWidget(new QLabel("Net", this),
                     0,
                     toValue(GlobalConnectField::Net),
                     Qt::AlignCenter);
  layout_->addWidget(new QLabel("Region", this),
                     0,
                     toValue(GlobalConnectField::Region),
                     Qt::AlignCenter);
  for (auto* rule : block_->getGlobalConnects()) {
    addRule(rule);
  }

  region_->addItem("All",
                   QVariant::fromValue(static_cast<odb::dbRegion*>(nullptr)));
  for (auto* region : block_->getRegions()) {
    region_->addItem(QString::fromStdString(region->getName()),
                     QVariant::fromValue(static_cast<odb::dbRegion*>(region)));
  }

  for (auto* net : block_->getNets()) {
    if (!net->isSpecial()) {
      continue;
    }
    net_->addItem(QString::fromStdString(net->getName()),
                  QVariant::fromValue(static_cast<odb::dbNet*>(net)));
  }

  const int row_idx = rules_.size() + 1;
  layout_->addWidget(inst_pattern_,
                     row_idx,
                     toValue(GlobalConnectField::Instance),
                     Qt::AlignCenter);
  layout_->addWidget(
      pin_pattern_, row_idx, toValue(GlobalConnectField::Pin), Qt::AlignCenter);
  layout_->addWidget(
      net_, row_idx, toValue(GlobalConnectField::Net), Qt::AlignCenter);
  layout_->addWidget(
      region_, row_idx, toValue(GlobalConnectField::Region), Qt::AlignCenter);
  layout_->addWidget(
      add_, row_idx, toValue(GlobalConnectField::Run), Qt::AlignCenter);

  connect(this, SIGNAL(connectionsMade(int)), this, SLOT(announceConnections(int)));
  connect(inst_pattern_, SIGNAL(textChanged(const QString&)), this, SLOT(addRegexTextChanged(const QString&)));
  connect(pin_pattern_, SIGNAL(textChanged(const QString&)), this, SLOT(addRegexTextChanged(const QString&)));
}

void GlobalConnectDialog::runRules()
{
  runRule(nullptr);
}

void GlobalConnectDialog::clearRules()
{
  auto rule_set = block_->getGlobalConnects();
  std::set<odb::dbGlobalConnect*> rules(rule_set.begin(), rule_set.end());
  for (auto* rule : rules) {
    deleteRule(rule);
  }
}

void GlobalConnectDialog::deleteRule(odb::dbGlobalConnect* gc)
{
  auto& widgets = rules_[gc];

  layout_->removeWidget(widgets.inst_pattern);
  layout_->removeWidget(widgets.pin_pattern);
  layout_->removeWidget(widgets.net);
  layout_->removeWidget(widgets.region);
  layout_->removeWidget(widgets.run);
  layout_->removeWidget(widgets.remove);

  delete widgets.inst_pattern;
  delete widgets.pin_pattern;
  delete widgets.net;
  delete widgets.region;
  delete widgets.run;
  delete widgets.remove;

  rules_.erase(gc);

  odb::dbGlobalConnect::destroy(gc);

  adjustSize();
}

void GlobalConnectDialog::addRule(odb::dbGlobalConnect* gc)
{
  const int row_idx = rules_.size() + 1;
  auto& widgets = rules_[gc];

  auto setup_line = [](QLineEdit* line) {
    line->setReadOnly(true);
    line->setStyleSheet("color: black; background-color: lightGray");
  };

  widgets.inst_pattern = new QLineEdit(this);
  setup_line(widgets.inst_pattern);
  widgets.inst_pattern->setText(QString::fromStdString(gc->getInstPattern()));
  layout_->addWidget(widgets.inst_pattern,
                     row_idx,
                     toValue(GlobalConnectField::Instance),
                     Qt::AlignCenter);
  widgets.pin_pattern = new QLineEdit(this);
  setup_line(widgets.pin_pattern);
  widgets.pin_pattern->setText(QString::fromStdString(gc->getPinPattern()));
  layout_->addWidget(widgets.pin_pattern,
                     row_idx,
                     toValue(GlobalConnectField::Pin),
                     Qt::AlignCenter);
  widgets.net = new QLineEdit(this);
  setup_line(widgets.net);
  widgets.net->setText(QString::fromStdString(gc->getNet()->getName()));
  layout_->addWidget(
      widgets.net, row_idx, toValue(GlobalConnectField::Net), Qt::AlignCenter);

  widgets.region = new QLineEdit(this);
  odb::dbRegion* dbregion = gc->getRegion();
  if (dbregion == nullptr) {
    widgets.region->setText("All");
  } else {
    widgets.region->setText(QString::fromStdString(dbregion->getName()));
  }
  setup_line(widgets.region);
  layout_->addWidget(widgets.region,
                     row_idx,
                     toValue(GlobalConnectField::Region),
                     Qt::AlignCenter);

  widgets.run = new QPushButton(this);
  widgets.run->setIcon(QIcon(":/play.png"));
  widgets.run->setToolTip(tr("Run"));
  widgets.run->setAutoDefault(false);
  widgets.run->setDefault(false);
  connect(widgets.run, &QPushButton::pressed, this, [this, gc]() {
    runRule(gc);
  });
  layout_->addWidget(
      widgets.run, row_idx, toValue(GlobalConnectField::Run), Qt::AlignCenter);

  widgets.remove = new QPushButton(this);
  widgets.remove->setIcon(QIcon(":/delete.png"));
  widgets.remove->setToolTip(tr("Delete"));
  widgets.remove->setAutoDefault(false);
  widgets.remove->setDefault(false);
  connect(widgets.remove, &QPushButton::pressed, this, [this, gc]() {
    deleteRule(gc);
  });
  layout_->addWidget(widgets.remove,
                     row_idx,
                     toValue(GlobalConnectField::Remove),
                     Qt::AlignCenter);

  adjustSize();
}

void GlobalConnectDialog::makeRule()
{
  const std::string inst_pattern = inst_pattern_->text().toStdString();
  const std::string pin_pattern = pin_pattern_->text().toStdString();
  odb::dbNet* net = net_->currentData().value<odb::dbNet*>();
  odb::dbRegion* region = region_->currentData().value<odb::dbRegion*>();

  try {
    auto* rule
        = odb::dbGlobalConnect::create(net, region, inst_pattern, pin_pattern);
    addRule(rule);
  } catch (const std::runtime_error& error) {
    QMessageBox::critical(this, error.what(), "Failed to add rule.");
  }

  layout_->removeWidget(inst_pattern_);
  layout_->removeWidget(pin_pattern_);
  layout_->removeWidget(net_);
  layout_->removeWidget(region_);
  layout_->removeWidget(add_);

  const int row_idx = rules_.size() + 1;
  layout_->addWidget(inst_pattern_,
                     row_idx,
                     toValue(GlobalConnectField::Instance),
                     Qt::AlignCenter);
  layout_->addWidget(
      pin_pattern_, row_idx, toValue(GlobalConnectField::Pin), Qt::AlignCenter);
  layout_->addWidget(
      net_, row_idx, toValue(GlobalConnectField::Net), Qt::AlignCenter);
  layout_->addWidget(
      region_, row_idx, toValue(GlobalConnectField::Region), Qt::AlignCenter);
  layout_->addWidget(
      add_, row_idx, toValue(GlobalConnectField::Run), Qt::AlignCenter);

  inst_pattern_->clear();
  pin_pattern_->clear();
  add_->setEnabled(false);

  adjustSize();
}

void GlobalConnectDialog::announceConnections(int connections)
{
  connections_->setText(QString::fromStdString(fmt::format("Connected {} pin(s)", connections)));
}

void GlobalConnectDialog::runRule(odb::dbGlobalConnect* gc)
{
  QApplication::setOverrideCursor(Qt::WaitCursor);
  QApplication::processEvents();
  int connections = 0;
  if (gc == nullptr) {
    connections = block_->globalConnect();
  } else {
    connections = block_->globalConnect(gc);
  }
  QApplication::restoreOverrideCursor();
  emit connectionsMade(connections);
}

void GlobalConnectDialog::addRegexTextChanged(const QString& text)
{
  // test if this is a valid regex
  try {
    std::regex(text.toStdString());
  }
  catch (const std::regex_error&) {
    add_->setEnabled(false);
    return;
  }

  const bool enable = !inst_pattern_->text().isEmpty() && !pin_pattern_->text().isEmpty();
  add_->setEnabled(enable);
}

}  // namespace gui
<commit_msg>gui: add check for effectively empty regex<commit_after>/////////////////////////////////////////////////////////////////////////////
//
// Copyright (c) 2022, The Regents of the University of California
// All rights reserved.
//
// BSD 3-Clause License
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are met:
//
// * Redistributions of source code must retain the above copyright notice, this
//   list of conditions and the following disclaimer.
//
// * Redistributions in binary form must reproduce the above copyright notice,
//   this list of conditions and the following disclaimer in the documentation
//   and/or other materials provided with the distribution.
//
// * Neither the name of the copyright holder nor the names of its
//   contributors may be used to endorse or promote products derived from
//   this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
// POSSIBILITY OF SUCH DAMAGE.
//
///////////////////////////////////////////////////////////////////////////////

#include "globalConnectDialog.h"
#include "utl/Logger.h"

#include <QApplication>
#include <QHBoxLayout>
#include <QLabel>
#include <QMessageBox>
#include <QVBoxLayout>

Q_DECLARE_METATYPE(odb::dbNet*);
Q_DECLARE_METATYPE(odb::dbRegion*);

namespace gui {

enum class GlobalConnectField
{
  Instance,
  Pin,
  Net,
  Region,
  Run,
  Remove
};

static int toValue(GlobalConnectField f)
{
  return static_cast<int>(f);
}

/////////

GlobalConnectDialog::GlobalConnectDialog(odb::dbBlock* block, QWidget* parent)
    : QDialog(parent),
      block_(block),
      layout_(new QGridLayout),
      add_(new QPushButton(this)),
      clear_(new QPushButton(this)),
      run_(new QPushButton(this)),
      rules_({}),
      inst_pattern_(new QLineEdit(this)),
      pin_pattern_(new QLineEdit(this)),
      net_(new QComboBox(this)),
      region_(new QComboBox(this)),
      connections_(new QLabel(this))
{
  setWindowTitle("Global Connect Rules");

  QVBoxLayout* layout = new QVBoxLayout;
  layout->addLayout(layout_);
  QHBoxLayout* button_layout = new QHBoxLayout;
  button_layout->addWidget(clear_);
  button_layout->addWidget(run_);
  layout->addLayout(button_layout);
  layout->addWidget(connections_);

  setLayout(layout);

  add_->setIcon(QIcon(":/add.png"));
  add_->setToolTip(tr("Run"));
  add_->setAutoDefault(false);
  add_->setDefault(false);
  add_->setEnabled(false);

  run_->setIcon(QIcon(":/play.png"));
  run_->setToolTip(tr("Run"));
  run_->setAutoDefault(false);
  run_->setDefault(false);

  clear_->setIcon(QIcon(":/delete.png"));
  clear_->setToolTip(tr("Clear"));
  clear_->setAutoDefault(false);
  clear_->setDefault(false);

  connect(add_, SIGNAL(pressed()), this, SLOT(makeRule()));
  connect(run_, SIGNAL(pressed()), this, SLOT(runRules()));
  connect(clear_, SIGNAL(pressed()), this, SLOT(clearRules()));

  layout_->addWidget(new QLabel("Instance pattern", this),
                     0,
                     toValue(GlobalConnectField::Instance),
                     Qt::AlignCenter);
  layout_->addWidget(new QLabel("Pin pattern", this),
                     0,
                     toValue(GlobalConnectField::Pin),
                     Qt::AlignCenter);
  layout_->addWidget(new QLabel("Net", this),
                     0,
                     toValue(GlobalConnectField::Net),
                     Qt::AlignCenter);
  layout_->addWidget(new QLabel("Region", this),
                     0,
                     toValue(GlobalConnectField::Region),
                     Qt::AlignCenter);
  for (auto* rule : block_->getGlobalConnects()) {
    addRule(rule);
  }

  region_->addItem("All",
                   QVariant::fromValue(static_cast<odb::dbRegion*>(nullptr)));
  for (auto* region : block_->getRegions()) {
    region_->addItem(QString::fromStdString(region->getName()),
                     QVariant::fromValue(static_cast<odb::dbRegion*>(region)));
  }

  for (auto* net : block_->getNets()) {
    if (!net->isSpecial()) {
      continue;
    }
    net_->addItem(QString::fromStdString(net->getName()),
                  QVariant::fromValue(static_cast<odb::dbNet*>(net)));
  }

  const int row_idx = rules_.size() + 1;
  layout_->addWidget(inst_pattern_,
                     row_idx,
                     toValue(GlobalConnectField::Instance),
                     Qt::AlignCenter);
  layout_->addWidget(
      pin_pattern_, row_idx, toValue(GlobalConnectField::Pin), Qt::AlignCenter);
  layout_->addWidget(
      net_, row_idx, toValue(GlobalConnectField::Net), Qt::AlignCenter);
  layout_->addWidget(
      region_, row_idx, toValue(GlobalConnectField::Region), Qt::AlignCenter);
  layout_->addWidget(
      add_, row_idx, toValue(GlobalConnectField::Run), Qt::AlignCenter);

  connect(this, SIGNAL(connectionsMade(int)), this, SLOT(announceConnections(int)));
  connect(inst_pattern_, SIGNAL(textChanged(const QString&)), this, SLOT(addRegexTextChanged(const QString&)));
  connect(pin_pattern_, SIGNAL(textChanged(const QString&)), this, SLOT(addRegexTextChanged(const QString&)));
}

void GlobalConnectDialog::runRules()
{
  runRule(nullptr);
}

void GlobalConnectDialog::clearRules()
{
  auto rule_set = block_->getGlobalConnects();
  std::set<odb::dbGlobalConnect*> rules(rule_set.begin(), rule_set.end());
  for (auto* rule : rules) {
    deleteRule(rule);
  }
}

void GlobalConnectDialog::deleteRule(odb::dbGlobalConnect* gc)
{
  auto& widgets = rules_[gc];

  layout_->removeWidget(widgets.inst_pattern);
  layout_->removeWidget(widgets.pin_pattern);
  layout_->removeWidget(widgets.net);
  layout_->removeWidget(widgets.region);
  layout_->removeWidget(widgets.run);
  layout_->removeWidget(widgets.remove);

  delete widgets.inst_pattern;
  delete widgets.pin_pattern;
  delete widgets.net;
  delete widgets.region;
  delete widgets.run;
  delete widgets.remove;

  rules_.erase(gc);

  odb::dbGlobalConnect::destroy(gc);

  adjustSize();
}

void GlobalConnectDialog::addRule(odb::dbGlobalConnect* gc)
{
  const int row_idx = rules_.size() + 1;
  auto& widgets = rules_[gc];

  auto setup_line = [](QLineEdit* line) {
    line->setReadOnly(true);
    line->setStyleSheet("color: black; background-color: lightGray");
  };

  widgets.inst_pattern = new QLineEdit(this);
  setup_line(widgets.inst_pattern);
  widgets.inst_pattern->setText(QString::fromStdString(gc->getInstPattern()));
  layout_->addWidget(widgets.inst_pattern,
                     row_idx,
                     toValue(GlobalConnectField::Instance),
                     Qt::AlignCenter);
  widgets.pin_pattern = new QLineEdit(this);
  setup_line(widgets.pin_pattern);
  widgets.pin_pattern->setText(QString::fromStdString(gc->getPinPattern()));
  layout_->addWidget(widgets.pin_pattern,
                     row_idx,
                     toValue(GlobalConnectField::Pin),
                     Qt::AlignCenter);
  widgets.net = new QLineEdit(this);
  setup_line(widgets.net);
  widgets.net->setText(QString::fromStdString(gc->getNet()->getName()));
  layout_->addWidget(
      widgets.net, row_idx, toValue(GlobalConnectField::Net), Qt::AlignCenter);

  widgets.region = new QLineEdit(this);
  odb::dbRegion* dbregion = gc->getRegion();
  if (dbregion == nullptr) {
    widgets.region->setText("All");
  } else {
    widgets.region->setText(QString::fromStdString(dbregion->getName()));
  }
  setup_line(widgets.region);
  layout_->addWidget(widgets.region,
                     row_idx,
                     toValue(GlobalConnectField::Region),
                     Qt::AlignCenter);

  widgets.run = new QPushButton(this);
  widgets.run->setIcon(QIcon(":/play.png"));
  widgets.run->setToolTip(tr("Run"));
  widgets.run->setAutoDefault(false);
  widgets.run->setDefault(false);
  connect(widgets.run, &QPushButton::pressed, this, [this, gc]() {
    runRule(gc);
  });
  layout_->addWidget(
      widgets.run, row_idx, toValue(GlobalConnectField::Run), Qt::AlignCenter);

  widgets.remove = new QPushButton(this);
  widgets.remove->setIcon(QIcon(":/delete.png"));
  widgets.remove->setToolTip(tr("Delete"));
  widgets.remove->setAutoDefault(false);
  widgets.remove->setDefault(false);
  connect(widgets.remove, &QPushButton::pressed, this, [this, gc]() {
    deleteRule(gc);
  });
  layout_->addWidget(widgets.remove,
                     row_idx,
                     toValue(GlobalConnectField::Remove),
                     Qt::AlignCenter);

  adjustSize();
}

void GlobalConnectDialog::makeRule()
{
  const std::string inst_pattern = inst_pattern_->text().toStdString();
  const std::string pin_pattern = pin_pattern_->text().toStdString();
  odb::dbNet* net = net_->currentData().value<odb::dbNet*>();
  odb::dbRegion* region = region_->currentData().value<odb::dbRegion*>();

  try {
    auto* rule
        = odb::dbGlobalConnect::create(net, region, inst_pattern, pin_pattern);
    addRule(rule);
  } catch (const std::runtime_error& error) {
    QMessageBox::critical(this, error.what(), "Failed to add rule.");
  }

  layout_->removeWidget(inst_pattern_);
  layout_->removeWidget(pin_pattern_);
  layout_->removeWidget(net_);
  layout_->removeWidget(region_);
  layout_->removeWidget(add_);

  const int row_idx = rules_.size() + 1;
  layout_->addWidget(inst_pattern_,
                     row_idx,
                     toValue(GlobalConnectField::Instance),
                     Qt::AlignCenter);
  layout_->addWidget(
      pin_pattern_, row_idx, toValue(GlobalConnectField::Pin), Qt::AlignCenter);
  layout_->addWidget(
      net_, row_idx, toValue(GlobalConnectField::Net), Qt::AlignCenter);
  layout_->addWidget(
      region_, row_idx, toValue(GlobalConnectField::Region), Qt::AlignCenter);
  layout_->addWidget(
      add_, row_idx, toValue(GlobalConnectField::Run), Qt::AlignCenter);

  inst_pattern_->clear();
  pin_pattern_->clear();
  add_->setEnabled(false);

  adjustSize();
}

void GlobalConnectDialog::announceConnections(int connections)
{
  connections_->setText(QString::fromStdString(fmt::format("Connected {} pin(s)", connections)));
}

void GlobalConnectDialog::runRule(odb::dbGlobalConnect* gc)
{
  QApplication::setOverrideCursor(Qt::WaitCursor);
  QApplication::processEvents();
  int connections = 0;
  if (gc == nullptr) {
    connections = block_->globalConnect();
  } else {
    connections = block_->globalConnect(gc);
  }
  QApplication::restoreOverrideCursor();
  emit connectionsMade(connections);
}

void GlobalConnectDialog::addRegexTextChanged(const QString& text)
{
  // test if this is a valid regex
  try {
    std::regex(text.toStdString());
  }
  catch (const std::regex_error&) {
    add_->setEnabled(false);
    return;
  }

  QString check = text;
  check.remove('$');
  check.remove('^');
  if (check.isEmpty()) {
    add_->setEnabled(false);
    return;
  }

  const bool enable = !inst_pattern_->text().isEmpty() && !pin_pattern_->text().isEmpty();
  add_->setEnabled(enable);
}

}  // namespace gui
<|endoftext|>


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

#include "ibmras/common/util/sysUtils.h"

#if defined(WINDOWS)
	#include <windows.h>
//    #include <winsock2.h>
	#include <Psapi.h>
#elif defined(LINUX) || defined(__MACH__) || defined(__APPLE__)
#include <sys/time.h>
#elif defined(AIX)
#include <sys/time.h>
#elif defined(_ZOS)
#define _XOPEN_SOURCE_EXTENDED 1
#undef _ALL_SOURCE
#include <sys/time.h>
#endif

#include <ctime>

namespace ibmras {
namespace common {
namespace util {

unsigned long long getMilliseconds() {
	unsigned long long millisecondsSinceEpoch;
#if defined(WINDOWS)

	SYSTEMTIME st;
	GetSystemTime(&st);

	millisecondsSinceEpoch = time(NULL)*1000+st.wMilliseconds;

#else
		struct timeval tv;
	gettimeofday(&tv, NULL);

	millisecondsSinceEpoch =
	    (unsigned long long)(tv.tv_sec) * 1000 +
	    (unsigned long long)(tv.tv_usec) / 1000;
#endif
	return millisecondsSinceEpoch;
}

}/*end of namespace util*/
}/*end of namespace common*/
} /*end of namespace ibmras*/

<commit_msg>Put z/OS #defines at the top of sysUtils.cpp<commit_after>/*******************************************************************************
 * Copyright 2016 IBM Corp.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *******************************************************************************/
#if defined(_ZOS)
#define _XOPEN_SOURCE_EXTENDED 1
#undef _ALL_SOURCE
#endif

#include "ibmras/common/util/sysUtils.h"

#if defined(WINDOWS)
	#include <windows.h>
//    #include <winsock2.h>
	#include <Psapi.h>
#elif defined(LINUX) || defined(__MACH__) || defined(__APPLE__)
#include <sys/time.h>
#elif defined(AIX)
#include <sys/time.h>
#elif defined(_ZOS)
#include <sys/time.h>
#endif

#include <ctime>

namespace ibmras {
namespace common {
namespace util {

unsigned long long getMilliseconds() {
	unsigned long long millisecondsSinceEpoch;
#if defined(WINDOWS)

	SYSTEMTIME st;
	GetSystemTime(&st);

	millisecondsSinceEpoch = time(NULL)*1000+st.wMilliseconds;

#else
		struct timeval tv;
	gettimeofday(&tv, NULL);

	millisecondsSinceEpoch =
	    (unsigned long long)(tv.tv_sec) * 1000 +
	    (unsigned long long)(tv.tv_usec) / 1000;
#endif
	return millisecondsSinceEpoch;
}

}/*end of namespace util*/
}/*end of namespace common*/
} /*end of namespace ibmras*/

<|endoftext|>


<commit_before>#include "Settings.h"

#include <Shlwapi.h>
#include <algorithm>

#include "HotkeyActions.h"
#include "Logger.h"
#include "MiscUtils.h"
#include "Skin.h"
#include "StringUtils.h"

#define XML_AUDIODEV "audioDeviceID"
#define XML_HIDE_WHENFULL "hideFullscreen"
#define XML_HIDEANIM "hideAnimation"
#define XML_HIDETIME "hideDelay"
#define XML_HIDESPEED "hideSpeed"
#define XML_LANGUAGE "language"
#define XML_NOTIFYICON "notifyIcon"
#define XML_ONTOP "onTop"
#define XML_OSD_OFFSET "osdEdgeOffset"
#define XML_OSD_POS "osdPosition"
#define XML_OSD_X "osdX"
#define XML_OSD_Y "osdY"
#define XML_SKIN "skin"
#define XML_SOUNDS "soundEffects"

std::wstring Settings::_appDir(L"");
Settings *Settings::instance;

std::vector<std::wstring> Settings::OSDPosNames
        = { L"top", L"left", L"right", L"bottom", L"center", L"custom" };
std::vector<std::wstring> Settings::HideAnimNames = { L"none", L"fade" };

Settings::Settings() {

}

Settings *Settings::Instance() {
    if (instance == NULL) {
        instance = new Settings();
        instance->Reload();
    }
    return instance;
}

void Settings::Reload() {
    _file = AppDir() + L"\\Settings.xml";
    CLOG(L"Loading settings file: %s", _file.c_str());

    std::string u8FileName = StringUtils::Narrow(_file);
    tinyxml2::XMLError result = _xml.LoadFile(u8FileName.c_str());
    if (result != tinyxml2::XMLError::XML_SUCCESS) {
        throw std::runtime_error("Failed to parse XML file");
    }

    _root = _xml.GetDocument()->FirstChildElement("settings");
    if (_root == NULL) {
        throw std::runtime_error("Could not find root XML element");
    }

    _skin = new Skin(SkinXML());
}

int Settings::Save() {
    FILE *stream;
    errno_t err = _wfopen_s(&stream, _file.c_str(), L"w");
    if (err != 0) {
        CLOG(L"Could not open settings file for writing!");
        return 100 + err;
    }
    tinyxml2::XMLError result = _xml.SaveFile(stream);
    fclose(stream);
    return result;
}

std::wstring Settings::AppDir() {
    if (_appDir.empty()) {
        wchar_t path[MAX_PATH];
        GetModuleFileName(NULL, path, MAX_PATH);
        PathRemoveFileSpec(path);
        _appDir = std::wstring(path);
    }
    return _appDir;
}

std::wstring Settings::SettingsApp() {
    return Settings::AppDir() + L"\\" + SETTINGS_APP;
}

std::wstring Settings::LanguagesDir() {
    return AppDir() + L"\\" + LANG_DIR;
}

std::wstring Settings::AudioDeviceID() {
    return GetText(XML_AUDIODEV);
}

std::wstring Settings::LanguageName() {
    std::wstring lang = GetText(XML_LANGUAGE);

    if (lang == L"") {
        return DEFAULT_LANGUAGE;
    } else {
        return lang;
    }
}

bool Settings::AlwaysOnTop() {
    return GetEnabled(XML_ONTOP);
}

bool Settings::HideFullscreen() {
    return GetEnabled(XML_HIDE_WHENFULL);
}

int Settings::OSDEdgeOffset() {
    if (HasSetting(XML_OSD_OFFSET)) {
        return GetInt(XML_OSD_OFFSET);
    } else {
        return DEFAULT_OSD_OFFSET;
    }
}

void Settings::OSDEdgeOffset(int offset) {
    SetInt(XML_OSD_OFFSET, offset);
}

Settings::OSDPos Settings::OSDPosition() {
    std::wstring pos = GetText(XML_OSD_POS);
    std::transform(pos.begin(), pos.end(), pos.begin(), ::tolower);

    for (unsigned int i = 0; i < OSDPosNames.size(); ++i) {
        if (pos == OSDPosNames[i]) {
            return (Settings::OSDPos) i;
        }
    }

    return DEFAULT_OSD_POS;
}

void Settings::OSDPosition(OSDPos pos) {
    std::wstring posStr = OSDPosNames[(int) pos];
    SetText(XML_OSD_POS, StringUtils::Narrow(posStr));
}

int Settings::OSDX() {
    return GetInt(XML_OSD_X);
}

void Settings::OSDX(int x) {
    SetInt(XML_OSD_X, x);
}

int Settings::OSDY() {
    return GetInt(XML_OSD_Y);
}

void Settings::OSDY(int y) {
    SetInt(XML_OSD_Y, y);
}

Skin *Settings::CurrentSkin() {
    return _skin;
}

Settings::HideAnim Settings::HideAnimation() {
    std::wstring anim = GetText(XML_HIDEANIM);
    std::transform(anim.begin(), anim.end(), anim.begin(), ::tolower);

    for (unsigned int i = 0; i < HideAnimNames.size(); ++i) {
        if (anim == HideAnimNames[i]) {
            return (Settings::HideAnim) i;
        }
    }

    return DEFAULT_HIDE_ANIM;
}

void Settings::HideAnimation(Settings::HideAnim anim) {
    std::wstring hideStr = HideAnimNames[(int) anim];
    SetText(XML_HIDEANIM, StringUtils::Narrow(hideStr));
}

int Settings::HideDelay() {
    return GetInt(XML_HIDETIME);
}

void Settings::HideDelay(int delay) {
    SetInt(XML_HIDETIME, delay);
}

int Settings::HideSpeed() {
    return GetInt(XML_HIDESPEED);
}

void Settings::HideSpeed(int speed) {
    SetInt(XML_HIDESPEED, speed);
}

bool Settings::CurrentSkin(std::wstring skinName) {
    std::string name = StringUtils::Narrow(skinName);
    std::wstring xml = SkinXML(skinName);
    if (MiscUtils::FileExists(xml) == false) {
        return false;
    }

    SetText(XML_SKIN, name);
    return true;
}

std::wstring Settings::SkinName() {
    std::wstring name = GetText("skin");

    if (name == L"") {
        return DEFAULT_SKIN;
    } else {
        return name;
    }
}

std::wstring Settings::SkinXML() {
    return SkinXML(SkinName());
}

std::wstring Settings::SkinXML(std::wstring skinName) {
    std::wstring skinXML = Settings::AppDir() + L"\\" + SKINS_DIR L"\\"
        + skinName + L"\\" SKIN_XML;
    return skinXML;
}

std::unordered_map<int, int> Settings::Hotkeys() {
    std::unordered_map<int, int> keyMappings;

    tinyxml2::XMLElement *hotkeys = _root->FirstChildElement("hotkeys");
    tinyxml2::XMLElement *hotkey = hotkeys->FirstChildElement("hotkey");

    for (; hotkey != NULL; hotkey = hotkey->NextSiblingElement()) {
        int action = -1;
        hotkey->QueryIntAttribute("action", &action);
        if (action == -1) {
            CLOG(L"No action provided for hotkey; skipping");
            continue;
        }

        int combination = -1;
        hotkey->QueryIntAttribute("combination", &combination);
        if (combination == -1) {
            CLOG(L"No key combination provided for hotkey; skipping");
            continue;
        }

        /* Whew, we made it! */
        CLOG(L"Adding hotkey mapping: %d -> %d", combination, action);
        keyMappings[combination] = action;
    }

    return keyMappings;
}

bool Settings::NotifyIconEnabled() {
    return GetEnabled(XML_NOTIFYICON);
}

void Settings::NotifyIconEnabled(bool enable) {
    SetEnabled(XML_NOTIFYICON, enable);
}

bool Settings::SoundEffectsEnabled() {
    return GetEnabled(XML_SOUNDS);
}

void Settings::SoundEffectsEnabled(bool enable) {
    SetEnabled(XML_SOUNDS, enable);
}

bool Settings::HasSetting(std::string elementName) {
    tinyxml2::XMLElement *el = _root->FirstChildElement(elementName.c_str());
    return (el != NULL);
}

bool Settings::GetEnabled(std::string elementName) {
    tinyxml2::XMLElement *el = _root->FirstChildElement(elementName.c_str());
    if (el == NULL) {
        CLOG(L"Warning: XML element '%s' not found", elementName.c_str());
        return false;
    } else {
        bool val = false;
        el->QueryBoolText(&val);
        return val;
    }
}

void Settings::SetEnabled(std::string elementName, bool enabled) {
    tinyxml2::XMLElement *el = GetOrCreateElement(elementName);
    el->SetText(enabled ? "true" : "false");
}

std::wstring Settings::GetText(std::string elementName) {
    tinyxml2::XMLElement *el = _root->FirstChildElement(elementName.c_str());
    if (el == NULL) {
        CLOG(L"Warning: XML element '%s' not found", elementName.c_str());
        return L"";
    }

    const char* str = el->GetText();
    if (str == NULL) {
        return L"";
    } else {
        return StringUtils::Widen(str);
    }
}

void Settings::SetText(std::string elementName, std::string text) {
    tinyxml2::XMLElement *el = GetOrCreateElement(elementName);
    el->SetText(text.c_str());
}

int Settings::GetInt(std::string elementName) {
    tinyxml2::XMLElement *el = _root->FirstChildElement(elementName.c_str());
    if (el == NULL) {
        CLOG(L"Warning: XML element '%s' not found", elementName.c_str());
        return 0;
    }

    int val = 0;
    el->QueryIntText(&val);
    return val;
}

void Settings::SetInt(std::string elementName, int value) {
    tinyxml2::XMLElement *el = GetOrCreateElement(elementName);
    el->SetText(value);
}

tinyxml2::XMLElement *Settings::GetOrCreateElement(std::string elementName) {
    tinyxml2::XMLElement *el = _root->FirstChildElement(elementName.c_str());
    if (el == NULL) {
        el = _xml.NewElement(elementName.c_str());
        _root->InsertEndChild(el);
    }
    return el;
}<commit_msg>Always on top, hide fullscreen setters<commit_after>#include "Settings.h"

#include <Shlwapi.h>
#include <algorithm>

#include "HotkeyActions.h"
#include "Logger.h"
#include "MiscUtils.h"
#include "Skin.h"
#include "StringUtils.h"

#define XML_AUDIODEV "audioDeviceID"
#define XML_HIDE_WHENFULL "hideFullscreen"
#define XML_HIDEANIM "hideAnimation"
#define XML_HIDETIME "hideDelay"
#define XML_HIDESPEED "hideSpeed"
#define XML_LANGUAGE "language"
#define XML_NOTIFYICON "notifyIcon"
#define XML_ONTOP "onTop"
#define XML_OSD_OFFSET "osdEdgeOffset"
#define XML_OSD_POS "osdPosition"
#define XML_OSD_X "osdX"
#define XML_OSD_Y "osdY"
#define XML_SKIN "skin"
#define XML_SOUNDS "soundEffects"

std::wstring Settings::_appDir(L"");
Settings *Settings::instance;

std::vector<std::wstring> Settings::OSDPosNames
        = { L"top", L"left", L"right", L"bottom", L"center", L"custom" };
std::vector<std::wstring> Settings::HideAnimNames = { L"none", L"fade" };

Settings::Settings() {

}

Settings *Settings::Instance() {
    if (instance == NULL) {
        instance = new Settings();
        instance->Reload();
    }
    return instance;
}

void Settings::Reload() {
    _file = AppDir() + L"\\Settings.xml";
    CLOG(L"Loading settings file: %s", _file.c_str());

    std::string u8FileName = StringUtils::Narrow(_file);
    tinyxml2::XMLError result = _xml.LoadFile(u8FileName.c_str());
    if (result != tinyxml2::XMLError::XML_SUCCESS) {
        throw std::runtime_error("Failed to parse XML file");
    }

    _root = _xml.GetDocument()->FirstChildElement("settings");
    if (_root == NULL) {
        throw std::runtime_error("Could not find root XML element");
    }

    _skin = new Skin(SkinXML());
}

int Settings::Save() {
    FILE *stream;
    errno_t err = _wfopen_s(&stream, _file.c_str(), L"w");
    if (err != 0) {
        CLOG(L"Could not open settings file for writing!");
        return 100 + err;
    }
    tinyxml2::XMLError result = _xml.SaveFile(stream);
    fclose(stream);
    return result;
}

std::wstring Settings::AppDir() {
    if (_appDir.empty()) {
        wchar_t path[MAX_PATH];
        GetModuleFileName(NULL, path, MAX_PATH);
        PathRemoveFileSpec(path);
        _appDir = std::wstring(path);
    }
    return _appDir;
}

std::wstring Settings::SettingsApp() {
    return Settings::AppDir() + L"\\" + SETTINGS_APP;
}

std::wstring Settings::LanguagesDir() {
    return AppDir() + L"\\" + LANG_DIR;
}

std::wstring Settings::AudioDeviceID() {
    return GetText(XML_AUDIODEV);
}

std::wstring Settings::LanguageName() {
    std::wstring lang = GetText(XML_LANGUAGE);

    if (lang == L"") {
        return DEFAULT_LANGUAGE;
    } else {
        return lang;
    }
}

bool Settings::AlwaysOnTop() {
    return GetEnabled(XML_ONTOP);
}

void Settings::AlwaysOnTop(bool enable) {
    SetEnabled(XML_ONTOP, enable);
}

bool Settings::HideFullscreen() {
    return GetEnabled(XML_HIDE_WHENFULL);
}

void Settings::HideFullscreen(bool enable) {
    SetEnabled(XML_HIDE_WHENFULL, enable);
}
int Settings::OSDEdgeOffset() {
    if (HasSetting(XML_OSD_OFFSET)) {
        return GetInt(XML_OSD_OFFSET);
    } else {
        return DEFAULT_OSD_OFFSET;
    }
}

void Settings::OSDEdgeOffset(int offset) {
    SetInt(XML_OSD_OFFSET, offset);
}

Settings::OSDPos Settings::OSDPosition() {
    std::wstring pos = GetText(XML_OSD_POS);
    std::transform(pos.begin(), pos.end(), pos.begin(), ::tolower);

    for (unsigned int i = 0; i < OSDPosNames.size(); ++i) {
        if (pos == OSDPosNames[i]) {
            return (Settings::OSDPos) i;
        }
    }

    return DEFAULT_OSD_POS;
}

void Settings::OSDPosition(OSDPos pos) {
    std::wstring posStr = OSDPosNames[(int) pos];
    SetText(XML_OSD_POS, StringUtils::Narrow(posStr));
}

int Settings::OSDX() {
    return GetInt(XML_OSD_X);
}

void Settings::OSDX(int x) {
    SetInt(XML_OSD_X, x);
}

int Settings::OSDY() {
    return GetInt(XML_OSD_Y);
}

void Settings::OSDY(int y) {
    SetInt(XML_OSD_Y, y);
}

Skin *Settings::CurrentSkin() {
    return _skin;
}

Settings::HideAnim Settings::HideAnimation() {
    std::wstring anim = GetText(XML_HIDEANIM);
    std::transform(anim.begin(), anim.end(), anim.begin(), ::tolower);

    for (unsigned int i = 0; i < HideAnimNames.size(); ++i) {
        if (anim == HideAnimNames[i]) {
            return (Settings::HideAnim) i;
        }
    }

    return DEFAULT_HIDE_ANIM;
}

void Settings::HideAnimation(Settings::HideAnim anim) {
    std::wstring hideStr = HideAnimNames[(int) anim];
    SetText(XML_HIDEANIM, StringUtils::Narrow(hideStr));
}

int Settings::HideDelay() {
    return GetInt(XML_HIDETIME);
}

void Settings::HideDelay(int delay) {
    SetInt(XML_HIDETIME, delay);
}

int Settings::HideSpeed() {
    return GetInt(XML_HIDESPEED);
}

void Settings::HideSpeed(int speed) {
    SetInt(XML_HIDESPEED, speed);
}

bool Settings::CurrentSkin(std::wstring skinName) {
    std::string name = StringUtils::Narrow(skinName);
    std::wstring xml = SkinXML(skinName);
    if (MiscUtils::FileExists(xml) == false) {
        return false;
    }

    SetText(XML_SKIN, name);
    return true;
}

std::wstring Settings::SkinName() {
    std::wstring name = GetText("skin");

    if (name == L"") {
        return DEFAULT_SKIN;
    } else {
        return name;
    }
}

std::wstring Settings::SkinXML() {
    return SkinXML(SkinName());
}

std::wstring Settings::SkinXML(std::wstring skinName) {
    std::wstring skinXML = Settings::AppDir() + L"\\" + SKINS_DIR L"\\"
        + skinName + L"\\" SKIN_XML;
    return skinXML;
}

std::unordered_map<int, int> Settings::Hotkeys() {
    std::unordered_map<int, int> keyMappings;

    tinyxml2::XMLElement *hotkeys = _root->FirstChildElement("hotkeys");
    tinyxml2::XMLElement *hotkey = hotkeys->FirstChildElement("hotkey");

    for (; hotkey != NULL; hotkey = hotkey->NextSiblingElement()) {
        int action = -1;
        hotkey->QueryIntAttribute("action", &action);
        if (action == -1) {
            CLOG(L"No action provided for hotkey; skipping");
            continue;
        }

        int combination = -1;
        hotkey->QueryIntAttribute("combination", &combination);
        if (combination == -1) {
            CLOG(L"No key combination provided for hotkey; skipping");
            continue;
        }

        /* Whew, we made it! */
        CLOG(L"Adding hotkey mapping: %d -> %d", combination, action);
        keyMappings[combination] = action;
    }

    return keyMappings;
}

bool Settings::NotifyIconEnabled() {
    return GetEnabled(XML_NOTIFYICON);
}

void Settings::NotifyIconEnabled(bool enable) {
    SetEnabled(XML_NOTIFYICON, enable);
}

bool Settings::SoundEffectsEnabled() {
    return GetEnabled(XML_SOUNDS);
}

void Settings::SoundEffectsEnabled(bool enable) {
    SetEnabled(XML_SOUNDS, enable);
}

bool Settings::HasSetting(std::string elementName) {
    tinyxml2::XMLElement *el = _root->FirstChildElement(elementName.c_str());
    return (el != NULL);
}

bool Settings::GetEnabled(std::string elementName) {
    tinyxml2::XMLElement *el = _root->FirstChildElement(elementName.c_str());
    if (el == NULL) {
        CLOG(L"Warning: XML element '%s' not found", elementName.c_str());
        return false;
    } else {
        bool val = false;
        el->QueryBoolText(&val);
        return val;
    }
}

void Settings::SetEnabled(std::string elementName, bool enabled) {
    tinyxml2::XMLElement *el = GetOrCreateElement(elementName);
    el->SetText(enabled ? "true" : "false");
}

std::wstring Settings::GetText(std::string elementName) {
    tinyxml2::XMLElement *el = _root->FirstChildElement(elementName.c_str());
    if (el == NULL) {
        CLOG(L"Warning: XML element '%s' not found", elementName.c_str());
        return L"";
    }

    const char* str = el->GetText();
    if (str == NULL) {
        return L"";
    } else {
        return StringUtils::Widen(str);
    }
}

void Settings::SetText(std::string elementName, std::string text) {
    tinyxml2::XMLElement *el = GetOrCreateElement(elementName);
    el->SetText(text.c_str());
}

int Settings::GetInt(std::string elementName) {
    tinyxml2::XMLElement *el = _root->FirstChildElement(elementName.c_str());
    if (el == NULL) {
        CLOG(L"Warning: XML element '%s' not found", elementName.c_str());
        return 0;
    }

    int val = 0;
    el->QueryIntText(&val);
    return val;
}

void Settings::SetInt(std::string elementName, int value) {
    tinyxml2::XMLElement *el = GetOrCreateElement(elementName);
    el->SetText(value);
}

tinyxml2::XMLElement *Settings::GetOrCreateElement(std::string elementName) {
    tinyxml2::XMLElement *el = _root->FirstChildElement(elementName.c_str());
    if (el == NULL) {
        el = _xml.NewElement(elementName.c_str());
        _root->InsertEndChild(el);
    }
    return el;
}<|endoftext|>


<commit_before>void CalculateHVs(Float_t requiredADCperMIP = 2.6, Int_t run = 137366)
{
  AliCDBManager *man = AliCDBManager::Instance();
  man->SetDefaultStorage("raw://");
  man->SetSpecificStorage("VZERO/Calib/PMGains","local://$ALICE_ROOT/OCDB");
  man->SetSpecificStorage("VZERO/Calib/LightYields","local://$ALICE_ROOT/OCDB");
  man->SetRun(run);

  AliCDBEntry *ent = man->Get("VZERO/Calib/Data");
  AliVZEROCalibData *calData = (AliVZEROCalibData*)ent->GetObject();
  for(Int_t i = 0; i < 64; ++i) {
    printf("%d %.0f (%.0f) S%d R%d\n",
	   i,
	   calData->GetHV(i,requiredADCperMIP),
	   calData->GetMeanHV(i),
	   i%8,
	   (i<32) ? i/8 : (i-32)/8);
  }
}
<commit_msg>Minor changes<commit_after>void CalculateHVs(Float_t requiredADCperMIP = 2.6, Int_t run = 137366, Bool_t specificStorage = kTRUE)
{
  AliCDBManager *man = AliCDBManager::Instance();
  man->SetDefaultStorage("raw://");
  if (specificStorage) {
    man->SetSpecificStorage("VZERO/Calib/PMGains","local://$ALICE_ROOT/OCDB");
    man->SetSpecificStorage("VZERO/Calib/LightYields","local://$ALICE_ROOT/OCDB");
  }
  man->SetRun(run);

  AliCDBEntry *ent = man->Get("VZERO/Calib/Data");
  AliVZEROCalibData *calData = (AliVZEROCalibData*)ent->GetObject();
  for(Int_t i = 0; i < 64; ++i) {
    printf("%d %.0f (%.0f) S%d R%d     Delta=%.0f\n",
	   i,
	   calData->GetHV(i,requiredADCperMIP),
	   calData->GetMeanHV(i),
	   i%8,
	   (i<32) ? i/8 : (i-32)/8,
	   calData->GetHV(i,requiredADCperMIP)-calData->GetMeanHV(i));
  }
}
<|endoftext|>


<commit_before>//#include <glib.h>
//#include <glib/gprintf.h>
//#include <errno.h>
//#include <string.h>
//#include <stdio.h>
//#include <stdlib.h>
#include <unistd.h>
#include <linux/i2c-dev.h>
#include <sys/ioctl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>

#include <ros/ros.h>
#include <sensor_msgs/Range.h>
#include <linux/i2c-dev.h>

#define CM_PER_IT       1.5

int main(int argc, char** argv) {
  ros::init(argc, argv, "kulbabu_hardware_ultrasonic");
  ros::NodeHandle nh;
  ros::NodeHandle ph("~");

  std::string topic_name;
  int i2c_adapter, i2c_address;
  double field_of_view, max_range, min_range, publish_frequency;

  ph.param<std::string>("topic_name", topic_name, "range");
  ph.param<int>("i2c_adapter", i2c_adapter, 1);
  ph.param<int>("i2c_address", i2c_address, 0x10);
  ph.param<double>("field_of_view", field_of_view, 0.261799388); // 15deg in rad (HCSR04)
  ph.param<double>("max_range", max_range, 4.00);
  ph.param<double>("min_range", min_range, 0.02);
  ph.param<double>("publish_frequency", publish_frequency, 10.0);

  char topic[24];
  strcpy(topic, topic_name.c_str());

  ros::Publisher range_pubs[8];

  for (uint8_t x=0;x<8;x++) {
    char name[25];
    sprintf(name, "%s%d", topic, x);
    range_pubs[x] = nh.advertise<sensor_msgs::Range>(name, 50);
  }

  // Setup i2c
  int file;
  char filename[20];

  snprintf(filename, 19, "/dev/i2c-%d", i2c_adapter);
  file = open(filename, O_RDWR);
  if (file < 0) {
    /* ERROR HANDLING; you can check errno to see what went wrong */
    ROS_ERROR_STREAM( "I2C failed opening connection");
    exit(1);
  }

  if (ioctl(file, I2C_SLAVE, i2c_address) < 0) {
    /* ERROR HANDLING; you can check errno to see what went wrong */
    ROS_ERROR_STREAM( "I2C failed addressing connection");
    exit(1);
  }

  ros::Rate r(publish_frequency);
  while (nh.ok()) {
    ros::Time current_time = ros::Time::now();

    //uint8_t res;
    // Read data from i2c
    //res = i2c_smbus_read_word_data(file, addr);
    //if (res < 0) {

    uint8_t buf[8];
    buf[0] = i2c_address;

    if (read(file, buf, sizeof(buf)) != sizeof(buf)) {
      /* ERROR HANDLING: i2c transaction failed */
      ROS_WARN_STREAM( "I2C failed " << buf );
    } else {
      // https://github.com/ros/common_msgs/blob/indigo-devel/sensor_msgs/msg/Range.msg
      sensor_msgs::Range range_msg;
      range_msg.header.stamp = current_time;

      range_msg.radiation_type = 0; // Ultrasonic // FIXME: sensor_msgs::Range::ULTRASOUND
      range_msg.field_of_view = field_of_view;
      range_msg.max_range = max_range;
      range_msg.min_range = min_range;

      for (uint8_t x=0;x<8;x++) {
        char name[25];
	    sprintf(name, "%s%d", topic, x);
        range_msg.header.frame_id = name;
        range_msg.range = (((float)buf[x])*CM_PER_IT)/100;
        ROS_INFO( "I2C read %s %d", name, buf[x] );
        range_pubs[x].publish(range_msg);
      }
    }

    ros::spinOnce();
    r.sleep();
  }

  // Wait until shutdown signal recieved
  //ros::waitForShutdown();

  return 0;
}
<commit_msg>Add link suffix<commit_after>//#include <glib.h>
//#include <glib/gprintf.h>
//#include <errno.h>
//#include <string.h>
//#include <stdio.h>
//#include <stdlib.h>
#include <unistd.h>
#include <linux/i2c-dev.h>
#include <sys/ioctl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>

#include <ros/ros.h>
#include <sensor_msgs/Range.h>
#include <linux/i2c-dev.h>

#define CM_PER_IT       1.5

int main(int argc, char** argv) {
  ros::init(argc, argv, "kulbabu_hardware_ultrasonic");
  ros::NodeHandle nh;
  ros::NodeHandle ph("~");

  std::string topic_name;
  int i2c_adapter, i2c_address;
  double field_of_view, max_range, min_range, publish_frequency;

  ph.param<std::string>("topic_name", topic_name, "range");
  ph.param<int>("i2c_adapter", i2c_adapter, 1);
  ph.param<int>("i2c_address", i2c_address, 0x10);
  ph.param<double>("field_of_view", field_of_view, 0.261799388); // 15deg in rad (HCSR04)
  ph.param<double>("max_range", max_range, 4.00);
  ph.param<double>("min_range", min_range, 0.02);
  ph.param<double>("publish_frequency", publish_frequency, 10.0);

  char topic[24];
  strcpy(topic, topic_name.c_str());

  ros::Publisher range_pubs[8];

  for (uint8_t x=0;x<8;x++) {
    char name[25];
    sprintf(name, "%s%d", topic, x);
    range_pubs[x] = nh.advertise<sensor_msgs::Range>(name, 50);
  }

  // Setup i2c
  int file;
  char filename[20];

  snprintf(filename, 19, "/dev/i2c-%d", i2c_adapter);
  file = open(filename, O_RDWR);
  if (file < 0) {
    /* ERROR HANDLING; you can check errno to see what went wrong */
    ROS_ERROR_STREAM( "I2C failed opening connection");
    exit(1);
  }

  if (ioctl(file, I2C_SLAVE, i2c_address) < 0) {
    /* ERROR HANDLING; you can check errno to see what went wrong */
    ROS_ERROR_STREAM( "I2C failed addressing connection");
    exit(1);
  }

  ros::Rate r(publish_frequency);
  while (nh.ok()) {
    ros::Time current_time = ros::Time::now();

    //uint8_t res;
    // Read data from i2c
    //res = i2c_smbus_read_word_data(file, addr);
    //if (res < 0) {

    uint8_t buf[8];
    buf[0] = i2c_address;

    if (read(file, buf, sizeof(buf)) != sizeof(buf)) {
      /* ERROR HANDLING: i2c transaction failed */
      ROS_WARN_STREAM( "I2C failed " << buf );
    } else {
      // https://github.com/ros/common_msgs/blob/indigo-devel/sensor_msgs/msg/Range.msg
      sensor_msgs::Range range_msg;
      range_msg.header.stamp = current_time;

      range_msg.radiation_type = 0; // Ultrasonic // FIXME: sensor_msgs::Range::ULTRASOUND
      range_msg.field_of_view = field_of_view;
      range_msg.max_range = max_range;
      range_msg.min_range = min_range;

      for (uint8_t x=0;x<8;x++) {
        char name[25];
	    sprintf(name, "%s%d_link", topic, x);
        range_msg.header.frame_id = name;
        range_msg.range = (((float)buf[x])*CM_PER_IT)/100;
        ROS_INFO( "I2C read %s %d", name, buf[x] );
        range_pubs[x].publish(range_msg);
      }
    }

    ros::spinOnce();
    r.sleep();
  }

  // Wait until shutdown signal recieved
  //ros::waitForShutdown();

  return 0;
}
<|endoftext|>


<commit_before>#include "httpclient.h"
#include "utilities/utils.h"

#include <assert.h>
#include <openssl/crypto.h>
#include <openssl/err.h>
#include <openssl/evp.h>
#include <openssl/opensslv.h>
#include <openssl/pem.h>
#include <openssl/pkcs12.h>
#include <openssl/rand.h>
#include <openssl/rsa.h>
#include <openssl/ssl.h>

#include "crypto/openssl_compat.h"
#include "utilities/utils.h"

struct WriteStringArg {
  std::string out;
  int64_t limit{0};
};

/*****************************************************************************/
/**
 * \par Description:
 *    A writeback handler for the curl library. It handles writing response
 *    data from curl into a string.
 *    https://curl.haxx.se/libcurl/c/CURLOPT_WRITEFUNCTION.html
 *
 */
static size_t writeString(void* contents, size_t size, size_t nmemb, void* userp) {
  assert(contents);
  assert(userp);
  // append the writeback data to the provided string
  auto* arg = static_cast<WriteStringArg*>(userp);
  if (arg->limit > 0) {
    if (arg->out.length() + size * nmemb > static_cast<uint64_t>(arg->limit)) {
      return 0;
    }
  }
  (static_cast<WriteStringArg*>(userp))->out.append(static_cast<char*>(contents), size * nmemb);

  // return size of written data
  return size * nmemb;
}

HttpClient::HttpClient() : user_agent(std::string("Aktualizr/") + AKTUALIZR_VERSION) {
  curl = curl_easy_init();
  if (curl == nullptr) {
    throw std::runtime_error("Could not initialize curl");
  }
  headers = nullptr;
  http_code = 0;

  curlEasySetoptWrapper(curl, CURLOPT_NOSIGNAL, 1L);
  curlEasySetoptWrapper(curl, CURLOPT_TIMEOUT, 60L);
  curlEasySetoptWrapper(curl, CURLOPT_CONNECTTIMEOUT, 60L);

  // let curl use our write function
  curlEasySetoptWrapper(curl, CURLOPT_WRITEFUNCTION, writeString);
  curlEasySetoptWrapper(curl, CURLOPT_WRITEDATA, NULL);

  curlEasySetoptWrapper(curl, CURLOPT_VERBOSE, get_curlopt_verbose());

  headers = curl_slist_append(headers, "Content-Type: application/json");
  headers = curl_slist_append(headers, "Accept: */*");
  curlEasySetoptWrapper(curl, CURLOPT_HTTPHEADER, headers);
  curlEasySetoptWrapper(curl, CURLOPT_USERAGENT, user_agent.c_str());
}

HttpClient::HttpClient(const HttpClient& curl_in) : pkcs11_key(curl_in.pkcs11_key), pkcs11_cert(curl_in.pkcs11_key) {
  curl = curl_easy_duphandle(curl_in.curl);

  struct curl_slist* inlist = curl_in.headers;
  headers = nullptr;
  struct curl_slist* tmp;

  while (inlist != nullptr) {
    tmp = curl_slist_append(headers, inlist->data);

    if (tmp == nullptr) {
      curl_slist_free_all(headers);
      throw std::runtime_error("curl_slist_append returned null");
    }

    headers = tmp;
    inlist = inlist->next;
  }
}

CurlGlobalInitWrapper HttpClient::manageCurlGlobalInit_{};

HttpClient::~HttpClient() {
  curl_slist_free_all(headers);
  curl_easy_cleanup(curl);
}

HttpResponse HttpClient::get(const std::string& url, int64_t maxsize) {
  CURL* curl_get = curl_easy_duphandle(curl);

  // TODO: it is a workaround for an unidentified bug in libcurl. Ideally the bug itself should be fixed.
  if (pkcs11_key) {
    curlEasySetoptWrapper(curl_get, CURLOPT_SSLENGINE, "pkcs11");
    curlEasySetoptWrapper(curl_get, CURLOPT_SSLKEYTYPE, "ENG");
  }

  if (pkcs11_cert) {
    curlEasySetoptWrapper(curl_get, CURLOPT_SSLCERTTYPE, "ENG");
  }

  // Clear POSTFIELDS to remove any lingering references to strings that have
  // probably since been deallocated.
  curlEasySetoptWrapper(curl_get, CURLOPT_POSTFIELDS, "");
  curlEasySetoptWrapper(curl_get, CURLOPT_URL, url.c_str());
  curlEasySetoptWrapper(curl_get, CURLOPT_HTTPGET, 1L);
  if (maxsize >= 0) {
    // it will only take effect if the server declares the size in advance,
    //    writeString callback takes care of the other case
    curlEasySetoptWrapper(curl_get, CURLOPT_MAXFILESIZE_LARGE, maxsize);
  }
  curlEasySetoptWrapper(curl_get, CURLOPT_LOW_SPEED_TIME, speed_limit_time_interval_);
  curlEasySetoptWrapper(curl_get, CURLOPT_LOW_SPEED_LIMIT, speed_limit_bytes_per_sec_);
  LOG_DEBUG << "GET " << url;
  HttpResponse response = perform(curl_get, RETRY_TIMES, maxsize);
  curl_easy_cleanup(curl_get);
  return response;
}

void HttpClient::setCerts(const std::string& ca, CryptoSource ca_source, const std::string& cert,
                          CryptoSource cert_source, const std::string& pkey, CryptoSource pkey_source) {
  curlEasySetoptWrapper(curl, CURLOPT_SSL_VERIFYPEER, 1);
  curlEasySetoptWrapper(curl, CURLOPT_SSL_VERIFYHOST, 2);
  curlEasySetoptWrapper(curl, CURLOPT_USE_SSL, CURLUSESSL_ALL);

  if (ca_source == CryptoSource::kPkcs11) {
    throw std::runtime_error("Accessing CA certificate on PKCS11 devices isn't currently supported");
  }
  std::unique_ptr<TemporaryFile> tmp_ca_file = std_::make_unique<TemporaryFile>("tls-ca");
  tmp_ca_file->PutContents(ca);
  curlEasySetoptWrapper(curl, CURLOPT_CAINFO, tmp_ca_file->Path().c_str());
  tls_ca_file = std::move_if_noexcept(tmp_ca_file);

  if (cert_source == CryptoSource::kPkcs11) {
    curlEasySetoptWrapper(curl, CURLOPT_SSLCERT, cert.c_str());
    curlEasySetoptWrapper(curl, CURLOPT_SSLCERTTYPE, "ENG");
  } else {  // cert_source == CryptoSource::kFile
    std::unique_ptr<TemporaryFile> tmp_cert_file = std_::make_unique<TemporaryFile>("tls-cert");
    tmp_cert_file->PutContents(cert);
    curlEasySetoptWrapper(curl, CURLOPT_SSLCERT, tmp_cert_file->Path().c_str());
    curlEasySetoptWrapper(curl, CURLOPT_SSLCERTTYPE, "PEM");
    tls_cert_file = std::move_if_noexcept(tmp_cert_file);
  }
  pkcs11_cert = (cert_source == CryptoSource::kPkcs11);

  if (pkey_source == CryptoSource::kPkcs11) {
    curlEasySetoptWrapper(curl, CURLOPT_SSLENGINE, "pkcs11");
    curlEasySetoptWrapper(curl, CURLOPT_SSLENGINE_DEFAULT, 1L);
    curlEasySetoptWrapper(curl, CURLOPT_SSLKEY, pkey.c_str());
    curlEasySetoptWrapper(curl, CURLOPT_SSLKEYTYPE, "ENG");
  } else {  // pkey_source == CryptoSource::kFile
    std::unique_ptr<TemporaryFile> tmp_pkey_file = std_::make_unique<TemporaryFile>("tls-pkey");
    tmp_pkey_file->PutContents(pkey);
    curlEasySetoptWrapper(curl, CURLOPT_SSLKEY, tmp_pkey_file->Path().c_str());
    curlEasySetoptWrapper(curl, CURLOPT_SSLKEYTYPE, "PEM");
    tls_pkey_file = std::move_if_noexcept(tmp_pkey_file);
  }
  pkcs11_key = (pkey_source == CryptoSource::kPkcs11);
}

HttpResponse HttpClient::post(const std::string& url, const Json::Value& data) {
  curlEasySetoptWrapper(curl, CURLOPT_URL, url.c_str());
  curlEasySetoptWrapper(curl, CURLOPT_POST, 1);
  std::string data_str = Json::FastWriter().write(data);
  curlEasySetoptWrapper(curl, CURLOPT_POSTFIELDS, data_str.c_str());
  LOG_TRACE << "post request body:" << data;
  return perform(curl, RETRY_TIMES, HttpInterface::kPostRespLimit);
}

HttpResponse HttpClient::put(const std::string& url, const Json::Value& data) {
  CURL* curl_put = curl_easy_duphandle(curl);

  // TODO: it is a workaround for an unidentified bug in libcurl. Ideally the bug itself should be fixed.
  if (pkcs11_key) {
    curlEasySetoptWrapper(curl_put, CURLOPT_SSLENGINE, "pkcs11");
    curlEasySetoptWrapper(curl_put, CURLOPT_SSLKEYTYPE, "ENG");
  }

  if (pkcs11_cert) {
    curlEasySetoptWrapper(curl_put, CURLOPT_SSLCERTTYPE, "ENG");
  }

  curlEasySetoptWrapper(curl_put, CURLOPT_URL, url.c_str());
  std::string data_str = Json::FastWriter().write(data);
  curlEasySetoptWrapper(curl_put, CURLOPT_POSTFIELDS, data_str.c_str());
  curlEasySetoptWrapper(curl_put, CURLOPT_CUSTOMREQUEST, "PUT");
  LOG_TRACE << "put request body:" << data;
  HttpResponse result = perform(curl_put, RETRY_TIMES, HttpInterface::kPutRespLimit);
  curl_easy_cleanup(curl_put);
  return result;
}

HttpResponse HttpClient::perform(CURL* curl_handler, int retry_times, int64_t size_limit) {
  WriteStringArg response_arg;
  response_arg.limit = size_limit;
  curlEasySetoptWrapper(curl_handler, CURLOPT_WRITEDATA, static_cast<void*>(&response_arg));
  CURLcode result = curl_easy_perform(curl_handler);
  curl_easy_getinfo(curl_handler, CURLINFO_RESPONSE_CODE, &http_code);
  HttpResponse response(response_arg.out, http_code, result, (result != CURLE_OK) ? curl_easy_strerror(result) : "");
  if (response.curl_code != CURLE_OK || response.http_status_code >= 500) {
    std::ostringstream error_message;
    error_message << "curl error " << response.curl_code << " (http code " << response.http_status_code
                  << "): " << response.error_message;
    LOG_ERROR << error_message.str();
    if (retry_times != 0) {
      sleep(1);
      response = perform(curl_handler, --retry_times, size_limit);
    }
  }
  LOG_TRACE << "response http code: " << response.http_status_code;
  LOG_TRACE << "response: " << response.body;
  return response;
}

HttpResponse HttpClient::download(const std::string& url, curl_write_callback callback, void* userp, size_t from) {
  CURL* curl_download = curl_easy_duphandle(curl);

  // TODO: it is a workaround for an unidentified bug in libcurl. Ideally the bug itself should be fixed.
  if (pkcs11_key) {
    curlEasySetoptWrapper(curl_download, CURLOPT_SSLENGINE, "pkcs11");
    curlEasySetoptWrapper(curl_download, CURLOPT_SSLKEYTYPE, "ENG");
  }

  if (pkcs11_cert) {
    curlEasySetoptWrapper(curl_download, CURLOPT_SSLCERTTYPE, "ENG");
  }

  curlEasySetoptWrapper(curl_download, CURLOPT_URL, url.c_str());
  curlEasySetoptWrapper(curl_download, CURLOPT_HTTPGET, 1L);
  curlEasySetoptWrapper(curl_download, CURLOPT_FOLLOWLOCATION, 1L);
  curlEasySetoptWrapper(curl_download, CURLOPT_WRITEFUNCTION, callback);
  curlEasySetoptWrapper(curl_download, CURLOPT_WRITEDATA, userp);
  curlEasySetoptWrapper(curl_download, CURLOPT_LOW_SPEED_TIME, speed_limit_time_interval_);
  curlEasySetoptWrapper(curl_download, CURLOPT_LOW_SPEED_LIMIT, speed_limit_bytes_per_sec_);
  curlEasySetoptWrapper(curl_download, CURLOPT_RESUME_FROM, from);

  CURLcode result = curl_easy_perform(curl_download);
  curl_easy_getinfo(curl_download, CURLINFO_RESPONSE_CODE, &http_code);
  HttpResponse response("", http_code, result, (result != CURLE_OK) ? curl_easy_strerror(result) : "");
  curl_easy_cleanup(curl_download);
  return response;
}

// vim: set tabstop=2 shiftwidth=2 expandtab:
<commit_msg>Disable timeout for download operation<commit_after>#include "httpclient.h"
#include "utilities/utils.h"

#include <assert.h>
#include <openssl/crypto.h>
#include <openssl/err.h>
#include <openssl/evp.h>
#include <openssl/opensslv.h>
#include <openssl/pem.h>
#include <openssl/pkcs12.h>
#include <openssl/rand.h>
#include <openssl/rsa.h>
#include <openssl/ssl.h>

#include "crypto/openssl_compat.h"
#include "utilities/utils.h"

struct WriteStringArg {
  std::string out;
  int64_t limit{0};
};

/*****************************************************************************/
/**
 * \par Description:
 *    A writeback handler for the curl library. It handles writing response
 *    data from curl into a string.
 *    https://curl.haxx.se/libcurl/c/CURLOPT_WRITEFUNCTION.html
 *
 */
static size_t writeString(void* contents, size_t size, size_t nmemb, void* userp) {
  assert(contents);
  assert(userp);
  // append the writeback data to the provided string
  auto* arg = static_cast<WriteStringArg*>(userp);
  if (arg->limit > 0) {
    if (arg->out.length() + size * nmemb > static_cast<uint64_t>(arg->limit)) {
      return 0;
    }
  }
  (static_cast<WriteStringArg*>(userp))->out.append(static_cast<char*>(contents), size * nmemb);

  // return size of written data
  return size * nmemb;
}

HttpClient::HttpClient() : user_agent(std::string("Aktualizr/") + AKTUALIZR_VERSION) {
  curl = curl_easy_init();
  if (curl == nullptr) {
    throw std::runtime_error("Could not initialize curl");
  }
  headers = nullptr;
  http_code = 0;

  curlEasySetoptWrapper(curl, CURLOPT_NOSIGNAL, 1L);
  curlEasySetoptWrapper(curl, CURLOPT_TIMEOUT, 60L);
  curlEasySetoptWrapper(curl, CURLOPT_CONNECTTIMEOUT, 60L);

  // let curl use our write function
  curlEasySetoptWrapper(curl, CURLOPT_WRITEFUNCTION, writeString);
  curlEasySetoptWrapper(curl, CURLOPT_WRITEDATA, NULL);

  curlEasySetoptWrapper(curl, CURLOPT_VERBOSE, get_curlopt_verbose());

  headers = curl_slist_append(headers, "Content-Type: application/json");
  headers = curl_slist_append(headers, "Accept: */*");
  curlEasySetoptWrapper(curl, CURLOPT_HTTPHEADER, headers);
  curlEasySetoptWrapper(curl, CURLOPT_USERAGENT, user_agent.c_str());
}

HttpClient::HttpClient(const HttpClient& curl_in) : pkcs11_key(curl_in.pkcs11_key), pkcs11_cert(curl_in.pkcs11_key) {
  curl = curl_easy_duphandle(curl_in.curl);

  struct curl_slist* inlist = curl_in.headers;
  headers = nullptr;
  struct curl_slist* tmp;

  while (inlist != nullptr) {
    tmp = curl_slist_append(headers, inlist->data);

    if (tmp == nullptr) {
      curl_slist_free_all(headers);
      throw std::runtime_error("curl_slist_append returned null");
    }

    headers = tmp;
    inlist = inlist->next;
  }
}

CurlGlobalInitWrapper HttpClient::manageCurlGlobalInit_{};

HttpClient::~HttpClient() {
  curl_slist_free_all(headers);
  curl_easy_cleanup(curl);
}

HttpResponse HttpClient::get(const std::string& url, int64_t maxsize) {
  CURL* curl_get = curl_easy_duphandle(curl);

  // TODO: it is a workaround for an unidentified bug in libcurl. Ideally the bug itself should be fixed.
  if (pkcs11_key) {
    curlEasySetoptWrapper(curl_get, CURLOPT_SSLENGINE, "pkcs11");
    curlEasySetoptWrapper(curl_get, CURLOPT_SSLKEYTYPE, "ENG");
  }

  if (pkcs11_cert) {
    curlEasySetoptWrapper(curl_get, CURLOPT_SSLCERTTYPE, "ENG");
  }

  // Clear POSTFIELDS to remove any lingering references to strings that have
  // probably since been deallocated.
  curlEasySetoptWrapper(curl_get, CURLOPT_POSTFIELDS, "");
  curlEasySetoptWrapper(curl_get, CURLOPT_URL, url.c_str());
  curlEasySetoptWrapper(curl_get, CURLOPT_HTTPGET, 1L);
  if (maxsize >= 0) {
    // it will only take effect if the server declares the size in advance,
    //    writeString callback takes care of the other case
    curlEasySetoptWrapper(curl_get, CURLOPT_MAXFILESIZE_LARGE, maxsize);
  }
  curlEasySetoptWrapper(curl_get, CURLOPT_LOW_SPEED_TIME, speed_limit_time_interval_);
  curlEasySetoptWrapper(curl_get, CURLOPT_LOW_SPEED_LIMIT, speed_limit_bytes_per_sec_);
  LOG_DEBUG << "GET " << url;
  HttpResponse response = perform(curl_get, RETRY_TIMES, maxsize);
  curl_easy_cleanup(curl_get);
  return response;
}

void HttpClient::setCerts(const std::string& ca, CryptoSource ca_source, const std::string& cert,
                          CryptoSource cert_source, const std::string& pkey, CryptoSource pkey_source) {
  curlEasySetoptWrapper(curl, CURLOPT_SSL_VERIFYPEER, 1);
  curlEasySetoptWrapper(curl, CURLOPT_SSL_VERIFYHOST, 2);
  curlEasySetoptWrapper(curl, CURLOPT_USE_SSL, CURLUSESSL_ALL);

  if (ca_source == CryptoSource::kPkcs11) {
    throw std::runtime_error("Accessing CA certificate on PKCS11 devices isn't currently supported");
  }
  std::unique_ptr<TemporaryFile> tmp_ca_file = std_::make_unique<TemporaryFile>("tls-ca");
  tmp_ca_file->PutContents(ca);
  curlEasySetoptWrapper(curl, CURLOPT_CAINFO, tmp_ca_file->Path().c_str());
  tls_ca_file = std::move_if_noexcept(tmp_ca_file);

  if (cert_source == CryptoSource::kPkcs11) {
    curlEasySetoptWrapper(curl, CURLOPT_SSLCERT, cert.c_str());
    curlEasySetoptWrapper(curl, CURLOPT_SSLCERTTYPE, "ENG");
  } else {  // cert_source == CryptoSource::kFile
    std::unique_ptr<TemporaryFile> tmp_cert_file = std_::make_unique<TemporaryFile>("tls-cert");
    tmp_cert_file->PutContents(cert);
    curlEasySetoptWrapper(curl, CURLOPT_SSLCERT, tmp_cert_file->Path().c_str());
    curlEasySetoptWrapper(curl, CURLOPT_SSLCERTTYPE, "PEM");
    tls_cert_file = std::move_if_noexcept(tmp_cert_file);
  }
  pkcs11_cert = (cert_source == CryptoSource::kPkcs11);

  if (pkey_source == CryptoSource::kPkcs11) {
    curlEasySetoptWrapper(curl, CURLOPT_SSLENGINE, "pkcs11");
    curlEasySetoptWrapper(curl, CURLOPT_SSLENGINE_DEFAULT, 1L);
    curlEasySetoptWrapper(curl, CURLOPT_SSLKEY, pkey.c_str());
    curlEasySetoptWrapper(curl, CURLOPT_SSLKEYTYPE, "ENG");
  } else {  // pkey_source == CryptoSource::kFile
    std::unique_ptr<TemporaryFile> tmp_pkey_file = std_::make_unique<TemporaryFile>("tls-pkey");
    tmp_pkey_file->PutContents(pkey);
    curlEasySetoptWrapper(curl, CURLOPT_SSLKEY, tmp_pkey_file->Path().c_str());
    curlEasySetoptWrapper(curl, CURLOPT_SSLKEYTYPE, "PEM");
    tls_pkey_file = std::move_if_noexcept(tmp_pkey_file);
  }
  pkcs11_key = (pkey_source == CryptoSource::kPkcs11);
}

HttpResponse HttpClient::post(const std::string& url, const Json::Value& data) {
  curlEasySetoptWrapper(curl, CURLOPT_URL, url.c_str());
  curlEasySetoptWrapper(curl, CURLOPT_POST, 1);
  std::string data_str = Json::FastWriter().write(data);
  curlEasySetoptWrapper(curl, CURLOPT_POSTFIELDS, data_str.c_str());
  LOG_TRACE << "post request body:" << data;
  return perform(curl, RETRY_TIMES, HttpInterface::kPostRespLimit);
}

HttpResponse HttpClient::put(const std::string& url, const Json::Value& data) {
  CURL* curl_put = curl_easy_duphandle(curl);

  // TODO: it is a workaround for an unidentified bug in libcurl. Ideally the bug itself should be fixed.
  if (pkcs11_key) {
    curlEasySetoptWrapper(curl_put, CURLOPT_SSLENGINE, "pkcs11");
    curlEasySetoptWrapper(curl_put, CURLOPT_SSLKEYTYPE, "ENG");
  }

  if (pkcs11_cert) {
    curlEasySetoptWrapper(curl_put, CURLOPT_SSLCERTTYPE, "ENG");
  }

  curlEasySetoptWrapper(curl_put, CURLOPT_URL, url.c_str());
  std::string data_str = Json::FastWriter().write(data);
  curlEasySetoptWrapper(curl_put, CURLOPT_POSTFIELDS, data_str.c_str());
  curlEasySetoptWrapper(curl_put, CURLOPT_CUSTOMREQUEST, "PUT");
  LOG_TRACE << "put request body:" << data;
  HttpResponse result = perform(curl_put, RETRY_TIMES, HttpInterface::kPutRespLimit);
  curl_easy_cleanup(curl_put);
  return result;
}

HttpResponse HttpClient::perform(CURL* curl_handler, int retry_times, int64_t size_limit) {
  WriteStringArg response_arg;
  response_arg.limit = size_limit;
  curlEasySetoptWrapper(curl_handler, CURLOPT_WRITEDATA, static_cast<void*>(&response_arg));
  CURLcode result = curl_easy_perform(curl_handler);
  curl_easy_getinfo(curl_handler, CURLINFO_RESPONSE_CODE, &http_code);
  HttpResponse response(response_arg.out, http_code, result, (result != CURLE_OK) ? curl_easy_strerror(result) : "");
  if (response.curl_code != CURLE_OK || response.http_status_code >= 500) {
    std::ostringstream error_message;
    error_message << "curl error " << response.curl_code << " (http code " << response.http_status_code
                  << "): " << response.error_message;
    LOG_ERROR << error_message.str();
    if (retry_times != 0) {
      sleep(1);
      response = perform(curl_handler, --retry_times, size_limit);
    }
  }
  LOG_TRACE << "response http code: " << response.http_status_code;
  LOG_TRACE << "response: " << response.body;
  return response;
}

HttpResponse HttpClient::download(const std::string& url, curl_write_callback callback, void* userp, size_t from) {
  CURL* curl_download = curl_easy_duphandle(curl);

  // TODO: it is a workaround for an unidentified bug in libcurl. Ideally the bug itself should be fixed.
  if (pkcs11_key) {
    curlEasySetoptWrapper(curl_download, CURLOPT_SSLENGINE, "pkcs11");
    curlEasySetoptWrapper(curl_download, CURLOPT_SSLKEYTYPE, "ENG");
  }

  if (pkcs11_cert) {
    curlEasySetoptWrapper(curl_download, CURLOPT_SSLCERTTYPE, "ENG");
  }

  curlEasySetoptWrapper(curl_download, CURLOPT_URL, url.c_str());
  curlEasySetoptWrapper(curl_download, CURLOPT_HTTPGET, 1L);
  curlEasySetoptWrapper(curl_download, CURLOPT_FOLLOWLOCATION, 1L);
  curlEasySetoptWrapper(curl_download, CURLOPT_WRITEFUNCTION, callback);
  curlEasySetoptWrapper(curl_download, CURLOPT_WRITEDATA, userp);
  curlEasySetoptWrapper(curl_download, CURLOPT_TIMEOUT, 0);
  curlEasySetoptWrapper(curl_download, CURLOPT_LOW_SPEED_TIME, speed_limit_time_interval_);
  curlEasySetoptWrapper(curl_download, CURLOPT_LOW_SPEED_LIMIT, speed_limit_bytes_per_sec_);
  curlEasySetoptWrapper(curl_download, CURLOPT_RESUME_FROM, from);

  CURLcode result = curl_easy_perform(curl_download);
  curl_easy_getinfo(curl_download, CURLINFO_RESPONSE_CODE, &http_code);
  HttpResponse response("", http_code, result, (result != CURLE_OK) ? curl_easy_strerror(result) : "");
  curl_easy_cleanup(curl_download);
  return response;
}

// vim: set tabstop=2 shiftwidth=2 expandtab:
<|endoftext|>