spritecache.c
changeset 1353 48b59d472641
parent 1352 5880ab4de192
child 1354 5e5c89b9b169
--- a/spritecache.c	Thu Feb 10 22:26:28 2005 +0000
+++ b/spritecache.c	Fri Feb 11 13:35:27 2005 +0000
@@ -53,8 +53,13 @@
 static uint8 _sprite_ysize[NUM_SPRITES];
 #endif
 
+typedef struct MemBlock {
+	uint32 size;
+	byte data[VARARRAY_SIZE];
+} MemBlock;
+
 static uint _sprite_lru_counter;
-static byte *_spritecache_ptr;
+static MemBlock *_spritecache_ptr;
 static uint32 _spritecache_size;
 static int _compact_cache_counter;
 
@@ -411,21 +416,20 @@
 	return true;
 }
 
-#define S_DATA(x) (*(uint32*)(x))
 #define S_FREE_MASK 1
-#define S_HDRSIZE sizeof(uint32)
+
+static inline MemBlock* NextBlock(MemBlock* block)
+{
+	return (MemBlock*)((byte*)block + (block->size & ~S_FREE_MASK));
+}
 
 static uint32 GetSpriteCacheUsage(void)
 {
-	byte *s = _spritecache_ptr;
-	size_t cur_size, tot_size = 0;
-	for(; (cur_size=S_DATA(s)) != 0; s+=cur_size) {
-		if ( cur_size & S_FREE_MASK ) {
-			cur_size--;
-		} else {
-			tot_size += cur_size;
-		}
-	}
+	size_t tot_size = 0;
+	MemBlock* s;
+
+	for (s = _spritecache_ptr; s->size != 0; s = NextBlock(s))
+		if (!(s->size & S_FREE_MASK)) tot_size += s->size;
 
 	return tot_size;
 }
@@ -469,59 +473,51 @@
 // That is accomplished by moving the cached data.
 static void CompactSpriteCache(void)
 {
-	byte *s, *t;
-	size_t size, sizeb, cur_size;
-	int i;
-
-	DEBUG(spritecache, 2) ("compacting sprite cache, inuse=%d", GetSpriteCacheUsage());
-
-	s = _spritecache_ptr;
+	MemBlock *s;
 
-	while (true) {
-		size = S_DATA(s);
+	DEBUG(spritecache, 2) (
+		"compacting sprite cache, inuse=%d", GetSpriteCacheUsage()
+	);
 
-		// Only look for free blocks.
-		if (size & S_FREE_MASK) {
-			size -= S_FREE_MASK;
+	for (s = _spritecache_ptr; s->size != 0;) {
+		if (s->size & S_FREE_MASK) {
+			MemBlock* next = NextBlock(s);
+			MemBlock temp;
+			byte** i;
+
 			// Since free blocks are automatically coalesced, this should hold true.
-			assert(!(S_DATA(s+size) & S_FREE_MASK));
+			assert(!(next->size & S_FREE_MASK));
 
 			// If the next block is the sentinel block, we can safely return
-			if ( (sizeb=S_DATA(s + size)) == 0)
+			if (next->size == 0)
 				break;
 
-			// Locate the sprite number belonging to the next pointer.
-			for(i=0,t=s+size+S_HDRSIZE; _sprite_ptr[i] != t; i++) {assert(i < NUM_SPRITES);}
-
-			// If it's locked, we must not move it.
-#if defined(WANT_LOCKED)
-			if (!_sprite_locked[i]) {
-#endif
-
-				// Offset the sprite pointer by the size of the free block
-				_sprite_ptr[i] -= size;
-
-				// Move the memory
-				memmove(s + S_HDRSIZE, s + S_HDRSIZE + size, sizeb - S_HDRSIZE);
+			// Locate the sprite belonging to the next pointer.
+			for (i = _sprite_ptr; *i != next->data; ++i) {
+				assert(i != endof(_sprite_ptr));
+			}
 
-				// What we just did had the effect of swapping the allocated block with the free block, so we need to update
-				//  the block pointers. First update the allocated one. It is in use.
-				S_DATA(s) = sizeb;
+			#ifdef WANT_LOCKED
+			if (_sprite_locked[i]) {
+				s = next;
+				continue;
+			}
+			#endif
 
-				// Then coalesce the free ones that follow.
-				s += sizeb;
-				while ((cur_size = S_DATA(s+size)) & S_FREE_MASK)
-					size += cur_size - S_FREE_MASK;
-				S_DATA(s) = size + S_FREE_MASK;
-				continue;
-#if defined(WANT_LOCKED)
+			*i = s->data; // Adjust sprite array entry
+			// Swap this and the next block
+			temp = *s;
+			memmove(s, next, next->size);
+			s = NextBlock(s);
+			*s = temp;
+
+			// Coalesce free blocks
+			while (NextBlock(s)->size & S_FREE_MASK) {
+				s->size += NextBlock(s)->size & ~S_FREE_MASK;
 			}
-#endif
+		} else {
+			s = NextBlock(s);
 		}
-		// Continue with next block until the sentinel is reached.
-		s += size;
-		if (size == 0)
-			break;
 	}
 }
 
@@ -529,8 +525,7 @@
 {
 	int i;
 	int best = -1;
-	byte *s;
-	size_t cur_size, cur;
+	MemBlock* s;
 	int cur_lru;
 
 	DEBUG(spritecache, 2) ("DeleteEntryFromSpriteCache, inuse=%d", GetSpriteCacheUsage());
@@ -581,81 +576,69 @@
 		error("Out of sprite memory");
 
 	// Mark the block as free (the block must be in use)
-	s = _sprite_ptr[best];
-	assert(!(S_DATA(s - S_HDRSIZE) & S_FREE_MASK));
-	S_DATA(s - S_HDRSIZE) += S_FREE_MASK;
+	s = (MemBlock*)_sprite_ptr[best] - 1;
+	assert(!(s->size & S_FREE_MASK));
+	s->size |= S_FREE_MASK;
 	_sprite_ptr[best] = NULL;
 
 	// And coalesce adjacent free blocks
-	s = _spritecache_ptr;
-	for(; (cur_size=S_DATA(s)) != 0; s+=cur_size) {
-		if ( cur_size & S_FREE_MASK ) {
-			while ((cur=S_DATA(s+cur_size-S_FREE_MASK)) & S_FREE_MASK) {
-				cur_size += cur - S_FREE_MASK;
-				S_DATA(s) = cur_size;
+	for (s = _spritecache_ptr; s->size != 0; s = NextBlock(s)) {
+		if (s->size & S_FREE_MASK) {
+			while (NextBlock(s)->size & S_FREE_MASK) {
+				s->size += NextBlock(s)->size & ~S_FREE_MASK;
 			}
-			cur_size--;
 		}
 	}
 }
 
 static byte *LoadSpriteToMem(int sprite)
 {
-	byte *s;
-	size_t mem_req, cur_size;
+	size_t mem_req;
 
 	DEBUG(spritecache, 9) ("load sprite %d", sprite);
 
-restart:
 	// Number of needed bytes
-	mem_req = _sprite_size[sprite] + S_HDRSIZE;
+	mem_req = sizeof(MemBlock) + _sprite_size[sprite];
 
-	// Align this to an uint32 boundary. This also makes sure that the 2 least bit are not used,
-	//  so we could use those for other things.
+	/* Align this to an uint32 boundary. This also makes sure that the 2 least
+	 * bits are not used, so we could use those for other things. */
 	mem_req = (mem_req + sizeof(uint32) - 1) & ~(sizeof(uint32) - 1);
 
-	s = _spritecache_ptr;
-	for(;;) {
-		for(;;) {
-			cur_size = S_DATA(s);
-			if (! (cur_size & S_FREE_MASK) ) break;
-
-			cur_size -= S_FREE_MASK;
-
-			// Now s points at a free block.
-			// The block is exactly the size we need?
-			if (cur_size != mem_req) {
-
-				// No.. is it too small?
-				if (cur_size < mem_req + S_HDRSIZE)
-					break;
+	for (;;) {
+		MemBlock* s;
 
-				// Block was big enough, and we need to inject a free block too.
-				S_DATA(s + mem_req) = cur_size - mem_req + S_FREE_MASK;
-
-			}
-			// Set size and in use
-			S_DATA(s) = mem_req;
+		for (s = _spritecache_ptr; s->size != 0; s = NextBlock(s)) {
+			if (s->size & S_FREE_MASK) {
+				size_t cur_size = s->size & ~S_FREE_MASK;
 
-			_sprite_ptr[sprite] = (s += S_HDRSIZE);
-
-			FioSeekToFile(_sprite_file_pos[sprite]);
-			ReadSprite(_sprite_size[sprite], s);
+				/* Is the block exactly the size we need or
+				 * big enough for an additional free block? */
+				if (cur_size == mem_req ||
+						cur_size >= mem_req + sizeof(MemBlock)) {
+					// Set size and in use
+					s->size = mem_req;
 
-			// Patch the height to compensate for a TTD bug?
-			if (sprite == 142) { s[1] = 10; }
+					// Do we need to inject a free block too?
+					if (cur_size != mem_req) {
+						NextBlock(s)->size = (cur_size - mem_req) | S_FREE_MASK;
+					}
 
-			// Return sprite ptr
-			return s;
+					_sprite_ptr[sprite] = s->data;
+
+					FioSeekToFile(_sprite_file_pos[sprite]);
+					ReadSprite(_sprite_size[sprite], s->data);
+
+					// Patch the height to compensate for a TTD bug?
+					if (sprite == 142) s->data[1] = 10;
+
+					// Return sprite ptr
+					return s->data;
+				}
+			}
 		}
 
-		// Reached sentinel, but no block found yet. Need to delete some old entries.
-		if (cur_size == 0) {
-			DeleteEntryFromSpriteCache();
-			goto restart;
-		}
-
-		s += cur_size;
+		// Reached sentinel, but no block found yet. Delete some old entry.
+		DeleteEntryFromSpriteCache();
 	}
 }
 
@@ -951,16 +934,16 @@
 	_compact_cache_counter = 0;
 }
 
-static void GfxInitSpriteMem(byte *ptr, uint32 size)
+static void GfxInitSpriteMem(void *ptr, uint32 size)
 {
 	// initialize sprite cache heap
 	_spritecache_ptr = ptr;
 	_spritecache_size = size;
 
-	// Sentinel block (identified by size=0)
-	S_DATA(ptr + size - S_HDRSIZE) = 0;
 	// A big free block
-	S_DATA(ptr) = size - S_HDRSIZE + S_FREE_MASK;
+	_spritecache_ptr->size = (size - sizeof(MemBlock)) | S_FREE_MASK;
+	// Sentinel block (identified by size == 0)
+	NextBlock(_spritecache_ptr)->size = 0;
 
 	memset(_sprite_ptr, 0, sizeof(_sprite_ptr));
 }