text: /*
   *              Class "Virtual Array of Longs"
   *
   *              Written by Ilfak Guilfanov, 1991
   *              Latest modifications at 1997
   *              Copyright (c), All Rights Reserved.
   */
  
  #ifndef VARRAY_HPP
  #define VARRAY_HPP
  #pragma pack(push, 1)   // IDA uses 1 byte alignments!
  
  #include <pro.h>
  #include <vm.hpp>
  
  implementVM(ulong);
  
  // internal classes
  struct vaptr_t          // Chunk definition
  {
    ea_t start;           // Start address of the chunk
    ea_t end;             // End address of the chunk (not included)
    uval_t offset;        // Offset in the file
                          // Offsets should be in increasing order
    void correct_offset(int pagesize)
    {
      int psize = pagesize / sizeof(ulong);
      offset = offset - offset%pagesize + (start%psize)*sizeof(ulong);
    }
    inline asize_t size(void) const { return end - start; }
  };
  
  
  #define VA_MAGIC "Va0"
  
  struct vaheader_t                       // First bytes of VA file (header)
  {
    char magic[4];                        // Must be VA_MAGIC
    ushort nchunks;                       // Number of chunks
    ushort eof;                           // Number of last used page + 1
  };
  
  
  // Main class declared in the file:
  class Varray
  {
  public:
  
          Varray(void)    { Pages = NULL; }
          ~Varray(void)   { vclose(); }
  
          int     linkTo  (const char *file,int Psize,int PoolSize);
                                          // Psize - how many longs
                                          // PoolSize - how many pages in the cache
                                          // if file doesn't exist, it will be
                                          // created
          void    vclose   (void);
  //
  //      Address space functions
  //
          error_t enable  (ea_t start,ea_t end);        // 0-ok,else error
          error_t disable (ea_t start,ea_t end);        // 0-ok,else error
          bool    enabled (ea_t addr)                   // is enabled ?
                                  { return getoff(addr) != 0; }
          ea_t nextaddr  (ea_t addr); // get next enabled addr
                                      // if not exist, returns -1
          ea_t prevaddr  (ea_t addr); // get prev enabled addr
                                      // if not exist, returns -1
          ea_t prevchunk (ea_t addr); // return prev chunk last addr
                                      // if not exist, returns -1
          ea_t nextchunk (ea_t addr); // return next chunk first addr
                                      // if not exist, returns -1
          asize_t size   (ea_t addr); // return size of the chunk
                                      // addr may be any address in the chunk
          ea_t getstart  (ea_t addr); // get start address of the chunk
                                      // if illegal address given, returns -1
          int movechunk  (ea_t from, ea_t to, asize_t size);
                               // move information from one address to another. returns VAMOVE_...
  #define VAMOVE_OK        0   // all ok
  #define VAMOVE_BADFROM   -1  // the from address is not enabled
  #define VAMOVE_TOOBIG    -2  // the range to move is too big
  #define VAMOVE_OVERLAP   -3  // the target address range is already occupied
  #define VAMOVE_TOOMANY   -4  // too many chunks are defined, can't move
          int check_move_args(ea_t from, ea_t to, asize_t size); // returns VAMOVE_...
  
  //
  //      Read/Write functions
  //
          ulong   vread   (ea_t ea)            { return *Raddr(ea); }
          void    vwrite  (ea_t ea, ulong val) { *Waddr(ea)  =  val; }
          void    setbits (ea_t ea, ulong bit) { *Waddr(ea) |=  bit; }
          void    clrbits (ea_t ea, ulong bit) { *Waddr(ea) &= ~bit; }
          ulong*  Waddr   (ea_t ea);       // return &flags for ea, mark page as dirty
          ulong*  Raddr   (ea_t ea);       // return &flags for ea
          void    vflush  (void)          { Pages->vflush(); }
  
          void memset(ea_t start, asize_t size, ulong x);
          void memcpy(ea_t start, asize_t size, Varray &src, ea_t srcstart);
          ea_t memcmp(ea_t start, asize_t size, Varray &v2, ea_t v2start);
                                                  // returns -1 - if equal
                                                  // else address where mismatches
          ea_t memscn (ea_t start, asize_t size,ulong x);
          ea_t memtst (ea_t start, asize_t size,bool (idaapi*test)(ulong));
          ea_t memtstr(ea_t start, asize_t size,bool (idaapi*test)(ulong));
  
          ulong  *vread    (ea_t start,      ulong *buffer, size_t size);
          void    vwrite   (ea_t start,const ulong *buffer, size_t size);
  
          void    shright (ea_t from);    // Shift right tail of array
          void    shleft  (ea_t from);    // Shift left  tail of array
  
  //
  //      Sorted array functions (obsolete for 64-bit ea_t)!!!
  //
  #ifndef __EA64__
          ea_t bsearch(ea_t ea);          // Binary search
                                          // Returns index to >= ea
                                          // Attention: this func may return
                                          // pointer past array !
          bool addsorted(ea_t ea);        // Add an element to a sorted array.
                                          // If element exists, return 0
                                          // else 1
          bool delsorted(ea_t ea);        // Del element from a sorted array
                                          // If doesn't exist, return 0
                                          // else return 1
  #endif
  
          void vadump(const char *msg, bool ea_sort_order); // debugging
          char *check(bool ea_sort_order); // check internal consistency
  
  private:
    declareVM(ulong) *Pages;
    vaheader_t *header;                   // Header page
    ea_t lastea;                          // Last accessed address
    vaptr_t *lastvp;                      //  vptr of it
    ulong lastoff;                        //  offset of it
    ushort lastPage;
    ulong *lPage;
    size_t psize;                         // how many items can be put into a page
    size_t pagesize;                      // page size in bytes
  
  //
  // scan the Varray forward. call 'perform' for each page. If perform()
  // returns >=0, then this is an index in the page, return address with this index.
  // if perform() < 0, continue...
  // Returns the address calculated by the index returned by perform().
  //      perform() args: page - pointer to the page
  //                      s    - size of the page
  // 'change' - will perform() change the pages
  //
    ea_t vascan(ea_t start, asize_t size, ssize_t (idaapi*perform)(ulong *page, ssize_t s), bool change);
    ea_t vascanr(ea_t start, asize_t size, ssize_t (idaapi*perform)(ulong *page, ssize_t s), bool change);
  
    ulong getoff(ea_t addr);
    int shiftPages(uval_t offset, int op, int np, bool changecur);
    void move_vm_chunk(int p1, int p2, int n);
    void swap(int x1, int x2, int n);
    vaptr_t *split(vaptr_t *cvp, ea_t from, ea_t end);
    void split_page(int kp, ea_t from);
    void merge_if_necessary(vaptr_t *vp);
  
    int add_pages(uval_t offset, int npages, bool changecur)
      { return shiftPages(offset, 0, npages, changecur); }
    int del_pages(uval_t offset, int npages, bool changecur)
      { return shiftPages(offset, npages, 0, changecur); }
  
    vaptr_t *getv0(void) const { return (vaptr_t *)(header + 1); }
    int getidx(const vaptr_t *vp) const { return int(vp - getv0()); }
    bool is_first(const vaptr_t *vp) const { return getidx(vp) == 0; }
    bool is_last(const vaptr_t *vp) const;
  };
  
  #pragma pack(pop)
  #endif // VARRAY_HPP
  