/*************************************************************************** * GFitsTableBitCol.cpp - FITS table Bit column class * * ----------------------------------------------------------------------- * * copyright (C) 2008-2017 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 GFitsTableBitCol.cpp * @brief FITS table bit column class implementation * @author Juergen Knoedlseder */ /* __ Includes ___________________________________________________________ */ #ifdef HAVE_CONFIG_H #include #endif #include #include "GException.hpp" #include "GTools.hpp" #include "GFitsCfitsio.hpp" #include "GFitsTableBitCol.hpp" /* __ Method name definitions ____________________________________________ */ #define G_INSERT "GFitsTableBitCol::insert(int&, int&)" #define G_REMOVE "GFitsTableBitCol::remove(int&, int&)" #define G_LOAD_COLUMN "GFitsTableBitCol::load_column()" #define G_SAVE_COLUMN "GFitsTableBitCol::save_column()" #define G_GET_BIT "GFitsTableBitCol::get_bit(int&,int&)" /* __ Macros _____________________________________________________________ */ /* __ Coding definitions _________________________________________________ */ /* __ Debug definitions __________________________________________________ */ //#define G_CALL_GRAPH //!< Dump call graph in console /*========================================================================== = = = Constructors/destructors = = = ==========================================================================*/ /***********************************************************************//** * @brief Constructor ***************************************************************************/ GFitsTableBitCol::GFitsTableBitCol(void) : GFitsTableCol() { // Initialise class members for clean destruction init_members(); // Return return; } /***********************************************************************//** * @brief Constructor * * @param[in] name Name of column. * @param[in] nrows Number of rows in column. * @param[in] size Vector size of column. ***************************************************************************/ GFitsTableBitCol::GFitsTableBitCol(const std::string& name, const int& nrows, const int& size) : GFitsTableCol(name, nrows, size, 1) { // Initialise class members for clean destruction init_members(); // Return return; } /***********************************************************************//** * @brief Copy constructor * * @param[in] column Table column. ***************************************************************************/ GFitsTableBitCol::GFitsTableBitCol(const GFitsTableBitCol& column) : GFitsTableCol(column) { // Initialise class members for clean destruction init_members(); // Copy members copy_members(column); // Return return; } /***********************************************************************//** * @brief Destructor ***************************************************************************/ GFitsTableBitCol::~GFitsTableBitCol(void) { // Free members free_members(); // Return return; } /*========================================================================== = = = Operators = = = ==========================================================================*/ /***********************************************************************//** * @brief Assignment operator * * @param[in] column Table column. * @return Table column. ***************************************************************************/ GFitsTableBitCol& GFitsTableBitCol::operator=(const GFitsTableBitCol& 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. ***************************************************************************/ bool& GFitsTableBitCol::operator()(const int& row, const int& inx) { // If data are not available then load them now if (m_data == NULL) this->fetch_data(); // Set any pending Bit set_pending(); // Get Bit get_bit(row, inx); // Signal that a Bit is pending. We need this here since the non-const // operator allows changing the Bit after exiting the method, hence // we have to signal that the actual value of 'm_bit_value' could have // been modified and needs to be written back into the data array. m_bit_pending = true; // Return Bit return m_bit_value; } /***********************************************************************//** * @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 bool& GFitsTableBitCol::operator()(const int& row, const int& inx) const { // Circumvent const correctness GFitsTableBitCol* ptr = const_cast(this); // If data are not available then load them now if (m_data == NULL) ptr->fetch_data(); // Set any pending Bit ptr->set_pending(); // Get Bit ptr->get_bit(row, inx); // Return data bin return m_bit_value; } /*========================================================================== = = = Public methods = = = ==========================================================================*/ /***********************************************************************//** * @brief Clear instance * * This method properly resets the object to an initial state. ***************************************************************************/ void GFitsTableBitCol::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 ***************************************************************************/ GFitsTableBitCol* GFitsTableBitCol::clone(void) const { return new GFitsTableBitCol(*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 GFitsTableBitCol::string(const int& row, const int& inx) const { // Get Bit value bool bit = (*this)(row, inx); // Convert bit into string std::string result = (bit) ? "T" : "F"; // Return result return result; } /***********************************************************************//** * @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 GFitsTableBitCol::real(const int& row, const int& inx) const { // Get Bit value bool bit = (*this)(row, inx); // Convert bit into double double result = (bit) ? 1.0 : 0.0; // Return result return result; } /***********************************************************************//** * @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 GFitsTableBitCol::integer(const int& row, const int& inx) const { // Get Bit value bool bit = (*this)(row, inx); // Convert bit into double int result = (bit) ? 1 : 0; // Return result return result; } /***********************************************************************//** * @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 the column will be * loaded into memory. ***************************************************************************/ void GFitsTableBitCol::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(); // Set any pending Bit set_pending(); // Compute new column length int length = m_length + nrows; // Compute total number of Bits in column m_bits = m_number * length; // Compute number of Bytes and Bits per row m_bytes_per_row = (m_number > 0) ? ((m_number-1) / 8) + 1 : 0; m_bits_per_row = m_bytes_per_row * 8; // Compute length of memory array m_size = m_bytes_per_row * length; // Allocate new data to hold the column unsigned char* new_data = new unsigned char[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_bytes_per_row * row; int n_insert = m_bytes_per_row * nrows; int n_after = m_bytes_per_row * (m_length - row); // Copy and initialise data unsigned char* src = m_data; unsigned char* dst = new_data; for (int i = 0; i < n_before; ++i) { *dst++ = *src++; } for (int i = 0; i < n_insert; ++i) { *dst++ = 0; } 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 GFitsTableBitCol::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(); // Set any pending Bit set_pending(); // Compute new column length int length = m_length - nrows; // Compute total number of Bits in column m_bits = m_number * length; // Compute number of Bytes and Bits per row m_bytes_per_row = (m_number > 0) ? ((m_number-1) / 8) + 1 : 0; m_bits_per_row = m_bytes_per_row * 8; // Compute length of memory array m_size = m_bytes_per_row * 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 unsigned char* new_data = new unsigned char[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_bytes_per_row * row; int n_remove = m_bytes_per_row * nrows; int n_after = m_bytes_per_row * (length - row); // Copy data unsigned char* src = m_data; unsigned char* 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. * * @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 GFitsTableBitCol::nulval(const unsigned char* value) { // Allocate nul value alloc_nulval(value); // Update column // if (m_data != NULL) { // save(); // load(); // } // Return return; } /*========================================================================== = = = Private methods = = = ==========================================================================*/ /***********************************************************************//** * @brief Initialise class members ***************************************************************************/ void GFitsTableBitCol::init_members(void) { // Optionally print call graph #if defined(G_CALL_GRAPH) printf("GFitsTableBitCol::init_members\n"); #endif // Initialise members m_type = __TBIT; m_bits = 0; m_bytes_per_row = 0; m_bits_per_row = 0; m_data = NULL; m_nulval = NULL; m_bit_pending = false; m_bit_value = false; m_bit_byte = 0; m_bit_mask = 0; // Optionally print call graph #if defined(G_CALL_GRAPH) printf("exit GFitsTableBitCol::init_members\n"); #endif // 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 GFitsTableBitCol::copy_members(const GFitsTableBitCol& 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; m_bits = column.m_bits; m_bytes_per_row = column.m_bytes_per_row; m_bits_per_row = column.m_bits_per_row; m_bit_pending = column.m_bit_pending; m_bit_value = column.m_bit_value; m_bit_byte = column.m_bit_byte; m_bit_mask = column.m_bit_mask; // Copy column data if (column.m_data != NULL && m_size > 0) { alloc_data(); for (int i = 0; i < m_size; ++i) m_data[i] = column.m_data[i]; } // Copy NULL value alloc_nulval(column.m_nulval); // 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 GFitsTableBitCol::free_members(void) { // Optionally print call graph #if defined(G_CALL_GRAPH) printf("GFitsTableBitCol::free_members\n"); #endif // 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; // Optionally print call graph #if defined(G_CALL_GRAPH) printf("exit GFitsTableBitCol::free_members\n"); #endif // Return return; } /***********************************************************************//** * @brief Returns format string of ASCII table ***************************************************************************/ std::string GFitsTableBitCol::ascii_format(void) const { // Initialize format string std::string format; // Set type code format.append("I"); // Set width format.append(gammalib::str(m_width)); // Return format return format; } /***********************************************************************//** * @brief Allocates column data ***************************************************************************/ void GFitsTableBitCol::alloc_data(void) { // Optionally print call graph #if defined(G_CALL_GRAPH) printf("GFitsTableBitCol::alloc_data(%d)\n", m_size); #endif // Free any existing 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 unsigned char[m_size]; } // Optionally print call graph #if defined(G_CALL_GRAPH) printf("exit GFitsTableBitCol::alloc_data(m_data=%x)\n", m_data); #endif // Return return; } /***********************************************************************//** * @brief Fetch column data * * This method fetches column data when needed. It is declared const, so * that const data access methods can be implemented. * * 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 initialised. * * This method calls GFitsTableCol::load_column to do the job. ***************************************************************************/ void GFitsTableBitCol::fetch_data(void) const { // Load column (circumvent const correctness) const_cast(this)->load_column(); // 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. * * @todo Needs to be implemented ***************************************************************************/ void GFitsTableBitCol::resize_data(const int& index, const int& number) { //TODO // Return return; } /***********************************************************************//** * @brief Release column data ***************************************************************************/ void GFitsTableBitCol::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 Allocates null value ***************************************************************************/ void GFitsTableBitCol::alloc_nulval(const unsigned char* value) { // Free any existing memory if (m_nulval != NULL) delete m_nulval; // Mark pointer as free m_nulval = NULL; // If we have valid value, allocate and set nul value if (value != NULL) { m_nulval = new unsigned char; *m_nulval = *value; } // Return return; } /***********************************************************************//** * @brief Initialise column data ***************************************************************************/ void GFitsTableBitCol::init_data(void) { // Initialise data if they exist if (m_data != NULL) { for (int i = 0; i < m_size; ++i) { m_data[i] = 0; } } // Return return; } /***********************************************************************//** * @brief Load table column from FITS file * * @exception GException::fits_hdu_not_found * Specified HDU not found in FITS file. * @exception GException::fits_error * An error occured while loading column data from FITS file. * * Load Bit (vector) column into memory by reading 8 Bits at once. ***************************************************************************/ void GFitsTableBitCol::load_column(void) { // Compute total number of Bits in column m_bits = m_number * m_length; // Compute number of Bytes and Bits per row m_bytes_per_row = (m_number > 0) ? ((m_number-1) / 8) + 1 : 0; m_bits_per_row = m_bytes_per_row * 8; // Compute length of memory array m_size = m_bytes_per_row * m_length; // Load only if the column has a positive size if (m_size > 0) { // Allocate and initialise fresh memory alloc_data(); init_data(); // If a FITS file is attached then load column data from the FITS // file if (FPTR(m_fitsfile)->Fptr != NULL) { // Move to the HDU int status = 0; status = __ffmahd(FPTR(m_fitsfile), (FPTR(m_fitsfile)->HDUposition)+1, NULL, &status); if (status != 0) { throw GException::fits_hdu_not_found(G_LOAD_COLUMN, (FPTR(m_fitsfile)->HDUposition)+1, status); } // Load data 8 Bits at once status = __ffgcv(FPTR(m_fitsfile), __TBYTE, m_colnum, 1, 1, m_size, m_nulval, m_data, &m_anynul, &status); if (status != 0) { throw GException::fits_error(G_LOAD_COLUMN, status, "for column \""+m_name+"\"."); } } } // endif: column has a positive size // Return return; } /***********************************************************************//** * @brief Save table column into FITS file * * @exception GException::fits_hdu_not_found * Specified HDU not found in FITS file. * @exception GException::fits_error * Error occured during writing of the column data. * * Save Bit (vector) column into FITS file by writing 8 Bits at once. ***************************************************************************/ void GFitsTableBitCol::save_column(void) { // Continue only if a FITS file is connected and data have been loaded if (FPTR(m_fitsfile)->Fptr != NULL && m_colnum > 0 && m_data != NULL) { // Set any pending Bit set_pending(); // Move to the HDU int status = 0; status = __ffmahd(FPTR(m_fitsfile), (FPTR(m_fitsfile)->HDUposition)+1, NULL, &status); if (status != 0) { throw GException::fits_hdu_not_found(G_SAVE_COLUMN, (FPTR(m_fitsfile)->HDUposition)+1, status); } // Save data 8 Bits at once status = __ffpcn(FPTR(m_fitsfile), __TBYTE, m_colnum, 1, 1, m_size, m_data, m_nulval, &status); if (status != 0) { throw GException::fits_error(G_SAVE_COLUMN, status); } } // endif: FITS file was connected // Return return; } /***********************************************************************//** * @brief Get Bit for boolean access * * @param[in] row Row of column. * @param[in] inx Vector index in column row. * * @exception GException::fits_invalid_row * Table row out of valid range. * @exception GException::out_of_range * Table vector index out of valid range. * * Set the Bit for boolean data access. Note that this method assumes that * the data have already been loaded. ***************************************************************************/ void GFitsTableBitCol::get_bit(const int& row, const int& inx) { // Check row value #if defined(G_RANGE_CHECK) if (row < 0 || row >= m_length) { throw GException::fits_invalid_row(G_GET_BIT, row, m_length-1); } #endif // Check inx value #if defined(G_RANGE_CHECK) if (inx < 0 || inx >= m_number) { throw GException::out_of_range(G_GET_BIT, inx, 0, m_number-1); } #endif // Compute Byte and Bit mask m_bit_byte = row * m_bytes_per_row + inx / 8; m_bit_mask = 1 << (7 - (inx % 8)); // Set Bit value m_bit_value = (m_data[m_bit_byte] & m_bit_mask); // Return return; } /***********************************************************************//** * @brief Set pending Bit * * Write the pending Bit into the data. Note that this method assumes that * the data have already been loaded. ***************************************************************************/ void GFitsTableBitCol::set_pending(void) { // Continue only if we have a pending Bit if (m_bit_pending) { // Set or unset Bit if (m_bit_value) { m_data[m_bit_byte] = m_data[m_bit_byte] | m_bit_mask; } else { m_data[m_bit_byte] = m_data[m_bit_byte] & ~m_bit_mask; } // Signal that no more Bit is pending m_bit_pending = false; } // Return return; }