<commit_before>/* Copyright (C) 2000-2006 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; version 2 of the License.

   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 */

/* Functions to handle keys and fields in forms */

#include "mysql_priv.h"
#include "sql_trigger.h"

	/*
	** Search after with key field is. If no key starts with field test
	** if field is part of some key.
	**
	** returns number of key. keylength is set to length of key before
	** (not including) field
	** Used when calculating key for NEXT_NUMBER
	*/

int find_ref_key(TABLE *table,Field *field, uint *key_length)
{
  reg2 int i;
  reg3 KEY *key_info;
  uint fieldpos;

  fieldpos=    field->offset();

	/* Test if some key starts as fieldpos */

  for (i= 0, key_info= table->key_info ;
       i < (int) table->s->keys ;
       i++, key_info++)
  {
    if (key_info->key_part[0].offset == fieldpos)
    {						/* Found key. Calc keylength */
      *key_length=0;
      return(i);			/* Use this key */
    }
  }
	/* Test if some key contains fieldpos */

  for (i= 0, key_info= table->key_info ;
       i < (int) table->s->keys ;
       i++, key_info++)
  {
    uint j;
    KEY_PART_INFO *key_part;
    *key_length=0;
    for (j=0, key_part=key_info->key_part ;
	 j < key_info->key_parts ;
	 j++, key_part++)
    {
      if (key_part->offset == fieldpos)
	return(i);			/* Use this key */
      *key_length+=key_part->store_length;
    }
  }
  return(-1);					/* No key is ok */
}

/*
  Copy part of a record that forms a key or key prefix to a buffer.

  SYNOPSIS
    key_copy()
    to_key      buffer that will be used as a key
    from_record full record to be copied from
    key_info    descriptor of the index
    key_length  specifies length of all keyparts that will be copied

  DESCRIPTION
    The function takes a complete table record (as e.g. retrieved by
    handler::index_read()), and a description of an index on the same table,
    and extracts the first key_length bytes of the record which are part of a
    key into to_key. If length == 0 then copy all bytes from the record that
    form a key.

  RETURN
    None
*/

void key_copy(byte *to_key, byte *from_record, KEY *key_info, uint key_length)
{
  uint length;
  KEY_PART_INFO *key_part;

  if (key_length == 0)
    key_length= key_info->key_length;
  for (key_part= key_info->key_part; (int) key_length > 0; key_part++)
  {
    if (key_part->null_bit)
    {
      *to_key++= test(from_record[key_part->null_offset] &
		   key_part->null_bit);
      key_length--;
    }
    if (key_part->type == HA_KEYTYPE_BIT)
    {
      Field_bit *field= (Field_bit *) (key_part->field);
      if (field->bit_len)
      {
        uchar bits= get_rec_bits((uchar*) from_record +
                                 key_part->null_offset +
                                 (key_part->null_bit == 128),
                                 field->bit_ofs, field->bit_len);
        *to_key++= bits;
        key_length--;
      }
    }
    if (key_part->key_part_flag & HA_BLOB_PART ||
        key_part->key_part_flag & HA_VAR_LENGTH_PART)
    {
      key_length-= HA_KEY_BLOB_LENGTH;
      length= min(key_length, key_part->length);
      key_part->field->get_key_image(to_key, length, Field::itRAW);
      to_key+= HA_KEY_BLOB_LENGTH;
    }
    else
    {
      length= min(key_length, key_part->length);
      Field *field= key_part->field;
      CHARSET_INFO *cs= field->charset();
      uint bytes= field->get_key_image((char*) to_key, length, Field::itRAW);
      if (bytes < length)
        cs->cset->fill(cs, (char*) to_key + bytes, length - bytes, ' ');
    }
    to_key+= length;
    key_length-= length;
  }
}

/*
  Restore a key from some buffer to record.

  SYNOPSIS
    key_restore()
    to_record   record buffer where the key will be restored to
    from_key    buffer that contains a key
    key_info    descriptor of the index
    key_length  specifies length of all keyparts that will be restored

  DESCRIPTION
    This function converts a key into record format. It can be used in cases
    when we want to return a key as a result row.

  RETURN
    None
*/

void key_restore(byte *to_record, byte *from_key, KEY *key_info,
                 uint key_length)
{
  uint length;
  KEY_PART_INFO *key_part;

  if (key_length == 0)
  {
    key_length= key_info->key_length;
  }
  for (key_part= key_info->key_part ; (int) key_length > 0 ; key_part++)
  {
    if (key_part->null_bit)
    {
      if (*from_key++)
	to_record[key_part->null_offset]|= key_part->null_bit;
      else
	to_record[key_part->null_offset]&= ~key_part->null_bit;
      key_length--;
    }
    if (key_part->type == HA_KEYTYPE_BIT)
    {
      Field_bit *field= (Field_bit *) (key_part->field);
      if (field->bit_len)
      {
        uchar bits= *(from_key + key_part->length -
                      field->pack_length_in_rec() - 1);
        set_rec_bits(bits, to_record + key_part->null_offset +
                     (key_part->null_bit == 128),
                     field->bit_ofs, field->bit_len);
      }
    }
    if (key_part->key_part_flag & HA_BLOB_PART)
    {
      uint blob_length= uint2korr(from_key);
      from_key+= HA_KEY_BLOB_LENGTH;
      key_length-= HA_KEY_BLOB_LENGTH;
      ((Field_blob*) key_part->field)->set_ptr((ulong) blob_length,
					       (char*) from_key);
      length= key_part->length;
    }
    else if (key_part->key_part_flag & HA_VAR_LENGTH_PART)
    {
      key_length-= HA_KEY_BLOB_LENGTH;
      length= min(key_length, key_part->length);
      key_part->field->set_key_image((char *) from_key, length);
      from_key+= HA_KEY_BLOB_LENGTH;
    }
    else
    {
      length= min(key_length, key_part->length);
      memcpy(to_record + key_part->offset, from_key, (size_t) length);
    }
    from_key+= length;
    key_length-= length;
  }
}

/*
  Compare if a key has changed

  SYNOPSIS
    key_cmp_if_same()
    table		TABLE
    key			key to compare to row
    idx			Index used
    key_length		Length of key

  NOTES
    In theory we could just call field->cmp() for all field types,
    but as we are only interested if a key has changed (not if the key is
    larger or smaller than the previous value) we can do things a bit
    faster by using memcmp() instead.

  RETURN
    0	If key is equal
    1	Key has changed
*/

bool key_cmp_if_same(TABLE *table,const byte *key,uint idx,uint key_length)
{
  uint store_length;
  KEY_PART_INFO *key_part;
  const byte *key_end= key + key_length;;

  for (key_part=table->key_info[idx].key_part;
       key < key_end ;
       key_part++, key+= store_length)
  {
    uint length;
    store_length= key_part->store_length;

    if (key_part->null_bit)
    {
      if (*key != test(table->record[0][key_part->null_offset] &
		       key_part->null_bit))
	return 1;
      if (*key)
	continue;
      key++;
      store_length--;
    }
    if (key_part->key_part_flag & (HA_BLOB_PART | HA_VAR_LENGTH_PART |
                                   HA_BIT_PART))
    {
      if (key_part->field->key_cmp(key, key_part->length))
	return 1;
      continue;
    }
    length= min((uint) (key_end-key), store_length);
    if (!(key_part->key_type & (FIELDFLAG_NUMBER+FIELDFLAG_BINARY+
                                FIELDFLAG_PACK)))
    {
      CHARSET_INFO *cs= key_part->field->charset();
      uint char_length= key_part->length / cs->mbmaxlen;
      const byte *pos= table->record[0] + key_part->offset;
      if (length > char_length)
      {
        char_length= my_charpos(cs, pos, pos + length, char_length);
        set_if_smaller(char_length, length);
      }
      if (cs->coll->strnncollsp(cs,
                                (const uchar*) key, length,
                                (const uchar*) pos, char_length, 0))
        return 1;
      continue;
    }
    if (memcmp(key,table->record[0]+key_part->offset,length))
      return 1;
  }
  return 0;
}

	/* unpack key-fields from record to some buffer */
	/* This is used to get a good error message */

void key_unpack(String *to,TABLE *table,uint idx)
{
  KEY_PART_INFO *key_part,*key_part_end;
  Field *field;
  String tmp;
  DBUG_ENTER("key_unpack");

  to->length(0);
  for (key_part=table->key_info[idx].key_part,key_part_end=key_part+
	 table->key_info[idx].key_parts ;
       key_part < key_part_end;
       key_part++)
  {
    if (to->length())
      to->append('-');
    if (key_part->null_bit)
    {
      if (table->record[0][key_part->null_offset] & key_part->null_bit)
      {
	to->append(STRING_WITH_LEN("NULL"));
	continue;
      }
    }
    if ((field=key_part->field))
    {
      field->val_str(&tmp);
      if (key_part->length < field->pack_length())
	tmp.length(min(tmp.length(),key_part->length));
      to->append(tmp);
    }
    else
      to->append(STRING_WITH_LEN("???"));
  }
  DBUG_VOID_RETURN;
}

/*
  Check if key uses field that is listed in passed field list or is
  automatically updated (like a timestamp) or can be updated by before
  update trigger defined on the table.

  SYNOPSIS
    is_key_used()
      table   TABLE object with which keys and fields are associated.
      idx     Key to be checked.
      fields  List of fields to be checked.

  RETURN VALUE
    TRUE   Key uses field which meets one the above conditions
    FALSE  Otherwise
*/

bool is_key_used(TABLE *table, uint idx, List<Item> &fields)
{
  Table_triggers_list *triggers= table->triggers;
  List_iterator_fast<Item> f(fields);
  KEY_PART_INFO *key_part,*key_part_end;
  for (key_part=table->key_info[idx].key_part,key_part_end=key_part+
	 table->key_info[idx].key_parts ;
       key_part < key_part_end;
       key_part++)
  {
    Item_field *field;

    if (key_part->field == table->timestamp_field)
      return 1;					// Can't be used for update

    f.rewind();
    while ((field=(Item_field*) f++))
    {
      if (key_part->field->eq(field->field))
	return 1;
    }
    if (triggers &&
        triggers->is_updated_in_before_update_triggers(key_part->field))
      return 1;
  }

  /*
    If table handler has primary key as part of the index, check that primary
    key is not updated
  */
  if (idx != table->s->primary_key && table->s->primary_key < MAX_KEY &&
      (table->file->table_flags() & HA_PRIMARY_KEY_IN_READ_INDEX))
    return is_key_used(table, table->s->primary_key, fields);
  return 0;
}

/*
  Compare key in row to a given key

  SYNOPSIS
    key_cmp()
    key_part		Key part handler
    key			Key to compare to value in table->record[0]
    key_length		length of 'key'

  RETURN
    The return value is SIGN(key_in_row - range_key):

    0			Key is equal to range or 'range' == 0 (no range)
   -1			Key is less than range
    1			Key is larger than range
*/

int key_cmp(KEY_PART_INFO *key_part, const byte *key, uint key_length)
{
  uint store_length;

  for (const byte *end=key + key_length;
       key < end;
       key+= store_length, key_part++)
  {
    int cmp;
    store_length= key_part->store_length;
    if (key_part->null_bit)
    {
      /* This key part allows null values; NULL is lower than everything */
      register bool field_is_null= key_part->field->is_null();
      if (*key)                                 // If range key is null
      {
	/* the range is expecting a null value */
	if (!field_is_null)
	  return 1;                             // Found key is > range
        /* null -- exact match, go to next key part */
	continue;
      }
      else if (field_is_null)
	return -1;                              // NULL is less than any value
      key++;					// Skip null byte
      store_length--;
    }
    if ((cmp=key_part->field->key_cmp((byte*) key, key_part->length)) < 0)
      return -1;
    if (cmp > 0)
      return 1;
  }
  return 0;                                     // Keys are equal
}
<commit_msg>Patch to eliminate compilation errors under VC after bug #13191 fix.<commit_after>/* Copyright (C) 2000-2006 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; version 2 of the License.

   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 */

/* Functions to handle keys and fields in forms */

#include "mysql_priv.h"
#include "sql_trigger.h"

	/*
	** Search after with key field is. If no key starts with field test
	** if field is part of some key.
	**
	** returns number of key. keylength is set to length of key before
	** (not including) field
	** Used when calculating key for NEXT_NUMBER
	*/

int find_ref_key(TABLE *table,Field *field, uint *key_length)
{
  reg2 int i;
  reg3 KEY *key_info;
  uint fieldpos;

  fieldpos=    field->offset();

	/* Test if some key starts as fieldpos */

  for (i= 0, key_info= table->key_info ;
       i < (int) table->s->keys ;
       i++, key_info++)
  {
    if (key_info->key_part[0].offset == fieldpos)
    {						/* Found key. Calc keylength */
      *key_length=0;
      return(i);			/* Use this key */
    }
  }
	/* Test if some key contains fieldpos */

  for (i= 0, key_info= table->key_info ;
       i < (int) table->s->keys ;
       i++, key_info++)
  {
    uint j;
    KEY_PART_INFO *key_part;
    *key_length=0;
    for (j=0, key_part=key_info->key_part ;
	 j < key_info->key_parts ;
	 j++, key_part++)
    {
      if (key_part->offset == fieldpos)
	return(i);			/* Use this key */
      *key_length+=key_part->store_length;
    }
  }
  return(-1);					/* No key is ok */
}

/*
  Copy part of a record that forms a key or key prefix to a buffer.

  SYNOPSIS
    key_copy()
    to_key      buffer that will be used as a key
    from_record full record to be copied from
    key_info    descriptor of the index
    key_length  specifies length of all keyparts that will be copied

  DESCRIPTION
    The function takes a complete table record (as e.g. retrieved by
    handler::index_read()), and a description of an index on the same table,
    and extracts the first key_length bytes of the record which are part of a
    key into to_key. If length == 0 then copy all bytes from the record that
    form a key.

  RETURN
    None
*/

void key_copy(byte *to_key, byte *from_record, KEY *key_info, uint key_length)
{
  uint length;
  KEY_PART_INFO *key_part;

  if (key_length == 0)
    key_length= key_info->key_length;
  for (key_part= key_info->key_part; (int) key_length > 0; key_part++)
  {
    if (key_part->null_bit)
    {
      *to_key++= test(from_record[key_part->null_offset] &
		   key_part->null_bit);
      key_length--;
    }
    if (key_part->type == HA_KEYTYPE_BIT)
    {
      Field_bit *field= (Field_bit *) (key_part->field);
      if (field->bit_len)
      {
        uchar bits= get_rec_bits((uchar*) from_record +
                                 key_part->null_offset +
                                 (key_part->null_bit == 128),
                                 field->bit_ofs, field->bit_len);
        *to_key++= bits;
        key_length--;
      }
    }
    if (key_part->key_part_flag & HA_BLOB_PART ||
        key_part->key_part_flag & HA_VAR_LENGTH_PART)
    {
      key_length-= HA_KEY_BLOB_LENGTH;
      length= min(key_length, key_part->length);
      key_part->field->get_key_image((char*) to_key, length, Field::itRAW);
      to_key+= HA_KEY_BLOB_LENGTH;
    }
    else
    {
      length= min(key_length, key_part->length);
      Field *field= key_part->field;
      CHARSET_INFO *cs= field->charset();
      uint bytes= field->get_key_image((char*) to_key, length, Field::itRAW);
      if (bytes < length)
        cs->cset->fill(cs, (char*) to_key + bytes, length - bytes, ' ');
    }
    to_key+= length;
    key_length-= length;
  }
}

/*
  Restore a key from some buffer to record.

  SYNOPSIS
    key_restore()
    to_record   record buffer where the key will be restored to
    from_key    buffer that contains a key
    key_info    descriptor of the index
    key_length  specifies length of all keyparts that will be restored

  DESCRIPTION
    This function converts a key into record format. It can be used in cases
    when we want to return a key as a result row.

  RETURN
    None
*/

void key_restore(byte *to_record, byte *from_key, KEY *key_info,
                 uint key_length)
{
  uint length;
  KEY_PART_INFO *key_part;

  if (key_length == 0)
  {
    key_length= key_info->key_length;
  }
  for (key_part= key_info->key_part ; (int) key_length > 0 ; key_part++)
  {
    if (key_part->null_bit)
    {
      if (*from_key++)
	to_record[key_part->null_offset]|= key_part->null_bit;
      else
	to_record[key_part->null_offset]&= ~key_part->null_bit;
      key_length--;
    }
    if (key_part->type == HA_KEYTYPE_BIT)
    {
      Field_bit *field= (Field_bit *) (key_part->field);
      if (field->bit_len)
      {
        uchar bits= *(from_key + key_part->length -
                      field->pack_length_in_rec() - 1);
        set_rec_bits(bits, to_record + key_part->null_offset +
                     (key_part->null_bit == 128),
                     field->bit_ofs, field->bit_len);
      }
    }
    if (key_part->key_part_flag & HA_BLOB_PART)
    {
      uint blob_length= uint2korr(from_key);
      from_key+= HA_KEY_BLOB_LENGTH;
      key_length-= HA_KEY_BLOB_LENGTH;
      ((Field_blob*) key_part->field)->set_ptr((ulong) blob_length,
					       (char*) from_key);
      length= key_part->length;
    }
    else if (key_part->key_part_flag & HA_VAR_LENGTH_PART)
    {
      key_length-= HA_KEY_BLOB_LENGTH;
      length= min(key_length, key_part->length);
      key_part->field->set_key_image((char *) from_key, length);
      from_key+= HA_KEY_BLOB_LENGTH;
    }
    else
    {
      length= min(key_length, key_part->length);
      memcpy(to_record + key_part->offset, from_key, (size_t) length);
    }
    from_key+= length;
    key_length-= length;
  }
}

/*
  Compare if a key has changed

  SYNOPSIS
    key_cmp_if_same()
    table		TABLE
    key			key to compare to row
    idx			Index used
    key_length		Length of key

  NOTES
    In theory we could just call field->cmp() for all field types,
    but as we are only interested if a key has changed (not if the key is
    larger or smaller than the previous value) we can do things a bit
    faster by using memcmp() instead.

  RETURN
    0	If key is equal
    1	Key has changed
*/

bool key_cmp_if_same(TABLE *table,const byte *key,uint idx,uint key_length)
{
  uint store_length;
  KEY_PART_INFO *key_part;
  const byte *key_end= key + key_length;;

  for (key_part=table->key_info[idx].key_part;
       key < key_end ;
       key_part++, key+= store_length)
  {
    uint length;
    store_length= key_part->store_length;

    if (key_part->null_bit)
    {
      if (*key != test(table->record[0][key_part->null_offset] &
		       key_part->null_bit))
	return 1;
      if (*key)
	continue;
      key++;
      store_length--;
    }
    if (key_part->key_part_flag & (HA_BLOB_PART | HA_VAR_LENGTH_PART |
                                   HA_BIT_PART))
    {
      if (key_part->field->key_cmp(key, key_part->length))
	return 1;
      continue;
    }
    length= min((uint) (key_end-key), store_length);
    if (!(key_part->key_type & (FIELDFLAG_NUMBER+FIELDFLAG_BINARY+
                                FIELDFLAG_PACK)))
    {
      CHARSET_INFO *cs= key_part->field->charset();
      uint char_length= key_part->length / cs->mbmaxlen;
      const byte *pos= table->record[0] + key_part->offset;
      if (length > char_length)
      {
        char_length= my_charpos(cs, pos, pos + length, char_length);
        set_if_smaller(char_length, length);
      }
      if (cs->coll->strnncollsp(cs,
                                (const uchar*) key, length,
                                (const uchar*) pos, char_length, 0))
        return 1;
      continue;
    }
    if (memcmp(key,table->record[0]+key_part->offset,length))
      return 1;
  }
  return 0;
}

	/* unpack key-fields from record to some buffer */
	/* This is used to get a good error message */

void key_unpack(String *to,TABLE *table,uint idx)
{
  KEY_PART_INFO *key_part,*key_part_end;
  Field *field;
  String tmp;
  DBUG_ENTER("key_unpack");

  to->length(0);
  for (key_part=table->key_info[idx].key_part,key_part_end=key_part+
	 table->key_info[idx].key_parts ;
       key_part < key_part_end;
       key_part++)
  {
    if (to->length())
      to->append('-');
    if (key_part->null_bit)
    {
      if (table->record[0][key_part->null_offset] & key_part->null_bit)
      {
	to->append(STRING_WITH_LEN("NULL"));
	continue;
      }
    }
    if ((field=key_part->field))
    {
      field->val_str(&tmp);
      if (key_part->length < field->pack_length())
	tmp.length(min(tmp.length(),key_part->length));
      to->append(tmp);
    }
    else
      to->append(STRING_WITH_LEN("???"));
  }
  DBUG_VOID_RETURN;
}

/*
  Check if key uses field that is listed in passed field list or is
  automatically updated (like a timestamp) or can be updated by before
  update trigger defined on the table.

  SYNOPSIS
    is_key_used()
      table   TABLE object with which keys and fields are associated.
      idx     Key to be checked.
      fields  List of fields to be checked.

  RETURN VALUE
    TRUE   Key uses field which meets one the above conditions
    FALSE  Otherwise
*/

bool is_key_used(TABLE *table, uint idx, List<Item> &fields)
{
  Table_triggers_list *triggers= table->triggers;
  List_iterator_fast<Item> f(fields);
  KEY_PART_INFO *key_part,*key_part_end;
  for (key_part=table->key_info[idx].key_part,key_part_end=key_part+
	 table->key_info[idx].key_parts ;
       key_part < key_part_end;
       key_part++)
  {
    Item_field *field;

    if (key_part->field == table->timestamp_field)
      return 1;					// Can't be used for update

    f.rewind();
    while ((field=(Item_field*) f++))
    {
      if (key_part->field->eq(field->field))
	return 1;
    }
    if (triggers &&
        triggers->is_updated_in_before_update_triggers(key_part->field))
      return 1;
  }

  /*
    If table handler has primary key as part of the index, check that primary
    key is not updated
  */
  if (idx != table->s->primary_key && table->s->primary_key < MAX_KEY &&
      (table->file->table_flags() & HA_PRIMARY_KEY_IN_READ_INDEX))
    return is_key_used(table, table->s->primary_key, fields);
  return 0;
}

/*
  Compare key in row to a given key

  SYNOPSIS
    key_cmp()
    key_part		Key part handler
    key			Key to compare to value in table->record[0]
    key_length		length of 'key'

  RETURN
    The return value is SIGN(key_in_row - range_key):

    0			Key is equal to range or 'range' == 0 (no range)
   -1			Key is less than range
    1			Key is larger than range
*/

int key_cmp(KEY_PART_INFO *key_part, const byte *key, uint key_length)
{
  uint store_length;

  for (const byte *end=key + key_length;
       key < end;
       key+= store_length, key_part++)
  {
    int cmp;
    store_length= key_part->store_length;
    if (key_part->null_bit)
    {
      /* This key part allows null values; NULL is lower than everything */
      register bool field_is_null= key_part->field->is_null();
      if (*key)                                 // If range key is null
      {
	/* the range is expecting a null value */
	if (!field_is_null)
	  return 1;                             // Found key is > range
        /* null -- exact match, go to next key part */
	continue;
      }
      else if (field_is_null)
	return -1;                              // NULL is less than any value
      key++;					// Skip null byte
      store_length--;
    }
    if ((cmp=key_part->field->key_cmp((byte*) key, key_part->length)) < 0)
      return -1;
    if (cmp > 0)
      return 1;
  }
  return 0;                                     // Keys are equal
}
<|endoftext|>


<commit_before>// Copyright (c) 2010-2021, Lawrence Livermore National Security, LLC. Produced
// at the Lawrence Livermore National Laboratory. All Rights reserved. See files
// LICENSE and NOTICE for details. LLNL-CODE-806117.
//
// This file is part of the MFEM library. For more information and source code
// availability visit https://mfem.org.
//
// MFEM is free software; you can redistribute it and/or modify it under the
// terms of the BSD-3 license. We welcome feedback and contributions, see file
// CONTRIBUTING.md for details.

#include "lor.hpp"
#include "lor_assembly.hpp"
#include "../linalg/dtensor.hpp"
#include "../general/forall.hpp"

#define MFEM_DEBUG_COLOR 226
#include "../general/debug.hpp"

#define MFEM_NVTX_COLOR DarkOrchid
#include "../general/nvtx.hpp"

namespace mfem
{

// Defined in lor_assembly_ker.cpp
template <int order, bool use_smem = true>
void Assemble3DBatchedLOR(const Array<int> &dof_glob2loc,
                          const Array<int> &dof_glob2loc_offsets,
                          const Array<int> &el_dof_lex,
                          Mesh &mesh_ho,
                          SparseMatrix &A_mat);

static void AssembleBatchedLORWithoutBC(LORBase &lor_disc,
                                        BilinearForm &form_lor,
                                        FiniteElementSpace &fes_ho,
                                        OperatorHandle &Ah)
{
   MFEM_NVTX;
   Mesh &mesh_ho = *fes_ho.GetMesh();
   const int dim = mesh_ho.Dimension();
   const int order = fes_ho.GetMaxElementOrder();
   dbg("dim:%d order:%d",dim,order);

   const bool has_to_init = Ah.Ptr() == nullptr;
   dbg("has_to_init: %s", has_to_init?"yes":"no");
   SparseMatrix *A = has_to_init ? nullptr : Ah.As<SparseMatrix>();

   dbg("GetLORRestriction");
   const LORRestriction *R = lor_disc.GetLORRestriction();
   MFEM_VERIFY(R,"LOR Restriction error!");

   if (has_to_init)
   {
      MFEM_VERIFY(UsesTensorBasis(fes_ho),
                  "Batched LOR assembly requires tensor basis");
      if (Device::IsEnabled() || true)
      {
         dbg("Device::IsEnabled()");
#ifdef MFEM_USE_MPI
         ParFiniteElementSpace *pfes_ho = dynamic_cast<ParFiniteElementSpace*>(&fes_ho);
         if (pfes_ho)
         {
            dbg("Device::IsEnabled() and multiple ranks!");
            const int width = pfes_ho->GetVSize();
            const int height = pfes_ho->GetVSize();
            dbg("HxW: %dx%d",height,width);
            A = new SparseMatrix(height, width, 0);
         }
         else
#endif
         {
            dbg("Device::IsEnabled() but one rank!");
            const int width = fes_ho.GetVSize();
            const int height = fes_ho.GetVSize();
            dbg("HxW: %dx%d",height,width);
            A = new SparseMatrix(height, width, 0);
         }
         A->GetMemoryI().New(A->Height()+1, A->GetMemoryI().GetMemoryType());
         const int nnz = R->FillI(*A);
         A->GetMemoryJ().New(nnz, A->GetMemoryJ().GetMemoryType());
         A->GetMemoryData().New(nnz, A->GetMemoryData().GetMemoryType());
         R->FillJAndZeroData(*A); // J, A = 0.0
      }
      else
      {
         MFEM_ABORT("");
         dbg("NOT Device::IsEnabled()");
         // the sparsity pattern is defined from the map: element->dof
         const int ndofs = fes_ho.GetVSize();
         dbg("ndofs:%d",ndofs);
         const Table &elem_dof = form_lor.FESpace()->GetElementToDofTable();
         Table dof_dof, dof_elem;
         Transpose(elem_dof, dof_elem, ndofs);
         mfem::Mult(dof_elem, elem_dof, dof_dof);
         dof_dof.SortRows();
         int *I = dof_dof.GetI();
         int *J = dof_dof.GetJ();
         double *data = Memory<double>(I[ndofs]);
         A = new SparseMatrix(I,J,data,ndofs,ndofs,true,true,true);
         dof_dof.LoseData();
         *A = 0.0;
         dbg("done");
      }
   }

   void (*Kernel)(const Array<int> &dof_glob2loc,
                  const Array<int> &dof_glob2loc_offsets,
                  const Array<int> &el_dof_lex,
                  Mesh &mesh_ho,
                  SparseMatrix &A_mat) = nullptr;

   if (dim == 2) { MFEM_ABORT("Unsuported!"); }
   else if (dim == 3)
   {
      switch (order)
      {
         case 1: Kernel = Assemble3DBatchedLOR<1>; break;
         case 2: Kernel = Assemble3DBatchedLOR<2>; break;
         case 3: Kernel = Assemble3DBatchedLOR<3>; break;
         case 4: Kernel = Assemble3DBatchedLOR<4>; break;
         case 5: Kernel = Assemble3DBatchedLOR<5>; break;
         case 6: Kernel = Assemble3DBatchedLOR<6,false>; break;
         case 7: Kernel = Assemble3DBatchedLOR<7,false>; break;
         case 8: Kernel = Assemble3DBatchedLOR<8,false>; break;
         case 9: Kernel = Assemble3DBatchedLOR<9,false>; break;
         case 10: Kernel = Assemble3DBatchedLOR<10,false>; break;
         case 11: Kernel = Assemble3DBatchedLOR<11,false>; break;
         case 12: Kernel = Assemble3DBatchedLOR<12,false>; break;
         case 13: Kernel = Assemble3DBatchedLOR<13,false>; break;
         case 14: Kernel = Assemble3DBatchedLOR<14,false>; break;
         case 15: Kernel = Assemble3DBatchedLOR<15,false>; break;
         case 16: Kernel = Assemble3DBatchedLOR<16,false>; break;
         default: MFEM_ABORT("Kernel not ready!");
      }
   }

   Kernel(R->Indices(),
          R->Offsets(),
          R->GatherMap(),
          mesh_ho, *A);

   A->Finalize();

   if (has_to_init) { Ah.Reset(A); } // A now owns A_mat
}

void AssembleBatchedLOR(LORBase &lor_disc,
                        BilinearForm &form_lor,
                        FiniteElementSpace &fes_ho,
                        const Array<int> &ess_dofs,
                        OperatorHandle &Ah)
{
   MFEM_NVTX;
   AssembleBatchedLORWithoutBC(lor_disc, form_lor, fes_ho, Ah);

   // Set essential dofs to 0.0
   const int n_ess_dofs = ess_dofs.Size();
   const auto ess_dofs_d = ess_dofs.Read();

   const auto I = Ah.As<SparseMatrix>()->ReadI();
   const auto J = Ah.As<SparseMatrix>()->ReadJ();
   auto dA = Ah.As<SparseMatrix>()->ReadWriteData();

   MFEM_FORALL(i, n_ess_dofs,
   {
      const int idof = ess_dofs_d[i];
      for (int j=I[idof]; j<I[idof+1]; ++j)
      {
         const int jdof = J[j];
         if (jdof != idof)
         {
            dA[j] = 0.0;
            for (int k=I[jdof]; k<I[jdof+1]; ++k)
            {
               if (J[k] == idof)
               {
                  dA[k] = 0.0;
                  break;
               }
            }
         }
      }
   });
}

#ifdef MFEM_USE_MPI

void ParAssembleBatchedLOR(LORBase &lor_disc,
                           BilinearForm &form_lor,
                           FiniteElementSpace &fes_ho,
                           const Array<int> &ess_dofs,
                           OperatorHandle &Ah)
{
   dbg();
   MFEM_NVTX;
   ParFiniteElementSpace *pfes_ho = dynamic_cast<ParFiniteElementSpace*>(&fes_ho);
   assert(pfes_ho);

   OperatorHandle A_local;
   AssembleBatchedLORWithoutBC(lor_disc, form_lor, fes_ho, A_local);
   MFEM_VERIFY(A_local.As<SparseMatrix>()->Finalized(),
               "the local matrix must be finalized");

   OperatorHandle dA(Operator::Hypre_ParCSR),
                  Ph(Operator::Hypre_ParCSR);
   {
      NVTX("MakeSquareBlockDiag");
      dA.MakeSquareBlockDiag(pfes_ho->GetComm(), pfes_ho->GlobalVSize(),
                             pfes_ho->GetDofOffsets(), A_local.As<SparseMatrix>());
   }
   Ph.ConvertFrom(pfes_ho->Dof_TrueDof_Matrix());

   {
      NVTX("MakePtAP");
      Ah.MakePtAP(dA, Ph);
   }

   // {
   //    int a;
   //    ( a = 2, a+2);
   //    NVTX("EliminateRowsCols");
   //    Ah.As<HypreParMatrix>()->EliminateRowsCols(ess_dofs);
   // }

   // BCs

   HypreParMatrix *A_mat = Ah.As<HypreParMatrix>();
   hypre_ParCSRMatrix *A = *A_mat;
   A_mat->HypreReadWrite();

   hypre_CSRMatrix *diag = hypre_ParCSRMatrixDiag(A);
   hypre_CSRMatrix *offd = hypre_ParCSRMatrixOffd(A);

   const int n_ess_dofs = ess_dofs.Size();
   const auto ess_dofs_d = ess_dofs.Read();

   // Eliminate rows and columns in the diagonal block
   {
      const auto I = diag->i;
      const auto J = diag->j;
      auto data = diag->data;

      MFEM_FORALL(i, n_ess_dofs,
      {
         const int idof = ess_dofs_d[i];
         for (int j=I[idof]; j<I[idof+1]; ++j)
         {
            const int jdof = J[j];
            if (jdof != idof)
            {
               data[j] = 0.0;
               for (int k=I[jdof]; k<I[jdof+1]; ++k)
               {
                  if (J[k] == idof)
                  {
                     data[k] = 0.0;
                     break;
                  }
               }
            }
         }
      });
   }

   // Eliminate rows in the off-diagonal block
   {
      const auto I = offd->i;
      auto data = offd->data;
      MFEM_FORALL(i, n_ess_dofs,
      {
         const int idof = ess_dofs_d[i];
         for (int j=I[idof]; j<I[idof+1]; ++j)
         {
            data[j] = 0.0;
         }
      });
   }

   // Figure out which columns need to be eliminated in the off-diagonal block
   Array<HYPRE_Int> cols_to_eliminate;
   {
      ess_dofs.HostRead();

      hypre_ParCSRCommHandle *comm_handle;
      hypre_ParCSRCommPkg *comm_pkg;
      HYPRE_Int num_sends, *int_buf_data;
      HYPRE_Int index, start;
      HYPRE_Int i, j, k;

      HYPRE_Int diag_nrows       = hypre_CSRMatrixNumRows(diag);
      HYPRE_Int offd_ncols       = hypre_CSRMatrixNumCols(offd);

      HYPRE_Int *eliminate_row = hypre_CTAlloc(HYPRE_Int,  diag_nrows,
                                               HYPRE_MEMORY_HOST);
      HYPRE_Int *eliminate_col = hypre_CTAlloc(HYPRE_Int,  offd_ncols,
                                               HYPRE_MEMORY_HOST);

      /* make sure A has a communication package */
      comm_pkg = hypre_ParCSRMatrixCommPkg(A);
      if (!comm_pkg)
      {
         hypre_MatvecCommPkgCreate(A);
         comm_pkg = hypre_ParCSRMatrixCommPkg(A);
      }

      /* which of the local rows are to be eliminated */
      for (i = 0; i < diag_nrows; i++)
      {
         eliminate_row[i] = 0;
      }
      for (i = 0; i < n_ess_dofs; i++)
      {
         eliminate_row[ess_dofs[i]] = 1;
      }

      /* use a Matvec communication pattern to find (in eliminate_col)
         which of the local offd columns are to be eliminated */
      num_sends = hypre_ParCSRCommPkgNumSends(comm_pkg);
      int_buf_data = hypre_CTAlloc(HYPRE_Int,
                                   hypre_ParCSRCommPkgSendMapStart(comm_pkg,
                                                                   num_sends), HYPRE_MEMORY_HOST);
      index = 0;
      for (i = 0; i < num_sends; i++)
      {
         start = hypre_ParCSRCommPkgSendMapStart(comm_pkg, i);
         for (j = start; j < hypre_ParCSRCommPkgSendMapStart(comm_pkg, i+1); j++)
         {
            k = hypre_ParCSRCommPkgSendMapElmt(comm_pkg,j);
            int_buf_data[index++] = eliminate_row[k];
         }
      }
      comm_handle = hypre_ParCSRCommHandleCreate(11, comm_pkg,
                                                 int_buf_data, eliminate_col);
      hypre_ParCSRCommHandleDestroy(comm_handle);

      /* set the array cols_to_eliminate */
      int ncols_to_eliminate = 0;
      for (i = 0; i < offd_ncols; i++)
         if (eliminate_col[i])
         {
            ncols_to_eliminate++;
         }

      cols_to_eliminate.SetSize(ncols_to_eliminate);
      cols_to_eliminate = 0.0;

      ncols_to_eliminate = 0;
      for (i = 0; i < offd_ncols; i++)
         if (eliminate_col[i])
         {
            cols_to_eliminate[ncols_to_eliminate++] = i;
         }

      hypre_TFree(int_buf_data, HYPRE_MEMORY_HOST);
      hypre_TFree(eliminate_row, HYPRE_MEMORY_HOST);
      hypre_TFree(eliminate_col, HYPRE_MEMORY_HOST);
   }

   // Eliminate columns in the off-diagonal block
   {
      const int ncols_to_eliminate = cols_to_eliminate.Size();
      const int nrows_offd = hypre_CSRMatrixNumRows(offd);
      const auto cols = cols_to_eliminate.Read();
      const auto I = offd->i;
      const auto J = offd->i;
      auto data = offd->data;
      MFEM_FORALL(idx, ncols_to_eliminate,
      {
         int j = cols[idx];
         for (int i=0; i<nrows_offd; ++i)
         {
            for (int jj=I[i]; jj<I[i+1]; ++jj)
            {
               if (J[jj] == j)
               {
                  data[jj] = 0.0;
                  break;
               }
            }
         }
      });
   }
}

#endif

} // namespace mfem
<commit_msg>Try to overlap MPI communication and GPU kernels in BC elimination<commit_after>// Copyright (c) 2010-2021, Lawrence Livermore National Security, LLC. Produced
// at the Lawrence Livermore National Laboratory. All Rights reserved. See files
// LICENSE and NOTICE for details. LLNL-CODE-806117.
//
// This file is part of the MFEM library. For more information and source code
// availability visit https://mfem.org.
//
// MFEM is free software; you can redistribute it and/or modify it under the
// terms of the BSD-3 license. We welcome feedback and contributions, see file
// CONTRIBUTING.md for details.

#include "lor.hpp"
#include "lor_assembly.hpp"
#include "../linalg/dtensor.hpp"
#include "../general/forall.hpp"

#define MFEM_DEBUG_COLOR 226
#include "../general/debug.hpp"

#define MFEM_NVTX_COLOR DarkOrchid
#include "../general/nvtx.hpp"

namespace mfem
{

// Defined in lor_assembly_ker.cpp
template <int order, bool use_smem = true>
void Assemble3DBatchedLOR(const Array<int> &dof_glob2loc,
                          const Array<int> &dof_glob2loc_offsets,
                          const Array<int> &el_dof_lex,
                          Mesh &mesh_ho,
                          SparseMatrix &A_mat);

static void AssembleBatchedLORWithoutBC(LORBase &lor_disc,
                                        BilinearForm &form_lor,
                                        FiniteElementSpace &fes_ho,
                                        OperatorHandle &Ah)
{
   MFEM_NVTX;
   Mesh &mesh_ho = *fes_ho.GetMesh();
   const int dim = mesh_ho.Dimension();
   const int order = fes_ho.GetMaxElementOrder();
   dbg("dim:%d order:%d",dim,order);

   const bool has_to_init = Ah.Ptr() == nullptr;
   dbg("has_to_init: %s", has_to_init?"yes":"no");
   SparseMatrix *A = has_to_init ? nullptr : Ah.As<SparseMatrix>();

   dbg("GetLORRestriction");
   const LORRestriction *R = lor_disc.GetLORRestriction();
   MFEM_VERIFY(R,"LOR Restriction error!");

   if (has_to_init)
   {
      MFEM_VERIFY(UsesTensorBasis(fes_ho),
                  "Batched LOR assembly requires tensor basis");
      if (Device::IsEnabled() || true)
      {
         dbg("Device::IsEnabled()");
#ifdef MFEM_USE_MPI
         ParFiniteElementSpace *pfes_ho = dynamic_cast<ParFiniteElementSpace*>(&fes_ho);
         if (pfes_ho)
         {
            dbg("Device::IsEnabled() and multiple ranks!");
            const int width = pfes_ho->GetVSize();
            const int height = pfes_ho->GetVSize();
            dbg("HxW: %dx%d",height,width);
            A = new SparseMatrix(height, width, 0);
         }
         else
#endif
         {
            dbg("Device::IsEnabled() but one rank!");
            const int width = fes_ho.GetVSize();
            const int height = fes_ho.GetVSize();
            dbg("HxW: %dx%d",height,width);
            A = new SparseMatrix(height, width, 0);
         }
         A->GetMemoryI().New(A->Height()+1, A->GetMemoryI().GetMemoryType());
         const int nnz = R->FillI(*A);
         A->GetMemoryJ().New(nnz, A->GetMemoryJ().GetMemoryType());
         A->GetMemoryData().New(nnz, A->GetMemoryData().GetMemoryType());
         R->FillJAndZeroData(*A); // J, A = 0.0
      }
      else
      {
         MFEM_ABORT("");
         dbg("NOT Device::IsEnabled()");
         // the sparsity pattern is defined from the map: element->dof
         const int ndofs = fes_ho.GetVSize();
         dbg("ndofs:%d",ndofs);
         const Table &elem_dof = form_lor.FESpace()->GetElementToDofTable();
         Table dof_dof, dof_elem;
         Transpose(elem_dof, dof_elem, ndofs);
         mfem::Mult(dof_elem, elem_dof, dof_dof);
         dof_dof.SortRows();
         int *I = dof_dof.GetI();
         int *J = dof_dof.GetJ();
         double *data = Memory<double>(I[ndofs]);
         A = new SparseMatrix(I,J,data,ndofs,ndofs,true,true,true);
         dof_dof.LoseData();
         *A = 0.0;
         dbg("done");
      }
   }

   void (*Kernel)(const Array<int> &dof_glob2loc,
                  const Array<int> &dof_glob2loc_offsets,
                  const Array<int> &el_dof_lex,
                  Mesh &mesh_ho,
                  SparseMatrix &A_mat) = nullptr;

   if (dim == 2) { MFEM_ABORT("Unsuported!"); }
   else if (dim == 3)
   {
      switch (order)
      {
         case 1: Kernel = Assemble3DBatchedLOR<1>; break;
         case 2: Kernel = Assemble3DBatchedLOR<2>; break;
         case 3: Kernel = Assemble3DBatchedLOR<3>; break;
         case 4: Kernel = Assemble3DBatchedLOR<4>; break;
         case 5: Kernel = Assemble3DBatchedLOR<5>; break;
         case 6: Kernel = Assemble3DBatchedLOR<6,false>; break;
         case 7: Kernel = Assemble3DBatchedLOR<7,false>; break;
         case 8: Kernel = Assemble3DBatchedLOR<8,false>; break;
         case 9: Kernel = Assemble3DBatchedLOR<9,false>; break;
         case 10: Kernel = Assemble3DBatchedLOR<10,false>; break;
         case 11: Kernel = Assemble3DBatchedLOR<11,false>; break;
         case 12: Kernel = Assemble3DBatchedLOR<12,false>; break;
         case 13: Kernel = Assemble3DBatchedLOR<13,false>; break;
         case 14: Kernel = Assemble3DBatchedLOR<14,false>; break;
         case 15: Kernel = Assemble3DBatchedLOR<15,false>; break;
         case 16: Kernel = Assemble3DBatchedLOR<16,false>; break;
         default: MFEM_ABORT("Kernel not ready!");
      }
   }

   Kernel(R->Indices(),
          R->Offsets(),
          R->GatherMap(),
          mesh_ho, *A);

   A->Finalize();

   if (has_to_init) { Ah.Reset(A); } // A now owns A_mat
}

void AssembleBatchedLOR(LORBase &lor_disc,
                        BilinearForm &form_lor,
                        FiniteElementSpace &fes_ho,
                        const Array<int> &ess_dofs,
                        OperatorHandle &Ah)
{
   MFEM_NVTX;
   AssembleBatchedLORWithoutBC(lor_disc, form_lor, fes_ho, Ah);

   // Set essential dofs to 0.0
   const int n_ess_dofs = ess_dofs.Size();
   const auto ess_dofs_d = ess_dofs.Read();

   const auto I = Ah.As<SparseMatrix>()->ReadI();
   const auto J = Ah.As<SparseMatrix>()->ReadJ();
   auto dA = Ah.As<SparseMatrix>()->ReadWriteData();

   MFEM_FORALL(i, n_ess_dofs,
   {
      const int idof = ess_dofs_d[i];
      for (int j=I[idof]; j<I[idof+1]; ++j)
      {
         const int jdof = J[j];
         if (jdof != idof)
         {
            dA[j] = 0.0;
            for (int k=I[jdof]; k<I[jdof+1]; ++k)
            {
               if (J[k] == idof)
               {
                  dA[k] = 0.0;
                  break;
               }
            }
         }
      }
   });
}

#ifdef MFEM_USE_MPI

void ParAssembleBatchedLOR(LORBase &lor_disc,
                           BilinearForm &form_lor,
                           FiniteElementSpace &fes_ho,
                           const Array<int> &ess_dofs,
                           OperatorHandle &Ah)
{
   dbg();
   MFEM_NVTX;
   ParFiniteElementSpace *pfes_ho = dynamic_cast<ParFiniteElementSpace*>(&fes_ho);
   assert(pfes_ho);

   OperatorHandle A_local;
   AssembleBatchedLORWithoutBC(lor_disc, form_lor, fes_ho, A_local);
   MFEM_VERIFY(A_local.As<SparseMatrix>()->Finalized(),
               "the local matrix must be finalized");

   OperatorHandle dA(Operator::Hypre_ParCSR),
                  Ph(Operator::Hypre_ParCSR);
   {
      NVTX("MakeSquareBlockDiag");
      dA.MakeSquareBlockDiag(pfes_ho->GetComm(), pfes_ho->GlobalVSize(),
                             pfes_ho->GetDofOffsets(), A_local.As<SparseMatrix>());
   }
   Ph.ConvertFrom(pfes_ho->Dof_TrueDof_Matrix());

   {
      NVTX("MakePtAP");
      Ah.MakePtAP(dA, Ph);
   }

   // BCs
   HypreParMatrix *A_mat = Ah.As<HypreParMatrix>();
   hypre_ParCSRMatrix *A = *A_mat;
   A_mat->HypreReadWrite();

   hypre_CSRMatrix *diag = hypre_ParCSRMatrixDiag(A);
   hypre_CSRMatrix *offd = hypre_ParCSRMatrixOffd(A);

   HYPRE_Int diag_nrows = hypre_CSRMatrixNumRows(diag);
   HYPRE_Int offd_ncols = hypre_CSRMatrixNumCols(offd);

   const int n_ess_dofs = ess_dofs.Size();
   const auto ess_dofs_d = ess_dofs.Read();

   // Start communication to figure out which columns need to be eliminated in
   // the off-diagonal block
   hypre_ParCSRCommHandle *comm_handle;
   HYPRE_Int *int_buf_data, *eliminate_row, *eliminate_col;
   {
      eliminate_row = hypre_CTAlloc(HYPRE_Int, diag_nrows, HYPRE_MEMORY_HOST);
      eliminate_col = hypre_CTAlloc(HYPRE_Int, offd_ncols, HYPRE_MEMORY_HOST);

      // Make sure A has a communication package
      hypre_ParCSRCommPkg *comm_pkg = hypre_ParCSRMatrixCommPkg(A);
      if (!comm_pkg)
      {
         hypre_MatvecCommPkgCreate(A);
         comm_pkg = hypre_ParCSRMatrixCommPkg(A);
      }

      // Which of the local rows are to be eliminated?
      for (int i = 0; i < diag_nrows; i++)
      {
         eliminate_row[i] = 0;
      }

      ess_dofs.HostRead();
      for (int i = 0; i < n_ess_dofs; i++)
      {
         eliminate_row[ess_dofs[i]] = 1;
      }

      // Use a matvec communication pattern to find (in eliminate_col) which of
      // the local offd columns are to be eliminated
      HYPRE_Int num_sends = hypre_ParCSRCommPkgNumSends(comm_pkg);
      int_buf_data = hypre_CTAlloc(HYPRE_Int,
                                   hypre_ParCSRCommPkgSendMapStart(comm_pkg,
                                                                   num_sends), HYPRE_MEMORY_HOST);
      int index = 0;
      for (int i = 0; i < num_sends; i++)
      {
         int start = hypre_ParCSRCommPkgSendMapStart(comm_pkg, i);
         for (int j = start; j < hypre_ParCSRCommPkgSendMapStart(comm_pkg, i+1); j++)
         {
            int k = hypre_ParCSRCommPkgSendMapElmt(comm_pkg,j);
            int_buf_data[index++] = eliminate_row[k];
         }
      }
      comm_handle = hypre_ParCSRCommHandleCreate(
                       11, comm_pkg, int_buf_data, eliminate_col);
   }

   // Eliminate rows and columns in the diagonal block
   {
      const auto I = diag->i;
      const auto J = diag->j;
      auto data = diag->data;

      MFEM_FORALL(i, n_ess_dofs,
      {
         const int idof = ess_dofs_d[i];
         for (int j=I[idof]; j<I[idof+1]; ++j)
         {
            const int jdof = J[j];
            if (jdof != idof)
            {
               data[j] = 0.0;
               for (int k=I[jdof]; k<I[jdof+1]; ++k)
               {
                  if (J[k] == idof)
                  {
                     data[k] = 0.0;
                     break;
                  }
               }
            }
         }
      });
   }

   // Eliminate rows in the off-diagonal block
   {
      const auto I = offd->i;
      auto data = offd->data;
      MFEM_FORALL(i, n_ess_dofs,
      {
         const int idof = ess_dofs_d[i];
         for (int j=I[idof]; j<I[idof+1]; ++j)
         {
            data[j] = 0.0;
         }
      });
   }

   // Wait for MPI communication to finish
   Array<HYPRE_Int> cols_to_eliminate;
   {
      hypre_ParCSRCommHandleDestroy(comm_handle);

      // set the array cols_to_eliminate
      int ncols_to_eliminate = 0;
      for (int i = 0; i < offd_ncols; i++)
      {
         if (eliminate_col[i])
         {
            ncols_to_eliminate++;
         }
      }

      cols_to_eliminate.SetSize(ncols_to_eliminate);
      cols_to_eliminate = 0.0;

      ncols_to_eliminate = 0;
      for (int i = 0; i < offd_ncols; i++)
      {
         if (eliminate_col[i])
         {
            cols_to_eliminate[ncols_to_eliminate++] = i;
         }
      }

      hypre_TFree(int_buf_data, HYPRE_MEMORY_HOST);
      hypre_TFree(eliminate_row, HYPRE_MEMORY_HOST);
      hypre_TFree(eliminate_col, HYPRE_MEMORY_HOST);
   }

   // Eliminate columns in the off-diagonal block
   {
      const int ncols_to_eliminate = cols_to_eliminate.Size();
      const int nrows_offd = hypre_CSRMatrixNumRows(offd);
      const auto cols = cols_to_eliminate.Read();
      const auto I = offd->i;
      const auto J = offd->i;
      auto data = offd->data;
      // Note: could also try a different stragegy, looping over nnz in the
      // matrix and then doing a binary search in ncols_to_eliminate to see if
      // the column should be eliminated.
      MFEM_FORALL(idx, ncols_to_eliminate,
      {
         int j = cols[idx];
         for (int i=0; i<nrows_offd; ++i)
         {
            for (int jj=I[i]; jj<I[i+1]; ++jj)
            {
               if (J[jj] == j)
               {
                  data[jj] = 0.0;
                  break;
               }
            }
         }
      });
   }
}

#endif

} // namespace mfem
<|endoftext|>


<commit_before>/****************************************************************************
 **
 ** This file is part of the CAMP library.
 **
 ** The MIT License (MIT)
 **
 ** Copyright (C) 2009-2014 TEGESO/TEGESOFT and/or its subsidiary(-ies) and mother company.
 ** Contact: Tegesoft Information (contact@tegesoft.com)
 **
 ** 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 <camp/util.hpp>

// Convert to string:
//  #include "format.h"
//  http://zverovich.net/2013/09/07/integer-to-string-conversion-in-cplusplus.html

// Convert from string:
//  http://tinodidriksen.com/2010/02/16/cpp-convert-string-to-int-speed/

namespace camp
{
namespace detail
{

//-------------------------------------------------------------------------------------------------
// parse string

template <typename T>
static bool parse_integer(const std::string& from, T& to)
{
    try {
        const long p = std::stol(from.c_str());
        to = static_cast<T>(p);
    } catch (std::logic_error&) {
        return false;
    }
    return true;
}

template <typename T>
static bool parse_float(const std::string& from, T& to)
{
    try {
        const long p = std::stof(from.c_str());
        to = static_cast<T>(p);
    } catch (std::logic_error&) {
        return false;
    }
    return true;
}

bool conv(const std::string& from, char& to)
{
    if (from.length() == 1)
    {
        to = from[0];
        return true;
    }
    return false;
}

bool conv(const std::string& from, unsigned char& to)
{
    char r;
    if (!conv(from,r))
        return false;
    to = r;
    return true;
}

bool conv(const std::string& from, short& to)
{
    return parse_integer(from, to);
}

bool conv(const std::string& from, unsigned short& to)
{
    return parse_integer(from, to);
}

bool conv(const std::string& from, int& to)
{
    return parse_integer(from, to);
}

bool conv(const std::string& from, unsigned int& to)
{
    return parse_integer(from, to);
}

bool conv(const std::string& from, long& to)
{
    return parse_integer(from, to);
}

bool conv(const std::string& from, unsigned long& to)
{
    return parse_integer(from, to);
}

bool conv(const std::string& from, long long& to)
{
    try {
        to = std::stoll(from.c_str());
    } catch (std::logic_error&) {
        return false;
    }
    return true;
}

bool conv(const std::string& from, unsigned long long& to)
{
    try {
        to = std::stoull(from.c_str());
    } catch (std::logic_error&) {
        return false;
    }
    return true;
}

bool conv(const std::string& from, bool& to)
{
    const char *s = from.c_str();
    if (strcasecmp(s, "1")==0 || strcasecmp(s, "true")==0)
    {
        to = true;
        return true;
    }
    else if (strcasecmp(s, "0")==0 || strcasecmp(s, "false")==0)
    {
        to = false;
        return true;
    }
    return false;
}

bool conv(const std::string& from, float& to)
{
    try {
        to = std::stof(from.c_str());
    } catch (std::logic_error&) {
        return false;
    }
    return true;
}

bool conv(const std::string& from, double& to)
{
    try {
        to = std::stod(from.c_str());
    } catch (std::logic_error&) {
        return false;
    }
    return true;
}

//-------------------------------------------------------------------------------------------------

} // namespace util
} // namespace camp
<commit_msg>Remove unused function.<commit_after>/****************************************************************************
 **
 ** This file is part of the CAMP library.
 **
 ** The MIT License (MIT)
 **
 ** Copyright (C) 2009-2014 TEGESO/TEGESOFT and/or its subsidiary(-ies) and mother company.
 ** Contact: Tegesoft Information (contact@tegesoft.com)
 **
 ** 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 <camp/util.hpp>

// Convert to string:
//  #include "format.h"
//  http://zverovich.net/2013/09/07/integer-to-string-conversion-in-cplusplus.html

// Convert from string:
//  http://tinodidriksen.com/2010/02/16/cpp-convert-string-to-int-speed/

namespace camp
{
namespace detail
{

//-------------------------------------------------------------------------------------------------
// parse string

template <typename T>
static bool parse_integer(const std::string& from, T& to)
{
    try {
        const long p = std::stol(from.c_str());
        to = static_cast<T>(p);
    } catch (std::logic_error&) {
        return false;
    }
    return true;
}

bool conv(const std::string& from, char& to)
{
    if (from.length() == 1)
    {
        to = from[0];
        return true;
    }
    return false;
}

bool conv(const std::string& from, unsigned char& to)
{
    char r;
    if (!conv(from,r))
        return false;
    to = r;
    return true;
}

bool conv(const std::string& from, short& to)
{
    return parse_integer(from, to);
}

bool conv(const std::string& from, unsigned short& to)
{
    return parse_integer(from, to);
}

bool conv(const std::string& from, int& to)
{
    return parse_integer(from, to);
}

bool conv(const std::string& from, unsigned int& to)
{
    return parse_integer(from, to);
}

bool conv(const std::string& from, long& to)
{
    return parse_integer(from, to);
}

bool conv(const std::string& from, unsigned long& to)
{
    return parse_integer(from, to);
}

bool conv(const std::string& from, long long& to)
{
    try {
        to = std::stoll(from.c_str());
    } catch (std::logic_error&) {
        return false;
    }
    return true;
}

bool conv(const std::string& from, unsigned long long& to)
{
    try {
        to = std::stoull(from.c_str());
    } catch (std::logic_error&) {
        return false;
    }
    return true;
}

bool conv(const std::string& from, bool& to)
{
    const char *s = from.c_str();
    if (strcasecmp(s, "1")==0 || strcasecmp(s, "true")==0)
    {
        to = true;
        return true;
    }
    else if (strcasecmp(s, "0")==0 || strcasecmp(s, "false")==0)
    {
        to = false;
        return true;
    }
    return false;
}

bool conv(const std::string& from, float& to)
{
    try {
        to = std::stof(from.c_str());
    } catch (std::logic_error&) {
        return false;
    }
    return true;
}

bool conv(const std::string& from, double& to)
{
    try {
        to = std::stod(from.c_str());
    } catch (std::logic_error&) {
        return false;
    }
    return true;
}

//-------------------------------------------------------------------------------------------------

} // namespace util
} // namespace camp
<|endoftext|>


<commit_before>#pragma once

#include <set>
#include <algorithm>
#include <functional>
#include <memory>

#include "xigua/error.hpp"

namespace xig {

class data;
class enviroment;

//! Abstract class that is the base for writing your own Xigua functions
class method {
public:
  virtual data run(std::vector<data>, enviroment &,
                   std::vector<std::string>) = 0;

  virtual int amount_of_arguments() const = 0;
  virtual bool has_repeating_arguments() const;
  virtual bool has_process_arguments() const;
  virtual bool process_arguments_pass(std::vector<data> args, enviroment &env,
                                      std::vector<std::string> fcl);
  virtual bool should_evaluate_arguments() const;

  data call(std::vector<data> arguments, enviroment &enviroment,
            std::vector<std::string> function_call_list);
};

struct method_set_comparator {
  bool operator()(const std::shared_ptr<method> &a,
                  const std::shared_ptr<method> &b);
};

class function {
public:
  function() {};

  void insert_method(std::shared_ptr<method> in_method) {

    auto found = std::find_if(methods.begin(), methods.end(),
                              [&](std::shared_ptr<method> &a) {
      return (a->amount_of_arguments() == in_method->amount_of_arguments()) &&
             (a->has_repeating_arguments() ==
              in_method->has_repeating_arguments()) &&
             !a->has_process_arguments();
    });

    if (in_method->has_process_arguments() || found == methods.end()) {
      methods.push_back(in_method);
      std::sort(methods.begin(), methods.end(), method_set_comparator());
    } else {
    }
  }

  template <class T> function(T in_method) { add_method(in_method); }

  template <class T> void add_method(T in_method) {
    insert_method(std::shared_ptr<T>(new T(in_method)));
  }

  void merge_with_function(function other) {
    for (const std::shared_ptr<method> &i : other.methods) {
      insert_method(i);
    }
  }

  data call(std::vector<data> &args, enviroment &enviroment,
            std::vector<std::string> function_call_list);

private:
  std::vector<std::shared_ptr<method> > methods;
};
}
<commit_msg>function overload overloads<commit_after>#pragma once

#include <set>
#include <algorithm>
#include <functional>
#include <memory>

#include "xigua/error.hpp"

namespace xig {

class data;
class enviroment;

//! Abstract class that is the base for writing your own Xigua functions
class method {
public:
  virtual data run(std::vector<data>, enviroment &,
                   std::vector<std::string>) = 0;

  virtual int amount_of_arguments() const = 0;
  virtual bool has_repeating_arguments() const;
  virtual bool has_process_arguments() const;
  virtual bool process_arguments_pass(std::vector<data> args, enviroment &env,
                                      std::vector<std::string> fcl);
  virtual bool should_evaluate_arguments() const;

  data call(std::vector<data> arguments, enviroment &enviroment,
            std::vector<std::string> function_call_list);
};

struct method_set_comparator {
  bool operator()(const std::shared_ptr<method> &a,
                  const std::shared_ptr<method> &b);
};

class function {
public:
  function() {};

  void insert_method(std::shared_ptr<method> in_method) {

    auto found = std::find_if(methods.begin(), methods.end(),
                              [&](std::shared_ptr<method> &a) {
      return (a->amount_of_arguments() == in_method->amount_of_arguments()) &&
             (a->has_repeating_arguments() ==
              in_method->has_repeating_arguments()) &&
             !a->has_process_arguments();
    });

    if (in_method->has_process_arguments() || found == methods.end()) {
      methods.push_back(in_method);
      std::sort(methods.begin(), methods.end(), method_set_comparator());
    } else {
      (*found) = in_method;
    }
  }

  template <class T> function(T in_method) { add_method(in_method); }

  template <class T> void add_method(T in_method) {
    insert_method(std::shared_ptr<T>(new T(in_method)));
  }

  void merge_with_function(function other) {
    for (const std::shared_ptr<method> &i : other.methods) {
      insert_method(i);
    }
  }

  data call(std::vector<data> &args, enviroment &enviroment,
            std::vector<std::string> function_call_list);

private:
  std::vector<std::shared_ptr<method> > methods;
};
}
<|endoftext|>


<commit_before><commit_msg>maxconnections<commit_after><|endoftext|>


<commit_before>/***************************Copyright-DO-NOT-REMOVE-THIS-LINE**
 * CONDOR Copyright Notice
 *
 * See LICENSE.TXT for additional notices and disclaimers.
 *
 * Copyright (c)1990-1998 CONDOR Team, Computer Sciences Department,
 * University of Wisconsin-Madison, Madison, WI.  All Rights Reserved.
 * No use of the CONDOR Software Program Source Code is authorized
 * without the express consent of the CONDOR Team.  For more information
 * contact: CONDOR Team, Attention: Professor Miron Livny,
 * 7367 Computer Sciences, 1210 W. Dayton St., Madison, WI 53706-1685,
 * (608) 262-0856 or miron@cs.wisc.edu.
 *
 * U.S. Government Rights Restrictions: Use, duplication, or disclosure
 * by the U.S. Government is subject to restrictions as set forth in
 * subparagraph (c)(1)(ii) of The Rights in Technical Data and Computer
 * Software clause at DFARS 252.227-7013 or subparagraphs (c)(1) and
 * (2) of Commercial Computer Software-Restricted Rights at 48 CFR
 * 52.227-19, as applicable, CONDOR Team, Attention: Professor Miron
 * Livny, 7367 Computer Sciences, 1210 W. Dayton St., Madison,
 * WI 53706-1685, (608) 262-0856 or miron@cs.wisc.edu.
****************************Copyright-DO-NOT-REMOVE-THIS-LINE**/

#include "HashTable.h"
#include "../condor_daemon_core.V6/condor_daemon_core.h"
#include "scheduler.h"
#include "proc.h"

#include "Set.h"
#include "MyString.h"
#include "classad_collection_types.h"

template class HashTable<int, int>;
template class HashBucket<int,int>;
template class HashTable<int, shadow_rec *>;
template class HashBucket<int,shadow_rec *>;
template class HashTable<HashKey, match_rec *>;
template class HashBucket<HashKey,match_rec *>;
template class HashTable<PROC_ID, shadow_rec *>;
template class HashBucket<PROC_ID,shadow_rec *>;
template class Queue<shadow_rec*>;
template class List<shadow_rec*>;
template class Item<shadow_rec*>;
template class List<PROC_ID>;
template class Item<PROC_ID>;
template class HashTable<int, BaseCollection*>;
template class HashBucket<int, BaseCollection*>;
template class Set<MyString>;
template class SetElem<MyString>;
template class Set<int>;
template class SetElem<int>;
template class Set<RankedClassAd>;
template class SetElem<RankedClassAd>;

bool operator==(const PROC_ID a, const PROC_ID b)
{
	return a.cluster == b.cluster && a.proc == b.proc;
}
<commit_msg>Remove classad collection related template instantiations (done in c++ util lib now)<commit_after>/***************************Copyright-DO-NOT-REMOVE-THIS-LINE**
 * CONDOR Copyright Notice
 *
 * See LICENSE.TXT for additional notices and disclaimers.
 *
 * Copyright (c)1990-1998 CONDOR Team, Computer Sciences Department,
 * University of Wisconsin-Madison, Madison, WI.  All Rights Reserved.
 * No use of the CONDOR Software Program Source Code is authorized
 * without the express consent of the CONDOR Team.  For more information
 * contact: CONDOR Team, Attention: Professor Miron Livny,
 * 7367 Computer Sciences, 1210 W. Dayton St., Madison, WI 53706-1685,
 * (608) 262-0856 or miron@cs.wisc.edu.
 *
 * U.S. Government Rights Restrictions: Use, duplication, or disclosure
 * by the U.S. Government is subject to restrictions as set forth in
 * subparagraph (c)(1)(ii) of The Rights in Technical Data and Computer
 * Software clause at DFARS 252.227-7013 or subparagraphs (c)(1) and
 * (2) of Commercial Computer Software-Restricted Rights at 48 CFR
 * 52.227-19, as applicable, CONDOR Team, Attention: Professor Miron
 * Livny, 7367 Computer Sciences, 1210 W. Dayton St., Madison,
 * WI 53706-1685, (608) 262-0856 or miron@cs.wisc.edu.
****************************Copyright-DO-NOT-REMOVE-THIS-LINE**/

#include "HashTable.h"
#include "../condor_daemon_core.V6/condor_daemon_core.h"
#include "scheduler.h"
#include "proc.h"

template class HashTable<int, int>;
template class HashBucket<int,int>;
template class HashTable<int, shadow_rec *>;
template class HashBucket<int,shadow_rec *>;
template class HashTable<HashKey, match_rec *>;
template class HashBucket<HashKey,match_rec *>;
template class HashTable<PROC_ID, shadow_rec *>;
template class HashBucket<PROC_ID,shadow_rec *>;
template class Queue<shadow_rec*>;
template class List<shadow_rec*>;
template class Item<shadow_rec*>;
template class List<PROC_ID>;
template class Item<PROC_ID>;

bool operator==(const PROC_ID a, const PROC_ID b)
{
	return a.cluster == b.cluster && a.proc == b.proc;
}
<|endoftext|>


<commit_before>#include <iostream>
#include <iomanip>
#include "../Global.h"
#include "../Options.h"
#include "../Data.h"
#include "../Scheme.h"
#include "../Inputs/Input.h"
#include "../Loggers/Logger.h"
#include "../Loggers/Default.h"
#include "../Location.h"
#include "../Member.h"
#include <cstring>
bool mShowAll;
bool mShowSpecific;
void showLocations(Input* input);
void showOffsets(Input* input);
void showDates(Input* input);
void showMembers(Input* input);
void showVariables(Input* input);

int main(int argc, const char *argv[]) {
   Global::setLogger(new LoggerDefault(Logger::message));

   if(argc != 2 && argc != 3 && argc != 4) {
      std::cout << "Show information about a COMPS dataset" << std::endl;
      std::cout << std::endl;
      std::cout << "usage: info.exe dataset [--show-all]    show information about dataset" << std::endl;
      std::cout << "   or: info.exe dataset date            show detailed information for a specific date" << std::endl;
      std::cout << std::endl;
      std::cout << "Arguments:" << std::endl;
      std::cout << "   dataset         Tag of dataset from namelist" << std::endl;
      std::cout << "   date            Date in YYYYMMDD format" << std::endl;
      std::cout << "   --show-all      Do not limit variables/dates/offsets to the first 100" << std::endl;

      return 0;
   }

   mShowAll = false;
   mShowSpecific = false;
   int date = 0;

   std::string dataset(argv[1]);
   if(argc >= 3) {
      if(!std::strcmp(argv[2], "--show-all")) {
         mShowAll = true;
      }
      else {
         std::stringstream ss;
         ss<< std::string(argv[2]);
         ss >> date;
         mShowSpecific = true;
      }
   }

   InputContainer inputContainer(Options(""));
   Input* input = inputContainer.getInput(dataset);

   if(mShowSpecific) {
      // Show results for one specific date
      std::vector<float> offsets = input->getOffsets();
      std::vector<std::string> variables = input->getVariables();
      std::vector<Location> locations = input->getLocations();
      std::vector<Member> members = input->getMembers();

      std::vector<float> min(variables.size(), Global::INF);
      std::vector<float> mean(variables.size(), 0);
      std::vector<float> max(variables.size(), -Global::INF);
      std::vector<int> num(variables.size(), 0);

      std::cout << "Variable     min   mean  max        count (max "
                << offsets.size()*locations.size()*members.size() << ")" << std::endl;
      for(int v = 0; v < variables.size(); v++) {
         for(int i = 0; i < offsets.size(); i++) {
            for(int k = 0; k < locations.size(); k++) {
               for(int m = 0; m < members.size(); m++) {
                  float value = input->getValue(date, 0, offsets[i], locations[k].getId(), members[m].getId(), variables[v]);
                  if(Global::isValid(value)) {
                     min[v] = std::min(min[v], value);
                     mean[v] += value;
                     max[v] = std::max(max[v], value);
                     num[v]++;
                  }
               }
            }
         }
         if(num[v] > 0)
            mean[v] /= num[v];
         else {
            min[v] = Global::MV;
            mean[v] = Global::MV;
            max[v] = Global::MV;
         }
         std::cout << std::left << std::setfill(' ') << std::setw(12) << variables[v] << " "
                   << std::setprecision(3) << std::setw(5) << min[v] << " "
                   << std::setprecision(3) << std::setw(5) << mean[v]<< " "
                   << std::setprecision(3) << std::setw(10) << max[v] << " " << num[v] << std::endl;
      }
   }
   else {
      // Show general statistics
      showLocations(input);
      showOffsets(input);
      showDates(input);
      showVariables(input);
      showMembers(input);
   }
}
void showLocations(Input* input) {
   std::cout << "Locations:" << std::endl;
   const std::vector<Location>& locations = input->getLocations();
   std::cout << "Id       code   lat   lon   elev" << std::endl;
   for(int i = 0; i < locations.size(); i++) {
      if(mShowAll || locations.size() < 100 || i < 5 || i >= locations.size()-5) {
         std::cout << std::setw(6) << locations[i].getId() << " "
                   << std::setw(6) << locations[i].getCode() << " "
                   << std::setw(6) << std::fixed << std::setprecision(1) << locations[i].getLat() << " "
                   << std::setw(6) << locations[i].getLon() << " "
                   << std::setw(6) << std::fixed << std::setprecision(1) << locations[i].getElev() << std::endl;
      }
      else if(i == 5) {
         std::cout << "..." << std::endl;
      }
   }
   std::cout << std::endl;
}
void showOffsets(Input* input) {
   std::cout << "Offsets:" << std::endl;
   std::vector<float> offsets = input->getOffsets();
   std::cout << "Id       offset (h)" << std::endl;
   for(int i = 0; i < offsets.size(); i++) {
      if(mShowAll || offsets.size() < 100 || i < 5 || i >= offsets.size()-5) {
         std::cout << std::setw(6) << i << " "
                   << std::setw(6) << offsets[i] << std::endl;
      }
      else if(i == 5) {
         std::cout << "..." << std::endl;
      }
   }
   std::cout << std::endl;
}
void showDates(Input* input) {
   std::cout << "Dates:" << std::endl;
   std::vector<int> dates;
   input->getDates(dates);
   std::cout << "Id        dates" << std::endl;
   for(int i = 0; i < dates.size(); i++) {
      if(mShowAll || dates.size() < 100 || i < 5 || i >= dates.size()-5) {
         std::cout << std::setw(6) << i
                   << std::setw(9) << dates[i] << std::endl;
      }
      else if(i == 5) {
         std::cout << "..." << std::endl;
      }
   }
   std::cout << std::endl;
}
void showVariables(Input* input) {
   std::cout << "Variables:" << std::endl;
   std::vector<std::string> variables = input->getVariables();
   std::cout << "   Id      variable" << std::endl;
   for(int i = 0; i < variables.size(); i++) {
      if(mShowAll || variables.size() < 100 || i < 5 || i >= variables.size()-5) {
         std::cout << std::setw(6) << i << " "
                   << std::setw(12) << variables[i] << std::endl;
      }
      else if(i == 5) {
         std::cout << "..." << std::endl;
      }
   }
}
void showMembers(Input* input) {
   std::cout << "Members:" << std::endl;
   std::vector<Member> members = input->getMembers();
   std::cout << "   Id         model resolution" << std::endl;
   for(int i = 0; i < members.size(); i++) {
      if(mShowAll || members.size() < 100 || i < 5 || i >= members.size()-5) {
         float res = members[i].getResolution();
         std::cout << std::setw(6) << i << " "
                   << std::setw(12) << members[i].getModel();
         if(Global::isValid(res))
            std::cout << std::setw(9) << res << "km";
         std::cout << std::endl;
      }
      else if(i == 5) {
         std::cout << "..." << std::endl;
      }
   }
   std::cout << std::endl;
}
<commit_msg>Show gradient information in Info driver<commit_after>#include <iostream>
#include <iomanip>
#include "../Global.h"
#include "../Options.h"
#include "../Data.h"
#include "../Scheme.h"
#include "../Inputs/Input.h"
#include "../Loggers/Logger.h"
#include "../Loggers/Default.h"
#include "../Location.h"
#include "../Member.h"
#include <cstring>
bool mShowAll;
bool mShowSpecific;
void showLocations(Input* input);
void showOffsets(Input* input);
void showDates(Input* input);
void showMembers(Input* input);
void showVariables(Input* input);

int main(int argc, const char *argv[]) {
   Global::setLogger(new LoggerDefault(Logger::message));

   if(argc != 2 && argc != 3 && argc != 4) {
      std::cout << "Show information about a COMPS dataset" << std::endl;
      std::cout << std::endl;
      std::cout << "usage: info.exe dataset [--show-all]    show information about dataset" << std::endl;
      std::cout << "   or: info.exe dataset date            show detailed information for a specific date" << std::endl;
      std::cout << std::endl;
      std::cout << "Arguments:" << std::endl;
      std::cout << "   dataset         Tag of dataset from namelist" << std::endl;
      std::cout << "   date            Date in YYYYMMDD format" << std::endl;
      std::cout << "   --show-all      Do not limit variables/dates/offsets to the first 100" << std::endl;

      return 0;
   }

   mShowAll = false;
   mShowSpecific = false;
   int date = 0;

   std::string dataset(argv[1]);
   if(argc >= 3) {
      if(!std::strcmp(argv[2], "--show-all")) {
         mShowAll = true;
      }
      else {
         std::stringstream ss;
         ss<< std::string(argv[2]);
         ss >> date;
         mShowSpecific = true;
      }
   }

   InputContainer inputContainer(Options(""));
   Input* input = inputContainer.getInput(dataset);

   if(mShowSpecific) {
      // Show results for one specific date
      std::vector<float> offsets = input->getOffsets();
      std::vector<std::string> variables = input->getVariables();
      std::vector<Location> locations = input->getLocations();
      std::vector<Member> members = input->getMembers();

      std::vector<float> min(variables.size(), Global::INF);
      std::vector<float> mean(variables.size(), 0);
      std::vector<float> max(variables.size(), -Global::INF);
      std::vector<int> num(variables.size(), 0);

      std::cout << "Variable     min   mean  max        count (max "
                << offsets.size()*locations.size()*members.size() << ")" << std::endl;
      for(int v = 0; v < variables.size(); v++) {
         for(int i = 0; i < offsets.size(); i++) {
            for(int k = 0; k < locations.size(); k++) {
               for(int m = 0; m < members.size(); m++) {
                  float value = input->getValue(date, 0, offsets[i], locations[k].getId(), members[m].getId(), variables[v]);
                  if(Global::isValid(value)) {
                     min[v] = std::min(min[v], value);
                     mean[v] += value;
                     max[v] = std::max(max[v], value);
                     num[v]++;
                  }
               }
            }
         }
         if(num[v] > 0)
            mean[v] /= num[v];
         else {
            min[v] = Global::MV;
            mean[v] = Global::MV;
            max[v] = Global::MV;
         }
         std::cout << std::left << std::setfill(' ') << std::setw(12) << variables[v] << " "
                   << std::setprecision(3) << std::setw(5) << min[v] << " "
                   << std::setprecision(3) << std::setw(5) << mean[v]<< " "
                   << std::setprecision(3) << std::setw(10) << max[v] << " " << num[v] << std::endl;
      }
   }
   else {
      // Show general statistics
      showLocations(input);
      showOffsets(input);
      showDates(input);
      showVariables(input);
      showMembers(input);
   }
}
void showLocations(Input* input) {
   std::cout << "Locations:" << std::endl;
   const std::vector<Location>& locations = input->getLocations();
   std::cout << "Id       code   lat     lon   elev gradient" << std::endl;
   for(int i = 0; i < locations.size(); i++) {
      if(mShowAll || locations.size() < 100 || i < 5 || i >= locations.size()-5) {
         std::cout << std::setw(6) << locations[i].getId() << " "
                   << std::setw(6) << locations[i].getCode() << " "
                   << std::setw(6) << std::fixed << std::setprecision(1) << locations[i].getLat() << " "
                   << std::setw(6) << locations[i].getLon() << " "
                   << std::setw(6) << std::fixed << std::setprecision(1) << locations[i].getElev() << " "
                   << std::setw(6) << std::fixed << std::setprecision(3) << locations[i].getGradientX() << ","
                   << std::setw(6) << std::fixed << std::setprecision(3) << locations[i].getGradientY() << std::endl;
      }
      else if(i == 5) {
         std::cout << "..." << std::endl;
      }
   }
   std::cout << std::endl;
}
void showOffsets(Input* input) {
   std::cout << "Offsets:" << std::endl;
   std::vector<float> offsets = input->getOffsets();
   std::cout << "Id       offset (h)" << std::endl;
   for(int i = 0; i < offsets.size(); i++) {
      if(mShowAll || offsets.size() < 100 || i < 5 || i >= offsets.size()-5) {
         std::cout << std::setw(6) << i << " "
                   << std::setw(6) << offsets[i] << std::endl;
      }
      else if(i == 5) {
         std::cout << "..." << std::endl;
      }
   }
   std::cout << std::endl;
}
void showDates(Input* input) {
   std::cout << "Dates:" << std::endl;
   std::vector<int> dates;
   input->getDates(dates);
   std::cout << "Id        dates" << std::endl;
   for(int i = 0; i < dates.size(); i++) {
      if(mShowAll || dates.size() < 100 || i < 5 || i >= dates.size()-5) {
         std::cout << std::setw(6) << i
                   << std::setw(9) << dates[i] << std::endl;
      }
      else if(i == 5) {
         std::cout << "..." << std::endl;
      }
   }
   std::cout << std::endl;
}
void showVariables(Input* input) {
   std::cout << "Variables:" << std::endl;
   std::vector<std::string> variables = input->getVariables();
   std::cout << "   Id      variable" << std::endl;
   for(int i = 0; i < variables.size(); i++) {
      if(mShowAll || variables.size() < 100 || i < 5 || i >= variables.size()-5) {
         std::cout << std::setw(6) << i << " "
                   << std::setw(12) << variables[i] << std::endl;
      }
      else if(i == 5) {
         std::cout << "..." << std::endl;
      }
   }
}
void showMembers(Input* input) {
   std::cout << "Members:" << std::endl;
   std::vector<Member> members = input->getMembers();
   std::cout << "   Id         model resolution" << std::endl;
   for(int i = 0; i < members.size(); i++) {
      if(mShowAll || members.size() < 100 || i < 5 || i >= members.size()-5) {
         float res = members[i].getResolution();
         std::cout << std::setw(6) << i << " "
                   << std::setw(12) << members[i].getModel();
         if(Global::isValid(res))
            std::cout << std::setw(9) << res << "km";
         std::cout << std::endl;
      }
      else if(i == 5) {
         std::cout << "..." << std::endl;
      }
   }
   std::cout << std::endl;
}
<|endoftext|>


<commit_before>/*	libscratch - Multipurpose objective C++ library.

		Copyright (c) 2013 - 2016 Angelo Geels <spansjh@gmail.com>

		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.
*/

#pragma once

#include "Common.h"
#include "String.hpp"

SCRATCH_NAMESPACE_BEGIN;

enum ENewLineMode
{
	ENLM_CRLF,
	ENLM_LF,
	ENLM_CR,
};

class Stream
{
public:
	ENewLineMode strm_nlmNewLineMode;

public:
	Stream();
	~Stream();

	virtual uint32_t Size() = 0;
	virtual uint32_t Location() = 0;
	virtual void Seek(int32_t iPos, int32_t iOrigin) = 0;
	virtual bool AtEOF() = 0;

	virtual void Flush();
	virtual void Close();

	virtual void Write(const void* p, uint32_t iLen) = 0;
	inline void WriteIndex(const int32_t &i) { Write(&i, sizeof(int32_t)); }
	inline void WriteLong(const int64_t &l)	{ Write(&l, sizeof(int64_t)); }
	inline void WriteFloat(const float &f)	 { Write(&f, sizeof(float)); }
	inline void WriteDouble(const double &d) { Write(&d, sizeof(double)); }
	void WriteString(const String &str);
	void WriteStream(Stream &strm);

	virtual int Read(void* pDest, uint32_t iLen) = 0;
	void ReadToEnd(void* pDest);
	inline int32_t ReadIndex()	{ int32_t i = 0; Read(&i, sizeof(int32_t)); return i; }
	inline int64_t ReadLong()	 { int64_t l = 0; Read(&l, sizeof(int64_t)); return l; }
	inline float	 ReadFloat()	{ float	 f = 0; Read(&f, sizeof(float)); return f; }
	inline double	ReadDouble() { double	d = 0; Read(&d, sizeof(double)); return d; }
	String ReadString();

	inline char ReadChar() { char c = '\0'; Read(&c, 1); return c; }
	inline char PeekChar() { char c = '\0'; Read(&c, 1); Seek(-1, 1/*SEEK_CUR*/); return c; }

	bool Expect(const String &str);
	char ReadUntil(String &strOut, const String &strCharacters);

	void WriteText(const String &str);
	void WriteLine(const String &str);
	void WriteLine();
	String ReadLine();

	inline Stream& operator <<(int32_t i)		{ WriteIndex(i); return *this; }
	inline Stream& operator <<(float f)			{ WriteFloat(f); return *this; }
	inline Stream& operator <<(double d)		 { WriteDouble(d); return *this; }
	inline Stream& operator <<(String str)	 { WriteString(str); return *this; }
	inline Stream& operator <<(Stream &strm) { WriteStream(strm); return *this; }

	inline Stream& operator >>(int32_t &i)	 { i = ReadIndex(); return *this; }
	inline Stream& operator >>(float &f)		 { f = ReadFloat(); return *this; }
	inline Stream& operator >>(double &d)		{ d = ReadDouble(); return *this; }
	inline Stream& operator >>(String &str)	{ str = ReadString(); return *this; }
};

#ifdef SCRATCH_IMPL

Stream::Stream()
{
	strm_nlmNewLineMode = ENLM_LF;
}

Stream::~Stream()
{
	Close();
}

void Stream::Flush()
{
}

void Stream::Close()
{
}

void Stream::WriteString(const String &str)
{
	Write((const char*)str, strlen(str) + 1);
}

void Stream::WriteStream(Stream &strm)
{
	// get buffer size
	uint32_t ulBufferSize = strm.Size() - strm.Location();
	uint32_t ulBytesLeft = ulBufferSize;

	// allocate memory for chunks
	uint32_t ulChunkSize = 1024;
	void* pBuffer = malloc(ulChunkSize);

	// loop through chunks required to write
	while (ulBytesLeft > 0) {
		// check for end of stream
		if (ulBytesLeft < ulChunkSize) {
			ulChunkSize = ulBytesLeft;
		}
		// read chunk from other stream
		strm.Read(pBuffer, ulChunkSize);
		// write chunk into our stream
		Write(pBuffer, ulChunkSize);
	}

	// we're done, deallocate buffer
	free(pBuffer);
}

void Stream::ReadToEnd(void* pDest)
{
	Read(pDest, Size() - Location());
}

String Stream::ReadString()
{
	String strRet;
	char c;
	do {
		// read 1 character
		Read(&c, sizeof(char));

		// if it's not the terminator
		if (c != '\0') {
			// he'll be back
			strRet += c;
		}
	} while (c != '\0');

	// done, return
	return strRet;
}

bool Stream::Expect(const String &str)
{
	int iLen = strlen(str);

	char* szBuffer = new char[iLen + 1];
	szBuffer[iLen] = '\0';

	Read(szBuffer, iLen);
	bool ret = (str == szBuffer);

	if (!ret) {
		Seek(-iLen, SEEK_CUR);
	}

	delete[] szBuffer;
	return ret;
}

char Stream::ReadUntil(String &strOut, const String &strCharacters)
{
	String ret;
	char ccc = '\0';
	while (!AtEOF()) {
		char cc = ReadChar();
		if (strCharacters.Contains(cc)) {
			ccc = cc;
			break;
		}
		ret += cc;
	}
	strOut = ret;
	return ccc;
}

void Stream::WriteText(const String &str)
{
	// write text
	Write((const char*)str, strlen(str));
}

void Stream::WriteLine(const String &str)
{
	// write text
	WriteText(str);
	// write newline
	WriteLine();
}

void Stream::WriteLine()
{
	// write newline
	switch (strm_nlmNewLineMode) {
	case ENLM_CRLF: Write("\r\n", 2); break;
	case ENLM_LF: Write("\n", 1); break;
	case ENLM_CR: Write("\r", 1); break;
	}
}

String Stream::ReadLine()
{
	String strRet;
	char c;
	do {
		// read 1 character
		Read(&c, sizeof(char));

		// if at EOF
		if (AtEOF()) {
			// we're done here
			break;
		}

		// skip \r
		if (c == '\r') {
			//WARNING: This can potentially cause problems on certain operating systems
			//				 that work with \r _only_ as a newline character. We should incorporate
			//				 strm_nlmNewLineMode with this in a future version.
			continue;
		}

		// if it's not the terminator
		if (c != '\n') {
			// he'll be back
			strRet += c;
		}
	} while (c != '\n');

	// done, return
	return strRet;
}

#endif

SCRATCH_NAMESPACE_END;
<commit_msg>Fix utf8 issue with stream<commit_after>/*	libscratch - Multipurpose objective C++ library.

		Copyright (c) 2013 - 2016 Angelo Geels <spansjh@gmail.com>

		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.
*/

#pragma once

#include "Common.h"
#include "String.hpp"

SCRATCH_NAMESPACE_BEGIN;

enum ENewLineMode
{
	ENLM_CRLF,
	ENLM_LF,
	ENLM_CR,
};

class Stream
{
public:
	ENewLineMode strm_nlmNewLineMode;

public:
	Stream();
	~Stream();

	virtual uint32_t Size() = 0;
	virtual uint32_t Location() = 0;
	virtual void Seek(int32_t iPos, int32_t iOrigin) = 0;
	virtual bool AtEOF() = 0;

	virtual void Flush();
	virtual void Close();

	virtual void Write(const void* p, uint32_t iLen) = 0;
	inline void WriteIndex(const int32_t &i) { Write(&i, sizeof(int32_t)); }
	inline void WriteLong(const int64_t &l)	{ Write(&l, sizeof(int64_t)); }
	inline void WriteFloat(const float &f)	 { Write(&f, sizeof(float)); }
	inline void WriteDouble(const double &d) { Write(&d, sizeof(double)); }
	void WriteString(const String &str);
	void WriteStream(Stream &strm);

	virtual int Read(void* pDest, uint32_t iLen) = 0;
	void ReadToEnd(void* pDest);
	inline int32_t ReadIndex()	{ int32_t i = 0; Read(&i, sizeof(int32_t)); return i; }
	inline int64_t ReadLong()	 { int64_t l = 0; Read(&l, sizeof(int64_t)); return l; }
	inline float	 ReadFloat()	{ float	 f = 0; Read(&f, sizeof(float)); return f; }
	inline double	ReadDouble() { double	d = 0; Read(&d, sizeof(double)); return d; }
	String ReadString();

	inline char ReadChar() { char c = '\0'; Read(&c, 1); return c; }
	inline char PeekChar() { char c = '\0'; Read(&c, 1); Seek(-1, 1/*SEEK_CUR*/); return c; }

	bool Expect(const String &str);
	char ReadUntil(String &strOut, const String &strCharacters);

	void WriteText(const String &str);
	void WriteLine(const String &str);
	void WriteLine();
	String ReadLine();

	inline Stream& operator <<(int32_t i)		{ WriteIndex(i); return *this; }
	inline Stream& operator <<(float f)			{ WriteFloat(f); return *this; }
	inline Stream& operator <<(double d)		 { WriteDouble(d); return *this; }
	inline Stream& operator <<(String str)	 { WriteString(str); return *this; }
	inline Stream& operator <<(Stream &strm) { WriteStream(strm); return *this; }

	inline Stream& operator >>(int32_t &i)	 { i = ReadIndex(); return *this; }
	inline Stream& operator >>(float &f)		 { f = ReadFloat(); return *this; }
	inline Stream& operator >>(double &d)		{ d = ReadDouble(); return *this; }
	inline Stream& operator >>(String &str)	{ str = ReadString(); return *this; }
};

#ifdef SCRATCH_IMPL

Stream::Stream()
{
	strm_nlmNewLineMode = ENLM_LF;
}

Stream::~Stream()
{
	Close();
}

void Stream::Flush()
{
}

void Stream::Close()
{
}

void Stream::WriteString(const String &str)
{
	Write((const char*)str, strlen(str) + 1);
}

void Stream::WriteStream(Stream &strm)
{
	// get buffer size
	uint32_t ulBufferSize = strm.Size() - strm.Location();
	uint32_t ulBytesLeft = ulBufferSize;

	// allocate memory for chunks
	uint32_t ulChunkSize = 1024;
	void* pBuffer = malloc(ulChunkSize);

	// loop through chunks required to write
	while (ulBytesLeft > 0) {
		// check for end of stream
		if (ulBytesLeft < ulChunkSize) {
			ulChunkSize = ulBytesLeft;
		}
		// read chunk from other stream
		strm.Read(pBuffer, ulChunkSize);
		// write chunk into our stream
		Write(pBuffer, ulChunkSize);
	}

	// we're done, deallocate buffer
	free(pBuffer);
}

void Stream::ReadToEnd(void* pDest)
{
	Read(pDest, Size() - Location());
}

String Stream::ReadString()
{
	String strRet;
	char c;
	do {
		// read 1 character
		Read(&c, sizeof(char));

		// if it's not the terminator
		if (c != '\0') {
			// he'll be back
			strRet += c;
		}
	} while (c != '\0');

	// done, return
	return strRet;
}

bool Stream::Expect(const String &str)
{
	int iLen = strlen(str);

	char* szBuffer = new char[iLen + 1];
	szBuffer[iLen] = '\0';

	Read(szBuffer, iLen);
	bool ret = (str == szBuffer);

	if (!ret) {
		Seek(-iLen, SEEK_CUR);
	}

	delete[] szBuffer;
	return ret;
}

char Stream::ReadUntil(String &strOut, const String &strCharacters)
{
	String ret;
	char ccc = '\0';
	while (!AtEOF()) {
		char cc = ReadChar();
		if (strCharacters.Contains(cc)) {
			ccc = cc;
			break;
		}
		ret += cc;
	}
	strOut = ret;
	return ccc;
}

void Stream::WriteText(const String &str)
{
	// write text
	Write((const char*)str, str.Size());
}

void Stream::WriteLine(const String &str)
{
	// write text
	WriteText(str);
	// write newline
	WriteLine();
}

void Stream::WriteLine()
{
	// write newline
	switch (strm_nlmNewLineMode) {
	case ENLM_CRLF: Write("\r\n", 2); break;
	case ENLM_LF: Write("\n", 1); break;
	case ENLM_CR: Write("\r", 1); break;
	}
}

String Stream::ReadLine()
{
	String strRet;
	char c;
	do {
		// read 1 character
		Read(&c, sizeof(char));

		// if at EOF
		if (AtEOF()) {
			// we're done here
			break;
		}

		// skip \r
		if (c == '\r') {
			//WARNING: This can potentially cause problems on certain operating systems
			//				 that work with \r _only_ as a newline character. We should incorporate
			//				 strm_nlmNewLineMode with this in a future version.
			continue;
		}

		// if it's not the terminator
		if (c != '\n') {
			// he'll be back
			strRet += c;
		}
	} while (c != '\n');

	// done, return
	return strRet;
}

#endif

SCRATCH_NAMESPACE_END;
<|endoftext|>


<commit_before>#include "faketcp.h"

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <netinet/ip.h>
#include <netinet/tcp.h>
#include <linux/if_ether.h>

void tcp_input(int fd, const void* input, const void* payload, int tot_len)
{
  const struct iphdr* iphdr = static_cast<const struct iphdr*>(input);
  const struct tcphdr* tcphdr = static_cast<const struct tcphdr*>(payload);

}

int main()
{
  union
  {
    unsigned char buf[ETH_FRAME_LEN];
    struct iphdr iphdr;
  };

  const int iphdr_size = sizeof iphdr;

  char ifname[IFNAMSIZ] = "tun%d";
  int fd = tun_alloc(ifname);

  if (fd < 0)
  {
    fprintf(stderr, "tunnel interface allocation failed\n");
    exit(1);
  }

  printf("allocted tunnel interface %s\n", ifname);
  sleep(1);

  for (;;)
  {
    int nread = read(fd, buf, sizeof(buf));
    if (nread < 0)
    {
      perror("read");
      close(fd);
      exit(1);
    }
    printf("read %d bytes from tunnel interface %s.\n", nread, ifname);

    if (nread >= iphdr_size
        && iphdr.version == 4
        && iphdr.ihl*4 >= iphdr_size
        && iphdr.ihl*4 <= nread
        && iphdr.tot_len == htons(nread)
        && in_checksum(buf, iphdr.ihl*4) == 0)
    {
      const void* payload = buf + iphdr.ihl*4;
      if (iphdr.protocol == IPPROTO_ICMP)
      {
        icmp_input(fd, buf, payload, nread);
      }
      else if (iphdr.protocol == IPPROTO_TCP)
      {
        tcp_input(fd, buf, payload, nread);
      }
    }
    else
    {
      printf("bad packet\n");
      for (int i = 0; i < nread; ++i)
      {
        if (i % 4 == 0) printf("\n");
        printf("%02x ", buf[i]);
      }
      printf("\n");
    }
  }

  return 0;
}
<commit_msg>add acceptall.cc<commit_after>#include "faketcp.h"

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <netinet/ip.h>
#include <netinet/tcp.h>
#include <linux/if_ether.h>

void tcp_input(int fd, const void* input, const void* payload, int tot_len)
{
  const struct iphdr* iphdr = static_cast<const struct iphdr*>(input);
  const struct tcphdr* tcphdr = static_cast<const struct tcphdr*>(payload);
  const int iphdr_len = iphdr->ihl*4;
  const int tcp_seg_len = tot_len - iphdr_len;
  const int tcphdr_size = sizeof(*tcphdr);
  if (tcp_seg_len >= tcphdr_size
      && tcp_seg_len >= tcphdr->doff*4)
  {
    const int tcphdr_len = tcphdr->doff*4;

    char source[INET_ADDRSTRLEN];
    char dest[INET_ADDRSTRLEN];
    inet_ntop(AF_INET, &iphdr->saddr, source, INET_ADDRSTRLEN);
    inet_ntop(AF_INET, &iphdr->daddr, dest, INET_ADDRSTRLEN);
    printf("IP %s.%d > %s.%d: ",
           source, ntohs(tcphdr->source), dest, ntohs(tcphdr->dest));
    printf("Flags [%c], seq %u, win %d, length %d\n",
           tcphdr->syn ? 'S' : (tcphdr->fin ? 'F' : '.'),
           ntohl(tcphdr->seq),
           ntohs(tcphdr->window),
           tot_len - iphdr_len - tcphdr_len);

    union
    {
      unsigned char output[ETH_FRAME_LEN];
      struct
      {
        struct iphdr iphdr;
        struct tcphdr tcphdr;
      } out;
    };

    assert(sizeof(out) == sizeof(struct iphdr) + sizeof(struct tcphdr));
    int output_len = sizeof(out);
    bzero(&out, output_len + 4);
    memcpy(output, input, sizeof(struct iphdr));

    out.iphdr.tot_len = htons(output_len);
    std::swap(out.iphdr.saddr, out.iphdr.daddr);
    out.iphdr.check = 0;
    out.iphdr.check = in_checksum(output, sizeof(struct iphdr));

    out.tcphdr.source = tcphdr->dest;
    out.tcphdr.dest = tcphdr->source;
    out.tcphdr.ack_seq = htonl(ntohl(tcphdr->seq)+1);
    out.tcphdr.doff = sizeof(struct tcphdr) / 4;
    out.tcphdr.window = htons(5000);

    bool response = false;
    if (tcphdr->syn)
    {
      out.tcphdr.seq = htonl(123456);
      out.tcphdr.syn = 1;
      out.tcphdr.ack = 1;
      response = true;
    }
    else if (tcphdr->fin)
    {
      out.tcphdr.seq = htonl(123457);
      out.tcphdr.fin = 1;
      out.tcphdr.ack = 1;
      response = true;
    }

    unsigned char* pseudo = output + output_len;
    pseudo[0] = 0;
    pseudo[1] = IPPROTO_TCP;
    pseudo[2] = 0;
    pseudo[3] = sizeof(struct tcphdr);
    out.tcphdr.check = in_checksum(&out.iphdr.saddr, sizeof(struct tcphdr)+12);
    if (response)
    {
      write(fd, output, output_len);
    }
  }
}

int main()
{
  union
  {
    unsigned char buf[ETH_FRAME_LEN];
    struct iphdr iphdr;
  };

  const int iphdr_size = sizeof iphdr;

  char ifname[IFNAMSIZ] = "tun%d";
  int fd = tun_alloc(ifname);

  if (fd < 0)
  {
    fprintf(stderr, "tunnel interface allocation failed\n");
    exit(1);
  }

  printf("allocted tunnel interface %s\n", ifname);
  sleep(1);

  for (;;)
  {
    int nread = read(fd, buf, sizeof(buf));
    if (nread < 0)
    {
      perror("read");
      close(fd);
      exit(1);
    }
    printf("read %d bytes from tunnel interface %s.\n", nread, ifname);

    if (nread >= iphdr_size
        && iphdr.version == 4
        && iphdr.ihl*4 >= iphdr_size
        && iphdr.ihl*4 <= nread
        && iphdr.tot_len == htons(nread)
        && in_checksum(buf, iphdr.ihl*4) == 0)
    {
      const void* payload = buf + iphdr.ihl*4;
      if (iphdr.protocol == IPPROTO_ICMP)
      {
        icmp_input(fd, buf, payload, nread);
      }
      else if (iphdr.protocol == IPPROTO_TCP)
      {
        tcp_input(fd, buf, payload, nread);
      }
    }
    else
    {
      printf("bad packet\n");
      for (int i = 0; i < nread; ++i)
      {
        if (i % 4 == 0) printf("\n");
        printf("%02x ", buf[i]);
      }
      printf("\n");
    }
  }

  return 0;
}
<|endoftext|>