/*----------------------------------------------------------------------------
** Name: linenum.cpp
**
** Title: Line Number Table
**
** Purpose:
**  Create and manage the table that holds all module line numbers
**    Provides:
**       AddLinenum
**       GetLinenum
**       LinenumEnd
**       LinenumStart
**
** Base class = MemPool
**
** Status: PRELIMINARY | CODED | TESTED
**
** $Log:   S:/tbird/mt2_68k/symbol/linenum.cpv  $
** 
**    Rev 1.0   13 Feb 1997 09:06:12   gene
** Initial revision.
** 
**    Rev 1.1   14 Jun 1996 08:30:52   kevin
** Judy's modification
** 
**    Rev 1.0   07 Sep 1995 11:16:36   gene
** Initial revision.
** 
**    Rev 1.39   08 Mar 1993 11:14:16   nghia
** Fixed bugs: column number zero sometimes, test to look for linenumber 
** statement will fail if all lines have column 255.
** 
**    Rev 1.38   18 Dec 1992 18:18:22   brucea
** Fixed: ::GetLinenumStmt by initing leftColumn and priorLeftColumn to 0
**    so that code will, first pass, store offset into greaterThanOffset
** 
**    Rev 1.37   08 Dec 1992 07:07:10   brucea
** Fixed: bug in reporting column info when requested column beyond any in
**    storage (in LinenumTable::GetLinenumStmt); fixed default value for
**    greaterThanOffset
** 
**    Rev 1.36   08 Nov 1992 13:48:44   nghia
** Added SymGetLinenumInOrder() and GetLinenumInOrder() to fix ppr 7517 and 7520
** These routines are private to the Source Presenter only.
** 
**    Rev 1.35   22 Oct 1992 18:18:52   brucea
** Redesigned and implemented ::GetLinenumStmt to handle column requests
**    greater than the largest column number stored and to correct problems with
**    the line and column returned with multiple columns for one line and
**    column values that are out of order in the by-address sorted linenum table.
** 
**    Rev 1.34   13 Aug 1992 19:25:32   brucea
** Fixed: GetLinenumFirstLast to use correct start-end linenum pointers
**        GetLinenumFirstIndex - removed call to UpdateCacheParams which
**        auto-loaded linenums
** 
**    Rev 1.33   13 Aug 1992 11:03:14   brucea
** Changed: 0xFFFFFFFF to 0xFFFFFFFFL
** 
**    Rev 1.32   12 Aug 1992 09:45:10   brucea
** Bug fix: GetLinenumByIndex was not returning correct nextIndex when there
**    are multiple same linenums
** 
** 
**    Rev 1.31   10 Aug 1992 09:18:52   brucea
** Added: check for moduleDesc == NULL_SYMBOL in UpdateCacheParams
** Added: check for valid module descriptor at beginning of SymGetLinenum,
**    SymGetLinenumByIndex, SymGetLinenumStmt, SymGetLinenumStmtByIndex,
**    SymMapAddr2Linenum, SymMapAddr2LinenumStmt, SymMapAddr2LinenumModule, 
**    SymMapAddr2LinenumModuleStmt 
** 
**    Rev 1.30   06 Aug 1992 22:06:18   brucea
** Removed: functions ::AreThereLinenums and IsModuleCached (in .h) and
**    rolled both into ::UpdateCacheParams
** Removed: calls to IsModuleCached and AreThereLinenums
** Modified: ::GetLinenumFirstLast to check if lines are loaded and return
**    err if true rather than call UpdateCacheParams which auto loaded lines
** 
**    Rev 1.29   06 Aug 1992 10:08:02   brucea
** Changed access to .typeIndex.symType to use &0xF
** 
**    Rev 1.28   31 Jul 1992 18:49:08   brucea
** Added: test for linenums loaded in SymMapAddr2LinenumModule and 
**    SymMapAddr2LinenumStmtModule
** 
**    Rev 1.27   27 Jul 1992 23:29:28   brucea
** Fixed bug: in ::AddLinenum to remove later line with same address
** Added: module count class variable
** Changed: return of line number count to take into account the number of
**    modules loaded (and subtract extra linenum added for each module).
** Fixed bug: ::GetLinenumByIndex; a boundary problem getting index of
**    very first linenum in list.
** Removed: private class var <duplicateLinenums>
** Added: EXPORT function GetRawLinenumByIndex, GetLinenumFirstIndex
** 
**    Rev 1.26   25 Jul 1992 16:38:32   brucea
** Added: ::AreThereLinenums, a check to see if linenums exist for the passed
**    module descriptor
** Added: checks for lines in all the callable interfaces that needed it
** 
**    Rev 1.25   19 Jul 1992 21:29:56   brucea
** Modified: ::AddLinenum algorithm.  If sequential linenums have the same
**    address, the second is overwritten on top of the first.  This makes the
**    source/asm browser place all source lines with the same code address
**    above the asm for those lines.  This also reduces the number of linenums
**    that need to be stored
** Modified: ::GetLinenumByIndex - removed requirement for linenum to be greater
**    than start address of found linenum when searching for the end address
**    of the linenum-by-index
** Bug fix: ::LinenumEnd  The dummy linenum added is now given and address one
**    more than the end address of the module so that searching for an
**    address range will find the last address of the last linenum
** 
**    Rev 1.24   10 Jul 1992 19:06:38   brucea
** Bug fix: moved linenumLoadState = NOT_LOADING; to end of function after
**    AddLinenum(lastLinenumVar) so that the AddLinenum wouldn't fail.
** 
**    Rev 1.23   21 Apr 1992 16:52:48   brucea
** Added: LinenumTable::ObliterateTable in order to re-initialize private vars
**    after symbol_removesymbols has occurred.
** Moved: In LinenumTable::LinenumEnd, moved the setting of linenumLoadState to
**    top of function so that it will always be done.  Before it was after
**    a function that could create an error and never return.
** 
**    Rev 1.22   20 Apr 1992 08:53:12   brucea
** Enhanced: code now skips a linenum being loaded if it is identical to the
**    previous linenum (same linenum, column num, address)
** Algorithm change: during a linenum lookup, the end address was calculated as
**    the next linenum's address - 1.  However, it is possible that there are
**    different linenums with the same code address.  Therefore, the end address
**    is calculated as the next linenum with a larger address.
** Added: GetRawLinenumByIndex, LinenumTable::GetLinenumFirstIndex - used to
**    display linenums in CLI
** 
**    Rev 1.21   01 Apr 1992 23:36:36   brucea
** Fixed: compile warnings
** Added: SymMapAddr2LinenumStmt
** 
**    Rev 1.20   30 Mar 1992 18:17:16   brucea
** Fixed: boundary error when searching backward in linenum list.  Now check for
**    index <= linenumOffsetStart before decrementing index in
**    ::GetLinenumByIndex
** 
**    Rev 1.19   20 Mar 1992 14:41:24   brucea
** Fixed: bug in calculated the end address of a linenum; occurs in several
**    places
** 
**    Rev 1.18   18 Mar 1992 13:21:12   brucea
** Fixed bug in ::MapAddr2Linenum: passing address of columnRange->columnStart
**    in call to GetLinenumByIndex
** 
**    Rev 1.17   16 Mar 1992 13:53:26   brucea
** Changed: usage of MAX_COLUMN to SYM_MAX_COLUMN
** 
**    Rev 1.16   12 Mar 1992 22:20:30   brucea
** Changed: end address points to last byte of range; for linenums, the end
**   address is the address of the next linenum MINUS 1.
**   This is reflected in ::GetLinenum, ::GetLinenumByIndex, ::GetLinenumStmt
**   ::GetLinenumStmtByIndex
** Added: lineType to ::MapAddr2Linenum, made column into columnRange; altered
** Modified: calls to MapAddr2Linenum to reflect different interface
** Created: SymMapAddr2LinenumStmtModule to support statement-level lookup by
**    addr
** 
** 
**    Rev 1.15   27 Feb 1992 22:41:36   brucea
** Fixed: bug in binary search in LinenumTable::MapAddr2Linenum
** 
**    Rev 1.14   10 Jan 1992 15:52:50   brucea
** Fixed bug in GetLinenumFirstLast - didn't return last VALID linenum
** 
**    Rev 1.13   20 Dec 1991 17:05:56   brucea
** Fixed binary search routine in MapAddr2Linenum
** 
**    Rev 1.12   20 Dec 1991 15:46:40   brucea
** Fixed bug in SymMapAddr2LinenumModule - call to SymMapAddr2Symbol
** was passing the second param as an input constant rather than as
** an output (return) parameter
** 
**    Rev 1.11   20 Dec 1991 15:14:36   brucea
** Cosmetic changes
** Added function LinenumTable::GetLinenumFirstLast
** Fixed bug in SymMapAddr2LinenumModule - moved UtilOnDemandLoad call
**   past SymMapAddr2Symbol
** 
**    Rev 1.10   17 Dec 1991 16:10:32   john
** Changed ::LinenumStart to accept the module descriptor for the
** module we are adding linenums to.
** Changed SymAddLinenumStart for the same reason as ::LinenumStart.
** 
**    Rev 1.9   12 Dec 1991 20:06:22   brucea
** Changed the comparison (>) back to original (correct) form in GetLinenum
** 
**    Rev 1.8   12 Dec 1991 14:59:48   brucea
** removed EXPORT from member functions
** Fixed bug in GetLinenum: inside for loop, was subtracting bigger number
** from a smaller number
** 
**    Rev 1.7   12 Dec 1991 14:23:30   brucea
** Removed code to exclude duplicate addressed linenums
** Fixed bug in GetLinenum: > becomes < for comparison of present
** linenum to requested linenum
** 
**    Rev 1.6   11 Dec 1991 16:44:26   brucea
** Comment changes, put #define max values
** Addlinenum linenum changed to lineStruct; check for NOT_LOADING state
** Convert column value of 0 to MAX_COLUMN
** In GetLinenum, changed while to for
** In all functions that used Adr calls, setting addresses are done with
** separate calls that convert offset addresses to logical addresses:
**    UtilSetAddrOffset, UtilSetEndAddrOffset
** Added GetLinenumByIndex, GetLinenumStmtByIndex
** In LinenumStart, added check for linenums already loaded
** Consolidation with function calls to SymMapAddr2Linenum and ...Module
** 
**    Rev 1.5   09 Dec 1991 17:35:54   brucea
** Changes follow:
** 
** Added code which throws away duplicate linenums (those that have same
**   address)
** Added internal function UpdateCacheParams
** Code fixes to GetLinenum
** Implemented GetLinenumStmt
** Implemented SymGetLinenumStmt
**
**    Rev 1.4   02 Dec 1991 11:22:48   brucea
** Fixed up DESCRIPTOR variable names to include Desc on end
** 
**    Rev 1.3   29 Nov 1991 20:08:30   brucea
** Added includes for: addr.h, basetbl.h symget.h
** changed COMP_ADDR_RANGE_TYPE to DESCRIPTOR
** Added calls to addr.h: AdrSetAddrOffset, AdrSetEndAddrOffset
** changed call to SymGetModuleName to SymGetModuleDesc
** changed LOGICAL_ADDR_TYPE to OFFSET_ADDR_TYPE
** changed code in SymMapAddr2Linenum to support address descriptors
** 
**    Rev 1.2   18 Nov 1991 15:36:24   brucea
** Added LinenumTable::MapAddr2Linenum
** Changed SymGetLinenumNext to SymGetLinenumByIndex
**         SymGetLinenumStmtAddr to SymGetLinenumStmt
**         SymGetLinenumStmtNext to SymGetLinenumStmtByIndex
** Implemented SymMapAddr2LinenumModule
** Changed ADDR_STRUCT to LOGICAL_ADDR_TYPE and code that used it
** 
**    Rev 1.1   13 Nov 1991 18:10:22   brucea
** Changed definition and code which implements linenumOffsetEnd to
**    point to the dummy linenum rather than one past it
** Added call to SymGetModuleName in LinenumStart
** Fixed bugs
** 
**    Rev 1.0   12 Nov 1991 10:06:56   brucea
** Initial revision.
** 
** $Header:   S:/tbird/mt2_68k/symbol/linenum.cpv   1.0   13 Feb 1997 09:06:12   gene  $
**
** Copyright (C) 1991 Microtek International.  All rights reserved.
**
**--------------------------------------------------------------------------*/

                       /****************************
                        *                          *
                        *       INCLUDE FILES      *
                        *                          *
                        ****************************/

#ifndef _ADDR_
#include "addr.h"
#endif

#ifndef _BASETBL_
#include "basetbl.h"
#endif

#ifndef _LINENUM_
#include "linenum.h"
#endif

#ifndef _MEMPOOL_
#include "mempool.h"
#endif

#ifndef _SYMGET_
#include "symget.h"
#endif

                       /****************************
                        *                          *
                        *        DEFINITIONS       *
                        *                          *
                        ****************************/

#define LINENUM_INIT_SIZE      (sizeof(LINENUM_STRUCT_TYPE) * 1024L)
#define LINENUM_EXPANSION_SIZE (sizeof(LINENUM_STRUCT_TYPE) * 1024L)
#define MAX_LINENUM_OFFSET     0xFFFFFFFFL
#define MAX_DELTA              0xFFFF
#define MAX_LINENUM            0xFFFF


LinenumTable lt;       // declare static line number table object

                       /****************************
                        *                          *
                        *    EXTERNAL VARIABLES    *
                        *                          *
                        ****************************/
extern MemPool st;


                       /****************************
                        *                          *
                        *     EXECUTABLE CODE      *
                        *                          *
                        ****************************/

//------------------------------------------------------------------------
//                  LINE NUMBER IMPLEMENTATION COMMENTS
//
// Line number sorting by address
//    It appears that line numbers are created in address assending order, at
//    least from MRI.  When loading line numbers, a <sorted> flag will be
//    maintained.  It is checked when LinenumEnd is called and only sort
//    if it is FALSE.
//
// Column information
//    A column value of 0 is converted upon a line load to the maximum
//    possible value, since a value of 0 represents the end of the line.
//
// Last line number
//    A false line number is added to the end of the line number list.
//    It is used to create an address range for the last loaded line number.
//    Its address is the end address of the module if greater than the
//    last line number, or the last line number's address + 1.
//    Its line number value is the largest value possible.  Column = max.
//    LinenumOffsetEnd is set to point to this dummy line number.
//    The following is a picture of the offset pointers relative to the
//    line numbers loaded:
//
//          linenumOffsetStart --> first linenum loaded
//                                    ........   
//                                    ........
//                                 last  linenum loaded
//          linenumOffsetEnd ----> dummy linenum (holds range info for last)
//
//------------------------------------------------------------------------


// member functions for LinenumTable

LinenumTable::LinenumTable():MemPool()        // constructor
   {
   initialSize = LINENUM_INIT_SIZE;
   expandSize  = LINENUM_EXPANSION_SIZE;
   linenumLoadState    = NOT_LOADING;
   cachedModDescriptor = NULL_DESCRIPTOR;
   modulesLoaded = 0L;
};  // end of LinenumTable constructor

//------------------------------------------------------------------------
// LinenumTable::ObliterateTable
//
// Purpose:
//    Reinitialize private vars
//------------------------------------------------------------------------
VOID LinenumTable::ObliterateTable(VOID) {

   MemPool::ObliterateTable();
   initialSize = LINENUM_INIT_SIZE;
   expandSize  = LINENUM_EXPANSION_SIZE;
   linenumLoadState    = NOT_LOADING;
   cachedModDescriptor = NULL_DESCRIPTOR;

} // end of LinenumTable::ObliterateTable


//------------------------------------------------------------------------
// LinenumTable::AddLinenum
//
// Purpose:
//    Insert a line number into the object's table
//    MRI generates the same address for multiple source lines;
//    the previous one will be written over with the current linenum
//------------------------------------------------------------------------
RETCODE
LinenumTable::AddLinenum(LINENUM_STRUCT_TYPE& lineStruct)  {

   LINENUM_STRUCT_TYPE HUGE * linenumPtr;

   if (linenumLoadState == NOT_LOADING)
      return ER_LINENUM_BAD_STATE;
   // check for sorted by address
   if (lineStruct.addr < previousLine.addr)  {
      sorted = FALSE;
   }
   // convert 0 value column to maximum column which it represents
   if (lineStruct.column == 0)
      lineStruct.column = SYM_MAX_COLUMN;
   // write the line and column values overtop the previous linenum that has
   // the same address as the current one
   if (previousLine.addr == lineStruct.addr) { 
      if (linenums > 0) {   // at least one already loaded
         linenumPtr =
            (LINENUM_STRUCT_TYPE HUGE *)GetHugeDataPtr(previousLinenumOffset);
         linenumPtr->linenum = lineStruct.linenum;
         linenumPtr->column = lineStruct.column;
         return GOOD;
      }
   }
   // save state for next compare
   previousLine.addr = lineStruct.addr;  // save for next compare

   linenums++;  // keep a count
   return PutBytes((U8 *)&lineStruct,
                   LINENUM_STRUCT_SIZE,
                   previousLinenumOffset);   // class (static) variable
}  // end of LinenumTable::AddLinenum


//------------------------------------------------------------------------
// LinenumTable::GetLinenum
//
// Purpose: Gets the line number information given a line number to look up
//                
// Design:
//    Scan from start to end of linenum list
//    Mark the linenum that is closest (less than or equal to) the requested
//       linenum .
//    Break out of loop if an exact match occurs
//    Fill in the return values.
//       The address range must include all addresses of line numbers that are
//       identical AND contiguous
//       (e.g. can have 3 line 20's (different columns) with
//       different start addresses).  The end address is the address of the
//       first line number not equal to the one found
//------------------------------------------------------------------------
RETCODE
LinenumTable::GetLinenum(SYM_DESCRIPTOR     module,
                         LINENUM_TYPE       requestedLinenum,
                         DESCRIPTOR         addrRangeDesc,
                         LINENUM_TYPE       *actualLinenum,
                         COLUMN_TYPE        *actualColumn,
                         LINENUM_DESCRIPTOR *nextIndex)  {

   RETCODE         err;
   OFFSET_ADDR_TYPE startAddr, endAddr;


   // update class vars for start and end of linenums for this module
   if (GOOD != (err = UpdateCacheParams(module)))
      return err;

   TABLE_OFFSET offset;
   TABLE_OFFSET closestOffset;
   U16          linenumDelta;    // difference between requested linenum and
                                 // comparison linenum
   U16          tmpDelta;
   LINENUM_STRUCT_TYPE *linenumPtr;

   closestOffset = MAX_LINENUM_OFFSET; // set to unattainable value so that
                                       // loop can be checked at end
   // linenumOffsetEnd points to linenum AFTER last linenum in module.
   // Must end loop when loop pointer <offset> points to the last linenum -
   // i.e. the dummy linenum
   for (offset = linenumOffsetStart, linenumDelta = MAX_DELTA;
        offset < linenumOffsetEnd;
        offset += LINENUM_STRUCT_SIZE)  {
      linenumPtr = (LINENUM_STRUCT_TYPE *)GetHugeDataPtr(offset);

      if ((linenumPtr->linenum) > requestedLinenum)  {
         tmpDelta = (linenumPtr->linenum) - requestedLinenum;
         if (tmpDelta < linenumDelta) {
            // closer line number, save offset and update the delta
            closestOffset = offset;
            linenumDelta = tmpDelta;
         }
      }
      else if ((linenumPtr->linenum) == requestedLinenum)  {
            closestOffset = offset;
            break;  // don't need to look any further - have exact match
      }
   } // end of for

   // check for no match
   if (MAX_LINENUM_OFFSET == closestOffset)
      return ER_LINENUM_NOT_FOUND;

   linenumPtr = (LINENUM_STRUCT_TYPE *)GetHugeDataPtr(closestOffset);
   *actualLinenum = linenumPtr->linenum;
   *actualColumn  = linenumPtr->column;

   startAddr = linenumPtr->addr;   // used for comparison to end addr
   // fill in start address of line number found
   if (GOOD != (err =
      UtilSetAddrOffset(module, startAddr, addrRangeDesc)))
      return err;

   *nextIndex = closestOffset + LINENUM_STRUCT_SIZE;

   // search for the next line number not equal to the one found, or exit loop
   // if at end of line number list
   for ((offset = *nextIndex);     // point to one following found linenum
        (offset <= linenumOffsetEnd);  // stops on top of dummy linenum
        (offset += LINENUM_STRUCT_SIZE))  {

      linenumPtr = (LINENUM_STRUCT_TYPE *)GetHugeDataPtr(offset);
      if (((linenumPtr->linenum) != *actualLinenum) &&
          ((linenumPtr->addr) > startAddr))
         break;
   }  // end of for

   // set to first entry past matching linenum with non-duplicate address
   *nextIndex = offset;

   // fill in end address; linenumPtr points to first linenum after matching
   // linenums; if dummy, the linenum is max and address is correct value

   // the end address must point to the last byte of the linenum code range,
   // (rather than start of next linenum) therefore must subtract 1 from it
   endAddr = linenumPtr->addr;
   if (endAddr > startAddr) { // valid end address
      endAddr--;
   } else {
      // return a valid address range as well as an error
      err = UtilSetEndAddrOffset(module, startAddr, addrRangeDesc);
      return ER_SYM_INVALID_LN_END_ADDR;
   }
   return UtilSetEndAddrOffset(module, endAddr, addrRangeDesc);
}  // end of LinenumTable::GetLinenum

//------------------------------------------------------------------------
// LinenumTable::GetLinenumInOrder
//
// Purpose: Gets the line number information given a line number to look up
//                
// Design:
//    Scan from start to end of linenum list
//    Mark the linenum that is closest (less than or equal to) the requested
//       linenum.
//    Break out of loop if an exact match or a greater than match occurs
//    Fill in the return values.
//       The address range must include all addresses of line numbers that are
//       identical AND contiguous
//       (e.g. can have 3 line 20's (different columns) with
//       different start addresses).  The end address is the address of the
//       first line number not equal to the one found
//    NOTES: This routine implemented to fix PPR7517 and 7520.  Will be
//       removed when a new mixed mode data object implement.
//       This is private to Source Presenter only.
//------------------------------------------------------------------------
RETCODE
LinenumTable::GetLinenumInOrder(SYM_DESCRIPTOR     module,
                                LINENUM_TYPE       requestedLinenum,
                                DESCRIPTOR         addrRangeDesc,
                                LINENUM_TYPE       *actualLinenum,
                                LINENUM_DESCRIPTOR *nextIndex)  {

   RETCODE         err;
   OFFSET_ADDR_TYPE startAddr, endAddr;


   // update class vars for start and end of linenums for this module
   if (GOOD != (err = UpdateCacheParams(module)))
      return err;

   TABLE_OFFSET offset;
   TABLE_OFFSET closestOffset;
   LINENUM_STRUCT_TYPE *linenumPtr;

   closestOffset = MAX_LINENUM_OFFSET; // set to unattainable value so that
                                       // loop can be checked at end
   // linenumOffsetEnd points to linenum AFTER last linenum in module.
   // Must end loop when loop pointer <offset> points to the last linenum -
   // i.e. the dummy linenum
   for (offset = linenumOffsetStart; offset < linenumOffsetEnd;
        offset += LINENUM_STRUCT_SIZE)  {
      linenumPtr = (LINENUM_STRUCT_TYPE *)GetHugeDataPtr(offset);

      if ((linenumPtr->linenum) >= requestedLinenum)  {
         closestOffset = offset;
         break;  // don't need to look any further - have exact match
      }
   } // end of for

   // check for no match
   if (MAX_LINENUM_OFFSET == closestOffset)
      return ER_LINENUM_NOT_FOUND;

   linenumPtr = (LINENUM_STRUCT_TYPE *)GetHugeDataPtr(closestOffset);
   *actualLinenum = linenumPtr->linenum;

   startAddr = linenumPtr->addr;   // used for comparison to end addr
   // fill in start address of line number found
   if (GOOD != (err =  UtilSetAddrOffset(module, startAddr, addrRangeDesc)))
      return err;

   *nextIndex = closestOffset + LINENUM_STRUCT_SIZE;

   // search for the next line number not equal to the one found, or exit loop
   // if at end of line number list
   for ((offset = *nextIndex);     // point to one following found linenum
        (offset <= linenumOffsetEnd);  // stops on top of dummy linenum
        (offset += LINENUM_STRUCT_SIZE))  {

      linenumPtr = (LINENUM_STRUCT_TYPE *)GetHugeDataPtr(offset);
      if (((linenumPtr->linenum) != *actualLinenum) &&
          ((linenumPtr->addr) > startAddr))
         break;
   }  // end of for

   // set to first entry past matching linenum with non-duplicate address
   *nextIndex = offset;

   // fill in end address; linenumPtr points to first linenum after matching
   // linenums; if dummy, the linenum is max and address is correct value

   // the end address must point to the last byte of the linenum code range,
   // (rather than start of next linenum) therefore must subtract 1 from it
   endAddr = linenumPtr->addr;
   if (endAddr > startAddr) { // valid end address
      endAddr--;
   } else {
      // return a valid address range as well as an error
      err = UtilSetEndAddrOffset(module, startAddr, addrRangeDesc);
      return ER_SYM_INVALID_LN_END_ADDR;
   }
   return UtilSetEndAddrOffset(module, endAddr, addrRangeDesc);
}  // end of LinenumTable::GetLinenumInOrder

//------------------------------------------------------------------------
// GetRawLinenumByIndex
//
// Purpose: return basic info on linenum; return next index.  If index
//          out of bounds, return error
//------------------------------------------------------------------------
RETCODE
LinenumTable::GetRawLinenumByIndex(SYM_DESCRIPTOR     module,
                                   LINENUM_DESCRIPTOR index,
                                   DESCRIPTOR         linenumAddr,
                                   LINENUM_TYPE       *linenum,
                                   COLUMN_TYPE        *column,
                                   LINENUM_DESCRIPTOR *nextIndex) {

   RETCODE err;
   OFFSET_ADDR_TYPE startAddr;

   // update class vars for start and end of linenums for this module
   if (GOOD != (err = UpdateCacheParams(module)))
      return err;

   // check for the index beyond end of linenums - could have been
   // post-incremented on prior call
   if (index >= linenumOffsetEnd)
      return ER_LINENUM_INDEX_TOO_LARGE;

   LINENUM_STRUCT_TYPE *linenumPtr;

   linenumPtr = (LINENUM_STRUCT_TYPE *)GetHugeDataPtr(index);

   *linenum = linenumPtr->linenum;
   *column  = linenumPtr->column;
   *nextIndex = index + LINENUM_STRUCT_SIZE;

   startAddr = linenumPtr->addr;   // return the start address for linenum
   return UtilSetAddrOffset(module, startAddr, linenumAddr);
}  // end of GetRawLinenumByIndex


//------------------------------------------------------------------------
// GetLinenumByIndex
//
// Design:
//    Check for valid index, return if not
//    Look up linenum at location <index>, set linenum, column, and nextIndex
//    Fill in addrRangeDesc
//       Search backward until the previous linenums do not match looked up
//          linenum.  Use its address as the start address.
//       Search forward until linenums do not match looked up linenum.
//       Use last found matching linenum as end address.
//------------------------------------------------------------------------
RETCODE
LinenumTable::GetLinenumByIndex(SYM_DESCRIPTOR     module,
                                LINENUM_DESCRIPTOR index,
                                DESCRIPTOR         addrRangeDesc,
                                LINENUM_DESCRIPTOR *nextIndex,
                                LINENUM_TYPE       *linenum,
                                COLUMN_TYPE        *column)  {

   RETCODE err;
   OFFSET_ADDR_TYPE startAddr, endAddr;
   LINENUM_DESCRIPTOR testIndex;

   // update class vars for start and end of linenums for this module
   if (GOOD != (err = UpdateCacheParams(module)))
      return err;

   // check for the index beyond end of linenums - could have been
   // post-incremented on prior call
   if (index >= linenumOffsetEnd)
      return ER_LINENUM_INDEX_TOO_LARGE;

   LINENUM_STRUCT_TYPE *linenumPtr;

   linenumPtr = (LINENUM_STRUCT_TYPE *)GetHugeDataPtr(index);

   *linenum = linenumPtr->linenum;
   *column  = linenumPtr->column;
   *nextIndex = index + LINENUM_STRUCT_SIZE;

   // search backward to the point where the line number is not equal to the
   // one found, or exit loop if at beginning of linenum list
   testIndex = index;
   while (TRUE) {
      if (index <= linenumOffsetStart)
         break;
      testIndex = index - LINENUM_STRUCT_SIZE;
      linenumPtr = (LINENUM_STRUCT_TYPE *)GetHugeDataPtr(testIndex);
      if (linenumPtr->linenum == *linenum) {
         index -= LINENUM_STRUCT_SIZE;
      } else {
         break;  /* reached linenum not equal */
      }
   }
   // get latest linenum of <maybe> new index
   linenumPtr = (LINENUM_STRUCT_TYPE *)GetHugeDataPtr(index);

   startAddr = linenumPtr->addr;   // used for comparison to end addr
   if (GOOD != (err =
      UtilSetAddrOffset(module, startAddr, addrRangeDesc)))
      return err;

   // search forward for end of contiguous linenums
   index = *nextIndex;  // recover to one past retrieved linenum
   do {
      linenumPtr = (LINENUM_STRUCT_TYPE *)GetHugeDataPtr(index);
      if (linenumPtr->linenum != *linenum)
         break;
      index += LINENUM_STRUCT_SIZE;
   }
   while (index < linenumOffsetEnd);  // linenumOffsetEnd points to the
                                      // created dummy linenum
   // index now pointing to first linenum beyond last matching linenum
   // with a greater address
   *nextIndex = index;  // set up next index to also be the next unique
                        // linenum
   linenumPtr = (LINENUM_STRUCT_TYPE *)GetHugeDataPtr(index);

   // the end address must point to the last byte of the linenum code range,
   // (rather than start of next linenum) therefore must subtract 1 from it
   // do a sanity check first
   endAddr = linenumPtr->addr;
   if (endAddr < startAddr) { // invalid end address
      // return a valid address range as well as an error
      err = UtilSetEndAddrOffset(module, startAddr, addrRangeDesc);
      return ER_SYM_INVALID_LN_END_ADDR;
   } else {
      endAddr--;
   }
   return UtilSetEndAddrOffset(module, endAddr, addrRangeDesc);
}  // end of GetLinenumByIndex


//------------------------------------------------------------------------
// LinenumTable::GetLinenumFirstLast
//
// Purpose: Returns the first and last linenum
//
// Error: returns warning if there are no linenums or they haven't been
//        loaded yet (doesn't force a load)
//------------------------------------------------------------------------
RETCODE
LinenumTable::GetLinenumFirstLast(SYM_DESCRIPTOR     moduleDesc,
                                  LINENUM_TYPE&      firstLinenum,
                                  LINENUM_TYPE&      lastLinenum) {

   LINENUM_STRUCT_TYPE *linenumPtr;
   SYM_TYPE_MODULE     *modulePtr;

   modulePtr = (SYM_TYPE_MODULE *)st.GetHugeDataPtr(moduleDesc);
   if (modulePtr->linenumOffsetStart >= modulePtr->linenumOffsetEnd) {
      return ER_NO_LINENUMS_ADDED;  // interpreted as no linenums in table
   }
   linenumPtr = 
      (LINENUM_STRUCT_TYPE *)GetHugeDataPtr(modulePtr->linenumOffsetStart);
   firstLinenum = linenumPtr->linenum;

   // last valid linenum is the one previous to the last in table
   linenumPtr = (LINENUM_STRUCT_TYPE *)
      GetHugeDataPtr(modulePtr->linenumOffsetEnd-LINENUM_STRUCT_SIZE);
   lastLinenum = linenumPtr->linenum;

   return GOOD;
}  // end of LinenumTable::GetLinenumFirstLast


//------------------------------------------------------------------------
// LinenumTable::GetLinenumFirstIndex
//
// Purpose: Returns the index to the first linenum in module table
//
// Error: returns warning if there are no linenums              
//------------------------------------------------------------------------
RETCODE
LinenumTable::GetLinenumFirstIndex(SYM_DESCRIPTOR     moduleDesc,
                                   LINENUM_DESCRIPTOR FAR *nextIndex) {

   SYM_TYPE_MODULE *modulePtr;

   modulePtr = (SYM_TYPE_MODULE *)st.GetHugeDataPtr(moduleDesc);
   if ((*nextIndex = (modulePtr->linenumOffsetStart)) >=
        (modulePtr->linenumOffsetEnd)) {
      return ER_NO_LINENUMS_ADDED;  // interpreted as no linenums in table
   }
   return GOOD;
}  // end of LinenumTable::GetLinenumFirstIndex


//------------------------------------------------------------------------
// LinenumTable::GetLinenumStmt
//
// Purpose: Gets the line number information given a line number to look up
//                
// Design:
//    Scan from start to end of linenum list
//    If the present linenum is equal to the requested one, toggle a flag
//       and reset rightColumn to large number.  The flag is used to ignore
//       anymore linenums that are not equal to requested linenum.
//    When a linenum is found closer than the previous (but not equal to),
//       want the matching statement to be the closest to the beginning of
//       the line; i.e. the smallest column matching that line.  This means
//       that the requested column is actually 1 for this case.
//    Mark the entry with the secondary condition (assuming linenun delta is
//       the same as a previous entry) with a column value that is closer yet
//       greater than or equal to the requested column.
//    Mark the entry whose column is closest to the requested column value and
//       also less than or equal to the requested column.
//    Must scan through ALL line numbers because there can be more than one
//       close or equal linenum out of order that can be closer to the
//       requested column.
//    Fill in the return values.
//       The address range is only the range of the single entry. The end
//       address is the address of the next entry unless the same, then it
//       is the first linenum after with a different address.
//    The column range is surrounding column points that contain the requested
//       column.  The left column is initialized to 1 and is the largest
//       column value less than the requested column.  The right column is
//       initialized to smallest column value greater than or equal to the
//       requested column.  A column value of 0 is the same as SYM_MAX_COLUMN
//------------------------------------------------------------------------
RETCODE
LinenumTable::GetLinenumStmt(SYM_DESCRIPTOR     module,
                             LINENUM_TYPE       requestedLinenum,
                             COLUMN_TYPE        requestedColumn,
                             DESCRIPTOR         addrRangeDesc,
                             LINENUM_TYPE       *actualLinenum,
                             COLUMN_RANGE_TYPE  *actualColumnRange,
                             LINENUM_DESCRIPTOR *nextIndex)  {

   RETCODE  err;
   OFFSET_ADDR_TYPE startAddr, endAddr;

   TABLE_OFFSET offset;

   // set to unattainable value so that loop can be checked at end
   TABLE_OFFSET closestOffset = MAX_LINENUM_OFFSET;

   LINENUM_STRUCT_TYPE *linenumPtr;
   // difference between requested linenum and comparison linenum
   U16         linenumDelta = MAX_DELTA;
   U16         tmpDelta;
   COLUMN_TYPE leftColumn,
               priorLeftColumn,       // holds column before last column if
                                      // requested column past last column
               rightColumn,
               localColumn;           // local store of column being evaluated
   BOOLEAN     reqLessThanaColumnFound;  // requested column less than or
                                         // equal to stored columns found
   TABLE_OFFSET greaterThanOffset;    // represents offset of linenum on
                                      // matching line that has column closest
                                      // but less than requested column
   BOOLEAN     firstTimeOnReqLine = FALSE;
   // transition from not on line to on line.  Only TRUE for one entry;
   // used to reset rightColumn

   // update class vars for start and end of linenums for this module
   if (GOOD != (err = UpdateCacheParams(module)))
      return err;

   offset = linenumOffsetStart;

   // linenumOffsetEnd points to linenum AFTER last linenum in module.
   // Must end loop when loop pointer <offset> points to the last linenum -
   // i.e. the dummy linenum
   // init column value and state holders
   leftColumn = 0;  // init to be less than smallest column so first pass
                    // will cause greaterThanOffset to be updated
   priorLeftColumn = 0;   // same
   rightColumn = SYM_MAX_COLUMN;     // init right column marker
   reqLessThanaColumnFound = FALSE;
   greaterThanOffset = MAX_LINENUM_OFFSET; // default to largest so will fail
                                           // if no match found
   while (offset < linenumOffsetEnd)  {
      linenumPtr = (LINENUM_STRUCT_TYPE *)GetHugeDataPtr(offset);
      if (requestedLinenum <= (linenumPtr->linenum)) {
         tmpDelta = (linenumPtr->linenum) - requestedLinenum;
         localColumn = linenumPtr->column;

         if (tmpDelta == 0)  { // on the requested line
            linenumDelta = 0;  // set so that < comparison always fails
            if (!firstTimeOnReqLine) {
               rightColumn = SYM_MAX_COLUMN;
               firstTimeOnReqLine = TRUE;
            }
            if (localColumn >= requestedColumn) {
               // requested column is possibly within boundary of statement
               // note: local columns point to last valid char of statement
               reqLessThanaColumnFound = TRUE;
               if (rightColumn >= localColumn) {
                  // new entry has column closer to requested column than
                  // previous found
                  rightColumn = localColumn;
                  closestOffset = offset;  // save linenum with closer column
               }
            } else { // requested column is beyond column looked up
               // if new column looked up is closer to requested column,
               // save it and the linenum offset; it may become the
               // returned linenum if nothing closer shows up
               // Must keep track of second largest column; it defines the
               // end of the previous stmt which defines the left column
               // Cannot block out new columns less than previous column
               // because it could be the previous stmt.  This occurs because
               // columns can be out of order; e.g. 25, 41, 35
               // We want to end up with leftColumn the biggest column,
               // priorLeftColumn the next biggest
               if (localColumn > leftColumn) {
                  if (leftColumn > priorLeftColumn)
                     priorLeftColumn = leftColumn;
                  leftColumn = localColumn;  // slide left column closer to
                                             // requested column
                  greaterThanOffset = offset;
               } else {
                  if (localColumn > priorLeftColumn) {
                     priorLeftColumn = localColumn;
                  }
               }
            }
         // the next two else ifs are finding the closest column to
         // column 1
         } else if (tmpDelta < linenumDelta) { // a  closer match
            // init for new line
            leftColumn = 1;
            rightColumn = localColumn; // init right column marker to
                                       // end of this statement
            reqLessThanaColumnFound = TRUE;  // valid column data
            closestOffset = offset;  // save closer linenum

            // set the local requested column var copy to column 1 if not
            // matching requested line
            linenumDelta = tmpDelta;  // save closer approximation
         } else if (tmpDelta == linenumDelta) {
            // same "not equal" linenum, different column; move toward right
            if (localColumn < rightColumn) {
               rightColumn = localColumn;
               closestOffset = offset;  // save closer linenum
            }
         }  // end of if (tmpDelta == 0
      }  // end of if (requestedLinenum <= (linenumPtr->linenum))

      offset += LINENUM_STRUCT_SIZE;  // move to next linenum entry
   } // end of while

   if (reqLessThanaColumnFound == FALSE) {
      closestOffset = greaterThanOffset;  // go back to closest if < not found
      leftColumn = priorLeftColumn;       // start of column making up last
                                          // stored column
   }
   // check for no match
   if (MAX_LINENUM_OFFSET == closestOffset)
      return ER_LINENUM_NOT_FOUND;

   linenumPtr = (LINENUM_STRUCT_TYPE *)GetHugeDataPtr(closestOffset);

   *actualLinenum = linenumPtr->linenum;

   // fill in start address of line number found
   startAddr = linenumPtr->addr;   // used for comparison to end addr
   if (GOOD != (err =
      UtilSetAddrOffset(module, startAddr, addrRangeDesc)))
      return err;

   // columns point to end of stmt; need to bump by one except for column 1
   if (leftColumn > 1)
      leftColumn++;
   actualColumnRange->columnStart = leftColumn;
   actualColumnRange->columnEnd = rightColumn;

   *nextIndex = closestOffset + LINENUM_STRUCT_SIZE;

   // fill in end address of statement found
   for ((offset = *nextIndex);     // point to one following found linenum
        (offset <= linenumOffsetEnd);  // stops on top of dummy linenum
        (offset += LINENUM_STRUCT_SIZE))  {
      linenumPtr = (LINENUM_STRUCT_TYPE *)GetHugeDataPtr(offset);
      if ((linenumPtr->addr) > startAddr)
         break;
   }  // end of for

   // the end address must point to the last byte of the linenum code range,
   // (rather than start of next linenum) therefore must subtract 1 from it
   // do a sanity check first
   endAddr = linenumPtr->addr;
   if (endAddr < startAddr) { // invalid end address
      // return a valid address range as well as an error
      err = UtilSetEndAddrOffset(module, startAddr, addrRangeDesc);
      return ER_SYM_INVALID_LN_END_ADDR;
   } else {
      endAddr--;
   }
   return UtilSetEndAddrOffset(module, endAddr, addrRangeDesc);
}  // end of LinenumTable::GetLinenumStmt


//------------------------------------------------------------------------
// GetLinenumStmtByIndex
//
// Design:
//    Check for valid index, return if not
//    Look up linenum at location <index>, set linenum, the nextIndex,
//       and fill in addrRangeDesc
//    Fill in end columnRange = column of the retrieved linenum.
//    Fill in the starting column:
//       Initialize starting column to 1.
//       From beginning to end of linenum list:
//          if (entry = retrieved linenum) && (column < retrieved column)
//             set starting column to max(present value, column value)
//------------------------------------------------------------------------
RETCODE
LinenumTable::GetLinenumStmtByIndex(SYM_DESCRIPTOR     module,
                                    LINENUM_DESCRIPTOR index,
                                    DESCRIPTOR         addrRangeDesc,
                                    LINENUM_TYPE       *linenum,
                                    COLUMN_RANGE_TYPE  *columnRange,
                                    LINENUM_DESCRIPTOR *nextIndex)  {

   RETCODE err, firstErr = GOOD;
   OFFSET_ADDR_TYPE startAddr, endAddr;
   TABLE_OFFSET offset;

   // update class vars for start and end of linenums for this module
   if (GOOD != (err = UpdateCacheParams(module)))
      return err;

   // check for the index beyond end of linenums - could have been
   // post-incremented on prior call
   if (index >= linenumOffsetEnd)
      return ER_LINENUM_INDEX_TOO_LARGE;

   LINENUM_STRUCT_TYPE *linenumPtr;

   linenumPtr = (LINENUM_STRUCT_TYPE *)GetHugeDataPtr(index);

   *linenum = linenumPtr->linenum;
   columnRange->columnEnd  = linenumPtr->column;  // marks the end point

   startAddr = linenumPtr->addr;   // used for comparison to end addr
   if (GOOD != (err =
      UtilSetAddrOffset(module, startAddr, addrRangeDesc)))
      return err;

   *nextIndex = index + LINENUM_STRUCT_SIZE;

   // fill in end address of statement found
   for ((offset = *nextIndex);     // point to one following found linenum
        (offset <= linenumOffsetEnd);  // stops on top of dummy linenum
        (offset += LINENUM_STRUCT_SIZE))  {
      linenumPtr = (LINENUM_STRUCT_TYPE *)GetHugeDataPtr(offset);
      if ((linenumPtr->addr) > startAddr)
         break;
   }  // end of for

   // the end address must point to the last byte of the linenum code range,
   // (rather than start of next linenum) therefore must subtract 1 from it
   // do a sanity check first
   endAddr = linenumPtr->addr;
   if (endAddr < startAddr) { // invalid end address
      // return a valid address range as well as an error
      err = UtilSetEndAddrOffset(module, startAddr, addrRangeDesc);
      firstErr = ER_SYM_INVALID_LN_END_ADDR;
   } else {
      endAddr--;
   }
   err = UtilSetEndAddrOffset(module, endAddr, addrRangeDesc);

   // get the start column by finding the closest column value to the end
   // column on the same line
   for (index = linenumOffsetStart, columnRange->columnStart = 1;
        index < linenumOffsetEnd;
        index += LINENUM_STRUCT_SIZE)  {
      linenumPtr = (LINENUM_STRUCT_TYPE *)GetHugeDataPtr(index);
     
      if ((linenumPtr->linenum) == *linenum)  {
         if (linenumPtr->column < columnRange->columnEnd)  {
         columnRange->columnStart = max(columnRange->columnStart,
                                        linenumPtr->column);
         }
      }
   }  // end of for
   // columns point to last character of stmt; need to increment to point
   // to first char of next stmt but only if not first char
   if (columnRange->columnStart > 1)
      (columnRange->columnStart)++;
   if (GOOD == firstErr) firstErr = err;
   return firstErr;
}  // end of GetLinenumStmtByIndex


//------------------------------------------------------------------------
// LinenumTable::LinenumEnd()
//
// Purpose: Close the line number loading for a specific module
//
// Input parameters:
//
// Output parameters:
//
// Error: Generates error if no line numbers have been loaded
//
// PseudoCode:
//    Register the start and end pointers with owner module
//    If no line numbers have been added, return that error code
//    Insert a final dummy line number into table.  Its address is the end
//       address of the module if larger than the previous line number, or 
//       one greater than the previous if not.  Its line number value is
//       the largest value possible.
//    If not already sorted, sort linenum list by address
//------------------------------------------------------------------------
RETCODE
LinenumTable::LinenumEnd()  {

   TABLE_OFFSET        offset;
   LINENUM_STRUCT_TYPE *linenumPtr;
   SYM_TYPE_MODULE     *modulePtr;
   LINENUM_STRUCT_TYPE lastLinenumVar;
   U32                 linenumAddr;
   RETCODE             err;

   // update the end pointer of the line numbers (points to location where
   // the dummy line number will be added)
   linenumOffsetEnd = GetAvailMemOffset();

   // store linenum pointers into module structure
   modulePtr = (SYM_TYPE_MODULE *)st.GetHugeDataPtr(cachedModDescriptor);
   modulePtr->linenumOffsetStart = linenumOffsetStart;
   modulePtr->linenumOffsetEnd = linenumOffsetEnd;

   // test to see if any line numbers have been added
   if (linenumOffsetEnd <= linenumOffsetStart) {
      linenumLoadState = NOT_LOADING;  // <Judy 5/10/96>
      return ER_NO_LINENUMS_ADDED;
   }

   // get offset to last added line number, then address of that linenum
   offset = linenumOffsetEnd - LINENUM_STRUCT_SIZE;
   linenumPtr = (LINENUM_STRUCT_TYPE *)GetHugeDataPtr(offset);
   linenumAddr = linenumPtr->addr;

   // build up dummy line number, then store away.
   // First, set the address of the dummy line number to the end address of
   // the parent module + 1.  The value must be one greater than the range
   // of the module so the binary search will see that the requested address
   // is inside the last linenum and this "made-up" linenum.

   lastLinenumVar.addr = modulePtr->symHeader.symHeader.endAddrInfo.endAddr;
   if (lastLinenumVar.addr < 0xFFFFFFFFL)
      lastLinenumVar.addr++;

   // compare module end address with last line number address saved;
   // if module address is smaller, set the created last line number address
   // to one more than previous line number
   if (lastLinenumVar.addr <= linenumAddr)  {
      lastLinenumVar.addr = linenumAddr + 1L;
   }
   lastLinenumVar.linenum = MAX_LINENUM;
   lastLinenumVar.column = SYM_MAX_COLUMN;
   if ((err = AddLinenum(lastLinenumVar)) != GOOD) return err;

   linenumLoadState = NOT_LOADING;

   if (!sorted)  {
      // sort list
      return ER_LINENUMS_NOT_SORTED;
   }
   return GOOD;
}  // end of LinenumTable::LinenumEnd


//------------------------------------------------------------------------
// LinenumTable::LinenumStart
//
// Purpose: Starts, or opens, the addition of line numbers to selected
//          module
// Design:
//    Get module; register its SYM_DESCRIPTOR with the LT
//    Initialize start and end pointers
//------------------------------------------------------------------------
RETCODE
LinenumTable::LinenumStart(SYM_DESCRIPTOR moduleDescriptor)  {

   SYM_TYPE_MODULE *modulePtr;

   modulePtr = (SYM_TYPE_MODULE *)st.GetHugeDataPtr(moduleDescriptor);
   if (modulePtr->linenumOffsetEnd)  {
      // non-zero means this module already has linenums loaded
      return ER_LINENUMS_ALREADY_LOADED;
   }
   cachedModDescriptor = moduleDescriptor;
   linenumOffsetStart = linenumOffsetEnd = GetAvailMemOffset();

   // set defaults
   sorted = TRUE;
   previousLine.addr = 0L;
   previousLine.linenum = 0;
   previousLine.column = 0;
   linenums = 0;

   modulesLoaded++;
   linenumLoadState = LOADING;
   return GOOD;
}  // end of LinenumTable::LinenumStart


//------------------------------------------------------------------------
// LinenumTable::MapAddr2Linenum
//
// Purpose:
//    Gets the line number information given an address and module descriptor
//    The address range returned is the range of addresses of the same
//       contiguous linenum looked up
//
// Design:
//    Perform binary search on linenum list
//    Mark the linenum entry whose address range encloses the requested
//       address ( >= linenum address AND < next linenum addresss)
//    Break out of loop if an exact match occurs
//    Fill in the return values.
//       The address range is the range of only the line number that contains
//       the requested address.
//       Fill in index of linenum found
//------------------------------------------------------------------------
RETCODE
LinenumTable::MapAddr2Linenum(SYM_LINE_TYPE      lineType,
                              OFFSET_ADDR_TYPE   requestedAddr,
                              SYM_DESCRIPTOR     module,
                              LINENUM_TYPE       *linenum,
                              COLUMN_RANGE_TYPE  *columnRange,
                              DESCRIPTOR         addrRangeDesc,
                              LINENUM_DESCRIPTOR *index)  {

   RETCODE err;

   // update class vars for start and end of linenums for this module
   if (GOOD != (err = UpdateCacheParams(module)))
      return err;

   BOOLEAN found;
   S32 bottom, middle, top;  // table indices
   LINENUM_STRUCT_TYPE *linenumPtr, *nextLinenumPtr;

   // normalize all linenum table indices by dividing by the size of a
   // linenum structure
   bottom = linenumOffsetStart / LINENUM_STRUCT_SIZE;

   // the end of the list of linenums is one record less than the end pointer
   // because the end pointer points to the dummy linenum
   top = (linenumOffsetEnd - LINENUM_STRUCT_SIZE) / LINENUM_STRUCT_SIZE;

   found = FALSE;

   while (top >= bottom)  {
      middle = (top + bottom) / 2;
      linenumPtr =
      (LINENUM_STRUCT_TYPE *)GetHugeDataPtr(middle * LINENUM_STRUCT_SIZE);

      // test for requestedAddr < *middle linenum addr
      if (requestedAddr < (linenumPtr->addr))  {
         top = middle - 1;
      }
      // test for requestedAddr > *middle next linenum addr
      // NOTE: compare is >= because the end address is represented by the
      // next line number (start) address.  The size returned should subtract
      // one from that address when returning the range.
      // i.e. address of linenum entry - address of (entry + 1) - 1 =
      //   address range of the linenum
      else  {
         nextLinenumPtr = (LINENUM_STRUCT_TYPE *)
            GetHugeDataPtr((middle + 1) * LINENUM_STRUCT_SIZE);
         if (requestedAddr >= (nextLinenumPtr->addr))  {
            bottom = middle + 1;
         } else {
            // a match was found
            found = TRUE;
            break;
         }
      }
   }  // end of while

   if (!found)
      return ER_ADDRESS_NOT_FOUND;

   // fill in parameters
   if (lineType == SYM_LINE) {
      return GetLinenumByIndex(module,
                               (middle * LINENUM_STRUCT_SIZE),
                                     // offset to found linenum
                               addrRangeDesc,
                               index,
                               linenum,
                               &(columnRange->columnStart));
   } else { //
      return GetLinenumStmtByIndex(module,
                                   (middle * LINENUM_STRUCT_SIZE),
                                      // offset to found linenum
                                   addrRangeDesc,
                                   linenum,
                                   columnRange,
                                   index);
   }

}  // end of LinenumTable::MapAddr2Linenum


//------------------------------------------------------------------------
// LinenumTable::UpdateCacheParams
//
// Purpose:
//    fills in the cached module descriptor and start and end offsets
//    into the linenum table for the requested module, but only if
//    the requested module descriptor is different.
//    Checks that there are linenums and tries to get them (on-demand)
//    if not loaded.  Generates error if not linenums available.
//------------------------------------------------------------------------
RETCODE
LinenumTable::UpdateCacheParams(SYM_DESCRIPTOR moduleDesc)  {

   SYM_TYPE_MODULE *modulePtr;
   RETCODE         err;

     if (linenumLoadState == LOADING)
        LinenumEnd();  // should never occur if loader makes the correct call
                     // sequences
   // get module start-end indices to line number table if requested module
   // different than cached module descriptor
   if (moduleDesc != cachedModDescriptor)  {
      if (NULL_SYMBOL == moduleDesc) {
         return ER_SYMBOL_NOT_A_MODULE;
      }
      modulePtr = (SYM_TYPE_MODULE *)st.GetHugeDataPtr(moduleDesc);
      if (((modulePtr->symHeader.symHeader.typeIndex.symType) & 0xF)
         != SYM_MODULE)
         return ER_SYMBOL_NOT_A_MODULE;
      cachedModDescriptor = moduleDesc;
      linenumOffsetStart = modulePtr->linenumOffsetStart;
      linenumOffsetEnd   = modulePtr->linenumOffsetEnd;
   }

   // now check that linenums are loaded
   if (linenumOffsetStart >= linenumOffsetEnd) {
      // linenums not yet loaded
      if (GOOD != (err = UtilOnDemandLoad(moduleDesc))) {
         return err;
      }

      // now get cached params
      modulePtr = (SYM_TYPE_MODULE *)st.GetHugeDataPtr(moduleDesc);
      if (((modulePtr->symHeader.symHeader.typeIndex.symType) & 0xF)
         != SYM_MODULE) {
         return ER_SYMBOL_NOT_A_MODULE;
      }
      linenumOffsetStart = modulePtr->linenumOffsetStart;
      linenumOffsetEnd   = modulePtr->linenumOffsetEnd;

      // check once again for valid linenums
      if (linenumOffsetStart >= linenumOffsetEnd) {
         return ER_NO_LINENUMS_ADDED;
      }
   }
   return GOOD;
} // end of UpdateCacheParams


#if 0
//------------------------------------------------------------------------
// LinenumTable::AreThereLinenums
//
// Purpose:
//    Checks that there were linenums for this module.
//
// Return parameter:
//   TRUE ==> module contains linenums
//   FALSE ==> module does not contain linenums
//------------------------------------------------------------------------
BOOLEAN LinenumTable::AreThereLinenums(SYM_DESCRIPTOR module) {

   SYM_TYPE_MODULE *modulePtr;

   modulePtr = (SYM_TYPE_MODULE *)st.GetHugeDataPtr(module);
   if (modulePtr->linenumOffsetStart >= modulePtr->linenumOffsetEnd) {
      return FALSE;
   } else {
      return TRUE;
   }
}  // end of AreThereLinenums
#endif

                       /****************************
                        *                          *
                        *   NON-MEMBER FUNCTIONS   *
                        *                          *
                        ****************************/
#ifdef __cplusplus
extern "C" {
#endif

//------------------------------------------------------------------------
// SymAddLinenum
//
// Design:
//    Insert a line number into the lt table
//------------------------------------------------------------------------
RETCODE EXPORT
SymAddLinenum(LINENUM_TYPE     linenum,
              U8               column,
              OFFSET_ADDR_TYPE addr)  {

   LINENUM_STRUCT_TYPE  linenumVar;

   linenumVar.addr = addr;
   linenumVar.linenum = linenum;
   linenumVar.column = column;
   return lt.AddLinenum(linenumVar);
}  // end of SymAddLinenum


//------------------------------------------------------------------------
// SymAddLinenumEnd
//------------------------------------------------------------------------
RETCODE EXPORT
SymAddLinenumEnd()  {
   
   return lt.LinenumEnd();   // close the line number entries for module
}  // end of SymAddLinenumEnd


//------------------------------------------------------------------------
// SymAddLinenumStart
//
// Purpose: Starts, or opens, the addition of line numbers to selected
//          module
// Design:
//    Get module; register its SYM_DESCRIPTOR with the LT
//    Initialize start and end pointers
//------------------------------------------------------------------------
RETCODE EXPORT
SymAddLinenumStart(SYM_DESCRIPTOR moduleDescriptor)  {

   // open the line number entries for module
   return lt.LinenumStart(moduleDescriptor);
}  // end of SymAddLinenumStart


//------------------------------------------------------------------------
// SymGetLinenum
//
// Design:  Call loader if line numbers have not been loaded for this module
//------------------------------------------------------------------------
RETCODE EXPORT
SymGetLinenum(SYM_DESCRIPTOR     moduleDesc,
              LINENUM_TYPE       linenum,
              DESCRIPTOR         addrRangeDesc,
              LINENUM_TYPE       *actualLinenum,
              COLUMN_TYPE        *actualColumn,
              LINENUM_DESCRIPTOR *nextIndex)  {

   /* test for valid module desc */
   if (!UtilIsValidSymDescriptor(moduleDesc))
      return ER_ADDRESS_NOT_FOUND;

   return (lt.GetLinenum(moduleDesc,
                         linenum,
                         addrRangeDesc,
                         actualLinenum,
                         actualColumn,
                         nextIndex));
}  // end of SymGetLinenum

//------------------------------------------------------------------------
// SymGetLinenumInOrder
//
// Design:  Call loader if line numbers have not been loaded for this module
//------------------------------------------------------------------------
RETCODE EXPORT
SymGetLinenumInOrder(SYM_DESCRIPTOR     moduleDesc,
                     LINENUM_TYPE       linenum,
                     DESCRIPTOR         addrRangeDesc,
                     LINENUM_TYPE       *actualLinenum,
                     LINENUM_DESCRIPTOR *nextIndex)  {

   /* test for valid module desc */
   if (!UtilIsValidSymDescriptor(moduleDesc))
      return ER_ADDRESS_NOT_FOUND;

   return (lt.GetLinenumInOrder(moduleDesc,
                         linenum,
                         addrRangeDesc,
                         actualLinenum,
                         nextIndex));
}  // end of SymGetLinenumInOrder

//------------------------------------------------------------------------
// SymGetLinenumByIndex
//
// Design:
//------------------------------------------------------------------------
RETCODE EXPORT
SymGetLinenumByIndex(SYM_DESCRIPTOR     moduleDesc,
                     LINENUM_DESCRIPTOR index,
                     DESCRIPTOR         addrRangeDesc,
                     LINENUM_DESCRIPTOR *nextIndex,
                     LINENUM_TYPE       *linenum,
                     COLUMN_TYPE        *column)  {

   /* test for valid module desc */
   if (!UtilIsValidSymDescriptor(moduleDesc))
      return ER_ADDRESS_NOT_FOUND;

   return lt.GetLinenumByIndex(moduleDesc,
                               index,
                               addrRangeDesc,
                               nextIndex,
                               linenum,
                               column);
}  // end of SymGetLinenumByIndex


//------------------------------------------------------------------------
// SymGetLinenumStmt
//
// Design:
//------------------------------------------------------------------------
RETCODE EXPORT
SymGetLinenumStmt(SYM_DESCRIPTOR       moduleDesc,
                  LINENUM_TYPE         linenum,
                  COLUMN_TYPE          column,
                  DESCRIPTOR           addrRangeDesc,
                  LINENUM_TYPE         *actualLinenum,
                  COLUMN_RANGE_TYPE    *columnRange,
                  LINENUM_DESCRIPTOR   *nextIndex)  {

   /* test for valid module desc */
   if (!UtilIsValidSymDescriptor(moduleDesc))
      return ER_ADDRESS_NOT_FOUND;

   return lt.GetLinenumStmt(moduleDesc,
                            linenum,
                            column,
                            addrRangeDesc,
                            actualLinenum,
                            columnRange,
                            nextIndex);
}  // end of SymGetLinenumStmt


//------------------------------------------------------------------------
// SymGetLinenumStmtByIndex
//
// Design:
//------------------------------------------------------------------------
RETCODE EXPORT
SymGetLinenumStmtByIndex(SYM_DESCRIPTOR     moduleDesc,
                         LINENUM_DESCRIPTOR index,
                         DESCRIPTOR         addrRangeDesc,
                         LINENUM_TYPE       *linenum,
                         COLUMN_RANGE_TYPE  *columnRange,
                         LINENUM_DESCRIPTOR *nextIndex)  {

   /* test for valid module desc */
   if (!UtilIsValidSymDescriptor(moduleDesc))
      return ER_ADDRESS_NOT_FOUND;

   return lt.GetLinenumStmtByIndex(moduleDesc,
                                   index,
                                   addrRangeDesc,
                                   linenum,
                                   columnRange,
                                   nextIndex);
}  // end of SymGetLinenumStmtByIndex


//------------------------------------------------------------------------
// SymMapAddr2Linenum
//
// Design:
//    Convert abstract address into logical address
//       If sel:offset, offset is direct address to look up with linenum addr
//       If logical, must get base address pointed to by module desc base
//          index.  Subtract it from requested linenum address before search.
//    Check for line numbers loaded before calling the map function
//------------------------------------------------------------------------
RETCODE EXPORT
SymMapAddr2Linenum(DESCRIPTOR         linenumAddrDesc,
                   SYM_DESCRIPTOR     moduleDesc,
                   LINENUM_TYPE       *linenum,
                   COLUMN_TYPE        *column,
                   DESCRIPTOR         addrRangeDesc,
                   LINENUM_DESCRIPTOR *index)  {

   RETCODE           err;
   OFFSET_ADDR_TYPE  offsetAddr;
   COLUMN_RANGE_TYPE columnRange;

   /* test for valid module desc */
   if (!UtilIsValidSymDescriptor(moduleDesc))
      return ER_ADDRESS_NOT_FOUND;

   // get offset address from the module descriptor and requested linenum
   // logical address
   if (GOOD != (err = UtilGetOffsetFromAddrDesc(moduleDesc,
                                                linenumAddrDesc,
                                                &offsetAddr)))
       return err;
   err = lt.MapAddr2Linenum(SYM_LINE,
                            offsetAddr,
                            moduleDesc,
                            linenum,
                            &columnRange,
                            addrRangeDesc,
                            index);
   *column = columnRange.columnStart;
   return err;
}  // end of SymMapAddr2Linenum


//------------------------------------------------------------------------
// SymMapAddr2LinenumStmt
//
// Design:
//    Convert abstract address into logical address
//       If sel:offset, offset is direct address to look up with linenum addr
//       If logical, must get base address pointed to by module desc base
//          index.  Subtract it from requested linenum address before search.
//    Check for line numbers loaded before calling the map function
//
// Error: returns ER_ADDRESS_NOT_FOUND if module desciptor not valid
//------------------------------------------------------------------------
RETCODE EXPORT
SymMapAddr2LinenumStmt(DESCRIPTOR         linenumAddrDesc,
                       SYM_DESCRIPTOR     moduleDesc,
                       LINENUM_TYPE       *linenum,
                       COLUMN_RANGE_TYPE  *columnRange,
                       DESCRIPTOR         addrRangeDesc,
                       LINENUM_DESCRIPTOR *index)  {

   RETCODE          err;
   OFFSET_ADDR_TYPE offsetAddr;

   /* test for valid module desc */
   if (!UtilIsValidSymDescriptor(moduleDesc))
      return ER_ADDRESS_NOT_FOUND;

   // get offset address from the module descriptor and requested linenum
   // logical address
   if (GOOD != (err = UtilGetOffsetFromAddrDesc(moduleDesc,
                                                linenumAddrDesc,
                                                &offsetAddr)))
       return err;
   return lt.MapAddr2Linenum(SYM_STMT,
                             offsetAddr,
                             moduleDesc,
                             linenum,
                             columnRange,
                             addrRangeDesc,
                             index);
}  // end of SymMapAddr2LinenumStmt


//------------------------------------------------------------------------
// SymMapAddr2LinenumModule
//
// Design:
//    Convert abstract address into logical address
//    Look up the module descriptor for the requested address
//    Check for line numbers loaded before calling the MapAddr2Linenum
//       function
//------------------------------------------------------------------------
RETCODE EXPORT
SymMapAddr2LinenumModule(DESCRIPTOR         linenumAddrDesc,
                         SYM_DESCRIPTOR     *moduleDesc,
                         DESCRIPTOR         modAddrRangeDesc,
                         LINENUM_TYPE       *linenum,
                         COLUMN_TYPE        *column,
                         DESCRIPTOR         addrRangeDesc,
                         LINENUM_DESCRIPTOR *index)  {

   RETCODE        err;
   SYM_TYPE_TYPE  symbolType;
   U32            offset;
   SYM_DESCRIPTOR outputSymbol;
   SYM_DESCRIPTOR funcDescriptor;
   OFFSET_ADDR_TYPE offsetAddr;
   MEM_ADDR_CLASS memoryClass;
   COLUMN_RANGE_TYPE columnRange;

   if ((err = SymMapAddr2Symbol(linenumAddrDesc,
                                &memoryClass,
                                &symbolType,
                                &offset,
                                &outputSymbol,
                                &funcDescriptor,
                                moduleDesc)) != GOOD)  {
      return err;
   }
   /* test for valid module desc */
   if (!UtilIsValidSymDescriptor(*moduleDesc))
      return ER_ADDRESS_NOT_FOUND;

   COMMON_SYMBOL_HEADER  *modPtr;

   // set module address range
   modPtr = (COMMON_SYMBOL_HEADER *)st.GetHugeDataPtr(*moduleDesc);
   if (GOOD != (err =
      UtilSetAddrOffset(*moduleDesc,
                        modPtr->beginAddrInfo.startAddr,
                        modAddrRangeDesc)))
      return err;
   if (GOOD != (err =
      UtilSetEndAddrOffset(*moduleDesc,
                           modPtr->endAddrInfo.endAddr,
                           modAddrRangeDesc)))
      return err;

   // get offset address from the module descriptor and requested linenum
   // logical address
   if (GOOD != (err = UtilGetOffsetFromAddrDesc(*moduleDesc,
                                                linenumAddrDesc,
                                                &offsetAddr)))
       return err;
   err = lt.MapAddr2Linenum(SYM_LINE,
                            offsetAddr,
                            *moduleDesc,
                            linenum,
                            &columnRange,
                            addrRangeDesc,
                            index);
   *column = columnRange.columnStart;
   return err;
}  // end of SymMapAddr2LinenumModule


//------------------------------------------------------------------------
// SymMapAddr2LinenumStmtModule
//
// Design:
//    Convert abstract address into logical address
//    Look up the module descriptor for the requested address
//    Check for line numbers loaded before calling the MapAddr2Linenum
//       function
//------------------------------------------------------------------------
RETCODE EXPORT
SymMapAddr2LinenumStmtModule(DESCRIPTOR         linenumAddrDesc,
                             SYM_DESCRIPTOR     *moduleDesc,
                             DESCRIPTOR         modAddrRangeDesc,
                             LINENUM_TYPE       *linenum,
                             COLUMN_RANGE_TYPE  *columnRange,
                             DESCRIPTOR         addrRangeDesc,
                             LINENUM_DESCRIPTOR *index)  {

   RETCODE        err;
   SYM_TYPE_TYPE  symbolType;
   U32            offset;
   SYM_DESCRIPTOR outputSymbol;
   SYM_DESCRIPTOR funcDescriptor;
   OFFSET_ADDR_TYPE offsetAddr;
   MEM_ADDR_CLASS memoryClass;

   if ((err = SymMapAddr2Symbol(linenumAddrDesc,
                                &memoryClass,
                                &symbolType,
                                &offset,
                                &outputSymbol,
                                &funcDescriptor,
                                moduleDesc)) != GOOD)  {
      return err;
   }
   /* test for valid module desc */
   if (!UtilIsValidSymDescriptor(*moduleDesc))
      return ER_ADDRESS_NOT_FOUND;

   COMMON_SYMBOL_HEADER  *modPtr;

   // set module address range
   modPtr = (COMMON_SYMBOL_HEADER *)st.GetHugeDataPtr(*moduleDesc);
   if (GOOD != (err =
      UtilSetAddrOffset(*moduleDesc,
                        modPtr->beginAddrInfo.startAddr,
                        modAddrRangeDesc)))
      return err;
   if (GOOD != (err =
      UtilSetEndAddrOffset(*moduleDesc,
                           modPtr->endAddrInfo.endAddr,
                           modAddrRangeDesc)))
      return err;

   // get offset address from the module descriptor and requested linenum
   // logical address
   if (GOOD != (err = UtilGetOffsetFromAddrDesc(*moduleDesc,
                                                linenumAddrDesc,
                                                &offsetAddr)))
       return err;
   return lt.MapAddr2Linenum(SYM_STMT,
                             offsetAddr,
                             *moduleDesc,
                             linenum,
                             columnRange,
                             addrRangeDesc,
                             index);
}  // end of SymMapAddr2LinenumStmtModule


//------------------------------------------------------------------------
// GetRawLinenumByIndex
//
// Purpose: return basic info on linenum; return next index.  If index
//          out of bounds, return error
//
// Error: returns ER_LINENUM_INDEX_TOO_LARGE when the input index is
//        beyond the end of the table; this is used to test for the end
//        of iteration
//------------------------------------------------------------------------
RETCODE EXPORT
GetRawLinenumByIndex(SYM_DESCRIPTOR     module,
                     LINENUM_DESCRIPTOR index,
                     DESCRIPTOR         linenumAddr,
                     LINENUM_TYPE       *linenum,
                     COLUMN_TYPE        *column,
                     LINENUM_DESCRIPTOR *nextIndex) {

   return lt.GetRawLinenumByIndex(module,
                                  index,
                                  linenumAddr,
                                  linenum,    // already a pointer
                                  column,     //    "
                                  nextIndex); //    "

}  // end of GetRawLinenumByIndex


//------------------------------------------------------------------------
// GetLinenumFirstIndex
//
// Purpose: Returns the index to the first linenum in module table
//
// Error: returns warning if there are no linenums              
//------------------------------------------------------------------------
RETCODE EXPORT
GetLinenumFirstIndex(SYM_DESCRIPTOR         moduleDesc,
                     LINENUM_DESCRIPTOR FAR *nextIndex) {

   return GetLinenumFirstIndex(moduleDesc,
                               nextIndex);  // already a pointer

}  // end of GetLinenumFirstIndex

#ifdef __cplusplus
}
#endif
/******************************** E O F *************************************/
