/*************************************************************************** * GFitsTableStringCol.cpp - FITS table string column class * * ----------------------------------------------------------------------- * * copyright (C) 2010-2016 by Juergen Knoedlseder * * ----------------------------------------------------------------------- * * * * This program is free software: you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation, either version 3 of the License, or * * (at your option) any later version. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program. If not, see . * * * ***************************************************************************/ /** * @file GFitsTableStringCol.cpp * @brief FITS table string column class implementation * @author Juergen Knoedlseder */ /* __ Includes ___________________________________________________________ */ #ifdef HAVE_CONFIG_H #include #endif #include // std::strncpy, etc... #include #include "GException.hpp" #include "GTools.hpp" #include "GFitsCfitsio.hpp" #include "GFitsTableStringCol.hpp" /* __ Method name definitions ____________________________________________ */ #define G_INSERT "GFitsTableStringCol::insert(int&, int&)" #define G_REMOVE "GFitsTableStringCol::remove(int&, int&)" /* __ Macros _____________________________________________________________ */ /* __ Coding definitions _________________________________________________ */ /* __ Debug definitions __________________________________________________ */ /*========================================================================== = = = Constructors/destructors = = = ==========================================================================*/ /***********************************************************************//** * @brief Constructor ***************************************************************************/ GFitsTableStringCol::GFitsTableStringCol(void) : GFitsTableCol() { // Initialise class members for clean destruction init_members(); // Return return; } /***********************************************************************//** * @brief Constructor * * @param[in] name Name of column. * @param[in] length Length of column. * @param[in] width Length of individual string. * @param[in] size Number of strings in each column. ***************************************************************************/ GFitsTableStringCol::GFitsTableStringCol(const std::string& name, const int& length, const int& width, const int& size) : GFitsTableCol(name, length, size, width) { // Initialise class members for clean destruction init_members(); // Return return; } /***********************************************************************//** * @brief Copy constructor * * @param[in] column Table column. ***************************************************************************/ GFitsTableStringCol::GFitsTableStringCol(const GFitsTableStringCol& column) : GFitsTableCol(column) { // Initialise class members for clean destruction init_members(); // Copy members copy_members(column); // Return return; } /***********************************************************************//** * @brief Destructor ***************************************************************************/ GFitsTableStringCol::~GFitsTableStringCol(void) { // Free members free_members(); // Return return; } /*========================================================================== = = = Operators = = = ==========================================================================*/ /***********************************************************************//** * @brief Assignment operator * * @param[in] column Table column. * @return Table column. ***************************************************************************/ GFitsTableStringCol& GFitsTableStringCol::operator=(const GFitsTableStringCol& column) { // Execute only if object is not identical if (this != &column) { // Copy base class members this->GFitsTableCol::operator=(column); // Free members free_members(); // Initialise private members for clean destruction init_members(); // Copy members copy_members(column); } // endif: object was not identical // Return this object return *this; } /***********************************************************************//** * @brief Column data access operator * * @param[in] row Row of column to access. * @param[in] inx Vector index in column row to access. * * Provides access to data in a column. ***************************************************************************/ std::string& GFitsTableStringCol::operator()(const int& row, const int& inx) { // If data are not available then load them now if (m_data == NULL) fetch_data(); // Return data bin return m_data[offset(row, inx)]; } /***********************************************************************//** * @brief Column data access operator (const variant) * * @param[in] row Row of column to access. * @param[in] inx Vector index in column row to access. * * Provides access to data in a column. ***************************************************************************/ const std::string& GFitsTableStringCol::operator()(const int& row, const int& inx) const { // If data are not available then load them now if (m_data == NULL) fetch_data(); // Return data bin return m_data[offset(row, inx)]; } /*========================================================================== = = = Public methods = = = ==========================================================================*/ /***********************************************************************//** * @brief Clear instance * * This method properly resets the object to an initial state. ***************************************************************************/ void GFitsTableStringCol::clear(void) { // Free class members (base and derived classes, derived class first) free_members(); this->GFitsTableCol::free_members(); // Initialise members this->GFitsTableCol::init_members(); init_members(); // Return return; } /***********************************************************************//** * @brief Clone column ***************************************************************************/ GFitsTableStringCol* GFitsTableStringCol::clone(void) const { return new GFitsTableStringCol(*this); } /***********************************************************************//** * @brief Get string value * * @param[in] row Table row. * @param[in] inx Table column vector index. * * Returns value of specified row and vector index as string. ***************************************************************************/ std::string GFitsTableStringCol::string(const int& row, const int& inx) const { // If data are not available then load them now if (m_data == NULL) fetch_data(); // Return value return m_data[offset(row,inx)]; } /***********************************************************************//** * @brief Get double precision value * * @param[in] row Table row. * @param[in] inx Table column vector index. * * Returns value of specified row and vector index as double precision. ***************************************************************************/ double GFitsTableStringCol::real(const int& row, const int& inx) const { // If data are not available then load them now if (m_data == NULL) fetch_data(); // Assign string to double double value = gammalib::todouble(m_data[offset(row,inx)]); // Return value return value; } /***********************************************************************//** * @brief Get integer value * * @param[in] row Table row. * @param[in] inx Table column vector index. * * Returns value of specified row and vector index as integer. ***************************************************************************/ int GFitsTableStringCol::integer(const int& row, const int& inx) const { // If data are not available then load them now if (m_data == NULL) fetch_data(); // Assign string to int int value = gammalib::toint(m_data[offset(row,inx)]); // Return value return value; } /***********************************************************************//** * @brief Insert rows in column * * @param[in] row Row after which rows should be inserted (0=first row). * @param[in] nrows Number of rows to be inserted. * * @exception GException::fits_invalid_row * Specified row is invalid. * * Inserts rows into a FITS table. This implies that all columns will be * loaded into memory. ***************************************************************************/ void GFitsTableStringCol::insert(const int& row, const int& nrows) { // Make sure that row is valid if (row < 0 || row > m_length) { throw GException::fits_invalid_row(G_INSERT, row, m_length); } // Continue only if there are rows to be inserted if (nrows > 0) { // If we have no rows yet then simply set the length to the // number of rows to be inserted if (m_length == 0) { m_length = nrows; m_size = m_number * m_length; alloc_data(); init_data(); } // ... otherwise fetch data, allocate new data and copy over // the existing items else { // If data are not available then load them now if (m_data == NULL) fetch_data(); // Compute new column length int length = m_length + nrows; // Calculate size of new memory m_size = m_number * length; // Allocate new data to hold the column std::string* new_data = new std::string[m_size]; // Compute the number of elements before the insertion point, // the number of elements that get inserted, and the total // number of elements after the insertion point int n_before = m_number * row; int n_insert = m_number * nrows; int n_after = m_number * (m_length - row); // Copy and initialise data std::string* src = m_data; std::string* dst = new_data; for (int i = 0; i < n_before; ++i) { *dst++ = *src++; } for (int i = 0; i < n_insert; ++i) { dst->clear(); dst++; } for (int i = 0; i < n_after; ++i) { *dst++ = *src++; } // Free old data if (m_data != NULL) delete [] m_data; // Set pointer to new data and store length m_data = new_data; m_length = length; } // endelse: there were already data } // endfor: there were rows to be inserted // Return return; } /***********************************************************************//** * @brief Remove rows from column * * @param[in] row Row after which rows should be removed (0=first row). * @param[in] nrows Number of rows to be removed. * * @exception GException::fits_invalid_row * Specified row is invalid. * @exception GException::fits_invalid_nrows * Invalid number of rows specified. * * This method removes rows from a FITS table. This implies that the column * will be loaded into memory. ***************************************************************************/ void GFitsTableStringCol::remove(const int& row, const int& nrows) { // Make sure that row is valid if (row < 0 || row >= m_length) { throw GException::fits_invalid_row(G_REMOVE, row, m_length-1); } // Make sure that we don't remove beyond the limit if (nrows < 0 || nrows > m_length-row) { throw GException::fits_invalid_nrows(G_REMOVE, nrows, m_length-row); } // Continue only if there are rows to be removed if (nrows > 0) { // If data are not available then load them now if (m_data == NULL) fetch_data(); // Compute new column length int length = m_length - nrows; // Calculate size of new memory m_size = m_number * length; // If we have rows remaining then allocate new data to hold // the column if (m_size > 0) { // Allocate new data to hold the column std::string* new_data = new std::string[m_size]; // Compute the number of elements before the removal point, // the number of elements that get removed, and the total // number of elements after the removal point int n_before = m_number * row; int n_remove = m_number * nrows; int n_after = m_number * (length - row); // Copy data std::string* src = m_data; std::string* dst = new_data; for (int i = 0; i < n_before; ++i) { *dst++ = *src++; } src += n_remove; for (int i = 0; i < n_after; ++i) { *dst++ = *src++; } // Free old data if (m_data != NULL) delete [] m_data; // Set pointer to new data and store length m_data = new_data; m_length = length; } // endif: there are still elements after removal // ... otherwise just remove all data else { // Free old data if (m_data != NULL) delete [] m_data; // Set pointer to new data and store length m_data = NULL; m_length = length; } } // endfor: there were rows to be removed // Return return; } /***********************************************************************//** * @brief Set nul value * * @param[in] value Nul value string. * * Allows the specification of the FITS table nul string. The default nul * strings is an empty string which results in NULL entries in case that * no data has been written. * * @todo To correctly reflect the nul value in the data, the column should * be reloaded. However, the column may have been changed, so in principle * saving is needed. However, we may not want to store the data, hence saving * is also not desired. We thus have to develop a method to update the * column information for a new nul value in place ... ***************************************************************************/ void GFitsTableStringCol::nulval(const std::string& value) { // Allocate nul value alloc_nulval(value); // Update column // if (m_data != NULL) { // save(); // load(); // } // Return return; } /*========================================================================== = = = Private methods = = = ==========================================================================*/ /***********************************************************************//** * @brief Initialise class members ***************************************************************************/ void GFitsTableStringCol::init_members(void) { // Initialise members m_type = __TSTRING; m_data = NULL; m_buffer = NULL; m_nulval = NULL; // Initialise nul value alloc_nulval(""); // Return return; } /***********************************************************************//** * @brief Copy class members * * @param[in] column Column. * * Sets the content of the vector column by copying from another column. * If the code is compiled with the small memory option, and if the source * column has not yet been loaded, then we only load the column temporarily * for copying purposes and release it again once copying is finished. ***************************************************************************/ void GFitsTableStringCol::copy_members(const GFitsTableStringCol& column) { // Fetch data if necessary bool not_loaded = (!column.is_loaded()); if (not_loaded) { column.fetch_data(); } // Copy attributes m_type = column.m_type; m_size = column.m_size; m_varlen = column.m_varlen; m_rowstart = column.m_rowstart; // Copy column data if (column.m_data != NULL && m_size > 0) { if (m_data != NULL) delete [] m_data; m_data = new std::string[m_size]; for (int i = 0; i < m_size; ++i) { m_data[i] = column.m_data[i]; } } // Copy NULL value if (column.m_nulval != NULL) { if (m_nulval != NULL) delete [] m_nulval; m_nulval = new char[m_width+1]; std::strncpy(m_nulval, column.m_nulval, m_width); } // Small memory option: release column if it was fetch above #if defined(G_SMALL_MEMORY) if (not_loaded) { const_cast(&column)->release_data(); } #endif // Return return; } /***********************************************************************//** * @brief Delete class members ***************************************************************************/ void GFitsTableStringCol::free_members(void) { // Free memory if (m_data != NULL) delete [] m_data; if (m_nulval != NULL) delete [] m_nulval; // Mark memory as freed m_data = NULL; m_nulval = NULL; // Reset load flag m_size = 0; // Free buffer free_buffer(); // Return return; } /***********************************************************************//** * @brief Save table column into FITS file * * The data is transferred from the internal string vector into a character * transfer buffer that can be handled by the cfitsio routines. * * The table column is only saved if it is linked to a FITS file and if the * data are indeed present in the class instance. This avoids saving of data * that have not been modified. * * Refer to GFitsTableCol::save_column() for more information. ***************************************************************************/ void GFitsTableStringCol::save(void) { // Free buffer free_buffer(); // Allocate buffer. This also fills the entire buffer with '\0' // characters. alloc_buffer(); // Transfer string into buffer for (int i = 0; i < m_size; ++i) { if (m_data[i].length() > 0) { std::strncpy(m_buffer[i], m_data[i].c_str(), m_width); } } // Save column save_column(); // Free buffer free_buffer(); // Return return; } /***********************************************************************//** * @brief Returns format string of ASCII table ***************************************************************************/ std::string GFitsTableStringCol::ascii_format(void) const { // Initialize format string std::string format; // Set type code format.append("A"); // Set width format.append(gammalib::str(m_width)); // Return format return format; } /***********************************************************************//** * @brief Allocates column data ***************************************************************************/ void GFitsTableStringCol::alloc_data(void) { // Free memory if (m_data != NULL) delete [] m_data; // Mark pointer as free m_data = NULL; // Allocate new data if (m_size > 0) { m_data = new std::string[m_size]; } // Allocate also buffer free_buffer(); alloc_buffer(); // Return return; } /***********************************************************************//** * @brief Fetch column data * * If a FITS file is attached to the column the data are loaded into memory * from the FITS file. If no FITS file is attached, memory is allocated * to hold the column data and all cells are set to 0. * * Refer to GFitsTableCol::load_column for more information. ***************************************************************************/ void GFitsTableStringCol::fetch_data(void) const { // Calculate size of memory //m_size = m_number * m_length; // Free old buffer memory //free_buffer(); // Allocate buffer memory //alloc_buffer(); // Load column const_cast(this)->load_column(); // Extract string from buffer for (int i = 0; i < m_size; ++i) { if (m_buffer[i] != NULL) { m_data[i].assign(m_buffer[i]); } } // Free buffer memory free_buffer(); // Return return; } /***********************************************************************//** * @brief Resize column data * * @param[in] index Start index. * @param[in] number Number of elements to add/remove. * * Adds or removes elements from specified index on. Adding is done if * @p number is a positive number, removing if @p number is negative. * Note that the method does not change the validity of the arguments. * This needs to be done by the client. ***************************************************************************/ void GFitsTableStringCol::resize_data(const int& index, const int& number) { // Continue only if number of elements is not zero if (number != 0) { // If data are not available then load them now if (m_data == NULL) fetch_data(); // If elements should be removed then do not allocate new memory // but just move elements forward and change the logical size of // memory. Only if all elements should be removed the memory is // released. if (number < 0) { int left = index - number; std::string* dst = m_data + index; std::string* src = m_data + left; int num = m_size - left; for (int i = 0; i < num; ++i) { *dst++ = *src++; } m_size += number; if (m_size < 1) { release_data(); } } // If elements should be added then allocate new memory, copy over // the old data and initialise the new elements else { int left = m_size - index; m_size += number; std::string* new_data = new std::string[m_size]; std::string* dst = new_data; std::string* src = m_data; for (int i = 0; i < index; ++i) { *dst++ = *src++; } for (int i = 0; i < number; ++i) { *dst++ = ""; } for (int i = 0; i < left; ++i) { *dst++ = *src++; } if (m_data != NULL) delete [] m_data; m_data = new_data; } } // endif: number was non-zero // Return return; } /***********************************************************************//** * @brief Release column data ***************************************************************************/ void GFitsTableStringCol::release_data(void) { // Free any existing memory if (m_data != NULL) delete [] m_data; // Mark pointer as free and reset loaded vector size m_data = NULL; m_size = 0; // Return return; } /***********************************************************************//** * @brief Allocate nul value * * @param[in] value Nul value string. ***************************************************************************/ void GFitsTableStringCol::alloc_nulval(const std::string& value) { // Free any existing memory if (m_nulval != NULL) delete [] m_nulval; // Mark pointer as free m_nulval = NULL; // Allocate and initialise nul value m_nulval = new char[m_width+1]; for (int j = 0; j <= m_width; ++j) { m_nulval[j] = '\0'; } // Set nul value std::strncpy(m_nulval, value.c_str(), m_width); // Return return; } /***********************************************************************//** * @brief Initialise column data ***************************************************************************/ void GFitsTableStringCol::init_data(void) { // Initialise data if they exist if (m_data != NULL) { for (int i = 0; i < m_size; ++i) { m_data[i].clear(); } } // Return return; } /***********************************************************************//** * @brief Allocate CFITSIO transfer buffer * * The CFITSIO transfer buffer allows transparent conversion from the CFITSIO * storage format to a vector of strings. ***************************************************************************/ void GFitsTableStringCol::alloc_buffer(void) const { // Allocate buffer memory if (m_size > 0) { m_buffer = new char*[m_size]; for (int i = 0; i < m_size; ++i) { m_buffer[i] = new char[m_width+1]; } } // Initialise buffer if (m_buffer != NULL) { for (int i = 0; i < m_size; ++i) { for (int j = 0; j <= m_width; ++j) { (m_buffer[i])[j] = '\0'; } } } // Return return; } /***********************************************************************//** * @brief Free CFITSIO transfer buffer * * Release memory that has been allocated for the CFITSIO transfer buffer. ***************************************************************************/ void GFitsTableStringCol::free_buffer(void) const { // If there was a buffer allocated then free it if (m_buffer != NULL) { for (int i = 0; i < m_size; ++i) { if (m_buffer[i] != NULL) delete [] m_buffer[i]; } delete [] m_buffer; m_buffer = NULL; } // Return return; }