Linked list adventure

Started by
57 comments, last by frob 7 years, 2 months ago
Just to remind you people:

The main topic of this thread here are linked lists, how to implement and use them, even though its a bad structure for memory - which I was aware of at the very beginning.

It was not meant to convince people to use C or to convince me to use C++.

Btw. I am making progress for my little editor project I was talking about - which use a linked list to support fast removal of known tiles.
The adding and removing is really fast - I can draw like crazy, but the iterating is really slow due to the fact the I must chase every next pointer - and the memory may not be contiguoes.

Still searching for a solution to support iteration over the tiles in a contiguos way...

Oh and i forgot to mention: The project will be opensource and be put to a public repo ;)
Advertisement

I don't know if you ever read how a text-editor saves its data, but the consensus seems to be a fascinating simple solution. It's one array of characters, bigger than the size of the document. There is one gap in the document, namely at the position of your cursor. If you go 1 character to the left, you copy 1 character from the "pre-gap" data to the "post-gap" data. If you go down a line, you copy a line across the gap, etc.

Some text editors work that way. There are many variations of how they work.

There have been many writeups and comparisons of how different editors handle their memory. Here is a recent article on a few of them. Also google finds a physical book where the author generously placed versions up for free. There are tools like piece chains, gap buffers, page pools (referring to blocks of data not a page of the document), linked lines, and more.

The finseth.com book is actually a PhD thesis on the implementation of text editors (vi, to be precise), it's the only book I know that exists on the subject.

I read it. The thesis considers several different implementations, and eventually concludes a single gap buffer is the best solution.

Since block-copying only gets cheaper compared to jumping around, the conclusion is probably even more true today than it was at the time of publishing.

Wanted to report some progress:

I uploaded a very very basic C version to github: https://github.com/f1nalspace/gamedev_c_experiment

Its a simple tilemap editor with the core ability to draw negative tiles as well and to zoom and pan freely (Max Dimension is set to -128 to 128 - but can be increased to 1024 without any concerns later). Also i included one of my simple physics engine for moving boxes around - so its not a single editor anymore. In addition there is fixed memory archtecture without needing any allocations at runtime at all ;)

By far its not complete at all but it works but we can draw and clear solid tiles and switch between game and editor mode on-the-fly.

Also it uses a few C++ features like: Template for the generic linked list, Zero initialization, No typedef for struct, operator overloading.

I could live without templates and with typedef for structs, but not without Zero initialization and operator overloading. So i will just replace the template with a macro version.

Writing vector math without operator overloading is just painful (had some nasty order bugs in the past because of non-operator overload support):

vAB = vB + wB x rB - vB - wA x rA

Java code (wrong):
Vec2f vAB = new Vec2f(vB).add(Vec2f.cross(wB, rB).sub(vA).sub(Vec2f.cross(wA, rA)));
Java code (correct):
Vec2f vAB = new Vec2f();
vAB.set(vB).add(Vec2f.cross(wB, rB));
vAB.sub(vA).sub(Vec2f.cross(wA, rA));
C++ code (much nicer and less error prune)
Vec2f vAB = vB + Vec2Cross(vB, rB) - vA - Vec2Cross(vA, rA);
But this ones, i dont like at all:
Vec2f v = v0 * transform; // Does order matters here? its not obvious...
Mat4f mat = proj * model * v;
Better done:
Vec2f v = Vec2MultTransform(v0, transform); // Order does not matter here, vec is always first
Mat4f mat = Vec2MultMat4(v, Mat4Mult(proj, model)); // Order is important!

After i have finished the C version (Simple platformer, better physics system, tileset images, tile layers, loading and saving, simple profiler) i will create the actual c++ version of this.

Stay tuned.
Guys i am back fighting with macros again.

I am porting over the linked list template to a macro based version, but visual studio wont compile but i dont see any error at all...
What is wrong here (The pool part are just to test the linked list):


#include <malloc.h>
#include <stdio.h>
#include <string.h>
#include <assert.h>

typedef unsigned int U32;
typedef unsigned char U8;
typedef size_t memory_size;
#define Assert(exp) assert(exp);
#define KiloBytes(value) ((value) * 1024LL)
#define MegaBytes(value) (KiloBytes(value) * 1024LL)
#define GigaBytes(value) (MegaBytes(value) * 1024LL)
#define TeraBytes(value) (GigaBytes(value) * 1024LL)

//
// Memory block
//

struct MemoryBlock {
	void *base;
	memory_size used;
	memory_size size;
};
inline MemoryBlock MemoryBlockCreate(void *base, memory_size size) {
	Assert(base);
	Assert(size > 0);
	MemoryBlock result = {};
	result.base = base;
	result.size = size;
	return(result);
}
inline void *PushSizeInternal(MemoryBlock *block, memory_size size) {
	Assert(block);
	Assert(size > 0);
	Assert(block->used + size <= block->size);
	void *result = (U8*)block->base + block->used;
	block->used += size;
	return(result);
}
#define PushSize(block, size, ...) \
	PushSizeInternal(block, size, ## __VA_ARGS__)
#define PushStruct(block, type, ...) \
	(type *)PushSizeInternal(block, sizeof(type), ## __VA_ARGS__)
#define PushArray(block, type, count, ...) \
	(type *)PushSizeInternal(block, sizeof(type) * count, ## __VA_ARGS__)
inline void ZeroSize(void *base, memory_size size) {
	Assert(base);
	U8 *ptr = (U8 *)base;
	while (size--) {
		*ptr++ = 0;
	}
}
#define ZeroStruct(instance) \
	ZeroSize(instance, sizeof(*(instance)))
#define ZeroArray(ptr, count) \
	ZeroSize(ptr, count*sizeof((ptr)[0]))

//
// Internal list macros
//
#define DoubleLinkedListAddInternal(item, prev, next) do { \
	Assert(item); \
	Assert(prev); \
	Assert(next); \
	next->prev = item; \
	item->next = next; \
	item->prev = prev; \
	prev->next = item; \
} while (0)
#define DoubleLinkedListRemoveInternal(prev, next) do { \
	Assert(prev); \
	Assert(next); \
	next->prev = prev; \
	prev->next = next; \
} while (0)

//
// External list macros
//
#define DoubleLinkedListInit(list) do { \
	Assert(list); \
	(list)->next = (list)->prev = list; \
} while (0)

#define DoubleLinkedListPushFront(list, item) do { \
	Assert(list); \
	DoubleLinkedListAddInternal(item, list, (list)->next); \
} while (0)

#define DoubleLinkedListPushBack(list, item) do { \
	Assert(list); \
	DoubleLinkedListAddInternal(item, (list)->prev, list); \
} while (0)
#define DoubleLinkedListAdd(list, item) \
	DoubleLinkedListPushBack(list, item)

#define DoubleLinkedListRemove(item) do { \
	Assert(item); \
	DoubleLinkedListRemoveInternal((item)->prev, (item)->next); \
	(item)->next = (item)->prev = 0; \
} while (0)

#define DoubleLinkedListPopFront(list) do { \
	Assert(list); \
	DoubleLinkedListRemove((list)->next); \
} while (0)
#define DoubleLinkedListRemoveFirst(list) \
	DoubleLinkedListPopFront(list);

#define DoubleLinkedListPopBack(list) do { \
	Assert(list); \
	DoubleLinkedListRemove((list)->prev); \
} while (0)
#define DoubleLinkedListRemoveLast(list) \
	DoubleLinkedListPopBack(list);

#define IsListEmpty(list) ((list)->next == (list))

struct IndexList {
	struct IndexList *next, *prev;
	U32 index;
};
struct StaticPool {
	IndexList indexPool;
	IndexList freeIndexList;
	// NOTE: First are index list memory, than item memory
	void *base;
	U32 capacity;
	U32 itemStride;
	U32 firstFreeIndex;
};

inline StaticPool StaticPoolCreate(MemoryBlock *memory, U32 capacity, U32 itemStride) {
	StaticPool pool = {};
	pool.capacity = capacity;
	pool.base = PushSize(memory, sizeof(IndexList) * capacity + capacity * itemStride);
	pool.itemStride = itemStride;
	IndexList *indexMemory = (IndexList *)pool.base;
	DoubleLinkedListInit(&pool.indexPool);
	for (U32 index = 0; index < capacity; ++index) {
		IndexList *item = indexMemory + index;
		*item = {};
		DoubleLinkedListAdd(&pool.indexPool, item);
	}
	DoubleLinkedListInit(&pool.freeIndexList);
	return(pool);
}

int main() {
	struct DataItem {
		DataItem *next, *prev;
		int value;
	};

	struct App {
		StaticPool itemPool;
	};

	memory_size memorySize = MegaBytes(32);
	MemoryBlock memory = MemoryBlockCreate(malloc(memorySize), memorySize);

	int poolCapacity = 10;

	App app = {};
	app.itemPool = StaticPoolCreate(&memory, poolCapacity, sizeof(DataItem));

	for (int i = 0; i < 5; ++i) {

	}

	return 0;
}
Here are all Errors:

Severity	Code	Description	Project	File	Line	Suppression State
Error	C2039	'indexPool': is not a member of 'IndexList'	Lists	c:\users\x123713\documents\visual studio 2015\projects\lists\lists\main.cpp	143	
Error	C2039	'pool': is not a member of 'IndexList'	Lists	c:\users\x123713\documents\visual studio 2015\projects\lists\lists\main.cpp	143	
Error (active)		expected an expression	Lists	c:\Users\X123713\Documents\Visual Studio 2015\Projects\Lists\Lists\main.cpp	143	
Error	C2227	left of '->pool' must point to class/struct/union/generic type	Lists	c:\users\x123713\documents\visual studio 2015\projects\lists\lists\main.cpp	145	
Error	C2227	left of '->prev' must point to class/struct/union/generic type	Lists	c:\users\x123713\documents\visual studio 2015\projects\lists\lists\main.cpp	143	
Error	C2228	left of '.freeIndexList' must have class/struct/union	Lists	c:\users\x123713\documents\visual studio 2015\projects\lists\lists\main.cpp	145	
Error	C2059	syntax error: '&'	Lists	c:\users\x123713\documents\visual studio 2015\projects\lists\lists\main.cpp	143	
Error	C2059	syntax error: '('	Lists	c:\users\x123713\documents\visual studio 2015\projects\lists\lists\main.cpp	143	
Error	C2059	syntax error: ')'	Lists	c:\users\x123713\documents\visual studio 2015\projects\lists\lists\main.cpp	143	
Error	C2059	syntax error: ','	Lists	c:\users\x123713\documents\visual studio 2015\projects\lists\lists\main.cpp	143	
Error	C2059	syntax error: '->'	Lists	c:\users\x123713\documents\visual studio 2015\projects\lists\lists\main.cpp	143	
Error	C2143	syntax error: missing ';' before ','	Lists	c:\users\x123713\documents\visual studio 2015\projects\lists\lists\main.cpp	143	
Error	C2819	type 'IndexList' does not have an overloaded member 'operator ->'	Lists	c:\users\x123713\documents\visual studio 2015\projects\lists\lists\main.cpp	143	

You can see the preprocessed file if you compile it with the /P option (cl.exe /P main.cpp). It will dump a main.i where you can see how the macros were transformed.

link: https://msdn.microsoft.com/en-us/library/8z9z0bx6.aspx

In the meanwhile I've uploaded the .i file to http://pastebin.com/CazXV3wm

As the error suggests, the preprocessor is failing to include the surrounding () into &pool.indexPool

You can see the preprocessed file if you compile it with the /P option (cl.exe /P main.cpp). It will dump a main.i where you can see how the macros were transformed.

link: https://msdn.microsoft.com/en-us/library/8z9z0bx6.aspx

In the meanwhile I've uploaded the .i file to http://pastebin.com/CazXV3wm

As the error suggests, the preprocessor is failing to include the surrounding () into &pool.indexPool


Great thanks. This helped a lot!

Now i solved it by changing this part (Adding a type parameter):

#define DoubleLinkedListPushFront(list, item, type) do { \
	type *prev = list; \
	type *next = (list)->next; \
	DoubleLinkedListAddInternal(item, prev, next); \
} while (0)
#define DoubleLinkedListPushBack(list, item, type) do { \
	type *prev = (list)->prev; \
	type *next = list; \
	DoubleLinkedListAddInternal(item, prev, next); \
} while (0)

Thanks for the reminder of why most of the civilized world has moved on from that sort of thing.

I need to go attend to my eyes, they started bleeding reading that.

This topic is closed to new replies.

Advertisement