<commit_before>/****************************************************************************
Copyright (c) 2004, Colorado School of Mines and others. All rights reserved.
This program and accompanying materials are made available under the terms of
the Common Public License - v1.0, which accompanies this distribution, and is
available at http://www.eclipse.org/legal/cpl-v10.html
****************************************************************************/

/****************************************************************************
GlContext JNI glue.
@author Dave Hale, Colorado School of Mines
****************************************************************************/
#ifdef WIN32 // If Microsoft Windows, ...
#define MWIN
#include <windows.h>
#else // Else, assume X Windows, ...
#define XWIN
#include <GL/glx.h>
#endif
#include <GL/gl.h>
#include "jawt_md.h"
#include "../util/jniglue.h"
#include <stdio.h>

static void trace(const char* message) {
  fprintf(stderr,"%s\n",message);
}

// OpenGL context.
class GlContext {
public:
  virtual ~GlContext() {
  }
  virtual jboolean lock(JNIEnv* env) = 0;
  virtual jboolean unlock(JNIEnv* env) = 0;
  virtual jboolean swapBuffers(JNIEnv* env) = 0;
};

// OpenGL context for AWT Canvas.
class GlAwtCanvasContext : public GlContext {
public:
  GlAwtCanvasContext(JNIEnv* env, jobject canvas) {
    _env = env;
    _canvas = env->NewGlobalRef(canvas);
  }
  virtual ~GlAwtCanvasContext() {
    _env->DeleteGlobalRef(_canvas);
  }
  virtual jboolean lock(JNIEnv* env) {
    _awt.version = JAWT_VERSION_1_3;
    if (JAWT_GetAWT(env,&_awt)==JNI_FALSE) {
      trace("GlAwtCanvasContext.lock: cannot get AWT");
      return JNI_FALSE;
    }
    _ds = _awt.GetDrawingSurface(env,_canvas);
    if (_ds==0) {
      trace("GlAwtCanvasContext.lock: cannot get DrawingSurface");
      return JNI_FALSE;
    }
    jint lock = _ds->Lock(_ds);
    if ((lock&JAWT_LOCK_ERROR)!=0) {
      trace("GlAwtCanvasContext.lock: cannot lock DrawingSurface");
      _awt.FreeDrawingSurface(_ds);
      return JNI_FALSE;
    }
    _dsi = _ds->GetDrawingSurfaceInfo(_ds);
    if (_dsi==0) {
      trace("GlAwtCanvasContext.lock: cannot get DrawingSurfaceInfo");
      _ds->Unlock(_ds);
      _awt.FreeDrawingSurface(_ds);
      return JNI_FALSE;
    }
    makeCurrent(env);
    return JNI_TRUE;
  }
  virtual jboolean unlock(JNIEnv*) {
    _ds->FreeDrawingSurfaceInfo(_dsi);
    _ds->Unlock(_ds);
    _awt.FreeDrawingSurface(_ds);
    return JNI_TRUE;
  }
protected:
  JNIEnv* _env; // thread in which context was constructed
  jobject _canvas; // global reference to canvas, useful in any thread
  JAWT _awt;
  JAWT_DrawingSurface* _ds;
  JAWT_DrawingSurfaceInfo* _dsi;
  virtual void makeCurrent(JNIEnv* env) = 0;
};

// Microsoft Windows OpenGL context for AWT Canvas.
#if defined(MWIN)
class WglAwtCanvasContext : public GlAwtCanvasContext {
public:
  WglAwtCanvasContext(JNIEnv* env, jobject canvas) :
    GlAwtCanvasContext(env,canvas),_hglrc(0)
  {
  }
  virtual ~WglAwtCanvasContext() {
  }
  virtual void makeCurrent(JNIEnv*) {
    _dsi_win32 = (JAWT_Win32DrawingSurfaceInfo*)_dsi->platformInfo;
    _hwnd = _dsi_win32->hwnd;
    _hdc = _dsi_win32->hdc;
    if (_hglrc==0) {
      PIXELFORMATDESCRIPTOR pfd;
      ZeroMemory(&pfd,sizeof(pdf));
      pfd.nSize = sizeof(pfd);
      pfd.nVersion = 1;
      pfd.dwFlags =
        PFD_DRAW_TO_WINDOW |
        PFD_SUPPORT_OPENGL |
        PFD_DOUBLEBUFFER;
      pfd.iPixelType = PFD_TYPE_RGBA;
      pfd.cColorBits = 16;
      pfd.cDepthBits = 16;
      pfd.iLayerType = PFD_MAIN_PLANE;
      int format = ChoosePixelFormat(_hdc,&pfd);
      SetPixelFormat(_hdc,format,&pfd);
      _hglrc = wglCreateContext(_hdc);
    }
    wglMakeCurrent(_hdc,_hglrc);
  }
  virtual jboolean swapBuffers(JNIEnv*) {
    SwapBuffers(_hdc);
    return JNI_TRUE;
  }
private:
  JAWT_Win32DrawingSurfaceInfo* _dsi_win32;
  HWND _hwnd;
  HDC _hdc;
  HGLRC _hglrc;
};

// X Windows OpenGL context for AWT Canvas.
#elif defined(XWIN)
class GlxAwtCanvasContext : public GlAwtCanvasContext {
public:
  GlxAwtCanvasContext(JNIEnv* env, jobject canvas) :
    GlAwtCanvasContext(env,canvas),_context(0)
  {
  }
  virtual ~GlxAwtCanvasContext() {
    if (_context!=0)
      glXDestroyContext(_display,_context);
  }
  virtual void makeCurrent(JNIEnv*) {
    _dsi_x11 = (JAWT_X11DrawingSurfaceInfo*)_dsi->platformInfo;
    _display = _dsi_x11->display;
    _drawable = _dsi_x11->drawable;
    if (_context==0) {
      int config[] = {
        GLX_DOUBLEBUFFER,GLX_RGBA,
        GLX_DEPTH_SIZE,16,
        GLX_RED_SIZE,1,
        GLX_GREEN_SIZE,1,
        GLX_BLUE_SIZE,1,
        None};
      XVisualInfo* visualInfo = glXChooseVisual(
        _display,DefaultScreen(_display),config);
      _context = glXCreateContext(_display,visualInfo,0,GL_TRUE);
    }
    glXMakeCurrent(_display,_drawable,_context);
    XFlush(_display);
  }
  virtual jboolean swapBuffers(JNIEnv*) {
    glXSwapBuffers(_display,_drawable);
    return JNI_TRUE;
  }
private:
  JAWT_X11DrawingSurfaceInfo* _dsi_x11;
  Display* _display;
  Drawable _drawable;
  GLXContext _context;
};
#endif

/////////////////////////////////////////////////////////////////////////////
// native methods

extern "C" JNIEXPORT void JNICALL
Java_edu_mines_jves_opengl_GlContext_killGlContext(
  JNIEnv* env, jclass cls,
  jlong peer) {
  JNI_TRY
  GlContext* context = (GlContext*)toPointer(peer);
  delete context;
  JNI_CATCH
}

extern "C" JNIEXPORT jlong JNICALL
Java_edu_mines_jves_opengl_GlContext_makeGlAwtCanvasContext(
  JNIEnv* env, jclass cls,
  jobject canvas) {
  JNI_TRY
#if defined(MWIN)
  GlContext* context = new WglAwtCanvasContext(env,canvas);
#elif defined(XWIN)
  GlContext* context = new GlxAwtCanvasContext(env,canvas);
#endif
  return fromPointer(context);
  JNI_CATCH
}

extern "C" JNIEXPORT jboolean JNICALL
Java_edu_mines_jves_opengl_GlContext_lock(
  JNIEnv* env, jclass cls,
  jlong peer)
{
  JNI_TRY
  GlContext* context = (GlContext*)toPointer(peer);
  return context->lock(env);
  JNI_CATCH
}

extern "C" JNIEXPORT jboolean JNICALL
Java_edu_mines_jves_opengl_GlContext_unlock(
  JNIEnv* env, jclass cls,
  jlong peer)
{
  JNI_TRY
  GlContext* context = (GlContext*)toPointer(peer);
  return context->unlock(env);
  JNI_CATCH
}

extern "C" JNIEXPORT jboolean JNICALL
Java_edu_mines_jves_opengl_GlContext_swapBuffers(
  JNIEnv* env, jclass cls,
  jlong peer)
{
  JNI_TRY
  GlContext* context = (GlContext*)toPointer(peer);
  return context->swapBuffers(env);
  JNI_CATCH
}

extern "C" JNIEXPORT jlong JNICALL
Java_edu_mines_jves_opengl_GlContext_getProcAddress(
  JNIEnv* env, jclass cls,
  jstring jfunctionName)
{
  JNI_TRY
  Jstring functionName(env,jfunctionName);
#if defined(MWIN)
  void (*p)() = wglGetProcAddress(functionName);
#elif defined(XWIN)
  void (*p)() = glXGetProcAddressARB(functionName);
#endif
  return (jlong)(intptr_t)p;
  JNI_CATCH
}
<commit_msg>Added support for Windows.<commit_after>/****************************************************************************
Copyright (c) 2004, Colorado School of Mines and others. All rights reserved.
This program and accompanying materials are made available under the terms of
the Common Public License - v1.0, which accompanies this distribution, and is
available at http://www.eclipse.org/legal/cpl-v10.html
****************************************************************************/

/****************************************************************************
GlContext JNI glue.
@author Dave Hale, Colorado School of Mines
****************************************************************************/
#ifdef WIN32 // If Microsoft Windows, ...
#define MWIN
#include <windows.h>
#else // Else, assume X Windows, ...
#define XWIN
#include <GL/glx.h>
#endif
#include <GL/gl.h>
#include "jawt_md.h"
#include "../util/jniglue.h"
#include <stdio.h>

static void trace(const char* message) {
  fprintf(stderr,"%s\n",message);
}

// OpenGL context.
class GlContext {
public:
  virtual ~GlContext() {
  }
  virtual jboolean lock(JNIEnv* env) = 0;
  virtual jboolean unlock(JNIEnv* env) = 0;
  virtual jboolean swapBuffers(JNIEnv* env) = 0;
};

// OpenGL context for AWT Canvas.
class GlAwtCanvasContext : public GlContext {
public:
  GlAwtCanvasContext(JNIEnv* env, jobject canvas) {
    _env = env;
    _canvas = env->NewGlobalRef(canvas);
  }
  virtual ~GlAwtCanvasContext() {
    _env->DeleteGlobalRef(_canvas);
  }
  virtual jboolean lock(JNIEnv* env) {
    _awt.version = JAWT_VERSION_1_3;
    if (JAWT_GetAWT(env,&_awt)==JNI_FALSE) {
      trace("GlAwtCanvasContext.lock: cannot get AWT");
      return JNI_FALSE;
    }
    _ds = _awt.GetDrawingSurface(env,_canvas);
    if (_ds==0) {
      trace("GlAwtCanvasContext.lock: cannot get DrawingSurface");
      return JNI_FALSE;
    }
    jint lock = _ds->Lock(_ds);
    if ((lock&JAWT_LOCK_ERROR)!=0) {
      trace("GlAwtCanvasContext.lock: cannot lock DrawingSurface");
      _awt.FreeDrawingSurface(_ds);
      return JNI_FALSE;
    }
    _dsi = _ds->GetDrawingSurfaceInfo(_ds);
    if (_dsi==0) {
      trace("GlAwtCanvasContext.lock: cannot get DrawingSurfaceInfo");
      _ds->Unlock(_ds);
      _awt.FreeDrawingSurface(_ds);
      return JNI_FALSE;
    }
    makeCurrent(env);
    return JNI_TRUE;
  }
  virtual jboolean unlock(JNIEnv*) {
    _ds->FreeDrawingSurfaceInfo(_dsi);
    _ds->Unlock(_ds);
    _awt.FreeDrawingSurface(_ds);
    return JNI_TRUE;
  }
protected:
  JNIEnv* _env; // thread in which context was constructed
  jobject _canvas; // global reference to canvas, useful in any thread
  JAWT _awt;
  JAWT_DrawingSurface* _ds;
  JAWT_DrawingSurfaceInfo* _dsi;
  virtual void makeCurrent(JNIEnv* env) = 0;
};

// Microsoft Windows OpenGL context for AWT Canvas.
#if defined(MWIN)
class WglAwtCanvasContext : public GlAwtCanvasContext {
public:
  WglAwtCanvasContext(JNIEnv* env, jobject canvas) :
    GlAwtCanvasContext(env,canvas),_hglrc(0)
  {
  }
  virtual ~WglAwtCanvasContext() {
  }
  virtual void makeCurrent(JNIEnv*) {
    _dsi_win32 = (JAWT_Win32DrawingSurfaceInfo*)_dsi->platformInfo;
    _hwnd = _dsi_win32->hwnd;
    _hdc = _dsi_win32->hdc;
    if (_hglrc==0) {
      PIXELFORMATDESCRIPTOR pfd;
      ZeroMemory(&pfd,sizeof(pfd));
      pfd.nSize = sizeof(pfd);
      pfd.nVersion = 1;
      pfd.dwFlags =
        PFD_DRAW_TO_WINDOW |
        PFD_SUPPORT_OPENGL |
        PFD_DOUBLEBUFFER;
      pfd.iPixelType = PFD_TYPE_RGBA;
      pfd.cColorBits = 16;
      pfd.cDepthBits = 16;
      pfd.iLayerType = PFD_MAIN_PLANE;
      int format = ChoosePixelFormat(_hdc,&pfd);
      SetPixelFormat(_hdc,format,&pfd);
      _hglrc = wglCreateContext(_hdc);
    }
    wglMakeCurrent(_hdc,_hglrc);
  }
  virtual jboolean swapBuffers(JNIEnv*) {
    SwapBuffers(_hdc);
    return JNI_TRUE;
  }
private:
  JAWT_Win32DrawingSurfaceInfo* _dsi_win32;
  HWND _hwnd;
  HDC _hdc;
  HGLRC _hglrc;
};

// X Windows OpenGL context for AWT Canvas.
#elif defined(XWIN)
class GlxAwtCanvasContext : public GlAwtCanvasContext {
public:
  GlxAwtCanvasContext(JNIEnv* env, jobject canvas) :
    GlAwtCanvasContext(env,canvas),_context(0)
  {
  }
  virtual ~GlxAwtCanvasContext() {
    if (_context!=0)
      glXDestroyContext(_display,_context);
  }
  virtual void makeCurrent(JNIEnv*) {
    _dsi_x11 = (JAWT_X11DrawingSurfaceInfo*)_dsi->platformInfo;
    _display = _dsi_x11->display;
    _drawable = _dsi_x11->drawable;
    if (_context==0) {
      int config[] = {
        GLX_DOUBLEBUFFER,GLX_RGBA,
        GLX_DEPTH_SIZE,16,
        GLX_RED_SIZE,1,
        GLX_GREEN_SIZE,1,
        GLX_BLUE_SIZE,1,
        None};
      XVisualInfo* visualInfo = glXChooseVisual(
        _display,DefaultScreen(_display),config);
      _context = glXCreateContext(_display,visualInfo,0,GL_TRUE);
    }
    glXMakeCurrent(_display,_drawable,_context);
    XFlush(_display);
  }
  virtual jboolean swapBuffers(JNIEnv*) {
    glXSwapBuffers(_display,_drawable);
    return JNI_TRUE;
  }
private:
  JAWT_X11DrawingSurfaceInfo* _dsi_x11;
  Display* _display;
  Drawable _drawable;
  GLXContext _context;
};
#endif

/////////////////////////////////////////////////////////////////////////////
// native methods

extern "C" JNIEXPORT void JNICALL
Java_edu_mines_jves_opengl_GlContext_killGlContext(
  JNIEnv* env, jclass cls,
  jlong peer) {
  JNI_TRY
  GlContext* context = (GlContext*)toPointer(peer);
  delete context;
  JNI_CATCH
}

extern "C" JNIEXPORT jlong JNICALL
Java_edu_mines_jves_opengl_GlContext_makeGlAwtCanvasContext(
  JNIEnv* env, jclass cls,
  jobject canvas) {
  JNI_TRY
#if defined(MWIN)
  GlContext* context = new WglAwtCanvasContext(env,canvas);
#elif defined(XWIN)
  GlContext* context = new GlxAwtCanvasContext(env,canvas);
#endif
  return fromPointer(context);
  JNI_CATCH
}

extern "C" JNIEXPORT jboolean JNICALL
Java_edu_mines_jves_opengl_GlContext_lock(
  JNIEnv* env, jclass cls,
  jlong peer)
{
  JNI_TRY
  GlContext* context = (GlContext*)toPointer(peer);
  return context->lock(env);
  JNI_CATCH
}

extern "C" JNIEXPORT jboolean JNICALL
Java_edu_mines_jves_opengl_GlContext_unlock(
  JNIEnv* env, jclass cls,
  jlong peer)
{
  JNI_TRY
  GlContext* context = (GlContext*)toPointer(peer);
  return context->unlock(env);
  JNI_CATCH
}

extern "C" JNIEXPORT jboolean JNICALL
Java_edu_mines_jves_opengl_GlContext_swapBuffers(
  JNIEnv* env, jclass cls,
  jlong peer)
{
  JNI_TRY
  GlContext* context = (GlContext*)toPointer(peer);
  return context->swapBuffers(env);
  JNI_CATCH
}

extern "C" JNIEXPORT jlong JNICALL
Java_edu_mines_jves_opengl_GlContext_getProcAddress(
  JNIEnv* env, jclass cls,
  jstring jfunctionName)
{
  JNI_TRY
  Jstring functionName(env,jfunctionName);
#if defined(MWIN)
  typedef void (*PFV)();
  PFV p = (PFV)wglGetProcAddress(functionName);
#elif defined(XWIN)
  void (*p)() = glXGetProcAddressARB(functionName);
#endif
  return (jlong)(intptr_t)p;
  JNI_CATCH
}
<|endoftext|>


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

#include "flutter/shell/platform/linux/public/flutter_linux/fl_dart_project.h"

#include <gmodule.h>

struct _FlDartProject {
  GObject parent_instance;

  gchar* path;
};

enum { PROP_ASSETS_PATH = 1, PROP_ICU_DATA_PATH, PROP_PATH, PROP_LAST };

G_DEFINE_TYPE(FlDartProject, fl_dart_project, G_TYPE_OBJECT)

static void fl_dart_project_set_path(FlDartProject* self, const gchar* path) {
  g_free(self->path);

  if (g_path_is_absolute(path))
    self->path = g_strdup(path);
  else {
    g_autoptr(GError) error = NULL;
    g_autofree gchar* exe_path = g_file_read_link("/proc/self/exe", &error);
    if (exe_path == NULL)
      g_critical("Failed to determine location of executable: %s",
                 error->message);
    g_autofree gchar* dir = g_path_get_dirname(exe_path);
    self->path = g_build_filename(dir, path, NULL);
  }
}

static void fl_dart_project_set_property(GObject* object,
                                         guint prop_id,
                                         const GValue* value,
                                         GParamSpec* pspec) {
  FlDartProject* self = FL_DART_PROJECT(object);

  switch (prop_id) {
    case PROP_PATH:
      fl_dart_project_set_path(self, g_value_get_string(value));
      break;
    default:
      G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
      break;
  }
}

static void fl_dart_project_get_property(GObject* object,
                                         guint prop_id,
                                         GValue* value,
                                         GParamSpec* pspec) {
  FlDartProject* self = FL_DART_PROJECT(object);

  switch (prop_id) {
    case PROP_ASSETS_PATH:
      g_value_take_string(value, fl_dart_project_get_assets_path(self));
      break;
    case PROP_ICU_DATA_PATH:
      g_value_take_string(value, fl_dart_project_get_icu_data_path(self));
      break;
    case PROP_PATH:
      g_value_set_string(value, self->path);
      break;
    default:
      G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
      break;
  }
}

static void fl_dart_project_dispose(GObject* object) {
  FlDartProject* self = FL_DART_PROJECT(object);

  g_clear_pointer(&self->path, g_free);

  G_OBJECT_CLASS(fl_dart_project_parent_class)->dispose(object);
}

static void fl_dart_project_class_init(FlDartProjectClass* klass) {
  G_OBJECT_CLASS(klass)->set_property = fl_dart_project_set_property;
  G_OBJECT_CLASS(klass)->get_property = fl_dart_project_get_property;
  G_OBJECT_CLASS(klass)->dispose = fl_dart_project_dispose;

  g_object_class_install_property(
      G_OBJECT_CLASS(klass), PROP_ASSETS_PATH,
      g_param_spec_string(
          "assets-path", "assets-path", "Path to Flutter assets", nullptr,
          static_cast<GParamFlags>(G_PARAM_READABLE | G_PARAM_STATIC_STRINGS)));

  g_object_class_install_property(
      G_OBJECT_CLASS(klass), PROP_ICU_DATA_PATH,
      g_param_spec_string(
          "icu-data-path", "icu-data-path", "Path to ICU data", nullptr,
          static_cast<GParamFlags>(G_PARAM_READABLE | G_PARAM_STATIC_STRINGS)));

  g_object_class_install_property(
      G_OBJECT_CLASS(klass), PROP_PATH,
      g_param_spec_string(
          "path", "path", "Path to Flutter project", nullptr,
          static_cast<GParamFlags>(G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY |
                                   G_PARAM_STATIC_STRINGS)));
}

static void fl_dart_project_init(FlDartProject* self) {}

G_MODULE_EXPORT FlDartProject* fl_dart_project_new(const gchar* path) {
  return static_cast<FlDartProject*>(
      g_object_new(fl_dart_project_get_type(), "path", path, nullptr));
}

G_MODULE_EXPORT const gchar* fl_dart_project_get_path(FlDartProject* self) {
  g_return_val_if_fail(FL_IS_DART_PROJECT(self), nullptr);
  return self->path;
}

G_MODULE_EXPORT gchar* fl_dart_project_get_assets_path(FlDartProject* self) {
  g_return_val_if_fail(FL_IS_DART_PROJECT(self), nullptr);
  return g_build_filename(self->path, "flutter_assets", NULL);
}

G_MODULE_EXPORT gchar* fl_dart_project_get_icu_data_path(FlDartProject* self) {
  g_return_val_if_fail(FL_IS_DART_PROJECT(self), nullptr);
  return g_build_filename(self->path, "icudtl.dat", NULL);
}
<commit_msg>Replace usage of NULL with nullptr to match coding style (#18023)<commit_after>// Copyright 2013 The Flutter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "flutter/shell/platform/linux/public/flutter_linux/fl_dart_project.h"

#include <gmodule.h>

struct _FlDartProject {
  GObject parent_instance;

  gchar* path;
};

enum { PROP_ASSETS_PATH = 1, PROP_ICU_DATA_PATH, PROP_PATH, PROP_LAST };

G_DEFINE_TYPE(FlDartProject, fl_dart_project, G_TYPE_OBJECT)

static void fl_dart_project_set_path(FlDartProject* self, const gchar* path) {
  g_free(self->path);

  if (g_path_is_absolute(path))
    self->path = g_strdup(path);
  else {
    g_autoptr(GError) error = nullptr;
    g_autofree gchar* exe_path = g_file_read_link("/proc/self/exe", &error);
    if (exe_path == nullptr)
      g_critical("Failed to determine location of executable: %s",
                 error->message);
    g_autofree gchar* dir = g_path_get_dirname(exe_path);
    self->path = g_build_filename(dir, path, nullptr);
  }
}

static void fl_dart_project_set_property(GObject* object,
                                         guint prop_id,
                                         const GValue* value,
                                         GParamSpec* pspec) {
  FlDartProject* self = FL_DART_PROJECT(object);

  switch (prop_id) {
    case PROP_PATH:
      fl_dart_project_set_path(self, g_value_get_string(value));
      break;
    default:
      G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
      break;
  }
}

static void fl_dart_project_get_property(GObject* object,
                                         guint prop_id,
                                         GValue* value,
                                         GParamSpec* pspec) {
  FlDartProject* self = FL_DART_PROJECT(object);

  switch (prop_id) {
    case PROP_ASSETS_PATH:
      g_value_take_string(value, fl_dart_project_get_assets_path(self));
      break;
    case PROP_ICU_DATA_PATH:
      g_value_take_string(value, fl_dart_project_get_icu_data_path(self));
      break;
    case PROP_PATH:
      g_value_set_string(value, self->path);
      break;
    default:
      G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
      break;
  }
}

static void fl_dart_project_dispose(GObject* object) {
  FlDartProject* self = FL_DART_PROJECT(object);

  g_clear_pointer(&self->path, g_free);

  G_OBJECT_CLASS(fl_dart_project_parent_class)->dispose(object);
}

static void fl_dart_project_class_init(FlDartProjectClass* klass) {
  G_OBJECT_CLASS(klass)->set_property = fl_dart_project_set_property;
  G_OBJECT_CLASS(klass)->get_property = fl_dart_project_get_property;
  G_OBJECT_CLASS(klass)->dispose = fl_dart_project_dispose;

  g_object_class_install_property(
      G_OBJECT_CLASS(klass), PROP_ASSETS_PATH,
      g_param_spec_string(
          "assets-path", "assets-path", "Path to Flutter assets", nullptr,
          static_cast<GParamFlags>(G_PARAM_READABLE | G_PARAM_STATIC_STRINGS)));

  g_object_class_install_property(
      G_OBJECT_CLASS(klass), PROP_ICU_DATA_PATH,
      g_param_spec_string(
          "icu-data-path", "icu-data-path", "Path to ICU data", nullptr,
          static_cast<GParamFlags>(G_PARAM_READABLE | G_PARAM_STATIC_STRINGS)));

  g_object_class_install_property(
      G_OBJECT_CLASS(klass), PROP_PATH,
      g_param_spec_string(
          "path", "path", "Path to Flutter project", nullptr,
          static_cast<GParamFlags>(G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY |
                                   G_PARAM_STATIC_STRINGS)));
}

static void fl_dart_project_init(FlDartProject* self) {}

G_MODULE_EXPORT FlDartProject* fl_dart_project_new(const gchar* path) {
  return static_cast<FlDartProject*>(
      g_object_new(fl_dart_project_get_type(), "path", path, nullptr));
}

G_MODULE_EXPORT const gchar* fl_dart_project_get_path(FlDartProject* self) {
  g_return_val_if_fail(FL_IS_DART_PROJECT(self), nullptr);
  return self->path;
}

G_MODULE_EXPORT gchar* fl_dart_project_get_assets_path(FlDartProject* self) {
  g_return_val_if_fail(FL_IS_DART_PROJECT(self), nullptr);
  return g_build_filename(self->path, "flutter_assets", nullptr);
}

G_MODULE_EXPORT gchar* fl_dart_project_get_icu_data_path(FlDartProject* self) {
  g_return_val_if_fail(FL_IS_DART_PROJECT(self), nullptr);
  return g_build_filename(self->path, "icudtl.dat", nullptr);
}
<|endoftext|>


<commit_before>/*
 * Molch, an implementation of the axolotl ratchet based on libsodium
 *
 * ISC License
 *
 * Copyright (C) 2015-2016 1984not Security GmbH
 * Author: Max Bruckner (FSMaxB)
 *
 * Permission to use, copy, modify, and/or distribute this software for any
 * purpose with or without fee is hereby granted, provided that the above
 * copyright notice and this permission notice appear in all copies.
 *
 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 */

#include <exception>
#include <iterator>

#include "constants.h"
#include "conversation.hpp"
#include "molch.h"
#include "packet.hpp"
#include "header.hpp"
#include "molch-exception.hpp"
#include "destroyers.hpp"

namespace Molch {
	Conversation& Conversation::move(Conversation&& conversation) {
		this->id = conversation.id;
		this->ratchet = std::move(conversation.ratchet);

		return *this;
	}

	Conversation::Conversation(Conversation&& conversation) {
		this->move(std::move(conversation));
	}

	Conversation& Conversation::operator=(Conversation&& conversation) {
		return this->move(std::move(conversation));
	}

	/*
	 * Create a new conversation.
	 */
	void Conversation::create(
			const PrivateKey& our_private_identity,
			const PublicKey& our_public_identity,
			const PublicKey& their_public_identity,
			const PrivateKey& our_private_ephemeral,
			const PublicKey& our_public_ephemeral,
			const PublicKey& their_public_ephemeral) {
		//check input
		if (our_private_identity.empty
				|| our_public_identity.empty
				|| their_public_identity.empty
				|| our_public_ephemeral.empty
				|| our_public_ephemeral.empty
				|| their_public_ephemeral.empty) {
			throw Exception{status_type::INVALID_INPUT, "Invalid input for conversation_create."};
		}

		//create random id
		this->id.fillRandom();

		this->ratchet = std::make_unique<Ratchet>(
				our_private_identity,
				our_public_identity,
				their_public_identity,
				our_private_ephemeral,
				our_public_ephemeral,
				their_public_ephemeral);
	}

	Conversation::Conversation(
			const PrivateKey& our_private_identity,
			const PublicKey& our_public_identity,
			const PublicKey& their_public_identity,
			const PrivateKey& our_private_ephemeral,
			const PublicKey& our_public_ephemeral,
			const PublicKey& their_public_ephemeral) {
		this->create(
			our_private_identity,
			our_public_identity,
			their_public_identity,
			our_private_ephemeral,
			our_public_ephemeral,
			their_public_ephemeral);
	}

	/*
	 * Start a new conversation where we are the sender.
	 *
	 * Don't forget to destroy the return status with return_status_destroy_errors()
	 * if an error has occurred.
	 */
	Conversation::Conversation(
			const Buffer& message, //message we want to send to the receiver
			Buffer& packet, //output
			const PublicKey& sender_public_identity, //who is sending this message?
			const PrivateKey& sender_private_identity,
			const PublicKey& receiver_public_identity,
			Buffer& receiver_prekey_list) { //PREKEY_AMOUNT * PUBLIC_KEY_SIZE
		//check many error conditions
		if (receiver_public_identity.empty
				|| sender_public_identity.empty
				|| sender_private_identity.empty
				|| !receiver_prekey_list.contains((PREKEY_AMOUNT * PUBLIC_KEY_SIZE))) {
			throw Exception{status_type::INVALID_INPUT, "Invalid input to Conversation::Conversation."};
		}

		//create an ephemeral keypair
		PublicKey sender_public_ephemeral;
		PrivateKey sender_private_ephemeral;
		if (crypto_box_keypair(sender_public_ephemeral.data(), sender_private_ephemeral.data()) != 0) {
			throw Exception{status_type::KEYGENERATION_FAILED, "Failed to generate ephemeral keypair."};
		}
		sender_public_ephemeral.empty = false;
		sender_private_ephemeral.empty = false;

		//choose a prekey
		auto prekey_number{randombytes_uniform(PREKEY_AMOUNT)};
		PublicKey receiver_public_prekey;
		receiver_public_prekey.set(
				&receiver_prekey_list.content[prekey_number * PUBLIC_KEY_SIZE],
				PUBLIC_KEY_SIZE);

		//initialize the conversation
		this->create(
				sender_private_identity,
				sender_public_identity,
				receiver_public_identity,
				sender_private_ephemeral,
				sender_public_ephemeral,
				receiver_public_prekey);

		packet = this->send(
				message,
				&sender_public_identity,
				&sender_public_ephemeral,
				&receiver_public_prekey);
	}

	/*
	 * Start a new conversation where we are the receiver.
	 *
	 * Don't forget to destroy the return status with return_status_destroy_errors()
	 * if an error has occurred.
	 */
	Conversation::Conversation(
			const Buffer& packet, //received packet
			Buffer& message, //output
			const PublicKey& receiver_public_identity,
			const PrivateKey& receiver_private_identity,
			PrekeyStore& receiver_prekeys) { //prekeys of the receiver
		uint32_t receive_message_number{0};
		uint32_t previous_receive_message_number{0};

		if (receiver_public_identity.empty
				|| receiver_private_identity.empty) {
			throw Exception{status_type::INVALID_INPUT, "Invalid input to conversation_start_receive_conversation."};
		}

		//get the senders keys and our public prekey from the packet
		PublicKey sender_public_identity;
		PublicKey receiver_public_prekey;
		PublicKey sender_public_ephemeral;
		molch_message_type packet_type;
		uint32_t current_protocol_version;
		uint32_t highest_supported_protocol_version;
		packet_get_metadata_without_verification(
			current_protocol_version,
			highest_supported_protocol_version,
			packet_type,
			packet,
			&sender_public_identity,
			&sender_public_ephemeral,
			&receiver_public_prekey);

		if (packet_type != molch_message_type::PREKEY_MESSAGE) {
			throw Exception{status_type::INVALID_VALUE, "Packet is not a prekey message."};
		}

		//get the private prekey that corresponds to the public prekey used in the message
		PrivateKey receiver_private_prekey;
		receiver_prekeys.getPrekey(receiver_public_prekey, receiver_private_prekey);

		this->create(
				receiver_private_identity,
				receiver_public_identity,
				sender_public_identity,
				receiver_private_prekey,
				receiver_public_prekey,
				sender_public_ephemeral);

		message = this->receive(
				packet,
				receive_message_number,
				previous_receive_message_number);
	}

	/*
	 * Send a message using an existing conversation.
	 *
	 * Don't forget to destroy the return status with return_status_destroy_errors()
	 * if an error has occurred.
	 */
	Buffer Conversation::send(
			const Buffer& message,
			const PublicKey * const public_identity_key, //can be nullptr, if not nullptr, this will be a prekey message
			const PublicKey * const public_ephemeral_key, //can be nullptr, if not nullptr, this will be a prekey message
			const PublicKey * const public_prekey) { //can be nullptr, if not nullptr, this will be a prekey message
		//ensure that either both public keys are nullptr or set
		if (((public_identity_key == nullptr) && (public_prekey != nullptr)) || ((public_prekey == nullptr) && (public_identity_key != nullptr))) {
			throw Exception{status_type::INVALID_INPUT, "Invalid combination of provided key buffers."};
		}

		//check the size of the public keys
		if (((public_identity_key != nullptr) && public_identity_key->empty) || ((public_prekey != nullptr) && public_prekey->empty)) {
			throw Exception{status_type::INCORRECT_BUFFER_SIZE, "Public key output has incorrect size."};
		}

		HeaderKey send_header_key;
		MessageKey send_message_key;
		PublicKey send_ephemeral_key;
		uint32_t send_message_number;
		uint32_t previous_send_message_number;
		this->ratchet->send(
				send_header_key,
				send_message_number,
				previous_send_message_number,
				send_ephemeral_key,
				send_message_key);

		auto header{header_construct(
				send_ephemeral_key,
				send_message_number,
				previous_send_message_number)};

		auto packet_type{molch_message_type::NORMAL_MESSAGE};
		//check if this is a prekey message
		if (public_identity_key != nullptr) {
			packet_type = molch_message_type::PREKEY_MESSAGE;
		}

		return packet_encrypt(
				packet_type,
				header,
				send_header_key,
				message,
				send_message_key,
				public_identity_key,
				public_ephemeral_key,
				public_prekey);
	}

	/*
	 * Try to decrypt a packet with skipped over header and message keys.
	 * This corresponds to "try_skipped_header_and_message_keys" from the
	 * Axolotl protocol description.
	 *
	 * Returns 0, if it was able to decrypt the packet.
	 */
	int Conversation::trySkippedHeaderAndMessageKeys(
			const Buffer& packet,
			Buffer& message,
			uint32_t& receive_message_number,
			uint32_t& previous_receive_message_number) {
		//create buffers

		for (size_t index{0}; index < this->ratchet->skipped_header_and_message_keys.keys.size(); index++) {
			auto& node = this->ratchet->skipped_header_and_message_keys.keys[index];
			Buffer header;
			auto decryption_successful{true};
			try {
				header = packet_decrypt_header(packet, node.header_key);
			} catch (const Exception& exception) {
				decryption_successful = false;
			}
			if (decryption_successful) {
				try {
					message = packet_decrypt_message(packet, node.message_key);
				} catch (const Exception& exception) {
					decryption_successful = false;
				}
				if (decryption_successful) {
					this->ratchet->skipped_header_and_message_keys.keys.erase(std::begin(this->ratchet->skipped_header_and_message_keys.keys) + static_cast<ptrdiff_t>(index));
					index--;

					PublicKey their_signed_public_ephemeral;
					header_extract(
							their_signed_public_ephemeral,
							receive_message_number,
							previous_receive_message_number,
							header);
					return static_cast<int>(status_type::SUCCESS);
				}
			}
		}

		return static_cast<int>(status_type::NOT_FOUND);
	}

	/*
	 * Receive and decrypt a message using an existing conversation.
	 *
	 * Don't forget to destroy the return status with return_status_destroy_errors()
	 * if an error has occurred.
	 */
	Buffer Conversation::receive(
			const Buffer& packet, //received packet
			uint32_t& receive_message_number,
			uint32_t& previous_receive_message_number) {
		try {
			auto decryptable{true};

			Buffer message;
			auto status{trySkippedHeaderAndMessageKeys(
					packet,
					message,
					receive_message_number,
					previous_receive_message_number)};
			if (status == static_cast<int>(status_type::SUCCESS)) {
				// found a key and successfully decrypted the message
				return message;
			}

			HeaderKey current_receive_header_key;
			HeaderKey next_receive_header_key;
			this->ratchet->getReceiveHeaderKeys(current_receive_header_key, next_receive_header_key);

			//try to decrypt the packet header with the current receive header key
			Buffer header;
			try {
				header = packet_decrypt_header(packet, current_receive_header_key);
			} catch (const Exception& exception) {
				decryptable = false;
			}
			if (decryptable) {
				this->ratchet->setHeaderDecryptability(Ratchet::HeaderDecryptability::CURRENT_DECRYPTABLE);
			} else {
				//since this failed, try to decrypt it with the next receive header key
				decryptable = true;
				try {
					header = packet_decrypt_header(packet, next_receive_header_key);
				} catch (const Exception& exception) {
					decryptable = false;
				}
				if (decryptable) {
					this->ratchet->setHeaderDecryptability(Ratchet::HeaderDecryptability::NEXT_DECRYPTABLE);
				} else {
					this->ratchet->setHeaderDecryptability(Ratchet::HeaderDecryptability::UNDECRYPTABLE);
					throw Exception{status_type::DECRYPT_ERROR, "Failed to decrypt the message."};
				}
			}

			//extract data from the header
			PublicKey their_signed_public_ephemeral;
			uint32_t local_receive_message_number;
			uint32_t local_previous_receive_message_number;
			header_extract(
					their_signed_public_ephemeral,
					local_receive_message_number,
					local_previous_receive_message_number,
					header);

			//and now decrypt the message with the message key
			//now we have all the data we need to advance the ratchet
			//so let's do that
			MessageKey message_key;
			this->ratchet->receive(
				message_key,
				their_signed_public_ephemeral,
				local_receive_message_number,
				local_previous_receive_message_number);

			message = packet_decrypt_message(packet, message_key);

			this->ratchet->setLastMessageAuthenticity(true);

			receive_message_number = local_receive_message_number;
			previous_receive_message_number = local_previous_receive_message_number;

			return message;
		} catch (const std::exception& exception) {
			this->ratchet->setLastMessageAuthenticity(false);
			throw exception;
		}
	}

	ProtobufCConversation* Conversation::exportProtobuf(ProtobufPool& pool) const {
		//export the ratchet
		auto exported_conversation{this->ratchet->exportProtobuf(pool)};

		//export the conversation id
		auto id{pool.allocate<unsigned char>(CONVERSATION_ID_SIZE)};
		this->id.copyTo(id, CONVERSATION_ID_SIZE);
		exported_conversation->id.data = id;
		exported_conversation->id.len = CONVERSATION_ID_SIZE;

		return exported_conversation;
	}

	Conversation::Conversation(const ProtobufCConversation& conversation_protobuf) {
		//copy the id
		this->id.set(conversation_protobuf.id.data, conversation_protobuf.id.len);

		//import the ratchet
		this->ratchet = std::make_unique<Ratchet>(conversation_protobuf);
	}

	std::ostream& Conversation::print(std::ostream& stream) const {
		stream << "Conversation-ID:\n";
		this->id.printHex(stream) << "\n";

		return stream;
	}
}
<commit_msg>Fix rethrowing exceptions in conversation.cpp<commit_after>/*
 * Molch, an implementation of the axolotl ratchet based on libsodium
 *
 * ISC License
 *
 * Copyright (C) 2015-2016 1984not Security GmbH
 * Author: Max Bruckner (FSMaxB)
 *
 * Permission to use, copy, modify, and/or distribute this software for any
 * purpose with or without fee is hereby granted, provided that the above
 * copyright notice and this permission notice appear in all copies.
 *
 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 */

#include <exception>
#include <iterator>

#include "constants.h"
#include "conversation.hpp"
#include "molch.h"
#include "packet.hpp"
#include "header.hpp"
#include "molch-exception.hpp"
#include "destroyers.hpp"

namespace Molch {
	Conversation& Conversation::move(Conversation&& conversation) {
		this->id = conversation.id;
		this->ratchet = std::move(conversation.ratchet);

		return *this;
	}

	Conversation::Conversation(Conversation&& conversation) {
		this->move(std::move(conversation));
	}

	Conversation& Conversation::operator=(Conversation&& conversation) {
		return this->move(std::move(conversation));
	}

	/*
	 * Create a new conversation.
	 */
	void Conversation::create(
			const PrivateKey& our_private_identity,
			const PublicKey& our_public_identity,
			const PublicKey& their_public_identity,
			const PrivateKey& our_private_ephemeral,
			const PublicKey& our_public_ephemeral,
			const PublicKey& their_public_ephemeral) {
		//check input
		if (our_private_identity.empty
				|| our_public_identity.empty
				|| their_public_identity.empty
				|| our_public_ephemeral.empty
				|| our_public_ephemeral.empty
				|| their_public_ephemeral.empty) {
			throw Exception{status_type::INVALID_INPUT, "Invalid input for conversation_create."};
		}

		//create random id
		this->id.fillRandom();

		this->ratchet = std::make_unique<Ratchet>(
				our_private_identity,
				our_public_identity,
				their_public_identity,
				our_private_ephemeral,
				our_public_ephemeral,
				their_public_ephemeral);
	}

	Conversation::Conversation(
			const PrivateKey& our_private_identity,
			const PublicKey& our_public_identity,
			const PublicKey& their_public_identity,
			const PrivateKey& our_private_ephemeral,
			const PublicKey& our_public_ephemeral,
			const PublicKey& their_public_ephemeral) {
		this->create(
			our_private_identity,
			our_public_identity,
			their_public_identity,
			our_private_ephemeral,
			our_public_ephemeral,
			their_public_ephemeral);
	}

	/*
	 * Start a new conversation where we are the sender.
	 *
	 * Don't forget to destroy the return status with return_status_destroy_errors()
	 * if an error has occurred.
	 */
	Conversation::Conversation(
			const Buffer& message, //message we want to send to the receiver
			Buffer& packet, //output
			const PublicKey& sender_public_identity, //who is sending this message?
			const PrivateKey& sender_private_identity,
			const PublicKey& receiver_public_identity,
			Buffer& receiver_prekey_list) { //PREKEY_AMOUNT * PUBLIC_KEY_SIZE
		//check many error conditions
		if (receiver_public_identity.empty
				|| sender_public_identity.empty
				|| sender_private_identity.empty
				|| !receiver_prekey_list.contains((PREKEY_AMOUNT * PUBLIC_KEY_SIZE))) {
			throw Exception{status_type::INVALID_INPUT, "Invalid input to Conversation::Conversation."};
		}

		//create an ephemeral keypair
		PublicKey sender_public_ephemeral;
		PrivateKey sender_private_ephemeral;
		if (crypto_box_keypair(sender_public_ephemeral.data(), sender_private_ephemeral.data()) != 0) {
			throw Exception{status_type::KEYGENERATION_FAILED, "Failed to generate ephemeral keypair."};
		}
		sender_public_ephemeral.empty = false;
		sender_private_ephemeral.empty = false;

		//choose a prekey
		auto prekey_number{randombytes_uniform(PREKEY_AMOUNT)};
		PublicKey receiver_public_prekey;
		receiver_public_prekey.set(
				&receiver_prekey_list.content[prekey_number * PUBLIC_KEY_SIZE],
				PUBLIC_KEY_SIZE);

		//initialize the conversation
		this->create(
				sender_private_identity,
				sender_public_identity,
				receiver_public_identity,
				sender_private_ephemeral,
				sender_public_ephemeral,
				receiver_public_prekey);

		packet = this->send(
				message,
				&sender_public_identity,
				&sender_public_ephemeral,
				&receiver_public_prekey);
	}

	/*
	 * Start a new conversation where we are the receiver.
	 *
	 * Don't forget to destroy the return status with return_status_destroy_errors()
	 * if an error has occurred.
	 */
	Conversation::Conversation(
			const Buffer& packet, //received packet
			Buffer& message, //output
			const PublicKey& receiver_public_identity,
			const PrivateKey& receiver_private_identity,
			PrekeyStore& receiver_prekeys) { //prekeys of the receiver
		uint32_t receive_message_number{0};
		uint32_t previous_receive_message_number{0};

		if (receiver_public_identity.empty
				|| receiver_private_identity.empty) {
			throw Exception{status_type::INVALID_INPUT, "Invalid input to conversation_start_receive_conversation."};
		}

		//get the senders keys and our public prekey from the packet
		PublicKey sender_public_identity;
		PublicKey receiver_public_prekey;
		PublicKey sender_public_ephemeral;
		molch_message_type packet_type;
		uint32_t current_protocol_version;
		uint32_t highest_supported_protocol_version;
		packet_get_metadata_without_verification(
			current_protocol_version,
			highest_supported_protocol_version,
			packet_type,
			packet,
			&sender_public_identity,
			&sender_public_ephemeral,
			&receiver_public_prekey);

		if (packet_type != molch_message_type::PREKEY_MESSAGE) {
			throw Exception{status_type::INVALID_VALUE, "Packet is not a prekey message."};
		}

		//get the private prekey that corresponds to the public prekey used in the message
		PrivateKey receiver_private_prekey;
		receiver_prekeys.getPrekey(receiver_public_prekey, receiver_private_prekey);

		this->create(
				receiver_private_identity,
				receiver_public_identity,
				sender_public_identity,
				receiver_private_prekey,
				receiver_public_prekey,
				sender_public_ephemeral);

		message = this->receive(
				packet,
				receive_message_number,
				previous_receive_message_number);
	}

	/*
	 * Send a message using an existing conversation.
	 *
	 * Don't forget to destroy the return status with return_status_destroy_errors()
	 * if an error has occurred.
	 */
	Buffer Conversation::send(
			const Buffer& message,
			const PublicKey * const public_identity_key, //can be nullptr, if not nullptr, this will be a prekey message
			const PublicKey * const public_ephemeral_key, //can be nullptr, if not nullptr, this will be a prekey message
			const PublicKey * const public_prekey) { //can be nullptr, if not nullptr, this will be a prekey message
		//ensure that either both public keys are nullptr or set
		if (((public_identity_key == nullptr) && (public_prekey != nullptr)) || ((public_prekey == nullptr) && (public_identity_key != nullptr))) {
			throw Exception{status_type::INVALID_INPUT, "Invalid combination of provided key buffers."};
		}

		//check the size of the public keys
		if (((public_identity_key != nullptr) && public_identity_key->empty) || ((public_prekey != nullptr) && public_prekey->empty)) {
			throw Exception{status_type::INCORRECT_BUFFER_SIZE, "Public key output has incorrect size."};
		}

		HeaderKey send_header_key;
		MessageKey send_message_key;
		PublicKey send_ephemeral_key;
		uint32_t send_message_number;
		uint32_t previous_send_message_number;
		this->ratchet->send(
				send_header_key,
				send_message_number,
				previous_send_message_number,
				send_ephemeral_key,
				send_message_key);

		auto header{header_construct(
				send_ephemeral_key,
				send_message_number,
				previous_send_message_number)};

		auto packet_type{molch_message_type::NORMAL_MESSAGE};
		//check if this is a prekey message
		if (public_identity_key != nullptr) {
			packet_type = molch_message_type::PREKEY_MESSAGE;
		}

		return packet_encrypt(
				packet_type,
				header,
				send_header_key,
				message,
				send_message_key,
				public_identity_key,
				public_ephemeral_key,
				public_prekey);
	}

	/*
	 * Try to decrypt a packet with skipped over header and message keys.
	 * This corresponds to "try_skipped_header_and_message_keys" from the
	 * Axolotl protocol description.
	 *
	 * Returns 0, if it was able to decrypt the packet.
	 */
	int Conversation::trySkippedHeaderAndMessageKeys(
			const Buffer& packet,
			Buffer& message,
			uint32_t& receive_message_number,
			uint32_t& previous_receive_message_number) {
		//create buffers

		for (size_t index{0}; index < this->ratchet->skipped_header_and_message_keys.keys.size(); index++) {
			auto& node = this->ratchet->skipped_header_and_message_keys.keys[index];
			Buffer header;
			auto decryption_successful{true};
			try {
				header = packet_decrypt_header(packet, node.header_key);
			} catch (const Exception& exception) {
				decryption_successful = false;
			}
			if (decryption_successful) {
				try {
					message = packet_decrypt_message(packet, node.message_key);
				} catch (const Exception& exception) {
					decryption_successful = false;
				}
				if (decryption_successful) {
					this->ratchet->skipped_header_and_message_keys.keys.erase(std::begin(this->ratchet->skipped_header_and_message_keys.keys) + static_cast<ptrdiff_t>(index));
					index--;

					PublicKey their_signed_public_ephemeral;
					header_extract(
							their_signed_public_ephemeral,
							receive_message_number,
							previous_receive_message_number,
							header);
					return static_cast<int>(status_type::SUCCESS);
				}
			}
		}

		return static_cast<int>(status_type::NOT_FOUND);
	}

	/*
	 * Receive and decrypt a message using an existing conversation.
	 *
	 * Don't forget to destroy the return status with return_status_destroy_errors()
	 * if an error has occurred.
	 */
	Buffer Conversation::receive(
			const Buffer& packet, //received packet
			uint32_t& receive_message_number,
			uint32_t& previous_receive_message_number) {
		try {
			auto decryptable{true};

			Buffer message;
			auto status{trySkippedHeaderAndMessageKeys(
					packet,
					message,
					receive_message_number,
					previous_receive_message_number)};
			if (status == static_cast<int>(status_type::SUCCESS)) {
				// found a key and successfully decrypted the message
				return message;
			}

			HeaderKey current_receive_header_key;
			HeaderKey next_receive_header_key;
			this->ratchet->getReceiveHeaderKeys(current_receive_header_key, next_receive_header_key);

			//try to decrypt the packet header with the current receive header key
			Buffer header;
			try {
				header = packet_decrypt_header(packet, current_receive_header_key);
			} catch (const Exception& exception) {
				decryptable = false;
			}
			if (decryptable) {
				this->ratchet->setHeaderDecryptability(Ratchet::HeaderDecryptability::CURRENT_DECRYPTABLE);
			} else {
				//since this failed, try to decrypt it with the next receive header key
				decryptable = true;
				try {
					header = packet_decrypt_header(packet, next_receive_header_key);
				} catch (const Exception& exception) {
					decryptable = false;
				}
				if (decryptable) {
					this->ratchet->setHeaderDecryptability(Ratchet::HeaderDecryptability::NEXT_DECRYPTABLE);
				} else {
					this->ratchet->setHeaderDecryptability(Ratchet::HeaderDecryptability::UNDECRYPTABLE);
					throw Exception{status_type::DECRYPT_ERROR, "Failed to decrypt the message."};
				}
			}

			//extract data from the header
			PublicKey their_signed_public_ephemeral;
			uint32_t local_receive_message_number;
			uint32_t local_previous_receive_message_number;
			header_extract(
					their_signed_public_ephemeral,
					local_receive_message_number,
					local_previous_receive_message_number,
					header);

			//and now decrypt the message with the message key
			//now we have all the data we need to advance the ratchet
			//so let's do that
			MessageKey message_key;
			this->ratchet->receive(
				message_key,
				their_signed_public_ephemeral,
				local_receive_message_number,
				local_previous_receive_message_number);

			message = packet_decrypt_message(packet, message_key);

			this->ratchet->setLastMessageAuthenticity(true);

			receive_message_number = local_receive_message_number;
			previous_receive_message_number = local_previous_receive_message_number;

			return message;
		} catch (const std::exception& exception) {
			this->ratchet->setLastMessageAuthenticity(false);
			throw;
		}
	}

	ProtobufCConversation* Conversation::exportProtobuf(ProtobufPool& pool) const {
		//export the ratchet
		auto exported_conversation{this->ratchet->exportProtobuf(pool)};

		//export the conversation id
		auto id{pool.allocate<unsigned char>(CONVERSATION_ID_SIZE)};
		this->id.copyTo(id, CONVERSATION_ID_SIZE);
		exported_conversation->id.data = id;
		exported_conversation->id.len = CONVERSATION_ID_SIZE;

		return exported_conversation;
	}

	Conversation::Conversation(const ProtobufCConversation& conversation_protobuf) {
		//copy the id
		this->id.set(conversation_protobuf.id.data, conversation_protobuf.id.len);

		//import the ratchet
		this->ratchet = std::make_unique<Ratchet>(conversation_protobuf);
	}

	std::ostream& Conversation::print(std::ostream& stream) const {
		stream << "Conversation-ID:\n";
		this->id.printHex(stream) << "\n";

		return stream;
	}
}
<|endoftext|>


<commit_before><commit_msg>print stack trace to stderr as well as to dialog.  Stacktrace info was shown only in dialog, and chromeos was not getting this as it does not have xmessage. Changed to include stacktrace to the log message before being printed to stderr.<commit_after><|endoftext|>


<commit_before>/*
may need to code against htslib rather than samtools.

For each read in the BAM file:
    calculate the 5' ends of the fragment:
    if last 5' end seen is less than the current 5' end:
        flush buffers, gathering stats on duplicates
    if last 5' end seen is greater than the current 5' end BAM is unsorted and freak out
    create signature and add to buffer

on buffer flush:
    for each signatures:
        skip if count is 1
        add number of reads in signature to histogram of duplicate fragments
        sort into bins by tile
        for each tile, create distance matrix
            increment counts in distance histogram
    clear out signatures hash

for each read we need:
    read name
    chromosome
    position
    a populated mate cigar tag (MC:Z:)
    tlen

we need a method to parse out readnames
we need a method to determine how far apart two readnames are
we need a method to generate a signature
we need a method to expand the cigar strings for the signature
we need a histogram template

we need a small amount of test data

diagnose_dups -i bam_file -o dup_stats
*/

#include "common/Options.hpp"
#include "diagnose_dups/Read.hpp"
#include "diagnose_dups/Signature.hpp"

#include <boost/unordered_map.hpp>

#include <sam.h>
#include <iostream>

using namespace std;

int main(int argc, char** argv) {
    Options opts = Options(argc, argv);

    htsFile *fp = hts_open(opts.vm["input"].as<string>().c_str(), "r");
    if(fp == 0) {
       cerr << "Unable to open " << opts.vm["input"].as<string>() << "\n";
       exit(1);
    }
    bam_hdr_t *header = sam_hdr_read(fp);
    bam1_t *record = bam_init1();

    typedef vector<Read> read_vector;
    typedef boost::unordered_map<Signature, read_vector> signature_map;
    signature_map signatures;

    int32_t last_pos = -1;
    int32_t last_tid = -1;
    int32_t pos_window = 300;

    typedef boost::unordered_map<int, int> histogram;
    histogram dup_insert_sizes;
    histogram nondup_insert_sizes;
    histogram distances;
    histogram number_of_dups;

    while(sam_read1(fp, header, record) >= 0) {
        //skip non-properly paired alignments, they're not going to tell us anything about dups
        //as well as anything secondary, qcfail or supplementary
        if(!(record->core.flag & BAM_FPROPER_PAIR)
                || (record->core.flag & (BAM_FSECONDARY | BAM_FQCFAIL | BAM_FSUPPLEMENTARY) )
                || (record->core.flag & BAM_FREAD2)) {
            continue;
        }
        Read read(record);
        Signature sig(record);
        if(last_pos > -1) {
            //grab iterators
            //iterate over sigs in signatures
            for(signature_map::iterator i = signatures.begin(); i != signatures.end(); ++i) {

                if(last_tid != sig.tid
                        || (last_tid == i->first.tid && (last_pos - pos_window) > i->first.pos)) {
                    if(i->second.size() > 1) {
                        //it's a dup

                        //add up number of dups
                        number_of_dups[i->second.size()] += 1;

                        typedef vector<Read>::iterator read_vec_iter;
                        for(read_vec_iter current_read_iter = i->second.begin(); current_read_iter != i->second.end() - 1; ++current_read_iter) {
                            dup_insert_sizes[abs(current_read_iter->insert_size)] += 1;
                            for(read_vec_iter distance_calc_iter = current_read_iter + 1; distance_calc_iter != i->second.end(); ++distance_calc_iter) {
                                if(current_read_iter->is_on_same_tile(*distance_calc_iter) &&
                                        !(current_read_iter->flowcell == distance_calc_iter->flowcell
                                            && current_read_iter->tile == distance_calc_iter->tile
                                            && current_read_iter->lane == distance_calc_iter->lane
                                            && current_read_iter->x == distance_calc_iter->x
                                            && current_read_iter->y == distance_calc_iter->y)) {
                                    int flow_cell_distance = current_read_iter->distance(*distance_calc_iter);
                                    distances[flow_cell_distance] += 1;
                                }
                            }
                        }
                    }
                    else {
                        nondup_insert_sizes[abs(i->second[0].insert_size)] += 1;
                    }
                    signatures.erase(i);
                }
            }
        }
        signatures[sig].push_back(read);
        last_tid = sig.tid;
        last_pos = sig.pos;
    }
    bam_destroy1(record);
    bam_hdr_destroy(header);
    hts_close(fp);

    cout << "Inter-tile distance\tFrequency\n";
    for(histogram::iterator i = distances.begin(); i != distances.end(); ++i) {
        cout << i->first << "\t" << i->second << "\n";
    }
    return 0;
}
<commit_msg>dump the rest of the stats in the same file. currently split out using csplit on the cmdline<commit_after>/*
may need to code against htslib rather than samtools.

For each read in the BAM file:
    calculate the 5' ends of the fragment:
    if last 5' end seen is less than the current 5' end:
        flush buffers, gathering stats on duplicates
    if last 5' end seen is greater than the current 5' end BAM is unsorted and freak out
    create signature and add to buffer

on buffer flush:
    for each signatures:
        skip if count is 1
        add number of reads in signature to histogram of duplicate fragments
        sort into bins by tile
        for each tile, create distance matrix
            increment counts in distance histogram
    clear out signatures hash

for each read we need:
    read name
    chromosome
    position
    a populated mate cigar tag (MC:Z:)
    tlen

we need a method to parse out readnames
we need a method to determine how far apart two readnames are
we need a method to generate a signature
we need a method to expand the cigar strings for the signature
we need a histogram template

we need a small amount of test data

diagnose_dups -i bam_file -o dup_stats
*/

#include "common/Options.hpp"
#include "diagnose_dups/Read.hpp"
#include "diagnose_dups/Signature.hpp"

#include <boost/unordered_map.hpp>

#include <sam.h>
#include <iostream>

using namespace std;

int main(int argc, char** argv) {
    Options opts = Options(argc, argv);

    htsFile *fp = hts_open(opts.vm["input"].as<string>().c_str(), "r");
    if(fp == 0) {
       cerr << "Unable to open " << opts.vm["input"].as<string>() << "\n";
       exit(1);
    }
    bam_hdr_t *header = sam_hdr_read(fp);
    bam1_t *record = bam_init1();

    typedef vector<Read> read_vector;
    typedef boost::unordered_map<Signature, read_vector> signature_map;
    signature_map signatures;

    int32_t last_pos = -1;
    int32_t last_tid = -1;
    int32_t pos_window = 300;

    typedef boost::unordered_map<int, int> histogram;
    histogram dup_insert_sizes;
    histogram nondup_insert_sizes;
    histogram distances;
    histogram number_of_dups;

    while(sam_read1(fp, header, record) >= 0) {
        //skip non-properly paired alignments, they're not going to tell us anything about dups
        //as well as anything secondary, qcfail or supplementary
        if(!(record->core.flag & BAM_FPROPER_PAIR)
                || (record->core.flag & (BAM_FSECONDARY | BAM_FQCFAIL | BAM_FSUPPLEMENTARY) )
                || (record->core.flag & BAM_FREAD2)) {
            continue;
        }
        Read read(record);
        Signature sig(record);
        if(last_pos > -1) {
            //grab iterators
            //iterate over sigs in signatures
            for(signature_map::iterator i = signatures.begin(); i != signatures.end(); ++i) {

                if(last_tid != sig.tid
                        || (last_tid == i->first.tid && (last_pos - pos_window) > i->first.pos)) {
                    if(i->second.size() > 1) {
                        //it's a dup

                        //add up number of dups
                        number_of_dups[i->second.size()] += 1;

                        typedef vector<Read>::iterator read_vec_iter;
                        for(read_vec_iter current_read_iter = i->second.begin(); current_read_iter != i->second.end() - 1; ++current_read_iter) {
                            dup_insert_sizes[abs(current_read_iter->insert_size)] += 1;
                            nondup_insert_sizes[abs(current_read_iter->insert_size)] += 0;
                            for(read_vec_iter distance_calc_iter = current_read_iter + 1; distance_calc_iter != i->second.end(); ++distance_calc_iter) {
                                if(current_read_iter->is_on_same_tile(*distance_calc_iter) &&
                                        !(current_read_iter->flowcell == distance_calc_iter->flowcell
                                            && current_read_iter->tile == distance_calc_iter->tile
                                            && current_read_iter->lane == distance_calc_iter->lane
                                            && current_read_iter->x == distance_calc_iter->x
                                            && current_read_iter->y == distance_calc_iter->y)) {
                                    int flow_cell_distance = current_read_iter->distance(*distance_calc_iter);
                                    distances[flow_cell_distance] += 1;
                                }
                            }
                        }
                    }
                    else {
                        nondup_insert_sizes[abs(i->second[0].insert_size)] += 1;
                        dup_insert_sizes[abs(i->second[0].insert_size)] += 0;
                    }
                    signatures.erase(i);
                }
            }
        }
        signatures[sig].push_back(read);
        last_tid = sig.tid;
        last_pos = sig.pos;
    }
    bam_destroy1(record);
    bam_hdr_destroy(header);
    hts_close(fp);

    cout << "Inter-tile distance\tFrequency\n";
    for(histogram::iterator i = distances.begin(); i != distances.end(); ++i) {
        cout << i->first << "\t" << i->second << "\n";
    }
    cout << "\n";

    cout << "Number of dups at location\tFrequency\n";
    for(histogram::iterator i = number_of_dups.begin(); i != number_of_dups.end(); ++i) {
        cout << i->first << "\t" << i->second << "\n";
    }
    cout << "\n";

    cout << "Size\tUniq frequency\tDup Frequency\n";
    for(histogram::iterator i = nondup_insert_sizes.begin(); i != nondup_insert_sizes.end(); ++i) {
        cout << i->first << "\t" << i->second << "\t" << dup_insert_sizes[i->first] << "\n";
    }

    return 0;
}
<|endoftext|>


<commit_before>// @(#)root/treeplayer:$Id$
// Author: Luca Giommi   22/08/16

/*************************************************************************
 * Copyright (C) 1995-2016, Rene Brun and Fons Rademakers.               *
 * All rights reserved.                                                  *
 *                                                                       *
 * For the licensing terms see $ROOTSYS/LICENSE.                         *
 * For the list of contributors see $ROOTSYS/README/CREDITS.             *
 *************************************************************************/

#include "TSimpleAnalysis.h"

#include "TFile.h"
#include "TChain.h"
#include "TH1.h"
#include "TError.h"
#include "TKey.h"

#include <string>
#include <fstream>
#include <vector>
#include <map>
#include <iostream>

/** \class TSimpleAnalysis

A TSimpleAnalysis object creates histograms from a TChain. These histograms
are stored to an output file. The histogrammed (TTreeFormula) expressions,
their cuts, the input and output files are configured through a simple config
file that allows comments starting with '#'.
Here an example of configuration file:
```
# This is an example of configuration file
file_output.root   #the output file in which histograms are stored

# The next line has the name of the tree of the input data. It is
# optional if there is exactly one tree in the first input file.
ntuple   #name of the input tree

# The lines of the next block correspond to .root input files that
# contain the tree
hsimple1.root   #first .root input file
hsimple2.root   #second .root input file

# The next block is composed by lines that allow to configure the
# histograms. They have the following syntax:
# NAME = EXPRESSION if CUT
# which corresponds to chain->Draw("EXPRESSION >> NAME", "CUT")
# i.e. it will create a histogram called NAME and store it in
# file_output.root.
# "if CUT" is optional
hpx=px if px<-3   #first histogram
hpxpy=px:py    #second histogram

# End of the configuration file
```
*/

////////////////////////////////////////////////////////////////////////////////
/// Delete comments, leading and trailing white spaces in a string.
///
/// param[in] line - line read from the input file

static void DeleteCommentsAndSpaces(std::string& line)
{
   // Delete comments
   std::size_t comment = line.find('#');
   line = line.substr(0, comment);
   // Delete leading spaces
   std::size_t firstNotSpace = line.find_first_not_of(" \t");
   if (firstNotSpace != std::string::npos)
      line = line.substr(firstNotSpace);
   else {
      line.clear();
      return;
   }
   // Delete trailing spaces
   std::size_t lastNotSpace = line.find_last_not_of(" \t");
   if (lastNotSpace != std::string::npos)
      line = line.substr(0, lastNotSpace + 1);
}

////////////////////////////////////////////////////////////////////////////////
/// Handle the expression lines of the input file in order to pass the
/// elements to the members of the object.
///
/// param[in] line - TTreeFormula expression, either read form the configuration
///                  file or passed as expression to the constructor

std::string TSimpleAnalysis::HandleExpressionConfig(const std::string& line)
{
   static const std::string kCutIntr = " if ";

   std::size_t equal = line.find("=");
   if (equal == std::string::npos)
      return "Error: missing '='";

   // Set the histName value
   std::string histName = line.substr(0, equal);
   DeleteCommentsAndSpaces(histName);
   if (histName.empty())
      return "Error: no histName found";

   //Set the histExpression value
   std::size_t cutPos = line.find(kCutIntr, equal);
   std::string histExpression;
   if (cutPos == std::string::npos)
      histExpression = line.substr(equal + 1);
   else
      histExpression = line.substr(equal + 1, cutPos - equal - 1);
   DeleteCommentsAndSpaces(histExpression);
   if (histExpression.empty())
      return "Error: no expression found";

   // Set the histCut value
   std::string histCut;
   if (cutPos != std::string::npos) {
      histCut = line.substr(cutPos + kCutIntr.size());
      DeleteCommentsAndSpaces(histCut);
      if (histCut.empty())
         return "Error: missing cut expression after 'if'";
   }
   else
      histCut = "";

   // Set the map that contains the histName, histExpressions and histCut values
   auto check = fHists.insert(std::make_pair((const std::string&)histName,
                                             std::make_pair(histExpression, histCut)));

   // Check if there are histograms with the same name
   if (!check.second)
      return "Duplicate histogram name";
   return "";
}

////////////////////////////////////////////////////////////////////////////////
/// Constructor for the case of command line parsing arguments. It sets the members
/// of the object.
///
/// \param[in] output - name of the output file
/// \param[in] inputFiles - name of the input .root files
/// \param[in] expressions - what is shown in the histograms
/// \param[in] treeName - name of the tree
/// \throws std::runtime_error in case of ill-formed expressions

TSimpleAnalysis::TSimpleAnalysis(const std::string& output,
                                 const std::vector<std::string>& inputFiles,
                                 const std::vector<std::string>& expressions,
                                 const std::string& treeName = ""):
   fInputFiles(inputFiles), fOutputFile(output), fTreeName(treeName)
{
   for (const std::string& expr: expressions) {
      std::string errMessage = HandleExpressionConfig(expr);
      if (!errMessage.empty())
         throw std::runtime_error(errMessage + " in " +  expr);
   }
}

////////////////////////////////////////////////////////////////////////////////
/// Extract the name of the tree from the first input file when the tree name
/// isn't in the configuration file. Returns the name of the tree.

static std::string ExtractTreeName(std::string& firstInputFile)
{
   std::string treeName = "";
   TFile inputFile (firstInputFile.c_str());

   // Loop over all the keys inside the first input file
   for (TObject* keyAsObj : *inputFile.GetListOfKeys()) {
      TKey* key = dynamic_cast<TKey*>(keyAsObj);
      TClass* clObj = TClass::GetClass(key->GetClassName());
      if (!clObj)
         continue;
      // If the key is releted to and object that inherits from TTree::Class we
      // set treeName with the name of this key if treeName is empty, otherwise
      // error occours
      if (clObj->InheritsFrom(TTree::Class())) {
         if (treeName.empty())
            treeName = key->GetName();
         else {
            ::Error("TSimpleAnalysis::Analyze", "Multiple trees inside %s", firstInputFile.c_str());
            return "";
         }
      }
   }
   // If treeName is yet empty, error occours
   if (treeName.empty()) {
      ::Error("TSimpleAnalysis::Analyze", "No tree inside %s", firstInputFile.c_str());
      return "";
   }
   return treeName;
}

////////////////////////////////////////////////////////////////////////////////
/// Execute all the TChain::Draw() as configured and stores the output histograms.
/// Returns true if the analysis succeeds.

bool TSimpleAnalysis::Run()
{
   // Silence possible error message from TFile constructor if this is a tree name.
   int oldLevel = gErrorIgnoreLevel;
   gErrorIgnoreLevel = kFatal;
   // Disambiguate tree name from first input file:
   // just try to open it, if that works it's an input file.
   if (!fTreeName.empty()) {
      if (TFile* probe = TFile::Open(fTreeName.c_str())) {
         fInputFiles.insert(fInputFiles.begin(), fTreeName);
         fTreeName.clear();
         delete probe;
      }
   }
   // If fTreeName is empty we try to find the name of the tree through reading
   // of the first input file
   if (fTreeName.empty())
      fTreeName = ExtractTreeName(fInputFiles[0]);
   if (fTreeName.empty())  // No tree name found
      return false;
   gErrorIgnoreLevel = oldLevel;

   // Do the chain of the fInputFiles
   TChain chain(fTreeName.c_str());
   for (const std::string& inputfile: fInputFiles)
      chain.Add(inputfile.c_str());

   // Sanity check that we can open the first file
   int errValue = chain.LoadTree(0);
   if (errValue < 0) {
      ::Error("TSimpleAnalysis::Analyze",
              "The chain is not correctly set up, chain.LoadTree(0) returns %d", errValue);
      return false;
   }

   TFile ofile(fOutputFile.c_str(), "RECREATE");
   if (ofile.IsZombie()) {
      ::Error("TSimpleAnalysis::Analyze", "Impossible to create %s", fOutputFile.c_str());
      return false;
   }

   // Save the histograms into the output file
   for (const auto &histo : fHists) {
      const std::string& expr = histo.second.first;
      const std::string& histoName = histo.first;
      const std::string& cut = histo.second.second;

      chain.Draw((expr + ">>" + histoName).c_str(), cut.c_str(), "goff");
      TH1F *ptrHisto = (TH1F*)gDirectory->Get(histoName.c_str());
      if (!ptrHisto)
         return false;
      ptrHisto->Write();
   }
   return true;
}

////////////////////////////////////////////////////////////////////////////////
/// Returns false if not a tree name, otherwise sets the name of the tree.
///
/// param[in] line - line read from the input file

bool TSimpleAnalysis::HandleInputFileNameConfig(const std::string& line)
{
   if (line.find("=") == std::string::npos) {
      fInputFiles.push_back(line);
      return true;
   }
   return false;  // It's an expression
}

////////////////////////////////////////////////////////////////////////////////
/// Skip subsequent empty lines read from fIn and returns the next not empty line.
///
/// param[in] numbLine - number of the input file line

std::string TSimpleAnalysis::GetLine(int& numbLine)
{
   std::string notEmptyLine;

   do {
      getline(fIn, notEmptyLine);
      DeleteCommentsAndSpaces(notEmptyLine);
      numbLine++;
   } while (fIn && notEmptyLine.empty());

   return notEmptyLine;
}

////////////////////////////////////////////////////////////////////////////////
/// This function has the aim of setting the arguments read from the input file.

bool TSimpleAnalysis::Configure()
{
   int readingSection = kReadingOutput;
   std::string line;
   int numbLine = 0;

   // Error if the input file does not exist
   fIn.open(fInputName);
   if (!fIn) {
      ::Error("TSimpleAnalysis", "File %s not found", fInputName.c_str());
      return false;
   }

   while (!fIn.eof()) {
      line = GetLine(numbLine);
      if (line.empty())  // It can happen if fIn.eof()
         continue;
      std::string errMessage;

      switch (readingSection) {

         // Set the name of the output file
      case kReadingOutput:
         fOutputFile = line;
         readingSection++;
         break;

         // Set the name of the tree
      case kReadingTreeName:
         fTreeName = line;
         readingSection++;
         break;

         // Set the input files
      case kReadingInput:
         if (!HandleInputFileNameConfig(line)) {
            // Not an input file name; try to parse as an expression
            errMessage = HandleExpressionConfig(line);
            readingSection = kReadingExpressions;
         }
         break;

         // Set the expressions
      case kReadingExpressions:
         errMessage = HandleExpressionConfig(line);
         break;
      }

      // Report any errors if occour during the configuration proceedings
      if (!errMessage.empty()) {
         ::Error("TSimpleAnalysis::Configure", "%s in %s:%d", errMessage.c_str(),
                 fInputName.c_str(), numbLine);
         return false;
      }
   }  // while (!fIn.eof())
   return true;
}

////////////////////////////////////////////////////////////////////////////////
/// Function that allows to create the TSimpleAnalysis object and execute its
/// Configure and Analyze functions.
///
/// param[in] configurationFile - name of the input file used to create the TSimpleAnalysis object

bool RunSimpleAnalysis (const char* configurationFile) {
   TSimpleAnalysis obj(configurationFile);
   if (!obj.Configure())
      return false;
   if (!obj.Run())
      return false;
   return true;  // Return true only if Configure() and Run() functions were performed correctly
}
<commit_msg>Do not leak file used to extract tree name.<commit_after>// @(#)root/treeplayer:$Id$
// Author: Luca Giommi   22/08/16

/*************************************************************************
 * Copyright (C) 1995-2016, Rene Brun and Fons Rademakers.               *
 * All rights reserved.                                                  *
 *                                                                       *
 * For the licensing terms see $ROOTSYS/LICENSE.                         *
 * For the list of contributors see $ROOTSYS/README/CREDITS.             *
 *************************************************************************/

#include "TSimpleAnalysis.h"

#include "TFile.h"
#include "TChain.h"
#include "TH1.h"
#include "TError.h"
#include "TKey.h"

#include <string>
#include <fstream>
#include <vector>
#include <map>
#include <iostream>

/** \class TSimpleAnalysis

A TSimpleAnalysis object creates histograms from a TChain. These histograms
are stored to an output file. The histogrammed (TTreeFormula) expressions,
their cuts, the input and output files are configured through a simple config
file that allows comments starting with '#'.
Here an example of configuration file:
```
# This is an example of configuration file
file_output.root   #the output file in which histograms are stored

# The next line has the name of the tree of the input data. It is
# optional if there is exactly one tree in the first input file.
ntuple   #name of the input tree

# The lines of the next block correspond to .root input files that
# contain the tree
hsimple1.root   #first .root input file
hsimple2.root   #second .root input file

# The next block is composed by lines that allow to configure the
# histograms. They have the following syntax:
# NAME = EXPRESSION if CUT
# which corresponds to chain->Draw("EXPRESSION >> NAME", "CUT")
# i.e. it will create a histogram called NAME and store it in
# file_output.root.
# "if CUT" is optional
hpx=px if px<-3   #first histogram
hpxpy=px:py    #second histogram

# End of the configuration file
```
*/

////////////////////////////////////////////////////////////////////////////////
/// Delete comments, leading and trailing white spaces in a string.
///
/// param[in] line - line read from the input file

static void DeleteCommentsAndSpaces(std::string& line)
{
   // Delete comments
   std::size_t comment = line.find('#');
   line = line.substr(0, comment);
   // Delete leading spaces
   std::size_t firstNotSpace = line.find_first_not_of(" \t");
   if (firstNotSpace != std::string::npos)
      line = line.substr(firstNotSpace);
   else {
      line.clear();
      return;
   }
   // Delete trailing spaces
   std::size_t lastNotSpace = line.find_last_not_of(" \t");
   if (lastNotSpace != std::string::npos)
      line = line.substr(0, lastNotSpace + 1);
}

////////////////////////////////////////////////////////////////////////////////
/// Handle the expression lines of the input file in order to pass the
/// elements to the members of the object.
///
/// param[in] line - TTreeFormula expression, either read form the configuration
///                  file or passed as expression to the constructor

std::string TSimpleAnalysis::HandleExpressionConfig(const std::string& line)
{
   static const std::string kCutIntr = " if ";

   std::size_t equal = line.find("=");
   if (equal == std::string::npos)
      return "Error: missing '='";

   // Set the histName value
   std::string histName = line.substr(0, equal);
   DeleteCommentsAndSpaces(histName);
   if (histName.empty())
      return "Error: no histName found";

   //Set the histExpression value
   std::size_t cutPos = line.find(kCutIntr, equal);
   std::string histExpression;
   if (cutPos == std::string::npos)
      histExpression = line.substr(equal + 1);
   else
      histExpression = line.substr(equal + 1, cutPos - equal - 1);
   DeleteCommentsAndSpaces(histExpression);
   if (histExpression.empty())
      return "Error: no expression found";

   // Set the histCut value
   std::string histCut;
   if (cutPos != std::string::npos) {
      histCut = line.substr(cutPos + kCutIntr.size());
      DeleteCommentsAndSpaces(histCut);
      if (histCut.empty())
         return "Error: missing cut expression after 'if'";
   }
   else
      histCut = "";

   // Set the map that contains the histName, histExpressions and histCut values
   auto check = fHists.insert(std::make_pair((const std::string&)histName,
                                             std::make_pair(histExpression, histCut)));

   // Check if there are histograms with the same name
   if (!check.second)
      return "Duplicate histogram name";
   return "";
}

////////////////////////////////////////////////////////////////////////////////
/// Constructor for the case of command line parsing arguments. It sets the members
/// of the object.
///
/// \param[in] output - name of the output file
/// \param[in] inputFiles - name of the input .root files
/// \param[in] expressions - what is shown in the histograms
/// \param[in] treeName - name of the tree
/// \throws std::runtime_error in case of ill-formed expressions

TSimpleAnalysis::TSimpleAnalysis(const std::string& output,
                                 const std::vector<std::string>& inputFiles,
                                 const std::vector<std::string>& expressions,
                                 const std::string& treeName = ""):
   fInputFiles(inputFiles), fOutputFile(output), fTreeName(treeName)
{
   for (const std::string& expr: expressions) {
      std::string errMessage = HandleExpressionConfig(expr);
      if (!errMessage.empty())
         throw std::runtime_error(errMessage + " in " +  expr);
   }
}

////////////////////////////////////////////////////////////////////////////////
/// Extract the name of the tree from the first input file when the tree name
/// isn't in the configuration file. Returns the name of the tree.

static std::string ExtractTreeName(std::string& firstInputFile)
{
   std::string treeName = "";
   std::unique_ptr<TFile> inputFile{TFile::Open(firstInputFile.c_str())};

   // Loop over all the keys inside the first input file
   for (TObject* keyAsObj : *inputFile->GetListOfKeys()) {
      TKey* key = dynamic_cast<TKey*>(keyAsObj);
      TClass* clObj = TClass::GetClass(key->GetClassName());
      if (!clObj)
         continue;
      // If the key is releted to and object that inherits from TTree::Class we
      // set treeName with the name of this key if treeName is empty, otherwise
      // error occours
      if (clObj->InheritsFrom(TTree::Class())) {
         if (treeName.empty())
            treeName = key->GetName();
         else {
            ::Error("TSimpleAnalysis::Analyze", "Multiple trees inside %s", firstInputFile.c_str());
            return "";
         }
      }
   }
   // If treeName is yet empty, error occours
   if (treeName.empty()) {
      ::Error("TSimpleAnalysis::Analyze", "No tree inside %s", firstInputFile.c_str());
      return "";
   }
   return treeName;
}

////////////////////////////////////////////////////////////////////////////////
/// Execute all the TChain::Draw() as configured and stores the output histograms.
/// Returns true if the analysis succeeds.

bool TSimpleAnalysis::Run()
{
   // Silence possible error message from TFile constructor if this is a tree name.
   int oldLevel = gErrorIgnoreLevel;
   gErrorIgnoreLevel = kFatal;
   // Disambiguate tree name from first input file:
   // just try to open it, if that works it's an input file.
   if (!fTreeName.empty()) {
      if (TFile* probe = TFile::Open(fTreeName.c_str())) {
         fInputFiles.insert(fInputFiles.begin(), fTreeName);
         fTreeName.clear();
         delete probe;
      }
   }
   // If fTreeName is empty we try to find the name of the tree through reading
   // of the first input file
   if (fTreeName.empty())
      fTreeName = ExtractTreeName(fInputFiles[0]);
   if (fTreeName.empty())  // No tree name found
      return false;
   gErrorIgnoreLevel = oldLevel;

   // Do the chain of the fInputFiles
   TChain chain(fTreeName.c_str());
   for (const std::string& inputfile: fInputFiles)
      chain.Add(inputfile.c_str());

   // Sanity check that we can open the first file
   int errValue = chain.LoadTree(0);
   if (errValue < 0) {
      ::Error("TSimpleAnalysis::Analyze",
              "The chain is not correctly set up, chain.LoadTree(0) returns %d", errValue);
      return false;
   }

   TFile ofile(fOutputFile.c_str(), "RECREATE");
   if (ofile.IsZombie()) {
      ::Error("TSimpleAnalysis::Analyze", "Impossible to create %s", fOutputFile.c_str());
      return false;
   }

   // Save the histograms into the output file
   for (const auto &histo : fHists) {
      const std::string& expr = histo.second.first;
      const std::string& histoName = histo.first;
      const std::string& cut = histo.second.second;

      chain.Draw((expr + ">>" + histoName).c_str(), cut.c_str(), "goff");
      TH1F *ptrHisto = (TH1F*)gDirectory->Get(histoName.c_str());
      if (!ptrHisto)
         return false;
      ptrHisto->Write();
   }
   return true;
}

////////////////////////////////////////////////////////////////////////////////
/// Returns false if not a tree name, otherwise sets the name of the tree.
///
/// param[in] line - line read from the input file

bool TSimpleAnalysis::HandleInputFileNameConfig(const std::string& line)
{
   if (line.find("=") == std::string::npos) {
      fInputFiles.push_back(line);
      return true;
   }
   return false;  // It's an expression
}

////////////////////////////////////////////////////////////////////////////////
/// Skip subsequent empty lines read from fIn and returns the next not empty line.
///
/// param[in] numbLine - number of the input file line

std::string TSimpleAnalysis::GetLine(int& numbLine)
{
   std::string notEmptyLine;

   do {
      getline(fIn, notEmptyLine);
      DeleteCommentsAndSpaces(notEmptyLine);
      numbLine++;
   } while (fIn && notEmptyLine.empty());

   return notEmptyLine;
}

////////////////////////////////////////////////////////////////////////////////
/// This function has the aim of setting the arguments read from the input file.

bool TSimpleAnalysis::Configure()
{
   int readingSection = kReadingOutput;
   std::string line;
   int numbLine = 0;

   // Error if the input file does not exist
   fIn.open(fInputName);
   if (!fIn) {
      ::Error("TSimpleAnalysis", "File %s not found", fInputName.c_str());
      return false;
   }

   while (!fIn.eof()) {
      line = GetLine(numbLine);
      if (line.empty())  // It can happen if fIn.eof()
         continue;
      std::string errMessage;

      switch (readingSection) {

         // Set the name of the output file
      case kReadingOutput:
         fOutputFile = line;
         readingSection++;
         break;

         // Set the name of the tree
      case kReadingTreeName:
         fTreeName = line;
         readingSection++;
         break;

         // Set the input files
      case kReadingInput:
         if (!HandleInputFileNameConfig(line)) {
            // Not an input file name; try to parse as an expression
            errMessage = HandleExpressionConfig(line);
            readingSection = kReadingExpressions;
         }
         break;

         // Set the expressions
      case kReadingExpressions:
         errMessage = HandleExpressionConfig(line);
         break;
      }

      // Report any errors if occour during the configuration proceedings
      if (!errMessage.empty()) {
         ::Error("TSimpleAnalysis::Configure", "%s in %s:%d", errMessage.c_str(),
                 fInputName.c_str(), numbLine);
         return false;
      }
   }  // while (!fIn.eof())
   return true;
}

////////////////////////////////////////////////////////////////////////////////
/// Function that allows to create the TSimpleAnalysis object and execute its
/// Configure and Analyze functions.
///
/// param[in] configurationFile - name of the input file used to create the TSimpleAnalysis object

bool RunSimpleAnalysis (const char* configurationFile) {
   TSimpleAnalysis obj(configurationFile);
   if (!obj.Configure())
      return false;
   if (!obj.Run())
      return false;
   return true;  // Return true only if Configure() and Run() functions were performed correctly
}
<|endoftext|>