Subversion Repositories Kolibri OS

Rev

Blame | Last modification | View Log | Download | RSS feed

  1. /****************************  containers.h   ********************************
  2. * Author:        Agner Fog
  3. * Date created:  2006-07-15
  4. * Last modified: 2007-02-01
  5. * Project:       objconv
  6. * Module:        containers.h
  7. * Description:
  8. * Header file for container classes and dynamic memory allocation
  9. *
  10. * Copyright 2006-2008 GNU General Public License http://www.gnu.org/licenses
  11. *****************************************************************************/
  12.  
  13. /*****************************************************************************
  14. This header file declares various container classes for dynamic allocation
  15. of memory for files and other types of data with unpredictable sizes.
  16. These classes have private access to the memory buffer in order to prevent
  17. memory leaks. It is important to use these classes for all dynamic memory
  18. allocation.
  19.  
  20. The class CMemoryBuffer and its descendants are used for many purposes of
  21. storage of data with a size that is not known in advance. CMemoryBuffer
  22. allows the size of its data to grow when new data are appended with the
  23. Push() member function.
  24.  
  25. The class CFileBuffer, which is derived from CMemoryBuffer, is used for
  26. reading, writing and storing object files and other files.
  27.  
  28. There are many different classes for different things you can do with
  29. an object file. These classes, declared in converters.h, are all
  30. descendants of CFileBuffer. It is possible to transfer a data buffer
  31. from one object to another by the operator
  32.  
  33.        A >> B
  34.  
  35. where A and B are both objects of classes that descend from CFileBuffer.
  36. The file buffer that was owned by A is transferred to B and A is left empty
  37. after the A >> B operation. This makes sure that a memory buffer is always
  38. owned by one, and only one, object. The opposite operator B << A does the
  39. same thing.
  40.  
  41. The >> operator is used whenever we want to do something to a file buffer
  42. that requires a specialized class. The file buffer is transferred from the
  43. object that owns it to an object of the specialized class and transferred
  44. back again to the original owner when the object of the specialized class
  45. has done its job.
  46.  
  47. You may say that the descendants of CFileBuffer have a chameleonic nature:
  48. You can change the nature of a piece of data owned by an object by
  49. transferring it to an object of a different class. This couldn't be done
  50. by traditional polymorphism because it is not possible to change the class
  51. of an object after it is created, and there are too many different things
  52. you can do with object files for a single class to handle them all.
  53.  
  54. The container class CMemoryBuffer is useful for storing data of mixed types.
  55. Data of arbitrary type can be accessed by Get<type>(offset) or by
  56. Buf() + offset.
  57.  
  58. If all items in a dynamic array are of the same type then it is easier to
  59. use one of the template classes CArrayBuf<> or CSList<>. These can be
  60. used in the same way as normal arrays with the operator [].
  61. CArrayBuf<> and CSList<> both have a member function SetNum() to allocate
  62. the size. The size of CArrayBuf<> can be set only once, while the size of
  63. CSList<> can be changed at any time. CSList<> also has a member function
  64. Push() that adds records sequentially. CSList can be sorted if operators
  65. < and == are defined for the record type.
  66.  
  67. Warning:
  68. It is necessary to use CArrayBuf<> rather than CSList<> if the record type
  69. has a constructor or destructor.
  70.  
  71. Warning:
  72. It is not safe to make pointers to data inside a dynamic array of type
  73. CMemoryBuffer or CSList<> because the buffer may be re-allocated when the
  74. size grows. Such pointers will only work if we are finished with all push
  75. operations. It is safer to address data inside the buffer by their index
  76. or offset relative to the buffer.
  77.  
  78. *****************************************************************************/
  79.  
  80. #ifndef CONTAINERS_H
  81. #define CONTAINERS_H
  82.  
  83. extern CErrorReporter err;                       // Defined in error.cpp
  84.  
  85. class CFileBuffer;                               // Declared below
  86.  
  87. void operator >> (CFileBuffer & a, CFileBuffer & b); // Transfer ownership of buffer and other properties
  88.  
  89. // Class CMemoryBuffer makes a dynamic array which can grow as new data are
  90. // added. Used for storage of files, file sections, tables, etc.
  91. class CMemoryBuffer {
  92. public:
  93.    CMemoryBuffer();                              // Constructor
  94.    ~CMemoryBuffer();                             // Destructor
  95.    void SetSize(uint32 size);                    // Allocate buffer of specified size
  96.    void SetDataSize(uint32 size);                // Claim space as a data
  97.    uint32 GetDataSize()  {return DataSize;};     // File data size
  98.    uint32 GetBufferSize(){return BufferSize;};   // Buffer size
  99.    uint32 GetNumEntries(){return NumEntries;};   // Get number of entries
  100.    uint32 Push(void const * obj, uint32 size);   // Add object to buffer, return offset
  101.    uint32 PushString(char const * s);            // Add ASCIIZ string to buffer, return offset
  102.    uint32 GetLastIndex();                        // Index of last object pushed (zero-based)
  103.    void Align(uint32 a);                         // Align next entry to address divisible by a
  104.    int8 * Buf() {return buffer;};                // Access to buffer
  105.    template <class TX> TX & Get(uint32 Offset) { // Get object of arbitrary type from buffer
  106.       if (Offset >= DataSize) {err.submit(2016); Offset = 0;} // Offset out of range
  107.       return *(TX*)(buffer + Offset);}
  108. private:
  109.    CMemoryBuffer(CMemoryBuffer&);                // Make private copy constructor to prevent copying
  110.    int8 * buffer;                                // Buffer containing binary data. To be modified only by SetSize and operator >>
  111.    uint32 BufferSize;                            // Size of allocated buffer ( > DataSize)
  112. protected:
  113.    uint32 NumEntries;                            // Number of objects pushed
  114.    uint32 DataSize;                              // Size of data, offset to vacant space
  115.    friend void operator >> (CFileBuffer & a, CFileBuffer & b); // Transfer ownership of buffer and other properties
  116. };
  117.  
  118. static inline void operator << (CFileBuffer & b, CFileBuffer & a) {a >> b;} // Same as operator << above
  119.  
  120.  
  121. // Class CFileBuffer is used for storage of input and output files
  122. class CFileBuffer : public CMemoryBuffer {
  123. public:
  124.    CFileBuffer();                                // Default constructor
  125.    CFileBuffer(char const * filename);           // Constructor
  126.    void Read(int IgnoreError = 0);               // Read file into buffer
  127.    void Write();                                 // Write buffer to file
  128.    int  GetFileType();                           // Get file format type
  129.    void SetFileType(int type);                   // Set file format type
  130.    void Reset();                                 // Set all members to zero
  131.    static char const * GetFileFormatName(int FileType); // Get name of file format type
  132.    char const * FileName;                        // Name of input file
  133.    char const * OutputFileName;                  // Output file name
  134.    int WordSize;                                 // Segment word size (16, 32, 64)
  135.    int FileType;                                 // Object file type
  136.    int Executable;                               // File is executable
  137.    char * SetFileNameExtension(const char * f);  // Set file name extension according to FileType
  138. protected:
  139.    void GetOMFWordSize();                        // Determine word size for OMF file
  140.    void CheckOutputFileName();                   // Make output file name or check that requested name is valid
  141. };
  142.  
  143.  
  144. // Class CTextFileBuffer is used for building text files
  145. class CTextFileBuffer : public CFileBuffer {
  146. public:
  147.    CTextFileBuffer();                            // Constructor
  148.    void Put(const char * text);                  // Write text string to buffer
  149.    void Put(const char character);               // Write single character to buffer
  150.    void NewLine();                               // Add linefeed
  151.    void Tabulate(uint32 i);                      // Insert spaces until column i
  152.    int  LineType;                                // 0 = DOS/Windows linefeeds, 1 = UNIX linefeeds
  153.    void PutDecimal(int32 x, int IsSigned = 0);   // Write decimal number to buffer
  154.    void PutHex(uint8  x, int MasmForm = 0);      // Write hexadecimal number to buffer
  155.    void PutHex(uint16 x, int MasmForm = 0);      // Write hexadecimal number to buffer
  156.    void PutHex(uint32 x, int MasmForm = 0);      // Write hexadecimal number to buffer
  157.    void PutHex(uint64 x, int MasmForm = 0);      // Write hexadecimal number to buffer
  158.    void PutFloat(float x);                       // Write floating point number to buffer
  159.    void PutFloat(double x);                      // Write floating point number to buffer
  160.    uint32 GetColumn() {return column;}           // Get column number
  161. protected:
  162.    uint32 column;                                // Current column
  163. private:
  164.    uint32 PushString(char const * s){return 0;}; // Make PushString private to prevent using it
  165. };
  166.  
  167.  
  168. // Class CArrayBuf<RecordType> is used for dynamic arrays.
  169. // The size of the array can be set only once.
  170. // Use CArrayBuf rather than one of the other container classes if RecordType
  171. // has a constructor or destructor.
  172. template <class RecordType>
  173. class CArrayBuf {
  174. private:
  175.    RecordType * buffer;                          // Dynamically allocated memory
  176.    uint32 num;                                   // Number of entries in array
  177.    CArrayBuf (CArrayBuf &);                      // Make private copy constructor to prevent copying
  178. public:
  179.    CArrayBuf() {                                 // Default constructor
  180.       num = 0;
  181.    }
  182.    ~CArrayBuf() {                                // Destructor
  183.       if (num) delete[] buffer;                  // Deallocate memory. Will call RecordType destructor if any
  184.    }
  185.    void SetNum(uint32 n) {                       // Set size of array. May be called only once!
  186.       if (n <= num) return;                      // Already allocated
  187.       if (num) {
  188.          err.submit(9004);                       // Cannot resize because items may have destructors
  189.       }
  190.       else {
  191.          buffer = new RecordType[n];             // Allocate memory. Will call RecordType constructor if any
  192.          if (!buffer) {
  193.             err.submit(9006);                    // Memory allocation failed
  194.          }
  195.          else {
  196.             num = n;                             // Save size
  197.             memset(buffer, 0, n*sizeof(RecordType));// Initialize to zero
  198.          }
  199.       }
  200.    }
  201.    uint32 GetNumEntries() {
  202.       return num;                                // Read size
  203.    }
  204.    RecordType & operator[] (uint32 i) {          // Access array element [i]
  205.       if (i >= num) {
  206.          err.submit(9003);  i = 0;               // Error: index out of range
  207.       }
  208.       return buffer[i];
  209.    }
  210.    void SetZero() {                              // Set all items in array to 0
  211.       memset(buffer, 0, num*sizeof(RecordType)); // Warning: overwrites all members of RecordType with 0
  212.    }
  213. };
  214.  
  215.  
  216. // Class CSList<RecordType> is used for dynamic arrays where all records
  217. // have the same type RecordType. The list can be sorted if desired.
  218. //
  219. // An array defined as
  220. //       CSList<RecordType> list;
  221. // can be used in several ways:
  222. //
  223. // 1. The size can be set with list.SetNum(n) where n is the maximum number of
  224. //    entries. New entries can then be added in random order with list[i] = x;
  225. //    where i < n. Unused entries will be zero.
  226. // 2. Entries can be added sequentially with
  227. //    list.Push(x);
  228. //    The first entry will be list[0]
  229. // 3. Entries added with method 1 or 2 can be sorted in ascending order by
  230. //    calling list.Sort();
  231. // 4. The list can be kept sorted at all times if records are added with
  232. //    list.PushSort(x);
  233. //    The list will be kept sorted in ascending order, provided that it
  234. //    was sorted before the call to PushSort.
  235. // 5. The list can be kept sorted at all times and without duplicates if
  236. //    records are added with list.PushUnique(x);
  237. //    The list will be sorted and without duplicates after PushUnique if
  238. //    it was so before the call to PushUnique.
  239. // 6. Entries can be read at all times as x = list[i];
  240. //    An error will be submitted if i >= list.GetNumEntries()
  241. // 7. A sorted list can be searched for entry x by i = list.FindFirst(x);
  242. //    or i = list.Exists(x);
  243. //
  244. // Requirements:
  245. // RecordType can be a simple type, a structure or a class.
  246. // If RecordType has a constructor or destructor then they will not be
  247. // called properly. Use CArrayBuf instead of CSList if RecordType has
  248. // a constructor or destructor.
  249. // The operator < const must be defined for RecordType if any of the sorting
  250. // features are used, i.e. Sort(), PushSort(), FindFirst(), Exists().
  251. //
  252. // Example:
  253. // struct S1 {                                   // Define RecordType
  254. //    int Index;
  255. //    int operator < (S1 const & x) const {      // Define operator <
  256. //       return Index < x.Index;}
  257. // };
  258. // CSList<S1> list;                              // Make list
  259. // S1 a;  a.Index = 5;                           // Make record
  260. // list.PushUnique(a);                           // Put record into list
  261.  
  262. template <class RecordType>
  263. class CSList : private CMemoryBuffer {
  264. public:
  265.    void Push(RecordType const & x) {
  266.       // Add member to list
  267.       CMemoryBuffer::Push(&x, sizeof(x));
  268.    }
  269.    void PushZero() {
  270.       // Add blank entry to list
  271.       CMemoryBuffer::Push(0, sizeof(RecordType));
  272.    }
  273.    void SetNum(uint32 n) {
  274.       // Reserve space for n entries. Fill with zeroes
  275.       SetSize(n * sizeof(RecordType));
  276.       NumEntries = n;  DataSize = n * sizeof(RecordType);
  277.    }
  278.    uint32 GetNumEntries() {
  279.       // Get number of entries
  280.       return NumEntries;
  281.    }
  282.    RecordType & operator[] (uint32 i) {
  283.       // Get entries by operator [] as for an array
  284.       if (i >= NumEntries) {
  285.          err.submit(9003); i = 0;}               // Error: index out of range
  286.       return *(RecordType*)(Buf() + i * sizeof(RecordType));
  287.    }
  288.    void Sort() {                                
  289.       // Sort list by ascending RecordType items
  290.       // Operator < must be defined for RecordType
  291.       // Simple Bubble sort:
  292.       int32 i, j;
  293.       RecordType temp, * p1, * p2;
  294.       for (i = 0; i < (int32)NumEntries; i++) {
  295.          for (j = 0; j < (int32)NumEntries - i - 1; j++) {
  296.             p1 = (RecordType*)(Buf() + j * sizeof(RecordType));
  297.             p2 = (RecordType*)(Buf() + (j+1) * sizeof(RecordType));
  298.             if (*p2 < *p1) {                    
  299.                // Swap records
  300.                temp = *p1;  *p1 = *p2;  *p2 = temp;
  301.             }
  302.          }
  303.       }
  304.    }
  305.    int32 FindFirst(RecordType const & x) {
  306.       // Returns index to first record >= x.
  307.       // Returns 0 if x is smaller than all entries.
  308.       // Returns NumEntries if x is bigger than all entries. Note that this
  309.       // is not a valid index into the list.
  310.       // List must be sorted before calling FindFirst
  311.       uint32 a = 0;                              // Start of search interval
  312.       uint32 b = NumEntries;                     // End of search interval + 1
  313.       uint32 c = 0;                              // Middle of search interval
  314.       // Binary search loop:
  315.       while (a < b) {
  316.          c = (a + b) / 2;
  317.          if ((*this)[c] < x) {
  318.             a = c + 1;}
  319.          else {
  320.             b = c;}
  321.       }
  322.       return (int32)a;
  323.    }
  324.    int32 Exists(RecordType const & x) {
  325.       // Returns the record number if a record equal to x exists in the list.
  326.       // Returns -1 if not. The list must be sorted before calling Exists.
  327.       // Two records a and b are assumed to be equal if !(a < b || b < a)
  328.       uint32 i = FindFirst(x);
  329.       if (i == NumEntries) return -1;
  330.       if (x < (*this)[i]) return -1; else return i;
  331.    }
  332.    int32 PushSort(RecordType const & x) {
  333.       // Add member to list and keep the list sorted.
  334.       // If the list is sorted before calling PushSort then it will also be
  335.       // sorted after the call. If x is equal to an existing entry then x
  336.       // will be inserted before the existing entry.
  337.       // Operator < must be defined for RecordType.
  338.       int32 i = FindFirst(x);                    // Find where to insert x
  339.       int32 RecordsToMove = (int32)NumEntries-i; // Number of records to move
  340.       SetNum(NumEntries + 1);                    // Make space for one more record
  341.       // Move subsequent entries up one place
  342.       if (RecordsToMove > 0) {
  343.          memmove(Buf() + i * sizeof(RecordType) + sizeof(RecordType),
  344.             Buf() + i * sizeof(RecordType),
  345.             RecordsToMove * sizeof(RecordType));
  346.       }
  347.       // Insert x at position i
  348.       (*this)[i] = x;
  349.       return i;
  350.    }
  351.    int32 PushUnique(RecordType const & x) {
  352.       // Add member to list and keep the list sorted. Avoids duplicate entries.
  353.       // PushUnique will insert x in the list and keep the list sorted.
  354.       // If an entry equal to x already exists in the list then x is not
  355.       // inserted, and the return value will be the index to the existing entry.
  356.       // If no entry equal to x existed then x is inserted and the return
  357.       // value is the index to the new entry.
  358.       // This list must be sorted and without duplicates before calling
  359.       // PushUnique.
  360.       // Operator < must be defined for RecordType.
  361.       int32 i = FindFirst(x);                    // Find where to insert x
  362.       if (i < (int32)NumEntries && !(x < (*this)[i])) {
  363.          return i;                               // Duplicate found. Return index
  364.       }
  365.       int32 RecordsToMove = (int32)NumEntries-i; // Number of records to move
  366.       SetNum(NumEntries + 1);                    // Make space for one more record
  367.       // Move subsequent entries up one place
  368.       if (RecordsToMove > 0) {
  369.          memmove(Buf() + i * sizeof(RecordType) + sizeof(RecordType),
  370.             Buf() + i * sizeof(RecordType),
  371.             RecordsToMove * sizeof(RecordType));
  372.       }
  373.       // Insert x at position i
  374.       (*this)[i] = x;
  375.       // Return index
  376.       return i;
  377.    }
  378.    void Remove(uint32 index) {
  379.       // Remove record with this index
  380.       if (index >= NumEntries) return;           // Index out of range
  381.       uint32 RecordsToMove = NumEntries - index - 1; // Number of records to move
  382.       // Move subsequent entries down one place
  383.       if (RecordsToMove > 0) {
  384.          memmove(Buf() + index * sizeof(RecordType),
  385.             Buf() + index * sizeof(RecordType) + sizeof(RecordType),
  386.             RecordsToMove * sizeof(RecordType));
  387.       }
  388.       // Count down number of entries
  389.       SetNum(NumEntries - 1);
  390.    }
  391. };
  392.  
  393. #endif // #ifndef CONTAINERS_H
  394.