Initial Commit
This commit is contained in:
13
OU3/.gitignore
vendored
Normal file
13
OU3/.gitignore
vendored
Normal file
@@ -0,0 +1,13 @@
|
||||
# Vim swap files.
|
||||
*.swp
|
||||
*.swo
|
||||
# Object files.
|
||||
*.o
|
||||
# gcc precompiled headers.
|
||||
*.gch
|
||||
# Compiled programs for testing.
|
||||
*_mwe
|
||||
*_mwe[0-9]
|
||||
*_mwe[0-9]i
|
||||
*_test
|
||||
*_test[0-9]
|
||||
6
OU3/.vscode/settings.json
vendored
Normal file
6
OU3/.vscode/settings.json
vendored
Normal file
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"files.associations": {
|
||||
"format": "c",
|
||||
"numbers": "c"
|
||||
}
|
||||
}
|
||||
98
OU3/CODING_STYLE.md
Normal file
98
OU3/CODING_STYLE.md
Normal file
@@ -0,0 +1,98 @@
|
||||
# Kodkonvention
|
||||
|
||||
I detta dokument beskrivs den kodkonvention som har följts/ska följas
|
||||
för kod som läggs till kodbasen. Konventionen är till stor del baserad
|
||||
på kodkonventionen som följs i Linux-kärnan. Därför kommer läsaren
|
||||
hänvisas till dokumentet med den officiella kernelkonventionen.
|
||||
|
||||
|
||||
# Linuxkärnans kodkonvention
|
||||
|
||||
Se [Linux kernel coding style](https://www.kernel.org/doc/html/v4.10/process/coding-style.html).
|
||||
Nedan följer en uppräkning av vilka punkter vi ska följa, samt
|
||||
eventuella avvikelser från dessa. Om en punkt från dokumentet ovan ej
|
||||
nämns nedan så gäller punkten ej.
|
||||
|
||||
## 1. Indentation
|
||||
|
||||
Indenteringskonventionen ska följas helt och hållet. Det är möjligt att
|
||||
konfigurera utvecklingsmiljön (e.g. Codelite) att automatiskt indentera
|
||||
korrekt.
|
||||
|
||||
## 2. Breaking long lines and strings
|
||||
|
||||
Denna punkt är också mycket viktig, samt något din utvecklingsmiljö kan
|
||||
hantera eller varna för.
|
||||
|
||||
## 3. Placing Braces and Spaces
|
||||
|
||||
Här ska alla punkter följas (även de under 3.1), med ett undantag:
|
||||
Ensamma satser i t.ex. if-satser *ska* ha måsvingar.
|
||||
|
||||
## 4. Naming
|
||||
|
||||
När det kommer till namngivning är det viktigt att ha i åtanke att koden
|
||||
tillsammans med kommentarerna ska tydligt förklara syftet med koden. Därför är
|
||||
det viktigt att konventionen som beskrivs under `4. Naming` tolkas med detta i
|
||||
hänsyn. Det innebär till exempel att om koden blir enklare att läsa med
|
||||
variabelnamnet `counter` istället för `c` eller `tmp`, bör `counter` användas
|
||||
istället.
|
||||
|
||||
## 5. Typedefs
|
||||
|
||||
Här avviker kursen från Linuxkärnans kodkonvention. För att främja
|
||||
datatypernas gränsytor och göra dem så lik de teoretiska gränsytorna
|
||||
som möjligt så används `typedefs` för t.ex. `struct list_pos`. I
|
||||
Linuxkärnan hade motsvarande implementation krävt att det skrevs
|
||||
`struct list_pos pos` överallt, medan datatyperna givna på kursen
|
||||
förenklar detta till `list_pos pos`. Det viktiga här är att lära sig
|
||||
gränsytorna.
|
||||
|
||||
## 6. Functions
|
||||
|
||||
Denna konvention ska följas så som den presenteras. Långa funktioner kommer
|
||||
under denna kurs alltid vara onödigt, dela upp i mindre funktioner! Det ökar
|
||||
läsbarheten, förenklar debugging, samt gör det enklare att diskutera koden.
|
||||
|
||||
## 7. Centralized exiting of functions
|
||||
|
||||
Det finns anledningar till att `goto` används i Linuxkärnan (e.g.
|
||||
bakåtkompabilitet), men under denna kurs ska ni ej använda detta.
|
||||
|
||||
## 8. Commenting
|
||||
|
||||
Följ det första exemplet för kommentarer. Det finns exempel i de givna
|
||||
datatyperna på hur funktioner kan dokumenteras.
|
||||
|
||||
## 14. Allocating memory
|
||||
|
||||
I Linuxkärnan finns inte `malloc` och `calloc`, motsvarande funktioner heter
|
||||
`kmalloc` samt `kcalloc`. Vi kommer under denna kurs att översätta Linuxkärnans
|
||||
konvention till följande:
|
||||
|
||||
```c
|
||||
p = malloc(sizeof(*p));
|
||||
```
|
||||
|
||||
samt
|
||||
|
||||
```c
|
||||
p = calloc(n, sizeof(*p));
|
||||
```
|
||||
|
||||
Det är fullt möjligt att i det senare fallet vända på parametrarna (i.e.
|
||||
`calloc(sizeof(*p), n)`) då storleken på minnet som allokeras beräknas som
|
||||
första argumentet gånger det andra argumentet (i.e.,
|
||||
`n*sizeof(*p) == sizeof(*p)*n`).
|
||||
|
||||
## 16. Function return values and names
|
||||
|
||||
Här är det första och sista stycket vad som är viktigt att följa. Om en funktion
|
||||
returnerar huruvida funktionen lyckades eller ej så signifierar `0` ett lyckat
|
||||
anrop, och negativa värden signifierar fel (e.g. `-1` om en fil inte finns, `-2`
|
||||
om filen inte gick att läsa). Om en funktion returnerar en pekare `p`, så
|
||||
indikerar `p == NULL` att funktionen inte lyckades.
|
||||
|
||||
# Avslutning
|
||||
|
||||
Det finns visst rum för tolkning under några av dessa punkter.
|
||||
114
OU3/ChangeLog.txt
Normal file
114
OU3/ChangeLog.txt
Normal file
@@ -0,0 +1,114 @@
|
||||
Summary changelog file for release.
|
||||
|
||||
Release 2.2.2.2, Jan 24, 2025.
|
||||
- Added int_queue_example_internal.
|
||||
- Added symmetric examples to queue.
|
||||
|
||||
Release 2.2.2.1, Jan 16, 2025.
|
||||
- Added int_queue_example without any queue_print.
|
||||
|
||||
Release 2.2.2.0, Jan 10, 2025.
|
||||
- Added int_queue.
|
||||
- Added release date to the code base.
|
||||
|
||||
Release 2.2.1.5, May 15, 2024.
|
||||
- Fixed array_1d merge confusion.
|
||||
|
||||
Release 2.2.1.4, May 15, 2024.
|
||||
- Whitespace/doc fix.
|
||||
|
||||
Release 2.2.1.3, May 15, 2024.
|
||||
- Bugfix to remove sprintf warning.
|
||||
|
||||
Release 2.2.1.2, May 10, 2024.
|
||||
- Untabified all code.
|
||||
|
||||
Release 2.2.1.1, May 10, 2024.
|
||||
- Bugfix to sprintf overflow warning.
|
||||
|
||||
Release 2.2.1.0, May 10, 2024.
|
||||
- Added typedef free_function for backward compatibility with pre-v2.0 code.
|
||||
|
||||
Release 2.2.0.0, May 10, 2024.
|
||||
- Added *_print_internal for all datatypes.
|
||||
- Updated several *_print_internal functions to improve visualization of encapsulation.
|
||||
- Added master Makefile to src.
|
||||
|
||||
Release 2.1.0.0, Apr 15, 2024.
|
||||
- Added *_print_internal for table, array_1d, array_2d and int_array_1d, int_stack.
|
||||
- Updated comments in table.h to allow for different internal handling of duplicates.
|
||||
|
||||
Release 2.0.0.1, Apr 05, 2024.
|
||||
- Bugfix of one MWE in array_1d that never should have been changed.
|
||||
|
||||
Release 2.0.0.0, Apr 04, 2024.
|
||||
- Major upgrade with functions to print the internal memory structure
|
||||
of the generic data types in graphviz/dot format.
|
||||
- Added internal_print functions for list, dlist, queue, and stack.
|
||||
- Added mweNi for list, dlist, queue, and stack.
|
||||
- Renamed free_function to kill_function to reduce confusion with free() function.
|
||||
|
||||
Release 1.0.14.3, Jan 16, 2024.
|
||||
- Cleanup of some #include directives that used citation marks.
|
||||
|
||||
Release 1.0.14.2, Mar 23, 2023.
|
||||
- Cleanup of empty statements in some list test programs.
|
||||
|
||||
Release 1.0.14.1, Mar 23, 2023.
|
||||
- Renamed all list_pos_are_equal to list_pos_is_equal.
|
||||
|
||||
Release 1.0.14.0, Mar 21, 2023.
|
||||
- Cleaned up list_test programs.
|
||||
- Renamed list header fields to head and tail.
|
||||
|
||||
Release 1.0.13.0, Jan 21, 2023.
|
||||
- Added two test programs to list and one to int_list_array.
|
||||
|
||||
Release 1.0.12.0, Jan 21, 2023.
|
||||
- Renamed list_pos_equal to list_pos_are_equal.
|
||||
- Added int_list_test program.
|
||||
|
||||
Release 1.0.11.0, Jan 19, 2023.
|
||||
- Added list_pos_equal and list_pos_is_valid to list types.
|
||||
|
||||
Release 1.0.10.0, Jan 14, 2023.
|
||||
- Added version information to lib + all MWEs.
|
||||
|
||||
Release 1.0.9, Mar 24, 2022.
|
||||
- Added int_stack.
|
||||
|
||||
Release 1.0.8, Mar 06, 2019.
|
||||
- Added table_choose_key().
|
||||
|
||||
Release 1.0.7, Mar 04, 2019.
|
||||
- Bugfix in table_remove().
|
||||
- Minor improvement of array_2d_print().
|
||||
|
||||
Release 1.0.6, Feb 21, 2019.
|
||||
- Added table version without dlist/memhandler.
|
||||
- Added Makefiles for each data type.
|
||||
- Updated various README.md files.
|
||||
|
||||
Release 1.0.5, Jan 23, 2019.
|
||||
- Cleaned up list_test.c: No cleanup on failure, improved messages and
|
||||
proper file comment.
|
||||
|
||||
Release 1.0.4, Apr 03, 2018.
|
||||
- Split MWE to one case per file.
|
||||
- Modified list_test to NOT use memhandler.
|
||||
|
||||
Release 1.0.3, Feb 08, 2018.
|
||||
- Added table implementation.
|
||||
|
||||
Release 1.0.2, Jan 29, 2018.
|
||||
- Removed table from public area.
|
||||
- Cleaned up links, tabbing of many files.
|
||||
- Added list_test test file.
|
||||
- Added ChangeLog (this file).
|
||||
- Updated README.md
|
||||
|
||||
Release 1.0.1, Jan 29, 2018.
|
||||
- Removed array from public area.
|
||||
|
||||
Release 1.0.0, Jan 28, 2018.
|
||||
- First public release.
|
||||
114
OU3/README.md
Normal file
114
OU3/README.md
Normal file
@@ -0,0 +1,114 @@
|
||||
# Datatypsimplementationer för kursen datastrukturer och algoritmer vid Institutionen för datavetenskap, Umeå universitet.
|
||||
|
||||
# Version 2.2.2.2, 2025-01-24.
|
||||
|
||||
# Givna datatyper
|
||||
|
||||
Här återfinns implementationer av några av de datatyper som kommer användas
|
||||
under denna kurs. Nedan följer information om hur dessa kompileras och hur
|
||||
`minimal working examples` körs för varje datatyp.
|
||||
|
||||
## Headerfiler
|
||||
|
||||
Headerfilerna för de givna datatyperna är alla samlade i mappen `include`. Dessa
|
||||
kan inkluderas med hjälp av `-I`-flaggan till `gcc`, se exempel nedan.
|
||||
|
||||
## undefined reference
|
||||
|
||||
Det är möjligt att du får t.ex. följande felmeddelande:
|
||||
|
||||
```bash
|
||||
/tmp/ccLd0e5H.o: In function `queue_empty':
|
||||
queue.c:(.text+0x14): undefined reference to `list_empty'
|
||||
```
|
||||
|
||||
Detta innebär att du inte anget alla nödvändiga C-filer till kompilatorn. I
|
||||
fallet ovan saknas `list.c`.
|
||||
|
||||
# En-dimensionellt fält
|
||||
|
||||
```bash
|
||||
user@host:~$ cd ~/datastructures/src/array_1d
|
||||
user@host:~/datastructures/src/array_1d$ gcc -std=c99 -Wall -I../../include/ array_1d.c array_1d_mwe1.c -o array_1d_mwe1
|
||||
user@host:~/datastructures/src$ ./array_1d_mwe1
|
||||
[ [1], [4], [9], [16], [25], [36] ]
|
||||
user@host:~/datastructures/src/array_1d$ gcc -std=c99 -Wall -I../../include/ array_1d.c array_1d_mwe2.c -o array_1d_mwe2
|
||||
user@host:~/datastructures/src$ ./array_1d_mwe2
|
||||
[ [(Jan, 31)], [(Feb, 28)], [(Mar, 31)], [(Apr, 30)], [(May, 31)], [(Jun, 30)], [(Jul, 31)], [(Aug, 31)], [(Sep, 30)], [(Oct, 31)], [(Nov, 30)], [(Dec, 31)] ]
|
||||
```
|
||||
# Två-dimensionellt fält
|
||||
|
||||
```bash
|
||||
user@host:~$ cd ~/datastructures/src/array_2d
|
||||
user@host:~/datastructures/src/array_2d$ gcc -std=c99 -Wall -I../../include/ array_2d.c array_2d_mwe1.c -o array_2d_mwe1
|
||||
user@host:~/datastructures/src/array_2d$ ./array_2d_mwe1
|
||||
[ [ [11], [12], [13] [ [21], [22], [23] [ [31], [32], [33] [ [41], [42], [43] ]
|
||||
```
|
||||
# Lista (oriktad, dubbellänkad)
|
||||
|
||||
```bash
|
||||
user@host:~$ cd ~/datastructures/src/list
|
||||
user@host:~/datastructures/src/list$ gcc -std=c99 -Wall -I../../include/ list.c list_mwe1.c -o list_mwe1
|
||||
user@host:~/datastructures/src/list$ ./list_mwe1
|
||||
List after inserting one value:
|
||||
( [5] )
|
||||
List after inserting second value at the end:
|
||||
( [5], [8] )
|
||||
List after inserting a third value in the middle:
|
||||
( [5], [2], [8] )
|
||||
List after removing first element:
|
||||
( [2], [8] )
|
||||
```
|
||||
|
||||
# Riktad lista (enkellänkad)
|
||||
|
||||
```bash
|
||||
user@host:~$ cd ~/datastructures/src/dlist
|
||||
user@host:~/datastructures/src/dlist$ gcc -std=c99 -Wall -I../../include/ dlist.c dlist_mwe1.c -o dlist_mwe1
|
||||
user@host:~/datastructures/src/dlist$ ./dlist_mwe1
|
||||
("Alfons", "Bengt", "Cia", "David", "Florian", "Gunnar")
|
||||
```
|
||||
|
||||
# Kö
|
||||
|
||||
```bash
|
||||
user@host:~$ cd ~/datastructures/src/queue
|
||||
user@host:~/datastructures/src/queue$ gcc -std=c99 -Wall -I../../include/ queue.c queue_mwe1.c ../list/list.c -o queue_mwe1
|
||||
user@host:~/datastructures/src/queue$ ./queue_mwe1
|
||||
QUEUE before dequeuing:
|
||||
{ [1], [2], [3] }
|
||||
QUEUE after dequeuing:
|
||||
{ [2], [3] }
|
||||
```
|
||||
|
||||
|
||||
# Stack
|
||||
|
||||
```bash
|
||||
user@host:~$ cd ~/datastructures/src/stack
|
||||
user@host:~/datastructures/src/stack$ gcc -std=c99 -Wall -I../../include/ stack.c stack_mwe1.c -o stack_mwe1
|
||||
user@host:~/datastructures/src/stack$ ./stack_mwe1
|
||||
--STACK before popping--
|
||||
{ [3], [2], [1] }
|
||||
--STACK after popping--
|
||||
{ [2], [1] }
|
||||
```
|
||||
|
||||
# Tabell
|
||||
|
||||
```bash
|
||||
user@host:~$ cd ~/datastructures/src/table
|
||||
user@host:~/datastructures/src/table$ gcc -std=c99 -Wall -I../../include/ table.c table_mwe1.c ../dlist/dlist.c -o table_mwe1
|
||||
user@host:~/datastructures/src/table$ ./table_mwe1
|
||||
Table after inserting 3 pairs:
|
||||
[98185, Kiruna]
|
||||
[90184, Umea]
|
||||
[90187, Umea]
|
||||
Lookup of postal code 90187: Umea.
|
||||
Table after adding a duplicate:
|
||||
[90187, Umea (Universitet)]
|
||||
[98185, Kiruna]
|
||||
[90184, Umea]
|
||||
[90187, Umea]
|
||||
Lookup of postal code 90187: Umea (Universitet).
|
||||
```
|
||||
801
OU3/arraytable (conflicted copy 2025-05-09 094442).c
Normal file
801
OU3/arraytable (conflicted copy 2025-05-09 094442).c
Normal file
@@ -0,0 +1,801 @@
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <ctype.h> // For isspace()
|
||||
#include <stdarg.h>
|
||||
|
||||
#include <table.h>
|
||||
#include <array_1d.h>
|
||||
|
||||
#define MAXSIZE 80000
|
||||
|
||||
/*
|
||||
* Implementation of a arraybased table implementation
|
||||
*
|
||||
* Duplicates are handled by insert, where values are overwritten.
|
||||
*
|
||||
* Authors: Marc Meunier (tfy22mmr@cs.umu.se)
|
||||
*
|
||||
*
|
||||
* Based on earlier code by: Johan Eliasson (johane@cs.umu.se).
|
||||
* Niclas Borlin (niclas@cs.umu.se)
|
||||
* Adam Dahlgren Lindstrom (dali@cs.umu.se)
|
||||
*
|
||||
* Version information:
|
||||
* v1.0 2018-02-06: First public version.
|
||||
*/
|
||||
|
||||
// ===========INTERNAL DATA TYPES ============
|
||||
|
||||
struct table
|
||||
{
|
||||
array_1d *entries; // The table entries are stored in a directed list
|
||||
compare_function *key_cmp_func;
|
||||
kill_function key_kill_func;
|
||||
kill_function value_kill_func;
|
||||
int count;
|
||||
};
|
||||
|
||||
typedef struct table_entry
|
||||
{
|
||||
void *key;
|
||||
void *value;
|
||||
} table_entry;
|
||||
|
||||
// ===========INTERNAL FUNCTION IMPLEMENTATIONS ============
|
||||
|
||||
/**
|
||||
* table_entry_create() - Allocate and populate a table entry.
|
||||
* @key: A pointer to a function to be used to compare keys.
|
||||
* @value: A pointer to a function (or NULL) to be called to
|
||||
* de-allocate memory for keys on remove/kill.
|
||||
*
|
||||
* Returns: A pointer to the newly created table entry.
|
||||
*/
|
||||
table_entry *table_entry_create(void *key, void *value)
|
||||
{
|
||||
// Allocate space for a table entry. Use calloc as a defensive
|
||||
// measure to ensure that all pointers are initialized to NULL.
|
||||
table_entry *e = calloc(1, sizeof(*e));
|
||||
// Populate the entry.
|
||||
e->key = key;
|
||||
e->value = value;
|
||||
// e->count = e->count + 1;
|
||||
|
||||
return e;
|
||||
}
|
||||
|
||||
/**
|
||||
* table_entry_kill() - Return the memory allocated to a table entry.
|
||||
* @e: The table entry to deallocate.
|
||||
*
|
||||
* Returns: Nothing.
|
||||
*/
|
||||
void table_entry_kill(void *v)
|
||||
{
|
||||
table_entry *e = v; // Convert the pointer (useful if debugging the code)
|
||||
|
||||
// All we need to do is to deallocate the struct.
|
||||
free(e);
|
||||
}
|
||||
|
||||
/**
|
||||
* table_empty() - Create an empty table.
|
||||
* @key_cmp_func: A pointer to a function to be used to compare keys.
|
||||
* @key_kill_func: A pointer to a function (or NULL) to be called to
|
||||
* de-allocate memory for keys on remove/kill.
|
||||
* @value_kill_func: A pointer to a function (or NULL) to be called to
|
||||
* de-allocate memory for values on remove/kill.
|
||||
*
|
||||
* Returns: Pointer to a new table.
|
||||
*/
|
||||
table *table_empty(compare_function *key_cmp_func,
|
||||
kill_function key_kill_func,
|
||||
kill_function value_kill_func)
|
||||
{
|
||||
// Allocate the table header.
|
||||
table *t = calloc(1, sizeof(table));
|
||||
// Create the list to hold the table_entry-ies.
|
||||
t->entries = array_1d_create(0, MAXSIZE - 1, NULL);
|
||||
// Store the key compare function and key/value kill functions.
|
||||
t->key_cmp_func = key_cmp_func;
|
||||
t->key_kill_func = key_kill_func;
|
||||
t->value_kill_func = value_kill_func;
|
||||
t->count = 0;
|
||||
|
||||
return t;
|
||||
}
|
||||
|
||||
/**
|
||||
* table_is_empty() - Check if a table is empty.
|
||||
* @table: Table to check.
|
||||
*
|
||||
* Returns: True if table contains no key/value pairs, false otherwise.
|
||||
*/
|
||||
bool table_is_empty(const table *t)
|
||||
{
|
||||
return (t->count == 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* table_lookup() - Look up a given key in a table.
|
||||
* @table: Table to inspect.
|
||||
* @key: Key to look up.
|
||||
*
|
||||
* Returns: The value corresponding to a given key, or NULL if the key
|
||||
* is not found in the table. If the table contains duplicate keys,
|
||||
* the value that was latest inserted will be returned.
|
||||
*/
|
||||
void *table_lookup(const table *t, const void *key)
|
||||
{
|
||||
int inserted_values = 0;
|
||||
// loop over array
|
||||
for (int i = 0; i < MAXSIZE; i++)
|
||||
{
|
||||
// finds populated entries
|
||||
if ((array_1d_inspect_value(t->entries, i) != NULL))
|
||||
{
|
||||
inserted_values++;
|
||||
table_entry *e;
|
||||
e = array_1d_inspect_value(t->entries, i);
|
||||
// checks if table key corresponds with seach key
|
||||
if (e != NULL && t->key_cmp_func(e->key, key) == 0)
|
||||
{
|
||||
return e->value;
|
||||
}
|
||||
}
|
||||
// will quit loop if no more values are left to check
|
||||
if (inserted_values == t->count)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
// No match found. Return NULL.
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* table_insert() - Add a key/value pair to a table.
|
||||
* @table: Table to manipulate.
|
||||
* @key: A pointer to the key value.
|
||||
* @value: A pointer to the value value.
|
||||
*
|
||||
* Insert the key/value pair into the table. No test is performed to
|
||||
* check if key is a duplicate. table_lookup() will return the latest
|
||||
* added value for a duplicate key. table_remove() will remove all
|
||||
* duplicates for a given key.
|
||||
*
|
||||
* Returns: Nothing.
|
||||
*/
|
||||
void table_insert(table *t, void *key, void *value)
|
||||
{
|
||||
// Allocate the key/value structure.
|
||||
table_entry *e = table_entry_create(key, value);
|
||||
// inserts value at first empty position if key is new
|
||||
if (table_lookup(t, key) == NULL)
|
||||
{
|
||||
|
||||
for (int i = 0; i < MAXSIZE; i++)
|
||||
{
|
||||
// finds first empty slot
|
||||
if (!(array_1d_inspect_value(t->entries, i) != NULL))
|
||||
{
|
||||
array_1d_set_value(t->entries, e, i);
|
||||
t->count++;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
// find place of old key and overwrites.
|
||||
else
|
||||
{
|
||||
for (int i = 0; i < MAXSIZE; i++)
|
||||
{
|
||||
table_entry *f = array_1d_inspect_value(t->entries, i);
|
||||
// find matching key
|
||||
if (f != NULL && t->key_cmp_func(f->key, key) == 0)
|
||||
{
|
||||
// kills memory of old value and key
|
||||
if (f != NULL)
|
||||
{
|
||||
if (t->value_kill_func != NULL)
|
||||
{
|
||||
t->value_kill_func(f->value);
|
||||
}
|
||||
if (t->key_kill_func != NULL)
|
||||
{
|
||||
t->key_kill_func(f->key);
|
||||
}
|
||||
table_entry_kill(f);
|
||||
}
|
||||
// insert new value
|
||||
array_1d_set_value(t->entries, e, i);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* table_choose_key() - Return an arbitrary key.
|
||||
* @t: Table to inspect.
|
||||
*
|
||||
* Return an arbitrary key stored in the table. Can be used together
|
||||
* with table_remove() to deconstruct the table. Returns NULL for an
|
||||
* empty table.
|
||||
*
|
||||
* Returns: An arbitrary key stored in the table.
|
||||
*/
|
||||
void *table_choose_key(const table *t)
|
||||
{
|
||||
// find first entrie in table
|
||||
for (int i = 0; i < MAXSIZE - 1; i++)
|
||||
{
|
||||
// if contain entry
|
||||
if ((array_1d_inspect_value(t->entries, i) != NULL))
|
||||
{
|
||||
table_entry *e;
|
||||
e = array_1d_inspect_value(t->entries, i);
|
||||
return e->key;
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* table_remove() - Remove a key/value pair in the table.
|
||||
* @table: Table to manipulate.
|
||||
* @key: Key for which to remove pair.
|
||||
*
|
||||
* Any matching duplicates will be removed. Will call any kill
|
||||
* functions set for keys/values. Does nothing if key is not found in
|
||||
* the table.
|
||||
*
|
||||
* Returns: Nothing.
|
||||
*/
|
||||
void table_remove(table *t, const void *key)
|
||||
{
|
||||
int inserted_values = 0;
|
||||
for (int i = 0; i < MAXSIZE; i++)
|
||||
{
|
||||
if ((array_1d_inspect_value(t->entries, i) != NULL))
|
||||
{
|
||||
inserted_values++;
|
||||
table_entry *e = array_1d_inspect_value(t->entries, i);
|
||||
// Find matching entry
|
||||
if (e != NULL && t->key_cmp_func(e->key, key) == 0)
|
||||
{
|
||||
// removes memory from entries
|
||||
if (e != NULL)
|
||||
{
|
||||
if (t->value_kill_func != NULL)
|
||||
{
|
||||
t->value_kill_func(e->value);
|
||||
}
|
||||
if (t->key_kill_func != NULL)
|
||||
{
|
||||
t->key_kill_func(e->key);
|
||||
}
|
||||
}
|
||||
table_entry_kill(e);
|
||||
// set values to NULL
|
||||
array_1d_set_value(t->entries, NULL, i);
|
||||
t->count--;
|
||||
break;
|
||||
}
|
||||
}
|
||||
// will quit loop if no more values are left to check
|
||||
if (inserted_values == t->count)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* table_kill() - Destroy a table.
|
||||
* @table: Table to destroy.
|
||||
*
|
||||
* Return all dynamic memory used by the table and its elements. If a
|
||||
* kill_func was registered for keys and/or values at table creation,
|
||||
* it is called each element to kill any user-allocated memory
|
||||
* occupied by the element values.
|
||||
*
|
||||
* Returns: Nothing.
|
||||
*/
|
||||
void table_kill(table *t)
|
||||
{
|
||||
for (int i = 0; i < MAXSIZE; i++)
|
||||
{
|
||||
// Inspect the key/value pair.
|
||||
table_entry *e = array_1d_inspect_value(t->entries, i);
|
||||
// Kill key and/or value
|
||||
if (e != NULL)
|
||||
{
|
||||
if (t->key_kill_func != NULL)
|
||||
{
|
||||
t->key_kill_func(e->key);
|
||||
}
|
||||
if (t->value_kill_func != NULL)
|
||||
{
|
||||
t->value_kill_func(e->value);
|
||||
}
|
||||
// Deallocate the table entry structure.
|
||||
table_entry_kill(e);
|
||||
}
|
||||
}
|
||||
|
||||
// Kill what's left of the list...
|
||||
array_1d_kill(t->entries);
|
||||
// ...and the table struct.
|
||||
free(t);
|
||||
}
|
||||
|
||||
/**
|
||||
* table_print() - Print the given table.
|
||||
* @t: Table to print.
|
||||
* @print_func: Function called for each key/value pair in the table.
|
||||
*
|
||||
* Iterates over the key/value pairs in the table and prints them.
|
||||
* Will print all stored elements, including duplicates.
|
||||
*
|
||||
* Returns: Nothing.
|
||||
*/
|
||||
void table_print(const table *t, inspect_callback_pair print_func)
|
||||
{
|
||||
// Iterate over all elements. Call print_func on keys/values.
|
||||
for (int i = 0; i < MAXSIZE; i++)
|
||||
{
|
||||
table_entry *e = array_1d_inspect_value(t->entries, i);
|
||||
// Call print_func
|
||||
if (e != NULL)
|
||||
{
|
||||
print_func(e->key, e->value);
|
||||
// pos = dlist_next(t->entries, i);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ===========INTERNAL FUNCTIONS USED BY list_print_internal ============
|
||||
|
||||
// The functions below output code in the dot language, used by
|
||||
// GraphViz. For documention of the dot language, see graphviz.org.
|
||||
|
||||
/**
|
||||
* indent() - Output indentation string.
|
||||
* @n: Indentation level.
|
||||
*
|
||||
* Print n tab characters.
|
||||
*
|
||||
* Returns: Nothing.
|
||||
*/
|
||||
static void indent(int n)
|
||||
{
|
||||
for (int i = 0; i < n; i++)
|
||||
{
|
||||
printf("\t");
|
||||
}
|
||||
}
|
||||
/**
|
||||
* iprintf(...) - Indent and print.
|
||||
* @n: Indentation level
|
||||
* @...: printf arguments
|
||||
*
|
||||
* Print n tab characters and calls printf.
|
||||
*
|
||||
* Returns: Nothing.
|
||||
*/
|
||||
static void iprintf(int n, const char *fmt, ...)
|
||||
{
|
||||
// Indent...
|
||||
indent(n);
|
||||
// ...and call printf
|
||||
va_list args;
|
||||
va_start(args, fmt);
|
||||
vprintf(fmt, args);
|
||||
va_end(args);
|
||||
}
|
||||
|
||||
/**
|
||||
* print_edge() - Print a edge between two addresses.
|
||||
* @from: The address of the start of the edge. Should be non-NULL.
|
||||
* @to: The address of the destination for the edge, including NULL.
|
||||
* @port: The name of the port on the source node, or NULL.
|
||||
* @label: The label for the edge, or NULL.
|
||||
* @options: A string with other edge options, or NULL.
|
||||
*
|
||||
* Print an edge from port PORT on node FROM to TO with label
|
||||
* LABEL. If to is NULL, the destination is the NULL node, otherwise a
|
||||
* memory node. If the port is NULL, the edge starts at the node, not
|
||||
* a specific port on it. If label is NULL, no label is used. The
|
||||
* options string, if non-NULL, is printed before the label.
|
||||
*
|
||||
* Returns: Nothing.
|
||||
*/
|
||||
static void print_edge(int indent_level, const void *from, const void *to, const char *port,
|
||||
const char *label, const char *options)
|
||||
{
|
||||
indent(indent_level);
|
||||
if (port)
|
||||
{
|
||||
printf("m%04lx:%s -> ", PTR2ADDR(from), port);
|
||||
}
|
||||
else
|
||||
{
|
||||
printf("m%04lx -> ", PTR2ADDR(from));
|
||||
}
|
||||
if (to == NULL)
|
||||
{
|
||||
printf("NULL");
|
||||
}
|
||||
else
|
||||
{
|
||||
printf("m%04lx", PTR2ADDR(to));
|
||||
}
|
||||
printf(" [");
|
||||
if (options != NULL)
|
||||
{
|
||||
printf("%s", options);
|
||||
}
|
||||
if (label != NULL)
|
||||
{
|
||||
printf(" label=\"%s\"", label);
|
||||
}
|
||||
printf("]\n");
|
||||
}
|
||||
|
||||
/**
|
||||
* print_head_node() - Print a node corresponding to the table struct.
|
||||
* @indent_level: Indentation level.
|
||||
* @t: Table to inspect.
|
||||
*
|
||||
* Returns: Nothing.
|
||||
*/
|
||||
static void print_head_node(int indent_level, const table *t)
|
||||
{
|
||||
iprintf(indent_level, "m%04lx [shape=record "
|
||||
"label=\"<e>entries\\n%04lx|cmp\\n%04lx|key_kill\\n%04lx|value_kill\\n%04lx\"]\n",
|
||||
PTR2ADDR(t), PTR2ADDR(t->entries), PTR2ADDR(t->key_cmp_func),
|
||||
PTR2ADDR(t->key_kill_func), PTR2ADDR(t->value_kill_func));
|
||||
}
|
||||
|
||||
// Internal function to print the head--entries edge in dot format.
|
||||
static void print_head_edge(int indent_level, const table *t)
|
||||
{
|
||||
print_edge(indent_level, t, t->entries, "e", "entries", NULL);
|
||||
}
|
||||
|
||||
// Internal function to print the table entry node in dot format.
|
||||
static void print_element_node(int indent_level, const table_entry *e)
|
||||
{
|
||||
iprintf(indent_level, "m%04lx [shape=record label=\"<k>key\\n%04lx|<v>value\\n%04lx\"]\n",
|
||||
PTR2ADDR(e), PTR2ADDR(e->key), PTR2ADDR(e->value));
|
||||
}
|
||||
|
||||
// Internal function to print the table entry node in dot format.
|
||||
static void print_key_value_nodes(int indent_level, const table_entry *e,
|
||||
inspect_callback key_print_func,
|
||||
inspect_callback value_print_func)
|
||||
{
|
||||
if (e->key != NULL)
|
||||
{
|
||||
iprintf(indent_level, "m%04lx [label=\"", PTR2ADDR(e->key));
|
||||
if (key_print_func != NULL)
|
||||
{
|
||||
key_print_func(e->key);
|
||||
}
|
||||
printf("\" xlabel=\"%04lx\"]\n", PTR2ADDR(e->key));
|
||||
}
|
||||
if (e->value != NULL)
|
||||
{
|
||||
iprintf(indent_level, "m%04lx [label=\"", PTR2ADDR(e->value));
|
||||
if (value_print_func != NULL)
|
||||
{
|
||||
value_print_func(e->value);
|
||||
}
|
||||
printf("\" xlabel=\"%04lx\"]\n", PTR2ADDR(e->value));
|
||||
}
|
||||
}
|
||||
|
||||
// Internal function to print edges from the table entry node in dot format.
|
||||
// Memory "owned" by the table is indicated by solid red lines. Memory
|
||||
// "borrowed" from the user is indicated by red dashed lines.
|
||||
static void print_key_value_edges(int indent_level, const table *t, const table_entry *e)
|
||||
{
|
||||
// Print the key edge
|
||||
if (e->key == NULL)
|
||||
{
|
||||
print_edge(indent_level, e, e->key, "k", "key", NULL);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (t->key_kill_func)
|
||||
{
|
||||
print_edge(indent_level, e, e->key, "k", "key", "color=red");
|
||||
}
|
||||
else
|
||||
{
|
||||
print_edge(indent_level, e, e->key, "k", "key", "color=red style=dashed");
|
||||
}
|
||||
}
|
||||
|
||||
// Print the value edge
|
||||
if (e->value == NULL)
|
||||
{
|
||||
print_edge(indent_level, e, e->value, "v", "value", NULL);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (t->value_kill_func)
|
||||
{
|
||||
print_edge(indent_level, e, e->value, "v", "value", "color=red");
|
||||
}
|
||||
else
|
||||
{
|
||||
print_edge(indent_level, e, e->value, "v", "value", "color=red style=dashed");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Internal function to print nodes and edges of all table entries in dot format.
|
||||
static void print_entries(int indent_level, const table *t, inspect_callback key_print_func,
|
||||
inspect_callback value_print_func)
|
||||
{
|
||||
if (t->entries == NULL)
|
||||
{
|
||||
return;
|
||||
}
|
||||
for (int i = 0; i < MAXSIZE; i++)
|
||||
{
|
||||
table_entry *e = array_1d_inspect_value(t->entries, i);
|
||||
if (e != NULL)
|
||||
{
|
||||
print_element_node(indent_level, e);
|
||||
print_key_value_nodes(indent_level, e, key_print_func, value_print_func);
|
||||
print_key_value_edges(indent_level, t, e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Create an escaped version of the input string. The most common
|
||||
// control characters - newline, horizontal tab, backslash, and double
|
||||
// quote - are replaced by their escape sequence. The returned pointer
|
||||
// must be deallocated by the caller.
|
||||
static char *escape_chars(const char *s)
|
||||
{
|
||||
int i, j;
|
||||
int escaped = 0; // The number of chars that must be escaped.
|
||||
|
||||
// Count how many chars need to be escaped, i.e. how much longer
|
||||
// the output string will be.
|
||||
for (i = escaped = 0; s[i] != '\0'; i++)
|
||||
{
|
||||
if (s[i] == '\n' || s[i] == '\t' || s[i] == '\\' || s[i] == '\"')
|
||||
{
|
||||
escaped++;
|
||||
}
|
||||
}
|
||||
// Allocate space for the escaped string. The variable i holds the input
|
||||
// length, escaped how much the string will grow.
|
||||
char *t = malloc(i + escaped + 1);
|
||||
|
||||
// Copy-and-escape loop
|
||||
for (i = j = 0; s[i] != '\0'; i++)
|
||||
{
|
||||
// Convert each control character by its escape sequence.
|
||||
// Non-control characters are copied as-is.
|
||||
switch (s[i])
|
||||
{
|
||||
case '\n':
|
||||
t[i + j] = '\\';
|
||||
t[i + j + 1] = 'n';
|
||||
j++;
|
||||
break;
|
||||
case '\t':
|
||||
t[i + j] = '\\';
|
||||
t[i + j + 1] = 't';
|
||||
j++;
|
||||
break;
|
||||
case '\\':
|
||||
t[i + j] = '\\';
|
||||
t[i + j + 1] = '\\';
|
||||
j++;
|
||||
break;
|
||||
case '\"':
|
||||
t[i + j] = '\\';
|
||||
t[i + j + 1] = '\"';
|
||||
j++;
|
||||
break;
|
||||
default:
|
||||
t[i + j] = s[i];
|
||||
break;
|
||||
}
|
||||
}
|
||||
// Terminal the output string
|
||||
t[i + j] = '\0';
|
||||
return t;
|
||||
}
|
||||
|
||||
/**
|
||||
* first_white_spc() - Return pointer to first white-space char.
|
||||
* @s: String.
|
||||
*
|
||||
* Returns: A pointer to the first white-space char in s, or NULL if none is found.
|
||||
*
|
||||
*/
|
||||
static const char *find_white_spc(const char *s)
|
||||
{
|
||||
const char *t = s;
|
||||
while (*t != '\0')
|
||||
{
|
||||
if (isspace(*t))
|
||||
{
|
||||
// We found a white-space char, return a point to it.
|
||||
return t;
|
||||
}
|
||||
// Advance to next char
|
||||
t++;
|
||||
}
|
||||
// No white-space found
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* insert_table_name() - Maybe insert the name of the table src file in the description string.
|
||||
* @s: Description string.
|
||||
*
|
||||
* Parses the description string to find of if it starts with a c file
|
||||
* name. In that case, the file name of this file is spliced into the
|
||||
* description string. The parsing is not very intelligent: If the
|
||||
* sequence ".c:" (case insensitive) is found before the first
|
||||
* white-space, the string up to and including ".c" is taken to be a c
|
||||
* file name.
|
||||
*
|
||||
* Returns: A dynamic copy of s, optionally including with the table src file name.
|
||||
*/
|
||||
static char *insert_table_name(const char *s)
|
||||
{
|
||||
// First, determine if the description string starts with a c file name
|
||||
// a) Search for the string ".c:"
|
||||
const char *dot_c = strstr(s, ".c:");
|
||||
// b) Search for the first white-space
|
||||
const char *spc = find_white_spc(s);
|
||||
|
||||
bool prefix_found;
|
||||
int output_length;
|
||||
|
||||
// If both a) and b) are found AND a) is before b, we assume that
|
||||
// s starts with a file name
|
||||
if (dot_c != NULL && spc != NULL && dot_c < spc)
|
||||
{
|
||||
// We found a match. Output string is input + 3 chars + __FILE__
|
||||
prefix_found = true;
|
||||
output_length = strlen(s) + 3 + strlen(__FILE__);
|
||||
}
|
||||
else
|
||||
{
|
||||
// No match found. Output string is just input
|
||||
prefix_found = false;
|
||||
output_length = strlen(s);
|
||||
}
|
||||
|
||||
// Allocate space for the whole string
|
||||
char *out = calloc(1, output_length + 1);
|
||||
strcpy(out, s);
|
||||
if (prefix_found)
|
||||
{
|
||||
// Overwrite the output buffer from the ":"
|
||||
strcpy(out + (dot_c - s + 2), " (");
|
||||
// Now out will be 0-terminated after "(", append the file name and ")"
|
||||
strcat(out, __FILE__);
|
||||
strcat(out, ")");
|
||||
// Finally append the input string from the : onwards
|
||||
strcat(out, dot_c + 2);
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
/**
|
||||
* table_print_internal() - Output the internal structure of the table.
|
||||
* @t: Table to print.
|
||||
* @key_print_func: Function called for each key in the table.
|
||||
* @value_print_func: Function called for each value in the table.
|
||||
* @desc: String with a description/state of the list.
|
||||
* @indent_level: Indentation level, 0 for outermost
|
||||
*
|
||||
* Iterates over the list and prints code that shows its' internal structure.
|
||||
*
|
||||
* Returns: Nothing.
|
||||
*/
|
||||
void table_print_internal(const table *t, inspect_callback key_print_func,
|
||||
inspect_callback value_print_func, const char *desc,
|
||||
int indent_level)
|
||||
{
|
||||
static int graph_number = 0;
|
||||
graph_number++;
|
||||
int il = indent_level;
|
||||
|
||||
if (indent_level == 0)
|
||||
{
|
||||
// If this is the outermost datatype, start a graph and set up defaults
|
||||
printf("digraph TABLE_%d {\n", graph_number);
|
||||
|
||||
// Specify default shape and fontname
|
||||
il++;
|
||||
iprintf(il, "node [shape=rectangle fontname=\"Courier New\"]\n");
|
||||
iprintf(il, "ranksep=0.01\n");
|
||||
iprintf(il, "subgraph cluster_nullspace {\n");
|
||||
iprintf(il + 1, "NULL\n");
|
||||
iprintf(il, "}\n");
|
||||
}
|
||||
|
||||
if (desc != NULL)
|
||||
{
|
||||
// Escape the string before printout
|
||||
char *escaped = escape_chars(desc);
|
||||
// Optionally, splice the source file name
|
||||
char *spliced = insert_table_name(escaped);
|
||||
|
||||
// Use different names on inner description nodes
|
||||
if (indent_level == 0)
|
||||
{
|
||||
iprintf(il, "description [label=\"%s\"]\n", spliced);
|
||||
}
|
||||
else
|
||||
{
|
||||
iprintf(il, "\tcluster_list_%d_description [label=\"%s\"]\n", graph_number, spliced);
|
||||
}
|
||||
// Return the memory used by the spliced and escaped strings
|
||||
free(spliced);
|
||||
free(escaped);
|
||||
}
|
||||
|
||||
if (indent_level == 0)
|
||||
{
|
||||
// Use a single "pointer" edge as a starting point for the
|
||||
// outermost datatype
|
||||
iprintf(il, "t [label=\"%04lx\" xlabel=\"t\"]\n", PTR2ADDR(t));
|
||||
iprintf(il, "t -> m%04lx\n", PTR2ADDR(t));
|
||||
}
|
||||
|
||||
if (indent_level == 0)
|
||||
{
|
||||
// Put the user nodes in userspace
|
||||
iprintf(il, "subgraph cluster_userspace { label=\"User space\"\n");
|
||||
il++;
|
||||
|
||||
// Iterate over the list to print the payload nodes
|
||||
for (int i = 0; i < MAXSIZE; i++)
|
||||
{
|
||||
print_key_value_nodes(il, array_1d_inspect_value(t->entries, i), key_print_func,
|
||||
value_print_func);
|
||||
}
|
||||
|
||||
// Close the subgraph
|
||||
il--;
|
||||
iprintf(il, "}\n");
|
||||
}
|
||||
|
||||
// Print the subgraph to surround the DList content
|
||||
iprintf(il, "subgraph cluster_table_%d { label=\"Table\"\n", graph_number);
|
||||
il++;
|
||||
|
||||
// Output the head node
|
||||
print_head_node(il, t);
|
||||
|
||||
// Output the edges from the head
|
||||
print_head_edge(il, t);
|
||||
|
||||
// Close the subgraph
|
||||
il--;
|
||||
iprintf(il, "}\n");
|
||||
|
||||
// Next, print each element stored in the list
|
||||
print_entries(il, t, key_print_func, value_print_func);
|
||||
|
||||
if (indent_level == 0)
|
||||
{
|
||||
// Termination of graph
|
||||
printf("}\n");
|
||||
}
|
||||
}
|
||||
801
OU3/arraytable.c
Normal file
801
OU3/arraytable.c
Normal file
@@ -0,0 +1,801 @@
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <ctype.h> // For isspace()
|
||||
#include <stdarg.h>
|
||||
|
||||
#include <table.h>
|
||||
#include <array_1d.h>
|
||||
|
||||
#define MAXSIZE 80000
|
||||
|
||||
/*
|
||||
* Implementation of a arraybased table implementation
|
||||
*
|
||||
* Duplicates are handled by insert, where values are overwritten.
|
||||
*
|
||||
* Authors: Marc Meunier (tfy22mmr@cs.umu.se)
|
||||
*
|
||||
*
|
||||
* Based on earlier code by: Johan Eliasson (johane@cs.umu.se).
|
||||
* Niclas Borlin (niclas@cs.umu.se)
|
||||
* Adam Dahlgren Lindstrom (dali@cs.umu.se)
|
||||
*
|
||||
* Version information:
|
||||
* v1.0 2018-02-06: First public version.
|
||||
*/
|
||||
|
||||
// ===========INTERNAL DATA TYPES ============
|
||||
|
||||
struct table
|
||||
{
|
||||
array_1d *entries; // The table entries are stored in a directed list
|
||||
compare_function *key_cmp_func;
|
||||
kill_function key_kill_func;
|
||||
kill_function value_kill_func;
|
||||
int count; // total entries in table
|
||||
};
|
||||
|
||||
typedef struct table_entry
|
||||
{
|
||||
void *key;
|
||||
void *value;
|
||||
} table_entry;
|
||||
|
||||
// ===========INTERNAL FUNCTION IMPLEMENTATIONS ============
|
||||
|
||||
/**
|
||||
* table_entry_create() - Allocate and populate a table entry.
|
||||
* @key: A pointer to a function to be used to compare keys.
|
||||
* @value: A pointer to a function (or NULL) to be called to
|
||||
* de-allocate memory for keys on remove/kill.
|
||||
*
|
||||
* Returns: A pointer to the newly created table entry.
|
||||
*/
|
||||
table_entry *table_entry_create(void *key, void *value)
|
||||
{
|
||||
// Allocate space for a table entry. Use calloc as a defensive
|
||||
// measure to ensure that all pointers are initialized to NULL.
|
||||
table_entry *e = calloc(1, sizeof(*e));
|
||||
// Populate the entry.
|
||||
e->key = key;
|
||||
e->value = value;
|
||||
// e->count = e->count + 1;
|
||||
|
||||
return e;
|
||||
}
|
||||
|
||||
/**
|
||||
* table_entry_kill() - Return the memory allocated to a table entry.
|
||||
* @e: The table entry to deallocate.
|
||||
*
|
||||
* Returns: Nothing.
|
||||
*/
|
||||
void table_entry_kill(void *v)
|
||||
{
|
||||
table_entry *e = v; // Convert the pointer (useful if debugging the code)
|
||||
|
||||
// All we need to do is to deallocate the struct.
|
||||
free(e);
|
||||
}
|
||||
|
||||
/**
|
||||
* table_empty() - Create an empty table.
|
||||
* @key_cmp_func: A pointer to a function to be used to compare keys.
|
||||
* @key_kill_func: A pointer to a function (or NULL) to be called to
|
||||
* de-allocate memory for keys on remove/kill.
|
||||
* @value_kill_func: A pointer to a function (or NULL) to be called to
|
||||
* de-allocate memory for values on remove/kill.
|
||||
*
|
||||
* Returns: Pointer to a new table.
|
||||
*/
|
||||
table *table_empty(compare_function *key_cmp_func,
|
||||
kill_function key_kill_func,
|
||||
kill_function value_kill_func)
|
||||
{
|
||||
// Allocate the table header.
|
||||
table *t = calloc(1, sizeof(table));
|
||||
// Create the list to hold the table_entry-ies.
|
||||
t->entries = array_1d_create(0, MAXSIZE - 1, NULL);
|
||||
// Store the key compare function and key/value kill functions.
|
||||
t->key_cmp_func = key_cmp_func;
|
||||
t->key_kill_func = key_kill_func;
|
||||
t->value_kill_func = value_kill_func;
|
||||
t->count = 0;
|
||||
|
||||
return t;
|
||||
}
|
||||
|
||||
/**
|
||||
* table_is_empty() - Check if a table is empty.
|
||||
* @table: Table to check.
|
||||
*
|
||||
* Returns: True if table contains no key/value pairs, false otherwise.
|
||||
*/
|
||||
bool table_is_empty(const table *t)
|
||||
{
|
||||
return (t->count == 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* table_lookup() - Look up a given key in a table.
|
||||
* @table: Table to inspect.
|
||||
* @key: Key to look up.
|
||||
*
|
||||
* Returns: The value corresponding to a given key, or NULL if the key
|
||||
* is not found in the table. If the table contains duplicate keys,
|
||||
* the value that was latest inserted will be returned.
|
||||
*/
|
||||
void *table_lookup(const table *t, const void *key)
|
||||
{
|
||||
int inserted_values = 0;
|
||||
// loop over array
|
||||
for (int i = 0; i < MAXSIZE; i++)
|
||||
{
|
||||
// finds populated entries
|
||||
if ((array_1d_inspect_value(t->entries, i) != NULL))
|
||||
{
|
||||
inserted_values++;
|
||||
table_entry *e;
|
||||
e = array_1d_inspect_value(t->entries, i);
|
||||
// checks if table key corresponds with seach key
|
||||
if (e != NULL && t->key_cmp_func(e->key, key) == 0)
|
||||
{
|
||||
return e->value;
|
||||
}
|
||||
}
|
||||
// will quit loop if no more values are left to check
|
||||
if (inserted_values == t->count)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
// No match found. Return NULL.
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* table_insert() - Add a key/value pair to a table.
|
||||
* @table: Table to manipulate.
|
||||
* @key: A pointer to the key value.
|
||||
* @value: A pointer to the value value.
|
||||
*
|
||||
* Insert the key/value pair into the table. No test is performed to
|
||||
* check if key is a duplicate. table_lookup() will return the latest
|
||||
* added value for a duplicate key. table_remove() will remove all
|
||||
* duplicates for a given key.
|
||||
*
|
||||
* Returns: Nothing.
|
||||
*/
|
||||
void table_insert(table *t, void *key, void *value)
|
||||
{
|
||||
// Allocate the key/value structure.
|
||||
table_entry *e = table_entry_create(key, value);
|
||||
// inserts value at first empty position if key is new
|
||||
if (table_lookup(t, key) == NULL)
|
||||
{
|
||||
|
||||
for (int i = 0; i < MAXSIZE; i++)
|
||||
{
|
||||
// finds first empty slot
|
||||
if (!(array_1d_inspect_value(t->entries, i) != NULL))
|
||||
{
|
||||
array_1d_set_value(t->entries, e, i);
|
||||
t->count++;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
// find place of old key and overwrites.
|
||||
else
|
||||
{
|
||||
for (int i = 0; i < MAXSIZE; i++)
|
||||
{
|
||||
table_entry *f = array_1d_inspect_value(t->entries, i);
|
||||
// find matching key
|
||||
if (f != NULL && t->key_cmp_func(f->key, key) == 0)
|
||||
{
|
||||
// kills memory of old value and key
|
||||
if (f != NULL)
|
||||
{
|
||||
if (t->value_kill_func != NULL)
|
||||
{
|
||||
t->value_kill_func(f->value);
|
||||
}
|
||||
if (t->key_kill_func != NULL)
|
||||
{
|
||||
t->key_kill_func(f->key);
|
||||
}
|
||||
table_entry_kill(f);
|
||||
}
|
||||
// insert new value
|
||||
array_1d_set_value(t->entries, e, i);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* table_choose_key() - Return an arbitrary key.
|
||||
* @t: Table to inspect.
|
||||
*
|
||||
* Return an arbitrary key stored in the table. Can be used together
|
||||
* with table_remove() to deconstruct the table. Returns NULL for an
|
||||
* empty table.
|
||||
*
|
||||
* Returns: An arbitrary key stored in the table.
|
||||
*/
|
||||
void *table_choose_key(const table *t)
|
||||
{
|
||||
// find first entrie in table
|
||||
for (int i = 0; i < MAXSIZE - 1; i++)
|
||||
{
|
||||
// if contain entry
|
||||
if ((array_1d_inspect_value(t->entries, i) != NULL))
|
||||
{
|
||||
table_entry *e;
|
||||
e = array_1d_inspect_value(t->entries, i);
|
||||
return e->key;
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* table_remove() - Remove a key/value pair in the table.
|
||||
* @table: Table to manipulate.
|
||||
* @key: Key for which to remove pair.
|
||||
*
|
||||
* Any matching duplicates will be removed. Will call any kill
|
||||
* functions set for keys/values. Does nothing if key is not found in
|
||||
* the table.
|
||||
*
|
||||
* Returns: Nothing.
|
||||
*/
|
||||
void table_remove(table *t, const void *key)
|
||||
{
|
||||
int inserted_values = 0;
|
||||
for (int i = 0; i < MAXSIZE; i++)
|
||||
{
|
||||
if ((array_1d_inspect_value(t->entries, i) != NULL))
|
||||
{
|
||||
inserted_values++;
|
||||
table_entry *e = array_1d_inspect_value(t->entries, i);
|
||||
// Find matching entry
|
||||
if (e != NULL && t->key_cmp_func(e->key, key) == 0)
|
||||
{
|
||||
// removes memory from entries
|
||||
if (e != NULL)
|
||||
{
|
||||
if (t->value_kill_func != NULL)
|
||||
{
|
||||
t->value_kill_func(e->value);
|
||||
}
|
||||
if (t->key_kill_func != NULL)
|
||||
{
|
||||
t->key_kill_func(e->key);
|
||||
}
|
||||
}
|
||||
table_entry_kill(e);
|
||||
// set values to NULL
|
||||
array_1d_set_value(t->entries, NULL, i);
|
||||
t->count--;
|
||||
break;
|
||||
}
|
||||
}
|
||||
// will quit loop if no more values are left to check
|
||||
if (inserted_values == t->count)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* table_kill() - Destroy a table.
|
||||
* @table: Table to destroy.
|
||||
*
|
||||
* Return all dynamic memory used by the table and its elements. If a
|
||||
* kill_func was registered for keys and/or values at table creation,
|
||||
* it is called each element to kill any user-allocated memory
|
||||
* occupied by the element values.
|
||||
*
|
||||
* Returns: Nothing.
|
||||
*/
|
||||
void table_kill(table *t)
|
||||
{
|
||||
for (int i = 0; i < MAXSIZE; i++)
|
||||
{
|
||||
// Inspect the key/value pair.
|
||||
table_entry *e = array_1d_inspect_value(t->entries, i);
|
||||
// Kill key and/or value
|
||||
if (e != NULL)
|
||||
{
|
||||
if (t->key_kill_func != NULL)
|
||||
{
|
||||
t->key_kill_func(e->key);
|
||||
}
|
||||
if (t->value_kill_func != NULL)
|
||||
{
|
||||
t->value_kill_func(e->value);
|
||||
}
|
||||
// Deallocate the table entry structure.
|
||||
table_entry_kill(e);
|
||||
}
|
||||
}
|
||||
|
||||
// Kill what's left of the list...
|
||||
array_1d_kill(t->entries);
|
||||
// ...and the table struct.
|
||||
free(t);
|
||||
}
|
||||
|
||||
/**
|
||||
* table_print() - Print the given table.
|
||||
* @t: Table to print.
|
||||
* @print_func: Function called for each key/value pair in the table.
|
||||
*
|
||||
* Iterates over the key/value pairs in the table and prints them.
|
||||
* Will print all stored elements, including duplicates.
|
||||
*
|
||||
* Returns: Nothing.
|
||||
*/
|
||||
void table_print(const table *t, inspect_callback_pair print_func)
|
||||
{
|
||||
// Iterate over all elements. Call print_func on keys/values.
|
||||
for (int i = 0; i < MAXSIZE; i++)
|
||||
{
|
||||
table_entry *e = array_1d_inspect_value(t->entries, i);
|
||||
// Call print_func
|
||||
if (e != NULL)
|
||||
{
|
||||
print_func(e->key, e->value);
|
||||
// pos = dlist_next(t->entries, i);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ===========INTERNAL FUNCTIONS USED BY list_print_internal ============
|
||||
|
||||
// The functions below output code in the dot language, used by
|
||||
// GraphViz. For documention of the dot language, see graphviz.org.
|
||||
|
||||
/**
|
||||
* indent() - Output indentation string.
|
||||
* @n: Indentation level.
|
||||
*
|
||||
* Print n tab characters.
|
||||
*
|
||||
* Returns: Nothing.
|
||||
*/
|
||||
static void indent(int n)
|
||||
{
|
||||
for (int i = 0; i < n; i++)
|
||||
{
|
||||
printf("\t");
|
||||
}
|
||||
}
|
||||
/**
|
||||
* iprintf(...) - Indent and print.
|
||||
* @n: Indentation level
|
||||
* @...: printf arguments
|
||||
*
|
||||
* Print n tab characters and calls printf.
|
||||
*
|
||||
* Returns: Nothing.
|
||||
*/
|
||||
static void iprintf(int n, const char *fmt, ...)
|
||||
{
|
||||
// Indent...
|
||||
indent(n);
|
||||
// ...and call printf
|
||||
va_list args;
|
||||
va_start(args, fmt);
|
||||
vprintf(fmt, args);
|
||||
va_end(args);
|
||||
}
|
||||
|
||||
/**
|
||||
* print_edge() - Print a edge between two addresses.
|
||||
* @from: The address of the start of the edge. Should be non-NULL.
|
||||
* @to: The address of the destination for the edge, including NULL.
|
||||
* @port: The name of the port on the source node, or NULL.
|
||||
* @label: The label for the edge, or NULL.
|
||||
* @options: A string with other edge options, or NULL.
|
||||
*
|
||||
* Print an edge from port PORT on node FROM to TO with label
|
||||
* LABEL. If to is NULL, the destination is the NULL node, otherwise a
|
||||
* memory node. If the port is NULL, the edge starts at the node, not
|
||||
* a specific port on it. If label is NULL, no label is used. The
|
||||
* options string, if non-NULL, is printed before the label.
|
||||
*
|
||||
* Returns: Nothing.
|
||||
*/
|
||||
static void print_edge(int indent_level, const void *from, const void *to, const char *port,
|
||||
const char *label, const char *options)
|
||||
{
|
||||
indent(indent_level);
|
||||
if (port)
|
||||
{
|
||||
printf("m%04lx:%s -> ", PTR2ADDR(from), port);
|
||||
}
|
||||
else
|
||||
{
|
||||
printf("m%04lx -> ", PTR2ADDR(from));
|
||||
}
|
||||
if (to == NULL)
|
||||
{
|
||||
printf("NULL");
|
||||
}
|
||||
else
|
||||
{
|
||||
printf("m%04lx", PTR2ADDR(to));
|
||||
}
|
||||
printf(" [");
|
||||
if (options != NULL)
|
||||
{
|
||||
printf("%s", options);
|
||||
}
|
||||
if (label != NULL)
|
||||
{
|
||||
printf(" label=\"%s\"", label);
|
||||
}
|
||||
printf("]\n");
|
||||
}
|
||||
|
||||
/**
|
||||
* print_head_node() - Print a node corresponding to the table struct.
|
||||
* @indent_level: Indentation level.
|
||||
* @t: Table to inspect.
|
||||
*
|
||||
* Returns: Nothing.
|
||||
*/
|
||||
static void print_head_node(int indent_level, const table *t)
|
||||
{
|
||||
iprintf(indent_level, "m%04lx [shape=record "
|
||||
"label=\"<e>entries\\n%04lx|cmp\\n%04lx|key_kill\\n%04lx|value_kill\\n%04lx\"]\n",
|
||||
PTR2ADDR(t), PTR2ADDR(t->entries), PTR2ADDR(t->key_cmp_func),
|
||||
PTR2ADDR(t->key_kill_func), PTR2ADDR(t->value_kill_func));
|
||||
}
|
||||
|
||||
// Internal function to print the head--entries edge in dot format.
|
||||
static void print_head_edge(int indent_level, const table *t)
|
||||
{
|
||||
print_edge(indent_level, t, t->entries, "e", "entries", NULL);
|
||||
}
|
||||
|
||||
// Internal function to print the table entry node in dot format.
|
||||
static void print_element_node(int indent_level, const table_entry *e)
|
||||
{
|
||||
iprintf(indent_level, "m%04lx [shape=record label=\"<k>key\\n%04lx|<v>value\\n%04lx\"]\n",
|
||||
PTR2ADDR(e), PTR2ADDR(e->key), PTR2ADDR(e->value));
|
||||
}
|
||||
|
||||
// Internal function to print the table entry node in dot format.
|
||||
static void print_key_value_nodes(int indent_level, const table_entry *e,
|
||||
inspect_callback key_print_func,
|
||||
inspect_callback value_print_func)
|
||||
{
|
||||
if (e->key != NULL)
|
||||
{
|
||||
iprintf(indent_level, "m%04lx [label=\"", PTR2ADDR(e->key));
|
||||
if (key_print_func != NULL)
|
||||
{
|
||||
key_print_func(e->key);
|
||||
}
|
||||
printf("\" xlabel=\"%04lx\"]\n", PTR2ADDR(e->key));
|
||||
}
|
||||
if (e->value != NULL)
|
||||
{
|
||||
iprintf(indent_level, "m%04lx [label=\"", PTR2ADDR(e->value));
|
||||
if (value_print_func != NULL)
|
||||
{
|
||||
value_print_func(e->value);
|
||||
}
|
||||
printf("\" xlabel=\"%04lx\"]\n", PTR2ADDR(e->value));
|
||||
}
|
||||
}
|
||||
|
||||
// Internal function to print edges from the table entry node in dot format.
|
||||
// Memory "owned" by the table is indicated by solid red lines. Memory
|
||||
// "borrowed" from the user is indicated by red dashed lines.
|
||||
static void print_key_value_edges(int indent_level, const table *t, const table_entry *e)
|
||||
{
|
||||
// Print the key edge
|
||||
if (e->key == NULL)
|
||||
{
|
||||
print_edge(indent_level, e, e->key, "k", "key", NULL);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (t->key_kill_func)
|
||||
{
|
||||
print_edge(indent_level, e, e->key, "k", "key", "color=red");
|
||||
}
|
||||
else
|
||||
{
|
||||
print_edge(indent_level, e, e->key, "k", "key", "color=red style=dashed");
|
||||
}
|
||||
}
|
||||
|
||||
// Print the value edge
|
||||
if (e->value == NULL)
|
||||
{
|
||||
print_edge(indent_level, e, e->value, "v", "value", NULL);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (t->value_kill_func)
|
||||
{
|
||||
print_edge(indent_level, e, e->value, "v", "value", "color=red");
|
||||
}
|
||||
else
|
||||
{
|
||||
print_edge(indent_level, e, e->value, "v", "value", "color=red style=dashed");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Internal function to print nodes and edges of all table entries in dot format.
|
||||
static void print_entries(int indent_level, const table *t, inspect_callback key_print_func,
|
||||
inspect_callback value_print_func)
|
||||
{
|
||||
if (t->entries == NULL)
|
||||
{
|
||||
return;
|
||||
}
|
||||
for (int i = 0; i < MAXSIZE; i++)
|
||||
{
|
||||
table_entry *e = array_1d_inspect_value(t->entries, i);
|
||||
if (e != NULL)
|
||||
{
|
||||
print_element_node(indent_level, e);
|
||||
print_key_value_nodes(indent_level, e, key_print_func, value_print_func);
|
||||
print_key_value_edges(indent_level, t, e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Create an escaped version of the input string. The most common
|
||||
// control characters - newline, horizontal tab, backslash, and double
|
||||
// quote - are replaced by their escape sequence. The returned pointer
|
||||
// must be deallocated by the caller.
|
||||
static char *escape_chars(const char *s)
|
||||
{
|
||||
int i, j;
|
||||
int escaped = 0; // The number of chars that must be escaped.
|
||||
|
||||
// Count how many chars need to be escaped, i.e. how much longer
|
||||
// the output string will be.
|
||||
for (i = escaped = 0; s[i] != '\0'; i++)
|
||||
{
|
||||
if (s[i] == '\n' || s[i] == '\t' || s[i] == '\\' || s[i] == '\"')
|
||||
{
|
||||
escaped++;
|
||||
}
|
||||
}
|
||||
// Allocate space for the escaped string. The variable i holds the input
|
||||
// length, escaped how much the string will grow.
|
||||
char *t = malloc(i + escaped + 1);
|
||||
|
||||
// Copy-and-escape loop
|
||||
for (i = j = 0; s[i] != '\0'; i++)
|
||||
{
|
||||
// Convert each control character by its escape sequence.
|
||||
// Non-control characters are copied as-is.
|
||||
switch (s[i])
|
||||
{
|
||||
case '\n':
|
||||
t[i + j] = '\\';
|
||||
t[i + j + 1] = 'n';
|
||||
j++;
|
||||
break;
|
||||
case '\t':
|
||||
t[i + j] = '\\';
|
||||
t[i + j + 1] = 't';
|
||||
j++;
|
||||
break;
|
||||
case '\\':
|
||||
t[i + j] = '\\';
|
||||
t[i + j + 1] = '\\';
|
||||
j++;
|
||||
break;
|
||||
case '\"':
|
||||
t[i + j] = '\\';
|
||||
t[i + j + 1] = '\"';
|
||||
j++;
|
||||
break;
|
||||
default:
|
||||
t[i + j] = s[i];
|
||||
break;
|
||||
}
|
||||
}
|
||||
// Terminal the output string
|
||||
t[i + j] = '\0';
|
||||
return t;
|
||||
}
|
||||
|
||||
/**
|
||||
* first_white_spc() - Return pointer to first white-space char.
|
||||
* @s: String.
|
||||
*
|
||||
* Returns: A pointer to the first white-space char in s, or NULL if none is found.
|
||||
*
|
||||
*/
|
||||
static const char *find_white_spc(const char *s)
|
||||
{
|
||||
const char *t = s;
|
||||
while (*t != '\0')
|
||||
{
|
||||
if (isspace(*t))
|
||||
{
|
||||
// We found a white-space char, return a point to it.
|
||||
return t;
|
||||
}
|
||||
// Advance to next char
|
||||
t++;
|
||||
}
|
||||
// No white-space found
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* insert_table_name() - Maybe insert the name of the table src file in the description string.
|
||||
* @s: Description string.
|
||||
*
|
||||
* Parses the description string to find of if it starts with a c file
|
||||
* name. In that case, the file name of this file is spliced into the
|
||||
* description string. The parsing is not very intelligent: If the
|
||||
* sequence ".c:" (case insensitive) is found before the first
|
||||
* white-space, the string up to and including ".c" is taken to be a c
|
||||
* file name.
|
||||
*
|
||||
* Returns: A dynamic copy of s, optionally including with the table src file name.
|
||||
*/
|
||||
static char *insert_table_name(const char *s)
|
||||
{
|
||||
// First, determine if the description string starts with a c file name
|
||||
// a) Search for the string ".c:"
|
||||
const char *dot_c = strstr(s, ".c:");
|
||||
// b) Search for the first white-space
|
||||
const char *spc = find_white_spc(s);
|
||||
|
||||
bool prefix_found;
|
||||
int output_length;
|
||||
|
||||
// If both a) and b) are found AND a) is before b, we assume that
|
||||
// s starts with a file name
|
||||
if (dot_c != NULL && spc != NULL && dot_c < spc)
|
||||
{
|
||||
// We found a match. Output string is input + 3 chars + __FILE__
|
||||
prefix_found = true;
|
||||
output_length = strlen(s) + 3 + strlen(__FILE__);
|
||||
}
|
||||
else
|
||||
{
|
||||
// No match found. Output string is just input
|
||||
prefix_found = false;
|
||||
output_length = strlen(s);
|
||||
}
|
||||
|
||||
// Allocate space for the whole string
|
||||
char *out = calloc(1, output_length + 1);
|
||||
strcpy(out, s);
|
||||
if (prefix_found)
|
||||
{
|
||||
// Overwrite the output buffer from the ":"
|
||||
strcpy(out + (dot_c - s + 2), " (");
|
||||
// Now out will be 0-terminated after "(", append the file name and ")"
|
||||
strcat(out, __FILE__);
|
||||
strcat(out, ")");
|
||||
// Finally append the input string from the : onwards
|
||||
strcat(out, dot_c + 2);
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
/**
|
||||
* table_print_internal() - Output the internal structure of the table.
|
||||
* @t: Table to print.
|
||||
* @key_print_func: Function called for each key in the table.
|
||||
* @value_print_func: Function called for each value in the table.
|
||||
* @desc: String with a description/state of the list.
|
||||
* @indent_level: Indentation level, 0 for outermost
|
||||
*
|
||||
* Iterates over the list and prints code that shows its' internal structure.
|
||||
*
|
||||
* Returns: Nothing.
|
||||
*/
|
||||
void table_print_internal(const table *t, inspect_callback key_print_func,
|
||||
inspect_callback value_print_func, const char *desc,
|
||||
int indent_level)
|
||||
{
|
||||
static int graph_number = 0;
|
||||
graph_number++;
|
||||
int il = indent_level;
|
||||
|
||||
if (indent_level == 0)
|
||||
{
|
||||
// If this is the outermost datatype, start a graph and set up defaults
|
||||
printf("digraph TABLE_%d {\n", graph_number);
|
||||
|
||||
// Specify default shape and fontname
|
||||
il++;
|
||||
iprintf(il, "node [shape=rectangle fontname=\"Courier New\"]\n");
|
||||
iprintf(il, "ranksep=0.01\n");
|
||||
iprintf(il, "subgraph cluster_nullspace {\n");
|
||||
iprintf(il + 1, "NULL\n");
|
||||
iprintf(il, "}\n");
|
||||
}
|
||||
|
||||
if (desc != NULL)
|
||||
{
|
||||
// Escape the string before printout
|
||||
char *escaped = escape_chars(desc);
|
||||
// Optionally, splice the source file name
|
||||
char *spliced = insert_table_name(escaped);
|
||||
|
||||
// Use different names on inner description nodes
|
||||
if (indent_level == 0)
|
||||
{
|
||||
iprintf(il, "description [label=\"%s\"]\n", spliced);
|
||||
}
|
||||
else
|
||||
{
|
||||
iprintf(il, "\tcluster_list_%d_description [label=\"%s\"]\n", graph_number, spliced);
|
||||
}
|
||||
// Return the memory used by the spliced and escaped strings
|
||||
free(spliced);
|
||||
free(escaped);
|
||||
}
|
||||
|
||||
if (indent_level == 0)
|
||||
{
|
||||
// Use a single "pointer" edge as a starting point for the
|
||||
// outermost datatype
|
||||
iprintf(il, "t [label=\"%04lx\" xlabel=\"t\"]\n", PTR2ADDR(t));
|
||||
iprintf(il, "t -> m%04lx\n", PTR2ADDR(t));
|
||||
}
|
||||
|
||||
if (indent_level == 0)
|
||||
{
|
||||
// Put the user nodes in userspace
|
||||
iprintf(il, "subgraph cluster_userspace { label=\"User space\"\n");
|
||||
il++;
|
||||
|
||||
// Iterate over the list to print the payload nodes
|
||||
for (int i = 0; i < MAXSIZE; i++)
|
||||
{
|
||||
print_key_value_nodes(il, array_1d_inspect_value(t->entries, i), key_print_func,
|
||||
value_print_func);
|
||||
}
|
||||
|
||||
// Close the subgraph
|
||||
il--;
|
||||
iprintf(il, "}\n");
|
||||
}
|
||||
|
||||
// Print the subgraph to surround the DList content
|
||||
iprintf(il, "subgraph cluster_table_%d { label=\"Table\"\n", graph_number);
|
||||
il++;
|
||||
|
||||
// Output the head node
|
||||
print_head_node(il, t);
|
||||
|
||||
// Output the edges from the head
|
||||
print_head_edge(il, t);
|
||||
|
||||
// Close the subgraph
|
||||
il--;
|
||||
iprintf(il, "}\n");
|
||||
|
||||
// Next, print each element stored in the list
|
||||
print_entries(il, t, key_print_func, value_print_func);
|
||||
|
||||
if (indent_level == 0)
|
||||
{
|
||||
// Termination of graph
|
||||
printf("}\n");
|
||||
}
|
||||
}
|
||||
BIN
OU3/arraytabletest
Executable file
BIN
OU3/arraytabletest
Executable file
Binary file not shown.
BIN
OU3/arraytabletest (conflicted copy 2025-05-08 202741)
Executable file
BIN
OU3/arraytabletest (conflicted copy 2025-05-08 202741)
Executable file
Binary file not shown.
150
OU3/atiming.txt
Normal file
150
OU3/atiming.txt
Normal file
@@ -0,0 +1,150 @@
|
||||
1, 10000, 621
|
||||
2, 10000, 322
|
||||
3, 10000, 933
|
||||
4, 10000, 464
|
||||
5, 10000, 464
|
||||
1, 10000, 619
|
||||
2, 10000, 334
|
||||
3, 10000, 934
|
||||
4, 10000, 472
|
||||
5, 10000, 471
|
||||
1, 10000, 617
|
||||
2, 10000, 340
|
||||
3, 10000, 946
|
||||
4, 10000, 476
|
||||
5, 10000, 475
|
||||
1, 10000, 620
|
||||
2, 10000, 339
|
||||
3, 10000, 948
|
||||
4, 10000, 475
|
||||
5, 10000, 470
|
||||
1, 10000, 618
|
||||
2, 10000, 335
|
||||
3, 10000, 916
|
||||
4, 10000, 474
|
||||
5, 10000, 462
|
||||
1, 10000, 608
|
||||
2, 10000, 332
|
||||
3, 10000, 935
|
||||
4, 10000, 458
|
||||
5, 10000, 476
|
||||
1, 13000, 1014
|
||||
2, 13000, 579
|
||||
3, 13000, 1542
|
||||
4, 13000, 759
|
||||
5, 13000, 771
|
||||
1, 13000, 1039
|
||||
2, 13000, 582
|
||||
3, 13000, 1547
|
||||
4, 13000, 751
|
||||
5, 13000, 777
|
||||
1, 13000, 1025
|
||||
2, 13000, 589
|
||||
3, 13000, 1553
|
||||
4, 13000, 769
|
||||
5, 13000, 784
|
||||
1, 13000, 1045
|
||||
2, 13000, 604
|
||||
3, 13000, 1578
|
||||
4, 13000, 760
|
||||
5, 13000, 766
|
||||
1, 13000, 1019
|
||||
2, 13000, 585
|
||||
3, 13000, 1533
|
||||
4, 13000, 756
|
||||
5, 13000, 765
|
||||
1, 13000, 1006
|
||||
2, 13000, 579
|
||||
3, 13000, 1525
|
||||
4, 13000, 778
|
||||
5, 13000, 791
|
||||
1, 16000, 1540
|
||||
2, 16000, 921
|
||||
3, 16000, 2319
|
||||
4, 16000, 1155
|
||||
5, 16000, 1159
|
||||
1, 16000, 1535
|
||||
2, 16000, 920
|
||||
3, 16000, 2320
|
||||
4, 16000, 1155
|
||||
5, 16000, 1156
|
||||
1, 16000, 1540
|
||||
2, 16000, 915
|
||||
3, 16000, 2311
|
||||
4, 16000, 1158
|
||||
5, 16000, 1157
|
||||
1, 16000, 1536
|
||||
2, 16000, 918
|
||||
3, 16000, 2344
|
||||
4, 16000, 1180
|
||||
5, 16000, 1171
|
||||
1, 16000, 1573
|
||||
2, 16000, 931
|
||||
3, 16000, 2349
|
||||
4, 16000, 1177
|
||||
5, 16000, 1184
|
||||
1, 16000, 1541
|
||||
2, 16000, 932
|
||||
3, 16000, 2348
|
||||
4, 16000, 1172
|
||||
5, 16000, 1145
|
||||
1, 19000, 2173
|
||||
2, 19000, 1469
|
||||
3, 19000, 3265
|
||||
4, 19000, 1628
|
||||
5, 19000, 1680
|
||||
1, 19000, 2192
|
||||
2, 19000, 1435
|
||||
3, 19000, 3269
|
||||
4, 19000, 1647
|
||||
5, 19000, 1638
|
||||
1, 19000, 2217
|
||||
2, 19000, 1460
|
||||
3, 19000, 3256
|
||||
4, 19000, 1621
|
||||
5, 19000, 1624
|
||||
1, 19000, 2162
|
||||
2, 19000, 1431
|
||||
3, 19000, 3310
|
||||
4, 19000, 1651
|
||||
5, 19000, 1658
|
||||
1, 19000, 2183
|
||||
2, 19000, 1433
|
||||
3, 19000, 3291
|
||||
4, 19000, 1614
|
||||
5, 19000, 1616
|
||||
1, 19000, 2147
|
||||
2, 19000, 1415
|
||||
3, 19000, 3301
|
||||
4, 19000, 1608
|
||||
5, 19000, 1620
|
||||
1, 22000, 2944
|
||||
2, 22000, 1847
|
||||
3, 22000, 4449
|
||||
4, 22000, 2233
|
||||
5, 22000, 2213
|
||||
1, 22000, 2940
|
||||
2, 22000, 1856
|
||||
3, 22000, 4426
|
||||
4, 22000, 2209
|
||||
5, 22000, 2201
|
||||
1, 22000, 2889
|
||||
2, 22000, 1803
|
||||
3, 22000, 4385
|
||||
4, 22000, 2215
|
||||
5, 22000, 2190
|
||||
1, 22000, 2876
|
||||
2, 22000, 1834
|
||||
3, 22000, 4430
|
||||
4, 22000, 2212
|
||||
5, 22000, 2221
|
||||
1, 22000, 2957
|
||||
2, 22000, 1814
|
||||
3, 22000, 4498
|
||||
4, 22000, 2174
|
||||
5, 22000, 2175
|
||||
1, 22000, 2860
|
||||
2, 22000, 1792
|
||||
3, 22000, 4283
|
||||
4, 22000, 2159
|
||||
5, 22000, 2130
|
||||
1
OU3/changes (conflicted copy 2025-05-07 220750).txt
Normal file
1
OU3/changes (conflicted copy 2025-05-07 220750).txt
Normal file
@@ -0,0 +1 @@
|
||||
#
|
||||
4
OU3/changes.txt
Normal file
4
OU3/changes.txt
Normal file
@@ -0,0 +1,4 @@
|
||||
2025-05-16
|
||||
Lade till kommentar för tillägget i sturct table i arraytable implementationen.
|
||||
|
||||
Fixat text i rapport som stack ut utanför sidan.
|
||||
6
OU3/compile.sh
Normal file
6
OU3/compile.sh
Normal file
@@ -0,0 +1,6 @@
|
||||
#!/bin/bash
|
||||
gcc -Wall -I include/ -g -o tabletest tabletest.c src/table/table2.c src/dlist/dlist.c
|
||||
gcc -Wall -I include/ -g -o mtftabletest tabletest.c mtftable.c src/dlist/dlist.c
|
||||
gcc -Wall -I include/ -g -o arraytabletest tabletest.c arraytable.c src/array_1d/array_1d.c
|
||||
#gcc -Wall -I include/ -g -o arraytabletest src/table/table_mwe1.c arraytable.c src/array_1d/array_1d.c
|
||||
|
||||
2
OU3/compile_print.sh
Normal file
2
OU3/compile_print.sh
Normal file
@@ -0,0 +1,2 @@
|
||||
#!/bin/bash
|
||||
gcc -Wall -I include/ -g -o mtftableprint tableprint.c mtftable.c src/dlist/dlist.c
|
||||
1930
OU3/fig1.eps
Normal file
1930
OU3/fig1.eps
Normal file
File diff suppressed because it is too large
Load Diff
BIN
OU3/fig1.pdf
Normal file
BIN
OU3/fig1.pdf
Normal file
Binary file not shown.
1343
OU3/fig2.eps
Normal file
1343
OU3/fig2.eps
Normal file
File diff suppressed because it is too large
Load Diff
BIN
OU3/fig2.pdf
Normal file
BIN
OU3/fig2.pdf
Normal file
Binary file not shown.
3133
OU3/fig3.eps
Normal file
3133
OU3/fig3.eps
Normal file
File diff suppressed because it is too large
Load Diff
BIN
OU3/fig3.pdf
Normal file
BIN
OU3/fig3.pdf
Normal file
Binary file not shown.
150
OU3/include/array_1d.h
Normal file
150
OU3/include/array_1d.h
Normal file
@@ -0,0 +1,150 @@
|
||||
#ifndef __ARRAY_1D_H
|
||||
#define __ARRAY_1D_H
|
||||
|
||||
#include <stdbool.h>
|
||||
#include "util.h"
|
||||
|
||||
/*
|
||||
* Declaration of a generic 1D array for the "Datastructures and
|
||||
* algorithms" courses at the Department of Computing Science, Umea
|
||||
* University. The array stores void pointers, so it can be used to
|
||||
* store all types of values. After use, the function array_kill must
|
||||
* be called to de-allocate the dynamic memory used by the array
|
||||
* itself. The de-allocation of any dynamic memory allocated for the
|
||||
* element values is the responsibility of the user of the array,
|
||||
* unless a kill_function is registered in array_create.
|
||||
*
|
||||
* An element value of NULL is considered to be "no" value.
|
||||
*
|
||||
* Authors: Niclas Borlin (niclas@cs.umu.se)
|
||||
*
|
||||
* Based on earlier code by: Johan Eliasson (johane@cs.umu.se).
|
||||
*
|
||||
* Version information:
|
||||
* v1.0 2018-01-28: First public version.
|
||||
* v1.5 2024-03-13: Renamed free_* stuff to kill_*. Converted to 4-tabs.
|
||||
* v2.0 2024-05-10: Added print_internal.
|
||||
*/
|
||||
|
||||
// ==========PUBLIC DATA TYPES============
|
||||
|
||||
// List type.
|
||||
typedef struct array_1d array_1d;
|
||||
|
||||
// ==========DATA STRUCTURE INTERFACE==========
|
||||
|
||||
/**
|
||||
* array_1d_create() - Create an array without values.
|
||||
* @lo: low index limit.
|
||||
* @hi: high index limit.
|
||||
* @kill_func: A pointer to a function (or NULL) to be called to
|
||||
* de-allocate memory on remove/kill.
|
||||
*
|
||||
* The index limits are inclusive, i.e. all indices i such that low <=
|
||||
* i <= high are defined.
|
||||
*
|
||||
* Returns: A pointer to the new array, or NULL if not enough memory
|
||||
* was available.
|
||||
*/
|
||||
array_1d *array_1d_create(int lo, int hi, kill_function kill_func);
|
||||
|
||||
/**
|
||||
* array_1d_low() - Return the low index limit for the array.
|
||||
* @a: array to inspect.
|
||||
*
|
||||
* Returns: The low index limit.
|
||||
*/
|
||||
int array_1d_low(const array_1d *a);
|
||||
|
||||
/**
|
||||
* array_1d_high() - Return the high index limit for the array.
|
||||
* @a: array to inspect.
|
||||
*
|
||||
* Returns: The high index limit.
|
||||
*/
|
||||
int array_1d_high(const array_1d *a);
|
||||
|
||||
/**
|
||||
* array_1d_inspect_value() - Inspect a value at a given array position.
|
||||
* @a: array to inspect.
|
||||
* @i: index of position to inspect.
|
||||
*
|
||||
* Returns: The element value at the specified position. The result is
|
||||
* undefined if no value are stored at that position.
|
||||
*/
|
||||
void *array_1d_inspect_value(const array_1d *a, int i);
|
||||
|
||||
/**
|
||||
* array_1d_has_value() - Check if a value is set at a given array position.
|
||||
* @a: array to inspect.
|
||||
* @i: index of position to inspect.
|
||||
*
|
||||
* Returns: True if a value is set at the specified position, otherwise false.
|
||||
*/
|
||||
bool array_1d_has_value(const array_1d *a, int i);
|
||||
|
||||
/**
|
||||
* array_1d_set_value() - Set a value at a given array position.
|
||||
* @a: array to modify.
|
||||
* @v: value to set element to, or NULL to clear value.
|
||||
* @i: index of position to modify.
|
||||
*
|
||||
* If the old element value is non-NULL, calls kill_func if it was
|
||||
* specified at array creation.
|
||||
*
|
||||
* Returns: Nothing.
|
||||
*/
|
||||
void array_1d_set_value(array_1d *a, void *v, int i);
|
||||
|
||||
/**
|
||||
* array_1d_kill() - Return memory allocated by array.
|
||||
* @a: array to kill.
|
||||
*
|
||||
* Iterates over all elements. If kill_func was specified at array
|
||||
* creation, calls it for every non-NULL element value.
|
||||
*
|
||||
* Returns: Nothing.
|
||||
*/
|
||||
void array_1d_kill(array_1d *a);
|
||||
|
||||
/**
|
||||
* array_1d_print() - Iterate over the array element and print their values.
|
||||
* @a: Array to inspect.
|
||||
* @print_func: Function called for each non-NULL element.
|
||||
*
|
||||
* Iterates over each position in the array. Calls print_func for each
|
||||
* non-NULL value.
|
||||
*
|
||||
* Returns: Nothing.
|
||||
*/
|
||||
void array_1d_print(const array_1d * l, inspect_callback print_func);
|
||||
|
||||
/**
|
||||
* array_1d_print_internal() - Print the internal structure of the array in dot format.
|
||||
* @a: Array to inspect.
|
||||
* @print_func: Function called for each element value.
|
||||
* @desc: String with a description/state of the array, or NULL for no description.
|
||||
* @indent_level: Indentation level, 0 for outermost
|
||||
*
|
||||
* Iterates over the array and outputs dot code that shows the internal
|
||||
* structure of the array. The dot code can be visualized by
|
||||
* Graphviz.
|
||||
*
|
||||
* On linux system, the output can be parsed by the dot program, e.g.
|
||||
*
|
||||
* <array_program> | dot -Tsvg > /tmp/dot.svg; firefox /tmp/dot.svg
|
||||
*
|
||||
* where <array_program> is the name of the executable
|
||||
*
|
||||
* The output may also be possible to visualize online on
|
||||
* https://dreampuf.github.io/GraphvizOnline/ or google "graphviz
|
||||
* online".
|
||||
*
|
||||
* For documention of the dot language, see graphviz.org.
|
||||
*
|
||||
* Returns: Nothing.
|
||||
*/
|
||||
void array_1d_print_internal(const array_1d *a, inspect_callback print_func, const char *desc,
|
||||
int indent_level);
|
||||
|
||||
#endif
|
||||
158
OU3/include/array_2d.h
Normal file
158
OU3/include/array_2d.h
Normal file
@@ -0,0 +1,158 @@
|
||||
#ifndef __ARRAY_2D_H
|
||||
#define __ARRAY_2D_H
|
||||
|
||||
#include <stdbool.h>
|
||||
#include "util.h"
|
||||
|
||||
/*
|
||||
* Declaration of a generic 2D array for the "Datastructures and
|
||||
* algorithms" courses at the Department of Computing Science, Umea
|
||||
* University. The array stores void pointers, so it can be used to
|
||||
* store all types of values. After use, the function array_kill must
|
||||
* be called to de-allocate the dynamic memory used by the array
|
||||
* itself. The de-allocation of any dynamic memory allocated for the
|
||||
* element values is the responsibility of the user of the array,
|
||||
* unless a kill_function is registered in array_create.
|
||||
*
|
||||
* An element value of NULL is considered to be "no" value.
|
||||
*
|
||||
* Authors: Niclas Borlin (niclas@cs.umu.se)
|
||||
*
|
||||
* Based on earlier code by: Johan Eliasson (johane@cs.umu.se).
|
||||
*
|
||||
* Version information:
|
||||
* v1.0 2018-01-28: First public version.
|
||||
* v1.1 2018-04-03: Moved freehandler to last in create parameter list.
|
||||
* v1.2 2024-05-10: Added print_internal.
|
||||
*/
|
||||
|
||||
// ==========PUBLIC DATA TYPES============
|
||||
|
||||
// List type.
|
||||
typedef struct array_2d array_2d;
|
||||
|
||||
// ==========DATA STRUCTURE INTERFACE==========
|
||||
|
||||
/**
|
||||
* array_2d_create() - Create an array without values.
|
||||
* @lo1: low index limit for first dimension.
|
||||
* @hi1: high index limit for first dimension.
|
||||
* @lo2: low index limit for second dimension.
|
||||
* @hi2: high index limit for second dimension.
|
||||
* @kill_func: A pointer to a function (or NULL) to be called to
|
||||
* de-allocate memory on remove/kill.
|
||||
*
|
||||
* The index limits are inclusive, i.e. all indices i such that low <=
|
||||
* i <= high are defined.
|
||||
*
|
||||
* Returns: A pointer to the new array, or NULL if not enough memory
|
||||
* was available.
|
||||
*/
|
||||
array_2d *array_2d_create(int lo1, int hi1, int lo2, int hi2,
|
||||
kill_function kill_func);
|
||||
|
||||
/**
|
||||
* array_2d_low() - Return the low index limit for the array.
|
||||
* @a: array to inspect.
|
||||
* @d: dimension number, 1 or 2.
|
||||
*
|
||||
* Returns: The low index limit for dimension number d.
|
||||
*/
|
||||
int array_2d_low(const array_2d *a, int d);
|
||||
|
||||
/**
|
||||
* array_2d_high() - Return the high index limit for the array.
|
||||
* @a: array to inspect.
|
||||
* @d: dimension number, 1 or 2.
|
||||
*
|
||||
* Returns: The high index limit for dimension number d.
|
||||
*/
|
||||
int array_2d_high(const array_2d *a, int d);
|
||||
|
||||
/**
|
||||
* array_2d_inspect_value() - Inspect a value at a given array position.
|
||||
* @a: array to inspect.
|
||||
* @i: first index of position to inspect.
|
||||
* @j: second index of position to inspect.
|
||||
*
|
||||
* Returns: The element value at the specified position. The result is
|
||||
* undefined if no value are stored at that position.
|
||||
*/
|
||||
void *array_2d_inspect_value(const array_2d *a, int i, int j);
|
||||
|
||||
/**
|
||||
* array_2d_has_value() - Check if a value is set at a given array position.
|
||||
* @a: array to inspect.
|
||||
* @i: first index of position to inspect.
|
||||
* @j: second index of position to inspect.
|
||||
*
|
||||
* Returns: True if a value is set at the specified position, otherwise false.
|
||||
*/
|
||||
bool array_2d_has_value(const array_2d *a, int i, int j);
|
||||
|
||||
/**
|
||||
* array_2d_set_value() - Set a value at a given array position.
|
||||
* @a: array to modify.
|
||||
* @v: value to set element to, or NULL to clear value.
|
||||
* @i: first index of position to inspect.
|
||||
* @j: second index of position to inspect.
|
||||
*
|
||||
* If the old element value is non-NULL, calls kill_func if it was
|
||||
* specified at array creation.
|
||||
*
|
||||
* Returns: Nothing.
|
||||
*/
|
||||
void array_2d_set_value(array_2d *a, void *v, int i, int j);
|
||||
|
||||
/**
|
||||
* array_2d_kill() - Return memory allocated by array.
|
||||
* @a: array to kill.
|
||||
*
|
||||
* Iterates over all elements. If kill_func was specified at array
|
||||
* creation, calls it for every non-NULL element value.
|
||||
*
|
||||
* Returns: Nothing.
|
||||
*/
|
||||
void array_2d_kill(array_2d *a);
|
||||
|
||||
/**
|
||||
* array_2d_print() - Iterate over the array element and print their values.
|
||||
* @a: Array to inspect.
|
||||
* @print_func: Function called for each non-NULL element.
|
||||
*
|
||||
* Iterates over each position in the array. Calls print_func for each
|
||||
* non-NULL value.
|
||||
*
|
||||
* Returns: Nothing.
|
||||
*/
|
||||
void array_2d_print(const array_2d * l, inspect_callback print_func);
|
||||
|
||||
/**
|
||||
* array_2d_print_internal() - Print the arrays internal structure.
|
||||
* @a: Array to inspect.
|
||||
* @print_func: Function called for each element value.
|
||||
* @desc: String with a description/state of the array, or NULL for no description.
|
||||
* @indent_level: Indentation level, 0 for outermost
|
||||
*
|
||||
* Iterates over the array and outputs dot code that shows the internal
|
||||
* structure of the array. The dot code can be visualized by
|
||||
* Graphviz.
|
||||
*
|
||||
* On linux system, the output can be parsed by the dot program, e.g.
|
||||
*
|
||||
* <array_program> | dot -Tsvg > /tmp/dot.svg; firefox /tmp/dot.svg
|
||||
*
|
||||
* where <array_program> is the name of the executable
|
||||
*
|
||||
* The output may also be possible to visualize online on
|
||||
* https://dreampuf.github.io/GraphvizOnline/ or google "graphviz
|
||||
* online".
|
||||
*
|
||||
* For documention of the dot language, see graphviz.org.
|
||||
*
|
||||
* Returns: Nothing.
|
||||
*/
|
||||
void array_2d_print_internal(const array_2d *a, inspect_callback print_func, const char *desc,
|
||||
int indent_level);
|
||||
|
||||
#endif
|
||||
199
OU3/include/dlist.h
Normal file
199
OU3/include/dlist.h
Normal file
@@ -0,0 +1,199 @@
|
||||
#ifndef __DLIST_H
|
||||
#define __DLIST_H
|
||||
|
||||
#include <stdbool.h>
|
||||
#include "util.h"
|
||||
|
||||
/*
|
||||
* Declaration of a generic, directed list for the "Datastructures
|
||||
* and algorithms" courses at the Department of Computing Science,
|
||||
* Umea University. The list stores void pointers, so it can be used
|
||||
* to store all types of values. After use, the function list_kill
|
||||
* must be called to de-allocate the dynamic memory used by the list
|
||||
* itself. The de-allocation of any dynamic memory allocated for the
|
||||
* element values is the responsibility of the user of the list,
|
||||
* unless a kill_function is registered in list_empty.
|
||||
*
|
||||
* Authors: Niclas Borlin (niclas@cs.umu.se)
|
||||
* Adam Dahlgren Lindstrom (dali@cs.umu.se)
|
||||
* Lars Karlsson (larsk@cs.umu.se)
|
||||
*
|
||||
* Based on earlier code by: Johan Eliasson (johane@cs.umu.se).
|
||||
*
|
||||
* Version information:
|
||||
* v1.0 2018-01-28: First public version.
|
||||
* v1.1 2023-01-19: Added dlist_pos_equal and dlist_pos_is_valid functions.
|
||||
* v1.2 2023-01-20: Renamed dlist_pos_equal to dlist_pos_are_equal.
|
||||
* v1.3 2023-03-23: Renamed dlist_pos_are_equal to dlist_pos_is_equal.
|
||||
* v2.0 2024-03-14: Added dlist_print_internal to output dot code for visualization.
|
||||
* Renamed free_* stuff to kill_*. Converted to 4-tabs.
|
||||
* v2.1 2024-05-10: updated print_internal to enhance encapsulation.
|
||||
*/
|
||||
|
||||
// ==========PUBLIC DATA TYPES============
|
||||
|
||||
// List type.
|
||||
typedef struct dlist dlist;
|
||||
|
||||
// List position type.
|
||||
typedef struct cell *dlist_pos;
|
||||
|
||||
|
||||
// ==========DATA STRUCTURE INTERFACE==========
|
||||
|
||||
/**
|
||||
* dlist_empty() - Create an empty dlist.
|
||||
* @kill_func: A pointer to a function (or NULL) to be called to
|
||||
* de-allocate memory on remove/kill.
|
||||
*
|
||||
* Returns: A pointer to the new list.
|
||||
*/
|
||||
dlist *dlist_empty(kill_function kill_func);
|
||||
|
||||
/**
|
||||
* dlist_is_empty() - Check if a dlist is empty.
|
||||
* @l: List to check.
|
||||
*
|
||||
* Returns: True if the list is empty, otherwise false.
|
||||
*/
|
||||
bool dlist_is_empty(const dlist *l);
|
||||
|
||||
/**
|
||||
* dlist_first() - Return the first position of a dlist, i.e. the
|
||||
* position of the first element in the list.
|
||||
* @l: List to inspect.
|
||||
*
|
||||
* Returns: The first position in the given list.
|
||||
*/
|
||||
dlist_pos dlist_first(const dlist *l);
|
||||
|
||||
/**
|
||||
* dlist_next() - Return the next position in a dlist.
|
||||
* @l: List to inspect.
|
||||
* @p: Any valid position except the last in the list.
|
||||
*
|
||||
* Returns: The position in the list after the given position.
|
||||
* NOTE: The return value is undefined for the last position.
|
||||
*/
|
||||
dlist_pos dlist_next(const dlist *l, const dlist_pos p);
|
||||
|
||||
/**
|
||||
* dlist_is_end() - Check if a given position is at the end of a dlist.
|
||||
* @l: List to inspect.
|
||||
* @p: Any valid position in the list.
|
||||
*
|
||||
* Returns: True if p is at the end of the list.
|
||||
*/
|
||||
bool dlist_is_end(const dlist *l, const dlist_pos p);
|
||||
|
||||
/**
|
||||
* dlist_inspect() - Return the value of the element at a given
|
||||
* position in a list.
|
||||
* @l: List to inspect.
|
||||
* @p: Any valid position in the list, except the last.
|
||||
*
|
||||
* Returns: Returns the value at the given position as a void pointer.
|
||||
* NOTE: The return value is undefined for the last position.
|
||||
*/
|
||||
void *dlist_inspect(const dlist *l, const dlist_pos p);
|
||||
|
||||
/**
|
||||
* dlist_insert() - Insert a new element with a given value into a dlist.
|
||||
* @l: List to manipulate.
|
||||
* @v: Value (pointer) to be inserted into the list.
|
||||
* @p: Position in the list before which the value should be inserted.
|
||||
*
|
||||
* Creates a new element and inserts it into the list before p.
|
||||
* Stores data in the new element.
|
||||
*
|
||||
* Returns: The position of the newly created element.
|
||||
*/
|
||||
dlist_pos dlist_insert(dlist *l, void *v, const dlist_pos p);
|
||||
|
||||
/**
|
||||
* dlist_remove() - Remove an element from a dlist.
|
||||
* @l: List to manipulate.
|
||||
* @p: Position in the list of the element to remove.
|
||||
*
|
||||
* Removes the element at position p from the list. If a kill_func
|
||||
* was registered at list creation, calls it to deallocate the memory
|
||||
* held by the element value.
|
||||
*
|
||||
* Returns: The position after the removed element.
|
||||
*/
|
||||
dlist_pos dlist_remove(dlist *l, const dlist_pos p);
|
||||
|
||||
/**
|
||||
* dlist_kill() - Destroy a given dlist.
|
||||
* @l: List to destroy.
|
||||
*
|
||||
* Returns all dynamic memory used by the list and its elements. If a
|
||||
* kill_func was registered at list creation, also calls it for each
|
||||
* element to return any user-allocated memory occupied by the element values.
|
||||
*
|
||||
* Returns: Nothing.
|
||||
*/
|
||||
void dlist_kill(dlist *l);
|
||||
|
||||
/**
|
||||
* dlist_print() - Iterate over the list elements and print their values.
|
||||
* @l: List to inspect.
|
||||
* @print_func: Function called for each element.
|
||||
*
|
||||
* Iterates over the list and calls print_func with the value stored
|
||||
* in each element.
|
||||
*
|
||||
* Returns: Nothing.
|
||||
*/
|
||||
void dlist_print(const dlist *l, inspect_callback print_func);
|
||||
|
||||
/**
|
||||
* dlist_pos_is_valid() - Return true for a valid position in a dlist
|
||||
* @l: List to inspect.
|
||||
* @p: Any position.
|
||||
*
|
||||
* Returns: True if p is a valid position in the list, otherwise false.
|
||||
*/
|
||||
bool dlist_pos_is_valid(const dlist *l, const dlist_pos p);
|
||||
|
||||
/**
|
||||
* dlist_pos_is_equal() - Return true if two positions in a dlist are equal.
|
||||
* @l: List to inspect.
|
||||
* @p1: First position to compare.
|
||||
* @p2: Second position to compare.
|
||||
*
|
||||
* Returns: True if p1 and p2 refer to the same position in l, otherwise False.
|
||||
* NOTE: The result is defined only if p1 and p2 are valid positions in l.
|
||||
* p1 and p2 are assumed to be valid positions in l, no check is performed.
|
||||
*/
|
||||
bool dlist_pos_is_equal(const dlist *l, const dlist_pos p1, const dlist_pos p2);
|
||||
|
||||
/**
|
||||
* dlist_print_internal() - Print the lists internal structure in dot format.
|
||||
* @l: List to inspect.
|
||||
* @print_func: Function called for each element value.
|
||||
* @desc: String with a description/state of the list, or NULL for no description.
|
||||
* @indent_level: Indentation level, 0 for outermost
|
||||
*
|
||||
* Iterates over the list and outputs dot code that shows the internal
|
||||
* structure of the list. The dot code can be visualized by
|
||||
* Graphviz.
|
||||
*
|
||||
* On linux system, the output can be parsed by the dot program, e.g.
|
||||
*
|
||||
* <list_program> | dot -Tsvg > /tmp/dot.svg; firefox /tmp/dot.svg
|
||||
*
|
||||
* where <list_program> is the name of the executable
|
||||
*
|
||||
* The output may also be possible to visualize online on
|
||||
* https://dreampuf.github.io/GraphvizOnline/ or google "graphviz
|
||||
* online".
|
||||
*
|
||||
* For documention of the dot language, see graphviz.org.
|
||||
*
|
||||
* Returns: Nothing.
|
||||
*/
|
||||
void dlist_print_internal(const dlist *l, inspect_callback print_func, const char *desc,
|
||||
int indent_level);
|
||||
|
||||
#endif
|
||||
136
OU3/include/int_array_1d.h
Normal file
136
OU3/include/int_array_1d.h
Normal file
@@ -0,0 +1,136 @@
|
||||
#ifndef __INT_ARRAY_1D_H
|
||||
#define __INT_ARRAY_1D_H
|
||||
|
||||
#include <stdbool.h>
|
||||
#include "util.h"
|
||||
|
||||
/*
|
||||
* Declaration of an integer 1D array for the "Datastructures and
|
||||
* algorithms" courses at the Department of Computing Science, Umea
|
||||
* University. After use, the function array_kill must be called to
|
||||
* de-allocate the dynamic memory used by the array itself.
|
||||
*
|
||||
* An element value of 0 is considered to be "no" value.
|
||||
*
|
||||
* Authors: Niclas Borlin (niclas@cs.umu.se)
|
||||
*
|
||||
* Based on earlier code by: Johan Eliasson (johane@cs.umu.se).
|
||||
*
|
||||
* Version information:
|
||||
* v1.0 2018-01-28: First public version.
|
||||
* v1.2 2024-05-10: Added print_internal.
|
||||
*/
|
||||
|
||||
// ==========PUBLIC DATA TYPES============
|
||||
|
||||
// List type.
|
||||
typedef struct int_array_1d int_array_1d;
|
||||
|
||||
// ==========DATA STRUCTURE INTERFACE==========
|
||||
|
||||
/**
|
||||
* int_array_1d_create() - Create an array without values.
|
||||
* @lo: low index limit.
|
||||
* @hi: high index limit.
|
||||
*
|
||||
* The index limits are inclusive, i.e. all indices i such that low <=
|
||||
* i <= high are defined.
|
||||
*
|
||||
* Returns: A pointer to the new array, or NULL if not enough memory
|
||||
* was available.
|
||||
*/
|
||||
int_array_1d *int_array_1d_create(int lo, int hi);
|
||||
|
||||
/**
|
||||
* int_array_1d_low() - Return the low index limit for the array.
|
||||
* @a: array to inspect.
|
||||
*
|
||||
* Returns: The low index limit.
|
||||
*/
|
||||
int int_array_1d_low(const int_array_1d *a);
|
||||
|
||||
/**
|
||||
* int_array_1d_high() - Return the high index limit for the array.
|
||||
* @a: array to inspect.
|
||||
*
|
||||
* Returns: The high index limit.
|
||||
*/
|
||||
int int_array_1d_high(const int_array_1d *a);
|
||||
|
||||
/**
|
||||
* int_array_1d_inspect_value() - Inspect a value at a given array position.
|
||||
* @a: array to inspect.
|
||||
* @i: index of position to inspect.
|
||||
*
|
||||
* Returns: The element value at the specified position, or 0 if no
|
||||
* value is stored at that position.
|
||||
*/
|
||||
int int_array_1d_inspect_value(const int_array_1d *a, int i);
|
||||
|
||||
/**
|
||||
* int_array_1d_has_value() - Check if a value is set at a given array position.
|
||||
* @a: array to inspect.
|
||||
* @i: index of position to inspect.
|
||||
*
|
||||
* Returns: True if a value is set at the specified position, otherwise false.
|
||||
*/
|
||||
bool int_array_1d_has_value(const int_array_1d *a, int i);
|
||||
|
||||
/**
|
||||
* int_array_1d_set_value() - Set a value at a given array position.
|
||||
* @a: array to modify.
|
||||
* @v: value to set element to, or 0 to clear value.
|
||||
* @i: index of position to modify.
|
||||
*
|
||||
* Returns: Nothing.
|
||||
*/
|
||||
void int_array_1d_set_value(int_array_1d *a, int v, int i);
|
||||
|
||||
/**
|
||||
* int_array_1d_kill() - Return memory allocated by array.
|
||||
* @a: array to kill.
|
||||
*
|
||||
* Iterates over all elements. If free_func was specified at array
|
||||
* creation, calls it for every non-NULL element value.
|
||||
*
|
||||
* Returns: Nothing.
|
||||
*/
|
||||
void int_array_1d_kill(int_array_1d *a);
|
||||
|
||||
/**
|
||||
* int_array_1d_print() - Iterate over the array element and print their values.
|
||||
* @a: Array to inspect.
|
||||
*
|
||||
* Iterates over each position in the array. Prints each non-zero element.
|
||||
*
|
||||
* Returns: Nothing.
|
||||
*/
|
||||
void int_array_1d_print(const int_array_1d *a);
|
||||
|
||||
/**
|
||||
* int_array_1d_print_internal() - Print the arrays internal structure in dot format.
|
||||
* @a: Array to inspect.
|
||||
* @desc: String with a description/state of the array, or NULL for no description.
|
||||
* @indent_level: Indentation level, 0 for outermost
|
||||
*
|
||||
* Iterates over the array and outputs dot code that shows the internal
|
||||
* structure of the array. The dot code can be visualized by
|
||||
* Graphviz.
|
||||
*
|
||||
* On linux system, the output can be parsed by the dot program, e.g.
|
||||
*
|
||||
* <array_program> | dot -Tsvg > /tmp/dot.svg; firefox /tmp/dot.svg
|
||||
*
|
||||
* where <array_program> is the name of the executable
|
||||
*
|
||||
* The output may also be possible to visualize online on
|
||||
* https://dreampuf.github.io/GraphvizOnline/ or google "graphviz
|
||||
* online".
|
||||
*
|
||||
* For documention of the dot language, see graphviz.org.
|
||||
*
|
||||
* Returns: Nothing.
|
||||
*/
|
||||
void int_array_1d_print_internal(const int_array_1d *a, const char *desc, int indent_level);
|
||||
|
||||
#endif
|
||||
192
OU3/include/int_list.h
Normal file
192
OU3/include/int_list.h
Normal file
@@ -0,0 +1,192 @@
|
||||
#ifndef __INT_LIST_H
|
||||
#define __INT_LIST_H
|
||||
|
||||
#include <stdbool.h>
|
||||
#include "util.h"
|
||||
|
||||
/*
|
||||
* Declaration of a undirected list for storing integers for the
|
||||
* "Datastructures and algorithms" courses at the Department of
|
||||
* Computing Science, Umea University. The implementation uses a
|
||||
* dynamic, linked structure. After use, the function list_kill must
|
||||
* be called to de-allocate the dynamic memory used by the list
|
||||
* itself. The implementation is a written a code copy specialization
|
||||
* of the generic list to provide a simpler starting data structure
|
||||
* than the generic list.
|
||||
*
|
||||
* Authors: Niclas Borlin (niclas@cs.umu.se)
|
||||
*
|
||||
* Version information:
|
||||
* v1.0 2018-01-28: First public version.
|
||||
* v1.1 2023-01-19: Added list_pos_equal and list_pos_is_valid functions.
|
||||
* v1.2 2023-01-20: Renamed list_pos_equal to list_pos_are_equal.
|
||||
* v1.3 2023-03-23: Renamed list_pos_are_equal to list_pos_is_equal.
|
||||
* v1.4 2024-05-10: Added print_internal.
|
||||
*/
|
||||
|
||||
// ==========PUBLIC DATA TYPES============
|
||||
|
||||
// List type.
|
||||
typedef struct list list;
|
||||
|
||||
// List position type.
|
||||
typedef struct cell *list_pos;
|
||||
|
||||
// ==========DATA STRUCTURE INTERFACE==========
|
||||
|
||||
/**
|
||||
* list_empty() - Create an empty list.
|
||||
*
|
||||
* Returns: A pointer to the new list.
|
||||
*/
|
||||
list *list_empty(void);
|
||||
|
||||
/**
|
||||
* list_is_empty() - Check if a list is empty.
|
||||
* @l: List to check.
|
||||
*
|
||||
* Returns: True if the list is empty, otherwise false.
|
||||
*/
|
||||
bool list_is_empty(const list *l);
|
||||
|
||||
/**
|
||||
* list_first() - Return the first position of a list, i.e. the
|
||||
* position of the first element in the list.
|
||||
* @l: List to inspect.
|
||||
*
|
||||
* Returns: The first position in the given list.
|
||||
*/
|
||||
list_pos list_first(const list *l);
|
||||
|
||||
/**
|
||||
* list_end() - Return the last position of a list, i.e. the position
|
||||
* after the last element in the list.
|
||||
* @l: List to inspect.
|
||||
*
|
||||
* Returns: The last position in the given list.
|
||||
*/
|
||||
list_pos list_end(const list *l);
|
||||
|
||||
/**
|
||||
* list_next() - Return the next position in a list.
|
||||
* @l: List to inspect.
|
||||
* @p: Any valid position except the last in the list.
|
||||
*
|
||||
* Returns: The position in the list after the given position.
|
||||
* NOTE: The return value is undefined for the last position.
|
||||
*/
|
||||
list_pos list_next(const list *l, const list_pos p);
|
||||
|
||||
/**
|
||||
* list_prev() - Return the previous position in a list.
|
||||
* @l: List to inspect.
|
||||
* @p: Any valid position except the first in the list.
|
||||
*
|
||||
* Returns: The position in the list before the given position.
|
||||
* NOTE: The return value is undefined for the first position.
|
||||
*/
|
||||
list_pos list_prev(const list *l, const list_pos p);
|
||||
|
||||
/**
|
||||
* list_inspect() - Return the value of the element at a given
|
||||
* position in a list.
|
||||
* @l: List to inspect.
|
||||
* @p: Any valid position in the list, except the last.
|
||||
*
|
||||
* Returns: The integer value stored in the element at postiion pos.
|
||||
* NOTE: The return value is undefined for the last position.
|
||||
*/
|
||||
int list_inspect(const list *l, const list_pos p);
|
||||
|
||||
/**
|
||||
* list_insert() - Insert a new element with a given value into a list.
|
||||
* @l: List to manipulate.
|
||||
* @data: Integer value to be inserted into the list.
|
||||
* @p: Position in the list before which the value should be inserted.
|
||||
*
|
||||
* Creates a new element and inserts it into the list before p.
|
||||
* Stores data in the new element.
|
||||
*
|
||||
* Returns: The position of the newly created element.
|
||||
*/
|
||||
list_pos list_insert(list *l, int data, const list_pos p);
|
||||
|
||||
/**
|
||||
* list_remove() - Remove an element from a list.
|
||||
* @l: List to manipulate.
|
||||
* @p: Position in the list of the element to remove.
|
||||
*
|
||||
* Removes the element at position p from the list.
|
||||
*
|
||||
* Returns: The position after the removed element.
|
||||
*/
|
||||
list_pos list_remove(list *l, const list_pos p);
|
||||
|
||||
/**
|
||||
* list_kill() - Destroy a given list.
|
||||
* @l: List to destroy.
|
||||
*
|
||||
* Returns all dynamic memory used by the list.
|
||||
*
|
||||
* Returns: Nothing.
|
||||
*/
|
||||
void list_kill(list *l);
|
||||
|
||||
/**
|
||||
* list_print() - Iterate over the list elements and print their values.
|
||||
* @l: List to inspect.
|
||||
*
|
||||
* Iterates over the list and prints each stored integer.
|
||||
*
|
||||
* Returns: Nothing.
|
||||
*/
|
||||
void list_print(const list *l);
|
||||
|
||||
/**
|
||||
* list_pos_is_valid() - Return true for a valid position in a list
|
||||
* @l: List to inspect.
|
||||
* @p: Any position.
|
||||
*
|
||||
* Returns: True if p is a valid position in the list, otherwise false.
|
||||
*/
|
||||
bool list_pos_is_valid(const list *l, const list_pos p);
|
||||
|
||||
/**
|
||||
* list_pos_is_equal() - Return true if two positions in a list are equal.
|
||||
* @l: List to inspect.
|
||||
* @p1: First position to compare.
|
||||
* @p2: Second position to compare.
|
||||
*
|
||||
* Returns: True if p1 and p2 refer to the same position in l, otherwise False.
|
||||
* NOTE: The result is defined only if p1 and p2 are valid positions in l.
|
||||
* p1 and p2 are assumed to be valid positions in l, no check is performed.
|
||||
*/
|
||||
bool list_pos_is_equal(const list *l, const list_pos p1, const list_pos p2);
|
||||
|
||||
/**
|
||||
* list_print_internal() - Print the lists internal structure in dot format.
|
||||
* @l: List to inspect.
|
||||
* @desc: String with a description/state of the list, or NULL for no description.
|
||||
* @indent_level: Indentation level, 0 for outermost
|
||||
*
|
||||
* Iterates over the list and outputs dot code that shows the internal
|
||||
* structure of the list. The dot code can be visualized by
|
||||
* Graphviz.
|
||||
*
|
||||
* On linux system, the output can be parsed by the dot program, e.g.
|
||||
*
|
||||
* <list_program> | dot -Tsvg > /tmp/dot.svg; firefox /tmp/dot.svg
|
||||
*
|
||||
* where <list_program> is the name of the executable
|
||||
*
|
||||
* The output may also be possible to visualize online on
|
||||
* https://dreampuf.github.io/GraphvizOnline/ or google "graphviz
|
||||
* online".
|
||||
*
|
||||
* For documention of the dot language, see graphviz.org.
|
||||
*
|
||||
* Returns: Nothing.
|
||||
*/
|
||||
void list_print_internal(const list *l, const char *desc, int indent_level);
|
||||
|
||||
#endif
|
||||
193
OU3/include/int_list_array.h
Normal file
193
OU3/include/int_list_array.h
Normal file
@@ -0,0 +1,193 @@
|
||||
#ifndef __INT_LIST_ARRAY_H
|
||||
#define __INT_LIST_ARRAY_H
|
||||
|
||||
#include <stdbool.h>
|
||||
#include "util.h"
|
||||
|
||||
/*
|
||||
* Declaration of a undirected list for storing integers for the
|
||||
* "Datastructures and algorithms" courses at the Department of
|
||||
* Computing Science, Umea University. The implementation uses a
|
||||
* static array. After use, the function list_kill must be called to
|
||||
* de-allocate the dynamic memory used by the list itself. The
|
||||
* implementation is a written a code copy specialization of the
|
||||
* generic list to provide a simpler starting data structure than the
|
||||
* generic list.
|
||||
*
|
||||
* Authors: Niclas Borlin (niclas@cs.umu.se)
|
||||
*
|
||||
* Version information:
|
||||
* v1.0 2018-03-26: First public version.
|
||||
* v1.1 2023-01-19: Added list_pos_equal and list_pos_is_valid functions.
|
||||
* Bugfix in list_remove.
|
||||
* v1.2 2023-01-20: Renamed list_pos_equal to list_pos_are_equal.
|
||||
* v1.3 2023-03-23: Renamed list_pos_are_equal to list_pos_is_equal.
|
||||
* v1.4 2024-05-10: Added print_internal.
|
||||
*/
|
||||
|
||||
// ==========PUBLIC DATA TYPES============
|
||||
|
||||
// List type.
|
||||
typedef struct list list;
|
||||
|
||||
// List position type.
|
||||
typedef int list_pos;
|
||||
|
||||
// ==========DATA STRUCTURE INTERFACE==========
|
||||
|
||||
/**
|
||||
* list_empty() - Create an empty list.
|
||||
*
|
||||
* Returns: A pointer to the new list.
|
||||
*/
|
||||
list *list_empty(void);
|
||||
|
||||
/**
|
||||
* list_is_empty() - Check if a list is empty.
|
||||
* @l: List to check.
|
||||
*
|
||||
* Returns: True if the list is empty, otherwise false.
|
||||
*/
|
||||
bool list_is_empty(const list *l);
|
||||
|
||||
/**
|
||||
* list_first() - Return the first position of a list, i.e. the
|
||||
* position of the first element in the list.
|
||||
* @l: List to inspect.
|
||||
*
|
||||
* Returns: The first position in the given list.
|
||||
*/
|
||||
list_pos list_first(const list *l);
|
||||
|
||||
/**
|
||||
* list_end() - Return the last position of a list, i.e. the position
|
||||
* after the last element in the list.
|
||||
* @l: List to inspect.
|
||||
*
|
||||
* Returns: The last position in the given list.
|
||||
*/
|
||||
list_pos list_end(const list *l);
|
||||
|
||||
/**
|
||||
* list_next() - Return the next position in a list.
|
||||
* @l: List to inspect.
|
||||
* @p: Any valid position except the last in the list.
|
||||
*
|
||||
* Returns: The position in the list after the given position.
|
||||
* NOTE: The return value is undefined for the last position.
|
||||
*/
|
||||
list_pos list_next(const list *l, const list_pos p);
|
||||
|
||||
/**
|
||||
* list_prev() - Return the previous position in a list.
|
||||
* @l: List to inspect.
|
||||
* @p: Any valid position except the first in the list.
|
||||
*
|
||||
* Returns: The position in the list before the given position.
|
||||
* NOTE: The return value is undefined for the first position.
|
||||
*/
|
||||
list_pos list_prev(const list *l, const list_pos p);
|
||||
|
||||
/**
|
||||
* list_inspect() - Return the value of the element at a given
|
||||
* position in a list.
|
||||
* @l: List to inspect.
|
||||
* @p: Any valid position in the list, except the last.
|
||||
*
|
||||
* Returns: The integer value stored in the element at postiion pos.
|
||||
* NOTE: The return value is undefined for the last position.
|
||||
*/
|
||||
int list_inspect(const list *l, const list_pos p);
|
||||
|
||||
/**
|
||||
* list_insert() - Insert a new element with a given value into a list.
|
||||
* @l: List to manipulate.
|
||||
* @data: Integer value to be inserted into the list.
|
||||
* @p: Position in the list before which the value should be inserted.
|
||||
*
|
||||
* Creates a new element and inserts it into the list before p.
|
||||
* Stores data in the new element.
|
||||
*
|
||||
* Returns: The position of the newly created element.
|
||||
*/
|
||||
list_pos list_insert(list *l, int data, const list_pos p);
|
||||
|
||||
/**
|
||||
* list_remove() - Remove an element from a list.
|
||||
* @l: List to manipulate.
|
||||
* @p: Position in the list of the element to remove.
|
||||
*
|
||||
* Removes the element at position p from the list.
|
||||
*
|
||||
* Returns: The position after the removed element.
|
||||
*/
|
||||
list_pos list_remove(list *l, const list_pos p);
|
||||
|
||||
/**
|
||||
* list_kill() - Destroy a given list.
|
||||
* @l: List to destroy.
|
||||
*
|
||||
* Returns all dynamic memory used by the list.
|
||||
*
|
||||
* Returns: Nothing.
|
||||
*/
|
||||
void list_kill(list *l);
|
||||
|
||||
/**
|
||||
* list_print() - Iterate over the list elements and print their values.
|
||||
* @l: List to inspect.
|
||||
*
|
||||
* Iterates over the list and prints each stored integer.
|
||||
*
|
||||
* Returns: Nothing.
|
||||
*/
|
||||
void list_print(const list *l);
|
||||
|
||||
/**
|
||||
* list_pos_is_valid() - Return true for a valid position in a list
|
||||
* @l: List to inspect.
|
||||
* @p: Any position.
|
||||
*
|
||||
* Returns: True if p is a valid position in the list, otherwise false.
|
||||
*/
|
||||
bool list_pos_is_valid(const list *l, const list_pos p);
|
||||
|
||||
/**
|
||||
* list_pos_is_equal() - Return true if two positions in a list are equal.
|
||||
* @l: List to inspect.
|
||||
* @p1: First position to compare.
|
||||
* @p2: Second position to compare.
|
||||
*
|
||||
* Returns: True if p1 and p2 refer to the same position in l, otherwise False.
|
||||
* NOTE: The result is defined only if p1 and p2 are valid positions in l.
|
||||
* p1 and p2 are assumed to be valid positions in l, no check is performed.
|
||||
*/
|
||||
bool list_pos_is_equal(const list *l, const list_pos p1, const list_pos p2);
|
||||
|
||||
/**
|
||||
* list_print_internal() - Print the lists internal structure in dot format.
|
||||
* @l: List to inspect.
|
||||
* @desc: String with a description/state of the list, or NULL for no description.
|
||||
* @indent_level: Indentation level, 0 for outermost
|
||||
*
|
||||
* Iterates over the list and outputs dot code that shows the internal
|
||||
* structure of the list. The dot code can be visualized by
|
||||
* Graphviz.
|
||||
*
|
||||
* On linux system, the output can be parsed by the dot program, e.g.
|
||||
*
|
||||
* <list_program> | dot -Tsvg > /tmp/dot.svg; firefox /tmp/dot.svg
|
||||
*
|
||||
* where <list_program> is the name of the executable
|
||||
*
|
||||
* The output may also be possible to visualize online on
|
||||
* https://dreampuf.github.io/GraphvizOnline/ or google "graphviz
|
||||
* online".
|
||||
*
|
||||
* For documention of the dot language, see graphviz.org.
|
||||
*
|
||||
* Returns: Nothing.
|
||||
*/
|
||||
void list_print_internal(const list *l, const char *desc, int indent_level);
|
||||
|
||||
#endif
|
||||
126
OU3/include/int_queue.h
Normal file
126
OU3/include/int_queue.h
Normal file
@@ -0,0 +1,126 @@
|
||||
#ifndef __INT_QUEUE_H
|
||||
#define __INT_QUEUE_H
|
||||
|
||||
#include <stdbool.h>
|
||||
#include "util.h"
|
||||
|
||||
/*
|
||||
* Declaration of an integer queue for the "Datastructures and
|
||||
* algorithms" courses at the Department of Computing Science, Umea
|
||||
* University. The queue stores integers directly and does not use
|
||||
* dynamic memory. Thus, the clean-up function queue_kill is strictly
|
||||
* not necessary, but recommended to maintain symmetry with untyped,
|
||||
* generic queue implementations.
|
||||
*
|
||||
* Authors: Niclas Borlin (niclas@cs.umu.se)
|
||||
*
|
||||
* Version information:
|
||||
* v1.0 2025-01-10: First public version.
|
||||
*/
|
||||
|
||||
// ==========PUBLIC CONSTANTS AND DATA TYPES============
|
||||
|
||||
// Maximum size of the queue.
|
||||
#define MAX_QUEUE_SIZE 100
|
||||
|
||||
// We must define the struct publically for the compiler to know its
|
||||
// size for copying, etc.
|
||||
typedef struct queue {
|
||||
int first_free_pos;
|
||||
int elements[MAX_QUEUE_SIZE];
|
||||
} queue;
|
||||
|
||||
// ==========DATA STRUCTURE INTERFACE==========
|
||||
|
||||
/**
|
||||
* queue_empty() - Create an empty queue.
|
||||
*
|
||||
* Returns: A new, empty, queue.
|
||||
*/
|
||||
queue queue_empty(void);
|
||||
|
||||
/**
|
||||
* queue_is_empty() - Check if a queue is empty.
|
||||
* @q: Queue to check.
|
||||
*
|
||||
* Returns: True if queue is empty, otherwise false.
|
||||
*/
|
||||
bool queue_is_empty(const queue q);
|
||||
|
||||
/**
|
||||
* queue_enqueue() - Put a value at the end of a queue.
|
||||
* @q: Queue to manipulate.
|
||||
* @v: Value (integer) to be put in the queue.
|
||||
*
|
||||
* Returns: The modified queue.
|
||||
*/
|
||||
queue queue_enqueue(queue q, int v);
|
||||
|
||||
/**
|
||||
* queue_dequeue() - Remove the element at the beginning of a queue.
|
||||
* @q: Queue to manipulate.
|
||||
*
|
||||
* NOTE: Undefined for an empty queue.
|
||||
*
|
||||
* Returns: The modified queue.
|
||||
*/
|
||||
queue queue_dequeue(queue q);
|
||||
|
||||
/**
|
||||
* queue_front() - Inspect the value at the front of the queue.
|
||||
* @q: Queue to inspect.
|
||||
*
|
||||
* Returns: The value at the front of the queue.
|
||||
* NOTE: The return value is undefined for an empty queue.
|
||||
*/
|
||||
int queue_front(const queue q);
|
||||
|
||||
/**
|
||||
* queue_kill() - Destroy a given queue.
|
||||
* @q: Queue to destroy.
|
||||
*
|
||||
* Does nothing since the queue does not use any dynamic
|
||||
* memory. Included for symmetry with generic queue.h.
|
||||
*
|
||||
* Returns: Nothing.
|
||||
*/
|
||||
void queue_kill(queue q);
|
||||
|
||||
/**
|
||||
* queue_print() - Iterate over the queue elements and print their values.
|
||||
* @q: Queue to inspect.
|
||||
*
|
||||
* Iterates over the queue and prints each integer.
|
||||
*
|
||||
* Returns: Nothing.
|
||||
*/
|
||||
void queue_print(const queue q);
|
||||
|
||||
/**
|
||||
* queue_print_internal() - Print the internal structure of the queue in dot format.
|
||||
* @q: Queue to inspect.
|
||||
* @desc: String with a description/state of the queue, or NULL for no description.
|
||||
* @indent_level: Indentation level, 0 for outermost.
|
||||
* @max_elems: Maximum number of elements to print.
|
||||
*
|
||||
* Iterates over the queue and outputs dot code that shows the
|
||||
* internal structure of the queue. The dot code can be visualized by
|
||||
* Graphviz.
|
||||
*
|
||||
* On linux system, the output can be parsed by the dot program, e.g.
|
||||
*
|
||||
* <queue_program> | dot -Tsvg > /tmp/dot.svg; firefox /tmp/dot.svg
|
||||
*
|
||||
* where <queue_program> is the name of the executable
|
||||
*
|
||||
* The output may also be possible to visualize online on
|
||||
* https://dreampuf.github.io/GraphvizOnline/ or google "graphviz
|
||||
* online".
|
||||
*
|
||||
* For documention of the dot language, see graphviz.org.
|
||||
*
|
||||
* Returns: Nothing.
|
||||
*/
|
||||
void queue_print_internal(const queue q, const char *desc, int indent_level, int max_elems);
|
||||
|
||||
#endif
|
||||
134
OU3/include/int_stack.h
Normal file
134
OU3/include/int_stack.h
Normal file
@@ -0,0 +1,134 @@
|
||||
#ifndef __INT_STACK_H
|
||||
#define __INT_STACK_H
|
||||
|
||||
#include <stdbool.h>
|
||||
#include "util.h"
|
||||
|
||||
/*
|
||||
* Declaration of an integer stack for the "Datastructures and
|
||||
* algorithms" courses at the Department of Computing Science, Umea
|
||||
* University. The stack stores integers directly and does not use
|
||||
* dynamic memory. Thus, the clean-up function stack_kill is strictly
|
||||
* not necessary, but recommended to maintain symmetry with untyped,
|
||||
* generic stack implementations.
|
||||
*
|
||||
* Authors: Niclas Borlin (niclas@cs.umu.se)
|
||||
* Adam Dahlgren Lindstrom (dali@cs.umu.se)
|
||||
*
|
||||
* Based on earlier code by: Johan Eliasson (johane@cs.umu.se).
|
||||
*
|
||||
* Version information:
|
||||
* v1.0 2018-01-28: First public version.
|
||||
* v1.1 2024-04-15: Reduced default stack size to 15 to make output
|
||||
* from print_internal readable.
|
||||
* v1.2 2024-05-10: Updated print_internal to enhance encapsulation.
|
||||
*/
|
||||
|
||||
// ==========PUBLIC DATA TYPES============
|
||||
|
||||
// Stack type.
|
||||
#define MAX_STACK_SIZE 100
|
||||
|
||||
// We must define the struct publically for the compiler to know its
|
||||
// size for copying, etc.
|
||||
typedef struct stack {
|
||||
int first_free_pos;
|
||||
int elements[MAX_STACK_SIZE];
|
||||
} stack;
|
||||
|
||||
// ==========DATA STRUCTURE INTERFACE==========
|
||||
|
||||
/**
|
||||
* stack_empty() - Create an empty stack.
|
||||
*
|
||||
* Returns: A new stack.
|
||||
*/
|
||||
stack stack_empty(void);
|
||||
|
||||
/**
|
||||
* stack_is_empty() - Check if a stack is empty.
|
||||
* @s: Stack to check.
|
||||
*
|
||||
* Returns: True if stack is empty, otherwise false.
|
||||
*/
|
||||
bool stack_is_empty(const stack s);
|
||||
|
||||
/**
|
||||
* stack_push() - Push a value on top of a stack.
|
||||
* @s: Stack to manipulate.
|
||||
* @v: Value (integer) to be put on the stack.
|
||||
*
|
||||
* Returns: The modified stack.
|
||||
* NOTE: After the call, the input stack should be considered invalid.
|
||||
*/
|
||||
stack stack_push(stack s, int v);
|
||||
|
||||
/**
|
||||
* stack_pop() - Remove the element at the top of a stack.
|
||||
* @s: Stack to manipulate.
|
||||
*
|
||||
* NOTE: Undefined for an empty stack.
|
||||
*
|
||||
* Returns: The modified stack.
|
||||
* NOTE: After the call, the input stack should be considered invalid.
|
||||
*/
|
||||
stack stack_pop(stack s);
|
||||
|
||||
/**
|
||||
* stack_top() - Inspect the value at the top of the stack.
|
||||
* @s: Stack to inspect.
|
||||
*
|
||||
* Returns: The value at the top of the stack.
|
||||
* NOTE: The return value is undefined for an empty stack.
|
||||
*/
|
||||
int stack_top(const stack s);
|
||||
|
||||
/**
|
||||
* stack_kill() - Destroy a given stack.
|
||||
* @s: Stack to destroy.
|
||||
*
|
||||
* Does nothing since the stack does not use any dynamic
|
||||
* memory. Included for symmetry with generic stack.h.
|
||||
*
|
||||
* Returns: Nothing.
|
||||
*/
|
||||
void stack_kill(stack s);
|
||||
|
||||
/**
|
||||
* stack_print() - Iterate over the stack elements and print their values.
|
||||
* @s: Stack to inspect.
|
||||
*
|
||||
* Iterates over the stack and prints each integer.
|
||||
*
|
||||
* Returns: Nothing.
|
||||
*/
|
||||
void stack_print(const stack s);
|
||||
|
||||
/**
|
||||
* stack_print_internal() - Print the internal structure of the stack in dot format.
|
||||
* @s: Stack to inspect.
|
||||
* @desc: String with a description/state of the stack, or NULL for no description.
|
||||
* @indent_level: Indentation level, 0 for outermost.
|
||||
* @max_elems: Maximum number of elements to print.
|
||||
*
|
||||
* Iterates over the stack and outputs dot code that shows the
|
||||
* internal structure of the stack. The dot code can be visualized by
|
||||
* Graphviz.
|
||||
*
|
||||
* On linux system, the output can be parsed by the dot program, e.g.
|
||||
*
|
||||
* <stack_program> | dot -Tsvg > /tmp/dot.svg; firefox /tmp/dot.svg
|
||||
*
|
||||
* where <stack_program> is the name of the executable
|
||||
*
|
||||
* The output may also be possible to visualize online on
|
||||
* https://dreampuf.github.io/GraphvizOnline/ or google "graphviz
|
||||
* online".
|
||||
*
|
||||
* For documention of the dot language, see graphviz.org.
|
||||
*
|
||||
* Returns: Nothing.
|
||||
*/
|
||||
void stack_print_internal(const stack s, const char *desc, int indent_level, int max_elems);
|
||||
|
||||
#endif
|
||||
207
OU3/include/list.h
Normal file
207
OU3/include/list.h
Normal file
@@ -0,0 +1,207 @@
|
||||
#ifndef __LIST_H
|
||||
#define __LIST_H
|
||||
|
||||
#include <stdbool.h>
|
||||
#include "util.h"
|
||||
|
||||
/*
|
||||
* Declaration of a generic, undirected list for the "Datastructures
|
||||
* and algorithms" courses at the Department of Computing Science,
|
||||
* Umea University. The list stores void pointers, so it can be used
|
||||
* to store all types of values. After use, the function list_kill
|
||||
* must be called to de-allocate the dynamic memory used by the list
|
||||
* itself. The de-allocation of any dynamic memory allocated for the
|
||||
* element values is the responsibility of the user of the list,
|
||||
* unless a kill_function is registered in list_empty.
|
||||
*
|
||||
* Authors: Niclas Borlin (niclas@cs.umu.se)
|
||||
* Adam Dahlgren Lindstrom (dali@cs.umu.se)
|
||||
* Lars Karlsson (larsk@cs.umu.se)
|
||||
*
|
||||
* Based on earlier code by: Johan Eliasson (johane@cs.umu.se).
|
||||
*
|
||||
* Version information:
|
||||
* v1.0 2018-01-28: First public version.
|
||||
* v1.1 2023-01-19: Added list_pos_equal and list_pos_is_valid functions.
|
||||
* v1.2 2023-01-20: Renamed list_pos_equal to list_pos_are_equal.
|
||||
* v1.3 2023-03-23: Renamed list_pos_are_equal to list_pos_is_equal.
|
||||
* v2.0 2024-03-14: Added list_print_internal to output dot code for visualization.
|
||||
* v2.1 2024-05-10: Updated print_internal to enhance encapsulation.
|
||||
*/
|
||||
|
||||
// ==========PUBLIC DATA TYPES============
|
||||
|
||||
// List type.
|
||||
typedef struct list list;
|
||||
|
||||
// List position type.
|
||||
typedef struct cell *list_pos;
|
||||
|
||||
// ==========DATA STRUCTURE INTERFACE==========
|
||||
|
||||
/**
|
||||
* list_empty() - Create an empty list.
|
||||
* @kill_func: A pointer to a function (or NULL) to be called to
|
||||
* de-allocate memory on remove/kill.
|
||||
*
|
||||
* Returns: A pointer to the new list.
|
||||
*/
|
||||
list *list_empty(kill_function kill_func);
|
||||
|
||||
/**
|
||||
* list_is_empty() - Check if a list is empty.
|
||||
* @l: List to check.
|
||||
*
|
||||
* Returns: True if the list is empty, otherwise false.
|
||||
*/
|
||||
bool list_is_empty(const list *l);
|
||||
|
||||
/**
|
||||
* list_first() - Return the first position of a list, i.e. the
|
||||
* position of the first element in the list.
|
||||
* @l: List to inspect.
|
||||
*
|
||||
* Returns: The first position in the given list.
|
||||
*/
|
||||
list_pos list_first(const list *l);
|
||||
|
||||
/**
|
||||
* list_end() - Return the last position of a list, i.e. the position
|
||||
* after the last element in the list.
|
||||
* @l: List to inspect.
|
||||
*
|
||||
* Returns: The last position in the given list.
|
||||
*/
|
||||
list_pos list_end(const list *l);
|
||||
|
||||
/**
|
||||
* list_next() - Return the next position in a list.
|
||||
* @l: List to inspect.
|
||||
* @p: Any valid position except the last in the list.
|
||||
*
|
||||
* Returns: The position in the list after the given position.
|
||||
* NOTE: The return value is undefined for the last position.
|
||||
*/
|
||||
list_pos list_next(const list *l, const list_pos p);
|
||||
|
||||
/**
|
||||
* list_prev() - Return the previous position in a list.
|
||||
* @l: List to inspect.
|
||||
* @p: Any valid position except the first in the list.
|
||||
*
|
||||
* Returns: The position in the list before the given position.
|
||||
* NOTE: The return value is undefined for the first position.
|
||||
*/
|
||||
list_pos list_prev(const list *l, const list_pos p);
|
||||
|
||||
/**
|
||||
* list_inspect() - Return the value of the element at a given
|
||||
* position in a list.
|
||||
* @l: List to inspect.
|
||||
* @p: Any valid position in the list, except the last.
|
||||
*
|
||||
* Returns: Returns the value at the given position as a void pointer.
|
||||
* NOTE: The return value is undefined for the last position.
|
||||
*/
|
||||
void *list_inspect(const list *l, const list_pos p);
|
||||
|
||||
/**
|
||||
* list_insert() - Insert a new element with a given value into a list.
|
||||
* @l: List to manipulate.
|
||||
* @v: Value (pointer) to be inserted into the list.
|
||||
* @p: Position in the list before which the value should be inserted.
|
||||
*
|
||||
* Creates a new element and inserts it into the list before p.
|
||||
* Stores data in the new element.
|
||||
*
|
||||
* Returns: The position of the newly created element.
|
||||
*/
|
||||
list_pos list_insert(list *l, void *v, const list_pos p);
|
||||
|
||||
/**
|
||||
* list_remove() - Remove an element from a list.
|
||||
* @l: List to manipulate.
|
||||
* @p: Position in the list of the element to remove.
|
||||
*
|
||||
* Removes the element at position p from the list. If a kill_func
|
||||
* was registered at list creation, calls it to deallocate the memory
|
||||
* held by the element value.
|
||||
*
|
||||
* Returns: The position after the removed element.
|
||||
*/
|
||||
list_pos list_remove(list *l, const list_pos p);
|
||||
|
||||
/**
|
||||
* list_kill() - Destroy a given list.
|
||||
* @l: List to destroy.
|
||||
*
|
||||
* Returns all dynamic memory used by the list and its elements. If a
|
||||
* kill_func was registered at list creation, also calls it for each
|
||||
* element to return any user-allocated memory occupied by the element values.
|
||||
*
|
||||
* Returns: Nothing.
|
||||
*/
|
||||
void list_kill(list *l);
|
||||
|
||||
/**
|
||||
* list_print() - Iterate over the list elements and print their values.
|
||||
* @l: List to inspect.
|
||||
* @print_func: Function called for each element.
|
||||
*
|
||||
* Iterates over the list and calls print_func with the value stored
|
||||
* in each element.
|
||||
*
|
||||
* Returns: Nothing.
|
||||
*/
|
||||
void list_print(const list *l, inspect_callback print_func);
|
||||
|
||||
/**
|
||||
* list_pos_is_valid() - Return true for a valid position in a list
|
||||
* @l: List to inspect.
|
||||
* @p: Any position.
|
||||
*
|
||||
* Returns: True if p is a valid position in the list, otherwise false.
|
||||
*/
|
||||
bool list_pos_is_valid(const list *l, const list_pos p);
|
||||
|
||||
/**
|
||||
* list_pos_is_equal() - Return true if two positions in a list are equal.
|
||||
* @l: List to inspect.
|
||||
* @p1: First position to compare.
|
||||
* @p2: Second position to compare.
|
||||
*
|
||||
* Returns: True if p1 and p2 refer to the same position in l, otherwise False.
|
||||
* NOTE: The result is defined only if p1 and p2 are valid positions in l.
|
||||
* p1 and p2 are assumed to be valid positions in l, no check is performed.
|
||||
*/
|
||||
bool list_pos_is_equal(const list *l, const list_pos p1, const list_pos p2);
|
||||
|
||||
/**
|
||||
* list_print_internal() - Print the lists internal structure in dot format.
|
||||
* @l: List to inspect.
|
||||
* @print_func: Function called for each element value.
|
||||
* @desc: String with a description/state of the list, or NULL for no description.
|
||||
* @indent_level: Indentation level, 0 for outermost
|
||||
*
|
||||
* Iterates over the list and outputs dot code that shows the internal
|
||||
* structure of the list. The dot code can be visualized by
|
||||
* Graphviz.
|
||||
*
|
||||
* On linux system, the output can be parsed by the dot program, e.g.
|
||||
*
|
||||
* <list_program> | dot -Tsvg > /tmp/dot.svg; firefox /tmp/dot.svg
|
||||
*
|
||||
* where <list_program> is the name of the executable
|
||||
*
|
||||
* The output may also be possible to visualize online on
|
||||
* https://dreampuf.github.io/GraphvizOnline/ or google "graphviz
|
||||
* online".
|
||||
*
|
||||
* For documention of the dot language, see graphviz.org.
|
||||
*
|
||||
* Returns: Nothing.
|
||||
*/
|
||||
void list_print_internal(const list *l, inspect_callback print_func, const char *desc,
|
||||
int indent_level);
|
||||
|
||||
#endif
|
||||
131
OU3/include/queue.h
Normal file
131
OU3/include/queue.h
Normal file
@@ -0,0 +1,131 @@
|
||||
#ifndef __QUEUE_H
|
||||
#define __QUEUE_H
|
||||
|
||||
#include <stdbool.h>
|
||||
#include "util.h"
|
||||
|
||||
/*
|
||||
* Declaration of a generic queue for the "Datastructures and
|
||||
* algorithms" courses at the Department of Computing Science, Umea
|
||||
* University. The queue stores void pointers, so it can be used to
|
||||
* store all types of values. After use, the function queue_kill must
|
||||
* be called to de-allocate the dynamic memory used by the queue
|
||||
* itself. The de-allocation of any dynamic memory allocated for the
|
||||
* element values is the responsibility of the user of the queue,
|
||||
* unless a kill_function is registered in queue_empty.
|
||||
*
|
||||
* Authors: Niclas Borlin (niclas@cs.umu.se)
|
||||
* Adam Dahlgren Lindstrom (dali@cs.umu.se)
|
||||
*
|
||||
* Based on earlier code by: Johan Eliasson (johane@cs.umu.se).
|
||||
*
|
||||
* Version information:
|
||||
* v1.0 2018-01-28: First public version.
|
||||
* v1.1 2024-05-10: Added/updated print_internal to enhance encapsulation.
|
||||
*/
|
||||
|
||||
// ==========PUBLIC DATA TYPES============
|
||||
|
||||
// Queue type.
|
||||
typedef struct queue queue;
|
||||
|
||||
// ==========DATA STRUCTURE INTERFACE==========
|
||||
|
||||
/**
|
||||
* queue_empty() - Create an empty queue.
|
||||
* @kill_func: A pointer to a function (or NULL) to be called to
|
||||
* de-allocate memory on remove/kill.
|
||||
*
|
||||
* Returns: A pointer to the new queue.
|
||||
*/
|
||||
queue *queue_empty(kill_function kill_func);
|
||||
|
||||
/**
|
||||
* queue_is_empty() - Check if a queue is empty.
|
||||
* @q: Queue to check.
|
||||
*
|
||||
* Returns: True if queue is empty, otherwise false.
|
||||
*/
|
||||
bool queue_is_empty(const queue *q);
|
||||
|
||||
/**
|
||||
* queue_enqueue() - Put a value at the end of the queue.
|
||||
* @q: Queue to manipulate.
|
||||
* @v: Value (pointer) to be put in the queue.
|
||||
*
|
||||
* Returns: The modified queue.
|
||||
*/
|
||||
queue *queue_enqueue(queue *q, void *v);
|
||||
|
||||
/**
|
||||
* queue_dequeue() - Remove the element at the front of a queue.
|
||||
* @q: Queue to manipulate.
|
||||
*
|
||||
* NOTE: Undefined for an empty queue.
|
||||
*
|
||||
* Returns: The modified queue.
|
||||
*/
|
||||
queue *queue_dequeue(queue *q);
|
||||
|
||||
/**
|
||||
* queue_front() - Inspect the value at the front of the queue.
|
||||
* @q: Queue to inspect.
|
||||
*
|
||||
* Returns: The value at the top of the queue.
|
||||
* NOTE: The return value is undefined for an empty queue.
|
||||
*/
|
||||
void *queue_front(const queue *q);
|
||||
|
||||
/**
|
||||
* queue_kill() - Destroy a given queue.
|
||||
* @q: Queue to destroy.
|
||||
*
|
||||
* Return all dynamic memory used by the queue and its elements. If a
|
||||
* kill_func was registered at queue creation, also calls it for each
|
||||
* element to kill any user-allocated memory occupied by the element values.
|
||||
*
|
||||
* Returns: Nothing.
|
||||
*/
|
||||
void queue_kill(queue *q);
|
||||
|
||||
/**
|
||||
* queue_print() - Iterate over the queue elements and print their values.
|
||||
* @q: Queue to inspect.
|
||||
* @print_func: Function called for each element.
|
||||
*
|
||||
* Iterates over the queue and calls print_func with the value stored
|
||||
* in each element.
|
||||
*
|
||||
* Returns: Nothing.
|
||||
*/
|
||||
void queue_print(const queue *q, inspect_callback print_func);
|
||||
|
||||
/**
|
||||
* queue_print_internal() - Print the internal structure of the queue in dot format.
|
||||
* @q: Queue to inspect.
|
||||
* @print_func: Function called for each element value.
|
||||
* @desc: String with a description/state of the queue, or NULL for no description.
|
||||
* @indent_level: Indentation level, 0 for outermost
|
||||
*
|
||||
* Iterates over the queue and outputs dot code that shows the internal
|
||||
* structure of the queue. The dot code can be visualized by
|
||||
* Graphviz.
|
||||
*
|
||||
* On linux system, the output can be parsed by the dot program, e.g.
|
||||
*
|
||||
* <queue_program> | dot -Tsvg > /tmp/dot.svg; firefox /tmp/dot.svg
|
||||
*
|
||||
* where <queue_program> is the name of the executable
|
||||
*
|
||||
* The output may also be possible to visualize online on
|
||||
* https://dreampuf.github.io/GraphvizOnline/ or google "graphviz
|
||||
* online".
|
||||
*
|
||||
* For documention of the dot language, see graphviz.org.
|
||||
*
|
||||
* Returns: Nothing.
|
||||
*/
|
||||
void queue_print_internal(const queue *q, inspect_callback print_func, const char *desc,
|
||||
int indent_level);
|
||||
|
||||
#endif
|
||||
133
OU3/include/stack.h
Normal file
133
OU3/include/stack.h
Normal file
@@ -0,0 +1,133 @@
|
||||
#ifndef __STACK_H
|
||||
#define __STACK_H
|
||||
|
||||
#include <stdbool.h>
|
||||
#include "util.h"
|
||||
|
||||
/*
|
||||
* Declaration of a generic stack for the "Datastructures and
|
||||
* algorithms" courses at the Department of Computing Science, Umea
|
||||
* University. The stack stores void pointers, so it can be used to
|
||||
* store all types of values. After use, the function stack_kill must
|
||||
* be called to de-allocate the dynamic memory used by the stack
|
||||
* itself. The de-allocation of any dynamic memory allocated for the
|
||||
* element values is the responsibility of the user of the stack,
|
||||
* unless a kill_function is registered in stack_empty.
|
||||
*
|
||||
* Authors: Niclas Borlin (niclas@cs.umu.se)
|
||||
* Adam Dahlgren Lindstrom (dali@cs.umu.se)
|
||||
*
|
||||
* Based on earlier code by: Johan Eliasson (johane@cs.umu.se).
|
||||
*
|
||||
* Version information:
|
||||
* v1.0 2018-01-28: First public version.
|
||||
* v1.1 2024-05-10: Added/updated print_internal to enhance encapsulation.
|
||||
*/
|
||||
|
||||
// ==========PUBLIC DATA TYPES============
|
||||
|
||||
// Stack type.
|
||||
typedef struct stack stack;
|
||||
|
||||
// ==========DATA STRUCTURE INTERFACE==========
|
||||
|
||||
/**
|
||||
* stack_empty() - Create an empty stack.
|
||||
* @kill_func: A pointer to a function (or NULL) to be called to
|
||||
* de-allocate memory on remove/kill.
|
||||
*
|
||||
* Returns: A pointer to the new stack.
|
||||
*/
|
||||
stack *stack_empty(kill_function kill_func);
|
||||
|
||||
/**
|
||||
* stack_is_empty() - Check if a stack is empty.
|
||||
* @s: Stack to check.
|
||||
*
|
||||
* Returns: True if stack is empty, otherwise false.
|
||||
*/
|
||||
bool stack_is_empty(const stack *s);
|
||||
|
||||
/**
|
||||
* stack_push() - Push a value on top of a stack.
|
||||
* @s: Stack to manipulate.
|
||||
* @v: Value (pointer) to be put on the stack.
|
||||
*
|
||||
* Returns: The modified stack.
|
||||
* NOTE: After the call, the input stack should be considered invalid.
|
||||
*/
|
||||
stack *stack_push(stack *s, void *v);
|
||||
|
||||
/**
|
||||
* stack_pop() - Remove the element at the top of a stack.
|
||||
* @s: Stack to manipulate.
|
||||
*
|
||||
* NOTE: Undefined for an empty stack.
|
||||
*
|
||||
* Returns: The modified stack.
|
||||
* NOTE: After the call, the input stack should be considered invalid.
|
||||
*/
|
||||
stack *stack_pop(stack *s);
|
||||
|
||||
/**
|
||||
* stack_top() - Inspect the value at the top of the stack.
|
||||
* @s: Stack to inspect.
|
||||
*
|
||||
* Returns: The value at the top of the stack.
|
||||
* NOTE: The return value is undefined for an empty stack.
|
||||
*/
|
||||
void *stack_top(const stack *s);
|
||||
|
||||
/**
|
||||
* stack_kill() - Destroy a given stack.
|
||||
* @s: Stack to destroy.
|
||||
*
|
||||
* Return all dynamic memory used by the stack and its elements. If a
|
||||
* kill_func was registered at stack creation, also calls it for each
|
||||
* element to kill any user-allocated memory occupied by the element values.
|
||||
*
|
||||
* Returns: Nothing.
|
||||
*/
|
||||
void stack_kill(stack *s);
|
||||
|
||||
/**
|
||||
* stack_print() - Iterate over the stack elements and print their values.
|
||||
* @s: Stack to inspect.
|
||||
* @print_func: Function called for each element.
|
||||
*
|
||||
* Iterates over the stack and calls print_func with the value stored
|
||||
* in each element.
|
||||
*
|
||||
* Returns: Nothing.
|
||||
*/
|
||||
void stack_print(const stack *s, inspect_callback print_func);
|
||||
|
||||
/**
|
||||
* stack_print_internal() - Print the stacks internal structure in dot format.
|
||||
* @l: Stack to inspect.
|
||||
* @print_func: Function called for each element value.
|
||||
* @desc: String with a description/state of the stack, or NULL for no description.
|
||||
* @indent_level: Indentation level, 0 for outermost
|
||||
*
|
||||
* Iterates over the stack and outputs dot code that shows the internal
|
||||
* structure of the stack. The dot code can be visualized by
|
||||
* Graphviz.
|
||||
*
|
||||
* On linux system, the output can be parsed by the dot program, e.g.
|
||||
*
|
||||
* <stack_program> | dot -Tsvg > /tmp/dot.svg; firefox /tmp/dot.svg
|
||||
*
|
||||
* where <stack_program> is the name of the executable
|
||||
*
|
||||
* The output may also be possible to visualize online on
|
||||
* https://dreampuf.github.io/GraphvizOnline/ or google "graphviz
|
||||
* online".
|
||||
*
|
||||
* For documention of the dot language, see graphviz.org.
|
||||
*
|
||||
* Returns: Nothing.
|
||||
*/
|
||||
void stack_print_internal(const stack *s, inspect_callback print_func, const char *desc,
|
||||
int indent_level);
|
||||
|
||||
#endif
|
||||
157
OU3/include/table.h
Normal file
157
OU3/include/table.h
Normal file
@@ -0,0 +1,157 @@
|
||||
#ifndef TABLE_H
|
||||
#define TABLE_H
|
||||
|
||||
#include <stdbool.h>
|
||||
#include "util.h"
|
||||
|
||||
/*
|
||||
* Declaration of a generic table for the "Datastructures and
|
||||
* algorithms" courses at the Department of Computing Science, Umea
|
||||
* University. The table stores void pointers, so it can be used to
|
||||
* store all types of values. After use, the function table_kill must
|
||||
* be called to de-allocate the dynamic memory used by the table
|
||||
* itself. The de-allocation of any dynamic memory allocated for the
|
||||
* key and/or value values is the responsibility of the user of the
|
||||
* table, unless a corresponding kill_function is registered in
|
||||
* table_empty.
|
||||
*
|
||||
* The internal handling of duplicates are transparent to the user of
|
||||
* the table. Depending on the table design, a duplicate may or may
|
||||
* not be stored internally. The user of the table should not make any
|
||||
* assumption about which solution is implemented.
|
||||
*
|
||||
* Authors: Niclas Borlin (niclas@cs.umu.se)
|
||||
* Adam Dahlgren Lindstrom (dali@cs.umu.se)
|
||||
*
|
||||
* Based on earlier code by: Johan Eliasson (johane@cs.umu.se).
|
||||
*
|
||||
* Version information:
|
||||
* v1.0 2018-02-06: First public version.
|
||||
* v1.1 2024-04-15: Changed the comments to leave room for different
|
||||
* internal handling of duplicates.
|
||||
* v1.2 2024-05-10: Added/updated print_internal to enhance encapsulation.
|
||||
*/
|
||||
|
||||
// ==========PUBLIC DATA TYPES============
|
||||
// Table type.
|
||||
typedef struct table table;
|
||||
|
||||
// ==========DATA STRUCTURE INTERFACE==========
|
||||
|
||||
/**
|
||||
* table_empty() - Create an empty table.
|
||||
* @key_cmp_func: A pointer to a function to be used to compare keys. See
|
||||
* util.h for the definition of compare_function.
|
||||
* @key_kill_func: A pointer to a function (or NULL) to be called to
|
||||
* de-allocate memory for keys on remove/kill.
|
||||
* @value_kill_func: A pointer to a function (or NULL) to be called to
|
||||
* de-allocate memory for values on remove/kill.
|
||||
*
|
||||
* Returns: Pointer to a new table.
|
||||
*/
|
||||
table *table_empty(compare_function key_cmp_func,
|
||||
kill_function key_kill_func,
|
||||
kill_function value_kill_func);
|
||||
|
||||
/**
|
||||
* table_is_empty() - Check if a table is empty.
|
||||
* @t: Table to check.
|
||||
*
|
||||
* Returns: True if table contains no key/value pairs, false otherwise.
|
||||
*/
|
||||
bool table_is_empty(const table *t);
|
||||
|
||||
/**
|
||||
* table_insert() - Add a key/value pair to a table.
|
||||
* @t: Table to manipulate.
|
||||
* @key: A pointer to the key value.
|
||||
* @value: A pointer to the value value.
|
||||
*
|
||||
* Insert the key/value pair into the table. If the key already
|
||||
* exists, the key/value overwrites the existing pair. The technical
|
||||
* details are internal to the datatype. At any rate, table_lookup()
|
||||
* will return the latest added value for a duplicate
|
||||
* key and table_remove() will remove all duplicates for a given key.
|
||||
*
|
||||
* Returns: Nothing.
|
||||
*/
|
||||
void table_insert(table *t, void *key, void *value);
|
||||
|
||||
/**
|
||||
* table_lookup() - Look up a given key in a table.
|
||||
* @t: Table to inspect.
|
||||
* @key: Key to look up.
|
||||
*
|
||||
* Returns: The value corresponding to a given key, or NULL if the key
|
||||
* is not found in the table. If the table contains duplicate keys,
|
||||
* the value that was latest inserted will be returned.
|
||||
*/
|
||||
void *table_lookup(const table *t, const void *key);
|
||||
|
||||
/**
|
||||
* table_choose_key() - Return an arbitrary key.
|
||||
* @t: Table to inspect.
|
||||
*
|
||||
* Return an arbitrary key stored in the table. Can be used together
|
||||
* with table_remove() to deconstruct the table. Undefined for an
|
||||
* empty table.
|
||||
*
|
||||
* Returns: An arbitrary key stored in the table.
|
||||
*/
|
||||
void *table_choose_key(const table *t);
|
||||
|
||||
/**
|
||||
* table_remove() - Remove a key/value pair in the table.
|
||||
* @t: Table to manipulate.
|
||||
* @key: Key for which to remove pair.
|
||||
*
|
||||
* Any matching duplicates will be removed. Will call any kill
|
||||
* functions set for keys/values. Does nothing if key is not found in
|
||||
* the table.
|
||||
*
|
||||
* Returns: Nothing.
|
||||
*/
|
||||
void table_remove(table *t, const void *key);
|
||||
|
||||
/**
|
||||
* table_kill() - Destroy a table.
|
||||
* @t: Table to destroy.
|
||||
*
|
||||
* Return all dynamic memory used by the table and its elements. If a
|
||||
* kill_func was registered for keys and/or values at table creation,
|
||||
* it is called each element to free any user-allocated memory
|
||||
* occupied by the element values.
|
||||
*
|
||||
* Returns: Nothing.
|
||||
*/
|
||||
void table_kill(table *t);
|
||||
|
||||
/**
|
||||
* table_print() - Print the given table.
|
||||
* @t: Table to print.
|
||||
* @print_func: Function called for each key/value pair in the table.
|
||||
*
|
||||
* Iterates over the key/value pairs in the table and prints them.
|
||||
* Will print all stored elements, including any duplicates.
|
||||
*
|
||||
* Returns: Nothing.
|
||||
*/
|
||||
void table_print(const table *t, inspect_callback_pair print_func);
|
||||
|
||||
/**
|
||||
* table_print_internal() - Output the internal structure of the table.
|
||||
* @t: Table to print.
|
||||
* @key_print_func: Function called for each key in the table.
|
||||
* @value_print_func: Function called for each value in the table.
|
||||
* @desc: String with a description/state of the list.
|
||||
* @indent_level: Indentation level, 0 for outermost
|
||||
*
|
||||
* Iterates over the list and prints code that shows its' internal structure.
|
||||
*
|
||||
* Returns: Nothing.
|
||||
*/
|
||||
void table_print_internal(const table *t, inspect_callback key_print_func,
|
||||
inspect_callback value_print_func, const char *desc,
|
||||
int indent_level);
|
||||
|
||||
#endif
|
||||
83
OU3/include/util.h
Normal file
83
OU3/include/util.h
Normal file
@@ -0,0 +1,83 @@
|
||||
#ifndef __UTIL_H
|
||||
#define __UTIL_H
|
||||
|
||||
/*
|
||||
* Utility function types for deallocating, printing and comparing
|
||||
* values stored by various data types.
|
||||
*
|
||||
* Authors: Niclas Borlin (niclas@cs.umu.se)
|
||||
* Adam Dahlgren Lindstrom (dali@cs.umu.se)
|
||||
*
|
||||
* Based on earlier code by: Johan Eliasson (johane@cs.umu.se).
|
||||
*
|
||||
* Version information:
|
||||
* v1.0 2018-01-28: First public version.
|
||||
* v1.1 2018-02-06: Updated explanation for the compare_function.
|
||||
* v1.2 2023-01-14: Added version define constants and strings.
|
||||
* v1.3 2024-03-13: Added PTR2ADDR macro.
|
||||
*/
|
||||
|
||||
// Macros to create a version string out of version constants
|
||||
#define STR_HELPER(x) #x
|
||||
#define STR(x) STR_HELPER(x)
|
||||
|
||||
// Version constants
|
||||
#define CODE_BASE_MAJOR_VERSION 2
|
||||
#define CODE_BASE_MINOR_VERSION 2
|
||||
#define CODE_BASE_REVISION 2
|
||||
#define CODE_BASE_PATCH 2
|
||||
|
||||
#define CODE_BASE_RELEASE_DATE "2025-01-24"
|
||||
|
||||
// Create a short version string
|
||||
#define CODE_BASE_VERSION "v" \
|
||||
STR(CODE_BASE_MAJOR_VERSION) \
|
||||
"." \
|
||||
STR(CODE_BASE_MINOR_VERSION) \
|
||||
"." \
|
||||
STR(CODE_BASE_REVISION) \
|
||||
"." \
|
||||
STR(CODE_BASE_PATCH)
|
||||
|
||||
// Create a version string
|
||||
#define CODE_BASE_LONG_VERSION "Version: " \
|
||||
STR(CODE_BASE_MAJOR_VERSION) \
|
||||
"." \
|
||||
STR(CODE_BASE_MINOR_VERSION) \
|
||||
"." \
|
||||
STR(CODE_BASE_REVISION) \
|
||||
"." \
|
||||
STR(CODE_BASE_PATCH)
|
||||
|
||||
// Type definition for de-allocator function, e.g. free().
|
||||
typedef void (*kill_function)(void *);
|
||||
|
||||
// For backwards compatibility with pre-v2.0 code.
|
||||
typedef kill_function free_function;
|
||||
|
||||
// Type definition for read-only callback for single-value containers,
|
||||
// used by e.g. print functions.
|
||||
typedef void (*inspect_callback)(const void *);
|
||||
|
||||
// Ditto for dual-value containers.
|
||||
typedef void (*inspect_callback_pair)(const void *, const void *);
|
||||
|
||||
// Type definition for comparison function, used by e.g. table.
|
||||
//
|
||||
// Comparison functions should return values that indicate the order
|
||||
// of the arguments. If the first argument is considered less/lower
|
||||
// than the second, a negative value should be returned. If the first
|
||||
// argument is considered more/higher than the second, a positive value
|
||||
// should be returned. If the arguments are considered equal, a zero
|
||||
// value should be returned.
|
||||
typedef int compare_function(const void *,const void *);
|
||||
|
||||
// Constant used by ptr2addr, used by various print_internal functions.
|
||||
#define PTR2ADDR_MASK 0xffff
|
||||
|
||||
// Macro that acts as a function to convert the address in the pointer
|
||||
// p into an unsigned long value, keeping only the part indicated by
|
||||
// the mask.
|
||||
#define PTR2ADDR(p) (((unsigned long)p) & PTR2ADDR_MASK)
|
||||
|
||||
#endif
|
||||
1
OU3/lib/.gitignore
vendored
Normal file
1
OU3/lib/.gitignore
vendored
Normal file
@@ -0,0 +1 @@
|
||||
libdoa.a
|
||||
34
OU3/lib/Makefile
Normal file
34
OU3/lib/Makefile
Normal file
@@ -0,0 +1,34 @@
|
||||
SRC = ../src/list/list.c ../src/stack/stack.c \
|
||||
../src/array_2d/array_2d.c ../src/table/table.c \
|
||||
../src/table/table2.c ../src/array_1d/array_1d.c \
|
||||
../src/queue/queue.c ../src/dlist/dlist.c \
|
||||
../src/version/version.c
|
||||
H = ../include/queue.h ../include/dlist.h ../include/array_2d.h \
|
||||
../include/util.h ../include/table.h ../include/list.h \
|
||||
../include/array_1d.h ../include/stack.h
|
||||
|
||||
OBJ = $(SRC:.c=.o)
|
||||
|
||||
LIB = libdoa.a
|
||||
|
||||
CC = gcc
|
||||
CFLAGS = -std=c99 -Wall -I../include -g
|
||||
|
||||
all: lib
|
||||
|
||||
# Library
|
||||
lib: $(LIB)
|
||||
|
||||
# Object file for library
|
||||
$(LIB): $(OBJ) $(H)
|
||||
$(AR) r $@ $(OBJ)
|
||||
ranlib $@
|
||||
|
||||
# Clean up
|
||||
clean:
|
||||
-rm -f $(OBJ)
|
||||
|
||||
# Clean up
|
||||
cleaner:
|
||||
-rm -f $(LIB) $(OBJ)
|
||||
|
||||
697
OU3/mtftable.c
Normal file
697
OU3/mtftable.c
Normal file
@@ -0,0 +1,697 @@
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <ctype.h> // For isspace()
|
||||
#include <stdarg.h>
|
||||
|
||||
#include <table.h>
|
||||
#include <dlist.h>
|
||||
|
||||
/*
|
||||
* Implementation of a generic table with the slight change of
|
||||
* inserting looked up value in front of the list for faster
|
||||
* lookup next time.
|
||||
*
|
||||
* Duplicates are handled by inspect and remove.
|
||||
*
|
||||
* Authors: Marc Meunier (tfy22mmr@cs.umu.se)
|
||||
*
|
||||
*
|
||||
* Based on earlier code by: Johan Eliasson (johane@cs.umu.se)
|
||||
* Niclas Borlin (niclas@cs.umu.se)
|
||||
* Adam Dahlgren Lindstrom (dali@cs.umu.se)
|
||||
*
|
||||
* Version information:
|
||||
* v1.0 2018-02-06: First public version.
|
||||
*/
|
||||
|
||||
// ===========INTERNAL DATA TYPES ============
|
||||
|
||||
struct table {
|
||||
dlist *entries; // The table entries are stored in a directed list
|
||||
compare_function *key_cmp_func;
|
||||
kill_function key_kill_func;
|
||||
kill_function value_kill_func;
|
||||
};
|
||||
|
||||
typedef struct table_entry {
|
||||
void *key;
|
||||
void *value;
|
||||
} table_entry;
|
||||
|
||||
// ===========INTERNAL FUNCTION IMPLEMENTATIONS ============
|
||||
|
||||
/**
|
||||
* table_entry_create() - Allocate and populate a table entry.
|
||||
* @key: A pointer to a function to be used to compare keys.
|
||||
* @value: A pointer to a function (or NULL) to be called to
|
||||
* de-allocate memory for keys on remove/kill.
|
||||
*
|
||||
* Returns: A pointer to the newly created table entry.
|
||||
*/
|
||||
table_entry *table_entry_create(void *key, void *value)
|
||||
{
|
||||
// Allocate space for a table entry. Use calloc as a defensive
|
||||
// measure to ensure that all pointers are initialized to NULL.
|
||||
table_entry *e = calloc(1, sizeof(*e));
|
||||
// Populate the entry.
|
||||
e->key = key;
|
||||
e->value = value;
|
||||
|
||||
return e;
|
||||
}
|
||||
|
||||
/**
|
||||
* table_entry_kill() - Return the memory allocated to a table entry.
|
||||
* @e: The table entry to deallocate.
|
||||
*
|
||||
* Returns: Nothing.
|
||||
*/
|
||||
void table_entry_kill(void *v)
|
||||
{
|
||||
table_entry *e = v; // Convert the pointer (useful if debugging the code)
|
||||
|
||||
// All we need to do is to deallocate the struct.
|
||||
free(e);
|
||||
}
|
||||
|
||||
/**
|
||||
* table_empty() - Create an empty table.
|
||||
* @key_cmp_func: A pointer to a function to be used to compare keys.
|
||||
* @key_kill_func: A pointer to a function (or NULL) to be called to
|
||||
* de-allocate memory for keys on remove/kill.
|
||||
* @value_kill_func: A pointer to a function (or NULL) to be called to
|
||||
* de-allocate memory for values on remove/kill.
|
||||
*
|
||||
* Returns: Pointer to a new table.
|
||||
*/
|
||||
table *table_empty(compare_function *key_cmp_func,
|
||||
kill_function key_kill_func,
|
||||
kill_function value_kill_func)
|
||||
{
|
||||
// Allocate the table header.
|
||||
table *t = calloc(1, sizeof(table));
|
||||
// Create the list to hold the table_entry-ies.
|
||||
t->entries = dlist_empty(NULL);
|
||||
// Store the key compare function and key/value kill functions.
|
||||
t->key_cmp_func = key_cmp_func;
|
||||
t->key_kill_func = key_kill_func;
|
||||
t->value_kill_func = value_kill_func;
|
||||
|
||||
return t;
|
||||
}
|
||||
|
||||
/**
|
||||
* table_is_empty() - Check if a table is empty.
|
||||
* @table: Table to check.
|
||||
*
|
||||
* Returns: True if table contains no key/value pairs, false otherwise.
|
||||
*/
|
||||
bool table_is_empty(const table *t)
|
||||
{
|
||||
return dlist_is_empty(t->entries);
|
||||
}
|
||||
|
||||
/**
|
||||
* table_insert() - Add a key/value pair to a table.
|
||||
* @table: Table to manipulate.
|
||||
* @key: A pointer to the key value.
|
||||
* @value: A pointer to the value value.
|
||||
*
|
||||
* Insert the key/value pair into the table. No test is performed to
|
||||
* check if key is a duplicate. table_lookup() will return the latest
|
||||
* added value for a duplicate key. table_remove() will remove all
|
||||
* duplicates for a given key.
|
||||
*
|
||||
* Returns: Nothing.
|
||||
*/
|
||||
void table_insert(table *t, void *key, void *value)
|
||||
{
|
||||
// Allocate the key/value structure.
|
||||
table_entry *e = table_entry_create(key, value);
|
||||
|
||||
dlist_insert(t->entries, e, dlist_first(t->entries));
|
||||
}
|
||||
|
||||
/**
|
||||
* table_lookup() - Look up a given key in a table.
|
||||
* @table: Table to inspect.
|
||||
* @key: Key to look up.
|
||||
*
|
||||
* Returns: The value corresponding to a given key, or NULL if the key
|
||||
* is not found in the table. If the table contains duplicate keys,
|
||||
* the value that was latest inserted will be returned. The looked up
|
||||
* entry will be placed first in talbe for faster lookup.
|
||||
*/
|
||||
void *table_lookup(const table *t, const void *key)
|
||||
{
|
||||
dlist_pos pos = dlist_first(t->entries);
|
||||
// The value pointer to return.
|
||||
void *value_ptr = NULL;
|
||||
//dlist_pos pos_before = pos;
|
||||
|
||||
while (!dlist_is_end(t->entries, pos)) {
|
||||
// Inspect the table entry
|
||||
table_entry *e = dlist_inspect(t->entries, pos);
|
||||
// Check if the entry key matches the search key.
|
||||
if (t->key_cmp_func(e->key, key) == 0) {
|
||||
value_ptr = e->value;
|
||||
//pos_before = pos;
|
||||
pos = dlist_remove(t->entries, pos);
|
||||
// inserts value in front
|
||||
dlist_insert(t->entries, e, dlist_first(t->entries));
|
||||
return value_ptr;
|
||||
}
|
||||
else {
|
||||
// Continue with the next position.
|
||||
pos = dlist_next(t->entries, pos);
|
||||
}
|
||||
}
|
||||
return value_ptr;
|
||||
}
|
||||
|
||||
/**
|
||||
* table_choose_key() - Return an arbitrary key.
|
||||
* @t: Table to inspect.
|
||||
*
|
||||
* Return an arbitrary key stored in the table. Can be used together
|
||||
* with table_remove() to deconstruct the table. Undefined for an
|
||||
* empty table.
|
||||
*
|
||||
* Returns: An arbitrary key stored in the table.
|
||||
*/
|
||||
void *table_choose_key(const table *t)
|
||||
{
|
||||
// Return first key value.
|
||||
dlist_pos pos = dlist_first(t->entries);
|
||||
table_entry *e = dlist_inspect(t->entries, pos);
|
||||
|
||||
return e->key;
|
||||
}
|
||||
|
||||
/**
|
||||
* table_remove() - Remove a key/value pair in the table.
|
||||
* @table: Table to manipulate.
|
||||
* @key: Key for which to remove pair.
|
||||
*
|
||||
* Any matching duplicates will be removed. Will call any kill
|
||||
* functions set for keys/values. Does nothing if key is not found in
|
||||
* the table.
|
||||
*
|
||||
* Returns: Nothing.
|
||||
*/
|
||||
void table_remove(table *t, const void *key)
|
||||
{
|
||||
// Will be set if we need to delay a free.
|
||||
void *deferred_ptr = NULL;
|
||||
|
||||
// Start at beginning of the list.
|
||||
dlist_pos pos = dlist_first(t->entries);
|
||||
|
||||
// Iterate over the list. Remove any entries with matching keys.
|
||||
while (!dlist_is_end(t->entries, pos)) {
|
||||
// Inspect the table entry
|
||||
table_entry *e = dlist_inspect(t->entries, pos);
|
||||
|
||||
// Compare the supplied key with the key of this entry.
|
||||
if (t->key_cmp_func(e->key, key) == 0) {
|
||||
// If we have a match, call kill on the key
|
||||
// and/or value if given the responsiblity
|
||||
if (t->key_kill_func != NULL) {
|
||||
if (e->key == key) {
|
||||
// The given key points to the same
|
||||
// memory as entry->key. Freeing it here
|
||||
// would trigger a memory error in the
|
||||
// next iteration. Instead, defer free
|
||||
// of this pointer to the very end.
|
||||
deferred_ptr = e->key;
|
||||
} else {
|
||||
t->key_kill_func(e->key);
|
||||
}
|
||||
}
|
||||
if (t->value_kill_func != NULL) {
|
||||
t->value_kill_func(e->value);
|
||||
}
|
||||
// Remove the list element itself.
|
||||
pos = dlist_remove(t->entries, pos);
|
||||
// Deallocate the table entry structure.
|
||||
table_entry_kill(e);
|
||||
} else {
|
||||
// No match, move on to next element in the list.
|
||||
pos = dlist_next(t->entries, pos);
|
||||
}
|
||||
}
|
||||
if (deferred_ptr != NULL) {
|
||||
// Take care of the delayed free.
|
||||
t->key_kill_func(deferred_ptr);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* table_kill() - Destroy a table.
|
||||
* @table: Table to destroy.
|
||||
*
|
||||
* Return all dynamic memory used by the table and its elements. If a
|
||||
* kill_func was registered for keys and/or values at table creation,
|
||||
* it is called each element to kill any user-allocated memory
|
||||
* occupied by the element values.
|
||||
*
|
||||
* Returns: Nothing.
|
||||
*/
|
||||
void table_kill(table *t)
|
||||
{
|
||||
// Iterate over the list. Destroy all elements.
|
||||
dlist_pos pos = dlist_first(t->entries);
|
||||
|
||||
while (!dlist_is_end(t->entries, pos)) {
|
||||
// Inspect the key/value pair.
|
||||
table_entry *e = dlist_inspect(t->entries, pos);
|
||||
// Kill key and/or value if given the authority to do so.
|
||||
if (t->key_kill_func != NULL) {
|
||||
t->key_kill_func(e->key);
|
||||
}
|
||||
if (t->value_kill_func != NULL) {
|
||||
t->value_kill_func(e->value);
|
||||
}
|
||||
// Move on to next element.
|
||||
pos = dlist_next(t->entries, pos);
|
||||
// Deallocate the table entry structure.
|
||||
table_entry_kill(e);
|
||||
}
|
||||
|
||||
// Kill what's left of the list...
|
||||
dlist_kill(t->entries);
|
||||
// ...and the table struct.
|
||||
free(t);
|
||||
}
|
||||
|
||||
/**
|
||||
* table_print() - Print the given table.
|
||||
* @t: Table to print.
|
||||
* @print_func: Function called for each key/value pair in the table.
|
||||
*
|
||||
* Iterates over the key/value pairs in the table and prints them.
|
||||
* Will print all stored elements, including duplicates.
|
||||
*
|
||||
* Returns: Nothing.
|
||||
*/
|
||||
void table_print(const table *t, inspect_callback_pair print_func)
|
||||
{
|
||||
// Iterate over all elements. Call print_func on keys/values.
|
||||
dlist_pos pos = dlist_first(t->entries);
|
||||
|
||||
while (!dlist_is_end(t->entries, pos)) {
|
||||
table_entry *e = dlist_inspect(t->entries, pos);
|
||||
// Call print_func
|
||||
print_func(e->key, e->value);
|
||||
pos = dlist_next(t->entries, pos);
|
||||
}
|
||||
}
|
||||
|
||||
// ===========INTERNAL FUNCTIONS USED BY list_print_internal ============
|
||||
|
||||
// The functions below output code in the dot language, used by
|
||||
// GraphViz. For documention of the dot language, see graphviz.org.
|
||||
|
||||
/**
|
||||
* indent() - Output indentation string.
|
||||
* @n: Indentation level.
|
||||
*
|
||||
* Print n tab characters.
|
||||
*
|
||||
* Returns: Nothing.
|
||||
*/
|
||||
static void indent(int n)
|
||||
{
|
||||
for (int i=0; i<n; i++) {
|
||||
printf("\t");
|
||||
}
|
||||
}
|
||||
/**
|
||||
* iprintf(...) - Indent and print.
|
||||
* @n: Indentation level
|
||||
* @...: printf arguments
|
||||
*
|
||||
* Print n tab characters and calls printf.
|
||||
*
|
||||
* Returns: Nothing.
|
||||
*/
|
||||
static void iprintf(int n, const char *fmt, ...)
|
||||
{
|
||||
// Indent...
|
||||
indent(n);
|
||||
// ...and call printf
|
||||
va_list args;
|
||||
va_start(args, fmt);
|
||||
vprintf(fmt, args);
|
||||
va_end(args);
|
||||
}
|
||||
|
||||
/**
|
||||
* print_edge() - Print a edge between two addresses.
|
||||
* @from: The address of the start of the edge. Should be non-NULL.
|
||||
* @to: The address of the destination for the edge, including NULL.
|
||||
* @port: The name of the port on the source node, or NULL.
|
||||
* @label: The label for the edge, or NULL.
|
||||
* @options: A string with other edge options, or NULL.
|
||||
*
|
||||
* Print an edge from port PORT on node FROM to TO with label
|
||||
* LABEL. If to is NULL, the destination is the NULL node, otherwise a
|
||||
* memory node. If the port is NULL, the edge starts at the node, not
|
||||
* a specific port on it. If label is NULL, no label is used. The
|
||||
* options string, if non-NULL, is printed before the label.
|
||||
*
|
||||
* Returns: Nothing.
|
||||
*/
|
||||
static void print_edge(int indent_level, const void *from, const void *to, const char *port,
|
||||
const char *label, const char *options)
|
||||
{
|
||||
indent(indent_level);
|
||||
if (port) {
|
||||
printf("m%04lx:%s -> ", PTR2ADDR(from), port);
|
||||
} else {
|
||||
printf("m%04lx -> ", PTR2ADDR(from));
|
||||
}
|
||||
if (to == NULL) {
|
||||
printf("NULL");
|
||||
} else {
|
||||
printf("m%04lx", PTR2ADDR(to));
|
||||
}
|
||||
printf(" [");
|
||||
if (options != NULL) {
|
||||
printf("%s", options);
|
||||
}
|
||||
if (label != NULL) {
|
||||
printf(" label=\"%s\"",label);
|
||||
}
|
||||
printf("]\n");
|
||||
}
|
||||
|
||||
/**
|
||||
* print_head_node() - Print a node corresponding to the table struct.
|
||||
* @indent_level: Indentation level.
|
||||
* @t: Table to inspect.
|
||||
*
|
||||
* Returns: Nothing.
|
||||
*/
|
||||
static void print_head_node(int indent_level, const table *t)
|
||||
{
|
||||
iprintf(indent_level, "m%04lx [shape=record "
|
||||
"label=\"<e>entries\\n%04lx|cmp\\n%04lx|key_kill\\n%04lx|value_kill\\n%04lx\"]\n",
|
||||
PTR2ADDR(t), PTR2ADDR(t->entries), PTR2ADDR(t->key_cmp_func),
|
||||
PTR2ADDR(t->key_kill_func), PTR2ADDR(t->value_kill_func));
|
||||
}
|
||||
|
||||
// Internal function to print the head--entries edge in dot format.
|
||||
static void print_head_edge(int indent_level, const table *t)
|
||||
{
|
||||
print_edge(indent_level, t, t->entries, "e", "entries", NULL);
|
||||
}
|
||||
|
||||
// Internal function to print the table entry node in dot format.
|
||||
static void print_element_node(int indent_level, const table_entry *e)
|
||||
{
|
||||
iprintf(indent_level, "m%04lx [shape=record label=\"<k>key\\n%04lx|<v>value\\n%04lx\"]\n",
|
||||
PTR2ADDR(e), PTR2ADDR(e->key), PTR2ADDR(e->value));
|
||||
}
|
||||
|
||||
// Internal function to print the table entry node in dot format.
|
||||
static void print_key_value_nodes(int indent_level, const table_entry *e,
|
||||
inspect_callback key_print_func,
|
||||
inspect_callback value_print_func)
|
||||
{
|
||||
if (e->key != NULL) {
|
||||
iprintf(indent_level, "m%04lx [label=\"", PTR2ADDR(e->key));
|
||||
if (key_print_func != NULL) {
|
||||
key_print_func(e->key);
|
||||
}
|
||||
printf("\" xlabel=\"%04lx\"]\n", PTR2ADDR(e->key));
|
||||
}
|
||||
if (e->value != NULL) {
|
||||
iprintf(indent_level, "m%04lx [label=\"", PTR2ADDR(e->value));
|
||||
if (value_print_func != NULL) {
|
||||
value_print_func(e->value);
|
||||
}
|
||||
printf("\" xlabel=\"%04lx\"]\n", PTR2ADDR(e->value));
|
||||
}
|
||||
}
|
||||
|
||||
// Internal function to print edges from the table entry node in dot format.
|
||||
// Memory "owned" by the table is indicated by solid red lines. Memory
|
||||
// "borrowed" from the user is indicated by red dashed lines.
|
||||
static void print_key_value_edges(int indent_level, const table *t, const table_entry *e)
|
||||
{
|
||||
// Print the key edge
|
||||
if (e->key == NULL) {
|
||||
print_edge(indent_level, e, e->key, "k", "key", NULL);
|
||||
} else {
|
||||
if (t->key_kill_func) {
|
||||
print_edge(indent_level, e, e->key, "k", "key", "color=red");
|
||||
} else {
|
||||
print_edge(indent_level, e, e->key, "k", "key", "color=red style=dashed");
|
||||
}
|
||||
}
|
||||
|
||||
// Print the value edge
|
||||
if (e->value == NULL) {
|
||||
print_edge(indent_level, e, e->value, "v", "value", NULL);
|
||||
} else {
|
||||
if (t->value_kill_func) {
|
||||
print_edge(indent_level, e, e->value, "v", "value", "color=red");
|
||||
} else {
|
||||
print_edge(indent_level, e, e->value, "v", "value", "color=red style=dashed");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Internal function to print nodes and edges of all table entries in dot format.
|
||||
static void print_entries(int indent_level, const table *t, inspect_callback key_print_func,
|
||||
inspect_callback value_print_func)
|
||||
{
|
||||
if (t->entries == NULL) {
|
||||
return;
|
||||
}
|
||||
dlist *l = t->entries;
|
||||
dlist_pos p = dlist_first(l);
|
||||
while (!dlist_is_end(l, p)) {
|
||||
table_entry *e = dlist_inspect(l, p);
|
||||
print_element_node(indent_level, e);
|
||||
print_key_value_nodes(indent_level, e, key_print_func, value_print_func);
|
||||
print_key_value_edges(indent_level, t, e);
|
||||
p = dlist_next(l, p);
|
||||
}
|
||||
}
|
||||
|
||||
// Create an escaped version of the input string. The most common
|
||||
// control characters - newline, horizontal tab, backslash, and double
|
||||
// quote - are replaced by their escape sequence. The returned pointer
|
||||
// must be deallocated by the caller.
|
||||
static char *escape_chars(const char *s)
|
||||
{
|
||||
int i, j;
|
||||
int escaped = 0; // The number of chars that must be escaped.
|
||||
|
||||
// Count how many chars need to be escaped, i.e. how much longer
|
||||
// the output string will be.
|
||||
for (i = escaped = 0; s[i] != '\0'; i++) {
|
||||
if (s[i] == '\n' || s[i] == '\t' || s[i] == '\\' || s[i] == '\"') {
|
||||
escaped++;
|
||||
}
|
||||
}
|
||||
// Allocate space for the escaped string. The variable i holds the input
|
||||
// length, escaped how much the string will grow.
|
||||
char *t = malloc(i + escaped + 1);
|
||||
|
||||
// Copy-and-escape loop
|
||||
for (i = j = 0; s[i] != '\0'; i++) {
|
||||
// Convert each control character by its escape sequence.
|
||||
// Non-control characters are copied as-is.
|
||||
switch (s[i]) {
|
||||
case '\n': t[i+j] = '\\'; t[i+j+1] = 'n'; j++; break;
|
||||
case '\t': t[i+j] = '\\'; t[i+j+1] = 't'; j++; break;
|
||||
case '\\': t[i+j] = '\\'; t[i+j+1] = '\\'; j++; break;
|
||||
case '\"': t[i+j] = '\\'; t[i+j+1] = '\"'; j++; break;
|
||||
default: t[i+j] = s[i]; break;
|
||||
}
|
||||
}
|
||||
// Terminal the output string
|
||||
t[i+j] = '\0';
|
||||
return t;
|
||||
}
|
||||
|
||||
/**
|
||||
* first_white_spc() - Return pointer to first white-space char.
|
||||
* @s: String.
|
||||
*
|
||||
* Returns: A pointer to the first white-space char in s, or NULL if none is found.
|
||||
*
|
||||
*/
|
||||
static const char *find_white_spc(const char *s)
|
||||
{
|
||||
const char *t = s;
|
||||
while (*t != '\0') {
|
||||
if (isspace(*t)) {
|
||||
// We found a white-space char, return a point to it.
|
||||
return t;
|
||||
}
|
||||
// Advance to next char
|
||||
t++;
|
||||
}
|
||||
// No white-space found
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* insert_table_name() - Maybe insert the name of the table src file in the description string.
|
||||
* @s: Description string.
|
||||
*
|
||||
* Parses the description string to find of if it starts with a c file
|
||||
* name. In that case, the file name of this file is spliced into the
|
||||
* description string. The parsing is not very intelligent: If the
|
||||
* sequence ".c:" (case insensitive) is found before the first
|
||||
* white-space, the string up to and including ".c" is taken to be a c
|
||||
* file name.
|
||||
*
|
||||
* Returns: A dynamic copy of s, optionally including with the table src file name.
|
||||
*/
|
||||
static char *insert_table_name(const char *s)
|
||||
{
|
||||
// First, determine if the description string starts with a c file name
|
||||
// a) Search for the string ".c:"
|
||||
const char *dot_c = strstr(s, ".c:");
|
||||
// b) Search for the first white-space
|
||||
const char *spc = find_white_spc(s);
|
||||
|
||||
bool prefix_found;
|
||||
int output_length;
|
||||
|
||||
// If both a) and b) are found AND a) is before b, we assume that
|
||||
// s starts with a file name
|
||||
if (dot_c != NULL && spc != NULL && dot_c < spc) {
|
||||
// We found a match. Output string is input + 3 chars + __FILE__
|
||||
prefix_found = true;
|
||||
output_length = strlen(s) + 3 + strlen(__FILE__);
|
||||
} else {
|
||||
// No match found. Output string is just input
|
||||
prefix_found = false;
|
||||
output_length = strlen(s);
|
||||
}
|
||||
|
||||
// Allocate space for the whole string
|
||||
char *out = calloc(1, output_length + 1);
|
||||
strcpy(out, s);
|
||||
if (prefix_found) {
|
||||
// Overwrite the output buffer from the ":"
|
||||
strcpy(out + (dot_c - s + 2), " (");
|
||||
// Now out will be 0-terminated after "(", append the file name and ")"
|
||||
strcat(out, __FILE__);
|
||||
strcat(out, ")");
|
||||
// Finally append the input string from the : onwards
|
||||
strcat(out, dot_c + 2);
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
/**
|
||||
* table_print_internal() - Output the internal structure of the table.
|
||||
* @t: Table to print.
|
||||
* @key_print_func: Function called for each key in the table.
|
||||
* @value_print_func: Function called for each value in the table.
|
||||
* @desc: String with a description/state of the list.
|
||||
* @indent_level: Indentation level, 0 for outermost
|
||||
*
|
||||
* Iterates over the list and prints code that shows its' internal structure.
|
||||
*
|
||||
* Returns: Nothing.
|
||||
*/
|
||||
void table_print_internal(const table *t, inspect_callback key_print_func,
|
||||
inspect_callback value_print_func, const char *desc,
|
||||
int indent_level)
|
||||
{
|
||||
static int graph_number = 0;
|
||||
graph_number++;
|
||||
int il = indent_level;
|
||||
|
||||
if (indent_level == 0) {
|
||||
// If this is the outermost datatype, start a graph and set up defaults
|
||||
printf("digraph TABLE_%d {\n", graph_number);
|
||||
|
||||
// Specify default shape and fontname
|
||||
il++;
|
||||
iprintf(il, "node [shape=rectangle fontname=\"Courier New\"]\n");
|
||||
iprintf(il, "ranksep=0.01\n");
|
||||
iprintf(il, "subgraph cluster_nullspace {\n");
|
||||
iprintf(il+1, "NULL\n");
|
||||
iprintf(il, "}\n");
|
||||
}
|
||||
|
||||
if (desc != NULL) {
|
||||
// Escape the string before printout
|
||||
char *escaped = escape_chars(desc);
|
||||
// Optionally, splice the source file name
|
||||
char *spliced = insert_table_name(escaped);
|
||||
|
||||
// Use different names on inner description nodes
|
||||
if (indent_level == 0) {
|
||||
iprintf(il, "description [label=\"%s\"]\n", spliced);
|
||||
} else {
|
||||
iprintf(il, "\tcluster_list_%d_description [label=\"%s\"]\n", graph_number, spliced);
|
||||
}
|
||||
// Return the memory used by the spliced and escaped strings
|
||||
free(spliced);
|
||||
free(escaped);
|
||||
}
|
||||
|
||||
if (indent_level == 0) {
|
||||
// Use a single "pointer" edge as a starting point for the
|
||||
// outermost datatype
|
||||
iprintf(il, "t [label=\"%04lx\" xlabel=\"t\"]\n", PTR2ADDR(t));
|
||||
iprintf(il, "t -> m%04lx\n", PTR2ADDR(t));
|
||||
}
|
||||
|
||||
if (indent_level == 0) {
|
||||
// Put the user nodes in userspace
|
||||
iprintf(il, "subgraph cluster_userspace { label=\"User space\"\n");
|
||||
il++;
|
||||
|
||||
// Iterate over the list to print the payload nodes
|
||||
dlist_pos p = dlist_first(t->entries);
|
||||
while (!dlist_is_end(t->entries, p)) {
|
||||
print_key_value_nodes(il, dlist_inspect(t->entries, p), key_print_func,
|
||||
value_print_func);
|
||||
// Advance
|
||||
p = dlist_next(t->entries, p);
|
||||
}
|
||||
|
||||
// Close the subgraph
|
||||
il--;
|
||||
iprintf(il, "}\n");
|
||||
}
|
||||
|
||||
// Print the subgraph to surround the DList content
|
||||
iprintf(il, "subgraph cluster_table_%d { label=\"Table\"\n", graph_number);
|
||||
il++;
|
||||
|
||||
// Output the head node
|
||||
print_head_node(il, t);
|
||||
|
||||
// Output the edges from the head
|
||||
print_head_edge(il, t);
|
||||
|
||||
if (t->entries) {
|
||||
// First, ask the dlist to output its internal structure.
|
||||
dlist_print_internal(t->entries, NULL, NULL, il);
|
||||
}
|
||||
|
||||
// Close the subgraph
|
||||
il--;
|
||||
iprintf(il, "}\n");
|
||||
|
||||
// Next, print each element stored in the list
|
||||
print_entries(il, t, key_print_func, value_print_func);
|
||||
|
||||
if (indent_level == 0) {
|
||||
// Termination of graph
|
||||
printf("}\n");
|
||||
}
|
||||
}
|
||||
BIN
OU3/mtftableprint
Executable file
BIN
OU3/mtftableprint
Executable file
Binary file not shown.
BIN
OU3/mtftabletest
Executable file
BIN
OU3/mtftabletest
Executable file
Binary file not shown.
BIN
OU3/mtftabletest (conflicted copy 2025-05-08 202741)
Executable file
BIN
OU3/mtftabletest (conflicted copy 2025-05-08 202741)
Executable file
Binary file not shown.
150
OU3/mtiming.txt
Normal file
150
OU3/mtiming.txt
Normal file
@@ -0,0 +1,150 @@
|
||||
1, 10000, 2
|
||||
2, 10000, 536
|
||||
3, 10000, 1650
|
||||
4, 10000, 912
|
||||
5, 10000, 459
|
||||
1, 10000, 2
|
||||
2, 10000, 533
|
||||
3, 10000, 1666
|
||||
4, 10000, 937
|
||||
5, 10000, 463
|
||||
1, 10000, 1
|
||||
2, 10000, 535
|
||||
3, 10000, 1616
|
||||
4, 10000, 902
|
||||
5, 10000, 459
|
||||
1, 10000, 1
|
||||
2, 10000, 537
|
||||
3, 10000, 1632
|
||||
4, 10000, 898
|
||||
5, 10000, 465
|
||||
1, 10000, 1
|
||||
2, 10000, 537
|
||||
3, 10000, 1629
|
||||
4, 10000, 905
|
||||
5, 10000, 466
|
||||
1, 10000, 1
|
||||
2, 10000, 532
|
||||
3, 10000, 1721
|
||||
4, 10000, 939
|
||||
5, 10000, 489
|
||||
1, 13000, 2
|
||||
2, 13000, 936
|
||||
3, 13000, 3046
|
||||
4, 13000, 1701
|
||||
5, 13000, 880
|
||||
1, 13000, 2
|
||||
2, 13000, 909
|
||||
3, 13000, 2955
|
||||
4, 13000, 1667
|
||||
5, 13000, 871
|
||||
1, 13000, 1
|
||||
2, 13000, 913
|
||||
3, 13000, 2906
|
||||
4, 13000, 1658
|
||||
5, 13000, 870
|
||||
1, 13000, 1
|
||||
2, 13000, 912
|
||||
3, 13000, 3015
|
||||
4, 13000, 1677
|
||||
5, 13000, 875
|
||||
1, 13000, 3
|
||||
2, 13000, 957
|
||||
3, 13000, 2951
|
||||
4, 13000, 1655
|
||||
5, 13000, 878
|
||||
1, 13000, 2
|
||||
2, 13000, 914
|
||||
3, 13000, 2916
|
||||
4, 13000, 1668
|
||||
5, 13000, 859
|
||||
1, 16000, 2
|
||||
2, 16000, 1416
|
||||
3, 16000, 4547
|
||||
4, 16000, 2606
|
||||
5, 16000, 1422
|
||||
1, 16000, 2
|
||||
2, 16000, 1427
|
||||
3, 16000, 4538
|
||||
4, 16000, 2618
|
||||
5, 16000, 1419
|
||||
1, 16000, 2
|
||||
2, 16000, 1428
|
||||
3, 16000, 4592
|
||||
4, 16000, 2614
|
||||
5, 16000, 1413
|
||||
1, 16000, 2
|
||||
2, 16000, 1419
|
||||
3, 16000, 4539
|
||||
4, 16000, 2608
|
||||
5, 16000, 1407
|
||||
1, 16000, 2
|
||||
2, 16000, 1421
|
||||
3, 16000, 4399
|
||||
4, 16000, 2567
|
||||
5, 16000, 1405
|
||||
1, 16000, 2
|
||||
2, 16000, 1417
|
||||
3, 16000, 4429
|
||||
4, 16000, 2571
|
||||
5, 16000, 1381
|
||||
1, 19000, 3
|
||||
2, 19000, 2034
|
||||
3, 19000, 6775
|
||||
4, 19000, 3911
|
||||
5, 19000, 2092
|
||||
1, 19000, 2
|
||||
2, 19000, 2064
|
||||
3, 19000, 6696
|
||||
4, 19000, 3909
|
||||
5, 19000, 2107
|
||||
1, 19000, 2
|
||||
2, 19000, 2065
|
||||
3, 19000, 6717
|
||||
4, 19000, 3871
|
||||
5, 19000, 2051
|
||||
1, 19000, 3
|
||||
2, 19000, 2030
|
||||
3, 19000, 6605
|
||||
4, 19000, 3861
|
||||
5, 19000, 2032
|
||||
1, 19000, 2
|
||||
2, 19000, 2035
|
||||
3, 19000, 6590
|
||||
4, 19000, 3708
|
||||
5, 19000, 2013
|
||||
1, 19000, 3
|
||||
2, 19000, 1985
|
||||
3, 19000, 6650
|
||||
4, 19000, 3800
|
||||
5, 19000, 2038
|
||||
1, 22000, 2
|
||||
2, 22000, 2684
|
||||
3, 22000, 8811
|
||||
4, 22000, 5025
|
||||
5, 22000, 2769
|
||||
1, 22000, 2
|
||||
2, 22000, 2732
|
||||
3, 22000, 8817
|
||||
4, 22000, 5154
|
||||
5, 22000, 2813
|
||||
1, 22000, 3
|
||||
2, 22000, 2839
|
||||
3, 22000, 8776
|
||||
4, 22000, 5045
|
||||
5, 22000, 2780
|
||||
1, 22000, 3
|
||||
2, 22000, 2740
|
||||
3, 22000, 9041
|
||||
4, 22000, 5178
|
||||
5, 22000, 2897
|
||||
1, 22000, 2
|
||||
2, 22000, 2729
|
||||
3, 22000, 9045
|
||||
4, 22000, 5186
|
||||
5, 22000, 2873
|
||||
1, 22000, 3
|
||||
2, 22000, 2750
|
||||
3, 22000, 8976
|
||||
4, 22000, 5103
|
||||
5, 22000, 2835
|
||||
95
OU3/parse_timings.m
Normal file
95
OU3/parse_timings.m
Normal file
@@ -0,0 +1,95 @@
|
||||
load ttiming.txt
|
||||
load mtiming.txt
|
||||
load atiming.txt
|
||||
% 3rd column contains the timing of 5 functions at 5 sizes repeated 6 times
|
||||
T=reshape(ttiming(:,3),[5,5,6]);
|
||||
M=reshape(mtiming(:,3),[5,5,6]);
|
||||
A=reshape(atiming(:,3),[5,5,6]);
|
||||
% 2nd column contains the problem sizes
|
||||
n = unique(ttiming(:,2))
|
||||
Ta=mean(T,3);
|
||||
Ma=mean(M,3);
|
||||
Aa=mean(A,3);
|
||||
|
||||
n1=@(n)n(:)';
|
||||
n2=@(n)n(:)'.^2;
|
||||
n3=@(n)n(:)'.^3;
|
||||
|
||||
figure(1)
|
||||
set(1,'name','Function #1 = insert')
|
||||
ix = 1;
|
||||
T1=mean(reshape(ttiming(ttiming(:,1)==ix,3),6,[]),1);
|
||||
M1=mean(reshape(mtiming(mtiming(:,1)==ix,3),6,[]),1);
|
||||
A1=mean(reshape(atiming(atiming(:,1)==ix,3),6,[]),1);
|
||||
g=n1;
|
||||
|
||||
subplot(2,1,1)
|
||||
plot(n,T1./g(n),'x-',n,M1./g(n),'o-',n,A1./g(n),'*-')
|
||||
legend('T1/n','M1/n','A1/n')
|
||||
set(gca,'ylim',[0,inf],'xtick',n)
|
||||
title(gca,'Insert')
|
||||
xlabel("n")
|
||||
ylabel("Time/n")
|
||||
subplot(2,1,2)
|
||||
plot(n,T1./g(n),'x-',n,M1./g(n),'o-')
|
||||
legend('T1/n','M1/n')
|
||||
set(gca,'ylim',[0,inf],'xtick',n)
|
||||
xlabel("n")
|
||||
ylabel("Time/n")
|
||||
title(gca,'Insert')
|
||||
%xlabel("n")
|
||||
%ylabel("")
|
||||
|
||||
figure(2)
|
||||
set(2,'name','Function #2 = remove')
|
||||
ix = 2;
|
||||
T1=mean(reshape(ttiming(ttiming(:,1)==ix,3),6,[]),1);
|
||||
M1=mean(reshape(mtiming(mtiming(:,1)==ix,3),6,[]),1);
|
||||
A1=mean(reshape(atiming(atiming(:,1)==ix,3),6,[]),1);
|
||||
g=n2;
|
||||
plot(n,T1./g(n),'x-',n,M1./g(n),'o-',n,A1./g(n),'*-')
|
||||
legend('T1/n^2','M1/n^2','A1/n^2')
|
||||
set(gca,'ylim',[0,inf],'xtick',n)
|
||||
title(gca,'Remove')
|
||||
xlabel("n")
|
||||
ylabel("Time/n")
|
||||
|
||||
figure(3)
|
||||
set(3,'name','Function #3..#5 = various lookup')
|
||||
titles={'Failed lookup', 'Random lookup', 'Skewed lookup'};
|
||||
|
||||
for i = 1:3
|
||||
ix = 2+i;
|
||||
T1=mean(reshape(ttiming(ttiming(:,1)==ix,3),6,[]),1);
|
||||
M1=mean(reshape(mtiming(mtiming(:,1)==ix,3),6,[]),1);
|
||||
A1=mean(reshape(atiming(atiming(:,1)==ix,3),6,[]),1);
|
||||
g=n2;
|
||||
subplot(1,3,i)
|
||||
plot(n,T1./g(n),'x-',n,M1./g(n),'o-',n,A1./g(n),'*-')
|
||||
set(gca,'ylim',[0,2e-5],'xtick',n)
|
||||
title(gca,titles{i})
|
||||
xlabel("n")
|
||||
ylabel("Time/n")
|
||||
end
|
||||
|
||||
|
||||
print -depsc -f1 fig1.eps
|
||||
print -depsc -f2 fig2.eps
|
||||
print -depsc -f3 fig3.eps
|
||||
|
||||
disp('You now have three files fig1.eps, fig2.eps, fig3.eps in the /tmp folder')
|
||||
disp('Under linux, do the following commands to convert them to pdf:')
|
||||
disp(' ')
|
||||
disp('cd /tmp')
|
||||
disp('epspdf fig1.eps')
|
||||
disp('epspdf fig2.eps')
|
||||
disp('epspdf fig3.eps')
|
||||
disp(' ')
|
||||
disp('The copy the pdf files to your latex folder or insert into overleaf.')
|
||||
|
||||
% Then run the following commands in linux:
|
||||
% cd /tmp
|
||||
% epspdf fig1.eps
|
||||
% epspdf fig2.eps
|
||||
% epspdf fig3.eps
|
||||
% and copy the fig1.pdf, etc., files to your latex folder.
|
||||
BIN
OU3/rapport_ou3 (conflicted copy 2025-05-09 092332).pdf
Normal file
BIN
OU3/rapport_ou3 (conflicted copy 2025-05-09 092332).pdf
Normal file
Binary file not shown.
BIN
OU3/rapport_ou3.pdf
Normal file
BIN
OU3/rapport_ou3.pdf
Normal file
Binary file not shown.
13
OU3/src/Makefile
Normal file
13
OU3/src/Makefile
Normal file
@@ -0,0 +1,13 @@
|
||||
# List of directories containing sub-projects
|
||||
SUB_DIRS := array_1d array_2d dlist int_array_1d int_list int_list_array int_stack list int_queue queue stack table
|
||||
|
||||
# Rule to call 'make all' in each sub-directory
|
||||
all:
|
||||
for dir in $(SUB_DIRS); do \
|
||||
$(MAKE) -C $$dir all; \
|
||||
done
|
||||
|
||||
clean:
|
||||
for dir in $(SUB_DIRS); do \
|
||||
$(MAKE) -C $$dir clean; \
|
||||
done
|
||||
49
OU3/src/array_1d/Makefile
Normal file
49
OU3/src/array_1d/Makefile
Normal file
@@ -0,0 +1,49 @@
|
||||
MWE = array_1d_mwe1 array_1d_mwe2 array_1d_mwe3 array_1d_mwe4 array_1d_mwe1i array_1d_mwe2i
|
||||
|
||||
SRC = array_1d.c
|
||||
OBJ = $(SRC:.c=.o)
|
||||
|
||||
CC = gcc
|
||||
CFLAGS = -std=c99 -Wall -I../../include -g
|
||||
|
||||
all: mwe
|
||||
|
||||
# Minimum working examples.
|
||||
mwe: $(MWE)
|
||||
|
||||
# Object file for library
|
||||
obj: $(OBJ)
|
||||
|
||||
# Clean up
|
||||
clean:
|
||||
-rm -f $(MWE) $(OBJ)
|
||||
|
||||
array_1d_mwe1: array_1d_mwe1.c array_1d.c
|
||||
gcc -o $@ $(CFLAGS) $^
|
||||
|
||||
array_1d_mwe2: array_1d_mwe2.c array_1d.c
|
||||
gcc -o $@ $(CFLAGS) $^
|
||||
|
||||
array_1d_mwe3: array_1d_mwe3.c array_1d.c
|
||||
gcc -o $@ $(CFLAGS) $^
|
||||
|
||||
array_1d_mwe4: array_1d_mwe4.c array_1d.c
|
||||
gcc -o $@ $(CFLAGS) $^
|
||||
|
||||
array_1d_mwe1i: array_1d_mwe1i.c array_1d.c
|
||||
gcc -o $@ $(CFLAGS) $^
|
||||
|
||||
array_1d_mwe2i: array_1d_mwe2i.c array_1d.c
|
||||
gcc -o $@ $(CFLAGS) $^
|
||||
|
||||
memtest1: array_1d_mwe1
|
||||
valgrind --leak-check=full --show-reachable=yes ./$<
|
||||
|
||||
memtest2: array_1d_mwe2
|
||||
valgrind --leak-check=full --show-reachable=yes ./$<
|
||||
|
||||
memtest3: array_1d_mwe3
|
||||
valgrind --leak-check=full --show-reachable=yes ./$<
|
||||
|
||||
memtest4: array_1d_mwe4
|
||||
valgrind --leak-check=full --show-reachable=yes ./$<
|
||||
491
OU3/src/array_1d/array_1d.c
Normal file
491
OU3/src/array_1d/array_1d.c
Normal file
@@ -0,0 +1,491 @@
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdarg.h>
|
||||
|
||||
#include <array_1d.h>
|
||||
|
||||
/*
|
||||
* Implementation of a generic 1D array for the "Datastructures and
|
||||
* algorithms" courses at the Department of Computing Science, Umea
|
||||
* University.
|
||||
*
|
||||
* Authors: Niclas Borlin (niclas@cs.umu.se)
|
||||
* Adam Dahlgren Lindstrom (dali@cs.umu.se)
|
||||
* Lars Karlsson (larsk@cs.umu.se)
|
||||
*
|
||||
* Based on earlier code by: Johan Eliasson (johane@cs.umu.se).
|
||||
*
|
||||
* Version information:
|
||||
* v1.0 2018-01-28: First public version.
|
||||
* v1.5 2024-03-13: Renamed free_* stuff to kill_*. Converted to 4-tabs.
|
||||
* v2.0 2024-05-10: Added print_internal.
|
||||
*/
|
||||
|
||||
// =========== INTERNAL DATA TYPES ============
|
||||
|
||||
struct array_1d {
|
||||
int low; // Low index limit.
|
||||
int high; // High index limit.
|
||||
int array_size; // Number of array elements.
|
||||
void **values; // Pointer to where the actual values are stored.
|
||||
kill_function kill_func;
|
||||
};
|
||||
|
||||
// =========== INTERNAL FUNCTION IMPLEMENTATIONS ============
|
||||
|
||||
/**
|
||||
* array_1d_create() - Create an array without values.
|
||||
* @lo: low index limit.
|
||||
* @hi: high index limit.
|
||||
* @kill_func: A pointer to a function (or NULL) to be called to
|
||||
* de-allocate memory on remove/kill.
|
||||
*
|
||||
* The index limits are inclusive, i.e. all indices i such that low <=
|
||||
* i <= high are defined.
|
||||
*
|
||||
* Returns: A pointer to the new array, or NULL if not enough memory
|
||||
* was available.
|
||||
*/
|
||||
array_1d *array_1d_create(int lo, int hi, kill_function kill_func)
|
||||
{
|
||||
// Allocate array structure.
|
||||
array_1d *a = calloc(1, sizeof(*a));
|
||||
// Store index limit.
|
||||
a->low = lo;
|
||||
a->high = hi;
|
||||
|
||||
// Number of elements.
|
||||
a->array_size = hi - lo + 1;
|
||||
|
||||
// Store kill function.
|
||||
a->kill_func = kill_func;
|
||||
|
||||
a->values = calloc(a->array_size, sizeof(void *));
|
||||
|
||||
// Check whether the allocation succeeded.
|
||||
if (a->values == NULL) {
|
||||
free(a);
|
||||
a = NULL;
|
||||
}
|
||||
return a;
|
||||
}
|
||||
|
||||
/**
|
||||
* array_1d_low() - Return the low index limit for the array.
|
||||
* @a: array to inspect.
|
||||
*
|
||||
* Returns: The low index limit.
|
||||
*/
|
||||
int array_1d_low(const array_1d *a)
|
||||
{
|
||||
return a->low;
|
||||
}
|
||||
|
||||
/**
|
||||
* array_1d_high() - Return the high index limit for the array.
|
||||
* @a: array to inspect.
|
||||
*
|
||||
* Returns: The high index limit.
|
||||
*/
|
||||
int array_1d_high(const array_1d *a)
|
||||
{
|
||||
return a->high;
|
||||
}
|
||||
|
||||
/**
|
||||
* array_1d_inspect_value() - Inspect a value at a given array position.
|
||||
* @a: array to inspect.
|
||||
* @i: index of position to inspect.
|
||||
*
|
||||
* Returns: The element value at the specified position. The result is
|
||||
* undefined if no value are stored at that position.
|
||||
*/
|
||||
void *array_1d_inspect_value(const array_1d *a, int i)
|
||||
{
|
||||
int offset = i - array_1d_low(a);
|
||||
// Return the value.
|
||||
return a->values[offset];
|
||||
}
|
||||
|
||||
/**
|
||||
* array_1d_has_value() - Check if a value is set at a given array position.
|
||||
* @a: array to inspect.
|
||||
* @i: index of position to inspect.
|
||||
*
|
||||
* Returns: True if a value is set at the specified position, otherwise false.
|
||||
*/
|
||||
bool array_1d_has_value(const array_1d *a, int i)
|
||||
{
|
||||
int offset = i - array_1d_low(a);
|
||||
// Return true if the value is not NULL.
|
||||
return a->values[offset] != NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* array_1d_set_value() - Set a value at a given array position.
|
||||
* @a: array to modify.
|
||||
* @v: value to set element to, or NULL to clear value.
|
||||
* @i: index of position to modify.
|
||||
*
|
||||
* If the old element value is non-NULL, calls kill_func if it was
|
||||
* specified at array creation.
|
||||
*
|
||||
* Returns: Nothing.
|
||||
*/
|
||||
void array_1d_set_value(array_1d *a, void *v, int i)
|
||||
{
|
||||
int offset = i - array_1d_low(a);
|
||||
// Call kill_func if specified and old element value was non-NULL.
|
||||
if (a->kill_func != NULL && a->values[offset] != NULL) {
|
||||
a->kill_func( a->values[offset] );
|
||||
}
|
||||
// Set value.
|
||||
a->values[offset] = v;
|
||||
}
|
||||
|
||||
/**
|
||||
* array_1d_kill() - Return memory allocated by array.
|
||||
* @a: array to kill.
|
||||
*
|
||||
* Iterates over all elements. If kill_func was specified at array
|
||||
* creation, calls it for every non-NULL element value.
|
||||
*
|
||||
* Returns: Nothing.
|
||||
*/
|
||||
void array_1d_kill(array_1d *a)
|
||||
{
|
||||
if (a->kill_func != NULL) {
|
||||
// Return user-allocated memory for each non-NULL element.
|
||||
for (int i = 0; i < a->array_size; i++) {
|
||||
if (a->values[i] != NULL) {
|
||||
a->kill_func(a->values[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
// Return memory for value pointers.
|
||||
free(a->values);
|
||||
// Return memory for array structure.
|
||||
free(a);
|
||||
}
|
||||
|
||||
/**
|
||||
* array_1d_print() - Iterate over the array element and print their values.
|
||||
* @a: Array to inspect.
|
||||
* @print_func: Function called for each non-NULL element.
|
||||
*
|
||||
* Iterates over each position in the array. Calls print_func for each
|
||||
* non-NULL value.
|
||||
*
|
||||
* Returns: Nothing.
|
||||
*/
|
||||
void array_1d_print(const array_1d *a, inspect_callback print_func)
|
||||
{
|
||||
printf("[ ");
|
||||
for (int i=array_1d_low(a); i<=array_1d_high(a); i++) {
|
||||
if (array_1d_has_value(a, i)) {
|
||||
printf("[");
|
||||
print_func(array_1d_inspect_value(a, i));
|
||||
printf("]");
|
||||
} else {
|
||||
printf(" []");
|
||||
}
|
||||
if (i < array_1d_high(a)) {
|
||||
printf(", ");
|
||||
}
|
||||
}
|
||||
printf(" ]\n");
|
||||
}
|
||||
|
||||
// ===========INTERNAL FUNCTIONS USED BY array_1d_print_internal ============
|
||||
|
||||
// The functions below output code in the dot language, used by
|
||||
// GraphViz. For documention of the dot language, see graphviz.org.
|
||||
|
||||
/**
|
||||
* indent() - Output indentation string.
|
||||
* @n: Indentation level.
|
||||
*
|
||||
* Print n tab characters.
|
||||
*
|
||||
* Returns: Nothing.
|
||||
*/
|
||||
static void indent(int n)
|
||||
{
|
||||
for (int i=0; i<n; i++) {
|
||||
printf("\t");
|
||||
}
|
||||
}
|
||||
/**
|
||||
* iprintf(...) - Indent and print.
|
||||
* @n: Indentation level
|
||||
* @...: printf arguments
|
||||
*
|
||||
* Print n tab characters and calls printf.
|
||||
*
|
||||
* Returns: Nothing.
|
||||
*/
|
||||
static void iprintf(int n, const char *fmt, ...)
|
||||
{
|
||||
// Indent...
|
||||
indent(n);
|
||||
// ...and call printf
|
||||
va_list args;
|
||||
va_start(args, fmt);
|
||||
vprintf(fmt, args);
|
||||
va_end(args);
|
||||
}
|
||||
|
||||
/**
|
||||
* print_edge() - Print a edge between two addresses.
|
||||
* @from: The address of the start of the edge. Should be non-NULL.
|
||||
* @to: The address of the destination for the edge, including NULL.
|
||||
* @port: The name of the port on the source node, or NULL.
|
||||
* @label: The label for the edge, or NULL.
|
||||
* @options: A string with other edge options, or NULL.
|
||||
*
|
||||
* Print an edge from port PORT on node FROM to TO with label
|
||||
* LABEL. If to is NULL, the destination is the NULL node, otherwise a
|
||||
* memory node. If the port is NULL, the edge starts at the node, not
|
||||
* a specific port on it. If label is NULL, no label is used. The
|
||||
* options string, if non-NULL, is printed before the label.
|
||||
*
|
||||
* Returns: Nothing.
|
||||
*/
|
||||
static void print_edge(int indent_level, const void *from, const void *to, const char *port,
|
||||
const char *label, const char *options)
|
||||
{
|
||||
indent(indent_level);
|
||||
if (port) {
|
||||
printf("m%04lx:%s -> ", PTR2ADDR(from), port);
|
||||
} else {
|
||||
printf("m%04lx -> ", PTR2ADDR(from));
|
||||
}
|
||||
if (to == NULL) {
|
||||
printf("NULL");
|
||||
} else {
|
||||
printf("m%04lx", PTR2ADDR(to));
|
||||
}
|
||||
printf(" [");
|
||||
if (options != NULL) {
|
||||
printf("%s", options);
|
||||
}
|
||||
if (label != NULL) {
|
||||
printf(" label=\"%s\"",label);
|
||||
}
|
||||
printf("]\n");
|
||||
}
|
||||
|
||||
/**
|
||||
* print_head_node() - Print a node corresponding to the array head struct.
|
||||
* @indent_level: Indentation level.
|
||||
* @a: Array to inspect.
|
||||
*
|
||||
* Returns: Nothing.
|
||||
*/
|
||||
static void print_head_node(int indent_level, const array_1d *a)
|
||||
{
|
||||
iprintf(indent_level, "m%04lx [shape=record "
|
||||
"label=\"kill\\n%04lx|low\\n%d|high\\n%d|array_size\\n%d|<v>values\\n%04lx\"]\n",
|
||||
PTR2ADDR(a), PTR2ADDR(a->kill_func), a->low, a->high, a->array_size,
|
||||
PTR2ADDR(a->values));
|
||||
}
|
||||
|
||||
void print_values(int indent_level, const array_1d *a)
|
||||
{
|
||||
iprintf(indent_level, "m%04lx [shape=record label=\"", PTR2ADDR(a->values));
|
||||
for (int i=0; i < a->array_size; i++) {
|
||||
printf("<%02d>%d\\n%02d\\n%04lx", i, i + a->low, i, PTR2ADDR(a->values[i]));
|
||||
if (i < a->array_size - 1) {
|
||||
printf("|");
|
||||
}
|
||||
}
|
||||
printf("\"]\n");
|
||||
}
|
||||
|
||||
// Print edge from the array head to the values array.
|
||||
static void print_head_edge(int indent_level, const array_1d *a)
|
||||
{
|
||||
iprintf(indent_level, "m%04lx:v -> ", PTR2ADDR(a));
|
||||
if (a->values == NULL) {
|
||||
printf("NULL");
|
||||
} else {
|
||||
printf("m%04lx", PTR2ADDR(a->values));
|
||||
}
|
||||
printf(" [label=\"values\"]\n");
|
||||
}
|
||||
|
||||
// Print nodes for each value memory block
|
||||
static void print_value_nodes(int indent_level, const array_1d *a, inspect_callback print_func)
|
||||
{
|
||||
for (int i=0; i <= a->array_size; i++) {
|
||||
if (a->values[i] != NULL) {
|
||||
iprintf(indent_level, "m%04lx [label=\"", PTR2ADDR(a->values[i]));
|
||||
if (print_func != NULL) {
|
||||
print_func(a->values[i]);
|
||||
}
|
||||
printf("\" xlabel=\"%04lx\"]\n", PTR2ADDR(a->values[i]));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Print edges from each value pointer to payload memory
|
||||
static void print_value_edges(int indent_level, const array_1d *a)
|
||||
{
|
||||
for (int i=0; i < a->array_size; i++) {
|
||||
// Buffer to store port name in. Good for array up to 1e9 elements.
|
||||
char port[15];
|
||||
// Create port name
|
||||
sprintf(port, "%02d", i);
|
||||
if (a->kill_func) {
|
||||
print_edge(indent_level, a->values, a->values[i], port, port,
|
||||
"color=red");
|
||||
} else {
|
||||
print_edge(indent_level, a->values, a->values[i], port, port,
|
||||
"color=red style=dashed");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Create an escaped version of the input string. The most common
|
||||
// control characters - newline, horizontal tab, backslash, and double
|
||||
// quote - are replaced by their escape sequence. The returned pointer
|
||||
// must be deallocated by the caller.
|
||||
static char *escape_chars(const char *s)
|
||||
{
|
||||
int i, j;
|
||||
int escaped = 0; // The number of chars that must be escaped.
|
||||
|
||||
// Count how many chars need to be escaped, i.e. how much longer
|
||||
// the output string will be.
|
||||
for (i = escaped = 0; s[i] != '\0'; i++) {
|
||||
if (s[i] == '\n' || s[i] == '\t' || s[i] == '\\' || s[i] == '\"') {
|
||||
escaped++;
|
||||
}
|
||||
}
|
||||
// Allocate space for the escaped string. The variable i holds the input
|
||||
// length, escaped how much the string will grow.
|
||||
char *t = malloc(i + escaped + 1);
|
||||
|
||||
// Copy-and-escape loop
|
||||
for (i = j = 0; s[i] != '\0'; i++) {
|
||||
// Convert each control character by its escape sequence.
|
||||
// Non-control characters are copied as-is.
|
||||
switch (s[i]) {
|
||||
case '\n': t[i+j] = '\\'; t[i+j+1] = 'n'; j++; break;
|
||||
case '\t': t[i+j] = '\\'; t[i+j+1] = 't'; j++; break;
|
||||
case '\\': t[i+j] = '\\'; t[i+j+1] = '\\'; j++; break;
|
||||
case '\"': t[i+j] = '\\'; t[i+j+1] = '\"'; j++; break;
|
||||
default: t[i+j] = s[i]; break;
|
||||
}
|
||||
}
|
||||
// Terminal the output string
|
||||
t[i+j] = '\0';
|
||||
return t;
|
||||
}
|
||||
|
||||
/**
|
||||
* array_1d_print_internal() - Print the internal structure of the array in dot format.
|
||||
* @a: Array to inspect.
|
||||
* @print_func: Function called for each element value.
|
||||
* @desc: String with a description/state of the array, or NULL for no description.
|
||||
* @indent_level: Indentation level, 0 for outermost
|
||||
*
|
||||
* Iterates over the array and outputs dot code that shows the internal
|
||||
* structure of the array. The dot code can be visualized by
|
||||
* Graphviz.
|
||||
*
|
||||
* On linux system, the output can be parsed by the dot program, e.g.
|
||||
*
|
||||
* <array_program> | dot -Tsvg > /tmp/dot.svg; firefox /tmp/dot.svg
|
||||
*
|
||||
* where <array_program> is the name of the executable
|
||||
*
|
||||
* The output may also be possible to visualize online on
|
||||
* https://dreampuf.github.io/GraphvizOnline/ or google "graphviz
|
||||
* online".
|
||||
*
|
||||
* For documention of the dot language, see graphviz.org.
|
||||
*
|
||||
* Returns: Nothing.
|
||||
*/
|
||||
void array_1d_print_internal(const array_1d *a, inspect_callback print_func, const char *desc,
|
||||
int indent_level)
|
||||
{
|
||||
static int graph_number = 0;
|
||||
graph_number++;
|
||||
int il = indent_level;
|
||||
|
||||
if (indent_level == 0) {
|
||||
// If this is the outermost datatype, start a graph...
|
||||
printf("digraph ARRAY_1D_%d {\n", graph_number);
|
||||
|
||||
// Specify default shape and fontname
|
||||
il++;
|
||||
iprintf(il, "node [shape=rectangle fontname=\"Courier New\"]\n");
|
||||
iprintf(il, "ranksep=0.01\n");
|
||||
iprintf(il, "subgraph cluster_nullspace {\n");
|
||||
iprintf(il+1, "NULL\n");
|
||||
iprintf(il, "}\n");
|
||||
}
|
||||
|
||||
if (desc != NULL) {
|
||||
// Escape the string before printout
|
||||
char *escaped = escape_chars(desc);
|
||||
// Use different names on inner description nodes
|
||||
if (indent_level == 0) {
|
||||
iprintf(il, "description [label=\"%s\"]\n", escaped);
|
||||
} else {
|
||||
iprintf(il, "cluster_array_1d_%d_description [label=\"%s\"]\n", graph_number,
|
||||
escaped);
|
||||
}
|
||||
// Return the memory used by the escaped string
|
||||
free(escaped);
|
||||
}
|
||||
|
||||
if (indent_level == 0) {
|
||||
// Use a single "pointer" edge as a starting point for the
|
||||
// outermost datatype
|
||||
iprintf(il, "a [label=\"%04lx\" xlabel=\"a\"]\n", PTR2ADDR(a));
|
||||
iprintf(il, "a -> m%04lx\n", PTR2ADDR(a));
|
||||
}
|
||||
|
||||
// Print the subgraph to surround the Array content
|
||||
iprintf(il, "subgraph cluster_array_1d_%d { label=\"Array_1d\"\n", graph_number);
|
||||
il++;
|
||||
|
||||
// Output the head node
|
||||
print_head_node(il, a);
|
||||
|
||||
// Output the values array
|
||||
print_values(il, a);
|
||||
|
||||
// Close the subgraph
|
||||
il--;
|
||||
iprintf(il, "}\n");
|
||||
|
||||
if (indent_level == 0) {
|
||||
// Put the user nodes in userspace
|
||||
iprintf(il, "subgraph cluster_userspace { label=\"User space\"\n");
|
||||
il++;
|
||||
}
|
||||
|
||||
// Print nodes for each value memory block
|
||||
print_value_nodes(il, a, print_func);
|
||||
|
||||
if (indent_level == 0) {
|
||||
// Close userspace
|
||||
il--;
|
||||
iprintf(il, "}\n");
|
||||
}
|
||||
|
||||
// Output the edges from the head
|
||||
print_head_edge(il, a);
|
||||
|
||||
// Print edges from each value pointer to payload memory
|
||||
print_value_edges(il, a);
|
||||
|
||||
if (indent_level == 0) {
|
||||
// Termination of graph
|
||||
printf("}\n");
|
||||
}
|
||||
}
|
||||
77
OU3/src/array_1d/array_1d_mwe1.c
Normal file
77
OU3/src/array_1d/array_1d_mwe1.c
Normal file
@@ -0,0 +1,77 @@
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <array_1d.h>
|
||||
|
||||
/*
|
||||
* Minimum working example 1 for array_1d.c.
|
||||
*
|
||||
* Authors: Niclas Borlin (niclas@cs.umu.se)
|
||||
*
|
||||
* Version information:
|
||||
* v1.0 2018-01-28: First public version.
|
||||
* v1.1 2018-04-03: Split into simpler, shorter versions.
|
||||
* Removed use of automatic, magic, free_handler.
|
||||
* v1.2 2023-01-14: Added printouts at start/end of main.
|
||||
* v1.3 2024-03-13: Added explicit create/kill functions.
|
||||
* v1.31 2024-04-05: Bugfix: Remove call to array_1d_print_internal
|
||||
* that shouldn't be here.
|
||||
*/
|
||||
|
||||
#define VERSION "v1.31"
|
||||
#define VERSION_DATE "2024-04-05"
|
||||
|
||||
// Integers are stored via int pointers stored as void pointers.
|
||||
// Convert the given pointer and print the dereferenced value.
|
||||
void print_int(const void *data)
|
||||
{
|
||||
const int *v = data;
|
||||
printf("%d", *v);
|
||||
}
|
||||
|
||||
// Create a dynamic copy of the integer i.
|
||||
int *int_create(int i)
|
||||
{
|
||||
// Allocate memory for an integer and set the value
|
||||
int *v = malloc(sizeof(*v));
|
||||
*v = i;
|
||||
return v;
|
||||
}
|
||||
|
||||
// Return the memory used by the integer.
|
||||
void int_kill(void *v)
|
||||
{
|
||||
int *p = v;
|
||||
free(p);
|
||||
}
|
||||
|
||||
int main(void)
|
||||
{
|
||||
printf("%s, %s %s: Create integer array without kill_function.\n",
|
||||
__FILE__, VERSION, VERSION_DATE);
|
||||
printf("Code base version %s (%s).\n\n", CODE_BASE_VERSION, CODE_BASE_RELEASE_DATE);
|
||||
|
||||
// Create an array with 6 positions.
|
||||
array_1d *a = array_1d_create(1, 6, NULL);
|
||||
|
||||
for (int i=array_1d_low(a); i<=array_1d_high(a); i++) {
|
||||
// Allocate memory for an integer.
|
||||
int *v=int_create(i * i);
|
||||
array_1d_set_value(a, v, i);
|
||||
}
|
||||
printf("Array after inserting i^2, i=1,...,6:\n");
|
||||
array_1d_print(a, print_int);
|
||||
|
||||
// Empty the array.
|
||||
for (int i=array_1d_low(a); i<=array_1d_high(a); i++) {
|
||||
if (array_1d_has_value(a, i)) {
|
||||
int *v = array_1d_inspect_value(a, i);
|
||||
int_kill(v);
|
||||
}
|
||||
}
|
||||
array_1d_kill(a);
|
||||
|
||||
printf("\nNormal exit.\n\n");
|
||||
return 0;
|
||||
}
|
||||
146
OU3/src/array_1d/array_1d_mwe1i.c
Normal file
146
OU3/src/array_1d/array_1d_mwe1i.c
Normal file
@@ -0,0 +1,146 @@
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <array_1d.h>
|
||||
|
||||
/*
|
||||
* Minimum working example 1 for array_1d.c.
|
||||
*
|
||||
* Authors: Niclas Borlin (niclas@cs.umu.se)
|
||||
*
|
||||
* Version information:
|
||||
* v1.0 2024-04-05: Adapted from array_1d_mwe1.c.
|
||||
*/
|
||||
|
||||
#define VERSION "v1.0"
|
||||
#define VERSION_DATE "2024-04-05"
|
||||
|
||||
// Integers are stored via int pointers stored as void pointers.
|
||||
// Convert the given pointer and print the dereferenced value.
|
||||
void print_int(const void *data)
|
||||
{
|
||||
const int *v = data;
|
||||
printf("%d", *v);
|
||||
}
|
||||
|
||||
// Create a dynamic copy of the integer i.
|
||||
int *int_create(int i)
|
||||
{
|
||||
// Allocate memory for an integer and set the value
|
||||
int *v = malloc(sizeof(*v));
|
||||
*v = i;
|
||||
return v;
|
||||
}
|
||||
|
||||
// Return the memory used by the integer.
|
||||
void int_kill(void *v)
|
||||
{
|
||||
int *p = v; // Convert to a readable pointer - useful in a debugger
|
||||
free(p);
|
||||
}
|
||||
|
||||
// Print cut lines before and after a call array_print_internal.
|
||||
void print_internal_with_cut_lines(const array_1d *a, const char *desc)
|
||||
{
|
||||
// Internal counter that will be remembered between calls.
|
||||
|
||||
// Used to generate sequentially numbered -- CUT HERE -- marker lines
|
||||
// to enable automatic parsing of the output.
|
||||
static int cut = 1;
|
||||
|
||||
// Print starting marker line.
|
||||
printf("\n--- START CUT HERE %d ---\n", cut);
|
||||
|
||||
// Call the internal print function to get the actual dot code.
|
||||
array_1d_print_internal(a, print_int, desc, 0);
|
||||
|
||||
// Print ending marker line
|
||||
printf("--- END CUT HERE %d ---\n\n", cut);
|
||||
|
||||
// Increment the cut number. Will be remembered next time the
|
||||
// function is called since cut is a static variable.
|
||||
cut++;
|
||||
}
|
||||
|
||||
// Print a message with intructions how to use the dot output. prog is
|
||||
// the name of the executable.
|
||||
void print_dot_usage(char *prog)
|
||||
{
|
||||
printf("\nGenerate dot code to visualize internal structure with GraphViz. ");
|
||||
printf("Use\n\n%s ", prog);
|
||||
printf("| sed -n '/START CUT HERE X/,/END CUT HERE X/{//!p}' | dot -Tsvg > /tmp/dot.svg\n\n");
|
||||
printf("to generate an svg file of cut X (replace X by the requested cut number).\n");
|
||||
printf("The generated file can then be visualized with\n\n");
|
||||
printf("firefox /tmp/dot.svg\n\n");
|
||||
printf("Use -Tpng to generate a .png file instead. "
|
||||
"See graphviz.org and %s for documentation.\n", __FILE__);
|
||||
|
||||
printf("\n--- Start of normal output ---\n\n");
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
printf("%s, %s %s: Create integer array without kill_function.\n",
|
||||
__FILE__, VERSION, VERSION_DATE);
|
||||
printf("Code base version %s (%s).\n\n", CODE_BASE_VERSION, CODE_BASE_RELEASE_DATE);
|
||||
|
||||
print_dot_usage(argv[0]);
|
||||
|
||||
// Create an array with 6 positions.
|
||||
array_1d *a = array_1d_create(1, 6, NULL);
|
||||
|
||||
printf("Empty array from the outside:\n");
|
||||
array_1d_print(a, print_int);
|
||||
print_internal_with_cut_lines(a, "Empty array showing the internal structure");
|
||||
|
||||
int i;
|
||||
|
||||
// Set values of 50% of the elements
|
||||
for (i = array_1d_low(a); i <= array_1d_high(a)/2; i++) {
|
||||
// Allocate memory for an integer.
|
||||
int *v=int_create(i * i);
|
||||
array_1d_set_value(a,v,i);
|
||||
}
|
||||
printf("Array from the outside after setting half the values:\n");
|
||||
array_1d_print(a, print_int);
|
||||
const char *long_desc = __FILE__
|
||||
": Internal structure of the Array after setting 3 values.\n"
|
||||
"Red lines are used for the array payload.\n\n"
|
||||
"The dashed red lines indicate that the payload memory is\n"
|
||||
"BORROWED by the array, i.e., the payload\n"
|
||||
"memory will NOT be deallocated by the array.\n\n"
|
||||
"See array_1d_mwe2i for an array example\nthat owns the payload memory.";
|
||||
print_internal_with_cut_lines(a, long_desc);
|
||||
|
||||
// Set the rest of the element values.
|
||||
// Note: The empty initialization is on purpose.
|
||||
for ( ; i <= array_1d_high(a) ; i++) {
|
||||
// Allocate memory for an integer.
|
||||
int *v=int_create(i * i);
|
||||
array_1d_set_value(a,v,i);
|
||||
}
|
||||
|
||||
printf("Array from the outside after setting all the values:\n");
|
||||
array_1d_print(a, print_int);
|
||||
const char *long_desc2 = __FILE__
|
||||
": Internal structure of the Array after setting all 6 values.\n"
|
||||
"Red lines are used for the array payload.\n\n"
|
||||
"The dashed red lines indicate that the payload memory is\n"
|
||||
"BORROWED by the array, i.e., the payload\n"
|
||||
"memory will NOT be deallocated by the array.\n\n"
|
||||
"See array_1d_mwe2i for an array example\nthat owns the payload memory.";
|
||||
print_internal_with_cut_lines(a, long_desc2);
|
||||
|
||||
// Empty the array.
|
||||
for (int i=array_1d_low(a); i<=array_1d_high(a); i++) {
|
||||
if (array_1d_has_value(a, i)) {
|
||||
int *v=array_1d_inspect_value(a,i);
|
||||
int_kill(v);
|
||||
}
|
||||
}
|
||||
array_1d_kill(a);
|
||||
|
||||
printf("\nNormal exit.\n\n");
|
||||
return 0;
|
||||
}
|
||||
67
OU3/src/array_1d/array_1d_mwe2.c
Normal file
67
OU3/src/array_1d/array_1d_mwe2.c
Normal file
@@ -0,0 +1,67 @@
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <array_1d.h>
|
||||
|
||||
/*
|
||||
* Minimum working example 2 for array_1d.c.
|
||||
*
|
||||
* Authors: Niclas Borlin (niclas@cs.umu.se)
|
||||
*
|
||||
* Version information:
|
||||
* v1.0 2018-01-28: First public version.
|
||||
* v1.1 2018-04-03: Split into simpler, shorter versions.
|
||||
* v1.2 2023-01-14: Added printouts at start/end of main.
|
||||
* v1.3 2024-03-13: Added explicit create/kill functions.
|
||||
*/
|
||||
|
||||
#define VERSION "v1.3"
|
||||
#define VERSION_DATE "2024-03-13"
|
||||
|
||||
// Integers are stored via int pointers stored as void pointers.
|
||||
// Convert the given pointer and print the dereferenced value.
|
||||
void print_int(const void *data)
|
||||
{
|
||||
const int *v = data;
|
||||
printf("%d", *v);
|
||||
}
|
||||
|
||||
// Create a dynamic copy of the integer i.
|
||||
int *int_create(int i)
|
||||
{
|
||||
// Allocate memory for an integer and set the value
|
||||
int *v = malloc(sizeof(*v));
|
||||
*v = i;
|
||||
return v;
|
||||
}
|
||||
|
||||
// Return the memory used by the integer.
|
||||
void int_kill(void *v)
|
||||
{
|
||||
int *p = v;
|
||||
free(p);
|
||||
}
|
||||
|
||||
int main(void)
|
||||
{
|
||||
printf("%s, %s %s: Create integer array with kill_function.\n",
|
||||
__FILE__, VERSION, VERSION_DATE);
|
||||
printf("Code base version %s (%s).\n\n", CODE_BASE_VERSION, CODE_BASE_RELEASE_DATE);
|
||||
|
||||
// Create an array with 6 positions.
|
||||
array_1d *a = array_1d_create(1, 6, int_kill);
|
||||
|
||||
for (int i=array_1d_low(a); i<=array_1d_high(a); i++) {
|
||||
// Allocate memory for an integer.
|
||||
int *v=int_create(i * i);
|
||||
array_1d_set_value(a, v, i);
|
||||
}
|
||||
printf("Array after inserting i^2, i=1,...,6:\n");
|
||||
array_1d_print(a, print_int);
|
||||
|
||||
array_1d_kill(a);
|
||||
|
||||
printf("\nNormal exit.\n\n");
|
||||
return 0;
|
||||
}
|
||||
140
OU3/src/array_1d/array_1d_mwe2i.c
Normal file
140
OU3/src/array_1d/array_1d_mwe2i.c
Normal file
@@ -0,0 +1,140 @@
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <array_1d.h>
|
||||
|
||||
/*
|
||||
* Minimum working example 1 for array_1d.c.
|
||||
*
|
||||
* Authors: Niclas Borlin (niclas@cs.umu.se)
|
||||
*
|
||||
* Version information:
|
||||
* v1.0 2024-04-05: Adapted from array_1d_mwe1i.c.
|
||||
*/
|
||||
|
||||
#define VERSION "v1.0"
|
||||
#define VERSION_DATE "2024-04-05"
|
||||
|
||||
// Integers are stored via int pointers stored as void pointers.
|
||||
// Convert the given pointer and print the dereferenced value.
|
||||
void print_int(const void *data)
|
||||
{
|
||||
const int *v = data;
|
||||
printf("%d", *v);
|
||||
}
|
||||
|
||||
// Create a dynamic copy of the integer i.
|
||||
int *int_create(int i)
|
||||
{
|
||||
// Allocate memory for an integer and set the value
|
||||
int *v = malloc(sizeof(*v));
|
||||
*v = i;
|
||||
return v;
|
||||
}
|
||||
|
||||
// Return the memory used by the integer.
|
||||
void int_kill(void *v)
|
||||
{
|
||||
int *p = v; // Convert to a readable pointer - useful in a debugger
|
||||
free(p);
|
||||
}
|
||||
|
||||
// Print cut lines before and after a call array_print_internal.
|
||||
void print_internal_with_cut_lines(const array_1d *a, const char *desc)
|
||||
{
|
||||
// Internal counter that will be remembered between calls.
|
||||
|
||||
// Used to generate sequentially numbered -- CUT HERE -- marker lines
|
||||
// to enable automatic parsing of the output.
|
||||
static int cut = 1;
|
||||
|
||||
// Print starting marker line.
|
||||
printf("\n--- START CUT HERE %d ---\n", cut);
|
||||
|
||||
// Call the internal print function to get the actual dot code.
|
||||
array_1d_print_internal(a, print_int, desc, 0);
|
||||
|
||||
// Print ending marker line
|
||||
printf("--- END CUT HERE %d ---\n\n", cut);
|
||||
|
||||
// Increment the cut number. Will be remembered next time the
|
||||
// function is called since cut is a static variable.
|
||||
cut++;
|
||||
}
|
||||
|
||||
// Print a message with intructions how to use the dot output. prog is
|
||||
// the name of the executable.
|
||||
void print_dot_usage(char *prog)
|
||||
{
|
||||
printf("\nGenerate dot code to visualize internal structure with GraphViz. ");
|
||||
printf("Use\n\n%s ", prog);
|
||||
printf("| sed -n '/START CUT HERE X/,/END CUT HERE X/{//!p}' | dot -Tsvg > /tmp/dot.svg\n\n");
|
||||
printf("to generate an svg file of cut X (replace X by the requested cut number).\n");
|
||||
printf("The generated file can then be visualized with\n\n");
|
||||
printf("firefox /tmp/dot.svg\n\n");
|
||||
printf("Use -Tpng to generate a .png file instead. "
|
||||
"See graphviz.org and %s for documentation.\n", __FILE__);
|
||||
|
||||
printf("\n--- Start of normal output ---\n\n");
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
printf("%s, %s %s: Create integer array with kill_function.\n",
|
||||
__FILE__, VERSION, VERSION_DATE);
|
||||
printf("Code base version %s (%s).\n\n", CODE_BASE_VERSION, CODE_BASE_RELEASE_DATE);
|
||||
|
||||
print_dot_usage(argv[0]);
|
||||
|
||||
// Create an array with 6 positions.
|
||||
array_1d *a = array_1d_create(1, 6, int_kill);
|
||||
|
||||
printf("Empty array from the outside:\n");
|
||||
array_1d_print(a, print_int);
|
||||
print_internal_with_cut_lines(a, "Empty array showing the internal structure");
|
||||
|
||||
int i;
|
||||
|
||||
// Set values of 50% of the elements
|
||||
for (i = array_1d_low(a); i <= array_1d_high(a)/2; i++) {
|
||||
// Allocate memory for an integer.
|
||||
int *v=int_create(i * i);
|
||||
array_1d_set_value(a,v,i);
|
||||
}
|
||||
printf("Array from the outside after setting half the values:\n");
|
||||
array_1d_print(a, print_int);
|
||||
const char *long_desc = __FILE__
|
||||
": Internal structure of the Array after setting 3 values.\n"
|
||||
"Red lines are used for the array payload.\n\n"
|
||||
"The solid red lines indicate that the payload memory is\n"
|
||||
"OWNED -- not borrowed -- by the array, i.e., the payload\n"
|
||||
"memory WILL be deallocated by the array.\n\n"
|
||||
"See array_1d_mwe1i for an array example\nthat borrows the payload memory.";
|
||||
print_internal_with_cut_lines(a, long_desc);
|
||||
|
||||
// Set the rest of the element values.
|
||||
// Note: The empty initialization is on purpose.
|
||||
for ( ; i <= array_1d_high(a) ; i++) {
|
||||
// Allocate memory for an integer.
|
||||
int *v=int_create(i * i);
|
||||
array_1d_set_value(a,v,i);
|
||||
}
|
||||
|
||||
printf("Array from the outside after setting all the values:\n");
|
||||
array_1d_print(a, print_int);
|
||||
const char *long_desc2 = __FILE__
|
||||
": Internal structure of the Array after setting all 6 values.\n"
|
||||
"Red lines are used for the array payload.\n\n"
|
||||
"The solid red lines indicate that the payload memory is\n"
|
||||
"OWNED -- not borrowed -- by the array, i.e., the payload\n"
|
||||
"memory WILL be deallocated by the array.\n\n"
|
||||
"See array_1d_mwe1i for an array example\nthat borrows the payload memory.";
|
||||
print_internal_with_cut_lines(a, long_desc2);
|
||||
|
||||
// Clean up the array.
|
||||
array_1d_kill(a);
|
||||
|
||||
printf("\nNormal exit.\n\n");
|
||||
return 0;
|
||||
}
|
||||
99
OU3/src/array_1d/array_1d_mwe3.c
Normal file
99
OU3/src/array_1d/array_1d_mwe3.c
Normal file
@@ -0,0 +1,99 @@
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <array_1d.h>
|
||||
|
||||
/*
|
||||
* Minimum working example 3 for array_1d.c.
|
||||
*
|
||||
* Authors: Niclas Borlin (niclas@cs.umu.se)
|
||||
*
|
||||
* Version information:
|
||||
* v1.0 2018-01-28: First public version.
|
||||
* v1.1 2018-04-03: Split into simpler, shorter versions.
|
||||
* Removed use of automatic, magic, free_handler.
|
||||
* v1.2 2023-01-14: Added printouts at start/end of main.
|
||||
* v1.3 2024-03-14: Added explicit create/kill functions.
|
||||
*/
|
||||
|
||||
#define VERSION "v1.3"
|
||||
#define VERSION_DATE "2024-03-13"
|
||||
|
||||
// Struct with month name and number of days.
|
||||
typedef struct month {
|
||||
char *name;
|
||||
int days;
|
||||
} month;
|
||||
|
||||
// Months are stored via void pointers. Convert the given pointer and
|
||||
// print the dereferenced values.
|
||||
void print_month(const void *data)
|
||||
{
|
||||
// Convert void pointer to pointer to month.
|
||||
const month *m=data;
|
||||
printf("(%s, %d)", m->name, m->days);
|
||||
}
|
||||
|
||||
// Allocate space for and populate a month structure
|
||||
month *month_create(const char *name, int days)
|
||||
{
|
||||
// Allocate memory for a month structure.
|
||||
month *m = malloc(sizeof(*m));
|
||||
// Allocate memory for the month name.
|
||||
m->name = calloc(strlen(name) + 1, sizeof(char));
|
||||
// Copy the string.
|
||||
strcpy(m->name, name);
|
||||
// Set days.
|
||||
m->days = days;
|
||||
return m;
|
||||
}
|
||||
|
||||
// Function to free both month structure and char * with name.
|
||||
void month_kill(void *data)
|
||||
{
|
||||
// Convert void pointer to pointer to month.
|
||||
month *m=data;
|
||||
free(m->name);
|
||||
free(m);
|
||||
}
|
||||
|
||||
int main(void)
|
||||
{
|
||||
printf("%s, %s %s: Create month array without kill_function.\n",
|
||||
__FILE__, VERSION, VERSION_DATE);
|
||||
printf("Code base version %s (%s).\n\n", CODE_BASE_VERSION, CODE_BASE_RELEASE_DATE);
|
||||
|
||||
const char *month_names[12] = {
|
||||
"Jan", "Feb", "Mar", "Apr", "May", "Jun",
|
||||
"Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
|
||||
};
|
||||
int month_days[12] = {
|
||||
31, 28, 31, 30, 31, 30,
|
||||
31, 31, 30, 31, 30, 31
|
||||
};
|
||||
|
||||
// Create an array with 12 positions.
|
||||
array_1d *a = array_1d_create(1, 12, NULL);
|
||||
|
||||
for (int i=array_1d_low(a); i<=array_1d_high(a); i++) {
|
||||
// Allocate and populate a month structure.
|
||||
month *m=month_create(month_names[i - 1], month_days[i - 1]);
|
||||
// Set value in array.
|
||||
array_1d_set_value(a,m,i);
|
||||
}
|
||||
printf("Array after inserting 12 month structures (Jan, 31), ..., (Dec, 31):\n");
|
||||
array_1d_print(a, print_month);
|
||||
|
||||
// Empty the array.
|
||||
for (int i=array_1d_low(a); i<=array_1d_high(a); i++) {
|
||||
if (array_1d_has_value(a,i)) {
|
||||
month *v=array_1d_inspect_value(a,i);
|
||||
month_kill(v);
|
||||
}
|
||||
}
|
||||
array_1d_kill(a);
|
||||
|
||||
printf("\nNormal exit.\n\n");
|
||||
return 0;
|
||||
}
|
||||
91
OU3/src/array_1d/array_1d_mwe4.c
Normal file
91
OU3/src/array_1d/array_1d_mwe4.c
Normal file
@@ -0,0 +1,91 @@
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <array_1d.h>
|
||||
|
||||
/*
|
||||
* Minimum working example 4 for array_1d.c.
|
||||
*
|
||||
* Authors: Niclas Borlin (niclas@cs.umu.se)
|
||||
*
|
||||
* Version information:
|
||||
* v1.0 2018-01-28: First public version.
|
||||
* v1.1 2018-04-03: Split into simpler, shorter versions.
|
||||
* v1.2 2023-01-14: Added printouts at start/end of main.
|
||||
* v1.3 2024-03-14: Added explicit create/kill functions.
|
||||
*/
|
||||
|
||||
#define VERSION "v1.3"
|
||||
#define VERSION_DATE "2024-03-13"
|
||||
|
||||
// Struct with month name and number of days.
|
||||
typedef struct month {
|
||||
char *name;
|
||||
int days;
|
||||
} month;
|
||||
|
||||
// Months are stored via void pointers. Convert the given pointer and
|
||||
// print the dereferenced values.
|
||||
void print_month(const void *data)
|
||||
{
|
||||
// Convert void pointer to pointer to month.
|
||||
const month *m=data;
|
||||
printf("(%s, %d)", m->name, m->days);
|
||||
}
|
||||
|
||||
// Allocate space for and populate a month structure
|
||||
month *month_create(const char *name, int days)
|
||||
{
|
||||
// Allocate memory for a month structure.
|
||||
month *m = malloc(sizeof(*m));
|
||||
// Allocate memory for the month name.
|
||||
m->name = calloc(strlen(name) + 1, sizeof(char));
|
||||
// Copy the string.
|
||||
strcpy(m->name, name);
|
||||
// Set days.
|
||||
m->days = days;
|
||||
return m;
|
||||
}
|
||||
|
||||
// Function to free both month structure and char * with name.
|
||||
void month_kill(void *data)
|
||||
{
|
||||
// Convert void pointer to pointer to month.
|
||||
month *m=data;
|
||||
free(m->name);
|
||||
free(m);
|
||||
}
|
||||
|
||||
int main(void)
|
||||
{
|
||||
printf("%s, %s %s: Create month array with kill_function.\n",
|
||||
__FILE__, VERSION, VERSION_DATE);
|
||||
printf("Code base version %s (%s).\n\n", CODE_BASE_VERSION, CODE_BASE_RELEASE_DATE);
|
||||
|
||||
const char *month_names[12] = {
|
||||
"Jan", "Feb", "Mar", "Apr", "May", "Jun",
|
||||
"Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
|
||||
};
|
||||
int month_days[12] = {
|
||||
31, 28, 31, 30, 31, 30,
|
||||
31, 31, 30, 31, 30, 31
|
||||
};
|
||||
|
||||
// Create an array with 12 positions.
|
||||
array_1d *a = array_1d_create(1, 12, month_kill);
|
||||
|
||||
for (int i=array_1d_low(a); i<=array_1d_high(a); i++) {
|
||||
// Allocate and populate a month structure.
|
||||
month *m=month_create(month_names[i - 1], month_days[i - 1]);
|
||||
// Set value in array.
|
||||
array_1d_set_value(a,m,i);
|
||||
}
|
||||
printf("Array after inserting 12 month structures (Jan, 31), ..., (Dec, 31):\n");
|
||||
array_1d_print(a, print_month);
|
||||
|
||||
array_1d_kill(a);
|
||||
|
||||
printf("\nNormal exit.\n\n");
|
||||
return 0;
|
||||
}
|
||||
37
OU3/src/array_2d/Makefile
Normal file
37
OU3/src/array_2d/Makefile
Normal file
@@ -0,0 +1,37 @@
|
||||
MWE = array_2d_mwe1 array_2d_mwe2 array_2d_mwe1i array_2d_mwe2i
|
||||
|
||||
SRC = array_2d.c
|
||||
OBJ = $(SRC:.c=.o)
|
||||
|
||||
CC = gcc
|
||||
CFLAGS = -std=c99 -Wall -I../../include -g
|
||||
|
||||
all: mwe
|
||||
|
||||
# Minimum working examples.
|
||||
mwe: $(MWE)
|
||||
|
||||
# Object file for library
|
||||
obj: $(OBJ)
|
||||
|
||||
# Clean up
|
||||
clean:
|
||||
-rm -f $(MWE) $(OBJ)
|
||||
|
||||
array_2d_mwe1: array_2d_mwe1.c array_2d.c
|
||||
gcc -o $@ $(CFLAGS) $^
|
||||
|
||||
array_2d_mwe2: array_2d_mwe2.c array_2d.c
|
||||
gcc -o $@ $(CFLAGS) $^
|
||||
|
||||
array_2d_mwe1i: array_2d_mwe1i.c array_2d.c
|
||||
gcc -o $@ $(CFLAGS) $^
|
||||
|
||||
array_2d_mwe2i: array_2d_mwe2i.c array_2d.c
|
||||
gcc -o $@ $(CFLAGS) $^
|
||||
|
||||
memtest1: array_2d_mwe1
|
||||
valgrind --leak-check=full --show-reachable=yes ./$<
|
||||
|
||||
memtest2: array_2d_mwe2
|
||||
valgrind --leak-check=full --show-reachable=yes ./$<
|
||||
526
OU3/src/array_2d/array_2d.c
Normal file
526
OU3/src/array_2d/array_2d.c
Normal file
@@ -0,0 +1,526 @@
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdarg.h>
|
||||
|
||||
#include <array_2d.h>
|
||||
|
||||
/*
|
||||
* Implementation of a generic 2D array for the "Datastructures and
|
||||
* algorithms" courses at the Department of Computing Science, Umea
|
||||
* University.
|
||||
*
|
||||
* Authors: Niclas Borlin (niclas@cs.umu.se)
|
||||
*
|
||||
* Based on earlier code by: Johan Eliasson (johane@cs.umu.se).
|
||||
*
|
||||
* Version information:
|
||||
* v1.0 2018-01-28: First public version.
|
||||
* v1.1 2018-04-03: Moved freehandler to last in create parameter list.
|
||||
* v1.2 2024-05-10: Added print_internal.
|
||||
* v1.21 2024-05-15: Bugfix to get rid of sprintf warning.
|
||||
*/
|
||||
|
||||
// ===========INTERNAL DATA TYPES ============
|
||||
|
||||
struct array_2d {
|
||||
int low[2]; // Low index limits.
|
||||
int high[2]; // High index limits.
|
||||
int array_size; // Number of array elements.
|
||||
void **values; // Pointer to where the actual values are stored.
|
||||
kill_function kill_func;
|
||||
};
|
||||
|
||||
// ===========INTERNAL FUNCTION IMPLEMENTATIONS ============
|
||||
|
||||
/**
|
||||
* array_2d_create() - Create an array without values.
|
||||
* @lo1: low index limit for first dimension.
|
||||
* @hi1: high index limit for first dimension.
|
||||
* @lo2: low index limit for second dimension.
|
||||
* @hi2: high index limit for second dimension.
|
||||
* @kill_func: A pointer to a function (or NULL) to be called to
|
||||
* de-allocate memory on remove/kill.
|
||||
*
|
||||
* The index limits are inclusive, i.e. all indices i such that low <=
|
||||
* i <= high are defined.
|
||||
*
|
||||
* Returns: A pointer to the new array, or NULL if not enough memory
|
||||
* was available.
|
||||
*/
|
||||
array_2d *array_2d_create(int lo1, int hi1, int lo2, int hi2,
|
||||
kill_function kill_func)
|
||||
{
|
||||
// Allocate array structure.
|
||||
array_2d *a = calloc(1, sizeof(*a));
|
||||
// Store index limit.
|
||||
a->low[0] = lo1;
|
||||
a->low[1] = lo2;
|
||||
a->high[0] = hi1;
|
||||
a->high[1] = hi2;
|
||||
|
||||
// Number of elements.
|
||||
a->array_size = (hi1 - lo1 + 1) * (hi2 - lo2 + 1);
|
||||
|
||||
// Store kill function.
|
||||
a->kill_func = kill_func;
|
||||
|
||||
a->values=calloc(a->array_size, sizeof(void *));
|
||||
|
||||
// Check whether the allocation succeeded.
|
||||
if (a->values == NULL) {
|
||||
free(a);
|
||||
a = NULL;
|
||||
}
|
||||
return a;
|
||||
}
|
||||
|
||||
/**
|
||||
* array_2d_low() - Return the low index limit for the array.
|
||||
* @a: array to inspect.
|
||||
* @d: dimension number, 1 or 2.
|
||||
*
|
||||
* Returns: The low index limit for dimension number d.
|
||||
*/
|
||||
int array_2d_low(const array_2d *a, int d)
|
||||
{
|
||||
return a->low[d - 1];
|
||||
}
|
||||
|
||||
/**
|
||||
* array_2d_high() - Return the high index limit for the array.
|
||||
* @a: array to inspect.
|
||||
* @d: dimension number, 1 or 2.
|
||||
*
|
||||
* Returns: The high index limit for dimension number d.
|
||||
*/
|
||||
int array_2d_high(const array_2d *a, int d)
|
||||
{
|
||||
return a->high[d - 1];
|
||||
}
|
||||
|
||||
/**
|
||||
* array_2d_linear_index() - Internal function to compute linear index from list
|
||||
* of indices.
|
||||
* @a: array to inspect.
|
||||
* @i: First index.
|
||||
* @j: Second index.
|
||||
*
|
||||
* Returns: The linear index corresponding to the list of indices.
|
||||
* NOTE: The result is undefined if the number of arguments
|
||||
* or index value are out of bounds.
|
||||
*/
|
||||
static int array_2d_linear_index(const array_2d *a, int i, int j)
|
||||
{
|
||||
int rows = a->high[0] - a->low[0] + 1;
|
||||
int ix=(i - a->low[0]) + (j - a->low[1]) * rows;
|
||||
return ix;
|
||||
}
|
||||
|
||||
/**
|
||||
* array_2d_inspect_value() - Inspect a value at a given array position.
|
||||
* @a: array to inspect.
|
||||
* @i: First index of position to inspect.
|
||||
* @j: Second index of position to inspect.
|
||||
*
|
||||
* Returns: The element value at the specified position. The result is
|
||||
* undefined if no value are stored at that position.
|
||||
*/
|
||||
void *array_2d_inspect_value(const array_2d *a, int i, int j)
|
||||
{
|
||||
int ix = array_2d_linear_index(a, i, j);
|
||||
// Return the value.
|
||||
return a->values[ix];
|
||||
}
|
||||
|
||||
/**
|
||||
* array_2d_has_value() - Check if a value is set at a given array position.
|
||||
* @a: array to inspect.
|
||||
* @i: First index of position to inspect.
|
||||
* @j: Second index of position to inspect.
|
||||
*
|
||||
* Returns: True if a value is set at the specified position, otherwise false.
|
||||
*/
|
||||
bool array_2d_has_value(const array_2d *a, int i, int j)
|
||||
{
|
||||
int ix = array_2d_linear_index(a, i, j);
|
||||
// Return true if the value is not NULL.
|
||||
return a->values[ix] != NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* array_2d_set_value() - Set a value at a given array position.
|
||||
* @a: array to modify.
|
||||
* @v: value to set element to, or NULL to clear value.
|
||||
* @i: First index of position to modify.
|
||||
* @j: Second index of position to modify.
|
||||
*
|
||||
* If the old element value is non-NULL, calls kill_func if it was
|
||||
* specified at array creation.
|
||||
*
|
||||
* Returns: Nothing.
|
||||
*/
|
||||
void array_2d_set_value(array_2d *a, void *v, int i, int j)
|
||||
{
|
||||
int ix = array_2d_linear_index(a, i, j);
|
||||
// Call kill_func if specified and old element value was non-NULL.
|
||||
if (a->kill_func != NULL && a->values[ix] != NULL) {
|
||||
a->kill_func(a->values[ix]);
|
||||
}
|
||||
// Set value.
|
||||
a->values[ix]=v;
|
||||
}
|
||||
|
||||
/**
|
||||
* array_2d_kill() - Return memory allocated by array.
|
||||
* @a: array to kill.
|
||||
*
|
||||
* Iterates over all elements. If kill_func was specified at array
|
||||
* creation, calls it for every non-NULL element value.
|
||||
*
|
||||
* Returns: Nothing.
|
||||
*/
|
||||
void array_2d_kill(array_2d *a)
|
||||
{
|
||||
if (a->kill_func) {
|
||||
// Return user-allocated memory for each non-NULL element.
|
||||
for (int i=0; i<a->array_size; i++) {
|
||||
if (a->values[i] != NULL) {
|
||||
a->kill_func(a->values[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
// Free actual storage.
|
||||
free(a->values);
|
||||
// Free array structure.
|
||||
free(a);
|
||||
}
|
||||
|
||||
/**
|
||||
* array_2d_print() - Iterate over the array element and print their values.
|
||||
* @a: Array to inspect.
|
||||
* @print_func: Function called for each non-NULL element.
|
||||
*
|
||||
* Iterates over each position in the array. Calls print_func for each
|
||||
* non-NULL value.
|
||||
*
|
||||
* Returns: Nothing.
|
||||
*/
|
||||
void array_2d_print(const array_2d *a, inspect_callback print_func)
|
||||
{
|
||||
printf("[\n");
|
||||
for (int i=array_2d_low(a,1); i<=array_2d_high(a,1); i++) {
|
||||
printf(" [ ");
|
||||
for (int j=array_2d_low(a,2); j<=array_2d_high(a,2); j++) {
|
||||
if (array_2d_has_value(a,i,j)) {
|
||||
printf("[");
|
||||
print_func(array_2d_inspect_value(a,i,j));
|
||||
printf("]");
|
||||
} else {
|
||||
printf("[ ]");
|
||||
}
|
||||
if (j<array_2d_high(a,2)) {
|
||||
printf(", ");
|
||||
}
|
||||
}
|
||||
printf(" ]\n");
|
||||
}
|
||||
printf(" ]\n");
|
||||
}
|
||||
|
||||
// ===========INTERNAL FUNCTIONS USED BY array_1d_print_internal ============
|
||||
|
||||
// The functions below output code in the dot language, used by
|
||||
// GraphViz. For documention of the dot language, see graphviz.org.
|
||||
|
||||
/**
|
||||
* indent() - Output indentation string.
|
||||
* @n: Indentation level.
|
||||
*
|
||||
* Print n tab characters.
|
||||
*
|
||||
* Returns: Nothing.
|
||||
*/
|
||||
static void indent(int n)
|
||||
{
|
||||
for (int i=0; i<n; i++) {
|
||||
printf("\t");
|
||||
}
|
||||
}
|
||||
/**
|
||||
* iprintf(...) - Indent and print.
|
||||
* @n: Indentation level
|
||||
* @...: printf arguments
|
||||
*
|
||||
* Print n tab characters and calls printf.
|
||||
*
|
||||
* Returns: Nothing.
|
||||
*/
|
||||
static void iprintf(int n, const char *fmt, ...)
|
||||
{
|
||||
// Indent...
|
||||
indent(n);
|
||||
// ...and call printf
|
||||
va_list args;
|
||||
va_start(args, fmt);
|
||||
vprintf(fmt, args);
|
||||
va_end(args);
|
||||
}
|
||||
|
||||
/**
|
||||
* print_edge() - Print a edge between two addresses.
|
||||
* @from: The address of the start of the edge. Should be non-NULL.
|
||||
* @to: The address of the destination for the edge, including NULL.
|
||||
* @port: The name of the port on the source node, or NULL.
|
||||
* @label: The label for the edge, or NULL.
|
||||
* @options: A string with other edge options, or NULL.
|
||||
*
|
||||
* Print an edge from port PORT on node FROM to TO with label
|
||||
* LABEL. If to is NULL, the destination is the NULL node, otherwise a
|
||||
* memory node. If the port is NULL, the edge starts at the node, not
|
||||
* a specific port on it. If label is NULL, no label is used. The
|
||||
* options string, if non-NULL, is printed before the label.
|
||||
*
|
||||
* Returns: Nothing.
|
||||
*/
|
||||
static void print_edge(int indent_level, const void *from, const void *to, const char *port,
|
||||
const char *label, const char *options)
|
||||
{
|
||||
indent(indent_level);
|
||||
if (port) {
|
||||
printf("m%04lx:%s -> ", PTR2ADDR(from), port);
|
||||
} else {
|
||||
printf("m%04lx -> ", PTR2ADDR(from));
|
||||
}
|
||||
if (to == NULL) {
|
||||
printf("NULL");
|
||||
} else {
|
||||
printf("m%04lx", PTR2ADDR(to));
|
||||
}
|
||||
printf(" [");
|
||||
if (options != NULL) {
|
||||
printf("%s", options);
|
||||
}
|
||||
if (label != NULL) {
|
||||
printf(" label=\"%s\"",label);
|
||||
}
|
||||
printf("]\n");
|
||||
}
|
||||
|
||||
/**
|
||||
* print_head_node() - Print a node corresponding to the array head struct.
|
||||
* @indent_level: Indentation level.
|
||||
* @a: Array to inspect.
|
||||
*
|
||||
* Returns: Nothing.
|
||||
*/
|
||||
static void print_head_node(int indent_level, const array_2d *p)
|
||||
{
|
||||
iprintf(indent_level, "m%04lx [shape=record label=\"func\\n%04lx|low[0]\\n%d|high[0]\\n%d"
|
||||
"|low[1]\\n%d|high[1]\\n%d|array_size\\n%d|<v>values\\n%04lx\"]\n",
|
||||
PTR2ADDR(p), PTR2ADDR(p->kill_func), p->low[0], p->high[0],
|
||||
p->low[1], p->high[1], p->array_size, PTR2ADDR(p->values));
|
||||
}
|
||||
|
||||
static void print_values(int indent_level, const array_2d *a)
|
||||
{
|
||||
iprintf(indent_level, "m%04lx [shape=record label=\"", PTR2ADDR(a->values));
|
||||
for (int j=array_2d_low(a, 2); j <= array_2d_high(a, 2); j++) {
|
||||
for (int i=array_2d_low(a, 1); i <= array_2d_high(a, 1); i++) {
|
||||
int li=array_2d_linear_index(a, i, j);
|
||||
printf("<%02d>(%d,%d)\\n%02d\\n%04lx",
|
||||
li, i, j, li, PTR2ADDR(a->values[li]));
|
||||
if (li < a->array_size - 1) {
|
||||
printf("|");
|
||||
}
|
||||
}
|
||||
}
|
||||
printf("\"]\n");
|
||||
}
|
||||
|
||||
// Print edge from the array head to the values array.
|
||||
static void print_head_edge(int indent_level, const array_2d *a)
|
||||
{
|
||||
iprintf(indent_level, "m%04lx:v -> ", PTR2ADDR(a));
|
||||
if (a->values == NULL) {
|
||||
printf("NULL");
|
||||
} else {
|
||||
printf("m%04lx", PTR2ADDR(a->values));
|
||||
}
|
||||
printf(" [label=\"values\"]\n");
|
||||
}
|
||||
|
||||
// Print nodes for each value memory block
|
||||
static void print_value_nodes(int indent_level, const array_2d *a, inspect_callback print_func)
|
||||
{
|
||||
for (int i=0; i <= a->array_size; i++) {
|
||||
if (a->values[i] != NULL) {
|
||||
iprintf(indent_level, "m%04lx [label=\"", PTR2ADDR(a->values[i]));
|
||||
if (print_func != NULL) {
|
||||
print_func(a->values[i]);
|
||||
}
|
||||
printf("\" xlabel=\"%04lx\"]\n", PTR2ADDR(a->values[i]));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Print edges from each value pointer to payload memory
|
||||
static void print_value_edges(int indent_level, const array_2d *a)
|
||||
{
|
||||
for (int i=0; i < a->array_size; i++) {
|
||||
// Buffer to store port name in. Good for array up to 1e9 elements.
|
||||
char port[15];
|
||||
// Create port name
|
||||
sprintf(port, "%02d", i);
|
||||
if (a->kill_func) {
|
||||
print_edge(indent_level, a->values, a->values[i], port, port,
|
||||
"color=red");
|
||||
} else {
|
||||
print_edge(indent_level, a->values, a->values[i], port, port,
|
||||
"color=red style=dashed");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Create an escaped version of the input string. The most common
|
||||
// control characters - newline, horizontal tab, backslash, and double
|
||||
// quote - are replaced by their escape sequence. The returned pointer
|
||||
// must be deallocated by the caller.
|
||||
static char *escape_chars(const char *s)
|
||||
{
|
||||
int i, j;
|
||||
int escaped = 0; // The number of chars that must be escaped.
|
||||
|
||||
// Count how many chars need to be escaped, i.e. how much longer
|
||||
// the output string will be.
|
||||
for (i = escaped = 0; s[i] != '\0'; i++) {
|
||||
if (s[i] == '\n' || s[i] == '\t' || s[i] == '\\' || s[i] == '\"') {
|
||||
escaped++;
|
||||
}
|
||||
}
|
||||
// Allocate space for the escaped string. The variable i holds the input
|
||||
// length, escaped how much the string will grow.
|
||||
char *t = malloc(i + escaped + 1);
|
||||
|
||||
// Copy-and-escape loop
|
||||
for (i = j = 0; s[i] != '\0'; i++) {
|
||||
// Convert each control character by its escape sequence.
|
||||
// Non-control characters are copied as-is.
|
||||
switch (s[i]) {
|
||||
case '\n': t[i+j] = '\\'; t[i+j+1] = 'n'; j++; break;
|
||||
case '\t': t[i+j] = '\\'; t[i+j+1] = 't'; j++; break;
|
||||
case '\\': t[i+j] = '\\'; t[i+j+1] = '\\'; j++; break;
|
||||
case '\"': t[i+j] = '\\'; t[i+j+1] = '\"'; j++; break;
|
||||
default: t[i+j] = s[i]; break;
|
||||
}
|
||||
}
|
||||
// Terminal the output string
|
||||
t[i+j] = '\0';
|
||||
return t;
|
||||
}
|
||||
|
||||
/**
|
||||
* array_2d_print_internal() - Print the arrays internal structure.
|
||||
* @a: Array to inspect.
|
||||
* @print_func: Function called for each element value.
|
||||
* @desc: String with a description/state of the array, or NULL for no description.
|
||||
* @indent_level: Indentation level, 0 for outermost
|
||||
*
|
||||
* Iterates over the array and outputs dot code that shows the internal
|
||||
* structure of the array. The dot code can be visualized by
|
||||
* Graphviz.
|
||||
*
|
||||
* On linux system, the output can be parsed by the dot program, e.g.
|
||||
*
|
||||
* <array_program> | dot -Tsvg > /tmp/dot.svg; firefox /tmp/dot.svg
|
||||
*
|
||||
* where <array_program> is the name of the executable
|
||||
*
|
||||
* The output may also be possible to visualize online on
|
||||
* https://dreampuf.github.io/GraphvizOnline/ or google "graphviz
|
||||
* online".
|
||||
*
|
||||
* For documention of the dot language, see graphviz.org.
|
||||
*
|
||||
* Returns: Nothing.
|
||||
*/
|
||||
void array_2d_print_internal(const array_2d *a, inspect_callback print_func, const char *desc,
|
||||
int indent_level)
|
||||
{
|
||||
static int graph_number = 0;
|
||||
graph_number++;
|
||||
int il = indent_level;
|
||||
|
||||
if (indent_level == 0) {
|
||||
// If this is the outermost datatype, start a graph...
|
||||
printf("digraph ARRAY_2D_%d {\n", graph_number);
|
||||
|
||||
// Specify default shape and fontname
|
||||
il++;
|
||||
iprintf(il, "node [shape=rectangle fontname=\"Courier New\"]\n");
|
||||
iprintf(il, "ranksep=0.01\n");
|
||||
iprintf(il, "subgraph cluster_nullspace {\n");
|
||||
iprintf(il+1, "NULL\n");
|
||||
iprintf(il, "}\n");
|
||||
}
|
||||
|
||||
if (desc != NULL) {
|
||||
// Escape the string before printout
|
||||
char *escaped = escape_chars(desc);
|
||||
// Use different names on inner description nodes
|
||||
if (indent_level == 0) {
|
||||
iprintf(il, "description [label=\"%s\"]\n", escaped);
|
||||
} else {
|
||||
iprintf(il, "\tcluster_array_2d_%d_description [label=\"%s\"]\n", graph_number,
|
||||
escaped);
|
||||
}
|
||||
// Return the memory used by the escaped string
|
||||
free(escaped);
|
||||
}
|
||||
|
||||
if (indent_level == 0) {
|
||||
// Use a single "pointer" edge as a starting point for the
|
||||
// outermost datatype
|
||||
iprintf(il, "a [label=\"%04lx\" xlabel=\"a\"]\n", PTR2ADDR(a));
|
||||
iprintf(il, "a -> m%04lx\n", PTR2ADDR(a));
|
||||
}
|
||||
|
||||
// Print the subgraph to surround the Array content
|
||||
iprintf(il, "subgraph cluster_array_1d_%d { label=\"Array_1d\"\n", graph_number);
|
||||
il++;
|
||||
|
||||
// Output the head node
|
||||
print_head_node(il, a);
|
||||
|
||||
// Output the values array
|
||||
print_values(il, a);
|
||||
|
||||
// Close the subgraph
|
||||
il--;
|
||||
iprintf(il, "}\n");
|
||||
|
||||
if (indent_level == 0) {
|
||||
// Put the user nodes in userspace
|
||||
iprintf(il, "subgraph cluster_userspace { label=\"User space\"\n");
|
||||
il++;
|
||||
}
|
||||
|
||||
// Print nodes for each value memory block
|
||||
print_value_nodes(il, a, print_func);
|
||||
|
||||
if (indent_level == 0) {
|
||||
// Close userspace
|
||||
il--;
|
||||
iprintf(il, "}\n");
|
||||
}
|
||||
|
||||
// Output the edges from the head
|
||||
print_head_edge(il, a);
|
||||
|
||||
// Print edges from each value pointer to payload memory
|
||||
print_value_edges(il, a);
|
||||
|
||||
if (indent_level == 0) {
|
||||
// Termination of graph
|
||||
printf("}\n");
|
||||
}
|
||||
}
|
||||
77
OU3/src/array_2d/array_2d_mwe1.c
Normal file
77
OU3/src/array_2d/array_2d_mwe1.c
Normal file
@@ -0,0 +1,77 @@
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <array_2d.h>
|
||||
|
||||
/*
|
||||
* Minimum working example for array_2d.c.
|
||||
*
|
||||
* Authors: Niclas Borlin (niclas@cs.umu.se)
|
||||
*
|
||||
* Version information:
|
||||
* v1.0 2018-01-28: First public version.
|
||||
* v1.1 2018-04-03: Split into versions with/without memhandler.
|
||||
* v1.2 2023-01-14: Added printouts at start/end of main.
|
||||
*/
|
||||
|
||||
#define VERSION "v1.2"
|
||||
#define VERSION_DATE "2023-01-14"
|
||||
|
||||
// Integers are stored via int pointers stored as void pointers.
|
||||
// Convert the given pointer and print the dereferenced value.
|
||||
void print_int(const void *data)
|
||||
{
|
||||
const int *v = data;
|
||||
printf("%d", *v);
|
||||
}
|
||||
|
||||
// Create a dynamic copy of the integer i.
|
||||
int *int_create(int i)
|
||||
{
|
||||
// Allocate memory for an integer and set the value
|
||||
int *v = malloc(sizeof(*v));
|
||||
*v = i;
|
||||
return v;
|
||||
}
|
||||
|
||||
// Return the memory used by the integer.
|
||||
void int_kill(void *v)
|
||||
{
|
||||
int *p = v;
|
||||
free(p);
|
||||
}
|
||||
|
||||
int main(void)
|
||||
{
|
||||
printf("%s, %s %s: Create 4-by-3 array of integers without kill function.\n",
|
||||
__FILE__, VERSION, VERSION_DATE);
|
||||
printf("Code base version %s (%s).\n\n", CODE_BASE_VERSION, CODE_BASE_RELEASE_DATE);
|
||||
|
||||
// Create a 4-by-3 array.
|
||||
array_2d *a = array_2d_create(1, 4, 1, 3, NULL);
|
||||
|
||||
for (int i = array_2d_low(a, 1); i <= array_2d_high(a, 1); i++) {
|
||||
for (int j = array_2d_low(a, 2); j <= array_2d_high(a, 2); j++) {
|
||||
// Allocate memory for an integer.
|
||||
int *v=int_create(i * 10 + j);
|
||||
array_2d_set_value(a, v, i, j);
|
||||
}
|
||||
}
|
||||
printf("After filling the array with values:\n");
|
||||
array_2d_print(a, print_int);
|
||||
|
||||
// Empty the array.
|
||||
for (int i = array_2d_low(a, 1); i <= array_2d_high(a, 1); i++) {
|
||||
for (int j = array_2d_low(a, 2); j <= array_2d_high(a, 2); j++) {
|
||||
if (array_2d_has_value(a, i, j)) {
|
||||
int *v=array_2d_inspect_value(a, i, j);
|
||||
int_kill(v);
|
||||
}
|
||||
}
|
||||
}
|
||||
// Return remaining memory.
|
||||
array_2d_kill(a);
|
||||
|
||||
printf("\nNormal exit.\n\n");
|
||||
return 0;
|
||||
}
|
||||
152
OU3/src/array_2d/array_2d_mwe1i.c
Normal file
152
OU3/src/array_2d/array_2d_mwe1i.c
Normal file
@@ -0,0 +1,152 @@
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <array_2d.h>
|
||||
|
||||
/*
|
||||
* Minimum working example for array_2d.c.
|
||||
*
|
||||
* Authors: Niclas Borlin (niclas@cs.umu.se)
|
||||
*
|
||||
* Version information:
|
||||
* v1.0 2024-04-07: Adapted from array_2d_mwe1.c.
|
||||
*/
|
||||
|
||||
#define VERSION "v1.0"
|
||||
#define VERSION_DATE "2024-04-07"
|
||||
|
||||
// Integers are stored via int pointers stored as void pointers.
|
||||
// Convert the given pointer and print the dereferenced value.
|
||||
void print_int(const void *data)
|
||||
{
|
||||
const int *v = data;
|
||||
printf("%d", *v);
|
||||
}
|
||||
|
||||
// Create a dynamic copy of the integer i.
|
||||
int *int_create(int i)
|
||||
{
|
||||
// Allocate memory for an integer and set the value
|
||||
int *v = malloc(sizeof(*v));
|
||||
*v = i;
|
||||
return v;
|
||||
}
|
||||
|
||||
// Return the memory used by the integer.
|
||||
void int_kill(void *v)
|
||||
{
|
||||
int *p = v;
|
||||
free(p);
|
||||
}
|
||||
|
||||
// Print cut lines before and after a call array_print_internal.
|
||||
void print_internal_with_cut_lines(const array_2d *a, const char *desc)
|
||||
{
|
||||
// Internal counter that will be remembered between calls.
|
||||
|
||||
// Used to generate sequentially numbered -- CUT HERE -- marker lines
|
||||
// to enable automatic parsing of the output.
|
||||
static int cut = 1;
|
||||
|
||||
// Print starting marker line.
|
||||
printf("\n--- START CUT HERE %d ---\n", cut);
|
||||
|
||||
// Call the internal print function to get the actual dot code.
|
||||
array_2d_print_internal(a, print_int, desc, 0);
|
||||
|
||||
// Print ending marker line
|
||||
printf("--- END CUT HERE %d ---\n\n", cut);
|
||||
|
||||
// Increment the cut number. Will be remembered next time the
|
||||
// function is called since cut is a static variable.
|
||||
cut++;
|
||||
}
|
||||
|
||||
// Print a message with intructions how to use the dot output. prog is
|
||||
// the name of the executable.
|
||||
void print_dot_usage(char *prog)
|
||||
{
|
||||
printf("\nGenerate dot code to visualize internal structure with GraphViz. ");
|
||||
printf("Use\n\n%s ", prog);
|
||||
printf("| sed -n '/START CUT HERE X/,/END CUT HERE X/{//!p}' | dot -Tsvg > /tmp/dot.svg\n\n");
|
||||
printf("to generate an svg file of cut X (replace X by the requested cut number).\n");
|
||||
printf("The generated file can then be visualized with\n\n");
|
||||
printf("firefox /tmp/dot.svg\n\n");
|
||||
printf("Use -Tpng to generate a .png file instead. "
|
||||
"See graphviz.org and %s for documentation.\n", __FILE__);
|
||||
|
||||
printf("\n--- Start of normal output ---\n\n");
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
printf("%s, %s %s: Create 4-by-3 array of integers without kill function.\n",
|
||||
__FILE__, VERSION, VERSION_DATE);
|
||||
printf("Code base version %s (%s).\n\n", CODE_BASE_VERSION, CODE_BASE_RELEASE_DATE);
|
||||
|
||||
print_dot_usage(argv[0]);
|
||||
|
||||
// Create a 4-by-3 array.
|
||||
array_2d *a = array_2d_create(1, 4, 1, 3, NULL);
|
||||
|
||||
printf("Empty array from the outside:\n");
|
||||
array_2d_print(a, print_int);
|
||||
print_internal_with_cut_lines(a, "Empty array showing the internal structure");
|
||||
|
||||
int i;
|
||||
|
||||
// Set values of 50% of the elements
|
||||
for (i = array_2d_low(a, 1); i <= array_2d_high(a, 1)/2; i++) {
|
||||
for (int j = array_2d_low(a, 2); j <= array_2d_high(a, 2); j++) {
|
||||
// Allocate memory for an integer.
|
||||
int *v=int_create(i * 10 + j);
|
||||
array_2d_set_value(a, v, i, j);
|
||||
}
|
||||
}
|
||||
printf("Array from the outside after setting half the values:\n");
|
||||
array_2d_print(a, print_int);
|
||||
const char *long_desc = __FILE__
|
||||
": Internal structure of the Array after setting half of the values.\n"
|
||||
"Red lines are used for the array payload.\n\n"
|
||||
"The dashed red lines indicate that the payload memory is\n"
|
||||
"BORROWED by the array, i.e., the payload\n"
|
||||
"memory will NOT be deallocated by the array.\n\n"
|
||||
"See array_2d_mwe2i for an array example\nthat owns the payload memory.";
|
||||
print_internal_with_cut_lines(a, long_desc);
|
||||
|
||||
// Set the rest of the element values.
|
||||
// Note: The empty initialization is on purpose.
|
||||
for ( ; i <= array_2d_high(a, 1); i++) {
|
||||
for (int j = array_2d_low(a, 2); j <= array_2d_high(a, 2); j++) {
|
||||
// Allocate memory for an integer.
|
||||
int *v=int_create(i * 10 + j);
|
||||
array_2d_set_value(a, v, i, j);
|
||||
}
|
||||
}
|
||||
|
||||
printf("Array from the outside after setting all the values:\n");
|
||||
array_2d_print(a, print_int);
|
||||
const char *long_desc2 = __FILE__
|
||||
": Internal structure of the Array after setting all the values.\n"
|
||||
"Red lines are used for the array payload.\n\n"
|
||||
"The dashed red lines indicate that the payload memory is\n"
|
||||
"BORROWED by the array, i.e., the payload\n"
|
||||
"memory will NOT be deallocated by the array.\n\n"
|
||||
"See array_2d_mwe2i for an array example\nthat owns the payload memory.";
|
||||
print_internal_with_cut_lines(a, long_desc2);
|
||||
|
||||
// Empty the array.
|
||||
for (int i = array_2d_low(a, 1); i <= array_2d_high(a, 1); i++) {
|
||||
for (int j = array_2d_low(a, 2); j <= array_2d_high(a, 2); j++) {
|
||||
if (array_2d_has_value(a, i, j)) {
|
||||
int *v=array_2d_inspect_value(a, i, j);
|
||||
int_kill(v);
|
||||
}
|
||||
}
|
||||
}
|
||||
// Return remaining memory.
|
||||
array_2d_kill(a);
|
||||
|
||||
printf("\nNormal exit.\n\n");
|
||||
return 0;
|
||||
}
|
||||
68
OU3/src/array_2d/array_2d_mwe2.c
Normal file
68
OU3/src/array_2d/array_2d_mwe2.c
Normal file
@@ -0,0 +1,68 @@
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <array_2d.h>
|
||||
|
||||
/*
|
||||
* Minimum working example for array_2d.c.
|
||||
*
|
||||
* Authors: Niclas Borlin (niclas@cs.umu.se)
|
||||
*
|
||||
* Version information:
|
||||
* v1.0 2018-01-28: First public version.
|
||||
* v1.1 2018-04-03: Split into versions with/without memhandler.
|
||||
* v1.2 2023-01-14: Added printouts at start/end of main.
|
||||
*/
|
||||
|
||||
#define VERSION "v1.2"
|
||||
#define VERSION_DATE "2023-01-14"
|
||||
|
||||
// Integers are stored via int pointers stored as void pointers.
|
||||
// Convert the given pointer and print the dereferenced value.
|
||||
void print_int(const void *data)
|
||||
{
|
||||
const int *v = data;
|
||||
printf("%d", *v);
|
||||
}
|
||||
|
||||
// Create a dynamic copy of the integer i.
|
||||
int *int_create(int i)
|
||||
{
|
||||
// Allocate memory for an integer and set the value
|
||||
int *v = malloc(sizeof(*v));
|
||||
*v = i;
|
||||
return v;
|
||||
}
|
||||
|
||||
// Return the memory used by the integer.
|
||||
void int_kill(void *v)
|
||||
{
|
||||
int *p = v;
|
||||
free(p);
|
||||
}
|
||||
|
||||
int main(void)
|
||||
{
|
||||
printf("%s, %s %s: Create 4-by-3 array of integers with kill function.\n",
|
||||
__FILE__, VERSION, VERSION_DATE);
|
||||
printf("Code base version %s (%s).\n\n", CODE_BASE_VERSION, CODE_BASE_RELEASE_DATE);
|
||||
|
||||
// Create a 4-by-3 array.
|
||||
array_2d *a = array_2d_create(1, 4, 1, 3, int_kill);
|
||||
|
||||
for (int i = array_2d_low(a, 1); i <= array_2d_high(a, 1); i++) {
|
||||
for (int j = array_2d_low(a, 2); j <= array_2d_high(a, 2); j++) {
|
||||
// Allocate memory for an integer.
|
||||
int *v=int_create(i * 10 + j);
|
||||
array_2d_set_value(a, v, i, j);
|
||||
}
|
||||
}
|
||||
printf("After filling the array with values:\n");
|
||||
array_2d_print(a, print_int);
|
||||
|
||||
// Return remaining memory.
|
||||
array_2d_kill(a);
|
||||
|
||||
printf("\nNormal exit.\n\n");
|
||||
return 0;
|
||||
}
|
||||
152
OU3/src/array_2d/array_2d_mwe2i.c
Normal file
152
OU3/src/array_2d/array_2d_mwe2i.c
Normal file
@@ -0,0 +1,152 @@
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <array_2d.h>
|
||||
|
||||
/*
|
||||
* Minimum working example for array_2d.c.
|
||||
*
|
||||
* Authors: Niclas Borlin (niclas@cs.umu.se)
|
||||
*
|
||||
* Version information:
|
||||
* v1.0 2024-04-07: Adapted from array_2d_mwe1i.c.
|
||||
*/
|
||||
|
||||
#define VERSION "v1.0"
|
||||
#define VERSION_DATE "2024-04-07"
|
||||
|
||||
// Integers are stored via int pointers stored as void pointers.
|
||||
// Convert the given pointer and print the dereferenced value.
|
||||
void print_int(const void *data)
|
||||
{
|
||||
const int *v = data;
|
||||
printf("%d", *v);
|
||||
}
|
||||
|
||||
// Create a dynamic copy of the integer i.
|
||||
int *int_create(int i)
|
||||
{
|
||||
// Allocate memory for an integer and set the value
|
||||
int *v = malloc(sizeof(*v));
|
||||
*v = i;
|
||||
return v;
|
||||
}
|
||||
|
||||
// Return the memory used by the integer.
|
||||
void int_kill(void *v)
|
||||
{
|
||||
int *p = v; // Convert to a readable pointer - useful in a debugger
|
||||
free(p);
|
||||
}
|
||||
|
||||
// Print cut lines before and after a call array_print_internal.
|
||||
void print_internal_with_cut_lines(const array_2d *a, const char *desc)
|
||||
{
|
||||
// Internal counter that will be remembered between calls.
|
||||
|
||||
// Used to generate sequentially numbered -- CUT HERE -- marker lines
|
||||
// to enable automatic parsing of the output.
|
||||
static int cut = 1;
|
||||
|
||||
// Print starting marker line.
|
||||
printf("\n--- START CUT HERE %d ---\n", cut);
|
||||
|
||||
// Call the internal print function to get the actual dot code.
|
||||
array_2d_print_internal(a, print_int, desc, 0);
|
||||
|
||||
// Print ending marker line
|
||||
printf("--- END CUT HERE %d ---\n\n", cut);
|
||||
|
||||
// Increment the cut number. Will be remembered next time the
|
||||
// function is called since cut is a static variable.
|
||||
cut++;
|
||||
}
|
||||
|
||||
// Print a message with intructions how to use the dot output. prog is
|
||||
// the name of the executable.
|
||||
void print_dot_usage(char *prog)
|
||||
{
|
||||
printf("\nGenerate dot code to visualize internal structure with GraphViz. ");
|
||||
printf("Use\n\n%s ", prog);
|
||||
printf("| sed -n '/START CUT HERE X/,/END CUT HERE X/{//!p}' | dot -Tsvg > /tmp/dot.svg\n\n");
|
||||
printf("to generate an svg file of cut X (replace X by the requested cut number).\n");
|
||||
printf("The generated file can then be visualized with\n\n");
|
||||
printf("firefox /tmp/dot.svg\n\n");
|
||||
printf("Use -Tpng to generate a .png file instead. "
|
||||
"See graphviz.org and %s for documentation.\n", __FILE__);
|
||||
|
||||
printf("\n--- Start of normal output ---\n\n");
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
printf("%s, %s %s: Create 4-by-3 array of integers with kill function.\n",
|
||||
__FILE__, VERSION, VERSION_DATE);
|
||||
printf("Code base version %s (%s).\n\n", CODE_BASE_VERSION, CODE_BASE_RELEASE_DATE);
|
||||
|
||||
print_dot_usage(argv[0]);
|
||||
|
||||
// Create a 4-by-3 array.
|
||||
array_2d *a = array_2d_create(1, 4, 1, 3, int_kill);
|
||||
|
||||
printf("Empty array from the outside:\n");
|
||||
array_2d_print(a, print_int);
|
||||
print_internal_with_cut_lines(a, "Empty array showing the internal structure");
|
||||
|
||||
int i;
|
||||
|
||||
// Set values of 50% of the elements
|
||||
for (i = array_2d_low(a, 1); i <= array_2d_high(a, 1)/2; i++) {
|
||||
for (int j = array_2d_low(a, 2); j <= array_2d_high(a, 2); j++) {
|
||||
// Allocate memory for an integer.
|
||||
int *v=int_create(i * 10 + j);
|
||||
array_2d_set_value(a, v, i, j);
|
||||
}
|
||||
}
|
||||
printf("Array from the outside after setting half the values:\n");
|
||||
array_2d_print(a, print_int);
|
||||
const char *long_desc = __FILE__
|
||||
": Internal structure of the Array after setting half of the values.\n"
|
||||
"Red lines are used for the array payload.\n\n"
|
||||
"The solid red lines indicate that the payload memory is\n"
|
||||
"OWNED -- not borrowed -- by the array, i.e., the payload\n"
|
||||
"memory WILL be deallocated by the array.\n\n"
|
||||
"See array_2d_mwe1i for an array example\nthat borrows the payload memory.";
|
||||
print_internal_with_cut_lines(a, long_desc);
|
||||
|
||||
// Set the rest of the element values.
|
||||
// Note: The empty initialization is on purpose.
|
||||
for ( ; i <= array_2d_high(a, 1); i++) {
|
||||
for (int j = array_2d_low(a, 2); j <= array_2d_high(a, 2); j++) {
|
||||
// Allocate memory for an integer.
|
||||
int *v=int_create(i * 10 + j);
|
||||
array_2d_set_value(a, v, i, j);
|
||||
}
|
||||
}
|
||||
|
||||
printf("Array from the outside after setting all the values:\n");
|
||||
array_2d_print(a, print_int);
|
||||
const char *long_desc2 = __FILE__
|
||||
": Internal structure of the Array after setting all the values.\n"
|
||||
"Red lines are used for the array payload.\n\n"
|
||||
"The solid red lines indicate that the payload memory is\n"
|
||||
"OWNED -- not borrowed -- by the array, i.e., the payload\n"
|
||||
"memory WILL be deallocated by the array.\n\n"
|
||||
"See array_2d_mwe1i for an array example\nthat borrows the payload memory.";
|
||||
print_internal_with_cut_lines(a, long_desc2);
|
||||
|
||||
// Empty the array.
|
||||
for (int i = array_2d_low(a, 1); i <= array_2d_high(a, 1); i++) {
|
||||
for (int j = array_2d_low(a, 2); j <= array_2d_high(a, 2); j++) {
|
||||
if (array_2d_has_value(a, i, j)) {
|
||||
int *v=array_2d_inspect_value(a, i, j);
|
||||
int_kill(v);
|
||||
}
|
||||
}
|
||||
}
|
||||
// Return remaining memory.
|
||||
array_2d_kill(a);
|
||||
|
||||
printf("\nNormal exit.\n\n");
|
||||
return 0;
|
||||
}
|
||||
42
OU3/src/dlist/Makefile
Normal file
42
OU3/src/dlist/Makefile
Normal file
@@ -0,0 +1,42 @@
|
||||
MWE = dlist_mwe1 dlist_mwe2 dlist_mwe1i dlist_mwe2i
|
||||
|
||||
SRC = dlist.c
|
||||
OBJ = $(SRC:.c=.o)
|
||||
|
||||
CC = gcc
|
||||
CFLAGS = -std=c99 -Wall -I../../include -g
|
||||
|
||||
all: mwe
|
||||
|
||||
# Minimum working examples.
|
||||
mwe: $(MWE)
|
||||
|
||||
# Object file for library
|
||||
obj: $(OBJ)
|
||||
|
||||
clean:
|
||||
-rm -f $(MWE) $(OBJ)
|
||||
|
||||
dlist_mwe1: dlist_mwe1.c dlist.c
|
||||
gcc -o $@ $(CFLAGS) $^
|
||||
|
||||
dlist_mwe2: dlist_mwe2.c dlist.c
|
||||
gcc -o $@ $(CFLAGS) $^
|
||||
|
||||
dlist_mwe1i: dlist_mwe1i.c dlist.c
|
||||
gcc -o $@ $(CFLAGS) $^
|
||||
|
||||
dlist_mwe2i: dlist_mwe2i.c dlist.c
|
||||
gcc -o $@ $(CFLAGS) $^
|
||||
|
||||
memtest1: dlist_mwe1
|
||||
valgrind --leak-check=full --show-reachable=yes ./$<
|
||||
|
||||
memtest2: dlist_mwe2
|
||||
valgrind --leak-check=full --show-reachable=yes ./$<
|
||||
|
||||
memtest3: dlist_mwe1i
|
||||
valgrind --leak-check=full --show-reachable=yes ./$<
|
||||
|
||||
memtest4: dlist_mwe2i
|
||||
valgrind --leak-check=full --show-reachable=yes ./$<
|
||||
9
OU3/src/dlist/README.md
Normal file
9
OU3/src/dlist/README.md
Normal file
@@ -0,0 +1,9 @@
|
||||
# Riktad lista
|
||||
|
||||
En implementation av ADTn _Riktad Lista_ baserad på en enkel-länkad lista.
|
||||
|
||||
## Minneshantering och utskrift
|
||||
|
||||
Det mesta av hur gränsytan används med avseende på minneshantering och
|
||||
utskrifter är analogt för hur [listimplementationen](../list/) fungerar.
|
||||
|
||||
588
OU3/src/dlist/dlist.c
Normal file
588
OU3/src/dlist/dlist.c
Normal file
@@ -0,0 +1,588 @@
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdarg.h>
|
||||
|
||||
#include <dlist.h>
|
||||
|
||||
/*
|
||||
* Implementation of a generic, undirected list for the
|
||||
* "Datastructures and algorithms" courses at the Department of
|
||||
* Computing Science, Umea University.
|
||||
*
|
||||
* Authors: Niclas Borlin (niclas@cs.umu.se)
|
||||
* Adam Dahlgren Lindstrom (dali@cs.umu.se)
|
||||
* Lars Karlsson (larsk@cs.umu.se)
|
||||
*
|
||||
* Based on earlier code by: Johan Eliasson (johane@cs.umu.se).
|
||||
*
|
||||
* Version information:
|
||||
* v1.0 2018-01-28: First public version.
|
||||
* v1.1 2023-01-19: Added dlist_pos_are_equal and dlist_pos_is_valid functions.
|
||||
* v1.2 2023-03-23: Renamed dlist_pos_are_equal to dlist_pos_is_equal.
|
||||
* v1.3 2023-03-23: Renamed dlist_pos_are_equal to dlist_pos_is_equal.
|
||||
* v2.0 2024-03-14: Added dlist_print_internal to output dot code for visualization.
|
||||
* Renamed free_* stuff to kill_*. Converted to 4-tabs.
|
||||
* v2.1 2024-05-10: Updated print_internal with improved encapsulation.
|
||||
*/
|
||||
|
||||
// ===========INTERNAL DATA TYPES============
|
||||
|
||||
/*
|
||||
* The list elements are implemented as one-cells with a forward link.
|
||||
* The list position is a pointer to the internal cell before the cell
|
||||
* with the value.
|
||||
*/
|
||||
typedef struct cell {
|
||||
struct cell *next;
|
||||
void *val;
|
||||
} cell;
|
||||
|
||||
|
||||
struct dlist {
|
||||
cell *head;
|
||||
kill_function kill_func;
|
||||
};
|
||||
|
||||
// ===========INTERNAL FUNCTION IMPLEMENTATIONS============
|
||||
|
||||
/**
|
||||
* dlist_empty() - Create an empty dlist.
|
||||
* @kill_func: A pointer to a function (or NULL) to be called to
|
||||
* de-allocate memory on remove/kill.
|
||||
*
|
||||
* Returns: A pointer to the new list.
|
||||
*/
|
||||
dlist *dlist_empty(kill_function kill_func)
|
||||
{
|
||||
// Allocate memory for the list structure.
|
||||
dlist *l = calloc(1, sizeof(*l));
|
||||
|
||||
// Allocate memory for the list head.
|
||||
l->head = calloc(1, sizeof(cell));
|
||||
|
||||
// No elements in list so far.
|
||||
l->head->next = NULL;
|
||||
|
||||
// Store the kill function.
|
||||
l->kill_func = kill_func;
|
||||
|
||||
return l;
|
||||
}
|
||||
|
||||
/**
|
||||
* dlist_is_empty() - Check if a dlist is empty.
|
||||
* @l: List to check.
|
||||
*
|
||||
* Returns: True if the list is empty, otherwise false.
|
||||
*/
|
||||
bool dlist_is_empty(const dlist *l)
|
||||
{
|
||||
return (l->head->next == NULL);
|
||||
}
|
||||
|
||||
/**
|
||||
* dlist_first() - Return the first position of a dlist, i.e. the
|
||||
* position of the first element in the list.
|
||||
* @l: List to inspect.
|
||||
*
|
||||
* Returns: The first position in the given list.
|
||||
*/
|
||||
dlist_pos dlist_first(const dlist *l)
|
||||
{
|
||||
return l->head;
|
||||
}
|
||||
|
||||
/**
|
||||
* dlist_next() - Return the next position in a dlist.
|
||||
* @l: List to inspect.
|
||||
* @p: Any valid position except the last in the list.
|
||||
*
|
||||
* Returns: The position in the list after the given position.
|
||||
* NOTE: The return value is undefined for the last position.
|
||||
*/
|
||||
dlist_pos dlist_next(const dlist *l, const dlist_pos p)
|
||||
{
|
||||
if (dlist_is_end(l, p)) {
|
||||
// This should really throw an error.
|
||||
fprintf(stderr,"dlist_next: Warning: Trying to navigate "
|
||||
"past end of list!");
|
||||
}
|
||||
return p->next;
|
||||
}
|
||||
|
||||
/**
|
||||
* dlist_is_end() - Check if a given position is at the end of a dlist.
|
||||
* @l: List to inspect.
|
||||
* @p: Any valid position in the list.
|
||||
*
|
||||
* Returns: True if p is at the end of the list.
|
||||
*/
|
||||
bool dlist_is_end(const dlist *l, const dlist_pos p)
|
||||
{
|
||||
return p->next == NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* dlist_inspect() - Return the value of the element at a given
|
||||
* position in a list.
|
||||
* @l: List to inspect.
|
||||
* @p: Any valid position in the list, except the last.
|
||||
*
|
||||
* Returns: Returns the value at the given position as a void pointer.
|
||||
* NOTE: The return value is undefined for the last position.
|
||||
*/
|
||||
void *dlist_inspect(const dlist *l, const dlist_pos p)
|
||||
{
|
||||
if (dlist_is_end(l, p)) {
|
||||
// This should really throw an error.
|
||||
fprintf(stderr,"dlist_inspect: Warning: Trying to inspect "
|
||||
"position at end of list!\n");
|
||||
}
|
||||
return p->next->val;
|
||||
}
|
||||
|
||||
/**
|
||||
* dlist_insert() - Insert a new element with a given value into a dlist.
|
||||
* @l: List to manipulate.
|
||||
* @v: Value (pointer) to be inserted into the list.
|
||||
* @p: Position in the list before which the value should be inserted.
|
||||
*
|
||||
* Creates a new element and inserts it into the list before p.
|
||||
* Stores data in the new element.
|
||||
*
|
||||
* Returns: The position of the newly created element.
|
||||
*/
|
||||
dlist_pos dlist_insert(dlist *l, void *v, const dlist_pos p)
|
||||
{
|
||||
// Create new element.
|
||||
dlist_pos new_pos=calloc(1, sizeof(cell));
|
||||
// Set value.
|
||||
new_pos->val=v;
|
||||
|
||||
// Set links.
|
||||
new_pos->next=p->next;
|
||||
p->next=new_pos;
|
||||
|
||||
return p;
|
||||
}
|
||||
|
||||
/**
|
||||
* dlist_remove() - Remove an element from a dlist.
|
||||
* @l: List to manipulate.
|
||||
* @p: Position in the list of the element to remove.
|
||||
*
|
||||
* Removes the element at position p from the list. If a kill_func
|
||||
* was registered at list creation, calls it to deallocate the memory
|
||||
* held by the element value.
|
||||
*
|
||||
* Returns: The position after the removed element.
|
||||
*/
|
||||
dlist_pos dlist_remove(dlist *l, const dlist_pos p)
|
||||
{
|
||||
// Cell to remove.
|
||||
dlist_pos c=p->next;
|
||||
|
||||
// Link past cell to remove.
|
||||
p->next=c->next;
|
||||
|
||||
// Call kill_func if registered.
|
||||
if(l->kill_func != NULL) {
|
||||
// Return any user-allocated memory for the value.
|
||||
l->kill_func(c->val);
|
||||
}
|
||||
// Free the memory allocated to the cell itself.
|
||||
free(c);
|
||||
// Return the position of the next element.
|
||||
return p;
|
||||
}
|
||||
|
||||
/**
|
||||
* dlist_kill() - Destroy a given dlist.
|
||||
* @l: List to destroy.
|
||||
*
|
||||
* Return all dynamic memory used by the list and its elements. If a
|
||||
* kill_func was registered at list creation, also calls it for each
|
||||
* element to return any user-allocated memory occupied by the element values.
|
||||
*
|
||||
* Returns: Nothing.
|
||||
*/
|
||||
void dlist_kill(dlist *l)
|
||||
{
|
||||
// Use public functions to traverse the list.
|
||||
|
||||
// Start with the first element (will be defined even for an
|
||||
// empty list).
|
||||
dlist_pos p = dlist_first(l);
|
||||
|
||||
// Remove first element until list is empty.
|
||||
while(!dlist_is_empty(l)) {
|
||||
p = dlist_remove(l, p);
|
||||
}
|
||||
|
||||
// Free the head and the list itself.
|
||||
free(l->head);
|
||||
free(l);
|
||||
}
|
||||
|
||||
/**
|
||||
* dlist_print() - Iterate over the list element and print their values.
|
||||
* @l: List to inspect.
|
||||
* @print_func: Function called for each element.
|
||||
*
|
||||
* Iterates over the list and calls print_func with the value stored
|
||||
* in each element.
|
||||
*
|
||||
* Returns: Nothing.
|
||||
*/
|
||||
void dlist_print(const dlist *l, inspect_callback print_func)
|
||||
{
|
||||
// Start at the beginning of the list.
|
||||
dlist_pos p = dlist_first(l);
|
||||
|
||||
printf("( ");
|
||||
while (!dlist_is_end(l, p)) {
|
||||
// Call print_func with the element value at the
|
||||
// current position.
|
||||
print_func(dlist_inspect(l, p));
|
||||
// Advance to next position.
|
||||
p = dlist_next(l, p);
|
||||
// Print separator unless at element.
|
||||
if (!dlist_is_end(l, p)) {
|
||||
printf(", ");
|
||||
}
|
||||
}
|
||||
printf(" )\n");
|
||||
}
|
||||
|
||||
/**
|
||||
* dlist_pos_is_equal() - Return true if two positions in a dlist are equal.
|
||||
* @l: List to inspect.
|
||||
* @p1: First position to compare.
|
||||
* @p2: Second position to compare.
|
||||
*
|
||||
* Returns: True if p1 and p2 refer to the same position in l, otherwise False.
|
||||
* NOTE: The result is defined only if p1 and p2 are valid positions in l.
|
||||
*/
|
||||
bool dlist_pos_is_equal(const dlist *l, const dlist_pos p1, const dlist_pos p2)
|
||||
{
|
||||
// Since we don't need to check whether p1 or p2 are valid, we
|
||||
// only need to compare them directly.
|
||||
return p1 == p2;
|
||||
}
|
||||
|
||||
/**
|
||||
* dlist_pos_is_valid() - Return true for a valid position in a dlist.
|
||||
* @l: List to inspect.
|
||||
* @p: Any position.
|
||||
*
|
||||
* Returns: True if p is a valid position in the list, otherwise false.
|
||||
*/
|
||||
bool dlist_pos_is_valid(const dlist *l, const dlist_pos p)
|
||||
{
|
||||
// Iterate over all positions in l.
|
||||
dlist_pos q = dlist_first(l);
|
||||
while (!dlist_is_end(l, q)) {
|
||||
if (dlist_pos_is_equal(l, p, q)) {
|
||||
// We found the position in the list.
|
||||
return true;
|
||||
}
|
||||
// Advance to the next valid position,
|
||||
q = dlist_next(l, q);
|
||||
}
|
||||
// p was not among valid positions in l.
|
||||
return false;
|
||||
}
|
||||
|
||||
// ===========INTERNAL FUNCTIONS USED BY dlist_print_internal ============
|
||||
|
||||
// The functions below output code in the dot language, used by
|
||||
// GraphViz. For documention of the dot language, see graphviz.org.
|
||||
|
||||
/**
|
||||
* indent() - Output indentation string.
|
||||
* @n: Indentation level.
|
||||
*
|
||||
* Print n tab characters.
|
||||
*
|
||||
* Returns: Nothing.
|
||||
*/
|
||||
static void indent(int n)
|
||||
{
|
||||
for (int i=0; i<n; i++) {
|
||||
printf("\t");
|
||||
}
|
||||
}
|
||||
/**
|
||||
* iprintf(...) - Indent and print.
|
||||
* @n: Indentation level
|
||||
* @...: printf arguments
|
||||
*
|
||||
* Print n tab characters and calls printf.
|
||||
*
|
||||
* Returns: Nothing.
|
||||
*/
|
||||
static void iprintf(int n, const char *fmt, ...)
|
||||
{
|
||||
// Indent...
|
||||
indent(n);
|
||||
// ...and call printf
|
||||
va_list args;
|
||||
va_start(args, fmt);
|
||||
vprintf(fmt, args);
|
||||
va_end(args);
|
||||
}
|
||||
|
||||
/**
|
||||
* print_edge() - Print a edge between two addresses.
|
||||
* @from: The address of the start of the edge. Should be non-NULL.
|
||||
* @to: The address of the destination for the edge, including NULL.
|
||||
* @port: The name of the port on the source node, or NULL.
|
||||
* @label: The label for the edge, or NULL.
|
||||
* @options: A string with other edge options, or NULL.
|
||||
*
|
||||
* Print an edge from port PORT on node FROM to TO with label
|
||||
* LABEL. If to is NULL, the destination is the NULL node, otherwise a
|
||||
* memory node. If the port is NULL, the edge starts at the node, not
|
||||
* a specific port on it. If label is NULL, no label is used. The
|
||||
* options string, if non-NULL, is printed before the label.
|
||||
*
|
||||
* Returns: Nothing.
|
||||
*/
|
||||
static void print_edge(int indent_level, const void *from, const void *to, const char *port,
|
||||
const char *label, const char *options)
|
||||
{
|
||||
indent(indent_level);
|
||||
if (port) {
|
||||
printf("m%04lx:%s -> ", PTR2ADDR(from), port);
|
||||
} else {
|
||||
printf("m%04lx -> ", PTR2ADDR(from));
|
||||
}
|
||||
if (to == NULL) {
|
||||
printf("NULL");
|
||||
} else {
|
||||
printf("m%04lx", PTR2ADDR(to));
|
||||
}
|
||||
printf(" [");
|
||||
if (options != NULL) {
|
||||
printf("%s", options);
|
||||
}
|
||||
if (label != NULL) {
|
||||
printf(" label=\"%s\"",label);
|
||||
}
|
||||
printf("]\n");
|
||||
}
|
||||
|
||||
/**
|
||||
* print_head_node() - Print a node corresponding to the dlist head struct.
|
||||
* @indent_level: Indentation level.
|
||||
* @l: List to inspect.
|
||||
*
|
||||
* Returns: Nothing.
|
||||
*/
|
||||
static void print_head_node(int indent_level, const dlist *l)
|
||||
{
|
||||
iprintf(indent_level, "m%04lx [shape=record "
|
||||
"label=\"kill\\n%04lx|<h>head\\n%04lx\" xlabel=\"%04lx\"]\n",
|
||||
PTR2ADDR(l), PTR2ADDR(l->kill_func), PTR2ADDR(l->head), PTR2ADDR(l));
|
||||
}
|
||||
|
||||
// Print edges from the list head to the head cell.
|
||||
static void print_head_edges(int indent_level, const dlist *l)
|
||||
{
|
||||
print_edge(indent_level, l, l->head, "h", "head", NULL);
|
||||
}
|
||||
|
||||
// Print a node corresponding to the cell at position p.
|
||||
static void print_elem_node(int indent_level, const dlist_pos p)
|
||||
{
|
||||
iprintf(indent_level, "m%04lx [shape=record "
|
||||
"label=\"<v>val\\n%04lx|<n>next\\n%04lx\" xlabel=\"%04lx\"]\n",
|
||||
PTR2ADDR(p), PTR2ADDR(p->val), PTR2ADDR(p->next), PTR2ADDR(p));
|
||||
}
|
||||
|
||||
// Print edges from the cell at position p to the next and previous
|
||||
// cells and the value. The value pointer is red, the others are
|
||||
// black. If the list owns the memory, the edge is solid, otherwise
|
||||
// dashed.
|
||||
static void print_elem_edges(int indent_level, const dlist *l, const dlist_pos p)
|
||||
{
|
||||
print_edge(indent_level, p, p->next, "n", "next", NULL);
|
||||
|
||||
// Ignore val ptr for head and tail nodes.
|
||||
if (p == l->head) {
|
||||
return;
|
||||
}
|
||||
if (l->kill_func) {
|
||||
print_edge(indent_level, p, p->val, "v", "val", "color=red");
|
||||
} else {
|
||||
print_edge(indent_level, p, p->val, "v", "val", "color=red style=dashed");
|
||||
}
|
||||
}
|
||||
|
||||
// Print the node for the memory block at p using the user-supplied
|
||||
// print_func to print the label.
|
||||
static void print_value_node(int indent_level, const void *p, inspect_callback print_func)
|
||||
{
|
||||
iprintf(indent_level, "m%04lx [label=\"", PTR2ADDR(p));
|
||||
if (print_func != NULL) {
|
||||
print_func(p);
|
||||
}
|
||||
printf("\" xlabel=\"%04lx\"]\n", PTR2ADDR(p));
|
||||
}
|
||||
|
||||
// Create an escaped version of the input string. The most common
|
||||
// control characters - newline, horizontal tab, backslash, and double
|
||||
// quote - are replaced by their escape sequence. The returned pointer
|
||||
// must be deallocated by the caller.
|
||||
static char *escape_chars(const char *s)
|
||||
{
|
||||
int i, j;
|
||||
int escaped = 0; // The number of chars that must be escaped.
|
||||
|
||||
// Count how many chars need to be escaped, i.e. how much longer
|
||||
// the output string will be.
|
||||
for (i = escaped = 0; s[i] != '\0'; i++) {
|
||||
if (s[i] == '\n' || s[i] == '\t' || s[i] == '\\' || s[i] == '\"') {
|
||||
escaped++;
|
||||
}
|
||||
}
|
||||
// Allocate space for the escaped string. The variable i holds the input
|
||||
// length, escaped how much the string will grow.
|
||||
char *t = malloc(i + escaped + 1);
|
||||
|
||||
// Copy-and-escape loop
|
||||
for (i = j = 0; s[i] != '\0'; i++) {
|
||||
// Convert each control character by its escape sequence.
|
||||
// Non-control characters are copied as-is.
|
||||
switch (s[i]) {
|
||||
case '\n': t[i+j] = '\\'; t[i+j+1] = 'n'; j++; break;
|
||||
case '\t': t[i+j] = '\\'; t[i+j+1] = 't'; j++; break;
|
||||
case '\\': t[i+j] = '\\'; t[i+j+1] = '\\'; j++; break;
|
||||
case '\"': t[i+j] = '\\'; t[i+j+1] = '\"'; j++; break;
|
||||
default: t[i+j] = s[i]; break;
|
||||
}
|
||||
}
|
||||
// Terminal the output string
|
||||
t[i+j] = '\0';
|
||||
return t;
|
||||
}
|
||||
|
||||
/**
|
||||
* dlist_print_internal() - Print the lists internal structure in dot format.
|
||||
* @l: List to inspect.
|
||||
* @print_func: Function called for each element value.
|
||||
* @desc: String with a description/state of the list, or NULL for no description.
|
||||
* @indent_level: Indentation level, 0 for outermost
|
||||
*
|
||||
* Iterates over the list and outputs dot code that shows the internal
|
||||
* structure of the list. The dot code can be visualized by
|
||||
* Graphviz.
|
||||
*
|
||||
* On linux system, the output can be parsed by the dot program, e.g.
|
||||
*
|
||||
* <list_program> | dot -Tsvg > /tmp/dot.svg; firefox /tmp/dot.svg
|
||||
*
|
||||
* where <list_program> is the name of the executable
|
||||
*
|
||||
* The output may also be possible to visualize online on
|
||||
* https://dreampuf.github.io/GraphvizOnline/ or google "graphviz
|
||||
* online".
|
||||
*
|
||||
* For documention of the dot language, see graphviz.org.
|
||||
*
|
||||
* Returns: Nothing.
|
||||
*/
|
||||
void dlist_print_internal(const dlist *l, inspect_callback print_func, const char *desc,
|
||||
int indent_level)
|
||||
{
|
||||
static int graph_number = 0;
|
||||
graph_number++;
|
||||
int il = indent_level;
|
||||
|
||||
if (indent_level == 0) {
|
||||
// If this is the outermost datatype, start a graph...
|
||||
printf("digraph DLIST_%d {\n", graph_number);
|
||||
|
||||
// Specify default shape and fontname
|
||||
il++;
|
||||
iprintf(il, "node [shape=rectangle fontname=\"Courier New\"]\n");
|
||||
iprintf(il, "ranksep=0.01\n");
|
||||
iprintf(il, "subgraph cluster_nullspace {\n");
|
||||
iprintf(il+1, "NULL\n");
|
||||
iprintf(il, "}\n");
|
||||
}
|
||||
|
||||
if (desc != NULL) {
|
||||
// Escape the string before printout
|
||||
char *escaped = escape_chars(desc);
|
||||
// Use different names on inner description nodes
|
||||
if (indent_level == 0) {
|
||||
iprintf(il, "description [label=\"%s\"]\n", escaped);
|
||||
} else {
|
||||
iprintf(il, "cluster_dlist_%d_description [label=\"%s\"]\n", graph_number, escaped);
|
||||
}
|
||||
// Return the memory used by the escaped string
|
||||
free(escaped);
|
||||
}
|
||||
|
||||
if (indent_level == 0) {
|
||||
// Use a single "pointer" edge as a starting point for the
|
||||
// outermost datatype
|
||||
iprintf(il, "l [label=\"%04lx\" xlabel=\"l\"]\n", PTR2ADDR(l));
|
||||
iprintf(il, "l -> m%04lx\n", PTR2ADDR(l));
|
||||
}
|
||||
|
||||
// Print the subgraph to surround the DList content
|
||||
iprintf(il, "subgraph cluster_dlist_%d { label=\"DList\"\n", graph_number);
|
||||
il++;
|
||||
|
||||
// Output the head node
|
||||
print_head_node(il, l);
|
||||
|
||||
// Output the element nodes
|
||||
dlist_pos p = l->head;
|
||||
while (p != NULL) {
|
||||
print_elem_node(il, p);
|
||||
p = p->next;
|
||||
}
|
||||
|
||||
// Close the subgraph
|
||||
il--;
|
||||
iprintf(il, "}\n");
|
||||
|
||||
if (indent_level == 0) {
|
||||
// Put the user nodes in userspace
|
||||
iprintf(il, "subgraph cluster_userspace { label=\"User space\"\n");
|
||||
il++;
|
||||
}
|
||||
|
||||
// Output the value nodes
|
||||
p = l->head;
|
||||
while (p != NULL) {
|
||||
if (p->val) {
|
||||
print_value_node(il, p->val, print_func);
|
||||
}
|
||||
p = p->next;
|
||||
}
|
||||
|
||||
if (indent_level == 0) {
|
||||
// Close userspace
|
||||
il--;
|
||||
iprintf(il, "}\n");
|
||||
}
|
||||
|
||||
// Output the edges from the head
|
||||
print_head_edges(il, l);
|
||||
|
||||
// Output the edges from each element
|
||||
p = l->head;
|
||||
while (p != NULL) {
|
||||
print_elem_edges(il, l, p);
|
||||
p = p->next;
|
||||
}
|
||||
|
||||
if (indent_level == 0) {
|
||||
// Termination of graph
|
||||
printf("}\n");
|
||||
}
|
||||
}
|
||||
79
OU3/src/dlist/dlist_mwe1.c
Normal file
79
OU3/src/dlist/dlist_mwe1.c
Normal file
@@ -0,0 +1,79 @@
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <dlist.h>
|
||||
|
||||
/*
|
||||
* Minimum working example 1 for dlist.c.
|
||||
*
|
||||
* Authors: Niclas Borlin (niclas@cs.umu.se)
|
||||
* Lars Karlsson (larsk@cs.umu.se)
|
||||
*
|
||||
* Version information:
|
||||
* v1.0 2018-01-28: First public version.
|
||||
* v1.1 2018-04-03: Split into versions with/without memhandler.
|
||||
* v1.2 2023-01-14: Added printouts at start/end of main.
|
||||
* v1.3 2024-03-14: Added explicit copy/kill string functions.
|
||||
*/
|
||||
|
||||
#define VERSION "v1.3"
|
||||
#define VERSION_DATE "2024-03-14"
|
||||
|
||||
// Make a dynamic copy of the input string.
|
||||
char *copy_string(char *str)
|
||||
{
|
||||
char *copy = calloc(strlen(str) + 1, sizeof(char));
|
||||
strcpy(copy, str);
|
||||
return copy;
|
||||
}
|
||||
|
||||
// String print function.
|
||||
void print_string(const void *value)
|
||||
{
|
||||
const char *s=value;
|
||||
printf("\"%s\"", s);
|
||||
}
|
||||
|
||||
// Return the memory occupied by the string
|
||||
void kill_string(void *v)
|
||||
{
|
||||
char *p = v; // Convert to a char ptr, useful in a debugger
|
||||
free(p);
|
||||
}
|
||||
|
||||
// Test program.
|
||||
int main(void)
|
||||
{
|
||||
printf("%s, %s %s: Create list of strings without memfreehandler.\n",
|
||||
__FILE__, VERSION, VERSION_DATE);
|
||||
printf("Code base version %s (%s).\n\n", CODE_BASE_VERSION, CODE_BASE_RELEASE_DATE);
|
||||
|
||||
// Names to insert in the list.
|
||||
char *names[] = {"Alfons", "Bengt", "Cia", "David", "Florian", "Gunnar"};
|
||||
|
||||
// Create the list
|
||||
dlist *l = dlist_empty(NULL);
|
||||
dlist_pos p = dlist_first(l);
|
||||
for (int i = 0; i < sizeof(names) / sizeof(names[0]); i++) {
|
||||
// Insert dynamic copies of strings at last position.
|
||||
p = dlist_insert(l, copy_string(names[i]), p);
|
||||
p = dlist_next(l, p);
|
||||
}
|
||||
// Print the list.
|
||||
printf("DList after inserting 6 strings:\n");
|
||||
dlist_print(l, print_string);
|
||||
|
||||
// Traverse the list and deallocate each value.
|
||||
p = dlist_first(l);
|
||||
while (!dlist_is_end(l,p)) {
|
||||
char *v=dlist_inspect(l,p);
|
||||
kill_string(v);
|
||||
p=dlist_remove(l,p);
|
||||
}
|
||||
// Destroy the list.
|
||||
dlist_kill(l);
|
||||
|
||||
printf("\nNormal exit.\n\n");
|
||||
return 0;
|
||||
}
|
||||
142
OU3/src/dlist/dlist_mwe1i.c
Normal file
142
OU3/src/dlist/dlist_mwe1i.c
Normal file
@@ -0,0 +1,142 @@
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <dlist.h>
|
||||
|
||||
/*
|
||||
* Minimum working example for dlist.c that shows how the internal
|
||||
* structure of a list can be visualized. In this version, the list
|
||||
* "borrows" the payload memory, i.e., the user of the list is
|
||||
* responsible for deallocating the payload memory. See dlist_mwe2i.c
|
||||
* for a version where the list takes over the responsibility.
|
||||
*
|
||||
* Authors: Niclas Borlin (niclas@cs.umu.se)
|
||||
*
|
||||
* Version information:
|
||||
* v1.0 2024-03-14: First public version.
|
||||
* v1.1 2024-03-15: Moved printout of dot instructions to separate function.
|
||||
*/
|
||||
|
||||
#define VERSION "v1.1"
|
||||
#define VERSION_DATE "2024-03-15"
|
||||
|
||||
// Make a dynamic copy of the input string.
|
||||
char *copy_string(char *str)
|
||||
{
|
||||
char *copy = calloc(strlen(str) + 1, sizeof(char));
|
||||
strcpy(copy, str);
|
||||
return copy;
|
||||
}
|
||||
|
||||
// Return the memory occupied by the string
|
||||
void kill_string(void *v)
|
||||
{
|
||||
char *p = v; // Convert to a char ptr, useful in a debugger
|
||||
free(p);
|
||||
}
|
||||
|
||||
// String print function.
|
||||
void print_string(const void *value)
|
||||
{
|
||||
const char *s=value;
|
||||
printf("\"%s\"", s);
|
||||
}
|
||||
|
||||
void print_string_internal(const void *value)
|
||||
{
|
||||
const char *s=value;
|
||||
// We must double-escape the double quotes since the output will
|
||||
// be parsed a second time by graphviz.
|
||||
printf("\\\"%s\\\"", s);
|
||||
}
|
||||
|
||||
// Print cut lines before and after a call list_print_internal.
|
||||
void print_internal_with_cut_lines(const dlist *l, const char *desc)
|
||||
{
|
||||
// Internal counter that will be remembered between calls.
|
||||
|
||||
// Used to generate sequentially numbered -- CUT HERE -- marker lines
|
||||
// to enable automatic parsing of the output.
|
||||
static int cut = 1;
|
||||
|
||||
// Print starting marker line.
|
||||
printf("\n--- START CUT HERE %d ---\n", cut);
|
||||
|
||||
// Call the internal print function to get the actual dot code.
|
||||
dlist_print_internal(l, print_string_internal, desc, 0);
|
||||
|
||||
// Print ending marker line
|
||||
printf("--- END CUT HERE %d ---\n\n", cut);
|
||||
|
||||
// Increment the cut number. Will be remembered next time the
|
||||
// function is called since cut is a static variable.
|
||||
cut++;
|
||||
}
|
||||
|
||||
// Print a message with intructions how to use the dot output. prog is
|
||||
// the name of the executable.
|
||||
void print_dot_usage(char *prog)
|
||||
{
|
||||
printf("\nGenerate dot code to visualize internal structure with GraphViz. ");
|
||||
printf("Use\n\n%s ", prog);
|
||||
printf("| sed -n '/START CUT HERE X/,/END CUT HERE X/{//!p}' | dot -Tsvg > /tmp/dot.svg\n\n");
|
||||
printf("to generate an svg file of cut X (replace X by the requested cut number).\n");
|
||||
printf("The generated file can then be visualized with\n\n");
|
||||
printf("firefox /tmp/dot.svg\n\n");
|
||||
printf("Use -Tpng to generate a .png file instead. "
|
||||
"See graphviz.org and %s for documentation.\n", __FILE__);
|
||||
|
||||
printf("\n--- Start of normal output ---\n\n");
|
||||
}
|
||||
|
||||
// Test program.
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
printf("%s, %s %s: Create list of strings without memfreehandler.\n",
|
||||
__FILE__, VERSION, VERSION_DATE);
|
||||
printf("Code base version %s (%s).\n\n", CODE_BASE_VERSION, CODE_BASE_RELEASE_DATE);
|
||||
|
||||
print_dot_usage(argv[0]);
|
||||
|
||||
// Names to insert in the list.
|
||||
char *names[] = {"Alfons", "Bengt", "Cia", "David", "Florian", "Gunnar"};
|
||||
|
||||
// Create the list
|
||||
dlist *l = dlist_empty(NULL);
|
||||
|
||||
printf("Empty list from the outside:\n");
|
||||
dlist_print(l, print_string);
|
||||
print_internal_with_cut_lines(l, "Empty list showing the internal structure");
|
||||
|
||||
dlist_pos p = dlist_first(l);
|
||||
for (int i = 0; i < sizeof(names) / sizeof(names[0]); i++) {
|
||||
// Insert dynamic copies of strings at last position.
|
||||
p = dlist_insert(l, copy_string(names[i]), p);
|
||||
p = dlist_next(l, p);
|
||||
}
|
||||
// Print the list.
|
||||
printf("DList from the outside after inserting 6 strings:\n");
|
||||
dlist_print(l, print_string);
|
||||
const char *long_desc = __FILE__
|
||||
": Internal structure of the DList after inserting 6 strings.\n"
|
||||
"Red lines are used for the list payload.\n\n"
|
||||
"The dashed red lines indicate that the payload memory is\n"
|
||||
"BORROWED by the list, i.e., the payload\n"
|
||||
"memory will NOT be deallocated by the list.\n\n"
|
||||
"See dlist_mwe2i for a list example\nthat owns the payload memory.";
|
||||
print_internal_with_cut_lines(l, long_desc);
|
||||
|
||||
// Traverse the list and deallocate each value.
|
||||
p = dlist_first(l);
|
||||
while (!dlist_is_end(l,p)) {
|
||||
char *v=dlist_inspect(l,p);
|
||||
kill_string(v);
|
||||
p=dlist_remove(l,p);
|
||||
}
|
||||
// Destroy the list.
|
||||
dlist_kill(l);
|
||||
|
||||
printf("\nNormal exit.\n\n");
|
||||
return 0;
|
||||
}
|
||||
71
OU3/src/dlist/dlist_mwe2.c
Normal file
71
OU3/src/dlist/dlist_mwe2.c
Normal file
@@ -0,0 +1,71 @@
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <dlist.h>
|
||||
|
||||
/*
|
||||
* Minimum working example 2 for dlist.c.
|
||||
*
|
||||
* Authors: Niclas Borlin (niclas@cs.umu.se)
|
||||
* Lars Karlsson (larsk@cs.umu.se)
|
||||
*
|
||||
* Version information:
|
||||
* v1.0 2018-01-28: First public version.
|
||||
* v1.1 2018-04-03: Split into versions with/without memhandler.
|
||||
* v1.2 2023-01-14: Added printouts at start/end of main.
|
||||
* v1.3 2024-03-14: Added explicit copy/kill string functions.
|
||||
*/
|
||||
|
||||
#define VERSION "v1.3"
|
||||
#define VERSION_DATE "2024-03-14"
|
||||
|
||||
// Make a dynamic copy of the input string.
|
||||
char *copy_string(char *str)
|
||||
{
|
||||
char *copy = calloc(strlen(str) + 1, sizeof(char));
|
||||
strcpy(copy, str);
|
||||
return copy;
|
||||
}
|
||||
|
||||
// String print function.
|
||||
void print_string(const void *value)
|
||||
{
|
||||
const char *s=value;
|
||||
printf("\"%s\"", s);
|
||||
}
|
||||
|
||||
// Return the memory occupied by the string
|
||||
void kill_string(void *v)
|
||||
{
|
||||
char *p = v; // Convert to a char ptr, useful in a debugger
|
||||
free(p);
|
||||
}
|
||||
|
||||
// Test program.
|
||||
int main(void)
|
||||
{
|
||||
printf("%s, %s %s: Create list of strings with standard memfreehandler.\n",
|
||||
__FILE__, VERSION, VERSION_DATE);
|
||||
printf("Code base version %s (%s).\n\n", CODE_BASE_VERSION, CODE_BASE_RELEASE_DATE);
|
||||
|
||||
// Names to insert in the list.
|
||||
char *names[] = {"Alfons", "Bengt", "Cia", "David", "Florian", "Gunnar"};
|
||||
|
||||
// Create the list
|
||||
dlist *l = dlist_empty(kill_string);
|
||||
dlist_pos p = dlist_first(l);
|
||||
for (int i = 0; i < sizeof(names) / sizeof(names[0]); i++) {
|
||||
// Insert dynamic copies of strings at last position.
|
||||
p = dlist_insert(l, copy_string(names[i]), p);
|
||||
p = dlist_next(l, p);
|
||||
}
|
||||
// Print the list.
|
||||
printf("DList after inserting 6 strings:\n");
|
||||
dlist_print(l, print_string);
|
||||
// Destroy the list.
|
||||
dlist_kill(l);
|
||||
|
||||
printf("\nNormal exit.\n\n");
|
||||
return 0;
|
||||
}
|
||||
136
OU3/src/dlist/dlist_mwe2i.c
Normal file
136
OU3/src/dlist/dlist_mwe2i.c
Normal file
@@ -0,0 +1,136 @@
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <dlist.h>
|
||||
|
||||
/*
|
||||
* Minimum working example for dlist.c that shows how the internal
|
||||
* structure of a list can be visualized. In this version, the list
|
||||
* "owns" the payload memory, i.e., the list takes over the
|
||||
* responsibility to deallocate the payload memory when the
|
||||
* corresponding elements are removed. See list_mwe1i.c for a version
|
||||
* where the list does not take over the responsibility.
|
||||
*
|
||||
* Authors: Niclas Borlin (niclas@cs.umu.se)
|
||||
*
|
||||
* Version information:
|
||||
* v1.0 2024-03-14: First public version.
|
||||
* v1.1 2024-03-15: Moved printout of dot instructions to separate function.
|
||||
*/
|
||||
|
||||
#define VERSION "v1.1"
|
||||
#define VERSION_DATE "2024-03-15"
|
||||
|
||||
// Make a dynamic copy of the input string.
|
||||
char *copy_string(char *str)
|
||||
{
|
||||
char *copy = calloc(strlen(str) + 1, sizeof(char));
|
||||
strcpy(copy, str);
|
||||
return copy;
|
||||
}
|
||||
|
||||
// Return the memory occupied by the string
|
||||
void kill_string(void *v)
|
||||
{
|
||||
char *p = v; // Convert to a char ptr, useful in a debugger
|
||||
free(p);
|
||||
}
|
||||
|
||||
// String print function.
|
||||
void print_string(const void *value)
|
||||
{
|
||||
const char *s=value;
|
||||
printf("\"%s\"", s);
|
||||
}
|
||||
|
||||
void print_string_internal(const void *value)
|
||||
{
|
||||
const char *s=value;
|
||||
// We must double-escape the double quotes since the output will
|
||||
// be parsed a second time by graphviz.
|
||||
printf("\\\"%s\\\"", s);
|
||||
}
|
||||
|
||||
// Print cut lines before and after a call list_print_internal.
|
||||
void print_internal_with_cut_lines(const dlist *l, const char *desc)
|
||||
{
|
||||
// Internal counter that will be remembered between calls.
|
||||
|
||||
// Used to generate sequentially numbered -- CUT HERE -- marker lines
|
||||
// to enable automatic parsing of the output.
|
||||
static int cut = 1;
|
||||
|
||||
// Print starting marker line.
|
||||
printf("\n--- START CUT HERE %d ---\n", cut);
|
||||
|
||||
// Call the internal print function to get the actual dot code.
|
||||
dlist_print_internal(l, print_string_internal, desc, 0);
|
||||
|
||||
// Print ending marker line
|
||||
printf("--- END CUT HERE %d ---\n\n", cut);
|
||||
|
||||
// Increment the cut number. Will be remembered next time the
|
||||
// function is called since cut is a static variable.
|
||||
cut++;
|
||||
}
|
||||
|
||||
// Print a message with intructions how to use the dot output. prog is
|
||||
// the name of the executable.
|
||||
void print_dot_usage(char *prog)
|
||||
{
|
||||
printf("\nGenerate dot code to visualize internal structure with GraphViz. ");
|
||||
printf("Use\n\n%s ", prog);
|
||||
printf("| sed -n '/START CUT HERE X/,/END CUT HERE X/{//!p}' | dot -Tsvg > /tmp/dot.svg\n\n");
|
||||
printf("to generate an svg file of cut X (replace X by the requested cut number).\n");
|
||||
printf("The generated file can then be visualized with\n\n");
|
||||
printf("firefox /tmp/dot.svg\n\n");
|
||||
printf("Use -Tpng to generate a .png file instead. "
|
||||
"See graphviz.org and %s for documentation.\n", __FILE__);
|
||||
|
||||
printf("\n--- Start of normal output ---\n\n");
|
||||
}
|
||||
|
||||
// Test program.
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
printf("%s, %s %s: Create list of strings without memfreehandler.\n",
|
||||
__FILE__, VERSION, VERSION_DATE);
|
||||
printf("Code base version %s (%s).\n\n", CODE_BASE_VERSION, CODE_BASE_RELEASE_DATE);
|
||||
|
||||
print_dot_usage(argv[0]);
|
||||
|
||||
// Names to insert in the list.
|
||||
char *names[] = {"Alfons", "Bengt", "Cia", "David", "Florian", "Gunnar"};
|
||||
|
||||
// Create the list
|
||||
dlist *l = dlist_empty(kill_string);
|
||||
|
||||
printf("Empty list from the outside:\n");
|
||||
dlist_print(l, print_string);
|
||||
print_internal_with_cut_lines(l, "Empty list showing the internal structure");
|
||||
|
||||
dlist_pos p = dlist_first(l);
|
||||
for (int i = 0; i < sizeof(names) / sizeof(names[0]); i++) {
|
||||
// Insert dynamic copies of strings at last position.
|
||||
p = dlist_insert(l, copy_string(names[i]), p);
|
||||
p = dlist_next(l, p);
|
||||
}
|
||||
// Print the list.
|
||||
printf("DList from the outside after inserting 6 strings:\n");
|
||||
dlist_print(l, print_string);
|
||||
const char *long_desc = __FILE__
|
||||
": Internal structure of the DList after inserting 6 strings.\n"
|
||||
"Red lines are used for the list payload.\n\n"
|
||||
"The solid red lines indicate that the payload memory is\n"
|
||||
"OWNED -- not borrowed -- by the list, i.e., the payload\n"
|
||||
"memory WILL be deallocated by the list.\n\n"
|
||||
"See dlist_mwe1i for a list example\nthat borrows the payload memory.";
|
||||
print_internal_with_cut_lines(l, long_desc);
|
||||
|
||||
// Destroy the list.
|
||||
dlist_kill(l);
|
||||
|
||||
printf("\nNormal exit.\n\n");
|
||||
return 0;
|
||||
}
|
||||
28
OU3/src/int_array_1d/Makefile
Normal file
28
OU3/src/int_array_1d/Makefile
Normal file
@@ -0,0 +1,28 @@
|
||||
MWE = int_array_1d_mwe1 int_array_1d_mwe1i
|
||||
|
||||
SRC = int_array_1d.c
|
||||
OBJ = $(SRC:.c=.o)
|
||||
|
||||
CC = gcc
|
||||
CFLAGS = -std=c99 -Wall -I../../include -g
|
||||
|
||||
all: mwe
|
||||
|
||||
# Minimum working examples.
|
||||
mwe: $(MWE)
|
||||
|
||||
# Object file for library
|
||||
obj: $(OBJ)
|
||||
|
||||
# Clean up
|
||||
clean:
|
||||
-rm -f $(MWE) $(OBJ)
|
||||
|
||||
int_array_1d_mwe1: int_array_1d_mwe1.c int_array_1d.c
|
||||
gcc -o $@ $(CFLAGS) $^
|
||||
|
||||
int_array_1d_mwe1i: int_array_1d_mwe1i.c int_array_1d.c
|
||||
gcc -o $@ $(CFLAGS) $^
|
||||
|
||||
memtest: int_array_1d_mwe1
|
||||
valgrind --leak-check=full --show-reachable=yes ./$<
|
||||
424
OU3/src/int_array_1d/int_array_1d.c
Normal file
424
OU3/src/int_array_1d/int_array_1d.c
Normal file
@@ -0,0 +1,424 @@
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdarg.h>
|
||||
|
||||
#include <int_array_1d.h>
|
||||
|
||||
/*
|
||||
* Implementation of a generic 1D array for the "Datastructures and
|
||||
* algorithms" courses at the Department of Computing Science, Umea
|
||||
* University.
|
||||
*
|
||||
* Authors: Niclas Borlin (niclas@cs.umu.se)
|
||||
* Adam Dahlgren Lindstrom (dali@cs.umu.se)
|
||||
* Lars Karlsson (larsk@cs.umu.se)
|
||||
*
|
||||
* Based on earlier code by: Johan Eliasson (johane@cs.umu.se).
|
||||
*
|
||||
* Version information:
|
||||
* v1.0 2018-01-28: First public version.
|
||||
* v1.1 2024-05-10: Added print_internal.
|
||||
*/
|
||||
|
||||
// =========== INTERNAL DATA TYPES ============
|
||||
|
||||
struct int_array_1d {
|
||||
int low; // Low index limit.
|
||||
int high; // High index limit.
|
||||
int array_size; // Number of array elements.
|
||||
int *values; // Pointer to where the actual values are stored.
|
||||
};
|
||||
|
||||
// =========== INTERNAL FUNCTION IMPLEMENTATIONS ============
|
||||
|
||||
/**
|
||||
* int_array_1d_create() - Create an array without values.
|
||||
* @lo: low index limit.
|
||||
* @hi: high index limit.
|
||||
*
|
||||
* The index limits are inclusive, i.e. all indices i such that low <=
|
||||
* i <= high are defined.
|
||||
*
|
||||
* Returns: A pointer to the new array, or NULL if not enough memory
|
||||
* was available.
|
||||
*/
|
||||
int_array_1d *int_array_1d_create(int lo, int hi)
|
||||
{
|
||||
// Allocate array structure.
|
||||
int_array_1d *a = calloc(1, sizeof(*a));
|
||||
// Store index limit.
|
||||
a->low = lo;
|
||||
a->high = hi;
|
||||
|
||||
// Number of elements.
|
||||
a->array_size = hi - lo + 1;
|
||||
|
||||
a->values=calloc(a->array_size, sizeof(a->values[0]));
|
||||
|
||||
// Check whether the allocation succeeded.
|
||||
if (a->values == NULL) {
|
||||
free(a);
|
||||
a = NULL;
|
||||
}
|
||||
return a;
|
||||
}
|
||||
|
||||
/**
|
||||
* int_array_1d_low() - Return the low index limit for the array.
|
||||
* @a: array to inspect.
|
||||
*
|
||||
* Returns: The low index limit.
|
||||
*/
|
||||
int int_array_1d_low(const int_array_1d *a)
|
||||
{
|
||||
return a->low;
|
||||
}
|
||||
|
||||
/**
|
||||
* int_array_1d_high() - Return the high index limit for the array.
|
||||
* @a: array to inspect.
|
||||
*
|
||||
* Returns: The high index limit.
|
||||
*/
|
||||
int int_array_1d_high(const int_array_1d *a)
|
||||
{
|
||||
return a->high;
|
||||
}
|
||||
|
||||
/**
|
||||
* int_array_1d_inspect_value() - Inspect a value at a given array position.
|
||||
* @a: array to inspect.
|
||||
* @i: index of position to inspect.
|
||||
*
|
||||
* Returns: The element value at the specified position. The result is
|
||||
* undefined if no value are stored at that position.
|
||||
*/
|
||||
int int_array_1d_inspect_value(const int_array_1d *a, int i)
|
||||
{
|
||||
int offset=i - int_array_1d_low(a);
|
||||
// Return the value.
|
||||
return a->values[offset];
|
||||
}
|
||||
|
||||
/**
|
||||
* int_array_1d_has_value() - Check if a value is set at a given array position.
|
||||
* @a: array to inspect.
|
||||
* @i: index of position to inspect.
|
||||
*
|
||||
* Returns: True if a value is set at the specified position, otherwise false.
|
||||
*/
|
||||
bool int_array_1d_has_value(const int_array_1d *a, int i)
|
||||
{
|
||||
int offset=i - int_array_1d_low(a);
|
||||
// Return true if the value is not 0.
|
||||
return a->values[offset] != 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* int_array_1d_set_value() - Set a value at a given array position.
|
||||
* @a: array to modify.
|
||||
* @v: value to set element to, or 0 to clear value.
|
||||
* @i: index of position to modify.
|
||||
*
|
||||
* Returns: Nothing.
|
||||
*/
|
||||
void int_array_1d_set_value(int_array_1d *a, int v, int i)
|
||||
{
|
||||
int offset=i - int_array_1d_low(a);
|
||||
// Set value.
|
||||
a->values[offset] = v;
|
||||
}
|
||||
|
||||
/**
|
||||
* int_array_1d_kill() - Return memory allocated by array.
|
||||
* @a: array to kill.
|
||||
*
|
||||
* Returns: Nothing.
|
||||
*/
|
||||
void int_array_1d_kill(int_array_1d *a)
|
||||
{
|
||||
// Free actual storage.
|
||||
free(a->values);
|
||||
// Free array structure.
|
||||
free(a);
|
||||
}
|
||||
|
||||
/**
|
||||
* int_array_1d_print() - Iterate over the array element and print their values.
|
||||
* @a: Array to inspect.
|
||||
* @print_func: Function called for each non-NULL element.
|
||||
*
|
||||
* Iterates over each position in the array. Calls print_func for each
|
||||
* non-NULL value.
|
||||
*
|
||||
* Returns: Nothing.
|
||||
*/
|
||||
void int_array_1d_print(const int_array_1d *a)
|
||||
{
|
||||
printf("[ ");
|
||||
for (int i=int_array_1d_low(a); i<=int_array_1d_high(a); i++) {
|
||||
if (int_array_1d_has_value(a, i)) {
|
||||
printf("[%d]", int_array_1d_inspect_value(a, i));
|
||||
} else {
|
||||
printf(" []");
|
||||
}
|
||||
if (i<int_array_1d_high(a)) {
|
||||
printf(", ");
|
||||
}
|
||||
}
|
||||
printf(" ]\n");
|
||||
}
|
||||
|
||||
// ===========INTERNAL FUNCTIONS USED BY int_array_1d_print_internal ============
|
||||
|
||||
// The functions below output code in the dot language, used by
|
||||
// GraphViz. For documention of the dot language, see graphviz.org.
|
||||
|
||||
/**
|
||||
* indent() - Output indentation string.
|
||||
* @n: Indentation level.
|
||||
*
|
||||
* Print n tab characters.
|
||||
*
|
||||
* Returns: Nothing.
|
||||
*/
|
||||
static void indent(int n)
|
||||
{
|
||||
for (int i=0; i<n; i++) {
|
||||
printf("\t");
|
||||
}
|
||||
}
|
||||
/**
|
||||
* iprintf(...) - Indent and print.
|
||||
* @n: Indentation level
|
||||
* @...: printf arguments
|
||||
*
|
||||
* Print n tab characters and calls printf.
|
||||
*
|
||||
* Returns: Nothing.
|
||||
*/
|
||||
static void iprintf(int n, const char *fmt, ...)
|
||||
{
|
||||
// Indent...
|
||||
indent(n);
|
||||
// ...and call printf
|
||||
va_list args;
|
||||
va_start(args, fmt);
|
||||
vprintf(fmt, args);
|
||||
va_end(args);
|
||||
}
|
||||
|
||||
/**
|
||||
* print_edge() - Print a edge between two addresses.
|
||||
* @from: The address of the start of the edge. Should be non-NULL.
|
||||
* @to: The address of the destination for the edge, including NULL.
|
||||
* @port: The name of the port on the source node, or NULL.
|
||||
* @label: The label for the edge, or NULL.
|
||||
* @options: A string with other edge options, or NULL.
|
||||
*
|
||||
* Print an edge from port PORT on node FROM to TO with label
|
||||
* LABEL. If to is NULL, the destination is the NULL node, otherwise a
|
||||
* memory node. If the port is NULL, the edge starts at the node, not
|
||||
* a specific port on it. If label is NULL, no label is used. The
|
||||
* options string, if non-NULL, is printed before the label.
|
||||
*
|
||||
* Returns: Nothing.
|
||||
*/
|
||||
static void print_edge(int indent_level, const void *from, const void *to, const char *port,
|
||||
const char *label, const char *options)
|
||||
{
|
||||
indent(indent_level);
|
||||
if (port) {
|
||||
printf("m%04lx:%s -> ", PTR2ADDR(from), port);
|
||||
} else {
|
||||
printf("m%04lx -> ", PTR2ADDR(from));
|
||||
}
|
||||
if (to == NULL) {
|
||||
printf("NULL");
|
||||
} else {
|
||||
printf("m%04lx", PTR2ADDR(to));
|
||||
}
|
||||
printf(" [");
|
||||
if (options != NULL) {
|
||||
printf("%s", options);
|
||||
}
|
||||
if (label != NULL) {
|
||||
printf(" label=\"%s\"",label);
|
||||
}
|
||||
printf("]\n");
|
||||
}
|
||||
|
||||
/**
|
||||
* print_head_node() - Print a node corresponding to the array head struct.
|
||||
* @indent_level: Indentation level.
|
||||
* @a: Array to inspect.
|
||||
*
|
||||
* Returns: Nothing.
|
||||
*/
|
||||
static void print_head_node(int indent_level, const int_array_1d *a)
|
||||
{
|
||||
iprintf(indent_level, "m%04lx [shape=record label=\"low\\n%d|high\\n%d"
|
||||
"|array_size\\n%d|<v>values\\n%04lx\"]\n", PTR2ADDR(a),
|
||||
a->low, a->high, a->array_size, PTR2ADDR(a->values));
|
||||
}
|
||||
|
||||
/**
|
||||
* print_values() - Print a node containing all positions in the array.
|
||||
* @indent_level: Indentation level.
|
||||
* @a: Array to inspect.
|
||||
* @max_values_to_print: Maximum number of values to output.
|
||||
*
|
||||
* Will print dot code to display each value in the array, up to
|
||||
* max_values_to_print. If there are more values to print, adds an
|
||||
* ellipsis (...) at the end.
|
||||
*
|
||||
* Returns: Nothing.
|
||||
*/
|
||||
void print_values(int indent_level, const int_array_1d *a, int max_values_to_print)
|
||||
{
|
||||
int values_to_print = a->array_size;
|
||||
int truncated = false;
|
||||
if (values_to_print > max_values_to_print) {
|
||||
values_to_print = max_values_to_print;
|
||||
truncated = true;
|
||||
}
|
||||
iprintf(indent_level, "m%04lx [shape=record label=\"", PTR2ADDR(a->values));
|
||||
for (int i=0; i < values_to_print; i++) {
|
||||
printf("%d\\n%02d\\n%d", i + a->low, i, a->values[i]);
|
||||
if (i < values_to_print - 1) {
|
||||
printf("|");
|
||||
}
|
||||
}
|
||||
if (truncated) {
|
||||
// Add ellipsis
|
||||
printf("|\\n...\\n");
|
||||
}
|
||||
printf("\"]\n");
|
||||
}
|
||||
|
||||
// Print edge from the array head to the values array.
|
||||
static void print_head_edge(int indent_level, const int_array_1d *a)
|
||||
{
|
||||
print_edge(indent_level, a, a->values, "v", "values", NULL);
|
||||
}
|
||||
|
||||
// Create an escaped version of the input string. The most common
|
||||
// control characters - newline, horizontal tab, backslash, and double
|
||||
// quote - are replaced by their escape sequence. The returned pointer
|
||||
// must be deallocated by the caller.
|
||||
static char *escape_chars(const char *s)
|
||||
{
|
||||
int i, j;
|
||||
int escaped = 0; // The number of chars that must be escaped.
|
||||
|
||||
// Count how many chars need to be escaped, i.e. how much longer
|
||||
// the output string will be.
|
||||
for (i = escaped = 0; s[i] != '\0'; i++) {
|
||||
if (s[i] == '\n' || s[i] == '\t' || s[i] == '\\' || s[i] == '\"') {
|
||||
escaped++;
|
||||
}
|
||||
}
|
||||
// Allocate space for the escaped string. The variable i holds the input
|
||||
// length, escaped how much the string will grow.
|
||||
char *t = malloc(i + escaped + 1);
|
||||
|
||||
// Copy-and-escape loop
|
||||
for (i = j = 0; s[i] != '\0'; i++) {
|
||||
// Convert each control character by its escape sequence.
|
||||
// Non-control characters are copied as-is.
|
||||
switch (s[i]) {
|
||||
case '\n': t[i+j] = '\\'; t[i+j+1] = 'n'; j++; break;
|
||||
case '\t': t[i+j] = '\\'; t[i+j+1] = 't'; j++; break;
|
||||
case '\\': t[i+j] = '\\'; t[i+j+1] = '\\'; j++; break;
|
||||
case '\"': t[i+j] = '\\'; t[i+j+1] = '\"'; j++; break;
|
||||
default: t[i+j] = s[i]; break;
|
||||
}
|
||||
}
|
||||
// Terminal the output string
|
||||
t[i+j] = '\0';
|
||||
return t;
|
||||
}
|
||||
|
||||
/**
|
||||
* int_array_1d_print_internal() - Print the arrays internal structure in dot format.
|
||||
* @a: Array to inspect.
|
||||
* @desc: String with a description/state of the array, or NULL for no description.
|
||||
* @indent_level: Indentation level, 0 for outermost
|
||||
*
|
||||
* Iterates over the array and outputs dot code that shows the internal
|
||||
* structure of the array. The dot code can be visualized by
|
||||
* Graphviz.
|
||||
*
|
||||
* On linux system, the output can be parsed by the dot program, e.g.
|
||||
*
|
||||
* <array_program> | dot -Tsvg > /tmp/dot.svg; firefox /tmp/dot.svg
|
||||
*
|
||||
* where <array_program> is the name of the executable
|
||||
*
|
||||
* The output may also be possible to visualize online on
|
||||
* https://dreampuf.github.io/GraphvizOnline/ or google "graphviz
|
||||
* online".
|
||||
*
|
||||
* For documention of the dot language, see graphviz.org.
|
||||
*
|
||||
* Returns: Nothing.
|
||||
*/
|
||||
void int_array_1d_print_internal(const int_array_1d *a, const char *desc, int indent_level)
|
||||
{
|
||||
static int graph_number = 0;
|
||||
graph_number++;
|
||||
int il = indent_level;
|
||||
|
||||
if (indent_level == 0) {
|
||||
// If this is the outermost datatype, start a graph...
|
||||
printf("digraph ARRAY_1D_%d {\n", graph_number);
|
||||
|
||||
// Specify default shape and fontname
|
||||
il++;
|
||||
iprintf(il, "node [shape=rectangle fontname=\"Courier New\"]\n");
|
||||
iprintf(il, "ranksep=0.01\n");
|
||||
}
|
||||
|
||||
if (desc != NULL) {
|
||||
// Escape the string before printout
|
||||
char *escaped = escape_chars(desc);
|
||||
// Use different names on inner description nodes
|
||||
if (indent_level == 0) {
|
||||
iprintf(il, "description [label=\"%s\"]\n", escaped);
|
||||
} else {
|
||||
iprintf(il, "cluster_int_array_1d_%d_description [label=\"%s\"]\n", graph_number,
|
||||
escaped);
|
||||
}
|
||||
// Return the memory used by the escaped string
|
||||
free(escaped);
|
||||
}
|
||||
|
||||
if (indent_level == 0) {
|
||||
// Use a single "pointer" edge as a starting point for the
|
||||
// outermost datatype
|
||||
iprintf(il, "a [label=\"%04lx\" xlabel=\"a\"]\n", PTR2ADDR(a));
|
||||
iprintf(il, "a -> m%04lx\n", PTR2ADDR(a));
|
||||
}
|
||||
|
||||
// Print the subgraph to surround the Array content
|
||||
iprintf(il, "subgraph cluster_int_array_1d_%d { label=\"Array_1d\"\n", graph_number);
|
||||
il++;
|
||||
|
||||
// Output the head node
|
||||
print_head_node(il, a);
|
||||
|
||||
// Output the values array. Limit output to 20 elements.
|
||||
print_values(il, a, 20);
|
||||
|
||||
// Close the subgraph
|
||||
il--;
|
||||
iprintf(il, "}\n");
|
||||
|
||||
// Output the edges from the head
|
||||
print_head_edge(il, a);
|
||||
|
||||
if (indent_level == 0) {
|
||||
// Termination of graph
|
||||
printf("}\n");
|
||||
}
|
||||
}
|
||||
38
OU3/src/int_array_1d/int_array_1d_mwe1.c
Normal file
38
OU3/src/int_array_1d/int_array_1d_mwe1.c
Normal file
@@ -0,0 +1,38 @@
|
||||
#include <stdio.h>
|
||||
#include <int_array_1d.h>
|
||||
|
||||
/*
|
||||
* Minimum working example for int_array_1d.c.
|
||||
*
|
||||
* Authors: Niclas Borlin (niclas@cs.umu.se)
|
||||
*
|
||||
* Version information:
|
||||
* v1.0 2018-01-28: First public version.
|
||||
* v1.1 2023-01-14: Added printouts at start/end of main.
|
||||
*/
|
||||
|
||||
#define VERSION "v1.1"
|
||||
#define VERSION_DATE "2023-01-14"
|
||||
|
||||
int main(void)
|
||||
{
|
||||
printf("%s, %s %s: Create typed integer array.\n",
|
||||
__FILE__, VERSION, VERSION_DATE);
|
||||
printf("Code base version %s (%s).\n\n", CODE_BASE_VERSION, CODE_BASE_RELEASE_DATE);
|
||||
|
||||
// Create an array with 6 positions.
|
||||
int_array_1d *a = int_array_1d_create(1, 6);
|
||||
|
||||
for (int i=int_array_1d_low(a); i<=int_array_1d_high(a); i++) {
|
||||
// Store square of index.
|
||||
int_array_1d_set_value(a, i*i, i);
|
||||
// Print array after setting each value.
|
||||
printf("After setting value at index %d:\n", i);
|
||||
int_array_1d_print(a);
|
||||
}
|
||||
|
||||
int_array_1d_kill(a);
|
||||
|
||||
printf("\nNormal exit.\n\n");
|
||||
return 0;
|
||||
}
|
||||
83
OU3/src/int_array_1d/int_array_1d_mwe1i.c
Normal file
83
OU3/src/int_array_1d/int_array_1d_mwe1i.c
Normal file
@@ -0,0 +1,83 @@
|
||||
#include <stdio.h>
|
||||
#include <int_array_1d.h>
|
||||
|
||||
/*
|
||||
* Minimum working example for int_array_1d.c.
|
||||
*
|
||||
* Authors: Niclas Borlin (niclas@cs.umu.se)
|
||||
*
|
||||
* Version information:
|
||||
* v1.0 2024-04-08: Adapted from int_array_1d_mwe1.c.
|
||||
*/
|
||||
|
||||
#define VERSION "v1.0"
|
||||
#define VERSION_DATE "2024-04-08"
|
||||
|
||||
// Print cut lines before and after a call array_print_internal.
|
||||
void print_internal_with_cut_lines(const int_array_1d *a, const char *desc)
|
||||
{
|
||||
// Internal counter that will be remembered between calls.
|
||||
|
||||
// Used to generate sequentially numbered -- CUT HERE -- marker lines
|
||||
// to enable automatic parsing of the output.
|
||||
static int cut = 1;
|
||||
|
||||
// Print starting marker line.
|
||||
printf("\n--- START CUT HERE %d ---\n", cut);
|
||||
|
||||
// Call the internal print function to get the actual dot code.
|
||||
int_array_1d_print_internal(a, desc, 0);
|
||||
|
||||
// Print ending marker line
|
||||
printf("--- END CUT HERE %d ---\n\n", cut);
|
||||
|
||||
// Increment the cut number. Will be remembered next time the
|
||||
// function is called since cut is a static variable.
|
||||
cut++;
|
||||
}
|
||||
|
||||
// Print a message with intructions how to use the dot output. prog is
|
||||
// the name of the executable.
|
||||
void print_dot_usage(char *prog)
|
||||
{
|
||||
printf("\nGenerate dot code to visualize internal structure with GraphViz. ");
|
||||
printf("Use\n\n%s ", prog);
|
||||
printf("| sed -n '/START CUT HERE X/,/END CUT HERE X/{//!p}' | dot -Tsvg > /tmp/dot.svg\n\n");
|
||||
printf("to generate an svg file of cut X (replace X by the requested cut number).\n");
|
||||
printf("The generated file can then be visualized with\n\n");
|
||||
printf("firefox /tmp/dot.svg\n\n");
|
||||
printf("Use -Tpng to generate a .png file instead. "
|
||||
"See graphviz.org and %s for documentation.\n", __FILE__);
|
||||
|
||||
printf("\n--- Start of normal output ---\n\n");
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
printf("%s, %s %s: Create typed integer array.\n",
|
||||
__FILE__, VERSION, VERSION_DATE);
|
||||
printf("Code base version %s (%s).\n\n", CODE_BASE_VERSION, CODE_BASE_RELEASE_DATE);
|
||||
|
||||
print_dot_usage(argv[0]);
|
||||
|
||||
// Create an array with 6 positions.
|
||||
int_array_1d *a = int_array_1d_create(1, 6);
|
||||
|
||||
printf("Empty array from the outside:\n");
|
||||
int_array_1d_print(a);
|
||||
print_internal_with_cut_lines(a, "Empty array");
|
||||
|
||||
for (int i=int_array_1d_low(a); i<=int_array_1d_high(a); i++) {
|
||||
// Store square of index.
|
||||
int_array_1d_set_value(a, i*i, i);
|
||||
}
|
||||
// Print array after setting all values.
|
||||
printf("Full array from the outside:\n");
|
||||
int_array_1d_print(a);
|
||||
print_internal_with_cut_lines(a, "Full array from the inside");
|
||||
|
||||
int_array_1d_kill(a);
|
||||
|
||||
printf("\nNormal exit.\n\n");
|
||||
return 0;
|
||||
}
|
||||
44
OU3/src/int_list/Makefile
Normal file
44
OU3/src/int_list/Makefile
Normal file
@@ -0,0 +1,44 @@
|
||||
MWE = int_list_mwe1 int_list_mwe1i
|
||||
TEST = int_list_test
|
||||
|
||||
SRC = int_list.c
|
||||
OBJ = $(SRC:.c=.o)
|
||||
|
||||
CC = gcc
|
||||
CFLAGS = -std=c99 -Wall -I../../include -g
|
||||
|
||||
all: mwe test
|
||||
|
||||
# Minimum working examples.
|
||||
mwe: $(MWE)
|
||||
|
||||
# Test programs.
|
||||
test: $(TEST)
|
||||
|
||||
# Object file for library
|
||||
obj: $(OBJ)
|
||||
|
||||
# Clean up
|
||||
clean:
|
||||
-rm -f $(MWE) $(TEST) $(OBJ)
|
||||
|
||||
int_list_mwe1: int_list_mwe1.c int_list.c
|
||||
gcc -o $@ $(CFLAGS) $^
|
||||
|
||||
int_list_mwe1i: int_list_mwe1i.c int_list.c
|
||||
gcc -o $@ $(CFLAGS) $^
|
||||
|
||||
int_list_test: int_list_test.c int_list.c
|
||||
gcc -o $@ $(CFLAGS) $^
|
||||
|
||||
test_run: test
|
||||
# Run the test
|
||||
./$(TEST)
|
||||
|
||||
memtest_mwe: int_list_mwe1
|
||||
valgrind --leak-check=full --show-reachable=yes ./$<
|
||||
|
||||
memtest_test: int_list_test
|
||||
valgrind --leak-check=full --show-reachable=yes ./$<
|
||||
|
||||
memtest: memtest_mwe memtest_test
|
||||
542
OU3/src/int_list/int_list.c
Normal file
542
OU3/src/int_list/int_list.c
Normal file
@@ -0,0 +1,542 @@
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdarg.h>
|
||||
|
||||
#include <int_list.h>
|
||||
|
||||
/*
|
||||
* Implementation of a typed, undirected list of integers for the
|
||||
* "Datastructures and algorithms" courses at the Department of
|
||||
* Computing Science, Umea University. The implementation uses linked
|
||||
* 2-cells.
|
||||
*
|
||||
* Authors: Niclas Borlin (niclas@cs.umu.se)
|
||||
*
|
||||
* Version information:
|
||||
* v1.0 2018-01-28: First public version.
|
||||
* v1.01 2018-03-26: Bugfix: Corrected const declaration in remove.
|
||||
* v1.1 2023-01-19: Added list_pos_equal and list_pos_is_valid functions.
|
||||
* v1.2 2023-01-20: Renamed list_pos_equal to list_pos_are_equal.
|
||||
* v1.3 2023-03-23: Renamed list_pos_are_equal to list_pos_is_equal.
|
||||
* v2.0 2024-05-10: Added print_internal.
|
||||
*/
|
||||
|
||||
// ===========INTERNAL DATA TYPES============
|
||||
|
||||
/*
|
||||
* The list elements are implemented as two-cells with forward and
|
||||
* backward links and place to store one integer. The list uses two
|
||||
* border cells at the start and end of the list.
|
||||
*/
|
||||
typedef struct cell {
|
||||
struct cell *next;
|
||||
struct cell *prev;
|
||||
int val;
|
||||
} cell;
|
||||
|
||||
struct list {
|
||||
cell *head;
|
||||
cell *tail;
|
||||
};
|
||||
|
||||
// ===========INTERNAL FUNCTION IMPLEMENTATIONS============
|
||||
|
||||
/**
|
||||
* list_empty() - Create an empty list.
|
||||
*
|
||||
* Returns: A pointer to the new list.
|
||||
*/
|
||||
list *list_empty(void)
|
||||
{
|
||||
// Allocate memory for the list head.
|
||||
list *l = calloc(1, sizeof(list));
|
||||
|
||||
// Allocate memory for the border cells.
|
||||
l->head = calloc(1, sizeof(cell));
|
||||
l->tail = calloc(1, sizeof(cell));
|
||||
|
||||
// Set consistent links between border elements.
|
||||
l->head->next = l->tail;
|
||||
l->tail->prev = l->head;
|
||||
|
||||
return l;
|
||||
}
|
||||
|
||||
/**
|
||||
* list_is_empty() - Check if a list is empty.
|
||||
* @l: List to check.
|
||||
*
|
||||
* Returns: True if the list is empty, otherwise false.
|
||||
*/
|
||||
bool list_is_empty(const list * l)
|
||||
{
|
||||
// List is empty if there are no cells between head and tail.
|
||||
return (l->head->next == l->tail);
|
||||
}
|
||||
|
||||
/**
|
||||
* list_first() - Return the first position of a list, i.e. the
|
||||
* position of the first element in the list.
|
||||
* @l: List to inspect.
|
||||
*
|
||||
* Returns: The first position in the given list.
|
||||
*/
|
||||
list_pos list_first(const list * l)
|
||||
{
|
||||
// First position is position of first element.
|
||||
return l->head->next;
|
||||
}
|
||||
|
||||
/**
|
||||
* list_end() - Return the last position of a list, i.e. the position
|
||||
* after the last element in the list.
|
||||
* @l: List to inspect.
|
||||
*
|
||||
* Returns: The last position in the given list.
|
||||
*/
|
||||
list_pos list_end(const list * l)
|
||||
{
|
||||
// Last position is position *after* last element.
|
||||
return l->tail;
|
||||
}
|
||||
|
||||
/**
|
||||
* list_next() - Return the next position in a list.
|
||||
* @l: List to inspect.
|
||||
* @p: Any valid position except the last in the list.
|
||||
*
|
||||
* Returns: The position in the list after the given position.
|
||||
* NOTE: The return value is undefined for the last position.
|
||||
*/
|
||||
list_pos list_next(const list * l, const list_pos p)
|
||||
{
|
||||
if (list_pos_is_equal(l, p, list_end(l))) {
|
||||
// This should really throw an error.
|
||||
fprintf(stderr,"list_next: Warning: Trying to navigate past end of list!");
|
||||
}
|
||||
return p->next;
|
||||
}
|
||||
|
||||
/**
|
||||
* list_prev() - Return the previous position in a list.
|
||||
* @l: List to inspect.
|
||||
* @p: Any valid position except the first in the list.
|
||||
*
|
||||
* Returns: The position in the list before the given position.
|
||||
* NOTE: The return value is undefined for the first position.
|
||||
*/
|
||||
list_pos list_prev(const list * l, const list_pos p)
|
||||
{
|
||||
if (list_pos_is_equal(l, p, list_first(l))) {
|
||||
// This should really throw an error.
|
||||
fprintf(stderr,"list_prev: Warning: Trying to navigate "
|
||||
"past beginning of list!\n");
|
||||
}
|
||||
return p->prev;
|
||||
}
|
||||
|
||||
/**
|
||||
* list_inspect() - Return the value of the element at a given
|
||||
* position in a list.
|
||||
* @l: List to inspect.
|
||||
* @p: Any valid position in the list, except the last.
|
||||
*
|
||||
* Returns: The integer value stored in the element at postiion pos.
|
||||
* NOTE: The return value is undefined for the last position.
|
||||
*/
|
||||
int list_inspect(const list * l, const list_pos p)
|
||||
{
|
||||
if (list_pos_is_equal(l, p, list_end(l))) {
|
||||
// This should really throw an error.
|
||||
fprintf(stderr,"list_inspect: Warning: Trying to inspect "
|
||||
"position at end of list!\n");
|
||||
}
|
||||
return p->val;
|
||||
}
|
||||
|
||||
/**
|
||||
* list_insert() - Insert a new element with a given value into a list.
|
||||
* @l: List to manipulate.
|
||||
* @v: Integer value to be inserted into the list.
|
||||
* @p: Position in the list before which the value should be inserted.
|
||||
*
|
||||
* Creates a new element and inserts it into the list before p.
|
||||
* Stores data in the new element.
|
||||
*
|
||||
* Returns: The position of the newly created element.
|
||||
*/
|
||||
list_pos list_insert(list * l, int v, const list_pos p)
|
||||
{
|
||||
// Allocate memory for a new cell.
|
||||
list_pos e = malloc(sizeof(cell));
|
||||
|
||||
// Store the value.
|
||||
e->val = v;
|
||||
// Add links to/from the new cell.
|
||||
e->next = p;
|
||||
e->prev = p->prev;
|
||||
p->prev = e;
|
||||
e->prev->next = e;
|
||||
|
||||
// Return the position of the new cell.
|
||||
return e;
|
||||
}
|
||||
|
||||
/**
|
||||
* list_remove() - Remove an element from a list.
|
||||
* @l: List to manipulate.
|
||||
* @p: Position in the list of the element to remove.
|
||||
*
|
||||
* Removes the element at position p from the list.
|
||||
*
|
||||
* Returns: The position after the removed element.
|
||||
*/
|
||||
list_pos list_remove(list *l, const list_pos p)
|
||||
{
|
||||
// Remember return position.
|
||||
list_pos next_pos = p->next;
|
||||
// Link past this element.
|
||||
p->prev->next = p->next;
|
||||
p->next->prev = p->prev;
|
||||
|
||||
// Free the memory allocated to the cell itself.
|
||||
free(p);
|
||||
// Return the position of the next element.
|
||||
return next_pos;
|
||||
}
|
||||
|
||||
/**
|
||||
* list_kill() - Destroy a given list.
|
||||
* @l: List to destroy.
|
||||
*
|
||||
* Returns all dynamic memory used by the list and its elements.
|
||||
*
|
||||
* Returns: Nothing.
|
||||
*/
|
||||
void list_kill(list * l)
|
||||
{
|
||||
// Use public functions to traverse the list.
|
||||
|
||||
// Start with the first element (will be defined even for an
|
||||
// empty list).
|
||||
list_pos p = list_first(l);
|
||||
|
||||
// Remove first element until list is empty.
|
||||
while (!list_is_empty(l)) {
|
||||
p = list_remove(l, p);
|
||||
}
|
||||
|
||||
// Free border elements and the list head.
|
||||
free(l->head);
|
||||
free(l->tail);
|
||||
free(l);
|
||||
}
|
||||
|
||||
/**
|
||||
* list_print() - Iterate over the list element and print their values.
|
||||
* @l: List to inspect.
|
||||
*
|
||||
* Iterates over the list and print each stored integer.
|
||||
*
|
||||
* Returns: Nothing.
|
||||
*/
|
||||
void list_print(const list * l)
|
||||
{
|
||||
// Start at the beginning of the list.
|
||||
list_pos p = list_first(l);
|
||||
|
||||
while (!list_pos_is_equal(l, p, list_end(l))) {
|
||||
// Call print_func with the element value at the
|
||||
// current position.
|
||||
printf("[%d]\n", list_inspect(l, p));
|
||||
p = list_next(l, p);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* list_pos_is_equal() - Return true if two positions in a list are equal.
|
||||
* @l: List to inspect.
|
||||
* @p1: First position to compare.
|
||||
* @p2: Second position to compare.
|
||||
*
|
||||
* Returns: True if p1 and p2 refer to the same position in l, otherwise False.
|
||||
* NOTE: The result is defined only if p1 and p2 are valid positions in l.
|
||||
*/
|
||||
bool list_pos_is_equal(const list *l, const list_pos p1, const list_pos p2)
|
||||
{
|
||||
// Since we don't need to check whether p1 or p2 are valid, we
|
||||
// only need to compare them directly.
|
||||
return p1 == p2;
|
||||
}
|
||||
|
||||
/**
|
||||
* list_pos_is_valid() - Return true for a valid position in a list
|
||||
* @l: List to inspect.
|
||||
* @p: Any position.
|
||||
*
|
||||
* Returns: True if p is a valid position in the list, otherwise false.
|
||||
*/
|
||||
bool list_pos_is_valid(const list *l, const list_pos p)
|
||||
{
|
||||
// Iterate over all positions in l.
|
||||
list_pos q = list_first(l);
|
||||
while (!list_pos_is_equal(l, q, list_end(l))) {
|
||||
if (list_pos_is_equal(l, p, q)) {
|
||||
// We found the position in the list.
|
||||
return true;
|
||||
}
|
||||
// Advance to the next valid position,
|
||||
q = list_next(l, q);
|
||||
}
|
||||
// p was not among valid positions in l.
|
||||
return false;
|
||||
}
|
||||
|
||||
// ===========INTERNAL FUNCTIONS USED BY int_list_print_internal ============
|
||||
|
||||
// The functions below output code in the dot language, used by
|
||||
// GraphViz. For documention of the dot language, see graphviz.org.
|
||||
|
||||
/**
|
||||
* indent() - Output indentation string.
|
||||
* @n: Indentation level.
|
||||
*
|
||||
* Print n tab characters.
|
||||
*
|
||||
* Returns: Nothing.
|
||||
*/
|
||||
static void indent(int n)
|
||||
{
|
||||
for (int i=0; i<n; i++) {
|
||||
printf("\t");
|
||||
}
|
||||
}
|
||||
/**
|
||||
* iprintf(...) - Indent and print.
|
||||
* @n: Indentation level
|
||||
* @...: printf arguments
|
||||
*
|
||||
* Print n tab characters and calls printf.
|
||||
*
|
||||
* Returns: Nothing.
|
||||
*/
|
||||
static void iprintf(int n, const char *fmt, ...)
|
||||
{
|
||||
// Indent...
|
||||
indent(n);
|
||||
// ...and call printf
|
||||
va_list args;
|
||||
va_start(args, fmt);
|
||||
vprintf(fmt, args);
|
||||
va_end(args);
|
||||
}
|
||||
|
||||
/**
|
||||
* print_edge() - Print a edge between two addresses.
|
||||
* @from: The address of the start of the edge. Should be non-NULL.
|
||||
* @to: The address of the destination for the edge, including NULL.
|
||||
* @port: The name of the port on the source node, or NULL.
|
||||
* @label: The label for the edge, or NULL.
|
||||
* @options: A string with other edge options, or NULL.
|
||||
*
|
||||
* Print an edge from port PORT on node FROM to TO with label
|
||||
* LABEL. If to is NULL, the destination is the NULL node, otherwise a
|
||||
* memory node. If the port is NULL, the edge starts at the node, not
|
||||
* a specific port on it. If label is NULL, no label is used. The
|
||||
* options string, if non-NULL, is printed before the label.
|
||||
*
|
||||
* Returns: Nothing.
|
||||
*/
|
||||
static void print_edge(int indent_level, const void *from, const void *to, const char *port,
|
||||
const char *label, const char *options)
|
||||
{
|
||||
indent(indent_level);
|
||||
if (port) {
|
||||
printf("m%04lx:%s -> ", PTR2ADDR(from), port);
|
||||
} else {
|
||||
printf("m%04lx -> ", PTR2ADDR(from));
|
||||
}
|
||||
if (to == NULL) {
|
||||
printf("NULL");
|
||||
} else {
|
||||
printf("m%04lx", PTR2ADDR(to));
|
||||
}
|
||||
printf(" [");
|
||||
if (options != NULL) {
|
||||
printf("%s", options);
|
||||
}
|
||||
if (label != NULL) {
|
||||
printf(" label=\"%s\"",label);
|
||||
}
|
||||
printf("]\n");
|
||||
}
|
||||
|
||||
/**
|
||||
* print_head_node() - Print a node corresponding to the list head struct.
|
||||
* @indent_level: Indentation level.
|
||||
* @l: List to inspect.
|
||||
*
|
||||
* Returns: Nothing.
|
||||
*/
|
||||
static void print_head_node(int indent_level, const list *l)
|
||||
{
|
||||
iprintf(indent_level, "m%04lx [shape=record "
|
||||
"label=\"<h>head\\n%04lx|<t>tail\\n%04lx\" xlabel=\"%04lx\"]\n",
|
||||
PTR2ADDR(l), PTR2ADDR(l->head), PTR2ADDR(l->tail), PTR2ADDR(l));
|
||||
}
|
||||
|
||||
// Print edges from the list head to the head and tail cells.
|
||||
static void print_head_edges(int indent_level, const list *l)
|
||||
{
|
||||
print_edge(indent_level, l, l->head, "h", "head", NULL);
|
||||
print_edge(indent_level, l, l->tail, "t", "tail", NULL);
|
||||
}
|
||||
|
||||
// Print a node corresponding to the cell at position p.
|
||||
static void print_elem_node(int indent_level, const list_pos p)
|
||||
{
|
||||
iprintf(indent_level, "m%04lx [shape=record "
|
||||
"label=\"<v>val\\n%d|<n>next\\n%04lx|<p>prev\\n%04lx\" xlabel=\"%04lx\"]\n",
|
||||
PTR2ADDR(p), p->val, PTR2ADDR(p->next), PTR2ADDR(p->prev), PTR2ADDR(p));
|
||||
}
|
||||
|
||||
// Print edges from the cell at position p to the next and previous
|
||||
// cells and the value. The value pointer is red, the others are
|
||||
// black. If the list owns the memory, the edge is solid, otherwise
|
||||
// dashed.
|
||||
static void print_elem_edges(int indent_level, const list *l, const list_pos p)
|
||||
{
|
||||
print_edge(indent_level, p, p->next, "n", "next", NULL);
|
||||
print_edge(indent_level, p, p->prev, "p", "prev", NULL);
|
||||
}
|
||||
|
||||
// Create an escaped version of the input string. The most common
|
||||
// control characters - newline, horizontal tab, backslash, and double
|
||||
// quote - are replaced by their escape sequence. The returned pointer
|
||||
// must be deallocated by the caller.
|
||||
static char *escape_chars(const char *s)
|
||||
{
|
||||
int i, j;
|
||||
int escaped = 0; // The number of chars that must be escaped.
|
||||
|
||||
// Count how many chars need to be escaped, i.e. how much longer
|
||||
// the output string will be.
|
||||
for (i = escaped = 0; s[i] != '\0'; i++) {
|
||||
if (s[i] == '\n' || s[i] == '\t' || s[i] == '\\' || s[i] == '\"') {
|
||||
escaped++;
|
||||
}
|
||||
}
|
||||
// Allocate space for the escaped string. The variable i holds the input
|
||||
// length, escaped how much the string will grow.
|
||||
char *t = malloc(i + escaped + 1);
|
||||
|
||||
// Copy-and-escape loop
|
||||
for (i = j = 0; s[i] != '\0'; i++) {
|
||||
// Convert each control character by its escape sequence.
|
||||
// Non-control characters are copied as-is.
|
||||
switch (s[i]) {
|
||||
case '\n': t[i+j] = '\\'; t[i+j+1] = 'n'; j++; break;
|
||||
case '\t': t[i+j] = '\\'; t[i+j+1] = 't'; j++; break;
|
||||
case '\\': t[i+j] = '\\'; t[i+j+1] = '\\'; j++; break;
|
||||
case '\"': t[i+j] = '\\'; t[i+j+1] = '\"'; j++; break;
|
||||
default: t[i+j] = s[i]; break;
|
||||
}
|
||||
}
|
||||
// Terminal the output string
|
||||
t[i+j] = '\0';
|
||||
return t;
|
||||
}
|
||||
|
||||
/**
|
||||
* list_print_internal() - Print the lists internal structure in dot format.
|
||||
* @l: List to inspect.
|
||||
* @desc: String with a description/state of the list, or NULL for no description.
|
||||
* @indent_level: Indentation level, 0 for outermost
|
||||
*
|
||||
* Iterates over the list and outputs dot code that shows the internal
|
||||
* structure of the list. The dot code can be visualized by
|
||||
* Graphviz.
|
||||
*
|
||||
* On linux system, the output can be parsed by the dot program, e.g.
|
||||
*
|
||||
* <list_program> | dot -Tsvg > /tmp/dot.svg; firefox /tmp/dot.svg
|
||||
*
|
||||
* where <list_program> is the name of the executable
|
||||
*
|
||||
* The output may also be possible to visualize online on
|
||||
* https://dreampuf.github.io/GraphvizOnline/ or google "graphviz
|
||||
* online".
|
||||
*
|
||||
* For documention of the dot language, see graphviz.org.
|
||||
*
|
||||
* Returns: Nothing.
|
||||
*/
|
||||
void list_print_internal(const list *l, const char *desc, int indent_level)
|
||||
{
|
||||
static int graph_number = 0;
|
||||
graph_number++;
|
||||
int il = indent_level;
|
||||
|
||||
if (indent_level == 0) {
|
||||
// If this is the outermost datatype, start a graph...
|
||||
printf("digraph LIST_%d {\n", graph_number);
|
||||
|
||||
// Specify default shape and fontname
|
||||
il++;
|
||||
iprintf(il, "node [shape=rectangle fontname=\"Courier New\"]\n");
|
||||
iprintf(il, "ranksep=0.01\n");
|
||||
}
|
||||
|
||||
if (desc != NULL) {
|
||||
// Escape the string before printout
|
||||
char *escaped = escape_chars(desc);
|
||||
// Use different names on inner description nodes
|
||||
if (indent_level == 0) {
|
||||
iprintf(il, "description [label=\"%s\"]\n", escaped);
|
||||
} else {
|
||||
iprintf(il, "\tcluster_int_list_%d_description [label=\"%s\"]\n", graph_number, escaped);
|
||||
}
|
||||
// Return the memory used by the escaped string
|
||||
free(escaped);
|
||||
}
|
||||
|
||||
if (indent_level == 0) {
|
||||
// Use a single "pointer" edge as a starting point for the
|
||||
// outermost datatype
|
||||
iprintf(il, "l [label=\"%04lx\" xlabel=\"l\"]\n", PTR2ADDR(l));
|
||||
iprintf(il, "l -> m%04lx\n", PTR2ADDR(l));
|
||||
}
|
||||
|
||||
// Print the subgraph to surround the DList content
|
||||
iprintf(il, "subgraph cluster_int_list_%d { label=\"List\"\n", graph_number);
|
||||
il++;
|
||||
|
||||
// Output the head node
|
||||
print_head_node(il, l);
|
||||
|
||||
// Output the element nodes
|
||||
list_pos p = l->head;
|
||||
while (p != NULL) {
|
||||
print_elem_node(il, p);
|
||||
p = p->next;
|
||||
}
|
||||
|
||||
// Close the subgraph
|
||||
il--;
|
||||
iprintf(il, "}\n");
|
||||
|
||||
// Output the edges from the head
|
||||
print_head_edges(il, l);
|
||||
|
||||
// Output the edges from each element
|
||||
p = l->head;
|
||||
while (p != NULL) {
|
||||
print_elem_edges(il, l, p);
|
||||
p = p->next;
|
||||
}
|
||||
|
||||
if (indent_level == 0) {
|
||||
// Termination of graph
|
||||
printf("}\n");
|
||||
}
|
||||
}
|
||||
60
OU3/src/int_list/int_list_mwe1.c
Normal file
60
OU3/src/int_list/int_list_mwe1.c
Normal file
@@ -0,0 +1,60 @@
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#include <int_list.h>
|
||||
|
||||
/*
|
||||
* Minimum working example for int_list.c. Create a list, insert two
|
||||
* elements, print list, free list.
|
||||
*
|
||||
* Authors: Niclas Borlin (niclas@cs.umu.se)
|
||||
* Adam Dahlgren Lindstrom (dali@cs.umu.se)
|
||||
*
|
||||
* Version information:
|
||||
* v1.0 2018-01-28: First public version.
|
||||
* v1.1 2023-01-14: Added printouts at start/end of main.
|
||||
* v1.11 2024-01-16: Fix include to be with brackets, not citation marks.
|
||||
*/
|
||||
|
||||
#define VERSION "v1.11"
|
||||
#define VERSION_DATE "2024-01-16"
|
||||
|
||||
int main(void)
|
||||
{
|
||||
printf("%s, %s %s: Create typed integer list.\n",
|
||||
__FILE__, VERSION, VERSION_DATE);
|
||||
printf("Code base version %s (%s).\n\n", CODE_BASE_VERSION, CODE_BASE_RELEASE_DATE);
|
||||
|
||||
// Create the list.
|
||||
list *l = list_empty();
|
||||
|
||||
// Insert the value 5 first in the list.
|
||||
list_insert(l, 5, list_first(l));
|
||||
|
||||
printf("List after inserting one value:\n");
|
||||
list_print(l);
|
||||
|
||||
// Insert the value 8 last in the list.
|
||||
list_insert(l, 8, list_end(l));
|
||||
|
||||
printf("List after inserting second value at the end:\n");
|
||||
list_print(l);
|
||||
|
||||
// Insert the value 2 at the second position in the list.
|
||||
list_insert(l, 2, list_next(l, list_first(l)));
|
||||
|
||||
printf("List after inserting a third value in the middle:\n");
|
||||
list_print(l);
|
||||
|
||||
// Remove first element.
|
||||
list_remove(l, list_first(l));
|
||||
|
||||
printf("List after removing first element:\n");
|
||||
list_print(l);
|
||||
|
||||
// Done, kill the list.
|
||||
list_kill(l);
|
||||
|
||||
printf("\nNormal exit.\n\n");
|
||||
return 0;
|
||||
}
|
||||
97
OU3/src/int_list/int_list_mwe1i.c
Normal file
97
OU3/src/int_list/int_list_mwe1i.c
Normal file
@@ -0,0 +1,97 @@
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#include <int_list.h>
|
||||
|
||||
/*
|
||||
* Minimum working example for int_list.c. Create a list, insert two
|
||||
* elements, print list, free list.
|
||||
*
|
||||
* Authors: Niclas Borlin (niclas@cs.umu.se)
|
||||
* Adam Dahlgren Lindstrom (dali@cs.umu.se)
|
||||
*
|
||||
* Version information:
|
||||
* v1.0 2018-01-28: First public version.
|
||||
* v1.1 2023-01-14: Added printouts at start/end of main.
|
||||
* v1.11 2024-01-16: Fix include to be with brackets, not citation marks.
|
||||
*/
|
||||
|
||||
#define VERSION "v1.11"
|
||||
#define VERSION_DATE "2024-01-16"
|
||||
|
||||
// Print cut lines before and after a call list_print_internal.
|
||||
void print_internal_with_cut_lines(const list *l, const char *desc)
|
||||
{
|
||||
// Internal counter that will be remembered between calls.
|
||||
|
||||
// Used to generate sequentially numbered -- CUT HERE -- marker lines
|
||||
// to enable automatic parsing of the output.
|
||||
static int cut = 1;
|
||||
|
||||
// Print starting marker line.
|
||||
printf("\n--- START CUT HERE %d ---\n", cut);
|
||||
|
||||
// Call the internal print function to get the actual dot code.
|
||||
list_print_internal(l, desc, 0);
|
||||
|
||||
// Print ending marker line
|
||||
printf("--- END CUT HERE %d ---\n\n", cut);
|
||||
|
||||
// Increment the cut number. Will be remembered next time the
|
||||
// function is called since cut is a static variable.
|
||||
cut++;
|
||||
}
|
||||
|
||||
// Print a message with intructions how to use the dot output. prog is
|
||||
// the name of the executable.
|
||||
void print_dot_usage(char *prog)
|
||||
{
|
||||
printf("\nGenerate dot code to visualize internal structure with GraphViz. ");
|
||||
printf("Use\n\n%s ", prog);
|
||||
printf("| sed -n '/START CUT HERE X/,/END CUT HERE X/{//!p}' | dot -Tsvg > /tmp/dot.svg\n\n");
|
||||
printf("to generate an svg file of cut X (replace X by the requested cut number).\n");
|
||||
printf("The generated file can then be visualized with\n\n");
|
||||
printf("firefox /tmp/dot.svg\n\n");
|
||||
printf("Use -Tpng to generate a .png file instead. "
|
||||
"See graphviz.org and %s for documentation.\n", __FILE__);
|
||||
|
||||
printf("\n--- Start of normal output ---\n\n");
|
||||
}
|
||||
|
||||
int main(void)
|
||||
{
|
||||
printf("%s, %s %s: Create typed integer list.\n",
|
||||
__FILE__, VERSION, VERSION_DATE);
|
||||
printf("Code base version %s (%s).\n\n", CODE_BASE_VERSION, CODE_BASE_RELEASE_DATE);
|
||||
|
||||
// Create the list.
|
||||
list *l = list_empty();
|
||||
|
||||
printf("Empty list from the outside.\n");
|
||||
list_print(l);
|
||||
print_internal_with_cut_lines(l, "Empty list showing the internals");
|
||||
|
||||
// Insert the value 5 first in the list.
|
||||
list_insert(l, 5, list_first(l));
|
||||
|
||||
printf("List from the outside after inserting one value:\n");
|
||||
list_print(l);
|
||||
print_internal_with_cut_lines(l, "List after inserting one value showing the internals");
|
||||
|
||||
// Insert the value 8 last in the list.
|
||||
list_insert(l, 8, list_end(l));
|
||||
|
||||
printf("List from the outside after inserting second value at the end:\n");
|
||||
list_print(l);
|
||||
print_internal_with_cut_lines(l, "List after inserting second value at the end showing the internals");
|
||||
|
||||
printf("List after removing first element:\n");
|
||||
list_print(l);
|
||||
print_internal_with_cut_lines(l, "List after removing first element showing the internals");
|
||||
|
||||
// Done, kill the list.
|
||||
list_kill(l);
|
||||
|
||||
printf("\nNormal exit.\n\n");
|
||||
return 0;
|
||||
}
|
||||
1101
OU3/src/int_list/int_list_test.c
Normal file
1101
OU3/src/int_list/int_list_test.c
Normal file
File diff suppressed because it is too large
Load Diff
44
OU3/src/int_list_array/Makefile
Normal file
44
OU3/src/int_list_array/Makefile
Normal file
@@ -0,0 +1,44 @@
|
||||
MWE = int_list_array_mwe1 int_list_array_mwe1i
|
||||
TEST = int_list_array_test
|
||||
|
||||
SRC = int_list_array.c
|
||||
OBJ = $(SRC:.c=.o)
|
||||
|
||||
CC = gcc
|
||||
CFLAGS = -std=c99 -Wall -I../../include -g
|
||||
|
||||
all: mwe test
|
||||
|
||||
# Minimum working examples.
|
||||
mwe: $(MWE)
|
||||
|
||||
# Test programs.
|
||||
test: $(TEST)
|
||||
|
||||
# Object file for library
|
||||
obj: $(OBJ)
|
||||
|
||||
# Clean up
|
||||
clean:
|
||||
-rm -f $(MWE) $(TEST) $(OBJ)
|
||||
|
||||
int_list_array_mwe1: int_list_array_mwe1.c int_list_array.c
|
||||
gcc -o $@ $(CFLAGS) $^
|
||||
|
||||
int_list_array_mwe1i: int_list_array_mwe1i.c int_list_array.c
|
||||
gcc -o $@ $(CFLAGS) $^
|
||||
|
||||
int_list_array_test: int_list_array_test.c int_list_array.c
|
||||
gcc -o $@ $(CFLAGS) $^
|
||||
|
||||
test_run: test
|
||||
# Run the test
|
||||
./$(TEST)
|
||||
|
||||
memtest_mwe1: int_list_array_mwe1
|
||||
valgrind --leak-check=full --show-reachable=yes ./$<
|
||||
|
||||
memtest_test: int_list_array_test
|
||||
valgrind --leak-check=full --show-reachable=yes ./$<
|
||||
|
||||
memtest: memtest_mwe1 memtest_test
|
||||
515
OU3/src/int_list_array/int_list_array.c
Normal file
515
OU3/src/int_list_array/int_list_array.c
Normal file
@@ -0,0 +1,515 @@
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <strings.h> // For bcopy
|
||||
#include <stdarg.h>
|
||||
|
||||
#include <int_list_array.h>
|
||||
|
||||
/*
|
||||
* Implementation of a typed, undirected list of integers for the
|
||||
* "Datastructures and algorithms" courses at the Department of
|
||||
* Computing Science, Umea University. The implementation uses a
|
||||
* static array.
|
||||
*
|
||||
* Authors: Niclas Borlin (niclas@cs.umu.se)
|
||||
*
|
||||
* Version information:
|
||||
* v1.0 2018-01-28: First public version.
|
||||
* v1.1 2023-01-19: Added list_pos_equal and list_pos_is_valid functions. Bugfix
|
||||
* in list_remove.
|
||||
* v1.2 2023-01-20: Renamed list_pos_equal to list_pos_are_equal.
|
||||
* v1.3 2023-03-23: Renamed list_pos_are_equal to list_pos_is_equal.
|
||||
* v1.4 2024-05-10: Added print_internal.
|
||||
*/
|
||||
|
||||
// ===========INTERNAL DATA TYPES============
|
||||
|
||||
/*
|
||||
* The list is implemented as a static array.
|
||||
*/
|
||||
struct list {
|
||||
int last_used_pos;
|
||||
int *values;
|
||||
int array_size;
|
||||
};
|
||||
|
||||
// ===========INTERNAL FUNCTION IMPLEMENTATIONS============
|
||||
|
||||
#define ARRAY_MAX_SIZE 100
|
||||
|
||||
/**
|
||||
* list_empty() - Create an empty list.
|
||||
*
|
||||
* Returns: A pointer to the new list.
|
||||
*/
|
||||
list *list_empty(void)
|
||||
{
|
||||
// Allocate memory for the list head.
|
||||
list *l=malloc(sizeof(list));
|
||||
// Set the size
|
||||
l->array_size = ARRAY_MAX_SIZE;
|
||||
// Allocate memory for the elements.
|
||||
l->values=calloc(l->array_size,sizeof(l->values[0]));
|
||||
// Set last used position.
|
||||
l->last_used_pos=-1;
|
||||
return l;
|
||||
}
|
||||
|
||||
/**
|
||||
* list_is_empty() - Check if a list is empty.
|
||||
* @l: List to check.
|
||||
*
|
||||
* Returns: True if the list is empty, otherwise false.
|
||||
*/
|
||||
bool list_is_empty(const list * l)
|
||||
{
|
||||
// List is empty if no elements are used.
|
||||
return l->last_used_pos < 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* list_first() - Return the first position of a list, i.e. the
|
||||
* position of the first element in the list.
|
||||
* @l: List to inspect.
|
||||
*
|
||||
* Returns: The first position in the given list.
|
||||
*/
|
||||
list_pos list_first(const list * l)
|
||||
{
|
||||
// First position is always 0.
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* list_end() - Return the last position of a list, i.e. the position
|
||||
* after the last element in the list.
|
||||
* @l: List to inspect.
|
||||
*
|
||||
* Returns: The last position in the given list.
|
||||
*/
|
||||
list_pos list_end(const list * l)
|
||||
{
|
||||
// Last position is position *after* last used element.
|
||||
return l->last_used_pos + 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* list_next() - Return the next position in a list.
|
||||
* @l: List to inspect.
|
||||
* @p: Any valid position except the last in the list.
|
||||
*
|
||||
* Returns: The position in the list after the given position.
|
||||
* NOTE: The return value is undefined for the last position.
|
||||
*/
|
||||
list_pos list_next(const list * l, const list_pos p)
|
||||
{
|
||||
if (list_pos_is_equal(l, p, list_end(l))) {
|
||||
// This should really throw an error.
|
||||
fprintf(stderr,"list_next: Warning: Trying to navigate "
|
||||
"past end of list!");
|
||||
}
|
||||
return p + 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* list_prev() - Return the previous position in a list.
|
||||
* @l: List to inspect.
|
||||
* @p: Any valid position except the first in the list.
|
||||
*
|
||||
* Returns: The position in the list before the given position.
|
||||
* NOTE: The return value is undefined for the first position.
|
||||
*/
|
||||
list_pos list_prev(const list * l, const list_pos p)
|
||||
{
|
||||
if (list_pos_is_equal(l, p, list_first(l))) {
|
||||
// This should really throw an error.
|
||||
fprintf(stderr,"list_prev: Warning: Trying to navigate "
|
||||
"past beginning of list!\n");
|
||||
}
|
||||
return p - 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* list_inspect() - Return the value of the element at a given
|
||||
* position in a list.
|
||||
* @l: List to inspect.
|
||||
* @p: Any valid position in the list, except the last.
|
||||
*
|
||||
* Returns: The integer value stored in the element at postiion pos.
|
||||
* NOTE: The return value is undefined for the last position.
|
||||
*/
|
||||
int list_inspect(const list * l, const list_pos p)
|
||||
{
|
||||
if (list_pos_is_equal(l, p, list_end(l))) {
|
||||
// This should really throw an error.
|
||||
fprintf(stderr,"list_inspect: Warning: Trying to inspect "
|
||||
"position at end of list!\n");
|
||||
}
|
||||
return l->values[p];
|
||||
}
|
||||
|
||||
/**
|
||||
* list_insert() - Insert a new element with a given value into a list.
|
||||
* @l: List to manipulate.
|
||||
* @v: Integer value to be inserted into the list.
|
||||
* @p: Position in the list before which the value should be inserted.
|
||||
*
|
||||
* Creates a new element and inserts it into the list before p.
|
||||
* Stores data in the new element.
|
||||
*
|
||||
* Returns: The position of the newly created element.
|
||||
*/
|
||||
list_pos list_insert(list * l, int v, const list_pos p)
|
||||
{
|
||||
// Move elements at position pos and later forward.
|
||||
bcopy(l->values + p, l->values + p + 1,
|
||||
sizeof(int) * (l->last_used_pos - p + 1));
|
||||
|
||||
// Set value.
|
||||
l->values[p] = v;
|
||||
|
||||
// Increment number of used elements.
|
||||
l->last_used_pos++;
|
||||
|
||||
// Return the position of the new value.
|
||||
return p;
|
||||
}
|
||||
|
||||
/**
|
||||
* list_remove() - Remove an element from a list.
|
||||
* @l: List to manipulate.
|
||||
* @p: Position in the list of the element to remove.
|
||||
*
|
||||
* Removes the element at position p from the list.
|
||||
*
|
||||
* Returns: The position after the removed element.
|
||||
*/
|
||||
list_pos list_remove(list *l, const list_pos p)
|
||||
{
|
||||
// Move elements at position pos and later forward.
|
||||
bcopy(l->values + p + 1, l->values + p, sizeof(int) * (l->last_used_pos - p));
|
||||
|
||||
// Decrement number of used elements.
|
||||
l->last_used_pos--;
|
||||
|
||||
// p now refers to the position after the removed element.
|
||||
return p;
|
||||
}
|
||||
|
||||
/**
|
||||
* list_kill() - Destroy a given list.
|
||||
* @l: List to destroy.
|
||||
*
|
||||
* Returns all dynamic memory used by the list and its elements.
|
||||
*
|
||||
* Returns: Nothing.
|
||||
*/
|
||||
void list_kill(list * l)
|
||||
{
|
||||
free(l->values);
|
||||
free(l);
|
||||
}
|
||||
|
||||
/**
|
||||
* list_print() - Iterate over the list element and print their values.
|
||||
* @l: List to inspect.
|
||||
*
|
||||
* Iterates over the list and print each stored integer.
|
||||
*
|
||||
* Returns: Nothing.
|
||||
*/
|
||||
void list_print(const list * l)
|
||||
{
|
||||
// Start at the beginning of the list.
|
||||
list_pos p = list_first(l);
|
||||
|
||||
while (!list_pos_is_equal(l, p, list_end(l))) {
|
||||
// Call print_func with the element value at the
|
||||
// current position.
|
||||
printf("[%d]\n",list_inspect(l, p));
|
||||
p = list_next(l, p);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* list_pos_is_equal() - Return true if two positions in a list are equal.
|
||||
* @l: List to inspect.
|
||||
* @p1: First position to compare.
|
||||
* @p2: Second position to compare.
|
||||
*
|
||||
* Returns: True if p1 and p2 refer to the same position in l, otherwise False.
|
||||
* NOTE: The result is defined only if p1 and p2 are valid positions in l.
|
||||
*/
|
||||
bool list_pos_is_equal(const list *l, const list_pos p1, const list_pos p2)
|
||||
{
|
||||
// Since we don't need to check whether p1 or p2 are valid, we
|
||||
// only need to compare them directly.
|
||||
return p1 == p2;
|
||||
}
|
||||
|
||||
/**
|
||||
* list_pos_is_valid() - Return true for a valid position in a list
|
||||
* @l: List to inspect.
|
||||
* @p: Any position.
|
||||
*
|
||||
* Returns: True if p is a valid position in the list, otherwise false.
|
||||
*/
|
||||
bool list_pos_is_valid(const list *l, const list_pos p)
|
||||
{
|
||||
// The position is valid if it is between 0 and last_used_pos + 1, inclusive.
|
||||
return p >= 0 && p <= l->last_used_pos+1;
|
||||
|
||||
}
|
||||
|
||||
// ===========INTERNAL FUNCTIONS USED BY int_list_array_print_internal ============
|
||||
|
||||
// The functions below output code in the dot language, used by
|
||||
// GraphViz. For documention of the dot language, see graphviz.org.
|
||||
|
||||
/**
|
||||
* indent() - Output indentation string.
|
||||
* @n: Indentation level.
|
||||
*
|
||||
* Print n tab characters.
|
||||
*
|
||||
* Returns: Nothing.
|
||||
*/
|
||||
static void indent(int n)
|
||||
{
|
||||
for (int i=0; i<n; i++) {
|
||||
printf("\t");
|
||||
}
|
||||
}
|
||||
/**
|
||||
* iprintf(...) - Indent and print.
|
||||
* @n: Indentation level
|
||||
* @...: printf arguments
|
||||
*
|
||||
* Print n tab characters and calls printf.
|
||||
*
|
||||
* Returns: Nothing.
|
||||
*/
|
||||
static void iprintf(int n, const char *fmt, ...)
|
||||
{
|
||||
// Indent...
|
||||
indent(n);
|
||||
// ...and call printf
|
||||
va_list args;
|
||||
va_start(args, fmt);
|
||||
vprintf(fmt, args);
|
||||
va_end(args);
|
||||
}
|
||||
|
||||
/**
|
||||
* print_edge() - Print a edge between two addresses.
|
||||
* @from: The address of the start of the edge. Should be non-NULL.
|
||||
* @to: The address of the destination for the edge, including NULL.
|
||||
* @port: The name of the port on the source node, or NULL.
|
||||
* @label: The label for the edge, or NULL.
|
||||
* @options: A string with other edge options, or NULL.
|
||||
*
|
||||
* Print an edge from port PORT on node FROM to TO with label
|
||||
* LABEL. If to is NULL, the destination is the NULL node, otherwise a
|
||||
* memory node. If the port is NULL, the edge starts at the node, not
|
||||
* a specific port on it. If label is NULL, no label is used. The
|
||||
* options string, if non-NULL, is printed before the label.
|
||||
*
|
||||
* Returns: Nothing.
|
||||
*/
|
||||
static void print_edge(int indent_level, const void *from, const void *to, const char *port,
|
||||
const char *label, const char *options)
|
||||
{
|
||||
indent(indent_level);
|
||||
if (port) {
|
||||
printf("m%04lx:%s -> ", PTR2ADDR(from), port);
|
||||
} else {
|
||||
printf("m%04lx -> ", PTR2ADDR(from));
|
||||
}
|
||||
if (to == NULL) {
|
||||
printf("NULL");
|
||||
} else {
|
||||
printf("m%04lx", PTR2ADDR(to));
|
||||
}
|
||||
printf(" [");
|
||||
if (options != NULL) {
|
||||
printf("%s", options);
|
||||
}
|
||||
if (label != NULL) {
|
||||
printf(" label=\"%s\"",label);
|
||||
}
|
||||
printf("]\n");
|
||||
}
|
||||
|
||||
/**
|
||||
* print_head_node() - Print a node corresponding to the list head struct.
|
||||
* @indent_level: Indentation level.
|
||||
* @l: List to inspect.
|
||||
*
|
||||
* Returns: Nothing.
|
||||
*/
|
||||
static void print_head_node(int indent_level, const list *l)
|
||||
{
|
||||
iprintf(indent_level, "m%04lx [shape=record "
|
||||
"label=\"last_used_pos\\n%d|array_size\\n%d|<v>values\\n%04lx\"]\n",
|
||||
PTR2ADDR(l), l->last_used_pos, l->array_size, PTR2ADDR(l->values));
|
||||
}
|
||||
|
||||
/**
|
||||
* print_values() - Print a node containing all positions in the array.
|
||||
* @indent_level: Indentation level.
|
||||
* @l: List to inspect.
|
||||
* @max_values_to_print: Maximum number of values to output.
|
||||
*
|
||||
* Will print dot code to display each value in the array, up to
|
||||
* max_values_to_print. If there are more values to print, adds an
|
||||
* ellipsis (...) at the end.
|
||||
*
|
||||
* Returns: Nothing.
|
||||
*/
|
||||
void print_values(int indent_level, const list *l, int max_values_to_print)
|
||||
{
|
||||
int values_to_print = l->array_size;
|
||||
int truncated = false;
|
||||
if (values_to_print > max_values_to_print) {
|
||||
values_to_print = max_values_to_print;
|
||||
truncated = true;
|
||||
}
|
||||
iprintf(indent_level, "m%04lx [shape=record label=\"", PTR2ADDR(l->values));
|
||||
for (int i=0; i < values_to_print; i++) {
|
||||
printf("%02d\\n%d", i, l->values[i]);
|
||||
if (i < values_to_print - 1) {
|
||||
printf("|");
|
||||
}
|
||||
}
|
||||
if (truncated) {
|
||||
// Add ellipsis
|
||||
printf("|\\n...\\n");
|
||||
}
|
||||
printf("\"]\n");
|
||||
}
|
||||
|
||||
// Print edges from the list head to the head and tail cells.
|
||||
static void print_head_edge(int indent_level, const list *l)
|
||||
{
|
||||
print_edge(indent_level, l, l->values, "v", "values", NULL);
|
||||
}
|
||||
|
||||
// Create an escaped version of the input string. The most common
|
||||
// control characters - newline, horizontal tab, backslash, and double
|
||||
// quote - are replaced by their escape sequence. The returned pointer
|
||||
// must be deallocated by the caller.
|
||||
static char *escape_chars(const char *s)
|
||||
{
|
||||
int i, j;
|
||||
int escaped = 0; // The number of chars that must be escaped.
|
||||
|
||||
// Count how many chars need to be escaped, i.e. how much longer
|
||||
// the output string will be.
|
||||
for (i = escaped = 0; s[i] != '\0'; i++) {
|
||||
if (s[i] == '\n' || s[i] == '\t' || s[i] == '\\' || s[i] == '\"') {
|
||||
escaped++;
|
||||
}
|
||||
}
|
||||
// Allocate space for the escaped string. The variable i holds the input
|
||||
// length, escaped how much the string will grow.
|
||||
char *t = malloc(i + escaped + 1);
|
||||
|
||||
// Copy-and-escape loop
|
||||
for (i = j = 0; s[i] != '\0'; i++) {
|
||||
// Convert each control character by its escape sequence.
|
||||
// Non-control characters are copied as-is.
|
||||
switch (s[i]) {
|
||||
case '\n': t[i+j] = '\\'; t[i+j+1] = 'n'; j++; break;
|
||||
case '\t': t[i+j] = '\\'; t[i+j+1] = 't'; j++; break;
|
||||
case '\\': t[i+j] = '\\'; t[i+j+1] = '\\'; j++; break;
|
||||
case '\"': t[i+j] = '\\'; t[i+j+1] = '\"'; j++; break;
|
||||
default: t[i+j] = s[i]; break;
|
||||
}
|
||||
}
|
||||
// Terminal the output string
|
||||
t[i+j] = '\0';
|
||||
return t;
|
||||
}
|
||||
|
||||
/**
|
||||
* list_print_internal() - Print the lists internal structure in dot format.
|
||||
* @l: List to inspect.
|
||||
* @desc: String with a description/state of the list, or NULL for no description.
|
||||
* @indent_level: Indentation level, 0 for outermost
|
||||
*
|
||||
* Iterates over the list and outputs dot code that shows the internal
|
||||
* structure of the list. The dot code can be visualized by
|
||||
* Graphviz.
|
||||
*
|
||||
* On linux system, the output can be parsed by the dot program, e.g.
|
||||
*
|
||||
* <list_program> | dot -Tsvg > /tmp/dot.svg; firefox /tmp/dot.svg
|
||||
*
|
||||
* where <list_program> is the name of the executable
|
||||
*
|
||||
* The output may also be possible to visualize online on
|
||||
* https://dreampuf.github.io/GraphvizOnline/ or google "graphviz
|
||||
* online".
|
||||
*
|
||||
* For documention of the dot language, see graphviz.org.
|
||||
*
|
||||
* Returns: Nothing.
|
||||
*/
|
||||
void list_print_internal(const list *l, const char *desc, int indent_level)
|
||||
{
|
||||
static int graph_number = 0;
|
||||
graph_number++;
|
||||
int il = indent_level;
|
||||
|
||||
if (indent_level == 0) {
|
||||
// If this is the outermost datatype, start a graph...
|
||||
printf("digraph LIST_%d {\n", graph_number);
|
||||
|
||||
// Specify default shape and fontname
|
||||
il++;
|
||||
iprintf(il, "node [shape=rectangle fontname=\"Courier New\"]\n");
|
||||
iprintf(il, "ranksep=0.01\n");
|
||||
}
|
||||
|
||||
if (desc != NULL) {
|
||||
// Escape the string before printout
|
||||
char *escaped = escape_chars(desc);
|
||||
// Use different names on inner description nodes
|
||||
if (indent_level == 0) {
|
||||
iprintf(il, "description [label=\"%s\"]\n", escaped);
|
||||
} else {
|
||||
iprintf(il, "\tcluster_int_list_%d_description [label=\"%s\"]\n", graph_number, escaped);
|
||||
}
|
||||
// Return the memory used by the escaped string
|
||||
free(escaped);
|
||||
}
|
||||
|
||||
if (indent_level == 0) {
|
||||
// Use a single "pointer" edge as a starting point for the
|
||||
// outermost datatype
|
||||
iprintf(il, "l [label=\"%04lx\" xlabel=\"l\"]\n", PTR2ADDR(l));
|
||||
iprintf(il, "l -> m%04lx\n", PTR2ADDR(l));
|
||||
}
|
||||
|
||||
// Print the subgraph to surround the List content
|
||||
iprintf(il, "subgraph cluster_int_list_%d { label=\"List\"\n", graph_number);
|
||||
il++;
|
||||
|
||||
// Output the head node
|
||||
print_head_node(il, l);
|
||||
|
||||
// Output the values array. Limit the output to 10 elements.
|
||||
print_values(il, l, 10);
|
||||
|
||||
// Close the subgraph
|
||||
il--;
|
||||
iprintf(il, "}\n");
|
||||
|
||||
// Output the edges from the head
|
||||
print_head_edge(il, l);
|
||||
|
||||
if (indent_level == 0) {
|
||||
// Termination of graph
|
||||
printf("}\n");
|
||||
}
|
||||
}
|
||||
60
OU3/src/int_list_array/int_list_array_mwe1.c
Normal file
60
OU3/src/int_list_array/int_list_array_mwe1.c
Normal file
@@ -0,0 +1,60 @@
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#include <int_list_array.h>
|
||||
|
||||
/*
|
||||
* Minimum working example for int_list.c. Create a list, insert two
|
||||
* elements, print list, free list.
|
||||
*
|
||||
* Authors: Niclas Borlin (niclas@cs.umu.se)
|
||||
* Adam Dahlgren Lindstrom (dali@cs.umu.se)
|
||||
*
|
||||
* Version information:
|
||||
* v1.0 2018-01-28: First public version.
|
||||
* v1.1 2023-01-14: Added printouts at start/end of main.
|
||||
* v1.11 2024-01-16: Fix include to be with brackets, not citation marks.
|
||||
*/
|
||||
|
||||
#define VERSION "v1.11"
|
||||
#define VERSION_DATE "2024-01-16"
|
||||
|
||||
int main(void)
|
||||
{
|
||||
printf("%s, %s %s: Create typed integer list, implemented as an array.\n",
|
||||
__FILE__, VERSION, VERSION_DATE);
|
||||
printf("Code base version %s (%s).\n\n", CODE_BASE_VERSION, CODE_BASE_RELEASE_DATE);
|
||||
|
||||
// Create the list.
|
||||
list *l = list_empty();
|
||||
|
||||
// Insert the value 5 first in the list.
|
||||
list_insert(l, 5, list_first(l));
|
||||
|
||||
printf("List after inserting one value:\n");
|
||||
list_print(l);
|
||||
|
||||
// Insert the value 8 last in the list.
|
||||
list_insert(l, 8, list_end(l));
|
||||
|
||||
printf("List after inserting second value at the end:\n");
|
||||
list_print(l);
|
||||
|
||||
// Insert the value 2 at the second position in the list.
|
||||
list_insert(l, 2, list_next(l, list_first(l)));
|
||||
|
||||
printf("List after inserting a third value in the middle:\n");
|
||||
list_print(l);
|
||||
|
||||
// Remove first element.
|
||||
list_remove(l, list_first(l));
|
||||
|
||||
printf("List after removing first element:\n");
|
||||
list_print(l);
|
||||
|
||||
// Done, kill the list.
|
||||
list_kill(l);
|
||||
|
||||
printf("\nNormal exit.\n\n");
|
||||
return 0;
|
||||
}
|
||||
97
OU3/src/int_list_array/int_list_array_mwe1i.c
Normal file
97
OU3/src/int_list_array/int_list_array_mwe1i.c
Normal file
@@ -0,0 +1,97 @@
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#include <int_list.h>
|
||||
|
||||
/*
|
||||
* Minimum working example for int_list.c. Create a list, insert two
|
||||
* elements, print list, free list.
|
||||
*
|
||||
* Authors: Niclas Borlin (niclas@cs.umu.se)
|
||||
* Adam Dahlgren Lindstrom (dali@cs.umu.se)
|
||||
*
|
||||
* Version information:
|
||||
* v1.0 2018-01-28: First public version.
|
||||
* v1.1 2023-01-14: Added printouts at start/end of main.
|
||||
* v1.11 2024-01-16: Fix include to be with brackets, not citation marks.
|
||||
*/
|
||||
|
||||
#define VERSION "v1.11"
|
||||
#define VERSION_DATE "2024-01-16"
|
||||
|
||||
// Print cut lines before and after a call list_print_internal.
|
||||
void print_internal_with_cut_lines(const list *l, const char *desc)
|
||||
{
|
||||
// Internal counter that will be remembered between calls.
|
||||
|
||||
// Used to generate sequentially numbered -- CUT HERE -- marker lines
|
||||
// to enable automatic parsing of the output.
|
||||
static int cut = 1;
|
||||
|
||||
// Print starting marker line.
|
||||
printf("\n--- START CUT HERE %d ---\n", cut);
|
||||
|
||||
// Call the internal print function to get the actual dot code.
|
||||
list_print_internal(l, desc, 0);
|
||||
|
||||
// Print ending marker line
|
||||
printf("--- END CUT HERE %d ---\n\n", cut);
|
||||
|
||||
// Increment the cut number. Will be remembered next time the
|
||||
// function is called since cut is a static variable.
|
||||
cut++;
|
||||
}
|
||||
|
||||
// Print a message with intructions how to use the dot output. prog is
|
||||
// the name of the executable.
|
||||
void print_dot_usage(char *prog)
|
||||
{
|
||||
printf("\nGenerate dot code to visualize internal structure with GraphViz. ");
|
||||
printf("Use\n\n%s ", prog);
|
||||
printf("| sed -n '/START CUT HERE X/,/END CUT HERE X/{//!p}' | dot -Tsvg > /tmp/dot.svg\n\n");
|
||||
printf("to generate an svg file of cut X (replace X by the requested cut number).\n");
|
||||
printf("The generated file can then be visualized with\n\n");
|
||||
printf("firefox /tmp/dot.svg\n\n");
|
||||
printf("Use -Tpng to generate a .png file instead. "
|
||||
"See graphviz.org and %s for documentation.\n", __FILE__);
|
||||
|
||||
printf("\n--- Start of normal output ---\n\n");
|
||||
}
|
||||
|
||||
int main(void)
|
||||
{
|
||||
printf("%s, %s %s: Create typed integer list.\n",
|
||||
__FILE__, VERSION, VERSION_DATE);
|
||||
printf("Code base version %s (%s).\n\n", CODE_BASE_VERSION, CODE_BASE_RELEASE_DATE);
|
||||
|
||||
// Create the list.
|
||||
list *l = list_empty();
|
||||
|
||||
printf("Empty list from the outside.\n");
|
||||
list_print(l);
|
||||
print_internal_with_cut_lines(l, "Empty list showing the internals");
|
||||
|
||||
// Insert the value 5 first in the list.
|
||||
list_insert(l, 5, list_first(l));
|
||||
|
||||
printf("List from the outside after inserting one value:\n");
|
||||
list_print(l);
|
||||
print_internal_with_cut_lines(l, "List after inserting one value showing the internals");
|
||||
|
||||
// Insert the value 8 last in the list.
|
||||
list_insert(l, 8, list_end(l));
|
||||
|
||||
printf("List from the outside after inserting second value at the end:\n");
|
||||
list_print(l);
|
||||
print_internal_with_cut_lines(l, "List after inserting second value at the end showing the internals");
|
||||
|
||||
printf("List after removing first element:\n");
|
||||
list_print(l);
|
||||
print_internal_with_cut_lines(l, "List after removing first element showing the internals");
|
||||
|
||||
// Done, kill the list.
|
||||
list_kill(l);
|
||||
|
||||
printf("\nNormal exit.\n\n");
|
||||
return 0;
|
||||
}
|
||||
1145
OU3/src/int_list_array/int_list_array_test.c
Normal file
1145
OU3/src/int_list_array/int_list_array_test.c
Normal file
File diff suppressed because it is too large
Load Diff
2
OU3/src/int_queue/.gitignore
vendored
Normal file
2
OU3/src/int_queue/.gitignore
vendored
Normal file
@@ -0,0 +1,2 @@
|
||||
int_queue_example_internal
|
||||
int_queue_example
|
||||
34
OU3/src/int_queue/Makefile
Normal file
34
OU3/src/int_queue/Makefile
Normal file
@@ -0,0 +1,34 @@
|
||||
MWE = int_queue_mwe1 int_queue_mwe1i int_queue_example int_queue_example_internal
|
||||
|
||||
SRC = int_queue.c
|
||||
OBJ = $(SRC:.c=.o)
|
||||
|
||||
CC = gcc
|
||||
CFLAGS = -std=c99 -Wall -I../../include -g
|
||||
|
||||
all: mwe
|
||||
|
||||
# Minimum working examples.
|
||||
mwe: $(MWE)
|
||||
|
||||
# Object file for library
|
||||
obj: $(OBJ)
|
||||
|
||||
# Clean up
|
||||
clean:
|
||||
-rm -f $(MWE) $(OBJ)
|
||||
|
||||
int_queue_mwe1: int_queue_mwe1.c int_queue.c
|
||||
gcc -o $@ $(CFLAGS) $^
|
||||
|
||||
int_queue_mwe1i: int_queue_mwe1i.c int_queue.c
|
||||
gcc -o $@ $(CFLAGS) $^
|
||||
|
||||
int_queue_example: int_queue_example.c int_queue.c
|
||||
gcc -o $@ $(CFLAGS) $^
|
||||
|
||||
int_queue_example_internal: int_queue_example_internal.c int_queue.c
|
||||
gcc -o $@ $(CFLAGS) $^
|
||||
|
||||
memtest1: int_queue_mwe1
|
||||
valgrind --leak-check=full --show-reachable=yes ./$<
|
||||
310
OU3/src/int_queue/int_queue.c
Normal file
310
OU3/src/int_queue/int_queue.c
Normal file
@@ -0,0 +1,310 @@
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdarg.h>
|
||||
|
||||
#include <int_queue.h>
|
||||
|
||||
/*
|
||||
* Implementation of an integer queue for the "Datastructures and
|
||||
* algorithms" courses at the Department of Computing Science, Umea
|
||||
* University. The queue stores integers directly and does not use
|
||||
* dynamic memory. Thus, the clean-up function queue_kill is strictly
|
||||
* not necessary, but recommended to maintain symmetry with untyped,
|
||||
* generic queue implementations.
|
||||
*
|
||||
* Authors: Niclas Borlin (niclas@cs.umu.se)
|
||||
*
|
||||
* Version information:
|
||||
* v1.0 2025-01-10: First public version.
|
||||
*/
|
||||
|
||||
/**
|
||||
* queue_empty() - Create an empty queue.
|
||||
*
|
||||
* Returns: A new, empty, queue.
|
||||
*/
|
||||
queue queue_empty(void)
|
||||
{
|
||||
queue q;
|
||||
q.first_free_pos = 0;
|
||||
// Set all elements to zero. Not really needed, but otherwise
|
||||
// the output from print_internal becomes unreadable.
|
||||
for (int i = 0; i < sizeof(q.elements)/sizeof(q.elements[0]); i++) {
|
||||
q.elements[i] = 0;
|
||||
}
|
||||
return q;
|
||||
}
|
||||
|
||||
/**
|
||||
* queue_is_empty() - Check if a queue is empty.
|
||||
* @q: Queue to check.
|
||||
*
|
||||
* Returns: True if queue is empty, otherwise false.
|
||||
*/
|
||||
bool queue_is_empty(const queue q)
|
||||
{
|
||||
// The queue is empty if no positions are occupied with elements,
|
||||
// i.e., the first free position is zero
|
||||
return q.first_free_pos == 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* queue_enqueue() - Put a value at the end of a queue.
|
||||
* @q: Queue to manipulate.
|
||||
* @v: Value (integer) to be put in the queue.
|
||||
*
|
||||
* Returns: The modified queue.
|
||||
*/
|
||||
queue queue_enqueue(queue q, int v)
|
||||
{
|
||||
// Store value at first free position.
|
||||
q.elements[q.first_free_pos] = v;
|
||||
// Update first free position.
|
||||
q.first_free_pos++;
|
||||
|
||||
// Return the new queue.
|
||||
return q;
|
||||
}
|
||||
|
||||
/**
|
||||
* queue_dequeue() - Remove the element at the beginning of a queue.
|
||||
* @q: Queue to manipulate.
|
||||
*
|
||||
* NOTE: Undefined for an empty queue.
|
||||
*
|
||||
* Returns: The modified queue.
|
||||
*/
|
||||
queue queue_dequeue(queue q)
|
||||
{
|
||||
if (queue_is_empty(q)) {
|
||||
fprintf(stderr, "queue_dequeue: Warning: dequeue on empty queue\n");
|
||||
} else {
|
||||
// Shift all elements one step to the left
|
||||
for (int i = 0; i < q.first_free_pos - 1; i++) {
|
||||
q.elements[i] = q.elements[i + 1];
|
||||
}
|
||||
// Update the position of the first free element
|
||||
q.first_free_pos--;
|
||||
}
|
||||
return q;
|
||||
}
|
||||
|
||||
/**
|
||||
* queue_front() - Inspect the value at the front of the queue.
|
||||
* @q: Queue to inspect.
|
||||
*
|
||||
* Returns: The value at the front of the queue.
|
||||
* NOTE: The return value is undefined for an empty queue.
|
||||
*/
|
||||
int queue_front(const queue q)
|
||||
{
|
||||
if (queue_is_empty(q)) {
|
||||
fprintf(stderr, "queue_front: Warning: front on empty queue\n");
|
||||
}
|
||||
return q.elements[0];
|
||||
}
|
||||
|
||||
/**
|
||||
* queue_kill() - Destroy a given queue.
|
||||
* @q: Queue to destroy.
|
||||
*
|
||||
* Does nothing since the queue does not use any dynamic
|
||||
* memory. Included for symmetry with generic queue.h.
|
||||
*
|
||||
* Returns: Nothing.
|
||||
*/
|
||||
void queue_kill(queue q)
|
||||
{
|
||||
// Do nothing.
|
||||
}
|
||||
|
||||
/**
|
||||
* queue_print() - Iterate over the queue elements and print their values.
|
||||
* @q: Queue to inspect.
|
||||
*
|
||||
* Iterates over the queue and prints each integer.
|
||||
*
|
||||
* Returns: Nothing.
|
||||
*/
|
||||
void queue_print(const queue q)
|
||||
{
|
||||
printf("{ ");
|
||||
// Print elements from the top down.
|
||||
for (int i = 0; i < q.first_free_pos; i++) {
|
||||
printf("[%d]", q.elements[i]);
|
||||
if (i < q.first_free_pos - 1) {
|
||||
printf(", ");
|
||||
}
|
||||
}
|
||||
printf(" }\n");
|
||||
}
|
||||
|
||||
// ===========INTERNAL FUNCTIONS USED BY queue_print_internal ============
|
||||
|
||||
// The functions below output code in the dot language, used by
|
||||
// GraphViz. For documention of the dot language, see graphviz.org.
|
||||
|
||||
/**
|
||||
* indent() - Output indentation string.
|
||||
* @n: Indentation level.
|
||||
*
|
||||
* Print n tab characters.
|
||||
*
|
||||
* Returns: Nothing.
|
||||
*/
|
||||
static void indent(int n)
|
||||
{
|
||||
for (int i=0; i<n; i++) {
|
||||
printf("\t");
|
||||
}
|
||||
}
|
||||
/**
|
||||
* iprintf(...) - Indent and print.
|
||||
* @n: Indentation level
|
||||
* @...: printf arguments
|
||||
*
|
||||
* Print n tab characters and calls printf.
|
||||
*
|
||||
* Returns: Nothing.
|
||||
*/
|
||||
static void iprintf(int n, const char *fmt, ...)
|
||||
{
|
||||
// Indent...
|
||||
indent(n);
|
||||
// ...and call printf
|
||||
va_list args;
|
||||
va_start(args, fmt);
|
||||
vprintf(fmt, args);
|
||||
va_end(args);
|
||||
}
|
||||
|
||||
/**
|
||||
* print_head_node() - Print a node corresponding to the queue struct.
|
||||
* @indent_level: Indentation level.
|
||||
* @max_elems: Maximum element to print.
|
||||
* @q: Queue to inspect.
|
||||
*
|
||||
* Returns: Nothing.
|
||||
*/
|
||||
static void print_head_node(int indent_level, int max_elems, const queue q)
|
||||
{
|
||||
int elems_to_print = sizeof(q.elements)/sizeof(q.elements[0]);
|
||||
if (max_elems < elems_to_print) {
|
||||
elems_to_print = max_elems;
|
||||
}
|
||||
iprintf(indent_level, "q [shape=record label=\"first_free_pos\\n%d", q.first_free_pos);
|
||||
for (int i = 0; i < elems_to_print; i++) {
|
||||
printf("|%d\\n%d", i, q.elements[i]);
|
||||
}
|
||||
printf("\" xlabel=\"q\"]\n");
|
||||
}
|
||||
|
||||
// Create an escaped version of the input string. The most common
|
||||
// control characters - newline, horizontal tab, backslash, and double
|
||||
// quote - are replaced by their escape sequence. The returned pointer
|
||||
// must be deallocated by the caller.
|
||||
static char *escape_chars(const char *s)
|
||||
{
|
||||
int i, j;
|
||||
int escaped = 0; // The number of chars that must be escaped.
|
||||
|
||||
// Count how many chars need to be escaped, i.e. how much longer
|
||||
// the output string will be.
|
||||
for (i = escaped = 0; s[i] != '\0'; i++) {
|
||||
if (s[i] == '\n' || s[i] == '\t' || s[i] == '\\' || s[i] == '\"') {
|
||||
escaped++;
|
||||
}
|
||||
}
|
||||
// Allocate space for the escaped string. The variable i holds the input
|
||||
// length, escaped how much the string will grow.
|
||||
char *t = malloc(i + escaped + 1);
|
||||
|
||||
// Copy-and-escape loop
|
||||
for (i = j = 0; s[i] != '\0'; i++) {
|
||||
// Convert each control character by its escape sequence.
|
||||
// Non-control characters are copied as-is.
|
||||
switch (s[i]) {
|
||||
case '\n': t[i+j] = '\\'; t[i+j+1] = 'n'; j++; break;
|
||||
case '\t': t[i+j] = '\\'; t[i+j+1] = 't'; j++; break;
|
||||
case '\\': t[i+j] = '\\'; t[i+j+1] = '\\'; j++; break;
|
||||
case '\"': t[i+j] = '\\'; t[i+j+1] = '\"'; j++; break;
|
||||
default: t[i+j] = s[i]; break;
|
||||
}
|
||||
}
|
||||
// Terminal the output string
|
||||
t[i+j] = '\0';
|
||||
return t;
|
||||
}
|
||||
|
||||
// Print edge from the description to the queue.
|
||||
static void print_desc_edge(int indent_level)
|
||||
{
|
||||
iprintf(indent_level, "description -> q [style=invis] "
|
||||
"// Dummy line to place description above\n");
|
||||
}
|
||||
|
||||
/**
|
||||
* queue_print_internal() - Print the internal structure of the queue in dot format.
|
||||
* @q: Queue to inspect.
|
||||
* @desc: String with a description/state of the queue, or NULL for no description.
|
||||
* @indent_level: Indentation level, 0 for outermost.
|
||||
* @max_elems: Maximum number of elements to print.
|
||||
*
|
||||
* Iterates over the queue and outputs dot code that shows the
|
||||
* internal structure of the queue. The dot code can be visualized by
|
||||
* Graphviz.
|
||||
*
|
||||
* On linux system, the output can be parsed by the dot program, e.g.
|
||||
*
|
||||
* <queue_program> | dot -Tsvg > /tmp/dot.svg; firefox /tmp/dot.svg
|
||||
*
|
||||
* where <queue_program> is the name of the executable
|
||||
*
|
||||
* The output may also be possible to visualize online on
|
||||
* https://dreampuf.github.io/GraphvizOnline/ or google "graphviz
|
||||
* online".
|
||||
*
|
||||
* For documention of the dot language, see graphviz.org.
|
||||
*
|
||||
* Returns: Nothing.
|
||||
*/
|
||||
void queue_print_internal(const queue q, const char *desc, int indent_level, int max_elems)
|
||||
{
|
||||
static int graph_number = 0;
|
||||
graph_number++;
|
||||
int il = indent_level;
|
||||
|
||||
if (indent_level == 0) {
|
||||
// If this is the outermost datatype, start a graph...
|
||||
printf("digraph QUEUE_%d {\n", graph_number);
|
||||
|
||||
// Specify default shape and fontname
|
||||
il++;
|
||||
iprintf(il, "node [shape=rectangle fontname=\"Courier New\"]\n");
|
||||
iprintf(il, "ranksep=0.01\n");
|
||||
}
|
||||
|
||||
if (desc != NULL) {
|
||||
// Escape the string before printout
|
||||
char *escaped = escape_chars(desc);
|
||||
// Use different names on inner description nodes
|
||||
if (indent_level == 0) {
|
||||
iprintf(il, "description [label=\"%s\"]\n", escaped);
|
||||
} else {
|
||||
iprintf(il, "cluster_queue_%d_description [label=\"%s\"]\n", graph_number, escaped);
|
||||
}
|
||||
// Return the memory used by the escaped string
|
||||
free(escaped);
|
||||
}
|
||||
|
||||
// Output the head node
|
||||
print_head_node(il, max_elems, q);
|
||||
|
||||
// Output a dummy line to place the description at the top
|
||||
print_desc_edge(il);
|
||||
|
||||
if (indent_level == 0) {
|
||||
// Termination of graph
|
||||
printf("}\n");
|
||||
}
|
||||
}
|
||||
50
OU3/src/int_queue/int_queue_example.c
Normal file
50
OU3/src/int_queue/int_queue_example.c
Normal file
@@ -0,0 +1,50 @@
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include <int_queue.h>
|
||||
|
||||
/*
|
||||
* A small example that uses an integer queue.
|
||||
*
|
||||
* Author: Niclas Borlin (niclas@cs.umu.se)
|
||||
*
|
||||
* Version information:
|
||||
* v1.0 2025-01-16: First public version.
|
||||
*/
|
||||
|
||||
int main(void)
|
||||
{
|
||||
// Create the queue.
|
||||
queue q = queue_empty();
|
||||
|
||||
// The queue should be empty. Is it?
|
||||
if (queue_is_empty(q)) {
|
||||
printf("The newly created queue is empty.\n");
|
||||
} else {
|
||||
printf("The newly created queue is NOT empty.\n");
|
||||
}
|
||||
|
||||
for (int i = 11; i <= 13; i++) {
|
||||
printf("Add the value %d at the end of the queue.\n", i);
|
||||
// Put some values in the queue.
|
||||
q = queue_enqueue(q, i);
|
||||
}
|
||||
|
||||
int v = queue_front(q);
|
||||
printf("The value at the front of the queue is: %d.\n", v);
|
||||
|
||||
// Remove one element from the queue.
|
||||
printf("Remove one element.\n");
|
||||
q = queue_dequeue(q);
|
||||
|
||||
v = queue_front(q);
|
||||
printf("The value at the front of the queue is now: %d.\n", v);
|
||||
|
||||
// Kill queue. Strictly not necessary for int_queue since it
|
||||
// does not use any dynamic memory. Included for symmetry with
|
||||
// queue.c.
|
||||
queue_kill(q);
|
||||
|
||||
printf("\nNormal exit.\n\n");
|
||||
return 0;
|
||||
}
|
||||
99
OU3/src/int_queue/int_queue_example_internal.c
Normal file
99
OU3/src/int_queue/int_queue_example_internal.c
Normal file
@@ -0,0 +1,99 @@
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include <int_queue.h>
|
||||
|
||||
// Size of internal string buffer
|
||||
#define BUFLEN 400
|
||||
|
||||
/*
|
||||
* A small example that uses an integer queue.
|
||||
*
|
||||
* Generates two kinds of output:
|
||||
* 1) The 'outside' view showing the stored values only.
|
||||
* 2) The 'inside' view showing the internal organization.
|
||||
*
|
||||
* Author: Niclas Borlin (niclas@cs.umu.se)
|
||||
*
|
||||
* Version information:
|
||||
* v1.0 2025-01-24: First public version.
|
||||
*/
|
||||
|
||||
#define VERSION "v1.0"
|
||||
#define VERSION_DATE "2025-01-24"
|
||||
|
||||
// Print a message, then the queue and wait for the user to press return.
|
||||
void print_and_wait(const queue q, const char *msg)
|
||||
{
|
||||
printf("\nThe output from queue_print() shows the values stored in the queue\n");
|
||||
printf("\n%s\n", msg);
|
||||
queue_print(q);
|
||||
printf("\nPress Enter to continue...");
|
||||
getchar(); // Waits for the user to press Enter
|
||||
printf("\n");
|
||||
}
|
||||
|
||||
// Print cut lines before and after a call array_print_internal.
|
||||
void print_internal_and_wait(queue q, const char *desc)
|
||||
{
|
||||
// Print starting marker line.
|
||||
printf("Direct your web browser to:\n https://dreampuf.github.io/GraphvizOnline/?engine=dot#digraph%%20G%%20%%7B%%7D%%0A\n");
|
||||
printf("\n\n1) Copy the lines between the cut marks\n"
|
||||
"2) Paste into the left half of the browser window.\n"
|
||||
"3) The right half of the window should now show a visualization of the\n internal structure of the queue.\n");
|
||||
printf("\n--- CUT HERE ---\n\n");
|
||||
|
||||
// Call the internal print function to get the actual dot code.
|
||||
queue_print_internal(q, desc, 0, 10);
|
||||
|
||||
// Print ending marker line
|
||||
printf("\n--- END CUT HERE ---\n\n");
|
||||
|
||||
printf("\nPress Enter to continue...");
|
||||
getchar(); // Waits for the user to press Enter
|
||||
printf("\n");
|
||||
}
|
||||
|
||||
int main(void)
|
||||
{
|
||||
// Message buffer
|
||||
char buf[BUFLEN];
|
||||
|
||||
printf("%s, %s %s: Example use of a typed integer queue.\n",
|
||||
__FILE__, VERSION, VERSION_DATE);
|
||||
printf("Code base version %s (%s).\n\n", CODE_BASE_VERSION, CODE_BASE_RELEASE_DATE);
|
||||
|
||||
// Create the queue.
|
||||
queue q = queue_empty();
|
||||
|
||||
// Call print functions to show the content of the queue
|
||||
print_and_wait(q, "The queue is empty");
|
||||
print_internal_and_wait(q, "The queue is empty");
|
||||
|
||||
for (int i = 11; i <= 13; i++) {
|
||||
q = queue_enqueue(q, i);
|
||||
snprintf(buf, BUFLEN, "The value %d was added to the queue", i);
|
||||
print_and_wait(q, buf);
|
||||
print_internal_and_wait(q, buf);
|
||||
}
|
||||
|
||||
int v = queue_front(q);
|
||||
printf("The value at the front of the queue is: %d.\n", v);
|
||||
|
||||
// Remove one element from the queue.
|
||||
q = queue_dequeue(q);
|
||||
snprintf(buf, BUFLEN, "The front element was removed from the queue");
|
||||
print_and_wait(q, buf);
|
||||
print_internal_and_wait(q, buf);
|
||||
|
||||
v = queue_front(q);
|
||||
printf("The value at the front of the queue is now: %d.\n", v);
|
||||
|
||||
// Kill queue. Strictly not necessary for int_queue since it
|
||||
// does not use any dynamic memory. Included for symmetry with
|
||||
// queue.c.
|
||||
queue_kill(q);
|
||||
|
||||
printf("\nNormal exit.\n\n");
|
||||
return 0;
|
||||
}
|
||||
56
OU3/src/int_queue/int_queue_mwe1.c
Normal file
56
OU3/src/int_queue/int_queue_mwe1.c
Normal file
@@ -0,0 +1,56 @@
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include <int_queue.h>
|
||||
|
||||
/*
|
||||
* Minimum working example for int_queue.c.
|
||||
*
|
||||
* Author: Niclas Borlin (niclas@cs.umu.se)
|
||||
*
|
||||
* Version information:
|
||||
* v1.0 2025-01-10: First public version.
|
||||
*/
|
||||
|
||||
#define VERSION "v1.0"
|
||||
#define VERSION_DATE "2025-01-10"
|
||||
|
||||
int main(void)
|
||||
{
|
||||
printf("%s, %s %s: Create typed integer queue.\n",
|
||||
__FILE__, VERSION, VERSION_DATE);
|
||||
printf("Code base version %s (%s).\n\n", CODE_BASE_VERSION, CODE_BASE_RELEASE_DATE);
|
||||
|
||||
// Create the queue.
|
||||
queue q = queue_empty();
|
||||
printf("--Empty QUEUE--\n");
|
||||
queue_print(q);
|
||||
|
||||
|
||||
for (int i = 11; i <= 13; i++) {
|
||||
// Put some values in the queue.
|
||||
q = queue_enqueue(q, i);
|
||||
printf("--QUEUE during insert--\n");
|
||||
queue_print(q);
|
||||
}
|
||||
|
||||
int v = queue_front(q);
|
||||
printf("The value at the front of the queue is: %d.\n", v);
|
||||
|
||||
printf("--QUEUE after enqueue, before dequeueing--\n");
|
||||
queue_print(q);
|
||||
|
||||
// Remove one element from the queue.
|
||||
q = queue_dequeue(q);
|
||||
|
||||
printf("--QUEUE after dequeueing--\n");
|
||||
queue_print(q);
|
||||
|
||||
// Kill queue. Strictly not necessary for int_queue since it
|
||||
// does not use any dynamic memory. Included for symmetry with
|
||||
// queue.c.
|
||||
queue_kill(q);
|
||||
|
||||
printf("\nNormal exit.\n\n");
|
||||
return 0;
|
||||
}
|
||||
104
OU3/src/int_queue/int_queue_mwe1i.c
Normal file
104
OU3/src/int_queue/int_queue_mwe1i.c
Normal file
@@ -0,0 +1,104 @@
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include <int_queue.h>
|
||||
|
||||
/*
|
||||
* Minimum working example for int_queue.c.
|
||||
*
|
||||
* Author: Niclas Borlin (niclas@cs.umu.se)
|
||||
*
|
||||
* Version information:
|
||||
* v1.0 2025-01-10: Adapted from int_queue_mwe1.c.
|
||||
*/
|
||||
|
||||
#define VERSION "v1.0"
|
||||
#define VERSION_DATE "2025-01-10"
|
||||
|
||||
// Print cut lines before and after a call array_print_internal.
|
||||
void print_internal_with_cut_lines(queue s, const char *desc)
|
||||
{
|
||||
// Internal counter that will be remembered between calls.
|
||||
|
||||
// Used to generate sequentially numbered -- CUT HERE -- marker lines
|
||||
// to enable automatic parsing of the output.
|
||||
static int cut = 1;
|
||||
|
||||
// Print starting marker line.
|
||||
printf("\n--- START CUT HERE %d ---\n", cut);
|
||||
|
||||
// Call the internal print function to get the actual dot code.
|
||||
queue_print_internal(s, desc, 0, 10);
|
||||
|
||||
// Print ending marker line
|
||||
printf("--- END CUT HERE %d ---\n\n", cut);
|
||||
|
||||
// Increment the cut number. Will be remembered next time the
|
||||
// function is called since cut is a static variable.
|
||||
cut++;
|
||||
}
|
||||
|
||||
// Print a message with intructions how to use the dot output. prog is
|
||||
// the name of the executable.
|
||||
void print_dot_usage(char *prog)
|
||||
{
|
||||
printf("\nGenerate dot code to visualize internal structure with GraphViz. ");
|
||||
printf("Use\n\n%s ", prog);
|
||||
printf("| sed -n '/START CUT HERE X/,/END CUT HERE X/{//!p}' | dot -Tsvg > /tmp/dot.svg\n\n");
|
||||
printf("to generate an svg file of cut X (replace X by the requested cut number).\n");
|
||||
printf("The generated file can then be visualized with\n\n");
|
||||
printf("firefox /tmp/dot.svg\n\n");
|
||||
printf("Use -Tpng to generate a .png file instead. "
|
||||
"See graphviz.org and %s for documentation.\n", __FILE__);
|
||||
|
||||
printf("\n--- Start of normal output ---\n\n");
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
printf("%s, %s %s: Create typed integer queue.\n",
|
||||
__FILE__, VERSION, VERSION_DATE);
|
||||
printf("Code base version %s (%s).\n\n", CODE_BASE_VERSION, CODE_BASE_RELEASE_DATE);
|
||||
|
||||
print_dot_usage(argv[0]);
|
||||
|
||||
// Create the queue.
|
||||
queue q=queue_empty();
|
||||
|
||||
printf("Empty queue from the outside:\n");
|
||||
queue_print(q);
|
||||
print_internal_with_cut_lines(q, "Empty queue showing the inside");
|
||||
|
||||
for (int i = 11; i <= 13; i++) {
|
||||
// Put values in the queue.
|
||||
q = queue_enqueue(q, i);
|
||||
}
|
||||
|
||||
printf("Queue for the outside after adding 3 elements:\n");
|
||||
queue_print(q);
|
||||
print_internal_with_cut_lines(q, "Inside of the queue after adding 3 elements");
|
||||
|
||||
// Remove one element from queue.
|
||||
q = queue_dequeue(q);
|
||||
|
||||
printf("Queue for the outside after removing 1 element:\n");
|
||||
queue_print(q);
|
||||
print_internal_with_cut_lines(q, "Inside of the queue after removing 1 element");
|
||||
|
||||
// Pop remaining elements
|
||||
while (!queue_is_empty(q)) {
|
||||
q = queue_dequeue(q);
|
||||
}
|
||||
|
||||
printf("Queue from the outside after removing all elements:\n");
|
||||
queue_print(q);
|
||||
print_internal_with_cut_lines(q, "Inside of the queue after removing all elements");
|
||||
|
||||
// Kill queue. Strictly not necessary for int_queue since it
|
||||
// does not use any dynamic memory. Included for symmetry with
|
||||
// queue_mwe1.c.
|
||||
queue_kill(q);
|
||||
|
||||
printf("\nNormal exit.\n\n");
|
||||
return 0;
|
||||
}
|
||||
28
OU3/src/int_stack/Makefile
Normal file
28
OU3/src/int_stack/Makefile
Normal file
@@ -0,0 +1,28 @@
|
||||
MWE = int_stack_mwe1 int_stack_mwe1i
|
||||
|
||||
SRC = int_stack.c
|
||||
OBJ = $(SRC:.c=.o)
|
||||
|
||||
CC = gcc
|
||||
CFLAGS = -std=c99 -Wall -I../../include -g
|
||||
|
||||
all: mwe
|
||||
|
||||
# Minimum working examples.
|
||||
mwe: $(MWE)
|
||||
|
||||
# Object file for library
|
||||
obj: $(OBJ)
|
||||
|
||||
# Clean up
|
||||
clean:
|
||||
-rm -f $(MWE) $(OBJ)
|
||||
|
||||
int_stack_mwe1: int_stack_mwe1.c int_stack.c
|
||||
gcc -o $@ $(CFLAGS) $^
|
||||
|
||||
int_stack_mwe1i: int_stack_mwe1i.c int_stack.c
|
||||
gcc -o $@ $(CFLAGS) $^
|
||||
|
||||
memtest1: int_stack_mwe1
|
||||
valgrind --leak-check=full --show-reachable=yes ./$<
|
||||
316
OU3/src/int_stack/int_stack.c
Normal file
316
OU3/src/int_stack/int_stack.c
Normal file
@@ -0,0 +1,316 @@
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdarg.h>
|
||||
|
||||
#include <int_stack.h>
|
||||
|
||||
/*
|
||||
* Implementation of an integer stack for the "Datastructures and
|
||||
* algorithms" courses at the Department of Computing Science, Umea
|
||||
* University. The stack stores integers directly and does not use
|
||||
* dynamic memory. Thus, the clean-up function stack_kill is strictly
|
||||
* not necessary, but recommended to maintain symmetry with untyped,
|
||||
* generic stack implementations.
|
||||
*
|
||||
* Author: Niclas Borlin (niclas@cs.umu.se)
|
||||
*
|
||||
* Based on earlier code by: Johan Eliasson (johane@cs.umu.se)
|
||||
* Niclas Borlin (niclas@cs.umu.se)
|
||||
* Adam Dahlgren Lindstrom (dali@cs.umu.se)
|
||||
*
|
||||
* Version information:
|
||||
* v1.0 2022-03-24: First public version.
|
||||
* v1.1 2024-04-10: Added print_internal.
|
||||
* v1.2 2024-05-10: Added/updated print_internal with improved encapsulation.
|
||||
*/
|
||||
|
||||
/**
|
||||
* stack_empty() - Create an empty stack.
|
||||
*
|
||||
* Returns: An empty stack.
|
||||
*/
|
||||
stack stack_empty(void)
|
||||
{
|
||||
stack s;
|
||||
s.first_free_pos = 0;
|
||||
// Set all elements to zero. Not really needed, but otherwise
|
||||
// the output from print_internal becomes unreadable.
|
||||
for (int i = 0; i < sizeof(s.elements)/sizeof(s.elements[0]); i++) {
|
||||
s.elements[i] = 0;
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
/**
|
||||
* stack_is_empty() - Check if a stack is empty.
|
||||
* @s: Stack to check.
|
||||
*
|
||||
* Returns: True if stack is empty, otherwise false.
|
||||
*/
|
||||
bool stack_is_empty(const stack s)
|
||||
{
|
||||
// The stack is empty if no positions are occupied with elements,
|
||||
// i.e., the first free position is zero
|
||||
return s.first_free_pos == 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* stack_push() - Push a value on top of a stack.
|
||||
* @s: Stack to manipulate.
|
||||
* @v: Value (integer) to be put on the stack.
|
||||
*
|
||||
* Returns: The modified stack.
|
||||
* NOTE: After the call, the input stack should be considered invalid.
|
||||
*/
|
||||
stack stack_push(stack s, int v)
|
||||
{
|
||||
// Store value at first free position.
|
||||
s.elements[s.first_free_pos] = v;
|
||||
// Update first free position.
|
||||
s.first_free_pos++;
|
||||
|
||||
// Return the new stack.
|
||||
return s;
|
||||
}
|
||||
|
||||
/**
|
||||
* stack_pop() - Remove the element at the top of a stack.
|
||||
* @s: Stack to manipulate.
|
||||
*
|
||||
* NOTE: Undefined for an empty stack.
|
||||
*
|
||||
* Returns: The modified stack.
|
||||
* NOTE: After the call, the input stack should be considered invalid.
|
||||
*/
|
||||
stack stack_pop(stack s)
|
||||
{
|
||||
if (stack_is_empty(s)) {
|
||||
fprintf(stderr, "stack_pop: Warning: pop on empty stack\n");
|
||||
} else {
|
||||
// We only have to decrease the first free position to
|
||||
// indicate that the element that used to be on top of the
|
||||
// stack is now free for use.
|
||||
s.first_free_pos--;
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
/**
|
||||
* stack_top() - Inspect the value at the top of the stack.
|
||||
* @s: Stack to inspect.
|
||||
*
|
||||
* Returns: The integer at the top of the stack.
|
||||
* NOTE: The return value is undefined for an empty stack.
|
||||
*/
|
||||
int stack_top(const stack s)
|
||||
{
|
||||
if (stack_is_empty(s)) {
|
||||
fprintf(stderr, "stack_top: Warning: top on empty stack\n");
|
||||
}
|
||||
return s.elements[s.first_free_pos - 1];
|
||||
}
|
||||
|
||||
/**
|
||||
* stack_kill() - Destroy a given stack.
|
||||
* @s: Stack to destroy.
|
||||
*
|
||||
* Does nothing since the stack does not use any dynamic
|
||||
* memory. Included for symmetry with generic stack.h.
|
||||
*
|
||||
* Returns: Nothing.
|
||||
*/
|
||||
void stack_kill(stack s)
|
||||
{
|
||||
// Do nothing.
|
||||
}
|
||||
|
||||
/**
|
||||
* stack_print() - Iterate over the stack elements and print their values.
|
||||
* @s: Stack to inspect.
|
||||
*
|
||||
* Iterates over the stack and prints each integer.
|
||||
*
|
||||
* Returns: Nothing.
|
||||
*/
|
||||
void stack_print(const stack s)
|
||||
{
|
||||
printf("{ ");
|
||||
// Print elements from the top down.
|
||||
for (int i = s.first_free_pos - 1; i >= 0; i--) {
|
||||
printf("[%d]", s.elements[i]);
|
||||
if (i>0) {
|
||||
printf(", ");
|
||||
}
|
||||
}
|
||||
printf(" }\n");
|
||||
}
|
||||
|
||||
// ===========INTERNAL FUNCTIONS USED BY stack_print_internal ============
|
||||
|
||||
// The functions below output code in the dot language, used by
|
||||
// GraphViz. For documention of the dot language, see graphviz.org.
|
||||
|
||||
/**
|
||||
* indent() - Output indentation string.
|
||||
* @n: Indentation level.
|
||||
*
|
||||
* Print n tab characters.
|
||||
*
|
||||
* Returns: Nothing.
|
||||
*/
|
||||
static void indent(int n)
|
||||
{
|
||||
for (int i=0; i<n; i++) {
|
||||
printf("\t");
|
||||
}
|
||||
}
|
||||
/**
|
||||
* iprintf(...) - Indent and print.
|
||||
* @n: Indentation level
|
||||
* @...: printf arguments
|
||||
*
|
||||
* Print n tab characters and calls printf.
|
||||
*
|
||||
* Returns: Nothing.
|
||||
*/
|
||||
static void iprintf(int n, const char *fmt, ...)
|
||||
{
|
||||
// Indent...
|
||||
indent(n);
|
||||
// ...and call printf
|
||||
va_list args;
|
||||
va_start(args, fmt);
|
||||
vprintf(fmt, args);
|
||||
va_end(args);
|
||||
}
|
||||
|
||||
/**
|
||||
* print_head_node() - Print a node corresponding to the stack struct.
|
||||
* @indent_level: Indentation level.
|
||||
* @max_elems: Maximum element to print.
|
||||
* @s: Stack to inspect.
|
||||
*
|
||||
* Returns: Nothing.
|
||||
*/
|
||||
static void print_head_node(int indent_level, int max_elems, const stack s)
|
||||
{
|
||||
int elems_to_print = sizeof(s.elements)/sizeof(s.elements[0]);
|
||||
if (max_elems < elems_to_print) {
|
||||
elems_to_print = max_elems;
|
||||
}
|
||||
iprintf(indent_level, "s [shape=record label=\"first_free_pos\\n%d", s.first_free_pos);
|
||||
for (int i = 0; i < elems_to_print; i++) {
|
||||
printf("|%d\\n%d", i, s.elements[i]);
|
||||
}
|
||||
printf("\" xlabel=\"s\"]\n");
|
||||
}
|
||||
|
||||
// Create an escaped version of the input string. The most common
|
||||
// control characters - newline, horizontal tab, backslash, and double
|
||||
// quote - are replaced by their escape sequence. The returned pointer
|
||||
// must be deallocated by the caller.
|
||||
static char *escape_chars(const char *s)
|
||||
{
|
||||
int i, j;
|
||||
int escaped = 0; // The number of chars that must be escaped.
|
||||
|
||||
// Count how many chars need to be escaped, i.e. how much longer
|
||||
// the output string will be.
|
||||
for (i = escaped = 0; s[i] != '\0'; i++) {
|
||||
if (s[i] == '\n' || s[i] == '\t' || s[i] == '\\' || s[i] == '\"') {
|
||||
escaped++;
|
||||
}
|
||||
}
|
||||
// Allocate space for the escaped string. The variable i holds the input
|
||||
// length, escaped how much the string will grow.
|
||||
char *t = malloc(i + escaped + 1);
|
||||
|
||||
// Copy-and-escape loop
|
||||
for (i = j = 0; s[i] != '\0'; i++) {
|
||||
// Convert each control character by its escape sequence.
|
||||
// Non-control characters are copied as-is.
|
||||
switch (s[i]) {
|
||||
case '\n': t[i+j] = '\\'; t[i+j+1] = 'n'; j++; break;
|
||||
case '\t': t[i+j] = '\\'; t[i+j+1] = 't'; j++; break;
|
||||
case '\\': t[i+j] = '\\'; t[i+j+1] = '\\'; j++; break;
|
||||
case '\"': t[i+j] = '\\'; t[i+j+1] = '\"'; j++; break;
|
||||
default: t[i+j] = s[i]; break;
|
||||
}
|
||||
}
|
||||
// Terminal the output string
|
||||
t[i+j] = '\0';
|
||||
return t;
|
||||
}
|
||||
|
||||
// Print edge from the description to the stack.
|
||||
static void print_desc_edge(int indent_level)
|
||||
{
|
||||
iprintf(indent_level, "description -> s [style=invis] "
|
||||
"// Dummy line to place description above\n");
|
||||
}
|
||||
|
||||
/**
|
||||
* stack_print_internal() - Print the internal structure of the stack in dot format.
|
||||
* @s: Stack to inspect.
|
||||
* @desc: String with a description/state of the stack, or NULL for no description.
|
||||
* @indent_level: Indentation level, 0 for outermost.
|
||||
* @max_elems: Maximum number of elements to print.
|
||||
*
|
||||
* Iterates over the stack and outputs dot code that shows the
|
||||
* internal structure of the stack. The dot code can be visualized by
|
||||
* Graphviz.
|
||||
*
|
||||
* On linux system, the output can be parsed by the dot program, e.g.
|
||||
*
|
||||
* <stack_program> | dot -Tsvg > /tmp/dot.svg; firefox /tmp/dot.svg
|
||||
*
|
||||
* where <stack_program> is the name of the executable
|
||||
*
|
||||
* The output may also be possible to visualize online on
|
||||
* https://dreampuf.github.io/GraphvizOnline/ or google "graphviz
|
||||
* online".
|
||||
*
|
||||
* For documention of the dot language, see graphviz.org.
|
||||
*
|
||||
* Returns: Nothing.
|
||||
*/
|
||||
void stack_print_internal(const stack s, const char *desc, int indent_level, int max_elems)
|
||||
{
|
||||
static int graph_number = 0;
|
||||
graph_number++;
|
||||
int il = indent_level;
|
||||
|
||||
if (indent_level == 0) {
|
||||
// If this is the outermost datatype, start a graph...
|
||||
printf("digraph STACK_%d {\n", graph_number);
|
||||
|
||||
// Specify default shape and fontname
|
||||
il++;
|
||||
iprintf(il, "node [shape=rectangle fontname=\"Courier New\"]\n");
|
||||
iprintf(il, "ranksep=0.01\n");
|
||||
}
|
||||
|
||||
if (desc != NULL) {
|
||||
// Escape the string before printout
|
||||
char *escaped = escape_chars(desc);
|
||||
// Use different names on inner description nodes
|
||||
if (indent_level == 0) {
|
||||
iprintf(il, "description [label=\"%s\"]\n", escaped);
|
||||
} else {
|
||||
iprintf(il, "cluster_stack_%d_description [label=\"%s\"]\n", graph_number, escaped);
|
||||
}
|
||||
// Return the memory used by the escaped string
|
||||
free(escaped);
|
||||
}
|
||||
|
||||
// Output the head node
|
||||
print_head_node(il, max_elems, s);
|
||||
|
||||
// Output a dummy line to place the description at the top
|
||||
print_desc_edge(il);
|
||||
|
||||
if (indent_level == 0) {
|
||||
// Termination of graph
|
||||
printf("}\n");
|
||||
}
|
||||
}
|
||||
48
OU3/src/int_stack/int_stack_mwe1.c
Normal file
48
OU3/src/int_stack/int_stack_mwe1.c
Normal file
@@ -0,0 +1,48 @@
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include <int_stack.h>
|
||||
|
||||
/*
|
||||
* Minimum working example for int_stack.c.
|
||||
*
|
||||
* Author: Niclas Borlin (niclas@cs.umu.se)
|
||||
*
|
||||
* Version information:
|
||||
* v1.0 2022-03-24: First public version.
|
||||
* v1.1 2023-01-14: Added printouts at start/end of main.
|
||||
*/
|
||||
|
||||
#define VERSION "v1.1"
|
||||
#define VERSION_DATE "2023-01-14"
|
||||
|
||||
int main(void)
|
||||
{
|
||||
printf("%s, %s %s: Create typed integer stack.\n", __FILE__, VERSION, VERSION_DATE);
|
||||
printf("Code base version %s (%s).\n\n", CODE_BASE_VERSION, CODE_BASE_RELEASE_DATE);
|
||||
|
||||
// Create the stack.
|
||||
stack s=stack_empty();
|
||||
|
||||
for (int i=1; i<=3; i++) {
|
||||
// Push value on stack.
|
||||
s = stack_push(s, i);
|
||||
}
|
||||
|
||||
printf("--STACK before popping--\n");
|
||||
stack_print(s);
|
||||
|
||||
// Pop element from stack.
|
||||
s=stack_pop(s);
|
||||
|
||||
printf("--STACK after popping--\n");
|
||||
stack_print(s);
|
||||
|
||||
// Kill stack. Strictly not necessary for int_stack since it
|
||||
// does not use any dynamic memory. Included for symmetry with
|
||||
// stack.c.
|
||||
stack_kill(s);
|
||||
|
||||
printf("\nNormal exit.\n\n");
|
||||
return 0;
|
||||
}
|
||||
103
OU3/src/int_stack/int_stack_mwe1i.c
Normal file
103
OU3/src/int_stack/int_stack_mwe1i.c
Normal file
@@ -0,0 +1,103 @@
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include <int_stack.h>
|
||||
|
||||
/*
|
||||
* Minimum working example for int_stack.c.
|
||||
*
|
||||
* Author: Niclas Borlin (niclas@cs.umu.se)
|
||||
*
|
||||
* Version information:
|
||||
* v1.0 2024-04-10: Adapted from int_stack_mwe1.c.
|
||||
*/
|
||||
|
||||
#define VERSION "v1.0"
|
||||
#define VERSION_DATE "2024-04-10"
|
||||
|
||||
// Print cut lines before and after a call array_print_internal.
|
||||
void print_internal_with_cut_lines(stack s, const char *desc)
|
||||
{
|
||||
// Internal counter that will be remembered between calls.
|
||||
|
||||
// Used to generate sequentially numbered -- CUT HERE -- marker lines
|
||||
// to enable automatic parsing of the output.
|
||||
static int cut = 1;
|
||||
|
||||
// Print starting marker line.
|
||||
printf("\n--- START CUT HERE %d ---\n", cut);
|
||||
|
||||
// Call the internal print function to get the actual dot code.
|
||||
stack_print_internal(s, desc, 0, 10);
|
||||
|
||||
// Print ending marker line
|
||||
printf("--- END CUT HERE %d ---\n\n", cut);
|
||||
|
||||
// Increment the cut number. Will be remembered next time the
|
||||
// function is called since cut is a static variable.
|
||||
cut++;
|
||||
}
|
||||
|
||||
// Print a message with intructions how to use the dot output. prog is
|
||||
// the name of the executable.
|
||||
void print_dot_usage(char *prog)
|
||||
{
|
||||
printf("\nGenerate dot code to visualize internal structure with GraphViz. ");
|
||||
printf("Use\n\n%s ", prog);
|
||||
printf("| sed -n '/START CUT HERE X/,/END CUT HERE X/{//!p}' | dot -Tsvg > /tmp/dot.svg\n\n");
|
||||
printf("to generate an svg file of cut X (replace X by the requested cut number).\n");
|
||||
printf("The generated file can then be visualized with\n\n");
|
||||
printf("firefox /tmp/dot.svg\n\n");
|
||||
printf("Use -Tpng to generate a .png file instead. "
|
||||
"See graphviz.org and %s for documentation.\n", __FILE__);
|
||||
|
||||
printf("\n--- Start of normal output ---\n\n");
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
printf("%s, %s %s: Create typed integer stack.\n", __FILE__, VERSION, VERSION_DATE);
|
||||
printf("Code base version %s (%s).\n\n", CODE_BASE_VERSION, CODE_BASE_RELEASE_DATE);
|
||||
|
||||
print_dot_usage(argv[0]);
|
||||
|
||||
// Create the stack.
|
||||
stack s=stack_empty();
|
||||
|
||||
printf("Empty stack from the outside:\n");
|
||||
stack_print(s);
|
||||
print_internal_with_cut_lines(s, "Empty stack showing the inside");
|
||||
|
||||
for (int i=1; i<=3; i++) {
|
||||
// Push value on stack.
|
||||
s = stack_push(s, 10 + i);
|
||||
}
|
||||
|
||||
printf("Stack for the outside after pushing 3 elements:\n");
|
||||
stack_print(s);
|
||||
print_internal_with_cut_lines(s, "Inside of the stack after pushing 3 elements");
|
||||
|
||||
// Pop one element from stack.
|
||||
s = stack_pop(s);
|
||||
|
||||
printf("Stack for the outside after popping 1 element:\n");
|
||||
stack_print(s);
|
||||
print_internal_with_cut_lines(s, "Inside of the stack after popping 1 element");
|
||||
|
||||
// Pop remaining elements
|
||||
while (!stack_is_empty(s)) {
|
||||
s = stack_pop(s);
|
||||
}
|
||||
|
||||
printf("Stack from the outside after popping all elements:\n");
|
||||
stack_print(s);
|
||||
print_internal_with_cut_lines(s, "Inside of the stack after popping all elements");
|
||||
|
||||
// Kill stack. Strictly not necessary for int_stack since it
|
||||
// does not use any dynamic memory. Included for symmetry with
|
||||
// stack_mwe1.c.
|
||||
stack_kill(s);
|
||||
|
||||
printf("\nNormal exit.\n\n");
|
||||
return 0;
|
||||
}
|
||||
68
OU3/src/list/Makefile
Normal file
68
OU3/src/list/Makefile
Normal file
@@ -0,0 +1,68 @@
|
||||
MWE = list_mwe1 list_mwe2 list_mwe1i list_mwe2i
|
||||
TEST = list_test1 list_test2
|
||||
|
||||
SRC = list.c
|
||||
OBJ = $(SRC:.c=.o)
|
||||
|
||||
CC = gcc
|
||||
CFLAGS = -std=c99 -Wall -I../../include -g
|
||||
|
||||
all: mwe test
|
||||
|
||||
# Minimum working examples.
|
||||
mwe: $(MWE)
|
||||
|
||||
# Object file for library
|
||||
obj: $(OBJ)
|
||||
|
||||
test: list_test1 list_test2
|
||||
|
||||
# Clean up
|
||||
clean:
|
||||
-rm -f $(MWE) $(TEST) $(OBJ)
|
||||
|
||||
list_mwe1: list_mwe1.c list.c
|
||||
gcc -o $@ $(CFLAGS) $^
|
||||
|
||||
list_mwe2: list_mwe2.c list.c
|
||||
gcc -o $@ $(CFLAGS) $^
|
||||
|
||||
list_mwe1i: list_mwe1i.c list.c
|
||||
gcc -o $@ $(CFLAGS) $^
|
||||
|
||||
list_mwe2i: list_mwe2i.c list.c
|
||||
gcc -o $@ $(CFLAGS) $^
|
||||
|
||||
memtest_mwe1: list_mwe1
|
||||
valgrind --leak-check=full --show-reachable=yes ./$<
|
||||
|
||||
memtest_mwe2: list_mwe2
|
||||
valgrind --leak-check=full --show-reachable=yes ./$<
|
||||
|
||||
memtest_mwe1i: list_mwe1i
|
||||
valgrind --leak-check=full --show-reachable=yes ./$<
|
||||
|
||||
memtest_mwe2i: list_mwe2i
|
||||
valgrind --leak-check=full --show-reachable=yes ./$<
|
||||
|
||||
list_test1: list_test1.c list.c
|
||||
gcc -o $@ $(CFLAGS) $^
|
||||
|
||||
list_test2: list_test2.c list.c
|
||||
gcc -o $@ $(CFLAGS) $^
|
||||
|
||||
test_run1: list_test1
|
||||
# Run the test
|
||||
./$<
|
||||
|
||||
test_run2: list_test2
|
||||
# Run the test
|
||||
./$<
|
||||
|
||||
memtest_test1: list_test1
|
||||
valgrind --leak-check=full --show-reachable=yes ./$<
|
||||
|
||||
memtest_test2: list_test2
|
||||
valgrind --leak-check=full --show-reachable=yes ./$<
|
||||
|
||||
memtests: memtest_mwe1 memtest_mwe2 memtest_mwe1i memtest_mwe2i memtest_test1 memtest_test2
|
||||
111
OU3/src/list/README.md
Normal file
111
OU3/src/list/README.md
Normal file
@@ -0,0 +1,111 @@
|
||||
# Dubbellänkad lista
|
||||
En två-cellsimplementation av ADT:n _Lista_.
|
||||
|
||||
## Minneshantering
|
||||
|
||||
Följande kodsnutt kommer skapa en lista med ett element som har värdet 5. I
|
||||
detta exempel så allokeras inget minne dynamiskt, därför behöver vi inte heller
|
||||
ta hand om något minne efter vi använt klart listan.
|
||||
|
||||
```c
|
||||
int val = 5;
|
||||
list *l = list_empty(NULL);
|
||||
list_insert(l, &val, list_first(l));
|
||||
```
|
||||
|
||||
Vi kan skriva om ovan exempel så att minne allokeras på heapen, men då måste vi
|
||||
manuellt komma ihåg att avallokera detta minne efter användning.
|
||||
|
||||
```c
|
||||
int *val = malloc(sizeof(int));
|
||||
*val = 5;
|
||||
|
||||
list *l = list_empty(NULL);
|
||||
list_insert(l, val, list_first(l));
|
||||
...
|
||||
list_kill(l);
|
||||
free(val);
|
||||
```
|
||||
|
||||
Om vi vill allokera minne dynamiskt, men vill att listan själva ska hantera
|
||||
avallokering av minne (e.g. vid `list_remove`) så behöver vi ange en
|
||||
minneshanteringsfunktion:
|
||||
```c
|
||||
int *val = malloc(sizeof(int));
|
||||
*val = 5;
|
||||
|
||||
list *l = list_empty(free);
|
||||
list_insert(l, val, list_first(l));
|
||||
...
|
||||
list_kill(l);
|
||||
```
|
||||
|
||||
I exemplet ovan kommer `list_kill(l)` att anropa `free` för varje element i
|
||||
listan, och därmed sköta minneshanteringen åt oss.
|
||||
|
||||
### Egen free-funktion
|
||||
|
||||
Det är också möjligt att definiera en egen funktion som ska sköta
|
||||
avallokeringen. Till exempel kanske vi vill skriva ut varje värde som
|
||||
avallokeras i debugging-syfte. Då kan vi skriva en enkel wrapper-funktion för
|
||||
`free` på följande sätt:
|
||||
|
||||
```c
|
||||
static void free_and_print(void *data)
|
||||
{
|
||||
printf("Free'ing [%d]\n", *(int*)data);
|
||||
free(data);
|
||||
}
|
||||
```
|
||||
|
||||
Denna funktion skulle då kunna användas på följande sätt:
|
||||
|
||||
```c
|
||||
list *l = list_empty(free_and_print);
|
||||
int *val = malloc(sizeof(int));
|
||||
*val = 5;
|
||||
list_insert(l, val, list_first(l));
|
||||
list_kill(l);
|
||||
```
|
||||
|
||||
Denna kodsnutt skulle då skriva ut `Free'ing [5]`.
|
||||
|
||||
|
||||
## Utskrift
|
||||
|
||||
För att skriva ut listan, t.ex. vid debugging eller för att visa innehållet för
|
||||
en användare så kan vi definiera en egen funktion för att presentera tillståndet
|
||||
hos datatypen. Det enklaste fallet är om vi enbart vill skriva ut, e.g., en
|
||||
lista med heltal där varje värde hamnar på en egen rad:
|
||||
|
||||
```c
|
||||
static void print_ints(const void *data)
|
||||
{
|
||||
printf("%d\n", *(int*)data);
|
||||
}
|
||||
```
|
||||
|
||||
Notera att inparametern är av typen `const`. `const` indikerar att pekaren bara
|
||||
får läsas, ej manipuleras. Vi vill inte att en print-funktion ska kunna ändra på
|
||||
listans innehåll.
|
||||
|
||||
`print_ints` kan nu användas på följande sätt:
|
||||
|
||||
```c
|
||||
list *l = list_empty(free);
|
||||
int *val = malloc(sizeof(int));
|
||||
*val = 5;
|
||||
list_insert(l, val, list_first(l));
|
||||
|
||||
list_print(l, print_ints);
|
||||
```
|
||||
|
||||
Detta exempel skulle skriva ut `5` på en egen rad.
|
||||
|
||||
|
||||
# Minimal working example
|
||||
|
||||
Mycket av det som behandlats ovan sammanfattas i följande minimal working
|
||||
example:
|
||||
|
||||
Se [list_mwe1.c](list_mwe1.c) och [list_mwe2.c](list_mwe2.c).
|
||||
620
OU3/src/list/list.c
Normal file
620
OU3/src/list/list.c
Normal file
@@ -0,0 +1,620 @@
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdarg.h>
|
||||
|
||||
#include <list.h>
|
||||
|
||||
/*
|
||||
* Implementation of a generic, undirected list for the
|
||||
* "Datastructures and algorithms" courses at the Department of
|
||||
* Computing Science, Umea University.
|
||||
*
|
||||
* Authors: Niclas Borlin (niclas@cs.umu.se)
|
||||
* Adam Dahlgren Lindstrom (dali@cs.umu.se)
|
||||
* Lars Karlsson (larsk@cs.umu.se)
|
||||
*
|
||||
* Based on earlier code by: Johan Eliasson (johane@cs.umu.se).
|
||||
*
|
||||
* Version information:
|
||||
* v1.0 2018-01-28: First public version.
|
||||
* v1.1 2023-01-19: Added list_pos_equal and list_pos_is_valid functions.
|
||||
* v1.2 2023-01-20: Renamed list_pos_equal to list_pos_are_equal.
|
||||
* v1.3 2023-02-25: Renamed list header fields head and tail.
|
||||
* v1.4 2023-03-23: Renamed list_pos_are_equal to list_pos_is_equal.
|
||||
* v1.5 2024-03-13: Renamed free_* stuff to kill_*. Converted to 4-tabs.
|
||||
* v2.0 2024-03-14: Added list_print_internal to output dot code for visualization.
|
||||
* v2.1 2024-05-10: Added/updated print_internal with improved encapsulation.
|
||||
*/
|
||||
|
||||
// ===========INTERNAL DATA TYPES ============
|
||||
|
||||
/*
|
||||
* The list elements are implemented as two-cells with forward and
|
||||
* backward links and a void * for the value. The list uses two border
|
||||
* cells at the start and end of the list.
|
||||
*/
|
||||
typedef struct cell {
|
||||
struct cell *next;
|
||||
struct cell *prev;
|
||||
void *val;
|
||||
} cell;
|
||||
|
||||
struct list {
|
||||
cell *head;
|
||||
cell *tail;
|
||||
kill_function kill_func;
|
||||
};
|
||||
|
||||
// ===========INTERNAL FUNCTION IMPLEMENTATIONS============
|
||||
|
||||
/**
|
||||
* list_empty() - Create an empty list.
|
||||
* @kill_func: A pointer to a function (or NULL) to be called to
|
||||
* de-allocate memory on remove/kill.
|
||||
*
|
||||
* Returns: A pointer to the new list.
|
||||
*/
|
||||
list *list_empty(kill_function kill_func)
|
||||
{
|
||||
// Allocate memory for the list head.
|
||||
list *l = calloc(1, sizeof(list));
|
||||
|
||||
// Allocate memory for the border cells.
|
||||
l->head = calloc(1, sizeof(cell));
|
||||
l->tail = calloc(1, sizeof(cell));
|
||||
|
||||
// Set consistent links between border elements.
|
||||
l->head->next = l->tail;
|
||||
l->tail->prev = l->head;
|
||||
|
||||
// Store the kill function.
|
||||
l->kill_func = kill_func;
|
||||
|
||||
return l;
|
||||
}
|
||||
|
||||
/**
|
||||
* list_is_empty() - Check if a list is empty.
|
||||
* @l: List to check.
|
||||
*
|
||||
* Returns: True if the list is empty, otherwise false.
|
||||
*/
|
||||
bool list_is_empty(const list *l)
|
||||
{
|
||||
// List is empty if there are no cells between head and tail.
|
||||
return (l->head->next == l->tail);
|
||||
}
|
||||
|
||||
/**
|
||||
* list_first() - Return the first position of a list, i.e. the
|
||||
* position of the first element in the list.
|
||||
* @l: List to inspect.
|
||||
*
|
||||
* Returns: The first position in the given list.
|
||||
*/
|
||||
list_pos list_first(const list *l)
|
||||
{
|
||||
// First position is position of first element.
|
||||
return l->head->next;
|
||||
}
|
||||
|
||||
/**
|
||||
* list_end() - Return the last position of a list, i.e. the position
|
||||
* after the last element in the list.
|
||||
* @l: List to inspect.
|
||||
*
|
||||
* Returns: The last position in the given list.
|
||||
*/
|
||||
list_pos list_end(const list *l)
|
||||
{
|
||||
// Last position is position *after* last element.
|
||||
return l->tail;
|
||||
}
|
||||
|
||||
/**
|
||||
* list_next() - Return the next position in a list.
|
||||
* @l: List to inspect.
|
||||
* @p: Any valid position except the last in the list.
|
||||
*
|
||||
* Returns: The position in the list after the given position.
|
||||
* NOTE: The return value is undefined for the last position.
|
||||
*/
|
||||
list_pos list_next(const list *l, const list_pos p)
|
||||
{
|
||||
if (list_pos_is_equal(l, p, list_end(l))) {
|
||||
// This should really throw an error.
|
||||
fprintf(stderr,"list_next: Warning: Trying to navigate past end of list!");
|
||||
}
|
||||
return p->next;
|
||||
}
|
||||
|
||||
/**
|
||||
* list_prev() - Return the previous position in a list.
|
||||
* @l: List to inspect.
|
||||
* @p: Any valid position except the first in the list.
|
||||
*
|
||||
* Returns: The position in the list before the given position.
|
||||
* NOTE: The return value is undefined for the first position.
|
||||
*/
|
||||
list_pos list_prev(const list *l, const list_pos p)
|
||||
{
|
||||
if (list_pos_is_equal(l, p, list_first(l))) {
|
||||
// This should really throw an error.
|
||||
fprintf(stderr,"list_prev: Warning: Trying to navigate "
|
||||
"past beginning of list!\n");
|
||||
}
|
||||
return p->prev;
|
||||
}
|
||||
|
||||
/**
|
||||
* list_inspect() - Return the value of the element at a given
|
||||
* position in a list.
|
||||
* @l: List to inspect.
|
||||
* @p: Any valid position in the list, except the last.
|
||||
*
|
||||
* Returns: The value at the given position as a void pointer.
|
||||
* NOTE: The return value is undefined for the last position.
|
||||
*/
|
||||
void *list_inspect(const list *l, const list_pos p)
|
||||
{
|
||||
if (list_pos_is_equal(l, p, list_end(l))) {
|
||||
// This should really throw an error.
|
||||
fprintf(stderr,"list_inspect: Warning: Trying to inspect "
|
||||
"position at end of list!\n");
|
||||
}
|
||||
return p->val;
|
||||
}
|
||||
|
||||
/**
|
||||
* list_insert() - Insert a new element with a given value into a list.
|
||||
* @l: List to manipulate.
|
||||
* @v: Value (pointer) to be inserted into the list.
|
||||
* @p: Position in the list before which the value should be inserted.
|
||||
*
|
||||
* Creates a new element and inserts it into the list before p.
|
||||
* Stores data in the new element.
|
||||
*
|
||||
* Returns: The position of the newly created element.
|
||||
*/
|
||||
list_pos list_insert(list *l, void *v, const list_pos p)
|
||||
{
|
||||
// Allocate memory for a new cell.
|
||||
list_pos e = malloc(sizeof(cell));
|
||||
|
||||
// Store the value.
|
||||
e->val = v;
|
||||
// Add links to/from the new cell.
|
||||
e->next = p;
|
||||
e->prev = p->prev;
|
||||
p->prev = e;
|
||||
e->prev->next = e;
|
||||
|
||||
// Return the position of the new cell.
|
||||
return e;
|
||||
}
|
||||
|
||||
/**
|
||||
* list_remove() - Remove an element from a list.
|
||||
* @l: List to manipulate.
|
||||
* @p: Position in the list of the element to remove.
|
||||
*
|
||||
* Removes the element at position p from the list. If a kill_func
|
||||
* was registered at list creation, calls it to deallocate the memory
|
||||
* held by the element value.
|
||||
*
|
||||
* Returns: The position after the removed element.
|
||||
*/
|
||||
list_pos list_remove(list *l, const list_pos p)
|
||||
{
|
||||
// Remember return position.
|
||||
list_pos next_pos = p->next;
|
||||
// Link past this element.
|
||||
p->prev->next = p->next;
|
||||
p->next->prev = p->prev;
|
||||
|
||||
// Call kill_func if registered.
|
||||
if (l->kill_func != NULL) {
|
||||
// Return any user-allocated memory for the value.
|
||||
l->kill_func(p->val);
|
||||
}
|
||||
// Return the memory allocated to the cell itself.
|
||||
free(p);
|
||||
// Return the position of the next element.
|
||||
return next_pos;
|
||||
}
|
||||
|
||||
/**
|
||||
* list_kill() - Destroy a given list.
|
||||
* @l: List to destroy.
|
||||
*
|
||||
* Returns all dynamic memory used by the list and its elements. If a
|
||||
* kill_func was registered at list creation, also calls it for each
|
||||
* element to return any user-allocated memory occupied by the element
|
||||
* values.
|
||||
*
|
||||
* Returns: Nothing.
|
||||
*/
|
||||
void list_kill(list *l)
|
||||
{
|
||||
// Use public functions to traverse the list.
|
||||
|
||||
// Start with the first element (will be defined even for an
|
||||
// empty list).
|
||||
list_pos p = list_first(l);
|
||||
|
||||
// Remove first element until list is empty.
|
||||
while (!list_is_empty(l)) {
|
||||
p = list_remove(l, p);
|
||||
}
|
||||
|
||||
// Free border elements and the list head.
|
||||
free(l->head);
|
||||
free(l->tail);
|
||||
free(l);
|
||||
}
|
||||
|
||||
/**
|
||||
* list_print() - Iterate over the list element and print their values.
|
||||
* @l: List to inspect.
|
||||
* @print_func: Function called for each element.
|
||||
*
|
||||
* Iterates over the list and calls print_func with the value stored
|
||||
* in each element.
|
||||
*
|
||||
* Returns: Nothing.
|
||||
*/
|
||||
void list_print(const list *l, inspect_callback print_func)
|
||||
{
|
||||
// Start at the beginning of the list.
|
||||
list_pos p = list_first(l);
|
||||
|
||||
printf("( ");
|
||||
while (!list_pos_is_equal(l, p, list_end(l))) {
|
||||
// Call print_func with the element value at the
|
||||
// current position.
|
||||
print_func(list_inspect(l, p));
|
||||
// Advance to next position.
|
||||
p = list_next(l, p);
|
||||
// Print separator unless at end.
|
||||
if (!list_pos_is_equal(l, p, list_end(l))) {
|
||||
printf(", ");
|
||||
}
|
||||
}
|
||||
printf(" )\n");
|
||||
}
|
||||
|
||||
/**
|
||||
* list_pos_is_equal() - Return true if two positions in a list are equal.
|
||||
* @l: List to inspect.
|
||||
* @p1: First position to compare.
|
||||
* @p2: Second position to compare.
|
||||
*
|
||||
* Returns: True if p1 and p2 refer to the same position in l, otherwise False.
|
||||
* NOTE: The result is defined only if p1 and p2 are valid positions in l.
|
||||
*/
|
||||
bool list_pos_is_equal(const list *l, const list_pos p1, const list_pos p2)
|
||||
{
|
||||
// Since we don't need to check whether p1 or p2 are valid, we
|
||||
// only need to compare them directly.
|
||||
return p1 == p2;
|
||||
}
|
||||
|
||||
/**
|
||||
* list_pos_is_valid() - Return true for a valid position in a list
|
||||
* @l: List to inspect.
|
||||
* @p: Any position.
|
||||
*
|
||||
* Returns: True if p is a valid position in the list, otherwise false.
|
||||
*/
|
||||
bool list_pos_is_valid(const list *l, const list_pos p)
|
||||
{
|
||||
// Iterate over all positions in l.
|
||||
list_pos q = list_first(l);
|
||||
while (!list_pos_is_equal(l, q, list_end(l))) {
|
||||
if (list_pos_is_equal(l, p, q)) {
|
||||
// We found the position in the list.
|
||||
return true;
|
||||
}
|
||||
// Advance to the next valid position,
|
||||
q = list_next(l, q);
|
||||
}
|
||||
// p was not among valid positions in l.
|
||||
return false;
|
||||
}
|
||||
|
||||
// ===========INTERNAL FUNCTIONS USED BY dlist_print_internal ============
|
||||
|
||||
// The functions below output code in the dot language, used by
|
||||
// GraphViz. For documention of the dot language, see graphviz.org.
|
||||
|
||||
/**
|
||||
* indent() - Output indentation string.
|
||||
* @n: Indentation level.
|
||||
*
|
||||
* Print n tab characters.
|
||||
*
|
||||
* Returns: Nothing.
|
||||
*/
|
||||
static void indent(int n)
|
||||
{
|
||||
for (int i=0; i<n; i++) {
|
||||
printf("\t");
|
||||
}
|
||||
}
|
||||
/**
|
||||
* iprintf(...) - Indent and print.
|
||||
* @n: Indentation level
|
||||
* @...: printf arguments
|
||||
*
|
||||
* Print n tab characters and calls printf.
|
||||
*
|
||||
* Returns: Nothing.
|
||||
*/
|
||||
static void iprintf(int n, const char *fmt, ...)
|
||||
{
|
||||
// Indent...
|
||||
indent(n);
|
||||
// ...and call printf
|
||||
va_list args;
|
||||
va_start(args, fmt);
|
||||
vprintf(fmt, args);
|
||||
va_end(args);
|
||||
}
|
||||
|
||||
/**
|
||||
* print_edge() - Print a edge between two addresses.
|
||||
* @from: The address of the start of the edge. Should be non-NULL.
|
||||
* @to: The address of the destination for the edge, including NULL.
|
||||
* @port: The name of the port on the source node, or NULL.
|
||||
* @label: The label for the edge, or NULL.
|
||||
* @options: A string with other edge options, or NULL.
|
||||
*
|
||||
* Print an edge from port PORT on node FROM to TO with label
|
||||
* LABEL. If to is NULL, the destination is the NULL node, otherwise a
|
||||
* memory node. If the port is NULL, the edge starts at the node, not
|
||||
* a specific port on it. If label is NULL, no label is used. The
|
||||
* options string, if non-NULL, is printed before the label.
|
||||
*
|
||||
* Returns: Nothing.
|
||||
*/
|
||||
static void print_edge(int indent_level, const void *from, const void *to, const char *port,
|
||||
const char *label, const char *options)
|
||||
{
|
||||
indent(indent_level);
|
||||
if (port) {
|
||||
printf("m%04lx:%s -> ", PTR2ADDR(from), port);
|
||||
} else {
|
||||
printf("m%04lx -> ", PTR2ADDR(from));
|
||||
}
|
||||
if (to == NULL) {
|
||||
printf("NULL");
|
||||
} else {
|
||||
printf("m%04lx", PTR2ADDR(to));
|
||||
}
|
||||
printf(" [");
|
||||
if (options != NULL) {
|
||||
printf("%s", options);
|
||||
}
|
||||
if (label != NULL) {
|
||||
printf(" label=\"%s\"",label);
|
||||
}
|
||||
printf("]\n");
|
||||
}
|
||||
|
||||
/**
|
||||
* print_head_node() - Print a node corresponding to the list head struct.
|
||||
* @indent_level: Indentation level.
|
||||
* @l: List to inspect.
|
||||
*
|
||||
* Returns: Nothing.
|
||||
*/
|
||||
static void print_head_node(int indent_level, const list *l)
|
||||
{
|
||||
iprintf(indent_level, "m%04lx [shape=record "
|
||||
"label=\"kill\\n%04lx|<h>head\\n%04lx|<t>tail\\n%04lx\" xlabel=\"%04lx\"]\n",
|
||||
PTR2ADDR(l), PTR2ADDR(l->kill_func), PTR2ADDR(l->head), PTR2ADDR(l->tail),
|
||||
PTR2ADDR(l));
|
||||
}
|
||||
|
||||
// Print edges from the list head to the head and tail cells.
|
||||
static void print_head_edges(int indent_level, const list *l)
|
||||
{
|
||||
print_edge(indent_level, l, l->head, "h", "head", NULL);
|
||||
print_edge(indent_level, l, l->tail, "t", "tail", NULL);
|
||||
}
|
||||
|
||||
// Print a node corresponding to the cell at position p.
|
||||
static void print_elem_node(int indent_level, const list_pos p)
|
||||
{
|
||||
iprintf(indent_level, "m%04lx [shape=record "
|
||||
"label=\"<v>val\\n%04lx|<n>next\\n%04lx|<p>prev\\n%04lx\" xlabel=\"%04lx\"]\n",
|
||||
PTR2ADDR(p), PTR2ADDR(p->val), PTR2ADDR(p->next), PTR2ADDR(p->prev), PTR2ADDR(p));
|
||||
}
|
||||
|
||||
// Print edges from the cell at position p to the next and previous
|
||||
// cells and the value. The value pointer is red, the others are
|
||||
// black. If the list owns the memory, the edge is solid, otherwise
|
||||
// dashed.
|
||||
static void print_elem_edges(int indent_level, const list *l, const list_pos p)
|
||||
{
|
||||
print_edge(indent_level, p, p->next, "n", "next", NULL);
|
||||
print_edge(indent_level, p, p->prev, "p", "prev", NULL);
|
||||
|
||||
// Ignore val ptr for head and tail nodes.
|
||||
if (p == l->head || p == l->tail) {
|
||||
return;
|
||||
}
|
||||
if (l->kill_func) {
|
||||
print_edge(indent_level, p, p->val, "v", "val", "color=red");
|
||||
} else {
|
||||
print_edge(indent_level, p, p->val, "v", "val", "color=red style=dashed");
|
||||
}
|
||||
}
|
||||
|
||||
// Print the node for the memory block at p using the user-supplied
|
||||
// print_func to print the label.
|
||||
static void print_value_node(int indent_level, const void *p, inspect_callback print_func)
|
||||
{
|
||||
iprintf(indent_level, "m%04lx [label=\"", PTR2ADDR(p));
|
||||
if (print_func != NULL) {
|
||||
print_func(p);
|
||||
}
|
||||
printf("\" xlabel=\"%04lx\"]\n", PTR2ADDR(p));
|
||||
}
|
||||
|
||||
// Create an escaped version of the input string. The most common
|
||||
// control characters - newline, horizontal tab, backslash, and double
|
||||
// quote - are replaced by their escape sequence. The returned pointer
|
||||
// must be deallocated by the caller.
|
||||
static char *escape_chars(const char *s)
|
||||
{
|
||||
int i, j;
|
||||
int escaped = 0; // The number of chars that must be escaped.
|
||||
|
||||
// Count how many chars need to be escaped, i.e. how much longer
|
||||
// the output string will be.
|
||||
for (i = escaped = 0; s[i] != '\0'; i++) {
|
||||
if (s[i] == '\n' || s[i] == '\t' || s[i] == '\\' || s[i] == '\"') {
|
||||
escaped++;
|
||||
}
|
||||
}
|
||||
// Allocate space for the escaped string. The variable i holds the input
|
||||
// length, escaped how much the string will grow.
|
||||
char *t = malloc(i + escaped + 1);
|
||||
|
||||
// Copy-and-escape loop
|
||||
for (i = j = 0; s[i] != '\0'; i++) {
|
||||
// Convert each control character by its escape sequence.
|
||||
// Non-control characters are copied as-is.
|
||||
switch (s[i]) {
|
||||
case '\n': t[i+j] = '\\'; t[i+j+1] = 'n'; j++; break;
|
||||
case '\t': t[i+j] = '\\'; t[i+j+1] = 't'; j++; break;
|
||||
case '\\': t[i+j] = '\\'; t[i+j+1] = '\\'; j++; break;
|
||||
case '\"': t[i+j] = '\\'; t[i+j+1] = '\"'; j++; break;
|
||||
default: t[i+j] = s[i]; break;
|
||||
}
|
||||
}
|
||||
// Terminal the output string
|
||||
t[i+j] = '\0';
|
||||
return t;
|
||||
}
|
||||
|
||||
/**
|
||||
* list_print_internal() - Print the lists internal structure in dot format.
|
||||
* @l: List to inspect.
|
||||
* @print_func: Function called for each element value.
|
||||
* @desc: String with a description/state of the list, or NULL for no description.
|
||||
* @indent_level: Indentation level, 0 for outermost
|
||||
*
|
||||
* Iterates over the list and outputs dot code that shows the internal
|
||||
* structure of the list. The dot code can be visualized by
|
||||
* Graphviz.
|
||||
*
|
||||
* On linux system, the output can be parsed by the dot program, e.g.
|
||||
*
|
||||
* <list_program> | dot -Tsvg > /tmp/dot.svg; firefox /tmp/dot.svg
|
||||
*
|
||||
* where <list_program> is the name of the executable
|
||||
*
|
||||
* The output may also be possible to visualize online on
|
||||
* https://dreampuf.github.io/GraphvizOnline/ or google "graphviz
|
||||
* online".
|
||||
*
|
||||
* For documention of the dot language, see graphviz.org.
|
||||
*
|
||||
* Returns: Nothing.
|
||||
*/
|
||||
void list_print_internal(const list *l, inspect_callback print_func, const char *desc,
|
||||
int indent_level)
|
||||
{
|
||||
static int graph_number = 0;
|
||||
graph_number++;
|
||||
int il = indent_level;
|
||||
|
||||
if (indent_level == 0) {
|
||||
// If this is the outermost datatype, start a graph...
|
||||
printf("digraph LIST_%d {\n", graph_number);
|
||||
|
||||
// Specify default shape and fontname
|
||||
il++;
|
||||
iprintf(il, "node [shape=rectangle fontname=\"Courier New\"]\n");
|
||||
iprintf(il, "ranksep=0.01\n");
|
||||
iprintf(il, "subgraph cluster_nullspace {\n");
|
||||
iprintf(il+1, "NULL\n");
|
||||
iprintf(il, "}\n");
|
||||
}
|
||||
|
||||
if (desc != NULL) {
|
||||
// Escape the string before printout
|
||||
char *escaped = escape_chars(desc);
|
||||
// Use different names on inner description nodes
|
||||
if (indent_level == 0) {
|
||||
iprintf(il, "description [label=\"%s\"]\n", escaped);
|
||||
} else {
|
||||
iprintf(il, "\tcluster_list_%d_description [label=\"%s\"]\n", graph_number, escaped);
|
||||
}
|
||||
// Return the memory used by the escaped string
|
||||
free(escaped);
|
||||
}
|
||||
|
||||
if (indent_level == 0) {
|
||||
// Use a single "pointer" edge as a starting point for the
|
||||
// outermost datatype
|
||||
iprintf(il, "l [label=\"%04lx\" xlabel=\"l\"]\n", PTR2ADDR(l));
|
||||
iprintf(il, "l -> m%04lx\n", PTR2ADDR(l));
|
||||
}
|
||||
|
||||
// Print the subgraph to surround the DList content
|
||||
iprintf(il, "subgraph cluster_list_%d { label=\"List\"\n", graph_number);
|
||||
il++;
|
||||
|
||||
// Output the head node
|
||||
print_head_node(il, l);
|
||||
|
||||
// Output the element nodes
|
||||
list_pos p = l->head;
|
||||
while (p != NULL) {
|
||||
print_elem_node(il, p);
|
||||
p = p->next;
|
||||
}
|
||||
|
||||
// Close the subgraph
|
||||
il--;
|
||||
iprintf(il, "}\n");
|
||||
|
||||
if (indent_level == 0) {
|
||||
// Put the user nodes in userspace
|
||||
iprintf(il, "subgraph cluster_userspace { label=\"User space\"\n");
|
||||
il++;
|
||||
}
|
||||
|
||||
// Output the value nodes
|
||||
p = l->head;
|
||||
while (p != NULL) {
|
||||
if (p->val) {
|
||||
print_value_node(il, p->val, print_func);
|
||||
}
|
||||
p = p->next;
|
||||
}
|
||||
|
||||
if (indent_level == 0) {
|
||||
// Close userspace
|
||||
il--;
|
||||
iprintf(il, "}\n");
|
||||
}
|
||||
|
||||
// Output the edges from the head
|
||||
print_head_edges(il, l);
|
||||
|
||||
// Output the edges from each element
|
||||
p = l->head;
|
||||
while (p != NULL) {
|
||||
print_elem_edges(il, l, p);
|
||||
p = p->next;
|
||||
}
|
||||
|
||||
if (indent_level == 0) {
|
||||
// Termination of graph
|
||||
printf("}\n");
|
||||
}
|
||||
}
|
||||
103
OU3/src/list/list_mwe1.c
Normal file
103
OU3/src/list/list_mwe1.c
Normal file
@@ -0,0 +1,103 @@
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include <list.h>
|
||||
|
||||
/*
|
||||
* Minimum working example for list.c.
|
||||
*
|
||||
* Authors: Niclas Borlin (niclas@cs.umu.se)
|
||||
* Adam Dahlgren Lindstrom (dali@cs.umu.se)
|
||||
*
|
||||
* Version information:
|
||||
* v1.0 2018-01-28: First public version.
|
||||
* v1.1 2018-04-03: Split into versions with/without memhandler.
|
||||
* v1.2 2023-01-14: Added printouts at start/end of main.
|
||||
* v1.3 2024-03-13: Added explicit create/kill functions.
|
||||
*/
|
||||
|
||||
#define VERSION "v1.3"
|
||||
#define VERSION_DATE "2024-03-13"
|
||||
|
||||
// Integers are stored via int pointers stored as void pointers.
|
||||
// Convert the given pointer and print the dereferenced value.
|
||||
static void print_int(const void *data)
|
||||
{
|
||||
const int *v = data;
|
||||
printf("%d", *v);
|
||||
}
|
||||
|
||||
// Create a dynamic copy of the integer i.
|
||||
int *int_create(int i)
|
||||
{
|
||||
// Allocate memory for an integer and set the value
|
||||
int *v=malloc(sizeof(*v));
|
||||
*v = i;
|
||||
return v;
|
||||
}
|
||||
|
||||
// Return the memory used by the integer.
|
||||
void int_kill(void *v)
|
||||
{
|
||||
int *p=v;
|
||||
free(p);
|
||||
}
|
||||
|
||||
int main(void)
|
||||
{
|
||||
printf("%s, %s %s: Create integer list without kill_function.\n",
|
||||
__FILE__, VERSION, VERSION_DATE);
|
||||
printf("Code base version %s (%s).\n\n", CODE_BASE_VERSION, CODE_BASE_RELEASE_DATE);
|
||||
|
||||
// Create the list.
|
||||
list *l = list_empty(NULL);
|
||||
|
||||
// Create a dynamic integer with value 5.
|
||||
int *val = int_create(5);
|
||||
|
||||
// Insert the value first in the list.
|
||||
list_insert(l, val, list_first(l));
|
||||
|
||||
printf("List after inserting one value:\n");
|
||||
list_print(l, print_int);
|
||||
|
||||
// Allocate space for another integer.
|
||||
val = int_create(8);
|
||||
// Insert the value last in the list.
|
||||
list_insert(l, val, list_end(l));
|
||||
|
||||
printf("List after inserting second value at the end:\n");
|
||||
list_print(l, print_int);
|
||||
|
||||
// Allocate space for a third integer.
|
||||
val = int_create(2);
|
||||
// Insert the value at the second position in the list.
|
||||
list_insert(l, val, list_next(l, list_first(l)));
|
||||
|
||||
printf("List after inserting a third value in the middle:\n");
|
||||
list_print(l, print_int);
|
||||
|
||||
// Remove first element.
|
||||
list_pos p=list_first(l);
|
||||
int *v=list_inspect(l, p);
|
||||
// Remove element from list.
|
||||
list_remove(l, p);
|
||||
// Free element content.
|
||||
int_kill(v);
|
||||
|
||||
printf("List after removing first element:\n");
|
||||
list_print(l, print_int);
|
||||
|
||||
// Empty the list.
|
||||
while (!list_is_empty(l)) {
|
||||
list_pos p=list_first(l);
|
||||
int *v=list_inspect(l,p);
|
||||
int_kill(v);
|
||||
list_remove(l,p);
|
||||
}
|
||||
// Done, kill the list.
|
||||
list_kill(l);
|
||||
|
||||
printf("\nNormal exit.\n\n");
|
||||
return 0;
|
||||
}
|
||||
131
OU3/src/list/list_mwe1i.c
Normal file
131
OU3/src/list/list_mwe1i.c
Normal file
@@ -0,0 +1,131 @@
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include <list.h>
|
||||
|
||||
/*
|
||||
* Minimum working example for list.c that shows how the internal
|
||||
* structure of a list can be visualized. In this version, the list
|
||||
* "borrows" the payload memory, i.e., the user of the list is
|
||||
* responsible for deallocating the payload memory. See list_mwe2i.c
|
||||
* for a version where the list takes over the responsibility.
|
||||
*
|
||||
* Authors: Niclas Borlin (niclas@cs.umu.se)
|
||||
*
|
||||
* Version information:
|
||||
* v1.0 2024-03-14: First public version.
|
||||
* v1.1 2024-03-15: Moved printout of dot instructions to separate function.
|
||||
*/
|
||||
|
||||
#define VERSION "v1.1"
|
||||
#define VERSION_DATE "2024-03-15"
|
||||
|
||||
// Integers are stored via int pointers stored as void pointers.
|
||||
// Convert the given pointer and print the dereferenced value.
|
||||
void print_int(const void *data)
|
||||
{
|
||||
const int *v = data;
|
||||
printf("%d", *v);
|
||||
}
|
||||
|
||||
// Create a dynamic copy of the integer i.
|
||||
int *int_create(int i)
|
||||
{
|
||||
// Allocate memory for an integer and set the value
|
||||
int *v = malloc(sizeof(*v));
|
||||
*v = i;
|
||||
return v;
|
||||
}
|
||||
|
||||
// Return the memory used by the integer.
|
||||
void int_kill(void *v)
|
||||
{
|
||||
int *p = v; // Convert to a readable pointer - useful in a debugger
|
||||
free(p);
|
||||
}
|
||||
|
||||
// Print cut lines before and after a call list_print_internal.
|
||||
void print_internal_with_cut_lines(const list *l, const char *desc)
|
||||
{
|
||||
// Internal counter that will be remembered between calls.
|
||||
|
||||
// Used to generate sequentially numbered -- CUT HERE -- marker lines
|
||||
// to enable automatic parsing of the output.
|
||||
static int cut = 1;
|
||||
|
||||
// Print starting marker line.
|
||||
printf("\n--- START CUT HERE %d ---\n", cut);
|
||||
|
||||
// Call the internal print function to get the actual dot code.
|
||||
list_print_internal(l, print_int, desc, 0);
|
||||
|
||||
// Print ending marker line
|
||||
printf("--- END CUT HERE %d ---\n\n", cut);
|
||||
|
||||
// Increment the cut number. Will be remembered next time the
|
||||
// function is called since cut is a static variable.
|
||||
cut++;
|
||||
}
|
||||
|
||||
// Print a message with intructions how to use the dot output. prog is
|
||||
// the name of the executable.
|
||||
void print_dot_usage(char *prog)
|
||||
{
|
||||
printf("\nGenerate dot code to visualize internal structure with GraphViz. ");
|
||||
printf("Use\n\n%s ", prog);
|
||||
printf("| sed -n '/START CUT HERE X/,/END CUT HERE X/{//!p}' | dot -Tsvg > /tmp/dot.svg\n\n");
|
||||
printf("to generate an svg file of cut X (replace X by the requested cut number).\n");
|
||||
printf("The generated file can then be visualized with\n\n");
|
||||
printf("firefox /tmp/dot.svg\n\n");
|
||||
printf("Use -Tpng to generate a .png file instead. "
|
||||
"See graphviz.org and %s for documentation.\n", __FILE__);
|
||||
|
||||
printf("\n--- Start of normal output ---\n\n");
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
printf("%s, %s %s: Create integer list without kill_function.\n",
|
||||
__FILE__, VERSION, VERSION_DATE);
|
||||
printf("Code base version %s (%s).\n\n", CODE_BASE_VERSION, CODE_BASE_RELEASE_DATE);
|
||||
|
||||
print_dot_usage(argv[0]);
|
||||
|
||||
// Create the list.
|
||||
list *l = list_empty(NULL);
|
||||
|
||||
printf("Empty list from the outside:\n");
|
||||
list_print(l, print_int);
|
||||
print_internal_with_cut_lines(l, "Empty list showing the internal structure");
|
||||
|
||||
// Insert the value 5 first in the list.
|
||||
list_insert(l, int_create(5), list_first(l));
|
||||
// Insert the value 8 last in the list.
|
||||
list_insert(l, int_create(8), list_end(l));
|
||||
// Insert the value 2 at the second position in the list.
|
||||
list_insert(l, int_create(2), list_next(l, list_first(l)));
|
||||
|
||||
printf("List from the outside after inserting 3 values:\n");
|
||||
list_print(l, print_int);
|
||||
const char *long_desc = __FILE__
|
||||
": Internal structure of the List after inserting 3 values.\n"
|
||||
"Red lines are used for the list payload.\n\n"
|
||||
"The dashed red lines indicate that the payload memory is\n"
|
||||
"BORROWED by the list, i.e., the payload\n"
|
||||
"memory will NOT be deallocated by the list.\n\n"
|
||||
"See list_mwe2i for a list example\nthat owns the payload memory.";
|
||||
print_internal_with_cut_lines(l, long_desc);
|
||||
|
||||
// Empty the list.
|
||||
while (!list_is_empty(l)) {
|
||||
list_pos p=list_first(l);
|
||||
int *v=list_inspect(l,p);
|
||||
int_kill(v);
|
||||
list_remove(l,p);
|
||||
}
|
||||
// Done, kill the list.
|
||||
list_kill(l);
|
||||
|
||||
printf("\nNormal exit.\n\n");
|
||||
return 0;
|
||||
}
|
||||
92
OU3/src/list/list_mwe2.c
Normal file
92
OU3/src/list/list_mwe2.c
Normal file
@@ -0,0 +1,92 @@
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include <list.h>
|
||||
|
||||
/*
|
||||
* Minimum working example for list.c.
|
||||
*
|
||||
* Authors: Niclas Borlin (niclas@cs.umu.se)
|
||||
* Adam Dahlgren Lindstrom (dali@cs.umu.se)
|
||||
*
|
||||
* Version information:
|
||||
* v1.0 2018-01-28: First public version.
|
||||
* v1.1 2018-04-03: Split into versions with/without memhandler.
|
||||
* v1.2 2023-01-14: Added printouts at start/end of main.
|
||||
* v1.21 2024-01-16: Fix include to be with brackets, not citation marks.
|
||||
* v1.3 2024-03-13: Added explicit create/kill functions.
|
||||
*/
|
||||
|
||||
#define VERSION "v1.3"
|
||||
#define VERSION_DATE "2024-03-13"
|
||||
|
||||
// Integers are stored via int pointers stored as void pointers.
|
||||
// Convert the given pointer and print the dereferenced value.
|
||||
static void print_int(const void *data)
|
||||
{
|
||||
const int *v = data;
|
||||
printf("%d", *v);
|
||||
}
|
||||
|
||||
// Create a dynamic copy of the integer i.
|
||||
int *int_create(int i)
|
||||
{
|
||||
// Allocate memory for an integer and set the value
|
||||
int *v=malloc(sizeof(*v));
|
||||
*v = i;
|
||||
return v;
|
||||
}
|
||||
|
||||
// Return the memory used by the integer.
|
||||
void int_kill(void *v)
|
||||
{
|
||||
int *p=v;
|
||||
free(p);
|
||||
}
|
||||
|
||||
int main(void)
|
||||
{
|
||||
printf("%s, %s %s: Create integer list with custom kill_function.\n",
|
||||
__FILE__, VERSION, VERSION_DATE);
|
||||
printf("Code base version %s (%s).\n\n", CODE_BASE_VERSION, CODE_BASE_RELEASE_DATE);
|
||||
|
||||
// Create the list.
|
||||
list *l = list_empty(int_kill);
|
||||
|
||||
// Create a dynamic integer with value 5.
|
||||
int *val = int_create(5);
|
||||
|
||||
// Insert the value first in the list.
|
||||
list_insert(l, val, list_first(l));
|
||||
|
||||
printf("List after inserting one value:\n");
|
||||
list_print(l, print_int);
|
||||
|
||||
// Allocate space for another integer.
|
||||
val = int_create(8);
|
||||
// Insert the value last in the list.
|
||||
list_insert(l, val, list_end(l));
|
||||
|
||||
printf("List after inserting second value at the end:\n");
|
||||
list_print(l, print_int);
|
||||
|
||||
// Allocate space for a third integer.
|
||||
val = int_create(2);
|
||||
// Insert the value at the second position in the list.
|
||||
list_insert(l, val, list_next(l, list_first(l)));
|
||||
|
||||
printf("List after inserting a third value in the middle:\n");
|
||||
list_print(l, print_int);
|
||||
|
||||
// Remove first element.
|
||||
list_remove(l, list_first(l));
|
||||
|
||||
printf("List after removing first element:\n");
|
||||
list_print(l, print_int);
|
||||
|
||||
// Done, kill the list.
|
||||
list_kill(l);
|
||||
|
||||
printf("\nNormal exit.\n\n");
|
||||
return 0;
|
||||
}
|
||||
125
OU3/src/list/list_mwe2i.c
Normal file
125
OU3/src/list/list_mwe2i.c
Normal file
@@ -0,0 +1,125 @@
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include <list.h>
|
||||
|
||||
/*
|
||||
* Minimum working example for list.c that shows how the internal
|
||||
* structure of a list can be visualized. In this version, the list
|
||||
* "owns" the payload memory, i.e., the list takes over the
|
||||
* responsibility to deallocate the payload memory when the
|
||||
* corresponding elements are removed. See list_mwe1i.c for a version
|
||||
* where the list does not take over the responsibility.
|
||||
*
|
||||
* Authors: Niclas Borlin (niclas@cs.umu.se)
|
||||
*
|
||||
* Version information:
|
||||
* v1.0 2024-03-14: First public version.
|
||||
* v1.1 2024-03-15: Moved printout of dot instructions to separate function.
|
||||
*/
|
||||
|
||||
#define VERSION "v1.1"
|
||||
#define VERSION_DATE "2024-03-15"
|
||||
|
||||
// Integers are stored via int pointers stored as void pointers.
|
||||
// Convert the given pointer and print the dereferenced value.
|
||||
void print_int(const void *data)
|
||||
{
|
||||
const int *v = data;
|
||||
printf("%d", *v);
|
||||
}
|
||||
|
||||
// Create a dynamic copy of the integer i.
|
||||
int *int_create(int i)
|
||||
{
|
||||
// Allocate memory for an integer and set the value
|
||||
int *v = malloc(sizeof(*v));
|
||||
*v = i;
|
||||
return v;
|
||||
}
|
||||
|
||||
// Return the memory used by the integer.
|
||||
void int_kill(void *v)
|
||||
{
|
||||
int *p = v; // Convert to a readable pointer - useful in a debugger
|
||||
free(p);
|
||||
}
|
||||
|
||||
// Print cut lines before and after a call list_print_internal.
|
||||
void print_internal_with_cut_lines(const list *l, const char *desc)
|
||||
{
|
||||
// Internal counter that will be remembered between calls.
|
||||
|
||||
// Used to generate sequentially numbered -- CUT HERE -- marker lines
|
||||
// to enable automatic parsing of the output.
|
||||
static int cut = 1;
|
||||
|
||||
// Print starting marker line.
|
||||
printf("\n--- START CUT HERE %d ---\n", cut);
|
||||
|
||||
// Call the internal print function to get the actual dot code.
|
||||
list_print_internal(l, print_int, desc, 0);
|
||||
|
||||
// Print ending marker line
|
||||
printf("--- END CUT HERE %d ---\n\n", cut);
|
||||
|
||||
// Increment the cut number. Will be remembered next time the
|
||||
// function is called since cut is a static variable.
|
||||
cut++;
|
||||
}
|
||||
|
||||
// Print a message with intructions how to use the dot output. prog is
|
||||
// the name of the executable.
|
||||
void print_dot_usage(char *prog)
|
||||
{
|
||||
printf("\nGenerate dot code to visualize internal structure with GraphViz. ");
|
||||
printf("Use\n\n%s ", prog);
|
||||
printf("| sed -n '/START CUT HERE X/,/END CUT HERE X/{//!p}' | dot -Tsvg > /tmp/dot.svg\n\n");
|
||||
printf("to generate an svg file of cut X (replace X by the requested cut number).\n");
|
||||
printf("The generated file can then be visualized with\n\n");
|
||||
printf("firefox /tmp/dot.svg\n\n");
|
||||
printf("Use -Tpng to generate a .png file instead. "
|
||||
"See graphviz.org and %s for documentation.\n", __FILE__);
|
||||
|
||||
printf("\n--- Start of normal output ---\n\n");
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
printf("%s, %s %s: Create integer list with kill_function.\n",
|
||||
__FILE__, VERSION, VERSION_DATE);
|
||||
printf("Code base version %s (%s).\n\n", CODE_BASE_VERSION, CODE_BASE_RELEASE_DATE);
|
||||
|
||||
print_dot_usage(argv[0]);
|
||||
|
||||
// Create the list.
|
||||
list *l = list_empty(int_kill);
|
||||
|
||||
printf("Empty list from the outside:\n");
|
||||
list_print(l, print_int);
|
||||
print_internal_with_cut_lines(l, "Empty list showing the internal structure");
|
||||
|
||||
// Insert the value 5 first in the list.
|
||||
list_insert(l, int_create(5), list_first(l));
|
||||
// Insert the value 8 last in the list.
|
||||
list_insert(l, int_create(8), list_end(l));
|
||||
// Insert the value 2 at the second position in the list.
|
||||
list_insert(l, int_create(2), list_next(l, list_first(l)));
|
||||
|
||||
printf("List from the outside after inserting 3 values:\n");
|
||||
list_print(l, print_int);
|
||||
const char *long_desc = __FILE__
|
||||
": Internal structure of the List after inserting 3 values.\n"
|
||||
"Red lines are used for the list payload.\n\n"
|
||||
"The solid red lines indicate that the payload memory is\n"
|
||||
"OWNED -- not borrowed -- by the list, i.e., the payload\n"
|
||||
"memory WILL be deallocated by the list.\n\n"
|
||||
"See list_mwe1i for a list example\nthat borrows the payload memory.";
|
||||
print_internal_with_cut_lines(l, long_desc);
|
||||
|
||||
// Kill the list, including the payload.
|
||||
list_kill(l);
|
||||
|
||||
printf("\nNormal exit.\n\n");
|
||||
return 0;
|
||||
}
|
||||
1190
OU3/src/list/list_test1.c
Normal file
1190
OU3/src/list/list_test1.c
Normal file
File diff suppressed because it is too large
Load Diff
1308
OU3/src/list/list_test2.c
Normal file
1308
OU3/src/list/list_test2.c
Normal file
File diff suppressed because it is too large
Load Diff
2
OU3/src/queue/.gitignore
vendored
Normal file
2
OU3/src/queue/.gitignore
vendored
Normal file
@@ -0,0 +1,2 @@
|
||||
queue_example
|
||||
queue_example_internal
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user