Initial Commit

This commit is contained in:
Marc
2025-09-13 14:40:16 +02:00
commit ded01301c2
383 changed files with 71046 additions and 0 deletions

10
OU1/changes.txt Normal file
View File

@@ -0,0 +1,10 @@
Changes 2025-05-01
Cleanup of uneccesary comments
Improved function comments
Added space between functions
Fixed value_equal to specifications
added exit() to solve broken tests
added check of stack to fix test 8 and test 10
Checked code again for memory leaks, no leaks found on my
machine (latest fedora). Also tested on scratchy with older GCC.
Updated report small code changes.

3
OU1/compile.sh Normal file
View File

@@ -0,0 +1,3 @@
#!bin/bash
gcc -o stack_test -I /home/marc/Documents/Umeå/DOA/OU1/datastructures-v2.2.2.2/include/ stack_test.c /home/marc/Documents/Umeå/DOA/OU1/datastructures-v2.2.2.2/src/stack/stack.c && gcc -o int_stack_test -I /home/marc/Documents/Umeå/DOA/OU1/datastructures-v2.2.2.2/include/ int_stack_test.c /home/marc/Documents/Umeå/DOA/OU1/datastructures-v2.2.2.2/src/int_stack/int_stack.c

13
OU1/datastructures-v2.2.2.2/.gitignore vendored Normal file
View 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]

View 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.

View 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.

View 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).
```

View 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

View 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

View 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

View 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

View 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

View 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

View 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

View 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

View 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

View 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

View 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

View 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

View 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

View File

@@ -0,0 +1 @@
libdoa.a

View 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)

View 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

View 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 ./$<

View 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");
}
}

View 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;
}

View 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;
}

View 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;
}

View 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;
}

View 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;
}

View 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;
}

View 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 ./$<

View 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");
}
}

View 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;
}

View 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;
}

View 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;
}

View 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;
}

View 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 ./$<

View 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.

View 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");
}
}

View 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;
}

View 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;
}

View 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;
}

View 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;
}

View 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 ./$<

View 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");
}
}

View 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;
}

View 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;
}

View 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

View 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");
}
}

View 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;
}

View 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;
}

File diff suppressed because it is too large Load Diff

View 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

View 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");
}
}

View 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;
}

View 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;
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,2 @@
int_queue_example_internal
int_queue_example

View 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 ./$<

View 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");
}
}

View 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;
}

View 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;
}

View 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;
}

View 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;
}

View 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 ./$<

View 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");
}
}

View 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;
}

View 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;
}

View 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

View 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).

View 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");
}
}

View 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;
}

View 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;
}

View 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;
}

View 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;
}

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,2 @@
queue_example
queue_example_internal

View File

@@ -0,0 +1,49 @@
MWE = queue_mwe1 queue_mwe2 queue_mwe1i queue_mwe2i queue_example queue_example_internal
SRC = 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)
queue_example_internal: queue_example_internal.c queue.c ../list/list.c
gcc -o $@ $(CFLAGS) $^
queue_example: queue_example.c queue.c ../list/list.c
gcc -o $@ $(CFLAGS) $^
queue_mwe1: queue_mwe1.c queue.c ../list/list.c
gcc -o $@ $(CFLAGS) $^
queue_mwe2: queue_mwe2.c queue.c ../list/list.c
gcc -o $@ $(CFLAGS) $^
queue_mwe1i: queue_mwe1i.c queue.c ../list/list.c
gcc -o $@ $(CFLAGS) $^
queue_mwe2i: queue_mwe2i.c queue.c ../list/list.c
gcc -o $@ $(CFLAGS) $^
memtest1: queue_mwe1
valgrind --leak-check=full --show-reachable=yes ./$<
memtest2: queue_mwe2
valgrind --leak-check=full --show-reachable=yes ./$<
memtest3: queue_mwe1i
valgrind --leak-check=full --show-reachable=yes ./$<
memtest4: queue_mwe2i
valgrind --leak-check=full --show-reachable=yes ./$<

View File

@@ -0,0 +1,11 @@
# Kö
En implementation av ADT:n __ baserad på en dubbel-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.
# Minimal working example
Se [queue_mwe1.c](queue_mwe1.c) och [queue_mwe2.c](queue_mwe2.c).

View File

@@ -0,0 +1,401 @@
#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
#include <queue.h>
#include <list.h>
/*
* Implementation of a generic queue 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)
*
* Based on earlier code by: Johan Eliasson (johane@cs.umu.se).
*
* Version information:
* v1.0 2018-01-28: First public version.
* v1.01 2024-01-16: Fix include to be with brackets, not citation marks.
* v2.0 2024-03-14: Added queue_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 queue is implemented using the list abstract datatype. Almost
* everything is done by the list.
*/
struct queue {
list *elements;
};
// ===========INTERNAL FUNCTION IMPLEMENTATIONS ============
/**
* 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)
{
// Allocate the queue head.
queue *q = calloc(1, sizeof(*q));
// Create an empty list.
q->elements = list_empty(kill_func);
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)
{
return list_is_empty(q->elements);
}
/**
* 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)
{
list_insert(q->elements, v, list_end(q->elements));
return q;
}
/**
* 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)
{
list_remove(q->elements, list_first(q->elements));
return 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)
{
return list_inspect(q->elements, list_first(q->elements));
}
/**
* 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)
{
list_kill(q->elements);
free(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)
{
printf("{ ");
list_pos pos = list_first(q->elements);
while (pos != list_end(q->elements)) {
print_func(list_inspect(q->elements, pos));
pos = list_next(q->elements, pos);
if (pos != list_end(q->elements)) {
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_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 queue head struct.
* @il: Indentation level.
* @q: Queue to inspect.
*
* Returns: Nothing.
*/
static void print_head_node(int indent_level, const queue *q)
{
iprintf(indent_level, "m%04lx [shape=record "
"label=\"<e>elements\\n%04lx\" xlabel=\"%04lx\"]\n",
PTR2ADDR(q), PTR2ADDR(q->elements), PTR2ADDR(q));
}
// Print edges from the queue head to the elements list.
static void print_head_edge(int indent_level, const queue *q)
{
print_edge(indent_level, q, q->elements, "e", "elements", NULL);
}
// 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;
}
/**
* queue_print_internal() - Print the queues internal structure in dot format.
* @l: 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)
{
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");
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_queue_%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, "q [label=\"%04lx\" xlabel=\"q\"]\n", PTR2ADDR(q));
iprintf(il, "q -> m%04lx\n", PTR2ADDR(q));
}
// Output the payload nodes before asking the list to print them
// to render them in their own subgraph.
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
list_pos p = list_first(q->elements);
while (!list_pos_is_equal(q->elements, p, list_end(q->elements))) {
print_value_node(il, list_inspect(q->elements, p), print_func);
// Advance
p = list_next(q->elements, p);
}
// Close the subgraph
il--;
iprintf(il, "}\n");
}
// Print the subgraph to surround the DList content
iprintf(il, "subgraph cluster_queue_%d { label=\"Queue\"\n", graph_number);
il++;
// Output the head node
print_head_node(il, q);
if (q->elements) {
list_print_internal(q->elements, print_func, NULL, il);
}
// Close the subgraph
il--;
iprintf(il, "}\n");
// Output the edges from the head
print_head_edge(il, q);
if (indent_level == 0) {
// Termination of graph
printf("}\n");
}
}

View File

@@ -0,0 +1,68 @@
#include <stdio.h>
#include <stdlib.h>
#include <queue.h>
/*
* A small example that uses the generic queue to store integers.
* Based on int_queue_example.c from int_queue.
*
* Author: Niclas Borlin (niclas@cs.umu.se)
*
* Version information:
* v1.0 2025-01-23: First public version.
*/
// 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);
}
int main(void)
{
// Create the queue.
queue *q = queue_empty(int_kill);
// 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);
int *v = int_create(i);
// Put some values in the queue.
q = queue_enqueue(q, v);
}
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;
}

View File

@@ -0,0 +1,124 @@
#include <stdio.h>
#include <stdlib.h>
#include <queue.h>
// Size of internal string buffer
#define BUFLEN 400
/*
* A small example that uses a generic queue to store integers.
*
* 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"
// 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 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, print_int);
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(const 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, print_int, desc, 0);
// 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(int_kill);
// 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++) {
int *v = int_create(i);
q = queue_enqueue(q, v);
snprintf(buf, BUFLEN, "The value %d was added to the queue", i);
print_and_wait(q, buf);
print_internal_and_wait(q, buf);
}
int *w = queue_front(q);
printf("The value at the front of the queue is: %d.\n", *w);
// 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);
w = queue_front(q);
printf("The value at the front of the queue is now: %d.\n", *w);
// 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;
}

View File

@@ -0,0 +1,89 @@
#include <stdio.h>
#include <stdlib.h>
#include <queue.h>
/*
* Minimum working example for queue.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-14: Added explicit create/kill functions.
*/
#define VERSION "v1.3"
#define VERSION_DATE "2024-03-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 integer queue 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 queue.
queue *q=queue_empty(NULL);
for (int i = 11; i <= 13; i++) {
// Allocate memory for one int.
int *v = int_create(i);
// Put value in queue.
q=queue_enqueue(q,v);
}
printf("--QUEUE before dequeueing--\n");
queue_print(q, print_int);
// Get value on top of queue.
int *v=queue_front(q);
// Remote element from queue.
q=queue_dequeue(q);
// Return memory
int_kill(v);
printf("--QUEUE after dequeueing--\n");
queue_print(q, print_int);
// Now we must empty the queue and free each value ourselves.
while (!queue_is_empty(q)) {
// Get value from top of queue.
int *v=queue_front(q);
// Remove element from queue.
q=queue_dequeue(q);
// Free value
int_kill(v);
}
// Finally, destroy the bare queue.
queue_kill(q);
printf("\nNormal exit.\n\n");
return 0;
}

View File

@@ -0,0 +1,134 @@
#include <stdio.h>
#include <stdlib.h>
#include <queue.h>
/*
* Minimum working example for queue.c that shows how the internal
* structure of a queue can be visualized. In this version, the queue
* "borrows" the payload memory, i.e., the user of the queue is
* responsible for deallocating the payload memory. See queue_mwe2i.c
* for a version where the queue 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 queue_print_internal.
void print_internal_with_cut_lines(const queue *q, 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(q, 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 queue 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 queue.
queue *q = queue_empty(NULL);
printf("Empty queue from the outside:\n");
queue_print(q, print_int);
print_internal_with_cut_lines(q, "Empty queue showing the internal structure");
for (int i = 11; i <= 13; i++) {
// Allocate memory for one int.
int *v = int_create(i);
// Put value in queue.
q = queue_enqueue(q, v);
}
printf("Queue from the outside after inserting 3 values:\n");
queue_print(q, print_int);
const char *long_desc = __FILE__
": Internal structure of the Queue after inserting 3 values.\n"
"All data is stored internally using a List.\n"
"Red lines are used for the queue payload.\n\n"
"The dashed red lines indicate that the payload memory is\n"
"BORROWED by the queue, i.e., the payload\n"
"memory will NOT be deallocated by the queue.\n\n"
"See queue_mwe2i for a queue example\nthat owns the payload memory.";
print_internal_with_cut_lines(q, long_desc);
// Now we must empty the queue and free each value ourselves.
while (!queue_is_empty(q)) {
// Get value from top of queue.
int *v = queue_front(q);
// Remove element from queue.
q = queue_dequeue(q);
// Free value
int_kill(v);
}
// Finally, destroy the bare queue.
queue_kill(q);
printf("\nNormal exit.\n\n");
return 0;
}

View File

@@ -0,0 +1,75 @@
#include <stdio.h>
#include <stdlib.h>
#include <queue.h>
/*
* Minimum working example for queue.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-14: Added explicit create/kill functions.
*/
#define VERSION "v1.3"
#define VERSION_DATE "2024-03-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 integer queue with kill_function.\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(int_kill);
for (int i = 11; i <= 13; i++) {
// Allocate memory for one int.
int *v = int_create(i);
// Put value in queue.
q=queue_enqueue(q,v);
}
printf("--QUEUE before dequeueing--\n");
queue_print(q, print_int);
// Dequeue one element. The payload memory will be automatically returned.
queue_dequeue(q);
printf("--QUEUE after dequeueing--\n");
queue_print(q, print_int);
queue_kill(q);
printf("\nNormal exit.\n\n");
return 0;
}

View File

@@ -0,0 +1,126 @@
#include <stdio.h>
#include <stdlib.h>
#include <queue.h>
/*
* Minimum working example for queue.c that shows how the internal
* structure of a queue can be visualized. In this version, the queue
* "owns" the payload memory, i.e., the queue takes over the
* responsibility to deallocate the payload memory when the
* corresponding elements are removed. See queue_mwe1i.c for a version
* where the queue 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 queue_print_internal.
void print_internal_with_cut_lines(const queue *q, 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(q, 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 queue 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 queue.
queue *q = queue_empty(int_kill);
printf("Empty queue from the outside:\n");
queue_print(q, print_int);
print_internal_with_cut_lines(q, "Empty queue showing the internal structure");
for (int i = 11; i <= 13; i++) {
// Allocate memory for one int.
int *v = int_create(i);
// Put value in queue.
q = queue_enqueue(q, v);
}
printf("Queue from the outside after inserting 3 values:\n");
queue_print(q, print_int);
const char *long_desc = __FILE__
": Internal structure of the Queue after inserting 3 values.\n"
"All data is stored internally using a List.\n"
"Red lines are used for the queue payload.\n\n"
"The solid red lines indicate that the payload memory is\n"
"OWNED -- not borrowed -- by the queue, i.e., the payload\n"
"memory WILL be deallocated by the queue.\n\n"
"See queue_mwe1i for a queue example\nthat borrows the payload memory.";
print_internal_with_cut_lines(q, long_desc);
// Destroy the queue and its content.
queue_kill(q);
printf("\nNormal exit.\n\n");
return 0;
}

View File

@@ -0,0 +1,43 @@
MWE = stack_mwe1 stack_mwe2 stack_mwe1i stack_mwe2i
SRC = 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)
stack_mwe1: stack_mwe1.c stack.c
gcc -o $@ $(CFLAGS) $^
stack_mwe2: stack_mwe2.c stack.c
gcc -o $@ $(CFLAGS) $^
stack_mwe1i: stack_mwe1i.c stack.c
gcc -o $@ $(CFLAGS) $^
stack_mwe2i: stack_mwe2i.c stack.c
gcc -o $@ $(CFLAGS) $^
memtest1: stack_mwe1
valgrind --leak-check=full --show-reachable=yes ./$<
memtest2: stack_mwe2
valgrind --leak-check=full --show-reachable=yes ./$<
memtest3: stack_mwe1i
valgrind --leak-check=full --show-reachable=yes ./$<
memtest4: stack_mwe2i
valgrind --leak-check=full --show-reachable=yes ./$<

View File

@@ -0,0 +1,11 @@
# Stack
En implementation av ADT:n _Stack_ implementerad som en enkellä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.
# Minimal working example
Se [stack_mwe1.c](stack_mwe1.c) och [stack_mwe2.c](stack_mwe2.c).

View File

@@ -0,0 +1,77 @@
#include <stdio.h>
#include <stdbool.h>
#include <stdlib.h> // for EXIT_FAILURE
/*#include <int_list.h>*/
#include <int_stack.h>
/*#include <stack.h>*/
// Macro to compute length of a C array
#define LEN(x) (sizeof(x)/sizeof(x[0]))
/*
* Stub test program for the list implementation in list.c.
*
* Author: Niclas Borlin (niclas.borlin@cs.umu.se).
*
* Version information:
* 2018-01-28: v1.0. First public version.
* 2019-01-23: v1.1. Added file comment. Removed memory cleanup on failure.
* 2023-01-21: v2.0. Complete re-write. Semi-serious attempt at a complete test program for
* int_list.
* 2023-01-21: v2.01. Updated print identification command.
* 2023-03-23: v2.1. Renamed list_pos_are_equal to list_pos_is_equal.
* 2023-03-23: v2.2. Removed empty if statements in test code.
*/
#define VERSION "v2.2"
#define VERSION_DATE "2023-03-23"
/*
* Function to compare the values stored in the list.
*/
bool value_equal(int v1, int v2)
{
return v1 == v2;
}
/*
* empty_returns_non_null() - Test that the stack_empty() function returns a non-null pointer.
* Precondition: None.
*/
void empty_returns_non_null(void)
{
// Print a starting message
fprintf(stderr,"Starting empty_returns_non_null()...");
// create an empty stack
stack s = stack_empty();
// l should be non-NULL
if (s == NULL) {
// Fail with error message
fprintf(stderr, "FAIL: Expected a list pointer, got NULL.\n");
exit(EXIT_FAILURE);
}
// Everything went well, clean up
fprintf(stderr,"cleaning up...");
list_kill(s);
fprintf(stderr,"done.\n");
}
int main(void)
{
printf("%s, %s %s: Test program for the typed int_stack datatype.\n",
__FILE__, VERSION, VERSION_DATE);
printf("Code base version %s.\n\n", CODE_BASE_VERSION);
empty_returns_non_null();
fprintf(stderr,"\nSUCCESS: Implementation passed all tests. Normal exit.\n");
return 0;
}

View File

@@ -0,0 +1,463 @@
#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
#include <stack.h>
/*
* Implementation of a generic stack 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)
*
* Based on earlier code by: Johan Eliasson (johane@cs.umu.se).
*
* Version information:
* v1.0 2018-01-28: First public version.
* v1.1 2022-03-24: Minor update to always take care of returned pointer.
* v2.0 2024-03-14: Added stack_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 stack elements are implemented as one-cells with forward and
* links. The stack has a single element pointer.
*/
typedef struct cell {
struct cell *next;
void *val;
} cell;
struct stack {
cell *top;
kill_function kill_func;
};
// ===========INTERNAL FUNCTION IMPLEMENTATIONS============
/**
* 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)
{
// Allocate memory for stack structure.
stack *s = calloc(1, sizeof(stack));
s->top = NULL;
s->kill_func = kill_func;
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)
{
return s->top == NULL;
}
/**
* 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)
{
// Allocate memory for element.
cell *e = calloc(1, sizeof(*e));
// Set element value.
e->val = v;
// Link to current top.
e->next = s->top;
// Put element on top of stack.
s->top = e;
// Return modified 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 {
// Remember top element.
cell *e = s->top;
// Link past top element.
s->top = s->top->next;
// De-allocate user memory.
if (s->kill_func != NULL) {
s->kill_func(e->val);
}
// De-allocate element memory.
free(e);
}
return 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)
{
if (stack_is_empty(s)) {
fprintf(stderr, "stack_top: Warning: top on empty stack\n");
}
return s->top->val;
}
/**
* 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)
{
while (!stack_is_empty(s)) {
s = stack_pop(s);
}
free(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)
{
printf("{ ");
cell *e = s->top;
while (e != NULL) {
print_func(e->val);
e = e->next;
if (e != NULL) {
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_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 stack head struct.
* @indent_level: Indentation level.
* @s: Stack to inspect.
*
* Returns: Nothing.
*/
static void print_head_node(int indent_level, const stack *s)
{
iprintf(indent_level, "m%04lx [shape=record label=\"kill\\n%04lx|<t>top\\n%04lx\"]\n",
PTR2ADDR(s), PTR2ADDR(s->kill_func), PTR2ADDR(s->top));
}
// Print edges from the stack head to the head cell.
static void print_head_edge(int indent_level, const stack *s)
{
print_edge(indent_level, s, s->top, "t", "top", NULL);
}
// Print a node corresponding to the cell at position p.
static void print_elem_node(int indent_level, const cell *p)
{
iprintf(indent_level, "m%04lx [shape=record label=\"<v>val\\n%04lx|<n>next\\n%04lx\"]\n",
PTR2ADDR(p), PTR2ADDR(p->val), PTR2ADDR(p->next));
}
// 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 stack owns the memory, the edge is solid, otherwise
// dashed.
static void print_elem_edges(int indent_level, const stack *s, const cell *p)
{
print_edge(indent_level, p, p->next, "n", "next", NULL);
if (s->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;
}
/**
* 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)
{
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");
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_stack_%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, "s [label=\"%04lx\" xlabel=\"s\"]\n", PTR2ADDR(s));
iprintf(il, "s -> m%04lx\n", PTR2ADDR(s));
}
// Print the subgraph to surround the Stack content
iprintf(il, "subgraph cluster_stack_%d { label=\"Stack\"\n", graph_number);
il++;
// Output the head node
print_head_node(il, s);
// Output the element nodes
cell *p = s->top;
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 = s->top;
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_edge(il, s);
// Output the edges from each element
p = s->top;
while (p != NULL) {
print_elem_edges(il, s, p);
p = p->next;
}
if (indent_level == 0) {
// Termination of graph
printf("}\n");
}
}

View File

@@ -0,0 +1,89 @@
#include <stdio.h>
#include <stdlib.h>
#include <stack.h>
/*
* Minimum working example for stack.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-14: Added explicit create/kill functions.
*/
#define VERSION "v1.3"
#define VERSION_DATE "2024-03-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; // Convert to a readable pointer - useful in a debugger
free(p);
}
int main(void)
{
printf("%s, %s %s: Create integer stack 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 stack.
stack *s = stack_empty(NULL);
for (int i=1; i<=3; i++) {
// Allocate memory for one int.
int *v = int_create(i);
// Push value on stack.
s = stack_push(s, v);
}
printf("--STACK before popping--\n");
stack_print(s, print_int);
// Get value on top of stack.
int *v = stack_top(s);
// Pop element from stack.
s = stack_pop(s);
// Return memory used by int
int_kill(v);
printf("--STACK after popping--\n");
stack_print(s, print_int);
// Now we must empty the stack and free each value ourselves.
while (!stack_is_empty(s)) {
// Get value from top of stack.
int *v = stack_top(s);
// Pop element from stack.
s = stack_pop(s);
// Return memory used by int
int_kill(v);
}
// Finally, destroy the bare stack.
stack_kill(s);
printf("\nNormal exit.\n\n");
return 0;
}

View File

@@ -0,0 +1,133 @@
#include <stdio.h>
#include <stdlib.h>
#include <stack.h>
/*
* Minimum working example for stack.c that shows how the internal
* structure of a stack can be visualized. In this version, the stack
* "borrows" the payload memory, i.e., the user of the stack is
* responsible for deallocating the payload memory. See stack_mwe2i.c
* for a version where the stack 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 stack_print_internal.
void print_internal_with_cut_lines(const 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, 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 stack 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 stack.
stack *s = stack_empty(NULL);
printf("Empty stack from the outside:\n");
stack_print(s, print_int);
print_internal_with_cut_lines(s, "Empty stack showing the internal structure");
for (int i=1; i<=3; i++) {
// Allocate memory for one int.
int *v = int_create(i);
// Push value on stack.
s = stack_push(s, v);
}
printf("Stack from the outside after pushing 3 values:\n");
stack_print(s, print_int);
const char *long_desc = __FILE__
": Internal structure of the Stack after pushing 3 values.\n"
"Red lines are used for the stack payload.\n\n"
"The dashed red lines indicate that the payload memory is\n"
"BORROWED by the stack, i.e., the payload\n"
"memory will NOT be deallocated by the stack.\n\n"
"See stack_mwe2i for a stack example\nthat owns the payload memory.";
print_internal_with_cut_lines(s, long_desc);
// Now we must empty the stack and free each value ourselves.
while (!stack_is_empty(s)) {
// Get value from top of stack.
int *v = stack_top(s);
// Pop element from stack.
s = stack_pop(s);
// Return memory used by int
int_kill(v);
}
// Finally, destroy the bare stack.
stack_kill(s);
printf("\nNormal exit.\n\n");
return 0;
}

View File

@@ -0,0 +1,78 @@
#include <stdio.h>
#include <stdlib.h>
#include <stack.h>
/*
* Minimum working example for stack.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 2022-03-24: Update to always take care of returned pointer.
* v1.3 2023-01-14: Added printouts at start/end of main.
* v1.4 2024-03-14: Added explicit create/kill functions.
*/
#define VERSION "v1.4"
#define VERSION_DATE "2024-03-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; // Convert to a readable pointer - useful in a debugger
free(p);
}
int main(void)
{
printf("%s, %s %s: Create integer stack with kill_function.\n",
__FILE__, VERSION, VERSION_DATE);
printf("Code base version %s (%s).\n\n", CODE_BASE_VERSION, CODE_BASE_RELEASE_DATE);
// Create the stack. Make the stack responsible for
// deallocation pushed values.
stack *s = stack_empty(int_kill);
for (int i=1; i<=3; i++) {
// Allocate memory for one int.
int *v = int_create(i);
// Push value on stack.
s = stack_push(s, v);
}
printf("--STACK before popping--\n");
stack_print(s, print_int);
// Pop'ing will automatically deallocate the payload
s = stack_pop(s);
printf("--STACK after popping--\n");
stack_print(s, print_int);
// Killing will automatically deallocate the payload
stack_kill(s);
printf("\nNormal exit.\n\n");
return 0;
}

View File

@@ -0,0 +1,125 @@
#include <stdio.h>
#include <stdlib.h>
#include <stack.h>
/*
* Minimum working example for stack.c that shows how the internal
* structure of a stack can be visualized. In this version, the stack
* "owns" the payload memory, i.e., the stack takes over the
* responsibility to deallocate the payload memory when the
* corresponding elements are removed. See stack_mwe1i.c for a version
* where the stack 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 stack_print_internal.
void print_internal_with_cut_lines(const 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, 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 stack 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 stack.
stack *s = stack_empty(int_kill);
printf("Empty stack from the outside:\n");
stack_print(s, print_int);
print_internal_with_cut_lines(s, "Empty stack showing the internal structure");
for (int i=1; i<=3; i++) {
// Allocate memory for one int.
int *v = int_create(i);
// Push value on stack.
s = stack_push(s, v);
}
printf("Stack from the outside after pushing 3 values:\n");
stack_print(s, print_int);
const char *long_desc = __FILE__
": Internal structure of the Stack after pushing 3 values.\n"
"Red lines are used for the stack payload.\n\n"
"The solid red lines indicate that the payload memory is\n"
"OWNED -- not borrowed -- by the stack, i.e., the payload\n"
"memory WILL be deallocated by the stack.\n\n"
"See stack_mwe1i for a stack example\nthat borrows the payload memory.";
print_internal_with_cut_lines(s, long_desc);
// Kill the stack. The payload memory is returned automatically.
stack_kill(s);
printf("\nNormal exit.\n\n");
return 0;
}

View File

@@ -0,0 +1,68 @@
MWE = table_mwe1 table_mwe2 table_mwe3 table2_mwe1 table2_mwe2 table2_mwe3 table_mwe1i table_mwe2i table2_mwe1i table2_mwe2i
SRC = table.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)
table_mwe1: table_mwe1.c table.c ../dlist/dlist.c
gcc -o $@ $(CFLAGS) $^
table_mwe2: table_mwe2.c table.c ../dlist/dlist.c
gcc -o $@ $(CFLAGS) $^
table_mwe3: table_mwe3.c table.c ../dlist/dlist.c
gcc -o $@ $(CFLAGS) $^
table_mwe1i: table_mwe1i.c table.c ../dlist/dlist.c
gcc -o $@ $(CFLAGS) $^
table_mwe2i: table_mwe2i.c table.c ../dlist/dlist.c
gcc -o $@ $(CFLAGS) $^
table2_mwe1: table_mwe1.c table2.c ../dlist/dlist.c
gcc -o $@ $(CFLAGS) $^
table2_mwe2: table_mwe2.c table2.c ../dlist/dlist.c
gcc -o $@ $(CFLAGS) $^
table2_mwe3: table_mwe3.c table2.c ../dlist/dlist.c
gcc -o $@ $(CFLAGS) $^
table2_mwe1i: table_mwe1i.c table2.c ../dlist/dlist.c
gcc -o $@ $(CFLAGS) $^
table2_mwe2i: table_mwe2i.c table2.c ../dlist/dlist.c
gcc -o $@ $(CFLAGS) $^
memtest11: table_mwe1
valgrind --leak-check=full --show-reachable=yes ./$<
memtest12: table_mwe2
valgrind --leak-check=full --show-reachable=yes ./$<
memtest13: table_mwe3
valgrind --leak-check=full --show-reachable=yes ./$<
memtest21: table2_mwe1
valgrind --leak-check=full --show-reachable=yes ./$<
memtest22: table2_mwe2
valgrind --leak-check=full --show-reachable=yes ./$<
memtest23: table2_mwe3
valgrind --leak-check=full --show-reachable=yes ./$<

View File

@@ -0,0 +1,125 @@
# Tabellimplementation
Det som skiljer tabellen från de övriga datatyperna är att vi här måste hantera
både nycklar och värden. Denna hantering fungerar analogt med de övriga, men vi
konkretiserar ändå med ett par exempel på hur tabellen kan användas.
## Jämförelsefunktion
För att vår tabell ska fungera behöver vi specificera hur nycklar ska jämföras.
Detta tillåter oss att t.ex. använda strängar eller heltal som nycklar utan att
den underliggande implementationen behöver ändras. Detta bygger på en
`compare_function`, som tar två pekare och returnerar `0` ifall de är lika, `0>`
om första värdet är större än det andra, samt `0<` om första värdet är mindre än
det andra. I fallet med heltal kan detta se ut såhär:
```c
static int compare_ints(const void *k1, const void *k2)
{
int key1 = *(int *)k1;
int key2 = *(int *)k2;
if ( key1 == key2 )
return 0;
if ( key1 < key2 )
return -1;
return 1;
}
```
Vi kan då skapa en tabell med `table_empty(compare_ints, free, free) som vi
sedan kan använda såhär:
```c
table *t = table_empty(compare_ints, free, free);
int *key = malloc(sizeof(int));
*key = 5;
char *value = calloc(5, sizeof(char));
strcpy(value, "test");
table_insert(t, key, value);
int key_to_lookup = 5;
table_lookup(t, &key_to_lookup);
```
Om vår `compare_function` är felaktigt implementerad så kommer inte
`table_lookup` hitta värdet, trots att vi stoppat in det.
## Minneshantering
När vi skapar en tabell måste vi specificera hur minnet för nycklar och värden
ska hanteras. Ofta kommer vi vilja allokera minne på heapen dynamiskt, och låta
tabellen avallokera både nyklar och värden:
```c
table *t = table_empty(compare_ints, free, free);
int *key = malloc(sizeof(int));
*key = 5;
char *value = calloc(5, sizeof(char));
strcpy(value, "test");
table_insert(t, key, value);
table_kill(t);
```
Vi skulle även kunna låta nycklarna ligga på stacken, och bara allokera värden
på heapen enl. följande exempel:
```c
table *t = table_empty(compare_ints, NULL, free);
int key = 5;
char *value = calloc(5, sizeof(char));
strcpy(value, "test");
table_insert(t, &key, value);
table_kill(t);
```
Det skulle också kunna vara så att nycklarna allokeras dynamiskt, men att de
också stoppas i en annan datatyp (t.ex. en lista). Följande kodsnutt illustrera
det exemplet:
```c
table *t = table_empty(compare_ints, NULL, free);
list *l = list_empty(free);
int *key = malloc(sizeof(int));
*key = 5;
char *value = calloc(5, sizeof(char));
strcpy(value, "test");
table_insert(t, key, value);
list_insert(l, key, list_first(l));
// Kommer inte frigöra key, bara value
table_kill(t);
// Frigör också key
list_kill(l);
```
## Utskrift
Till skillnad från de övriga datatyperna så vill vi skriva ut både nyckeln och
värdet med vår print-funktion. Istället för att exponera den interna `struct`:en
`table_entry` utanför c-filen, så använder vi oss istället av en print-funktion
som tar två parameterar.
```c
static void print_int_string_pair(const void *key, const void *value)
{
printf("[%d, %s]\n", *(int*)key, (char*)value);
}
```
Ett anrop till `table_print(t, print_int_string_pair)` skulle då skriva ut `[5,
test]` om vi utgår ifrån det tidigare exemplet.
# Minimal Working Example
Se [table_mwe1.c](table_mwe1.c), [table_mwe2.c](table_mwe2.c) och [table_mwe3.c](table_mwe3.c).

View File

@@ -0,0 +1,687 @@
#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 for the "Datastructures and
* algorithms" courses at the Department of Computing Science, Umea
* University.
*
* Duplicates are handled by inspect and remove.
*
* 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 2019-03-04: Bugfix in table_remove.
* v1.2 2024-04-15: Added table_print_internal.
* v2.0 2024-05-10: Updated print_internal with improved encapsulation.
*/
// ===========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(table_entry_kill);
// 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.
*/
void *table_lookup(const table *t, const void *key)
{
// Iterate over the list. Return first match.
dlist_pos pos = dlist_first(t->entries);
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) {
// If yes, return the corresponding value pointer.
return e->value;
}
// Continue with the next position.
pos = dlist_next(t->entries, pos);
}
// No match found. Return NULL.
return NULL;
}
/**
* 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);
} 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);
}
// 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");
}
}

View File

@@ -0,0 +1,692 @@
#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 for the "Datastructures and
* algorithms" courses at the Department of Computing Science, Umea
* University.
*
* Duplicates are handled by inspect and remove.
*
* 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 2019-02-21: Second version without dlist/memfreehandler.
* v1.2 2019-03-04: Bugfix in table_remove.
* v1.3 2024-04-15: Added table_print_internal.
* v2.0 2024-05-10: Updated print_internal with improved encapsulation.
*/
// ===========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.
*/
void *table_lookup(const table *t, const void *key)
{
// Iterate over the list. Return first match.
dlist_pos pos = dlist_first(t->entries);
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) {
// If yes, return the corresponding value pointer.
return e->value;
}
// Continue with the next position.
pos = dlist_next(t->entries, pos);
}
// No match found. Return NULL.
return NULL;
}
/**
* 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");
}
}

View File

@@ -0,0 +1,135 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <table.h>
/*
* Minimum working example for table.c. Inserts 4 key-value pairs into
* a table, including one duplicate. Makes two lookups and prints the
* result. The responsibility to deallocate the key-value pairs is NOT
* handed over to the table. Thus, all pointers must be stored outside
* the table.
*
* Authors: Niclas Borlin (niclas@cs.umu.se)
* Adam Dahlgren Lindstrom (dali@cs.umu.se)
*
* Version information:
* v1.0 2018-02-06: First public version.
* v1.01 2019-03-05: Improved docs.
* v1.1 2023-01-14: Added printouts at start/end of main.
* v1.2 2024-04-11: Added explicit create/kill of both strings and ints.
*/
#define VERSION "v1.2"
#define VERSION_DATE "2024-04-11"
// Create a dynamic copy of the string str.
char* string_copy(const char *str)
{
// Use calloc to ensure a '\0' termination.
char *copy = calloc(strlen(str) + 1, sizeof(*copy));
strcpy(copy, str);
return copy;
}
// Create a dynamic copy of the string str.
void string_kill(void *v)
{
// Convert pointer - useful in debugging.
char *s = v;
free(s);
}
// 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);
}
// Interpret the supplied key and value pointers and print their content.
void print_int_string_pair(const void *key, const void *value)
{
const int *k=key;
const char *s=value;
printf("[%d, %s]\n", *k, s);
}
// Compare two keys (int *).
int compare_ints(const void *k1, const void *k2)
{
int key1 = *(int *)k1;
int key2 = *(int *)k2;
if ( key1 == key2 )
return 0;
if ( key1 < key2 )
return -1;
return 1;
}
int main(void)
{
printf("%s, %s %s: Create (integer, string) table without any kill_function.\n",
__FILE__, VERSION, VERSION_DATE);
printf("Code base version %s (%s).\n\n", CODE_BASE_VERSION, CODE_BASE_RELEASE_DATE);
// Keep track of the key-value pairs we allocate.
int *key[4];
char *value[4];
// Keep the deallocation responsibility of the keys/values we allocate.
table *t = table_empty(compare_ints, NULL, NULL);
key[0] = int_create(90187);
value[0] = string_copy("Umea");
table_insert(t, key[0], value[0]);
key[1] = int_create(90184);
value[1] = string_copy("Umea");
table_insert(t, key[1], value[1]);
key[2] = int_create(98185);
value[2] = string_copy("Kiruna");
table_insert(t, key[2], value[2]);
printf("Table after inserting 3 pairs:\n");
table_print(t, print_int_string_pair);
int v = 90187;
const char *s = table_lookup(t, &v);
printf("Lookup of postal code %d: %s.\n", v, s);
key[3] = int_create(90187);
value[3] = string_copy("Umea (Universitet)");
table_insert(t, key[3], value[3]);
printf("Table after adding a duplicate:\n");
table_print(t, print_int_string_pair);
v = 90187;
s = table_lookup(t, &v);
printf("Lookup of postal code %d: %s.\n", v, s);
// Kill the table, excluding the keys and values we entered.
table_kill(t);
// Free key/value pairs that we put in the table.
for (int i = 0; i < sizeof(key)/sizeof(key[0]); i++) {
int_kill(key[i]);
string_kill(value[i]);
}
printf("\nNormal exit.\n\n");
return 0;
}

Some files were not shown because too many files have changed in this diff Show More