<commit_before>/* Copyright (C) 2002 MySQL AB

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

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

   You should have received a copy of the GNU General Public License
   along with this program; if not, write to the Free Software
   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA */

#include "mysql_priv.h"
#include "sp.h"
#include "sp_head.h"
#include "sp_cache.h"

/*
 *
 * DB storage of Stored PROCEDUREs and FUNCTIONs
 *
 */

// *opened=true means we opened ourselves
static int
db_find_routine_aux(THD *thd, int type, char *name, uint namelen,
		    enum thr_lock_type ltype, TABLE **tablep, bool *opened)
{
  DBUG_ENTER("db_find_routine_aux");
  DBUG_PRINT("enter", ("type: %d name: %*s", type, namelen, name));
  TABLE *table;
  byte key[65];			// We know name is 64 and the enum is 1 byte
  uint keylen;
  int ret;

  // Put the key together
  keylen= namelen;
  if (keylen > sizeof(key)-1)
    keylen= sizeof(key)-1;
  memcpy(key, name, keylen);
  memset(key+keylen, (int)' ', sizeof(key)-1 - keylen);	// Pad with space
  key[sizeof(key)-1]= type;
  keylen= sizeof(key);

  for (table= thd->open_tables ; table ; table= table->next)
    if (strcmp(table->table_cache_key, "mysql") == 0 &&
	strcmp(table->real_name, "proc") == 0)
      break;
  if (table)
    *opened= FALSE;
  else
  {
    TABLE_LIST tables;

    memset(&tables, 0, sizeof(tables));
    tables.db= (char*)"mysql";
    tables.real_name= tables.alias= (char*)"proc";
    if (! (table= open_ltable(thd, &tables, ltype)))
    {
      *tablep= NULL;
      DBUG_RETURN(SP_OPEN_TABLE_FAILED);
    }
    *opened= TRUE;
  }

  if (table->file->index_read_idx(table->record[0], 0,
				  key, keylen,
				  HA_READ_KEY_EXACT))
  {
    *tablep= NULL;
    DBUG_RETURN(SP_KEY_NOT_FOUND);
  }
  *tablep= table;

  DBUG_RETURN(SP_OK);
}

static int
db_find_routine(THD *thd, int type, char *name, uint namelen, sp_head **sphp)
{
  DBUG_ENTER("db_find_routine");
  DBUG_PRINT("enter", ("type: %d name: %*s", type, namelen, name));
  extern int yyparse(void *thd);
  TABLE *table;
  const char *defstr;
  int ret;
  bool opened;
  const char *creator;
  longlong created;
  longlong modified;
  bool suid= 1;
  char *ptr;
  uint length;
  char buff[65];
  String str(buff, sizeof(buff), &my_charset_bin);

  ret= db_find_routine_aux(thd, type, name, namelen, TL_READ, &table, &opened);
  if (ret != SP_OK)
    goto done;
  if ((defstr= get_field(&thd->mem_root, table->field[2])) == NULL)
  {
    ret= SP_GET_FIELD_FAILED;
    goto done;
  }

  // Get additional information
  if ((creator= get_field(&thd->mem_root, table->field[3])) == NULL)
  {
    ret= SP_GET_FIELD_FAILED;
    goto done;
  }

  modified= table->field[4]->val_int();
  created= table->field[5]->val_int();

  if ((ptr= get_field(&thd->mem_root, table->field[6])) == NULL)
  {
    ret= SP_GET_FIELD_FAILED;
    goto done;
  }
  if (ptr[0] == 'N')
    suid= 0;

  table->field[7]->val_str(&str, &str);
  ptr= 0;
  if ((length= str.length()))
    ptr= strmake_root(&thd->mem_root, str.ptr(), length);

  if (opened)
  {
    close_thread_tables(thd, 0, 1);
    table= NULL;
  }

  {
    LEX *oldlex= thd->lex;
    enum enum_sql_command oldcmd= thd->lex->sql_command;

    lex_start(thd, (uchar*)defstr, strlen(defstr));
    if (yyparse(thd) || thd->is_fatal_error || thd->lex->sphead == NULL)
    {
      LEX *newlex= thd->lex;
      sp_head *sp= newlex->sphead;

      if (sp)
      {
	if (oldlex != newlex)
	  sp->restore_lex(thd);
	delete sp;
	newlex->sphead= NULL;
      }
      ret= SP_PARSE_ERROR;
    }
    else
    {
      *sphp= thd->lex->sphead;
      (*sphp)->sp_set_info((char *) creator, (uint) strlen(creator),
			   created, modified, suid,
			   ptr, length);
    }
    thd->lex->sql_command= oldcmd;
  }

 done:
  if (table && opened)
    close_thread_tables(thd);
  DBUG_RETURN(ret);
}

static int
db_create_routine(THD *thd, int type,
		  char *name, uint namelen, char *def, uint deflen,
		  char *comment, uint commentlen, bool suid)
{
  DBUG_ENTER("db_create_routine");
  DBUG_PRINT("enter", ("type: %d name: %*s def: %*s", type, namelen, name, deflen, def));
  int ret;
  TABLE *table;
  TABLE_LIST tables;
  char creator[HOSTNAME_LENGTH+USERNAME_LENGTH+2];

  memset(&tables, 0, sizeof(tables));
  tables.db= (char*)"mysql";
  tables.real_name= tables.alias= (char*)"proc";

  if (! (table= open_ltable(thd, &tables, TL_WRITE)))
    ret= SP_OPEN_TABLE_FAILED;
  else
  {
    restore_record(table, default_values); // Get default values for fields
    strxmov(creator, thd->user, "@", thd->host_or_ip, NullS);

    table->field[0]->store(name, namelen, system_charset_info);
    table->field[1]->store((longlong)type);
    table->field[2]->store(def, deflen, system_charset_info);
    table->field[3]->store(creator, (uint)strlen(creator), system_charset_info);
    ((Field_timestamp *)table->field[5])->set_time();
    if (suid)
      table->field[6]->store((longlong)suid);
    if (comment)
      table->field[7]->store(comment, commentlen, system_charset_info);

    if (table->file->write_row(table->record[0]))
      ret= SP_WRITE_ROW_FAILED;
    else
      ret= SP_OK;
  }

  close_thread_tables(thd);
  DBUG_RETURN(ret);
}

static int
db_drop_routine(THD *thd, int type, char *name, uint namelen)
{
  DBUG_ENTER("db_drop_routine");
  DBUG_PRINT("enter", ("type: %d name: %*s", type, namelen, name));
  TABLE *table;
  int ret;
  bool opened;

  ret= db_find_routine_aux(thd, type, name, namelen, TL_WRITE, &table, &opened);
  if (ret == SP_OK)
  {
    if (table->file->delete_row(table->record[0]))
      ret= SP_DELETE_ROW_FAILED;
  }

  if (opened)
    close_thread_tables(thd);
  DBUG_RETURN(ret);
}

/*
 *
 * PROCEDURE
 *
 */

sp_head *
sp_find_procedure(THD *thd, LEX_STRING *name)
{
  DBUG_ENTER("sp_find_procedure");
  sp_head *sp;

  DBUG_PRINT("enter", ("name: %*s", name->length, name->str));

  sp= thd->sp_proc_cache->lookup(name->str, name->length);
  if (! sp)
  {
    if (db_find_routine(thd, TYPE_ENUM_PROCEDURE,
			name->str, name->length, &sp) == SP_OK)
    {
      thd->sp_proc_cache->insert(sp);
    }
  }

  DBUG_RETURN(sp);
}

int
sp_create_procedure(THD *thd, char *name, uint namelen, char *def, uint deflen,
		    char *comment, uint commentlen, bool suid)
{
  DBUG_ENTER("sp_create_procedure");
  DBUG_PRINT("enter", ("name: %*s def: %*s", namelen, name, deflen, def));
  int ret;

  ret= db_create_routine(thd, TYPE_ENUM_PROCEDURE, name, namelen, def, deflen,
			 comment, commentlen, suid);

  DBUG_RETURN(ret);
}

int
sp_drop_procedure(THD *thd, char *name, uint namelen)
{
  DBUG_ENTER("sp_drop_procedure");
  DBUG_PRINT("enter", ("name: %*s", namelen, name));
  sp_head *sp;
  int ret;

  sp= thd->sp_proc_cache->lookup(name, namelen);
  if (sp)
  {
    thd->sp_proc_cache->remove(sp);
    delete sp;
  }
  ret= db_drop_routine(thd, TYPE_ENUM_PROCEDURE, name, namelen);

  DBUG_RETURN(ret);
}

/*
 *
 * FUNCTION
 *
 */

sp_head *
sp_find_function(THD *thd, LEX_STRING *name)
{
  DBUG_ENTER("sp_find_function");
  sp_head *sp;

  DBUG_PRINT("enter", ("name: %*s", name->length, name->str));

  sp= thd->sp_func_cache->lookup(name->str, name->length);
  if (! sp)
  {
    if (db_find_routine(thd, TYPE_ENUM_FUNCTION,
			name->str, name->length, &sp) != SP_OK)
      sp= NULL;
  }
  DBUG_RETURN(sp);
}

int
sp_create_function(THD *thd, char *name, uint namelen, char *def, uint deflen,
		   char *comment, uint commentlen, bool suid)
{
  DBUG_ENTER("sp_create_function");
  DBUG_PRINT("enter", ("name: %*s def: %*s", namelen, name, deflen, def));
  int ret;

  ret= db_create_routine(thd, TYPE_ENUM_FUNCTION, name, namelen, def, deflen,
			 comment, commentlen, suid);

  DBUG_RETURN(ret);
}

int
sp_drop_function(THD *thd, char *name, uint namelen)
{
  DBUG_ENTER("sp_drop_function");
  DBUG_PRINT("enter", ("name: %*s", namelen, name));
  sp_head *sp;
  int ret;

  sp= thd->sp_func_cache->lookup(name, namelen);
  if (sp)
  {
    thd->sp_func_cache->remove(sp);
    delete sp;
  }
  ret= db_drop_routine(thd, TYPE_ENUM_FUNCTION, name, namelen);

  DBUG_RETURN(ret);
}

// QQ Temporary until the function call detection in sql_lex has been reworked.
bool
sp_function_exists(THD *thd, LEX_STRING *name)
{
  TABLE *table;
  bool ret= FALSE;
  bool opened= FALSE;

  if (thd->sp_func_cache->lookup(name->str, name->length) ||
      db_find_routine_aux(thd, TYPE_ENUM_FUNCTION,
			  name->str, name->length, TL_READ,
			  &table, &opened) == SP_OK)
  {
    ret= TRUE;
  }
  if (opened)
    close_thread_tables(thd, 0, 1);
  return ret;
}

byte *
sp_lex_spfuns_key(const byte *ptr, uint *plen, my_bool first)
{
  LEX_STRING *lsp= (LEX_STRING *)ptr;
  *plen= lsp->length;
  return (byte *)lsp->str;
}

void
sp_add_fun_to_lex(LEX *lex, LEX_STRING fun)
{
  if (! hash_search(&lex->spfuns, (byte *)fun.str, fun.length))
  {
    LEX_STRING *ls= (LEX_STRING *)sql_alloc(sizeof(LEX_STRING));
    ls->str= sql_strmake(fun.str, fun.length);
    ls->length= fun.length;

    hash_insert(&lex->spfuns, (byte *)ls);
  }
}

void
sp_merge_funs(LEX *dst, LEX *src)
{
  for (uint i=0 ; i < src->spfuns.records ; i++)
  {
    LEX_STRING *ls= (LEX_STRING *)hash_element(&src->spfuns, i);

    if (! hash_search(&dst->spfuns, (byte *)ls->str, ls->length))
      hash_insert(&dst->spfuns, (byte *)ls);
  }
}

int
sp_cache_functions(THD *thd, LEX *lex)
{
  HASH *h= &lex->spfuns;
  int ret= 0;

  for (uint i=0 ; i < h->records ; i++)
  {
    LEX_STRING *ls= (LEX_STRING *)hash_element(h, i);

    if (! thd->sp_func_cache->lookup(ls->str, ls->length))
    {
      sp_head *sp;
      LEX *oldlex= thd->lex;
      LEX *newlex= new st_lex;

      thd->lex= newlex;
      if (db_find_routine(thd, TYPE_ENUM_FUNCTION, ls->str, ls->length, &sp)
	  == SP_OK)
      {
	ret= sp_cache_functions(thd, newlex);
	delete newlex;
	thd->lex= oldlex;
	if (ret)
	  break;
	thd->sp_func_cache->insert(sp);
      }
      else
      {
	delete newlex;
	thd->lex= oldlex;
	net_printf(thd, ER_SP_DOES_NOT_EXIST, "FUNCTION", ls->str);
	ret= 1;
	break;
      }
    }
  }
  return ret;
}
<commit_msg>Fix bug in sp.cc:db_find_routine() which made tables remain open in some cases.<commit_after>/* Copyright (C) 2002 MySQL AB

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

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

   You should have received a copy of the GNU General Public License
   along with this program; if not, write to the Free Software
   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA */

#include "mysql_priv.h"
#include "sp.h"
#include "sp_head.h"
#include "sp_cache.h"

/*
 *
 * DB storage of Stored PROCEDUREs and FUNCTIONs
 *
 */

// *opened=true means we opened ourselves
static int
db_find_routine_aux(THD *thd, int type, char *name, uint namelen,
		    enum thr_lock_type ltype, TABLE **tablep, bool *opened)
{
  DBUG_ENTER("db_find_routine_aux");
  DBUG_PRINT("enter", ("type: %d name: %*s", type, namelen, name));
  TABLE *table;
  byte key[65];			// We know name is 64 and the enum is 1 byte
  uint keylen;
  int ret;

  // Put the key together
  keylen= namelen;
  if (keylen > sizeof(key)-1)
    keylen= sizeof(key)-1;
  memcpy(key, name, keylen);
  memset(key+keylen, (int)' ', sizeof(key)-1 - keylen);	// Pad with space
  key[sizeof(key)-1]= type;
  keylen= sizeof(key);

  for (table= thd->open_tables ; table ; table= table->next)
    if (strcmp(table->table_cache_key, "mysql") == 0 &&
	strcmp(table->real_name, "proc") == 0)
      break;
  if (table)
    *opened= FALSE;
  else
  {
    TABLE_LIST tables;

    memset(&tables, 0, sizeof(tables));
    tables.db= (char*)"mysql";
    tables.real_name= tables.alias= (char*)"proc";
    if (! (table= open_ltable(thd, &tables, ltype)))
    {
      *tablep= NULL;
      DBUG_RETURN(SP_OPEN_TABLE_FAILED);
    }
    *opened= TRUE;
  }

  if (table->file->index_read_idx(table->record[0], 0,
				  key, keylen,
				  HA_READ_KEY_EXACT))
  {
    *tablep= NULL;
    DBUG_RETURN(SP_KEY_NOT_FOUND);
  }
  *tablep= table;

  DBUG_RETURN(SP_OK);
}

static int
db_find_routine(THD *thd, int type, char *name, uint namelen, sp_head **sphp)
{
  DBUG_ENTER("db_find_routine");
  DBUG_PRINT("enter", ("type: %d name: %*s", type, namelen, name));
  extern int yyparse(void *thd);
  TABLE *table;
  const char *defstr;
  int ret;
  bool opened;
  const char *creator;
  longlong created;
  longlong modified;
  bool suid= 1;
  char *ptr;
  uint length;
  char buff[65];
  String str(buff, sizeof(buff), &my_charset_bin);

  ret= db_find_routine_aux(thd, type, name, namelen, TL_READ, &table, &opened);
  if (ret != SP_OK)
    goto done;
  if ((defstr= get_field(&thd->mem_root, table->field[2])) == NULL)
  {
    ret= SP_GET_FIELD_FAILED;
    goto done;
  }

  // Get additional information
  if ((creator= get_field(&thd->mem_root, table->field[3])) == NULL)
  {
    ret= SP_GET_FIELD_FAILED;
    goto done;
  }

  modified= table->field[4]->val_int();
  created= table->field[5]->val_int();

  if ((ptr= get_field(&thd->mem_root, table->field[6])) == NULL)
  {
    ret= SP_GET_FIELD_FAILED;
    goto done;
  }
  if (ptr[0] == 'N')
    suid= 0;

  table->field[7]->val_str(&str, &str);
  ptr= 0;
  if ((length= str.length()))
    ptr= strmake_root(&thd->mem_root, str.ptr(), length);

  if (opened)
  {
    close_thread_tables(thd, 0, 1);
    opened= FALSE;
  }

  {
    LEX *oldlex= thd->lex;
    enum enum_sql_command oldcmd= thd->lex->sql_command;

    lex_start(thd, (uchar*)defstr, strlen(defstr));
    if (yyparse(thd) || thd->is_fatal_error || thd->lex->sphead == NULL)
    {
      LEX *newlex= thd->lex;
      sp_head *sp= newlex->sphead;

      if (sp)
      {
	if (oldlex != newlex)
	  sp->restore_lex(thd);
	delete sp;
	newlex->sphead= NULL;
      }
      ret= SP_PARSE_ERROR;
    }
    else
    {
      *sphp= thd->lex->sphead;
      (*sphp)->sp_set_info((char *) creator, (uint) strlen(creator),
			   created, modified, suid,
			   ptr, length);
    }
    thd->lex->sql_command= oldcmd;
  }

 done:
  if (opened)
    close_thread_tables(thd);
  DBUG_RETURN(ret);
}

static int
db_create_routine(THD *thd, int type,
		  char *name, uint namelen, char *def, uint deflen,
		  char *comment, uint commentlen, bool suid)
{
  DBUG_ENTER("db_create_routine");
  DBUG_PRINT("enter", ("type: %d name: %*s def: %*s", type, namelen, name, deflen, def));
  int ret;
  TABLE *table;
  TABLE_LIST tables;
  char creator[HOSTNAME_LENGTH+USERNAME_LENGTH+2];

  memset(&tables, 0, sizeof(tables));
  tables.db= (char*)"mysql";
  tables.real_name= tables.alias= (char*)"proc";

  if (! (table= open_ltable(thd, &tables, TL_WRITE)))
    ret= SP_OPEN_TABLE_FAILED;
  else
  {
    restore_record(table, default_values); // Get default values for fields
    strxmov(creator, thd->user, "@", thd->host_or_ip, NullS);

    table->field[0]->store(name, namelen, system_charset_info);
    table->field[1]->store((longlong)type);
    table->field[2]->store(def, deflen, system_charset_info);
    table->field[3]->store(creator, (uint)strlen(creator), system_charset_info);
    ((Field_timestamp *)table->field[5])->set_time();
    if (suid)
      table->field[6]->store((longlong)suid);
    if (comment)
      table->field[7]->store(comment, commentlen, system_charset_info);

    if (table->file->write_row(table->record[0]))
      ret= SP_WRITE_ROW_FAILED;
    else
      ret= SP_OK;
  }

  close_thread_tables(thd);
  DBUG_RETURN(ret);
}

static int
db_drop_routine(THD *thd, int type, char *name, uint namelen)
{
  DBUG_ENTER("db_drop_routine");
  DBUG_PRINT("enter", ("type: %d name: %*s", type, namelen, name));
  TABLE *table;
  int ret;
  bool opened;

  ret= db_find_routine_aux(thd, type, name, namelen, TL_WRITE, &table, &opened);
  if (ret == SP_OK)
  {
    if (table->file->delete_row(table->record[0]))
      ret= SP_DELETE_ROW_FAILED;
  }

  if (opened)
    close_thread_tables(thd);
  DBUG_RETURN(ret);
}

/*
 *
 * PROCEDURE
 *
 */

sp_head *
sp_find_procedure(THD *thd, LEX_STRING *name)
{
  DBUG_ENTER("sp_find_procedure");
  sp_head *sp;

  DBUG_PRINT("enter", ("name: %*s", name->length, name->str));

  sp= thd->sp_proc_cache->lookup(name->str, name->length);
  if (! sp)
  {
    if (db_find_routine(thd, TYPE_ENUM_PROCEDURE,
			name->str, name->length, &sp) == SP_OK)
    {
      thd->sp_proc_cache->insert(sp);
    }
  }

  DBUG_RETURN(sp);
}

int
sp_create_procedure(THD *thd, char *name, uint namelen, char *def, uint deflen,
		    char *comment, uint commentlen, bool suid)
{
  DBUG_ENTER("sp_create_procedure");
  DBUG_PRINT("enter", ("name: %*s def: %*s", namelen, name, deflen, def));
  int ret;

  ret= db_create_routine(thd, TYPE_ENUM_PROCEDURE, name, namelen, def, deflen,
			 comment, commentlen, suid);

  DBUG_RETURN(ret);
}

int
sp_drop_procedure(THD *thd, char *name, uint namelen)
{
  DBUG_ENTER("sp_drop_procedure");
  DBUG_PRINT("enter", ("name: %*s", namelen, name));
  sp_head *sp;
  int ret;

  sp= thd->sp_proc_cache->lookup(name, namelen);
  if (sp)
  {
    thd->sp_proc_cache->remove(sp);
    delete sp;
  }
  ret= db_drop_routine(thd, TYPE_ENUM_PROCEDURE, name, namelen);

  DBUG_RETURN(ret);
}

/*
 *
 * FUNCTION
 *
 */

sp_head *
sp_find_function(THD *thd, LEX_STRING *name)
{
  DBUG_ENTER("sp_find_function");
  sp_head *sp;

  DBUG_PRINT("enter", ("name: %*s", name->length, name->str));

  sp= thd->sp_func_cache->lookup(name->str, name->length);
  if (! sp)
  {
    if (db_find_routine(thd, TYPE_ENUM_FUNCTION,
			name->str, name->length, &sp) != SP_OK)
      sp= NULL;
  }
  DBUG_RETURN(sp);
}

int
sp_create_function(THD *thd, char *name, uint namelen, char *def, uint deflen,
		   char *comment, uint commentlen, bool suid)
{
  DBUG_ENTER("sp_create_function");
  DBUG_PRINT("enter", ("name: %*s def: %*s", namelen, name, deflen, def));
  int ret;

  ret= db_create_routine(thd, TYPE_ENUM_FUNCTION, name, namelen, def, deflen,
			 comment, commentlen, suid);

  DBUG_RETURN(ret);
}

int
sp_drop_function(THD *thd, char *name, uint namelen)
{
  DBUG_ENTER("sp_drop_function");
  DBUG_PRINT("enter", ("name: %*s", namelen, name));
  sp_head *sp;
  int ret;

  sp= thd->sp_func_cache->lookup(name, namelen);
  if (sp)
  {
    thd->sp_func_cache->remove(sp);
    delete sp;
  }
  ret= db_drop_routine(thd, TYPE_ENUM_FUNCTION, name, namelen);

  DBUG_RETURN(ret);
}

// QQ Temporary until the function call detection in sql_lex has been reworked.
bool
sp_function_exists(THD *thd, LEX_STRING *name)
{
  TABLE *table;
  bool ret= FALSE;
  bool opened= FALSE;

  if (thd->sp_func_cache->lookup(name->str, name->length) ||
      db_find_routine_aux(thd, TYPE_ENUM_FUNCTION,
			  name->str, name->length, TL_READ,
			  &table, &opened) == SP_OK)
  {
    ret= TRUE;
  }
  if (opened)
    close_thread_tables(thd, 0, 1);
  return ret;
}

byte *
sp_lex_spfuns_key(const byte *ptr, uint *plen, my_bool first)
{
  LEX_STRING *lsp= (LEX_STRING *)ptr;
  *plen= lsp->length;
  return (byte *)lsp->str;
}

void
sp_add_fun_to_lex(LEX *lex, LEX_STRING fun)
{
  if (! hash_search(&lex->spfuns, (byte *)fun.str, fun.length))
  {
    LEX_STRING *ls= (LEX_STRING *)sql_alloc(sizeof(LEX_STRING));
    ls->str= sql_strmake(fun.str, fun.length);
    ls->length= fun.length;

    hash_insert(&lex->spfuns, (byte *)ls);
  }
}

void
sp_merge_funs(LEX *dst, LEX *src)
{
  for (uint i=0 ; i < src->spfuns.records ; i++)
  {
    LEX_STRING *ls= (LEX_STRING *)hash_element(&src->spfuns, i);

    if (! hash_search(&dst->spfuns, (byte *)ls->str, ls->length))
      hash_insert(&dst->spfuns, (byte *)ls);
  }
}

int
sp_cache_functions(THD *thd, LEX *lex)
{
  HASH *h= &lex->spfuns;
  int ret= 0;

  for (uint i=0 ; i < h->records ; i++)
  {
    LEX_STRING *ls= (LEX_STRING *)hash_element(h, i);

    if (! thd->sp_func_cache->lookup(ls->str, ls->length))
    {
      sp_head *sp;
      LEX *oldlex= thd->lex;
      LEX *newlex= new st_lex;

      thd->lex= newlex;
      if (db_find_routine(thd, TYPE_ENUM_FUNCTION, ls->str, ls->length, &sp)
	  == SP_OK)
      {
	ret= sp_cache_functions(thd, newlex);
	delete newlex;
	thd->lex= oldlex;
	if (ret)
	  break;
	thd->sp_func_cache->insert(sp);
      }
      else
      {
	delete newlex;
	thd->lex= oldlex;
	net_printf(thd, ER_SP_DOES_NOT_EXIST, "FUNCTION", ls->str);
	ret= 1;
	break;
      }
    }
  }
  return ret;
}
<|endoftext|>


<commit_before>
#include <iostream>
#include <vector>

using namespace std;

int main() {
	vector<int> v{};
	cout << "Initial Capacity: " << v.capacity() << endl;
	for(int i = 0;i < 100;i++) {
		v.push_back(i);
		cout << "Capacity: " << v.capacity() << endl;
	}

	return 0;
}
<commit_msg>Adding reserve() to vector capacity test<commit_after>
#include <iostream>
#include <vector>

using namespace std;

int main() {
	vector<int> v{};
	v.reserve(17);

	cout << "Initial Capacity: " << v.capacity() << endl;
	for(int i = 0;i < 100;i++) {
		v.push_back(i);
		cout << "Capacity: " << v.capacity() << endl;
	}

	return 0;
}
<|endoftext|>


<commit_before>/*
 * Copyright (c) 2008-2018 the MRtrix3 contributors.
 *
 * 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/
 *
 * MRtrix3 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.
 *
 * For more details, see http://www.mrtrix.org/
 */

#include "command.h"
#include "image.h"

#include <Eigen/Dense>
#include <Eigen/Eigenvalues>

#define DEFAULT_SIZE 5

using namespace MR;
using namespace App;

const char* const dtypes[] = { "float32", "float64", NULL };

void usage ()
{
  SYNOPSIS = "Denoise DWI data and estimate the noise level based on the optimal threshold for PCA";

  DESCRIPTION
    + "DWI data denoising and noise map estimation by exploiting data redundancy in the PCA domain "
    "using the prior knowledge that the eigenspectrum of random covariance matrices is described by "
    "the universal Marchenko Pastur distribution."

    + "Important note: image denoising must be performed as the first step of the image processing pipeline. "
    "The routine will fail if interpolation or smoothing has been applied to the data prior to denoising."

    + "Note that this function does not correct for non-Gaussian noise biases.";

  AUTHOR = "Daan Christiaens (daan.christiaens@kcl.ac.uk) & Jelle Veraart (jelle.veraart@nyumc.org) & J-Donald Tournier (jdtournier@gmail.com)";

  REFERENCES
    + "Veraart, J.; Novikov, D.S.; Christiaens, D.; Ades-aron, B.; Sijbers, J. & Fieremans, E. " // Internal
    "Denoising of diffusion MRI using random matrix theory. "
    "NeuroImage, 2016, 142, 394-406, doi: 10.1016/j.neuroimage.2016.08.016"

    + "Veraart, J.; Fieremans, E. & Novikov, D.S. " // Internal
    "Diffusion MRI noise mapping using random matrix theory. "
    "Magn. Res. Med., 2016, 76(5), 1582-1593, doi: 10.1002/mrm.26059";

  ARGUMENTS
  + Argument ("dwi", "the input diffusion-weighted image.").type_image_in ()

  + Argument ("out", "the output denoised DWI image.").type_image_out ();

  OPTIONS
    + Option ("mask", "only perform computation within the specified binary brain mask image.")
    +   Argument ("image").type_image_in()

    + Option ("extent", "set the window size of the denoising filter. (default = " + str(DEFAULT_SIZE) + "," + str(DEFAULT_SIZE) + "," + str(DEFAULT_SIZE) + ")")
    +   Argument ("window").type_sequence_int ()

    + Option ("noise", "the output noise map.")
    +   Argument ("level").type_image_out()

    + Option ("datatype", "datatype for SVD (float32 or float64).")
    +   Argument ("spec").type_choice(dtypes);

  COPYRIGHT = "Copyright (c) 2016 New York University, University of Antwerp, and the MRtrix3 contributors \n \n"
      "Permission is hereby granted, free of charge, to any non-commercial entity ('Recipient') obtaining a copy of this software and "
      "associated documentation files (the 'Software'), to the Software solely for non-commercial research, including the rights to "
      "use, copy and modify the Software, subject to the following conditions: \n \n"
      "\t 1. The above copyright notice and this permission notice shall be included by Recipient in all copies or substantial portions of "
      "the Software. \n \n"
      "\t 2. THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES"
      "OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE"
      "LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR"
      "IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. \n \n"
      "\t 3. In no event shall NYU be liable for direct, indirect, special, incidental or consequential damages in connection with the Software. "
      "Recipient will defend, indemnify and hold NYU harmless from any claims or liability resulting from the use of the Software by recipient. \n \n"
      "\t 4. Neither anything contained herein nor the delivery of the Software to recipient shall be deemed to grant the Recipient any right or "
      "licenses under any patents or patent application owned by NYU. \n \n"
      "\t 5. The Software may only be used for non-commercial research and may not be used for clinical care. \n \n"
      "\t 6. Any publication by Recipient of research involving the Software shall cite the references listed below.";

}

using value_type = float;

template <class ImageType, typename F = float>
class DenoisingFunctor {
  MEMALIGN(DenoisingFunctor)

public:

  typedef Eigen::Matrix<F, Eigen::Dynamic, Eigen::Dynamic> MatrixX;
  typedef Eigen::Matrix<F, Eigen::Dynamic, 1> VectorX;

  DenoisingFunctor (ImageType& dwi, vector<int> extent, Image<bool>& mask, ImageType& noise)
    : extent {{extent[0]/2, extent[1]/2, extent[2]/2}},
      m (dwi.size(3)),
      n (extent[0]*extent[1]*extent[2]),
      r ((m<n) ? m : n),
      X (m,n),
      Xm (r),
      pos {{0, 0, 0}},
      mask (mask),
      noise (noise)
  { }

  void operator () (ImageType& dwi, ImageType& out)
  {
    // Process voxels in mask only
    if (mask.valid()) {
      assign_pos_of (dwi).to (mask);
      if (!mask.value())
        return;
    }

    // Load data in local window
    load_data (dwi);

    // Compute Eigendecomposition:
    MatrixX XtX (r,r);
    if (m <= n)
      XtX.template triangularView<Eigen::Lower>() = X * X.adjoint();
    else
      XtX.template triangularView<Eigen::Lower>() = X.adjoint() * X;
    Eigen::SelfAdjointEigenSolver<MatrixX> eig (XtX);
    // eigenvalues provide squared singular values, sorted in increasing order:
    VectorX s = eig.eigenvalues();

    // Marchenko-Pastur optimal threshold
    const double lam_r = std::max(double(s[0]), 0.0) / std::max(m,n);
    double clam = 0.0;
    sigma2 = 0.0;
    ssize_t cutoff_p = 0;
    for (ssize_t p = 0; p < r; ++p)     // p+1 is the number of noise components
    {                                   // (as opposed to the paper where p is defined as the number of signal components)
      double lam = std::max(double(s[p]), 0.0) / std::max(m,n);
      clam += lam;
      double gam = double(p+1) / (std::max(m,n) - (r-p-1));
      double sigsq1 = clam / double(p+1);
      double sigsq2 = (lam - lam_r) / (4.0 * std::sqrt(gam));
      // sigsq2 > sigsq1 if signal else noise
      if (sigsq2 < sigsq1) {
        sigma2 = sigsq1;
        cutoff_p = p+1;
      }
    }

    if (cutoff_p > 0) {
      // recombine data using only eigenvectors above threshold:
      s.head (cutoff_p).setZero();
      s.tail (r-cutoff_p).setOnes();
      if (m <= n)
        X.col (n/2) = eig.eigenvectors() * ( s.asDiagonal() * ( eig.eigenvectors().adjoint() * X.col(n/2) ));
      else
        X.col (n/2) = X * ( eig.eigenvectors() * ( s.asDiagonal() * eig.eigenvectors().adjoint().col(n/2) ));
    }

    // Store output
    assign_pos_of(dwi).to(out);
    if (m <= n) {
      for (auto l = Loop (3) (out); l; ++l)
        out.value() = value_type (X(out.index(3), n/2));
    }
    else {
      for (auto l = Loop (3) (out); l; ++l)
        out.value() = value_type (X(out.index(3), n/2));
    }

    // store noise map if requested:
    if (noise.valid()) {
      assign_pos_of(dwi).to(noise);
      noise.value() = value_type (std::sqrt(sigma2));
    }
  }

  void load_data (ImageType& dwi)
  {
    pos[0] = dwi.index(0); pos[1] = dwi.index(1); pos[2] = dwi.index(2);
    X.setZero();
    size_t k = 0;
    for (int z = -extent[2]; z <= extent[2]; z++) {
      dwi.index(2) = wrapindex(z, 2, dwi.size(2));
      for (int y = -extent[1]; y <= extent[1]; y++) {
        dwi.index(1) = wrapindex(y, 1, dwi.size(1));
        for (int x = -extent[0]; x <= extent[0]; x++, k++) {
          dwi.index(0) = wrapindex(x, 0, dwi.size(0));
          X.col(k) = dwi.row(3);
        }
      }
    }

    // reset image position
    dwi.index(0) = pos[0];
    dwi.index(1) = pos[1];
    dwi.index(2) = pos[2];
  }

private:
  const std::array<ssize_t, 3> extent;
  const ssize_t m, n, r;
  MatrixX X;
  VectorX Xm;
  std::array<ssize_t, 3> pos;
  double sigma2;
  Image<bool> mask;
  ImageType noise;

  inline size_t wrapindex(int r, int axis, int max) const {
    int rr = pos[axis] + r;
    if (rr < 0)
      rr = extent[axis] - r;
    if (rr >= max)
      rr = (max-1) - extent[axis] - r;
    return rr;
  }

};

void run ()
{
  auto dwi_in = Image<value_type>::open (argument[0]).with_direct_io(3);

  if (dwi_in.ndim() != 4 || dwi_in.size(3) <= 1)
    throw Exception ("input image must be 4-dimensional");

  Image<bool> mask;
  auto opt = get_options ("mask");
  if (opt.size()) {
    mask = Image<bool>::open (opt[0][0]);
    check_dimensions (mask, dwi_in, 0, 3);
  }

  auto header = Header (dwi_in);
  header.datatype() = DataType::Float32;
  auto dwi_out = Image<value_type>::create (argument[1], header);

  opt = get_options("extent");
  vector<int> extent = { DEFAULT_SIZE, DEFAULT_SIZE, DEFAULT_SIZE };
  if (opt.size()) {
    extent = parse_ints(opt[0][0]);
    if (extent.size() == 1)
      extent = {extent[0], extent[0], extent[0]};
    if (extent.size() != 3)
      throw Exception ("-extent must be either a scalar or a list of length 3");
    for (auto &e : extent)
      if (!(e & 1))
        throw Exception ("-extent must be a (list of) odd numbers");
  }

  Image<value_type> noise;
  opt = get_options("noise");
  if (opt.size()) {
    header.ndim() = 3;
    noise = Image<value_type>::create (opt[0][0], header);
  }

  opt = get_options("datatype");
  if (!opt.size() || (int(opt[0][0]) == 0)) {
    DEBUG("Computing SVD with single precision.");
    DenoisingFunctor< Image<value_type> , float > func (dwi_in, extent, mask, noise);
    ThreadedLoop ("running MP-PCA denoising", dwi_in, 0, 3)
      .run (func, dwi_in, dwi_out);
  }
  else if (int(opt[0][0]) == 1) {
    DEBUG("Computing SVD with double precision.");
    DenoisingFunctor< Image<value_type> , double > func (dwi_in, extent, mask, noise);
    ThreadedLoop ("running MP-PCA denoising", dwi_in, 0, 3)
      .run (func, dwi_in, dwi_out);
  }
  else {
    assert(0);
  }

}

<commit_msg>dwidenoise: code cleanup.<commit_after>/*
 * Copyright (c) 2008-2018 the MRtrix3 contributors.
 *
 * 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/
 *
 * MRtrix3 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.
 *
 * For more details, see http://www.mrtrix.org/
 */

#include "command.h"
#include "image.h"

#include <Eigen/Dense>
#include <Eigen/Eigenvalues>

#define DEFAULT_SIZE 5

using namespace MR;
using namespace App;

const char* const dtypes[] = { "float32", "float64", NULL };

void usage ()
{
  SYNOPSIS = "Denoise DWI data and estimate the noise level based on the optimal threshold for PCA";

  DESCRIPTION
    + "DWI data denoising and noise map estimation by exploiting data redundancy in the PCA domain "
    "using the prior knowledge that the eigenspectrum of random covariance matrices is described by "
    "the universal Marchenko Pastur distribution."

    + "Important note: image denoising must be performed as the first step of the image processing pipeline. "
    "The routine will fail if interpolation or smoothing has been applied to the data prior to denoising."

    + "Note that this function does not correct for non-Gaussian noise biases.";

  AUTHOR = "Daan Christiaens (daan.christiaens@kcl.ac.uk) & "
           "Jelle Veraart (jelle.veraart@nyumc.org) & "
           "J-Donald Tournier (jdtournier@gmail.com)";

  REFERENCES
    + "Veraart, J.; Novikov, D.S.; Christiaens, D.; Ades-aron, B.; Sijbers, J. & Fieremans, E. " // Internal
    "Denoising of diffusion MRI using random matrix theory. "
    "NeuroImage, 2016, 142, 394-406, doi: 10.1016/j.neuroimage.2016.08.016"

    + "Veraart, J.; Fieremans, E. & Novikov, D.S. " // Internal
    "Diffusion MRI noise mapping using random matrix theory. "
    "Magn. Res. Med., 2016, 76(5), 1582-1593, doi: 10.1002/mrm.26059";

  ARGUMENTS
  + Argument ("dwi", "the input diffusion-weighted image.").type_image_in ()

  + Argument ("out", "the output denoised DWI image.").type_image_out ();

  OPTIONS
    + Option ("mask", "only perform computation within the specified binary brain mask image.")
    +   Argument ("image").type_image_in()

    + Option ("extent", "set the window size of the denoising filter. (default = " + str(DEFAULT_SIZE) + "," + str(DEFAULT_SIZE) + "," + str(DEFAULT_SIZE) + ")")
    +   Argument ("window").type_sequence_int ()

    + Option ("noise", "the output noise map.")
    +   Argument ("level").type_image_out()

    + Option ("datatype", "datatype for SVD (float32 or float64).")
    +   Argument ("spec").type_choice(dtypes);

  COPYRIGHT = "Copyright (c) 2016 New York University, University of Antwerp, and the MRtrix3 contributors \n \n"
      "Permission is hereby granted, free of charge, to any non-commercial entity ('Recipient') obtaining a copy of this software and "
      "associated documentation files (the 'Software'), to the Software solely for non-commercial research, including the rights to "
      "use, copy and modify the Software, subject to the following conditions: \n \n"
      "\t 1. The above copyright notice and this permission notice shall be included by Recipient in all copies or substantial portions of "
      "the Software. \n \n"
      "\t 2. THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES"
      "OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE"
      "LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR"
      "IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. \n \n"
      "\t 3. In no event shall NYU be liable for direct, indirect, special, incidental or consequential damages in connection with the Software. "
      "Recipient will defend, indemnify and hold NYU harmless from any claims or liability resulting from the use of the Software by recipient. \n \n"
      "\t 4. Neither anything contained herein nor the delivery of the Software to recipient shall be deemed to grant the Recipient any right or "
      "licenses under any patents or patent application owned by NYU. \n \n"
      "\t 5. The Software may only be used for non-commercial research and may not be used for clinical care. \n \n"
      "\t 6. Any publication by Recipient of research involving the Software shall cite the references listed below.";

}

using value_type = float;

template <class ImageType, typename F = float>
class DenoisingFunctor {
  MEMALIGN(DenoisingFunctor)

public:

  typedef Eigen::Matrix<F, Eigen::Dynamic, Eigen::Dynamic> MatrixX;
  typedef Eigen::Matrix<F, Eigen::Dynamic, 1> VectorX;

  DenoisingFunctor (ImageType& dwi, vector<int> extent, Image<bool>& mask, ImageType& noise)
    : extent {{extent[0]/2, extent[1]/2, extent[2]/2}},
      m (dwi.size(3)), n (extent[0]*extent[1]*extent[2]),
      r ((m<n) ? m : n), X (m,n), pos {{0, 0, 0}},
      mask (mask), noise (noise)
  { }

  void operator () (ImageType& dwi, ImageType& out)
  {
    // Process voxels in mask only
    if (mask.valid()) {
      assign_pos_of (dwi).to (mask);
      if (!mask.value())
        return;
    }

    // Load data in local window
    load_data (dwi);

    // Compute Eigendecomposition:
    MatrixX XtX (r,r);
    if (m <= n)
      XtX.template triangularView<Eigen::Lower>() = X * X.adjoint();
    else
      XtX.template triangularView<Eigen::Lower>() = X.adjoint() * X;
    Eigen::SelfAdjointEigenSolver<MatrixX> eig (XtX);
    // eigenvalues provide squared singular values, sorted in increasing order:
    VectorX s = eig.eigenvalues();

    // Marchenko-Pastur optimal threshold
    const double lam_r = std::max(double(s[0]), 0.0) / std::max(m,n);
    double clam = 0.0;
    sigma2 = 0.0;
    ssize_t cutoff_p = 0;
    for (ssize_t p = 0; p < r; ++p)     // p+1 is the number of noise components
    {                                   // (as opposed to the paper where p is defined as the number of signal components)
      double lam = std::max(double(s[p]), 0.0) / std::max(m,n);
      clam += lam;
      double gam = double(p+1) / (std::max(m,n) - (r-p-1));
      double sigsq1 = clam / double(p+1);
      double sigsq2 = (lam - lam_r) / (4.0 * std::sqrt(gam));
      // sigsq2 > sigsq1 if signal else noise
      if (sigsq2 < sigsq1) {
        sigma2 = sigsq1;
        cutoff_p = p+1;
      }
    }

    if (cutoff_p > 0) {
      // recombine data using only eigenvectors above threshold:
      s.head (cutoff_p).setZero();
      s.tail (r-cutoff_p).setOnes();
      if (m <= n)
        X.col (n/2) = eig.eigenvectors() * ( s.asDiagonal() * ( eig.eigenvectors().adjoint() * X.col(n/2) ));
      else
        X.col (n/2) = X * ( eig.eigenvectors() * ( s.asDiagonal() * eig.eigenvectors().adjoint().col(n/2) ));
    }

    // Store output
    assign_pos_of(dwi).to(out);
    if (m <= n) {
      for (auto l = Loop (3) (out); l; ++l)
        out.value() = value_type (X(out.index(3), n/2));
    }
    else {
      for (auto l = Loop (3) (out); l; ++l)
        out.value() = value_type (X(out.index(3), n/2));
    }

    // store noise map if requested:
    if (noise.valid()) {
      assign_pos_of(dwi).to(noise);
      noise.value() = value_type (std::sqrt(sigma2));
    }
  }

  void load_data (ImageType& dwi)
  {
    pos[0] = dwi.index(0); pos[1] = dwi.index(1); pos[2] = dwi.index(2);
    X.setZero();
    size_t k = 0;
    for (int z = -extent[2]; z <= extent[2]; z++) {
      dwi.index(2) = wrapindex(z, 2, dwi.size(2));
      for (int y = -extent[1]; y <= extent[1]; y++) {
        dwi.index(1) = wrapindex(y, 1, dwi.size(1));
        for (int x = -extent[0]; x <= extent[0]; x++, k++) {
          dwi.index(0) = wrapindex(x, 0, dwi.size(0));
          X.col(k) = dwi.row(3);
        }
      }
    }

    // reset image position
    dwi.index(0) = pos[0];
    dwi.index(1) = pos[1];
    dwi.index(2) = pos[2];
  }

private:
  const std::array<ssize_t, 3> extent;
  const ssize_t m, n, r;
  MatrixX X;
  VectorX Xm;
  std::array<ssize_t, 3> pos;
  double sigma2;
  Image<bool> mask;
  ImageType noise;

  inline size_t wrapindex(int r, int axis, int max) const {
    int rr = pos[axis] + r;
    if (rr < 0)    rr = extent[axis] - r;
    if (rr >= max) rr = (max-1) - extent[axis] - r;
    return rr;
  }

};

void run ()
{
  auto dwi_in = Image<value_type>::open (argument[0]).with_direct_io(3);

  if (dwi_in.ndim() != 4 || dwi_in.size(3) <= 1)
    throw Exception ("input image must be 4-dimensional");

  Image<bool> mask;
  auto opt = get_options ("mask");
  if (opt.size()) {
    mask = Image<bool>::open (opt[0][0]);
    check_dimensions (mask, dwi_in, 0, 3);
  }

  auto header = Header (dwi_in);
  header.datatype() = DataType::Float32;
  auto dwi_out = Image<value_type>::create (argument[1], header);

  opt = get_options("extent");
  vector<int> extent = { DEFAULT_SIZE, DEFAULT_SIZE, DEFAULT_SIZE };
  if (opt.size()) {
    extent = parse_ints(opt[0][0]);
    if (extent.size() == 1)
      extent = {extent[0], extent[0], extent[0]};
    if (extent.size() != 3)
      throw Exception ("-extent must be either a scalar or a list of length 3");
    for (auto &e : extent)
      if (!(e & 1))
        throw Exception ("-extent must be a (list of) odd numbers");
  }

  Image<value_type> noise;
  opt = get_options("noise");
  if (opt.size()) {
    header.ndim() = 3;
    noise = Image<value_type>::create (opt[0][0], header);
  }

  opt = get_options("datatype");
  if (!opt.size() || (int(opt[0][0]) == 0)) {
    DEBUG("Computing SVD with single precision.");
    DenoisingFunctor< Image<value_type> , float > func (dwi_in, extent, mask, noise);
    ThreadedLoop ("running MP-PCA denoising", dwi_in, 0, 3)
      .run (func, dwi_in, dwi_out);
  }
  else if (int(opt[0][0]) == 1) {
    DEBUG("Computing SVD with double precision.");
    DenoisingFunctor< Image<value_type> , double > func (dwi_in, extent, mask, noise);
    ThreadedLoop ("running MP-PCA denoising", dwi_in, 0, 3)
      .run (func, dwi_in, dwi_out);
  }
  else {
    assert(0);
  }

}

<|endoftext|>


<commit_before>#include <apfMesh.h>
#include <gmi_null.h>
#include <apfMDS.h>
#include <apfMesh2.h>
#include <PCU.h>
#include <apf.h>

void testTriEdge()
{
  gmi_model* model = gmi_load(".null");
  apf::Mesh2* m = apf::makeEmptyMdsMesh(model, 2, false);
  apf::MeshEntity* v[3];
  for (int i = 0; i < 3; ++i)
    v[i] = m->createVert(0);
  apf::MeshEntity* ev[2];
  ev[0] = v[2]; ev[1] = v[1];
  apf::MeshEntity* e =
    apf::buildElement(m, 0, apf::Mesh::EDGE, ev);
  apf::MeshEntity* t =
    apf::buildElement(m, 0, apf::Mesh::TRIANGLE, v);
  int which, rotate;
  bool flip;
  apf::getAlignment(m, t, e, which, flip, rotate);
  assert(which == 1);
  assert(flip == true);
  assert(rotate == 0);
  m->destroyNative();
  apf::destroyMesh(m);
}

void testTetTri1()
{
  gmi_model* model = gmi_load(".null");
  apf::Mesh2* m = apf::makeEmptyMdsMesh(model, 3, false);
  apf::MeshEntity* v[4];
  for (int i = 0; i < 4; ++i)
    v[i] = m->createVert(0);
  apf::MeshEntity* tv[3];
  tv[0] = v[2]; tv[1] = v[1]; tv[2] = v[0];
  apf::MeshEntity* e =
    apf::buildElement(m, 0, apf::Mesh::TRIANGLE, tv);
  apf::MeshEntity* t =
    apf::buildElement(m, 0, apf::Mesh::TET, v);
  int which, rotate;
  bool flip;
  apf::getAlignment(m, t, e, which, flip, rotate);
  assert(which == 0);
  assert(flip == true);
  assert(rotate == 0);
  m->destroyNative();
  apf::destroyMesh(m);
}

void testTetTri2()
{
  gmi_model* model = gmi_load(".null");
  apf::Mesh2* m = apf::makeEmptyMdsMesh(model, 3, false);
  apf::MeshEntity* v[4];
  for (int i = 0; i < 4; ++i)
    v[i] = m->createVert(0);
  apf::MeshEntity* tv[3];
  tv[0] = v[0]; tv[1] = v[1]; tv[2] = v[2];
  apf::MeshEntity* e =
    apf::buildElement(m, 0, apf::Mesh::TRIANGLE, tv);
  apf::MeshEntity* t =
    apf::buildElement(m, 0, apf::Mesh::TET, v);
  int which, rotate;
  bool flip;
  apf::getAlignment(m, t, e, which, flip, rotate);
  assert(which == 0);
  assert(flip == false);
  assert(rotate == 0);
  m->destroyNative();
  apf::destroyMesh(m);
}

void testTetTri3()
{
  gmi_model* model = gmi_load(".null");
  apf::Mesh2* m = apf::makeEmptyMdsMesh(model, 3, false);
  apf::MeshEntity* v[4];
  for (int i = 0; i < 4; ++i)
    v[i] = m->createVert(0);
  apf::MeshEntity* tv[3];
  tv[0] = v[0]; tv[1] = v[2]; tv[2] = v[1];
  apf::MeshEntity* e =
    apf::buildElement(m, 0, apf::Mesh::TRIANGLE, tv);
  apf::MeshEntity* t =
    apf::buildElement(m, 0, apf::Mesh::TET, v);
  int which, rotate;
  bool flip;
  apf::getAlignment(m, t, e, which, flip, rotate);
  assert(which == 0);
  assert(flip == true);
  assert(rotate == 2);
  m->destroyNative();
  apf::destroyMesh(m);
}

int main()
{
  MPI_Init(0,0);
  PCU_Comm_Init();
  gmi_register_null();
  testTriEdge();
  testTetTri1();
  testTetTri2();
  testTetTri3();
  PCU_Comm_Free();
  MPI_Finalize();
}
<commit_msg>mortals can write test functions<commit_after>#include <apfMesh.h>
#include <gmi_null.h>
#include <apfMDS.h>
#include <apfMesh2.h>
#include <PCU.h>
#include <apf.h>

void testTriEdge()
{
  int which, rotate;
  bool flip;
  for(int ed = 0; ed < 6; ++ed){
    gmi_model* model = gmi_load(".null");
    apf::Mesh2* m = apf::makeEmptyMdsMesh(model, 2, false);
    apf::MeshEntity* v[3];
    for (int i = 0; i < 3; ++i)
      v[i] = m->createVert(0);
    apf::MeshEntity* ev[2];
    ev[0] = v[apf::tri_edge_verts[ed % 3][ed >= 3]];
    ev[1] = v[apf::tri_edge_verts[ed % 3][ed <  3]];
    apf::MeshEntity* e =
        apf::buildElement(m, 0, apf::Mesh::EDGE, ev);
    apf::MeshEntity* t =
        apf::buildElement(m, 0, apf::Mesh::TRIANGLE, v);
    apf::getAlignment(m, t, e, which, flip, rotate);
    assert(which == (ed % 3));
    assert(flip == (ed >= 3));
    assert(rotate == 0);
    m->destroyNative();
    apf::destroyMesh(m);
  }
}
void testTetEdge()
{
  int which, rotate;
  bool flip;
  for(int ed = 0; ed < 12; ++ed){
    gmi_model* model = gmi_load(".null");
    apf::Mesh2* m = apf::makeEmptyMdsMesh(model, 3, false);
    apf::MeshEntity* v[4];
    for (int i = 0; i < 4; ++i)
      v[i] = m->createVert(0);
    apf::MeshEntity* ev[2];
    ev[0] = v[apf::tet_edge_verts[ed % 6][ed >= 6]];
    ev[1] = v[apf::tet_edge_verts[ed % 6][ed <  6]];
    apf::MeshEntity* e =
        apf::buildElement(m, 0, apf::Mesh::EDGE, ev);
    apf::MeshEntity* t =
        apf::buildElement(m, 0, apf::Mesh::TET, v);
    apf::getAlignment(m, t, e, which, flip, rotate);
    assert(which == (ed % 6));
    assert(flip == (ed >= 6));
    assert(rotate == 0);
    m->destroyNative();
    apf::destroyMesh(m);
  }
}
void testTetTri()
{
  int which, rotate;
  bool flip;
  int r[6] = {0,2,1,2,1,0};
  for(int flipped = 0; flipped < 2; ++flipped){
    for(int fa = 0; fa < 12; ++fa){
      gmi_model* model = gmi_load(".null");
      apf::Mesh2* m = apf::makeEmptyMdsMesh(model, 3, false);
      apf::MeshEntity* v[4];
      for (int i = 0; i < 4; ++i)
        v[i] = m->createVert(0);
      apf::MeshEntity* ev[3];
      ev[0] = v[apf::tet_tri_verts[fa % 4][(0+fa/4) % 3]];
      ev[1] = v[apf::tet_tri_verts[fa % 4][(1+fa/4) % 3]];
      ev[2] = v[apf::tet_tri_verts[fa % 4][(2+fa/4) % 3]];
      if(flipped)
        std::swap(ev[1],ev[2]);
      apf::MeshEntity* e =
          apf::buildElement(m, 0, apf::Mesh::TRIANGLE, ev);
      apf::MeshEntity* t =
          apf::buildElement(m, 0, apf::Mesh::TET, v);
      apf::getAlignment(m, t, e, which, flip, rotate);
      assert(which == fa % 4);
      assert(flip == flipped);
      assert(rotate == r[flipped*3+fa/4]);
      m->destroyNative();
      apf::destroyMesh(m);
    }
  }
}
int main()
{
  MPI_Init(0,0);
  PCU_Comm_Init();
  gmi_register_null();
  testTriEdge();
  testTetEdge();
  testTetTri();
  PCU_Comm_Free();
  MPI_Finalize();
}
<|endoftext|>


<commit_before>/*
 * Copyright (C) 2016 deipi.com LLC and contributors. All rights reserved.
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to
 * deal in the Software without restriction, including without limitation the
 * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
 * sell copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
 * IN THE SOFTWARE.
 */

#include "logger.h"

#include <stdarg.h>      // for va_list, va_end, va_start
#include <stdio.h>       // for fileno, vsnprintf, stderr
#include <unistd.h>      // for isatty
#include <ctime>         // for time_t
#include <functional>    // for ref
#include <iostream>      // for cerr
#include <regex>         // for regex_replace, regex
#include <stdexcept>     // for out_of_range
#include <system_error>  // for system_error

#include "datetime.h"    // for to_string
#include "exception.h"   // for traceback
#include "utils.h"       // for get_thread_name

#define BUFFER_SIZE (10 * 1024)
#define STACKED_INDENT "<indent>"

const std::regex filter_re("\033\\[[;\\d]*m");
std::mutex Log::stack_mtx;
std::unordered_map<std::thread::id, unsigned> Log::stack_levels;

const char *priorities[] = {
	EMERG_COL "█" NO_COL,   // LOG_EMERG    0 = System is unusable
	ALERT_COL "▉" NO_COL,   // LOG_ALERT    1 = Action must be taken immediately
	CRIT_COL "▊" NO_COL,    // LOG_CRIT     2 = Critical conditions
	ERR_COL "▋" NO_COL,     // LOG_ERR      3 = Error conditions
	WARNING_COL "▌" NO_COL, // LOG_WARNING  4 = Warning conditions
	NOTICE_COL "▍" NO_COL,  // LOG_NOTICE   5 = Normal but significant condition
	INFO_COL "▎" NO_COL,    // LOG_INFO     6 = Informational
	DEBUG_COL "▏" NO_COL,   // LOG_DEBUG    7 = Debug-level messages
};

void
StreamLogger::log(int priority, const std::string& str)
{
	ofs << std::regex_replace(priorities[priority < 0 ? -priority : priority] + str, filter_re, "") << std::endl;
}

void
StderrLogger::log(int priority, const std::string& str)
{
	if (isatty(fileno(stderr))) {
		std::cerr << priorities[priority < 0 ? -priority : priority] + str << std::endl;
	} else {
		std::cerr << std::regex_replace(priorities[priority < 0 ? -priority : priority] + str, filter_re, "") << std::endl;
	}
}

SysLog::SysLog(const char *ident, int option, int facility)
{
	openlog(ident, option, facility);
}

SysLog::~SysLog()
{
	closelog();
}

void
SysLog::log(int priority, const std::string& str)
{
	syslog(priority, "%s", std::regex_replace(priorities[priority < 0 ? -priority : priority] + str, filter_re, "").c_str());
}

Log::Log(const std::string& str, bool clean_, bool stacked_, int priority_, std::chrono::time_point<std::chrono::system_clock> created_at_)
	: stack_level(0),
	  stacked(stacked_),
	  clean(clean_),
	  created_at(created_at_),
	  cleared_at(created_at_),
	  str_start(str),
	  priority(priority_),
	  cleared(false),
	  cleaned(false) {

	if (stacked) {
		std::lock_guard<std::mutex> lk(stack_mtx);
		thread_id = std::this_thread::get_id();
		try {
			stack_level = ++stack_levels.at(thread_id);
		} catch (const std::out_of_range&) {
			stack_levels[thread_id] = 0;
		}
	}
}

Log::~Log()
{
	cleanup();
}

void
Log::cleanup()
{
	bool f = false;
	if (cleared.compare_exchange_strong(f, clean)) {
		cleared_at = std::chrono::system_clock::now();
	}

	if (!cleaned.exchange(true)) {
		if (stacked) {
			std::lock_guard<std::mutex> lk(stack_mtx);
			if (stack_levels.at(thread_id)-- == 0) {
				stack_levels.erase(thread_id);
			}
		}
	}
}

long double
Log::age()
{
	auto now = (cleared_at > created_at) ? cleared_at : std::chrono::system_clock::now();
	return std::chrono::duration_cast<std::chrono::nanoseconds>(now - created_at).count();
}

LogQueue::LogQueue()
	: queue(now())
{ }

LogType&
LogQueue::next(bool final, uint64_t final_key, bool keep_going)
{
	return queue.next(final, final_key, keep_going, false);
}

LogType&
LogQueue::peep()
{
	return queue.next(false, 0, true, true);
}

void
LogQueue::add(const LogType& l_ptr, uint64_t key)
{
	queue.add(l_ptr, key);
}

/*
 * https://isocpp.org/wiki/faq/ctors#static-init-order
 * Avoid the "static initialization order fiasco"
 */

LogThread&
Log::_thread()
{
	static LogThread* thread = new LogThread();
	return *thread;
}

int&
Log::_log_level()
{
	static auto* log_level = new int(DEFAULT_LOG_LEVEL);
	return *log_level;
}

std::vector<std::unique_ptr<Logger>>&
Log::_handlers()
{
	static auto* handlers = new std::vector<std::unique_ptr<Logger>>();
	return *handlers;
}

std::string
Log::str_format(bool stacked, int priority, const std::string& exc, const char *file, int line, const char *suffix, const char *prefix, const void* obj, const char *format, va_list argptr)
{
	char* buffer = new char[BUFFER_SIZE];
	vsnprintf(buffer, BUFFER_SIZE, format, argptr);
	std::string msg(buffer);
	auto iso8601 = "[" + Datetime::to_string(std::chrono::system_clock::now()) + "]";
	auto tid = " (" + get_thread_name() + ")";
	std::string result = iso8601 + tid;
#ifdef LOG_OBJ_ADDRESS
	if (obj) {
		snprintf(buffer, BUFFER_SIZE, " [%p]", obj);
		result += buffer;
	}
#endif
#ifdef TRACEBACK
	auto location = (priority >= LOCATION_LOG_LEVEL) ? " " + std::string(file) + ":" + std::to_string(line) : std::string();
	result += location + ": ";
#else
	result += " ";
	(void)obj;
#endif
	if (stacked) {
		result += STACKED_INDENT;
	}
	result += prefix + msg + suffix;
	delete []buffer;
	if (priority < 0) {
		if (exc.empty()) {
			result += DARK_GREY + traceback(file, line) + NO_COL;
		} else {
			result += NO_COL + exc + NO_COL;
		}
	}
	return result;
}

LogWrapper
Log::log(bool clean, bool stacked, std::chrono::time_point<std::chrono::system_clock> wakeup, int priority, const std::string& exc, const char *file, int line, const char *suffix, const char *prefix, const void *obj, const char *format, ...)
{
	va_list argptr;
	va_start(argptr, format);
	std::string str(str_format(stacked, priority, exc, file, line, suffix, prefix, obj, format, argptr));
	va_end(argptr);

	return print(str, clean, stacked, wakeup, priority);
}

bool
Log::clear()
{
	if (!cleared.exchange(true)) {
		cleared_at = std::chrono::system_clock::now();
		return true;
	}
	return false;
}

bool
Log::unlog(int priority, const char *file, int line, const char *suffix, const char *prefix, const void *obj, const char *format, ...)
{
	if (!clear()) {
		va_list argptr;
		va_start(argptr, format);
		std::string str(str_format(stacked, priority, std::string(), file, line, suffix, prefix, obj, format, argptr));
		va_end(argptr);

		print(str, false, stacked, 0, priority, created_at);

		return true;
	}
	return false;
}

LogWrapper
Log::add(const std::string& str, bool clean, bool stacked, std::chrono::time_point<std::chrono::system_clock> wakeup, int priority, std::chrono::time_point<std::chrono::system_clock> created_at)
{
	auto l_ptr = std::make_shared<Log>(str, clean, stacked, priority, created_at);

	static LogThread& thread = _thread();
	thread.add(l_ptr, wakeup);

	return LogWrapper(l_ptr);
}

void
Log::log(int priority, std::string str, int indent)
{
	static std::mutex log_mutex;
	std::lock_guard<std::mutex> lk(log_mutex);
	static const auto& handlers = _handlers();
	auto needle = str.find(STACKED_INDENT);
	if (needle != std::string::npos) {
		str.replace(needle, sizeof(STACKED_INDENT) - 1, std::string(indent, ' '));
	}
	for (auto& handler : handlers) {
		handler->log(priority, str);
	}
}

LogWrapper
Log::print(const std::string& str, bool clean, bool stacked, std::chrono::time_point<std::chrono::system_clock> wakeup, int priority, std::chrono::time_point<std::chrono::system_clock> created_at)
{
	static auto& log_level = _log_level();
	if (priority > log_level) {
		return LogWrapper(std::make_shared<Log>(str, clean, stacked, priority, created_at));
	}

	if (priority >= ASYNC_LOG_LEVEL || wakeup > std::chrono::system_clock::now()) {
		return add(str, clean, stacked, wakeup, priority, created_at);
	} else {
		auto l_ptr = std::make_shared<Log>(str, clean, stacked, priority, created_at);
		log(priority, str, l_ptr->stack_level * 2);
		return LogWrapper(l_ptr);
	}
}

void
Log::finish(int wait)
{
	static LogThread& thread = _thread();
	thread.finish(wait);
}

std::condition_variable LogThread::wakeup_signal;
std::atomic_ullong LogThread::next_wakeup_time;

LogThread::LogThread()
	: running(-1),
	  inner_thread(&LogThread::thread_function, this, std::ref(log_queue)) { }

LogThread::~LogThread()
{
	finish(true);
}

void
LogThread::finish(int wait)
{
	running = wait;
	wakeup_signal.notify_all();
	if (wait) {
		try {
			inner_thread.join();
		} catch (const std::system_error&) { }
	}
}

void
LogThread::add(const LogType& l_ptr, std::chrono::time_point<std::chrono::system_clock> wakeup)
{
	if (running != 0) {
		wakeup += 2ms;
		auto wt = time_point_to_ullong(wakeup);
		l_ptr->wakeup_time = wt;

		log_queue.add(l_ptr, time_point_to_key(wakeup));

		auto now = std::chrono::system_clock::now();
		if (wakeup < now) {
			wakeup = now;
		}
		auto nwt = next_wakeup_time.load();
		auto n = time_point_to_ullong(now);

		bool notify;
		do {
			notify = (nwt < n || nwt >= wt);
		} while (notify && !next_wakeup_time.compare_exchange_weak(nwt, wt));

		if (notify) {
			wakeup_signal.notify_one();
		}
	}
}

void
LogThread::thread_function(LogQueue& log_queue)
{
	std::mutex mtx;
	std::unique_lock<std::mutex> lk(mtx);

	next_wakeup_time = time_point_to_ullong(std::chrono::system_clock::now() + 100ms);

	while (running != 0) {
		if (--running < 0) {
			running = -1;
		}

		auto now = std::chrono::system_clock::now();
		auto wakeup = now + (running < 0 ? 3s : 100ms);
		auto wt = time_point_to_ullong(wakeup);
		auto nwt = next_wakeup_time.load();
		auto n = time_point_to_ullong(now);

		bool notify;
		do {
			notify = (nwt < n || nwt >= wt);
		} while (notify && !next_wakeup_time.compare_exchange_weak(nwt, wt));

		wakeup_signal.wait_until(lk, time_point_from_ullong(next_wakeup_time.load()));

		try {
			do {
				auto& l_ptr = log_queue.next(running < 0);
				if (l_ptr) {
					if (!l_ptr->cleared) {
						auto msg = l_ptr->str_start;
						auto age = l_ptr->age();
						if (age > 2e8) {
							msg += " ~" + delta_string(age, true);
						}
						if (l_ptr->clear()) {
							Log::log(l_ptr->priority, msg, l_ptr->stack_level * 2);
						}
					}

					l_ptr.reset();
				}
			} while (true);
		} catch(const StashContinue&) { }

		if (running >= 0) {
			break;
		}
	}
}
<commit_msg>Logger: Wakeup deferring improved<commit_after>/*
 * Copyright (C) 2016 deipi.com LLC and contributors. All rights reserved.
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to
 * deal in the Software without restriction, including without limitation the
 * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
 * sell copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
 * IN THE SOFTWARE.
 */

#include "logger.h"

#include <stdarg.h>      // for va_list, va_end, va_start
#include <stdio.h>       // for fileno, vsnprintf, stderr
#include <unistd.h>      // for isatty
#include <ctime>         // for time_t
#include <functional>    // for ref
#include <iostream>      // for cerr
#include <regex>         // for regex_replace, regex
#include <stdexcept>     // for out_of_range
#include <system_error>  // for system_error

#include "datetime.h"    // for to_string
#include "exception.h"   // for traceback
#include "utils.h"       // for get_thread_name

#define BUFFER_SIZE (10 * 1024)
#define STACKED_INDENT "<indent>"

const std::regex filter_re("\033\\[[;\\d]*m");
std::mutex Log::stack_mtx;
std::unordered_map<std::thread::id, unsigned> Log::stack_levels;

const char *priorities[] = {
	EMERG_COL "█" NO_COL,   // LOG_EMERG    0 = System is unusable
	ALERT_COL "▉" NO_COL,   // LOG_ALERT    1 = Action must be taken immediately
	CRIT_COL "▊" NO_COL,    // LOG_CRIT     2 = Critical conditions
	ERR_COL "▋" NO_COL,     // LOG_ERR      3 = Error conditions
	WARNING_COL "▌" NO_COL, // LOG_WARNING  4 = Warning conditions
	NOTICE_COL "▍" NO_COL,  // LOG_NOTICE   5 = Normal but significant condition
	INFO_COL "▎" NO_COL,    // LOG_INFO     6 = Informational
	DEBUG_COL "▏" NO_COL,   // LOG_DEBUG    7 = Debug-level messages
};

void
StreamLogger::log(int priority, const std::string& str)
{
	ofs << std::regex_replace(priorities[priority < 0 ? -priority : priority] + str, filter_re, "") << std::endl;
}

void
StderrLogger::log(int priority, const std::string& str)
{
	if (isatty(fileno(stderr))) {
		std::cerr << priorities[priority < 0 ? -priority : priority] + str << std::endl;
	} else {
		std::cerr << std::regex_replace(priorities[priority < 0 ? -priority : priority] + str, filter_re, "") << std::endl;
	}
}

SysLog::SysLog(const char *ident, int option, int facility)
{
	openlog(ident, option, facility);
}

SysLog::~SysLog()
{
	closelog();
}

void
SysLog::log(int priority, const std::string& str)
{
	syslog(priority, "%s", std::regex_replace(priorities[priority < 0 ? -priority : priority] + str, filter_re, "").c_str());
}

Log::Log(const std::string& str, bool clean_, bool stacked_, int priority_, std::chrono::time_point<std::chrono::system_clock> created_at_)
	: stack_level(0),
	  stacked(stacked_),
	  clean(clean_),
	  created_at(created_at_),
	  cleared_at(created_at_),
	  str_start(str),
	  priority(priority_),
	  cleared(false),
	  cleaned(false) {

	if (stacked) {
		std::lock_guard<std::mutex> lk(stack_mtx);
		thread_id = std::this_thread::get_id();
		try {
			stack_level = ++stack_levels.at(thread_id);
		} catch (const std::out_of_range&) {
			stack_levels[thread_id] = 0;
		}
	}
}

Log::~Log()
{
	cleanup();
}

void
Log::cleanup()
{
	bool f = false;
	if (cleared.compare_exchange_strong(f, clean)) {
		cleared_at = std::chrono::system_clock::now();
	}

	if (!cleaned.exchange(true)) {
		if (stacked) {
			std::lock_guard<std::mutex> lk(stack_mtx);
			if (stack_levels.at(thread_id)-- == 0) {
				stack_levels.erase(thread_id);
			}
		}
	}
}

long double
Log::age()
{
	auto now = (cleared_at > created_at) ? cleared_at : std::chrono::system_clock::now();
	return std::chrono::duration_cast<std::chrono::nanoseconds>(now - created_at).count();
}

LogQueue::LogQueue()
	: queue(now())
{ }

LogType&
LogQueue::next(bool final, uint64_t final_key, bool keep_going)
{
	return queue.next(final, final_key, keep_going, false);
}

LogType&
LogQueue::peep()
{
	return queue.next(false, 0, true, true);
}

void
LogQueue::add(const LogType& l_ptr, uint64_t key)
{
	queue.add(l_ptr, key);
}

/*
 * https://isocpp.org/wiki/faq/ctors#static-init-order
 * Avoid the "static initialization order fiasco"
 */

LogThread&
Log::_thread()
{
	static LogThread* thread = new LogThread();
	return *thread;
}

int&
Log::_log_level()
{
	static auto* log_level = new int(DEFAULT_LOG_LEVEL);
	return *log_level;
}

std::vector<std::unique_ptr<Logger>>&
Log::_handlers()
{
	static auto* handlers = new std::vector<std::unique_ptr<Logger>>();
	return *handlers;
}

std::string
Log::str_format(bool stacked, int priority, const std::string& exc, const char *file, int line, const char *suffix, const char *prefix, const void* obj, const char *format, va_list argptr)
{
	char* buffer = new char[BUFFER_SIZE];
	vsnprintf(buffer, BUFFER_SIZE, format, argptr);
	std::string msg(buffer);
	auto iso8601 = "[" + Datetime::to_string(std::chrono::system_clock::now()) + "]";
	auto tid = " (" + get_thread_name() + ")";
	std::string result = iso8601 + tid;
#ifdef LOG_OBJ_ADDRESS
	if (obj) {
		snprintf(buffer, BUFFER_SIZE, " [%p]", obj);
		result += buffer;
	}
#endif
#ifdef TRACEBACK
	auto location = (priority >= LOCATION_LOG_LEVEL) ? " " + std::string(file) + ":" + std::to_string(line) : std::string();
	result += location + ": ";
#else
	result += " ";
	(void)obj;
#endif
	if (stacked) {
		result += STACKED_INDENT;
	}
	result += prefix + msg + suffix;
	delete []buffer;
	if (priority < 0) {
		if (exc.empty()) {
			result += DARK_GREY + traceback(file, line) + NO_COL;
		} else {
			result += NO_COL + exc + NO_COL;
		}
	}
	return result;
}

LogWrapper
Log::log(bool clean, bool stacked, std::chrono::time_point<std::chrono::system_clock> wakeup, int priority, const std::string& exc, const char *file, int line, const char *suffix, const char *prefix, const void *obj, const char *format, ...)
{
	va_list argptr;
	va_start(argptr, format);
	std::string str(str_format(stacked, priority, exc, file, line, suffix, prefix, obj, format, argptr));
	va_end(argptr);

	return print(str, clean, stacked, wakeup, priority);
}

bool
Log::clear()
{
	if (!cleared.exchange(true)) {
		cleared_at = std::chrono::system_clock::now();
		return true;
	}
	return false;
}

bool
Log::unlog(int priority, const char *file, int line, const char *suffix, const char *prefix, const void *obj, const char *format, ...)
{
	if (!clear()) {
		va_list argptr;
		va_start(argptr, format);
		std::string str(str_format(stacked, priority, std::string(), file, line, suffix, prefix, obj, format, argptr));
		va_end(argptr);

		print(str, false, stacked, 0, priority, created_at);

		return true;
	}
	return false;
}

LogWrapper
Log::add(const std::string& str, bool clean, bool stacked, std::chrono::time_point<std::chrono::system_clock> wakeup, int priority, std::chrono::time_point<std::chrono::system_clock> created_at)
{
	auto l_ptr = std::make_shared<Log>(str, clean, stacked, priority, created_at);

	static LogThread& thread = _thread();
	thread.add(l_ptr, wakeup);

	return LogWrapper(l_ptr);
}

void
Log::log(int priority, std::string str, int indent)
{
	static std::mutex log_mutex;
	std::lock_guard<std::mutex> lk(log_mutex);
	static const auto& handlers = _handlers();
	auto needle = str.find(STACKED_INDENT);
	if (needle != std::string::npos) {
		str.replace(needle, sizeof(STACKED_INDENT) - 1, std::string(indent, ' '));
	}
	for (auto& handler : handlers) {
		handler->log(priority, str);
	}
}

LogWrapper
Log::print(const std::string& str, bool clean, bool stacked, std::chrono::time_point<std::chrono::system_clock> wakeup, int priority, std::chrono::time_point<std::chrono::system_clock> created_at)
{
	static auto& log_level = _log_level();
	if (priority > log_level) {
		return LogWrapper(std::make_shared<Log>(str, clean, stacked, priority, created_at));
	}

	if (priority >= ASYNC_LOG_LEVEL || wakeup > std::chrono::system_clock::now()) {
		return add(str, clean, stacked, wakeup, priority, created_at);
	} else {
		auto l_ptr = std::make_shared<Log>(str, clean, stacked, priority, created_at);
		log(priority, str, l_ptr->stack_level * 2);
		return LogWrapper(l_ptr);
	}
}

void
Log::finish(int wait)
{
	static LogThread& thread = _thread();
	thread.finish(wait);
}

std::condition_variable LogThread::wakeup_signal;
std::atomic_ullong LogThread::next_wakeup_time;

LogThread::LogThread()
	: running(-1),
	  inner_thread(&LogThread::thread_function, this, std::ref(log_queue)) { }

LogThread::~LogThread()
{
	finish(true);
}

void
LogThread::finish(int wait)
{
	running = wait;
	wakeup_signal.notify_all();
	if (wait) {
		try {
			inner_thread.join();
		} catch (const std::system_error&) { }
	}
}

void
LogThread::add(const LogType& l_ptr, std::chrono::time_point<std::chrono::system_clock> wakeup)
{
	if (running != 0) {
		auto now = std::chrono::system_clock::now();
		if (wakeup < now + 2ms) {
			wakeup = now + 2ms;  // defer log so we make sure we're not adding messages to the current slot
		}

		auto wt = time_point_to_ullong(wakeup);
		l_ptr->wakeup_time = wt;

		log_queue.add(l_ptr, time_point_to_key(wakeup));

		auto nwt = next_wakeup_time.load();
		auto n = time_point_to_ullong(now);

		bool notify;
		do {
			notify = (nwt < n || nwt >= wt);
		} while (notify && !next_wakeup_time.compare_exchange_weak(nwt, wt));

		if (notify) {
			wakeup_signal.notify_one();
		}
	}
}

void
LogThread::thread_function(LogQueue& log_queue)
{
	std::mutex mtx;
	std::unique_lock<std::mutex> lk(mtx);

	next_wakeup_time = time_point_to_ullong(std::chrono::system_clock::now() + 100ms);

	while (running != 0) {
		if (--running < 0) {
			running = -1;
		}

		auto now = std::chrono::system_clock::now();
		auto wakeup = now + (running < 0 ? 3s : 100ms);
		auto wt = time_point_to_ullong(wakeup);
		auto nwt = next_wakeup_time.load();
		auto n = time_point_to_ullong(now);

		bool notify;
		do {
			notify = (nwt < n || nwt >= wt);
		} while (notify && !next_wakeup_time.compare_exchange_weak(nwt, wt));

		wakeup_signal.wait_until(lk, time_point_from_ullong(next_wakeup_time.load()));

		try {
			do {
				auto& l_ptr = log_queue.next(running < 0);
				if (l_ptr) {
					if (!l_ptr->cleared) {
						auto msg = l_ptr->str_start;
						auto age = l_ptr->age();
						if (age > 2e8) {
							msg += " ~" + delta_string(age, true);
						}
						if (l_ptr->clear()) {
							Log::log(l_ptr->priority, msg, l_ptr->stack_level * 2);
						}
					}

					l_ptr.reset();
				}
			} while (true);
		} catch(const StashContinue&) { }

		if (running >= 0) {
			break;
		}
	}
}
<|endoftext|>


<commit_before>/*  parse_bam.cc -- Example BAM parser using the htslib API

    Copyright (c) 2015, Washington University

    Author: Avinash Ramu <avinash3003@yahoo.co.in>

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

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

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

#include <iostream>

#include "hts.h"
#include "sam.h"

using namespace std;

int usage() {
    cerr << "./parse_bam example.bam";
    return 0;
}

int parse_bam(int argc, char* argv[]) {
    if(argc < 2) {
        return usage();
    }
    string bam = string(argv[1]);
    string region_ = ".";
    if(argc > 2) {
        region_ = string(argv[2]);
    }
    if(!bam.empty()) {
        //open BAM for reading
        samFile *in = sam_open(bam.c_str(), "r");
        if(in == NULL) {
            throw runtime_error("Unable to open BAM/SAM file.");
        }
        //Load the index
        hts_idx_t *idx = sam_index_load(in, bam.c_str());
        if(idx == NULL) {
            throw runtime_error("Unable to open BAM/SAM index."
                                " Make sure alignments are indexed");
        }
        //Get the header
        bam_hdr_t *header = sam_hdr_read(in);
        //Initialize iterator
        hts_itr_t *iter = NULL;
        //Move the iterator to the region we are interested in
        iter  = sam_itr_querys(idx, header, region_.c_str());
        if(header == NULL || iter == NULL) {
            sam_close(in);
            throw runtime_error("Unable to iterate to region within BAM.");
        }
        //Initiate the alignment record
        bam1_t *aln = bam_init1();
        while(sam_itr_next(in, iter, aln) >= 0) {
            cout << "Read Chr: " << header->target_name[aln->core.tid];
            cout << "\tPos: " << aln->core.pos;
            cout << endl;
        }
        hts_itr_destroy(iter);
        hts_idx_destroy(idx);
        bam_destroy1(aln);
        bam_hdr_destroy(header);
        sam_close(in);
    }
    return 0;
}

int main(int argc, char* argv[]) {
    try {
        parse_bam(argc, argv);
    } catch (const runtime_error& e) {
        cerr << e.what();
    }
}
<commit_msg>Print out sequence of BAM record<commit_after>/*  parse_bam.cc -- Example BAM parser using the htslib API

    Copyright (c) 2015, Washington University

    Author: Avinash Ramu <avinash3003@yahoo.co.in>

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

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

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

#include <iostream>

#include "hts.h"
#include "sam.h"

using namespace std;

int usage() {
    cerr << "./parse_bam example.bam";
    return 0;
}

int parse_bam(int argc, char* argv[]) {
    if(argc < 2) {
        return usage();
    }
    string bam = string(argv[1]);
    string region_ = ".";
    if(argc > 2) {
        region_ = string(argv[2]);
    }
    if(!bam.empty()) {
        //open BAM for reading
        samFile *in = sam_open(bam.c_str(), "r");
        if(in == NULL) {
            throw runtime_error("Unable to open BAM/SAM file.");
        }
        //Load the index
        hts_idx_t *idx = sam_index_load(in, bam.c_str());
        if(idx == NULL) {
            throw runtime_error("Unable to open BAM/SAM index."
                                " Make sure alignments are indexed");
        }
        //Get the header
        bam_hdr_t *header = sam_hdr_read(in);
        //Initialize iterator
        hts_itr_t *iter = NULL;
        //Move the iterator to the region we are interested in
        iter  = sam_itr_querys(idx, header, region_.c_str());
        if(header == NULL || iter == NULL) {
            sam_close(in);
            throw runtime_error("Unable to iterate to region within BAM.");
        }
        //Initiate the alignment record
        bam1_t *aln = bam_init1();
        while(sam_itr_next(in, iter, aln) >= 0) {
            cout << "Read Chr: " << header->target_name[aln->core.tid];
            cout << "\tPos: " << aln->core.pos;
            cout << "\tSeq: ";
            for (int i = 0; i < aln->core.l_qseq; ++i)
                cout << seq_nt16_str[bam_seqi(bam_get_seq(aln), i)];
            cout << endl;
        }
        hts_itr_destroy(iter);
        hts_idx_destroy(idx);
        bam_destroy1(aln);
        bam_hdr_destroy(header);
        sam_close(in);
    }
    return 0;
}

int main(int argc, char* argv[]) {
    try {
        parse_bam(argc, argv);
    } catch (const runtime_error& e) {
        cerr << e.what();
    }
}
<|endoftext|>


<commit_before>/***************************************************************************
    copyright            : (C) 2010 by Alex Novichkov
    email                : novichko@atnet.ru

    copyright            : (C) 2006 by Lukáš Lalinský
    email                : lalinsky@gmail.com
                           (original WavPack implementation)
 ***************************************************************************/

/***************************************************************************
 *   This library is free software; you can redistribute it and/or modify  *
 *   it under the terms of the GNU Lesser General Public License version   *
 *   2.1 as published by the Free Software Foundation.                     *
 *                                                                         *
 *   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                                                       *
 *                                                                         *
 *   Alternatively, this file is available under the Mozilla Public        *
 *   License Version 1.1.  You may obtain a copy of the License at         *
 *   http://www.mozilla.org/MPL/                                           *
 ***************************************************************************/

#include <tstring.h>
#include <tdebug.h>
#include <bitset>
#include "id3v2tag.h"
#include "apeproperties.h"
#include "apefile.h"
#include "apetag.h"
#include "apefooter.h"

using namespace TagLib;

class APE::Properties::PropertiesPrivate
{
public:
  PropertiesPrivate() :
    length(0),
    bitrate(0),
    sampleRate(0),
    channels(0),
    version(0),
    bitsPerSample(0),
    sampleFrames(0) {}

  int length;
  int bitrate;
  int sampleRate;
  int channels;
  int version;
  int bitsPerSample;
  uint sampleFrames;
};

////////////////////////////////////////////////////////////////////////////////
// public members
////////////////////////////////////////////////////////////////////////////////

APE::Properties::Properties(File *file, ReadStyle style) :
  AudioProperties(style),
  d(new PropertiesPrivate())
{
  debug("APE::Properties::Properties() -- This constructor is no longer used.");
}

APE::Properties::Properties(File *file, long streamLength, ReadStyle style) :
  AudioProperties(style),
  d(new PropertiesPrivate())
{
  read(file, streamLength);
}

APE::Properties::~Properties()
{
  delete d;
}

int APE::Properties::length() const
{
  return lengthInSeconds();
}

int APE::Properties::lengthInSeconds() const
{
  return d->length / 1000;
}

int APE::Properties::lengthInMilliseconds() const
{
  return d->length;
}

int APE::Properties::bitrate() const
{
  return d->bitrate;
}

int APE::Properties::sampleRate() const
{
  return d->sampleRate;
}

int APE::Properties::channels() const
{
  return d->channels;
}

int APE::Properties::version() const
{
  return d->version;
}

int APE::Properties::bitsPerSample() const
{
  return d->bitsPerSample;
}

TagLib::uint APE::Properties::sampleFrames() const
{
  return d->sampleFrames;
}

////////////////////////////////////////////////////////////////////////////////
// private members
////////////////////////////////////////////////////////////////////////////////

void APE::Properties::read(File *file, long streamLength)
{
  // First we are searching the descriptor
  const long offset = file->find("MAC ", file->tell());
  if(offset < 0) {
    debug("APE::Properties::read() -- APE descriptor not found");
    return;
  }

  // Then we read the header common for all versions of APE
  file->seek(offset);
  const ByteVector commonHeader = file->readBlock(6);
  if(commonHeader.size() < 6) {
    debug("APE::Properties::read() -- header is too short.");
    return;
  }

  d->version = commonHeader.toUShort(4, false);

  if(d->version >= 3980)
    analyzeCurrent(file);
  else
    analyzeOld(file);

  if(d->sampleFrames > 0 && d->sampleRate > 0) {
    const double length = d->sampleFrames * 1000.0 / d->sampleRate;
    d->length  = static_cast<int>(length + 0.5);
    d->bitrate = static_cast<int>(streamLength * 8.0 / length + 0.5);
  }
}

void APE::Properties::analyzeCurrent(File *file)
{
  // Read the descriptor
  file->seek(2, File::Current);
  const ByteVector descriptor = file->readBlock(44);
  if(descriptor.size() < 44) {
    debug("APE::Properties::analyzeCurrent() -- descriptor is too short.");
    return;
  }

  const uint descriptorBytes = descriptor.toUInt(0, false);

  if((descriptorBytes - 52) > 0)
    file->seek(descriptorBytes - 52, File::Current);

  // Read the header
  const ByteVector header = file->readBlock(24);
  if(header.size() < 24) {
    debug("APE::Properties::analyzeCurrent() -- MAC header is too short.");
    return;
  }

  // Get the APE info
  d->channels      = header.toShort(18, false);
  d->sampleRate    = header.toUInt(20, false);
  d->bitsPerSample = header.toShort(16, false);

  const uint totalFrames = header.toUInt(12, false);
  if(totalFrames == 0)
    return;

  const uint blocksPerFrame   = header.toUInt(4, false);
  const uint finalFrameBlocks = header.toUInt(8, false);
  d->sampleFrames = (totalFrames - 1) * blocksPerFrame + finalFrameBlocks;
}

void APE::Properties::analyzeOld(File *file)
{
  const ByteVector header = file->readBlock(26);
  if(header.size() < 26) {
    debug("APE::Properties::analyzeOld() -- MAC header is too short.");
    return;
  }

  const uint totalFrames = header.toUInt(18, false);

  // Fail on 0 length APE files (catches non-finalized APE files)
  if(totalFrames == 0)
    return;

  const short compressionLevel = header.toShort(0, false);
  uint blocksPerFrame;
  if(d->version >= 3950)
    blocksPerFrame = 73728 * 4;
  else if(d->version >= 3900 || (d->version >= 3800 && compressionLevel == 4000))
    blocksPerFrame = 73728;
  else
    blocksPerFrame = 9216;

  // Get the APE info
  d->channels   = header.toShort(4, false);
  d->sampleRate = header.toUInt(6, false);

  const uint finalFrameBlocks = header.toUInt(22, false);
  d->sampleFrames = (totalFrames - 1) * blocksPerFrame + finalFrameBlocks;

  // Seek the RIFF chunk and get the bit depth
  long offset = file->tell();
  offset = file->find("WAVEfmt ", offset);
  if(offset < 0)
    return;

  file->seek(offset + 12);
  const ByteVector fmt = file->readBlock(16);
  if(fmt.size() < 16) {
    debug("APE::Properties::analyzeOld() -- fmt header is too short.");
    return;
  }

  d->bitsPerSample = fmt.toShort(14, false);
}
<commit_msg>APE: Reduce useless File::Find() operations.<commit_after>/***************************************************************************
    copyright            : (C) 2010 by Alex Novichkov
    email                : novichko@atnet.ru

    copyright            : (C) 2006 by Lukáš Lalinský
    email                : lalinsky@gmail.com
                           (original WavPack implementation)
 ***************************************************************************/

/***************************************************************************
 *   This library is free software; you can redistribute it and/or modify  *
 *   it under the terms of the GNU Lesser General Public License version   *
 *   2.1 as published by the Free Software Foundation.                     *
 *                                                                         *
 *   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                                                       *
 *                                                                         *
 *   Alternatively, this file is available under the Mozilla Public        *
 *   License Version 1.1.  You may obtain a copy of the License at         *
 *   http://www.mozilla.org/MPL/                                           *
 ***************************************************************************/

#include <tstring.h>
#include <tdebug.h>
#include <bitset>
#include "id3v2tag.h"
#include "apeproperties.h"
#include "apefile.h"
#include "apetag.h"
#include "apefooter.h"

using namespace TagLib;

class APE::Properties::PropertiesPrivate
{
public:
  PropertiesPrivate() :
    length(0),
    bitrate(0),
    sampleRate(0),
    channels(0),
    version(0),
    bitsPerSample(0),
    sampleFrames(0) {}

  int length;
  int bitrate;
  int sampleRate;
  int channels;
  int version;
  int bitsPerSample;
  uint sampleFrames;
};

////////////////////////////////////////////////////////////////////////////////
// public members
////////////////////////////////////////////////////////////////////////////////

APE::Properties::Properties(File *file, ReadStyle style) :
  AudioProperties(style),
  d(new PropertiesPrivate())
{
  debug("APE::Properties::Properties() -- This constructor is no longer used.");
}

APE::Properties::Properties(File *file, long streamLength, ReadStyle style) :
  AudioProperties(style),
  d(new PropertiesPrivate())
{
  read(file, streamLength);
}

APE::Properties::~Properties()
{
  delete d;
}

int APE::Properties::length() const
{
  return lengthInSeconds();
}

int APE::Properties::lengthInSeconds() const
{
  return d->length / 1000;
}

int APE::Properties::lengthInMilliseconds() const
{
  return d->length;
}

int APE::Properties::bitrate() const
{
  return d->bitrate;
}

int APE::Properties::sampleRate() const
{
  return d->sampleRate;
}

int APE::Properties::channels() const
{
  return d->channels;
}

int APE::Properties::version() const
{
  return d->version;
}

int APE::Properties::bitsPerSample() const
{
  return d->bitsPerSample;
}

TagLib::uint APE::Properties::sampleFrames() const
{
  return d->sampleFrames;
}

////////////////////////////////////////////////////////////////////////////////
// private members
////////////////////////////////////////////////////////////////////////////////

namespace
{
  inline int headerVersion(const ByteVector &header)
  {
    if(header.size() < 6 || !header.startsWith("MAC "))
      return -1;

    return header.toUShort(4, false);
  }
}

void APE::Properties::read(File *file, long streamLength)
{
  // First, we assume that the file pointer is set at the first descriptor.
  long offset = file->tell();
  int version = headerVersion(file->readBlock(6));

  // Next, we look for the descriptor.
  if(version < 0) {
    offset = file->find("MAC ", offset);
    file->seek(offset);
    version = headerVersion(file->readBlock(6));
  }

  if(version < 0) {
    debug("APE::Properties::read() -- APE descriptor not found");
    return;
  }

  d->version = version;

  if(d->version >= 3980)
    analyzeCurrent(file);
  else
    analyzeOld(file);

  if(d->sampleFrames > 0 && d->sampleRate > 0) {
    const double length = d->sampleFrames * 1000.0 / d->sampleRate;
    d->length  = static_cast<int>(length + 0.5);
    d->bitrate = static_cast<int>(streamLength * 8.0 / length + 0.5);
  }
}

void APE::Properties::analyzeCurrent(File *file)
{
  // Read the descriptor
  file->seek(2, File::Current);
  const ByteVector descriptor = file->readBlock(44);
  if(descriptor.size() < 44) {
    debug("APE::Properties::analyzeCurrent() -- descriptor is too short.");
    return;
  }

  const uint descriptorBytes = descriptor.toUInt(0, false);

  if((descriptorBytes - 52) > 0)
    file->seek(descriptorBytes - 52, File::Current);

  // Read the header
  const ByteVector header = file->readBlock(24);
  if(header.size() < 24) {
    debug("APE::Properties::analyzeCurrent() -- MAC header is too short.");
    return;
  }

  // Get the APE info
  d->channels      = header.toShort(18, false);
  d->sampleRate    = header.toUInt(20, false);
  d->bitsPerSample = header.toShort(16, false);

  const uint totalFrames = header.toUInt(12, false);
  if(totalFrames == 0)
    return;

  const uint blocksPerFrame   = header.toUInt(4, false);
  const uint finalFrameBlocks = header.toUInt(8, false);
  d->sampleFrames = (totalFrames - 1) * blocksPerFrame + finalFrameBlocks;
}

void APE::Properties::analyzeOld(File *file)
{
  const ByteVector header = file->readBlock(26);
  if(header.size() < 26) {
    debug("APE::Properties::analyzeOld() -- MAC header is too short.");
    return;
  }

  const uint totalFrames = header.toUInt(18, false);

  // Fail on 0 length APE files (catches non-finalized APE files)
  if(totalFrames == 0)
    return;

  const short compressionLevel = header.toShort(0, false);
  uint blocksPerFrame;
  if(d->version >= 3950)
    blocksPerFrame = 73728 * 4;
  else if(d->version >= 3900 || (d->version >= 3800 && compressionLevel == 4000))
    blocksPerFrame = 73728;
  else
    blocksPerFrame = 9216;

  // Get the APE info
  d->channels   = header.toShort(4, false);
  d->sampleRate = header.toUInt(6, false);

  const uint finalFrameBlocks = header.toUInt(22, false);
  d->sampleFrames = (totalFrames - 1) * blocksPerFrame + finalFrameBlocks;

  // Get the bit depth from the RIFF-fmt chunk.
  file->seek(16, File::Current);
  const ByteVector fmt = file->readBlock(28);
  if(fmt.size() < 28 || !fmt.startsWith("WAVEfmt ")) {
    debug("APE::Properties::analyzeOld() -- fmt header is too short.");
    return;
  }

  d->bitsPerSample = fmt.toShort(26, false);
}
<|endoftext|>