home *** CD-ROM | disk | FTP | other *** search
/ Developer CD Series 2000 April: Mac OS SDK / Dev.CD Apr 00 SDK1.toast / Development Kits / Mac OS / Text Encoding Converter 1.5 / Sample Code / UnicodeHub / LDynamicArray.cp < prev    next >
Encoding:
Text File  |  1998-06-15  |  14.3 KB  |  525 lines  |  [TEXT/CWIE]

  1. // ===========================================================================
  2. //    LDynamicArray.cp           ©1994-1996 Metrowerks Inc. All rights reserved.
  3. // ===========================================================================
  4. //    
  5. //    Description:
  6. //        An ordered collection of fixed-size items. Positions in the Array are
  7. //        one-based--the first item is at index 1.
  8. //
  9. //    Note about index values:
  10. //        Index values are signed, 32-bit integers. Index 0 is not an
  11. //        item in the Array and is used to indicate an nonexistant item. 
  12. //
  13. //    Note about items:
  14. //        When specifying an item, you pass a pointer to the item data
  15. //        as a parameter. The Array stores a copy of the item data.
  16.  
  17. #ifdef PowerPlant_PCH
  18. #include PowerPlant_PCH
  19. #endif
  20.  
  21. #include "LDynamicArray.h"
  22. #include <UMemoryMgr.h>
  23.  
  24. #pragma mark === Constructors / Destructor ===
  25.  
  26. // ---------------------------------------------------------------------------
  27. //        • LDynamicArray
  28. // ---------------------------------------------------------------------------
  29. //    Construct an empty Array of items of the specified size, pre-allocating
  30. //    space for a certain number of items
  31. //
  32. //    Errors:
  33. //        Construction can fail if there is not enough memory to create
  34. //        the Handle for storing the items
  35.  
  36. LDynamicArray::LDynamicArray(
  37.     Uint32    inItemSize,
  38.     Uint32    inSpaces)
  39. {
  40.     mItemSize = inItemSize;
  41.     mItemCount = 0;
  42.     mAllocation = inSpaces;
  43.     mLockCount = 0;
  44.     
  45.     mItemsH = nil;
  46.     if (inSpaces > 0) {
  47.         mItemsH = ::NewHandle(inSpaces * inItemSize);
  48.         ThrowIfMemFail_(mItemsH);
  49.     }
  50. }
  51.  
  52.  
  53. // ---------------------------------------------------------------------------
  54. //        • LDynamicArray
  55. // ---------------------------------------------------------------------------
  56. //    Construct an Array of items using an existing Handle.
  57. //
  58. //    Caller is responsible for ensuring that inItemsHandle is a valid
  59. //    Handle of Pointer values
  60. //
  61. //    The Array assumes ownership of inItemsHandle, meaning that the Array
  62. //    is responsible for disposing of it.
  63.  
  64. LDynamicArray::LDynamicArray(
  65.     Uint32    inItemSize,
  66.     Handle    inItemsHandle)
  67. {
  68.     mItemSize = inItemSize;
  69.     mItemsH = inItemsHandle;
  70.     mLockCount = 0;
  71.     mItemCount = ::GetHandleSize(inItemsHandle) / inItemSize;
  72.     ::HUnlock(mItemsH);                    // Maintain proper lock count
  73.     mAllocation = mItemCount;
  74. }
  75.  
  76.  
  77. // ---------------------------------------------------------------------------
  78. //        • ~LDynamicArray
  79. // ---------------------------------------------------------------------------
  80. //    Destructor
  81.  
  82. LDynamicArray::~LDynamicArray()
  83. {
  84.     if (mItemsH != nil) {
  85.         ::DisposeHandle(mItemsH);
  86.     }
  87. }
  88.  
  89.  
  90. // ---------------------------------------------------------------------------
  91. //        • ValidIndex
  92. // ---------------------------------------------------------------------------
  93. //    Return whether an index is valid (between 1 and the number of items)
  94. //    for the VariableArray.
  95. //
  96. //    If inIndex is the special flag index_Last, the index's value
  97. //    is changed to the actual index of the last item.
  98.  
  99. Boolean
  100. LDynamicArray::ValidIndex(
  101.     ArrayIndexT        &inIndex) const
  102. {
  103.     if (inIndex == index_Last) {
  104.         inIndex = mItemCount;
  105.     }
  106.     return (inIndex > 0) && (inIndex <= mItemCount);
  107. }
  108.  
  109.  
  110. // ---------------------------------------------------------------------------
  111. //        • InsertItemsAt
  112. // ---------------------------------------------------------------------------
  113. //    Insert items at the specified position in an Array
  114. //
  115. //    inCount items are inserted into the Array starting at inAtIndex.
  116. //    All items are set to the same value, as specified by inItem.
  117. //
  118. //    If inAtIndex is greater than the number of items in the Array,
  119. //    then the items are inserted after the last item.
  120. //
  121. //    Errors:
  122. //        Insertion can fail if there is not enough memory to store
  123. //        the new items 
  124.  
  125. void
  126. LDynamicArray::InsertItemsAt(
  127.     Uint32            inCount,
  128.     ArrayIndexT        inAtIndex,
  129.     const void        *inItem)
  130. {
  131.     if (inCount < 1) {                // Exit if nothing to insert
  132.         return;
  133.     }
  134.     
  135.     if (mLockCount > 0) {
  136.         SignalPStr_("\pCan't insert into a locked Array");
  137.         return;
  138.     }
  139.                                     // Grow storage
  140.     Uint32    newCount = mItemCount + inCount;
  141.     if (newCount > mAllocation) {
  142.         AllocateSpace(newCount - mAllocation + 1);
  143.     }
  144.         
  145.     Uint32    saveCount = mItemCount;
  146.     mItemCount = newCount;
  147.     
  148.     if (inAtIndex > saveCount) {    // Check upper bound
  149.         inAtIndex = saveCount + 1;    // Insert at end of Array
  150.         
  151.     } else {                        // Insert at start or middle of Array
  152.         if (inAtIndex < 1) {        // Check lower bound
  153.             inAtIndex = 1;            // Insert at start of Array
  154.         }
  155.         
  156.         if (saveCount > 0) {         // Move existing items to make
  157.                                     //   room for new ones
  158.             ShiftItems(inAtIndex, saveCount, inAtIndex + inCount);
  159.         }
  160.     }
  161.                                     // Inserted items are all set
  162.                                     //   to the same value, if specified
  163.     if (inItem != nil) {
  164.         ArrayIndexT    index = inAtIndex;
  165.         do {
  166.             PokeItem(index, inItem);
  167.         } while (++index < inAtIndex + inCount);
  168.     }
  169. }
  170.  
  171.  
  172. // ---------------------------------------------------------------------------
  173. //        • RemoveItemsAt
  174. // ---------------------------------------------------------------------------
  175. //    Remove items from an Array starting at a specified position
  176. //
  177. //    Does nothing if inAtIndex is out of range. Checks if inCount would remove
  178. //    items past the end of the Array, and adjusts it accordingly to remove
  179. //    the items from inAtIndex to the end of the Array. That means you can pass
  180. //    a large number to remove the items from inAtIndex to the end of the Array.
  181.  
  182. void
  183. LDynamicArray::RemoveItemsAt(
  184.     Uint32        inCount,
  185.     ArrayIndexT    inAtIndex)
  186. {
  187.     if (inCount < 1) {                // Nothing to do
  188.         return;
  189.     }
  190.  
  191.     if (mLockCount > 0) {
  192.         SignalPStr_("\pCan't remove from a locked Array");
  193.         return;
  194.     }
  195.  
  196.     if (ValidIndex(inAtIndex)) {
  197.     
  198.         if (inAtIndex + inCount <= mItemCount) {
  199.                                     // Removing items from the middle
  200.                                     // Shift down items that are above
  201.                                     //   the ones being removed
  202.             ShiftItems(inAtIndex + inCount, mItemCount, inAtIndex);
  203.             
  204.         } else {                    // Removing items at the end
  205.                                     // Limit inCount to the number of items
  206.                                     //   from inWhere to the end
  207.             inCount = mItemCount - inAtIndex + 1;
  208.         }
  209.         
  210.         mItemCount -= inCount;        // Reduce storage
  211.         mAllocation = mItemCount;
  212.         ::SetHandleSize(mItemsH, mItemCount * mItemSize);
  213.     }
  214. }
  215.  
  216.  
  217. // ---------------------------------------------------------------------------
  218. //        • FetchItemAt
  219. // ---------------------------------------------------------------------------
  220. //    Pass back the Item at the specified index
  221. //
  222. //    Returns true if an item exists at inIndex (and sets outItem)
  223. //    Returns false if inIndex is out of range (and leaves outItem unchanged)
  224.  
  225. Boolean
  226. LDynamicArray::FetchItemAt(
  227.     ArrayIndexT    inAtIndex,
  228.     void        *outItem) const        // Pointer to the item
  229. {
  230.     Boolean    itemExists = false;
  231.     
  232.     if (ValidIndex(inAtIndex)) {
  233.         PeekItem(inAtIndex, outItem);
  234.         itemExists = true;
  235.     }
  236.     
  237.     return itemExists;
  238. }
  239.  
  240.  
  241. // ---------------------------------------------------------------------------
  242. //        • AssignItemsAt
  243. // ---------------------------------------------------------------------------
  244. //    Assign the same value to Items starting at the specified index
  245. //
  246. //    inValue is a pointer to the item data. The Array makes and stores
  247. //    a copy of the item data.
  248. //
  249. //    Does nothing if inIndex is out of range
  250.  
  251. void
  252. LDynamicArray::AssignItemsAt(
  253.     Uint32            inCount,
  254.     ArrayIndexT        inAtIndex,
  255.     const void        *inValue)
  256. {
  257.     if (ValidIndex(inAtIndex)) {
  258.                                     
  259.         ArrayIndexT        lastIndex = inAtIndex + inCount - 1;
  260.         if (lastIndex > mItemCount) {
  261.             lastIndex = mItemCount;    // Don't go past end of Array
  262.         }
  263.         
  264.         for (ArrayIndexT i = inAtIndex; i <= lastIndex; i++) {
  265.             PokeItem(i, inValue);
  266.         }
  267.     }
  268. }
  269.  
  270.  
  271. // ---------------------------------------------------------------------------
  272. //        • SetItemAt
  273. // ---------------------------------------------------------------------------
  274. //    Sets the value of the Item at the specified index
  275. //
  276. //    inItem is a pointer to the item data. The Array makes and stores
  277. //    a copy of the item data.
  278. //
  279. //    Does nothing if inIndex is out of range
  280.  
  281. void
  282. LDynamicArray::SetItemAt(
  283.     ArrayIndexT        inAtIndex,
  284.     const void        *inItem)            // Pointer to the item
  285. {
  286.     if (ValidIndex(inAtIndex)) {
  287.         PokeItem(inAtIndex, inItem);
  288.     }
  289. }
  290.  
  291.  
  292. // ---------------------------------------------------------------------------
  293. //        • SwapItems
  294. // ---------------------------------------------------------------------------
  295. //    Swap the values of the Items at the specified indices
  296. //
  297. //    Does nothing if either index is out of range
  298.  
  299. void
  300. LDynamicArray::SwapItems(
  301.     ArrayIndexT    inIndexA,
  302.     ArrayIndexT    inIndexB)
  303. {
  304.                                     // Do nothing if either index is out
  305.                                     //   of range
  306.     if (ValidIndex(inIndexA) && ValidIndex(inIndexB)) {                                
  307.          
  308.                                      // Store copy of item A
  309.         StPointerBlock    itemACopy(mItemSize);
  310.         PeekItem(inIndexA, itemACopy.mPtr);
  311.         
  312.         PokeItem(inIndexA, GetItemPtr(inIndexB));    // A = B
  313.         PokeItem(inIndexB, itemACopy.mPtr);            // B = Copy of A
  314.     }
  315. }
  316.  
  317.  
  318. // ---------------------------------------------------------------------------
  319. //        • MoveItem
  320. // ---------------------------------------------------------------------------
  321. //    Move an item from one position to another in an Array. The net result
  322. //    is the same as removing the item and inserting at a new position.
  323. //
  324. //    Does nothing if either index is out of range
  325. //
  326. //    Example:
  327. //                BEFORE                        AFTER MoveItem(2, 6)
  328. //        index    1  2  3  4  5  6            1  2  3  4  5  6
  329. //        item    a  b  c  d  e  f            a  c  d  e  b  f
  330.  
  331. void
  332. LDynamicArray::MoveItem(
  333.     ArrayIndexT    inFromIndex,
  334.     ArrayIndexT    inToIndex)
  335. {
  336.                                     // Do nothing if either index is out
  337.                                     //   of range or if "from" and "to"
  338.                                     //   indices are the same
  339.     if ( ValidIndex(inFromIndex) && ValidIndex(inToIndex) &&                                
  340.          (inFromIndex != inToIndex) ) {
  341.         
  342.                                     // Store copy of item to move
  343.         StPointerBlock    itemToMove(mItemSize);
  344.         PeekItem(inFromIndex, itemToMove.mPtr);
  345.         
  346.         if (inFromIndex < inToIndex) {
  347.                                     // Move item to a higher index
  348.                                     // Shift items between "from" and "to"
  349.                                     //   down one spot
  350.             ShiftItems(inFromIndex + 1, inToIndex, inFromIndex);
  351.         } else {
  352.                                     // Move item to a lower index
  353.                                     // Shift items between "to" and "from"
  354.                                     //   up one spot
  355.             ShiftItems(inToIndex, inFromIndex - 1, inToIndex + 1);
  356.         }
  357.         
  358.                                     // Store item at new position
  359.         PokeItem(inToIndex, itemToMove.mPtr);
  360.     }
  361. }
  362.  
  363.  
  364. // ---------------------------------------------------------------------------
  365. //        • FetchIndexOf
  366. // ---------------------------------------------------------------------------
  367. //    Returns the index of the specified item within the Array
  368. //
  369. //    Returns index_Bad if the item is not in the Array
  370.  
  371. ArrayIndexT
  372. LDynamicArray::FetchIndexOf(
  373.     const void    *inItem) const        // Pointer to the item to find
  374. {
  375.     if (mItemCount == 0) {            // Array is empty
  376.         return index_Bad;
  377.     }
  378.  
  379.     ArrayIndexT    findIndex = 0;        // Search from beginning of Array
  380.  
  381.     if (mItemSize == sizeof(void*)) {
  382.                                     // Special case for Array of Pointers
  383.         void*    findMe = *((void**) inItem);
  384.         void*    *itemPtr = (void**) *mItemsH;
  385.         while (++findIndex <= mItemCount) {
  386.             if (findMe == *itemPtr++) {
  387.                 break;
  388.             }
  389.         }
  390.     
  391.     } else {                        // Items of any size
  392.         StHandleLocker    lock(mItemsH);
  393.         char    *itemPtr = *mItemsH;
  394.         while (++findIndex <= mItemCount) {
  395.             if (BlocksAreEqual(inItem, itemPtr, mItemSize)) {
  396.                 break;
  397.             }
  398.             itemPtr += mItemSize;
  399.         }
  400.     }
  401.     
  402.     if (findIndex > mItemCount) {    // Search stopped because we reached the 
  403.         findIndex = index_Bad;        //   end without finding the item
  404.     }
  405.  
  406.     return findIndex;
  407. }
  408.  
  409.  
  410. // ---------------------------------------------------------------------------
  411. //        • Remove
  412. // ---------------------------------------------------------------------------
  413. //    Remove an item from an Array
  414.  
  415. void
  416. LDynamicArray::Remove(
  417.     const void    *inItem)                // Pointer to the item to remove
  418. {
  419.     ArrayIndexT    index = FetchIndexOf(inItem);
  420.     if (index != index_Bad) {
  421.         RemoveItemsAt(1, index);
  422.     }
  423. }
  424.  
  425.  
  426. // ---------------------------------------------------------------------------
  427. //        • Lock
  428. // ---------------------------------------------------------------------------
  429. //    Lock the Handle that stores the data for the items in the Array
  430. //
  431. //    Class maintains a lock count, so each call to Lock() should be
  432. //    balanced by a corresponding call to Unlock()
  433.  
  434. void
  435. LDynamicArray::Lock()
  436. {
  437.     mLockCount++;
  438.     if ((mLockCount == 1) && (mItemsH != nil)) {
  439.                                         // First lock,
  440.         ::HLock(mItemsH);                //   so really lock the Handle
  441.     }
  442. }
  443.  
  444.  
  445. // ---------------------------------------------------------------------------
  446. //        • Unlock
  447. // ---------------------------------------------------------------------------
  448. //    Unlock the Handle that stores the data for the items in the Array
  449. //
  450. //    Class maintains a lock count, so each call to Lock() should be
  451. //    balanced by a corresponding call to Unlock()
  452.  
  453. void
  454. LDynamicArray::Unlock()
  455. {
  456.     SignalIf_(mLockCount == 0);            // Too many Unlocks
  457.     
  458.     if ((--mLockCount == 0) && (mItemsH != nil)) {
  459.                                         // Last unlock
  460.         ::HUnlock(mItemsH);                //   so really unlock the Handle
  461.     }
  462. }
  463.  
  464.  
  465. // ---------------------------------------------------------------------------
  466. //        • GetItemPtr
  467. // ---------------------------------------------------------------------------
  468. //    Returns a pointer to the start of an Items data within the internal
  469. //    storage Handle.
  470. //
  471. //    WARNING: The return pointer references information inside a
  472. //    relocatable block. This pointer will become invalid if the
  473. //    Handle block moves.
  474.  
  475. void*
  476. LDynamicArray::GetItemPtr(
  477.     Int32    inAtIndex) const
  478. {
  479.     return (*mItemsH + (inAtIndex - 1) * mItemSize);
  480. }
  481.  
  482.  
  483. // ---------------------------------------------------------------------------
  484. //        • AllocateSpace
  485. // ---------------------------------------------------------------------------
  486. //    Allocate space for more items
  487.  
  488. void
  489. LDynamicArray::AllocateSpace(
  490.     Uint32    inSpaces)
  491. {
  492.     Uint32    newSize = (mAllocation + inSpaces) * mItemSize;
  493.     
  494.     if (mItemsH == nil) {
  495.         mItemsH = ::NewHandle(newSize);
  496.     } else {
  497.         ::SetHandleSize(mItemsH, newSize);
  498.     }
  499.     ThrowIfMemError_();
  500.     
  501.     mAllocation += inSpaces;
  502. }
  503.  
  504.  
  505. // ---------------------------------------------------------------------------
  506. //        • ShiftItems
  507. // ---------------------------------------------------------------------------
  508. //    Moves items within the Handle used for internal storage
  509. //        Moves items in the range inStartPos to inEndPos (inclusive) so
  510. //        that those items start at index inDestPos, overwriting any items
  511. //        that were there.
  512. //
  513. //    For internal use. Performs no bounds checking.
  514.  
  515. void
  516. LDynamicArray::ShiftItems(
  517.     ArrayIndexT    inStartPos,
  518.     ArrayIndexT    inEndPos,
  519.     ArrayIndexT    inDestPos)
  520. {
  521.     ::BlockMoveData(*mItemsH + (inStartPos - 1) * mItemSize,
  522.                     *mItemsH + (inDestPos - 1) * mItemSize,
  523.                     (inEndPos - inStartPos + 1) * mItemSize);
  524. }
  525.