LCOV - code coverage report
Current view: top level - Core - OOShipGroup.m (source / functions) Hit Total Coverage
Test: coverxygen.info Lines: 0 23 0.0 %
Date: 2025-05-28 07:50:54 Functions: 0 0 -

          Line data    Source code
       1           0 : /*
       2             : OOShipGroup.m
       3             : 
       4             : IMPLEMENTATION NOTE:
       5             : This is implemented as a dynamic array rather than a hash table for the
       6             : following reasons:
       7             :  *      Ship groups are generally quite small, not motivating a more complex
       8             :         implementation.
       9             :  *      The code ship groups replace was all array-based and not a significant
      10             :         bottleneck.
      11             :  *      Ship groups are compacted (i.e., dead weak references removed) as a side
      12             :         effect of iteration.
      13             :  *      Many uses of ship groups involve iterating over the whole group anyway.
      14             : 
      15             : 
      16             : Oolite
      17             : Copyright (C) 2004-2013 Giles C Williams and contributors
      18             : 
      19             : This program is free software; you can redistribute it and/or
      20             : modify it under the terms of the GNU General Public License
      21             : as published by the Free Software Foundation; either version 2
      22             : of the License, or (at your option) any later version.
      23             : 
      24             : This program is distributed in the hope that it will be useful,
      25             : but WITHOUT ANY WARRANTY; without even the implied warranty of
      26             : MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
      27             : GNU General Public License for more details.
      28             : 
      29             : You should have received a copy of the GNU General Public License
      30             : along with this program; if not, write to the Free Software
      31             : Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
      32             : MA 02110-1301, USA.
      33             : 
      34             : */
      35             : 
      36             : #import "ShipEntity.h"
      37             : #import "OOShipGroup.h"
      38             : #import "OOMaths.h"
      39             : 
      40             : 
      41           0 : enum
      42             : {
      43             :         kMinSize                                = 4,
      44             :         kMaxFreeSpace                   = 128
      45             : };
      46             : 
      47             : 
      48           0 : @interface OOShipGroupEnumerator: NSEnumerator
      49             : {
      50             :         // ivars are public so ShipGroupIterate() can peek at both these and OOShipGroup's. Naughty!
      51             : @public
      52           0 :         OOShipGroup                             *_group;
      53           0 :         NSUInteger                              _index, _updateCount;
      54           0 :         BOOL                                    _considerCleanup, _cleanupNeeded;
      55             : }
      56             : 
      57           0 : - (id) initWithShipGroup:(OOShipGroup *)group;
      58             : 
      59           0 : - (NSUInteger) index;
      60           0 : - (void) setPerformCleanup:(BOOL)flag;
      61             : 
      62             : @end
      63             : 
      64             : 
      65             : @interface OOShipGroup (Private)
      66             : 
      67           0 : - (BOOL) resizeTo:(NSUInteger)newCapacity;
      68           0 : - (void) cleanUp;
      69             : 
      70           0 : - (NSUInteger) updateCount;
      71             : 
      72             : @end
      73             : 
      74             : 
      75           0 : static id ShipGroupIterate(OOShipGroupEnumerator *enumerator);
      76             : 
      77             : 
      78             : @implementation OOShipGroup
      79             : 
      80             : - (id) init
      81             : {
      82             :         return [self initWithName:nil];
      83             : }
      84             : 
      85             : 
      86             : - (id) initWithName:(NSString *)name
      87             : {
      88             :         if ((self = [super init]))
      89             :         {
      90             :                 _capacity = kMinSize;
      91             :                 _members = malloc(sizeof *_members * _capacity);
      92             :                 if (_members == NULL)
      93             :                 {
      94             :                         [self release];
      95             :                         return nil;
      96             :                 }
      97             :                 
      98             :                 [self setName:name];
      99             :         }
     100             :         
     101             :         return self;
     102             : }
     103             : 
     104             : 
     105             : + (instancetype) groupWithName:(NSString *)name
     106             : {
     107             :         return [[[self alloc] initWithName:name] autorelease];
     108             : }
     109             : 
     110             : 
     111             : + (instancetype) groupWithName:(NSString *)name leader:(ShipEntity *)leader
     112             : {
     113             :         OOShipGroup *result = [self groupWithName:name];
     114             :         [result setLeader:leader];
     115             :         return result;
     116             : }
     117             : 
     118             : 
     119           0 : - (void) dealloc
     120             : {
     121             :         NSUInteger i;
     122             :         
     123             :         for (i = 0; i < _count; i++)
     124             :         {
     125             :                 [_members[i] release];
     126             :         }
     127             :         free(_members);
     128             :         [_name release];
     129             :         
     130             :         [super dealloc];
     131             : }
     132             : 
     133             : 
     134           0 : - (NSString *) descriptionComponents
     135             : {
     136             :         NSString *desc = [NSString stringWithFormat:@"%llu ships", (unsigned long long)_count];
     137             :         if ([self name] != nil)
     138             :         {
     139             :                 desc = [NSString stringWithFormat:@"\"%@\", %@", [self name], desc];
     140             :         }
     141             :         if ([self leader] != nil)
     142             :         {
     143             :                 desc = [NSString stringWithFormat:@"%@, leader: %@", desc, [[self leader] shortDescription]];
     144             :         }
     145             :         return desc;
     146             : }
     147             : 
     148             : 
     149             : - (NSString *) name
     150             : {
     151             :         return _name;
     152             : }
     153             : 
     154             : 
     155             : - (void) setName:(NSString *)name
     156             : {
     157             :         _updateCount++;
     158             :         
     159             :         if (_name != name)
     160             :         {
     161             :                 [_name release];
     162             :                 _name = [name retain];
     163             :         }
     164             : }
     165             : 
     166             : 
     167             : - (ShipEntity *) leader
     168             : {
     169             :         ShipEntity *result = [_leader weakRefUnderlyingObject];
     170             :         
     171             :         // If reference is stale, delete weakref object.
     172             :         if (result == nil && _leader != nil)
     173             :         {
     174             :                 [_leader release];
     175             :                 _leader = nil;
     176             :         }
     177             :         
     178             :         return result;
     179             : }
     180             : 
     181             : 
     182             : - (void) setLeader:(ShipEntity *)leader
     183             : {
     184             :         _updateCount++;
     185             :         
     186             :         if (leader != [self leader])
     187             :         {
     188             :                 [_leader release];
     189             :                 [self addShip:leader];
     190             :                 _leader = [leader weakRetain];
     191             :         }
     192             : }
     193             : 
     194             : 
     195             : - (NSEnumerator *) objectEnumerator
     196             : {
     197             :         return [[[OOShipGroupEnumerator alloc] initWithShipGroup:self] autorelease];
     198             : }
     199             : 
     200             : 
     201             : - (NSEnumerator *) mutationSafeEnumerator
     202             : {
     203             :         return [[self memberArray] objectEnumerator];
     204             : }
     205             : 
     206             : 
     207             : - (NSSet *) members
     208             : {
     209             :         return [NSSet setWithArray:[self memberArray]];
     210             : }
     211             : 
     212             : 
     213             : - (NSSet *) membersExcludingLeader
     214             : {
     215             :         return [NSSet setWithArray:[self memberArrayExcludingLeader]];
     216             : }
     217             : 
     218             : 
     219             : #if OOLITE_FAST_ENUMERATION
     220             : - (NSArray *) memberArray
     221             : {
     222             :         id                                              *objects = NULL;
     223             :         NSUInteger                              count = 0;
     224             :         NSArray                                 *result = nil;
     225             :         
     226             :         if (_count == 0)  return [NSArray array];
     227             :         
     228             :         objects = malloc(sizeof *objects * _count);
     229             :         for (id ship in self)
     230             :         {
     231             :                 objects[count++] = ship;
     232             :         }
     233             :         
     234             :         result = [NSArray arrayWithObjects:objects count:count];
     235             :         free(objects);
     236             :         
     237             :         return result;
     238             : }
     239             : 
     240             : 
     241             : - (NSArray *) memberArrayExcludingLeader
     242             : {
     243             :         id                                              *objects = NULL;
     244             :         NSUInteger                              count = 0;
     245             :         NSArray                                 *result = nil;
     246             :         ShipEntity                              *leader = nil;
     247             :         
     248             :         if (_count == 0)  return [NSArray array];
     249             :         leader = self.leader;
     250             :         
     251             :         objects = malloc(sizeof *objects * _count);
     252             :         for (id ship in self)
     253             :         {
     254             :                 if (ship != leader)
     255             :                 {
     256             :                         objects[count++] = ship;
     257             :                 }
     258             :         }
     259             :         
     260             :         result = [NSArray arrayWithObjects:objects count:count];
     261             :         free(objects);
     262             :         
     263             :         return result;
     264             : }
     265             : 
     266             : 
     267             : - (BOOL) containsShip:(ShipEntity *)ship
     268             : {
     269             :         ShipEntity                              *containedShip = nil;
     270             :         
     271             :         for (containedShip in self)
     272             :         {
     273             :                 if ([ship isEqual:containedShip])
     274             :                 {
     275             :                         return YES;
     276             :                 }
     277             :         }
     278             :         
     279             :         return NO;
     280             : }
     281             : #else
     282             : - (NSArray *) memberArray
     283             : {
     284             :         return [[self objectEnumerator] allObjects];
     285             : }
     286             : 
     287             : 
     288             : - (NSArray *) memberArrayExcludingLeader
     289             : {
     290             :         id                                              *objects = NULL;
     291             :         NSUInteger                              count = 0;
     292             :         NSArray                                 *result = nil;
     293             :         NSEnumerator                    *shipEnum = nil;
     294             :         ShipEntity                              *ship = nil;
     295             :         ShipEntity                              *leader = nil;
     296             :         
     297             :         if (_count == 0)  return [NSArray array];
     298             :         leader = [self leader];
     299             :         if (leader == nil)  return [self memberArray];
     300             :         
     301             :         objects = malloc(sizeof *objects * _count);
     302             :         for (shipEnum = [self objectEnumerator]; (ship = [shipEnum nextObject]); )
     303             :         {
     304             :                 if (ship != leader)
     305             :                 {
     306             :                         objects[count++] = ship;
     307             :                 }
     308             :         }
     309             :         
     310             :         result = [NSArray arrayWithObjects:objects count:count];
     311             :         free(objects);
     312             :         
     313             :         return result;
     314             : }
     315             : 
     316             : 
     317             : - (BOOL) containsShip:(ShipEntity *)ship
     318             : {
     319             :         OOShipGroupEnumerator   *shipEnum = nil;
     320             :         ShipEntity                              *containedShip = nil;
     321             :         BOOL                                    result = NO;
     322             :         
     323             :         shipEnum = (OOShipGroupEnumerator *)[self objectEnumerator];
     324             :         [shipEnum setPerformCleanup:NO];
     325             :         while ((containedShip = [shipEnum nextObject]))
     326             :         {
     327             :                 if ([ship isEqual:containedShip])
     328             :                 {
     329             :                         result = YES;
     330             :                         break;
     331             :                 }
     332             :         }
     333             :         
     334             :         // Clean up
     335             :         [self cleanUp];
     336             :         
     337             :         return result;
     338             : }
     339             : #endif
     340             : 
     341             : 
     342             : - (BOOL) addShip:(ShipEntity *)ship
     343             : {
     344             :         _updateCount++;
     345             :         
     346             :         if ([self containsShip:ship])  return YES;      // it's in the group already, result!
     347             :         
     348             :         // Ensure there's space.
     349             :         if (_count == _capacity)
     350             :         {
     351             :                 if (![self resizeTo:(_capacity > kMaxFreeSpace) ? (_capacity + kMaxFreeSpace) : (_capacity * 2)])
     352             :                 {
     353             :                         if (![self resizeTo:_capacity + 1])
     354             :                         {
     355             :                                 // Out of memory?
     356             :                                 return NO;
     357             :                         }
     358             :                 }
     359             :         }
     360             :         
     361             :         _members[_count++] = [ship weakRetain];
     362             :         return YES;
     363             : }
     364             : 
     365             : 
     366             : - (BOOL) removeShip:(ShipEntity *)ship
     367             : {
     368             :         OOShipGroupEnumerator   *shipEnum = nil;
     369             :         ShipEntity                              *containedShip = nil;
     370             :         NSUInteger                              index;
     371             :         BOOL                                    foundIt = NO;
     372             :         
     373             :         _updateCount++;
     374             :         
     375             :         if (ship == [self leader])  [self setLeader:nil];
     376             :         
     377             :         shipEnum = (OOShipGroupEnumerator *)[self objectEnumerator];
     378             :         [shipEnum setPerformCleanup:NO];
     379             :         while ((containedShip = [shipEnum nextObject]))
     380             :         {
     381             :                 if ([ship isEqual:containedShip])
     382             :                 {
     383             :                         index = [shipEnum index] - 1;
     384             :                         _members[index] = _members[--_count];
     385             :                         foundIt = YES;
     386             :                         
     387             :                         // Clean up
     388             :                         [ship setGroup:nil];
     389             :                         [ship setOwner:ship];
     390             :                         [self cleanUp];
     391             :                         break;
     392             :                 }
     393             :         }
     394             :         return foundIt;
     395             : }
     396             : 
     397             : /* TODO post-1.78: profiling indicates this is a noticeable
     398             :  * contributor to ShipEntity::update time. Consider optimisation: may
     399             :  * be possible to return _count if invalidation of weakref and group
     400             :  * removal in ShipEntity::dealloc keeps the data consistent anyway -
     401             :  * CIM */
     402             : 
     403             : - (NSUInteger) count
     404             : {
     405             :         NSEnumerator            *memberEnum = nil;
     406             :         NSUInteger                      result = 0;
     407             :         
     408             :         if (_count != 0)
     409             :         {
     410             :                 memberEnum = [self objectEnumerator];
     411             :                 while ([memberEnum nextObject] != nil)  result++;
     412             :         }
     413             :         
     414             :         assert(result == _count);
     415             :         
     416             :         return result;
     417             : }
     418             : 
     419             : 
     420             : - (BOOL) isEmpty
     421             : {
     422             :         if (_count == 0)  return YES;
     423             :         
     424             :         return [[self objectEnumerator] nextObject] == nil;
     425             : }
     426             : 
     427             : 
     428           0 : - (BOOL) resizeTo:(NSUInteger)newCapacity
     429             : {
     430             :         OOWeakReference                 **temp = NULL;
     431             :         
     432             :         if (newCapacity < _count)  return NO;
     433             :         
     434             :         temp = realloc(_members, newCapacity * sizeof *_members);
     435             :         if (temp == NULL)  return NO;
     436             :         
     437             :         _members = temp;
     438             :         _capacity = newCapacity;
     439             :         return YES;
     440             : }
     441             : 
     442             : 
     443           0 : - (void) cleanUp
     444             : {
     445             :         NSUInteger                              newCapacity = _capacity;
     446             :         
     447             :         if (_count >= kMaxFreeSpace)
     448             :         {
     449             :                 if (_capacity > _count + kMaxFreeSpace)
     450             :                 {
     451             :                         newCapacity = _count + 1;       // +1 keeps us at powers of two + multiples of kMaxFreespace.
     452             :                 }
     453             :         }
     454             :         else
     455             :         {
     456             :                 if (_capacity > _count * 2)
     457             :                 {
     458             :                         newCapacity = OORoundUpToPowerOf2_NS(_count);
     459             :                         if (newCapacity < kMinSize) newCapacity = kMinSize;
     460             :                 }
     461             :         }
     462             :         
     463             :         if (newCapacity != _capacity)  [self resizeTo:newCapacity];
     464             : }
     465             : 
     466             : 
     467           0 : - (NSUInteger) updateCount
     468             : {
     469             :         return _updateCount;
     470             : }
     471             : 
     472             : 
     473           0 : static id ShipGroupIterate(OOShipGroupEnumerator *enumerator)
     474             : {
     475             :         // The work is done here so that we can have access to both OOShipGroup's and OOShipGroupEnumerator's ivars.
     476             :         
     477             :         OOShipGroup                             *group = enumerator->_group;
     478             :         ShipEntity                              *result = nil;
     479             :         BOOL                                    cleanupNeeded = NO;
     480             :         
     481             :         if (enumerator->_updateCount != group->_updateCount)
     482             :         {
     483             :                 [NSException raise:NSGenericException format:@"Collection <OOShipGroup: %p> was mutated while being enumerated.", group];
     484             :         }
     485             :         
     486             :         while (enumerator->_index < group->_count)
     487             :         {
     488             :                 result = [group->_members[enumerator->_index] weakRefUnderlyingObject];
     489             :                 if (result != nil)
     490             :                 {
     491             :                         enumerator->_index++;
     492             :                         break;
     493             :                 }
     494             :                 
     495             :                 // If we got here, the group contains a stale reference to a dead ship.
     496             :                 group->_members[enumerator->_index] = group->_members[--group->_count];
     497             :                 cleanupNeeded = YES;
     498             :         }
     499             :         
     500             :         // Clean-up handling. Only perform actual clean-up at end of iteration.
     501             :         if (enumerator->_considerCleanup)
     502             :         {
     503             :                 enumerator->_cleanupNeeded = enumerator->_cleanupNeeded && cleanupNeeded;
     504             :                 if (enumerator->_cleanupNeeded && result == nil)
     505             :                 {
     506             :                         [group cleanUp];
     507             :                 }
     508             :         }
     509             :         
     510             :         return result;
     511             : }
     512             : 
     513             : 
     514             : #if OOLITE_FAST_ENUMERATION
     515           0 : - (NSUInteger)countByEnumeratingWithState:(NSFastEnumerationState *)state objects:(id *)stackbuf count:(NSUInteger)len
     516             : {
     517             :         NSUInteger                              srcIndex, dstIndex = 0;
     518             :         ShipEntity                              *item = nil;
     519             :         BOOL                                    cleanupNeeded = NO;
     520             :         
     521             :         srcIndex = state->state;
     522             :         while (srcIndex < _count && dstIndex < len)
     523             :         {
     524             :                 item = [_members[srcIndex] weakRefUnderlyingObject];
     525             :                 if (item != nil)
     526             :                 {
     527             :                         stackbuf[dstIndex++] = item;
     528             :                         srcIndex++;
     529             :                 }
     530             :                 else
     531             :                 {
     532             :                         _members[srcIndex] = _members[--_count];
     533             :                         cleanupNeeded = YES;
     534             :                 }
     535             :         }
     536             :         
     537             :         if (cleanupNeeded)  [self cleanUp];
     538             :         
     539             :         state->state = srcIndex;
     540             :         state->itemsPtr = stackbuf;
     541             :         state->mutationsPtr = &_updateCount;
     542             :         
     543             :         return dstIndex;
     544             : }
     545             : #endif
     546             : 
     547             : 
     548             : /*      This method exists purely to suppress Clang static analyzer warnings that
     549             :         this ivar is unused (but may be used by categories, which they are).
     550             :         FIXME: there must be a feature macro we can use to avoid actually building
     551             :         this into the app, but I can't find it in docs.
     552             : */
     553           0 : - (BOOL) suppressClangStuff
     554             : {
     555             :         return !_jsSelf;
     556             : }
     557             : 
     558             : @end
     559             : 
     560             : 
     561             : @implementation OOShipGroupEnumerator
     562             : 
     563             : - (id) initWithShipGroup:(OOShipGroup *)group
     564             : {
     565             :         assert(group != nil);
     566             :         
     567             :         if ((self = [super init]))
     568             :         {
     569             :                 _group = [group retain];
     570             :                 _considerCleanup = YES;
     571             :                 _updateCount = [_group updateCount];
     572             :         }
     573             :         
     574             :         return self;
     575             : }
     576             : 
     577             : 
     578           0 : - (void) dealloc
     579             : {
     580             :         DESTROY(_group);
     581             :         
     582             :         [super dealloc];
     583             : }
     584             : 
     585             : 
     586           0 : - (id) nextObject
     587             : {
     588             :         return ShipGroupIterate(self);
     589             : }
     590             : 
     591             : 
     592             : - (NSUInteger) index
     593             : {
     594             :         return _index;
     595             : }
     596             : 
     597             : 
     598             : - (void) setPerformCleanup:(BOOL)flag
     599             : {
     600             :         _considerCleanup = flag;
     601             : }
     602             : 
     603             : @end

Generated by: LCOV version 1.14