/*
  Copyright (C) 2010-2011 David Anderson.  

  This program is free software; you can redistribute it and/or modify it
  under the terms of version 2 of the GNU General Public License as
  published by the Free Software Foundation.

  This program is distributed in the hope that it would be useful, but
  WITHOUT ANY WARRANTY; without even the implied warranty of
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.

  You should have received a copy of the GNU General Public License along
  with this program; if not, write the Free Software Foundation, Inc., 51
  Franklin Street - Fifth Floor, Boston MA 02110-1301, USA.

*/

// 
// irepframe.h
//

class IRCie {
public:
    IRCie(): cie_byte_length_(0), version_(0),
        code_alignment_factor_(1),
        data_alignment_factor_(1), 
        return_address_register_rule_(0) 
        {};
    IRCie(Dwarf_Unsigned length, Dwarf_Unsigned version,
        const std::string &augmentation, Dwarf_Unsigned code_align,
        Dwarf_Signed data_align, Dwarf_Half return_reg_rule,
        const void * init_instrs, Dwarf_Unsigned instrs_len):
        cie_byte_length_(length), version_(version),
        augmentation_(augmentation), code_alignment_factor_(code_align),
        data_alignment_factor_(data_align), 
        return_address_register_rule_(return_reg_rule) 
        {
            const Dwarf_Small *x = 
                reinterpret_cast<const Dwarf_Small *>(init_instrs);
            for(Dwarf_Unsigned i = 0; i < instrs_len; ++i) {
                initial_instructions_.push_back(x[i]);
            }
        }
    void insert_fde_index(unsigned i) { fde_index_.push_back(i); };
    ~IRCie() {};
    void get_basic_cie_data(Dwarf_Unsigned * version,
        std::string  * aug,
        Dwarf_Unsigned * code_align,
        Dwarf_Signed * data_align,
        Dwarf_Half   * ret_addr_reg) {
            *version = version_;
            *aug = augmentation_;
            *code_align = code_alignment_factor_;
            *data_align = data_alignment_factor_;
            *ret_addr_reg = return_address_register_rule_;
        }
    void get_init_instructions(Dwarf_Unsigned *len,
        void **bytes) {
        *len = initial_instructions_.size();
        *bytes = reinterpret_cast<void *>(&initial_instructions_[0]);
        };
    
private:
    //  Byte length  0 if not known yet.
    Dwarf_Unsigned cie_byte_length_; 
    Dwarf_Unsigned version_; 
    std::string augmentation_;
    Dwarf_Unsigned code_alignment_factor_;
    Dwarf_Signed data_alignment_factor_;
    Dwarf_Half   return_address_register_rule_;
    std::vector<Dwarf_Small> initial_instructions_; 
    // fde_index is the array of indexes into fdedata_
    // that are fdes used by this cie.
    std::vector<unsigned>  fde_index_;
};
class IRFde {
public:
    IRFde(): low_pc_(0), func_length_(0),
        cie_offset_(0), cie_index_(-1),
        fde_offset_(0)  {};
    IRFde(Dwarf_Addr low_pc,Dwarf_Unsigned func_length,
        Dwarf_Ptr fde_bytes, Dwarf_Unsigned fde_length,
        Dwarf_Off cie_offset,Dwarf_Signed cie_index,
        Dwarf_Off fde_offset): low_pc_(low_pc), func_length_(func_length),
        cie_offset_(cie_offset), cie_index_(cie_index),
        fde_offset_(fde_offset)  {
            const Dwarf_Small *x =
                reinterpret_cast<const Dwarf_Small *>(fde_bytes);
            for(Dwarf_Unsigned i = 0; i < fde_length; ++i) {
                fde_bytes_.push_back(x[i]);
            }
        };
    ~IRFde() {};
    Dwarf_Signed cie_index() { return cie_index_; };
    void get_fde_base_data(Dwarf_Addr *lowpc, Dwarf_Unsigned * funclen,
        Dwarf_Signed *cie_index_input) {
            *lowpc = low_pc_;
            *funclen = func_length_;
            *cie_index_input = cie_index_;
        };
    void get_fde_instrs_into_ir(Dwarf_Ptr ip,Dwarf_Unsigned len   )  {
        const Dwarf_Small *x =
        reinterpret_cast<const Dwarf_Small *>(ip);
        for(Dwarf_Unsigned i = 0; i < len; ++i) {
            fde_instrs_.push_back(x[i]);
        }
        };
  
    void get_fde_instructions(Dwarf_Unsigned *len,
        void **bytes) {
        *len = fde_instrs_.size();
        *bytes = reinterpret_cast<void *>(&fde_instrs_[0]);
        };
    void fde_instrs () {
        };  
 
private:
    Dwarf_Addr low_pc_;
    Dwarf_Unsigned func_length_;
    // fde_bytes_ may be empty if content bytes not yet created.
    std::vector<Dwarf_Small> fde_bytes_; 

    std::vector<Dwarf_Small> fde_instrs_; 
    // cie_offset may be 0 if not known yet.
    Dwarf_Off  cie_offset_;
    // cie_index is the index in ciedata_  of
    // the applicable CIE. Begins with index 0.
    Dwarf_Signed cie_index_;
    // fde_offset may be 0 if not yet known.
    Dwarf_Off   fde_offset_;
};

class IRFrame {
public:
    IRFrame() {};
    ~IRFrame() {};
    void insert_cie(IRCie &cie) {
        ciedata_.push_back(cie);
    }
    void insert_fde(IRFde &fdedata) {
        fdedata_.push_back(fdedata);
        unsigned findex = fdedata_.size() -1;
        Dwarf_Signed cindex = fdedata.cie_index();
        if( cindex != -1) {
            IRCie & mycie =  ciedata_[cindex];
            mycie.insert_fde_index(findex);
        }
    }
    std::vector<IRCie> &get_cie_vec() { return ciedata_; };
    std::vector<IRFde> &get_fde_vec() { return fdedata_; };
private:
    std::vector<IRCie> ciedata_;
    std::vector<IRFde> fdedata_;
};
