├── doc ├── logo.png ├── logo_bw.png ├── existing │ ├── tmgg.png │ ├── hydra.png │ ├── libws.png │ ├── rbakery.png │ └── rpreview.png ├── libstent-2020.odp └── libstent-2020.pdf ├── src ├── tests │ ├── use_null.c │ ├── release_null.c │ ├── leak.c │ ├── dangling_ref.c │ ├── vector_use_null.c │ ├── ref_copy.c │ ├── zero_initialized.c │ ├── dangling_ref_copy.c │ ├── void_cast.c │ ├── vector_dangling_ref.c │ ├── invalid_cast.c │ ├── copy.c │ ├── vector_oob.c │ ├── invalid_void_cast.c │ ├── vector.c │ ├── ref.c │ ├── vec.c │ └── fstream.c └── harness │ └── main.c ├── CMakeLists.txt ├── LICENSE ├── README.md └── include ├── vec.h └── stent.h /doc/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/osen/stent/HEAD/doc/logo.png -------------------------------------------------------------------------------- /doc/logo_bw.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/osen/stent/HEAD/doc/logo_bw.png -------------------------------------------------------------------------------- /doc/existing/tmgg.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/osen/stent/HEAD/doc/existing/tmgg.png -------------------------------------------------------------------------------- /doc/libstent-2020.odp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/osen/stent/HEAD/doc/libstent-2020.odp -------------------------------------------------------------------------------- /doc/libstent-2020.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/osen/stent/HEAD/doc/libstent-2020.pdf -------------------------------------------------------------------------------- /doc/existing/hydra.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/osen/stent/HEAD/doc/existing/hydra.png -------------------------------------------------------------------------------- /doc/existing/libws.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/osen/stent/HEAD/doc/existing/libws.png -------------------------------------------------------------------------------- /doc/existing/rbakery.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/osen/stent/HEAD/doc/existing/rbakery.png -------------------------------------------------------------------------------- /doc/existing/rpreview.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/osen/stent/HEAD/doc/existing/rpreview.png -------------------------------------------------------------------------------- /src/tests/use_null.c: -------------------------------------------------------------------------------- 1 | #define STENT_IMPLEMENTATION 2 | #include 3 | 4 | struct Employee 5 | { 6 | int id; 7 | }; 8 | 9 | int main() 10 | { 11 | ref(Employee) emp = NULL; 12 | 13 | _(emp).id = 9; 14 | 15 | return 0; 16 | } 17 | -------------------------------------------------------------------------------- /src/tests/release_null.c: -------------------------------------------------------------------------------- 1 | #define STENT_IMPLEMENTATION 2 | #include 3 | 4 | struct Employee 5 | { 6 | int id; 7 | }; 8 | 9 | int main() 10 | { 11 | ref(Employee) emp = NULL; 12 | 13 | release(emp); 14 | 15 | return 0; 16 | } 17 | -------------------------------------------------------------------------------- /src/tests/leak.c: -------------------------------------------------------------------------------- 1 | #define STENT_IMPLEMENTATION 2 | #include 3 | 4 | struct Employee 5 | { 6 | int id; 7 | }; 8 | 9 | int main() 10 | { 11 | ref(Employee) emp = NULL; 12 | 13 | emp = allocate(Employee); 14 | _(emp).id = 9; 15 | 16 | return 0; 17 | } 18 | -------------------------------------------------------------------------------- /src/tests/dangling_ref.c: -------------------------------------------------------------------------------- 1 | #define STENT_IMPLEMENTATION 2 | #include 3 | 4 | struct Employee 5 | { 6 | int id; 7 | }; 8 | 9 | int main() 10 | { 11 | ref(Employee) emp = NULL; 12 | 13 | emp = allocate(Employee); 14 | 15 | release(emp); 16 | 17 | _(emp).id = 9; 18 | 19 | return 0; 20 | } 21 | -------------------------------------------------------------------------------- /src/tests/vector_use_null.c: -------------------------------------------------------------------------------- 1 | #define STENT_IMPLEMENTATION 2 | #include 3 | 4 | struct Employee 5 | { 6 | int id; 7 | }; 8 | 9 | int main() 10 | { 11 | vector(struct Employee) emps = NULL; 12 | struct Employee emp = {0}; 13 | 14 | emp.id = 1; 15 | vector_push_back(emps, emp); 16 | 17 | return 0; 18 | } 19 | -------------------------------------------------------------------------------- /src/harness/main.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | int main(int argc, char *argv[]) 4 | { 5 | int rc = 0; 6 | 7 | if(argc < 2) 8 | { 9 | abort(); 10 | } 11 | 12 | rc = system(argv[1]); 13 | 14 | if(rc == 0) 15 | { 16 | rc = 1; 17 | } 18 | else 19 | { 20 | rc = 0; 21 | } 22 | 23 | return rc; 24 | } 25 | -------------------------------------------------------------------------------- /src/tests/ref_copy.c: -------------------------------------------------------------------------------- 1 | #define STENT_IMPLEMENTATION 2 | #include 3 | 4 | ref(Employee) copy = NULL; 5 | 6 | struct Employee 7 | { 8 | int id; 9 | }; 10 | 11 | int main() 12 | { 13 | ref(Employee) emp = NULL; 14 | 15 | emp = allocate(Employee); 16 | copy = emp; 17 | _(copy).id = 9; 18 | release(emp); 19 | 20 | return 0; 21 | } 22 | -------------------------------------------------------------------------------- /src/tests/zero_initialized.c: -------------------------------------------------------------------------------- 1 | #define STENT_IMPLEMENTATION 2 | #include 3 | 4 | struct Employee 5 | { 6 | int id; 7 | }; 8 | 9 | int main() 10 | { 11 | ref(Employee) emp = NULL; 12 | 13 | emp = allocate(Employee); 14 | 15 | if(_(emp).id != 0) 16 | { 17 | abort(); 18 | } 19 | 20 | release(emp); 21 | 22 | return 0; 23 | } 24 | -------------------------------------------------------------------------------- /src/tests/dangling_ref_copy.c: -------------------------------------------------------------------------------- 1 | #define STENT_IMPLEMENTATION 2 | #include 3 | 4 | ref(Employee) copy; 5 | 6 | struct Employee 7 | { 8 | int id; 9 | }; 10 | 11 | int main() 12 | { 13 | ref(Employee) emp = NULL; 14 | 15 | emp = allocate(Employee); 16 | copy = emp; 17 | 18 | release(emp); 19 | 20 | _(copy).id = 9; 21 | 22 | return 0; 23 | } 24 | -------------------------------------------------------------------------------- /src/tests/void_cast.c: -------------------------------------------------------------------------------- 1 | #define STENT_IMPLEMENTATION 2 | #include 3 | 4 | struct Employee 5 | { 6 | int id; 7 | }; 8 | 9 | int main() 10 | { 11 | ref(Employee) emp = NULL; 12 | refvoid vemp = NULL; 13 | 14 | emp = allocate(Employee); 15 | vemp = void_cast(emp); 16 | emp = cast(Employee, vemp); 17 | 18 | _(emp).id = 9; 19 | 20 | release(emp); 21 | 22 | return 0; 23 | } 24 | -------------------------------------------------------------------------------- /src/tests/vector_dangling_ref.c: -------------------------------------------------------------------------------- 1 | #define STENT_IMPLEMENTATION 2 | #include 3 | 4 | struct Employee 5 | { 6 | int id; 7 | }; 8 | 9 | int main() 10 | { 11 | vector(struct Employee) emps = NULL; 12 | struct Employee emp = {0}; 13 | 14 | emps = vector_new(struct Employee); 15 | vector_push_back(emps, emp); 16 | vector_delete(emps); 17 | vector_push_back(emps, emp); 18 | 19 | return 0; 20 | } 21 | -------------------------------------------------------------------------------- /src/tests/invalid_cast.c: -------------------------------------------------------------------------------- 1 | #define STENT_IMPLEMENTATION 2 | #include 3 | 4 | struct Employee 5 | { 6 | int id; 7 | }; 8 | 9 | struct Manager 10 | { 11 | int id; 12 | }; 13 | 14 | int main() 15 | { 16 | ref(Employee) emp = NULL; 17 | ref(Manager) mgr = NULL; 18 | 19 | emp = allocate(Employee); 20 | mgr = cast(Manager, emp); 21 | 22 | _(mgr).id = 9; 23 | 24 | release(emp); 25 | 26 | return 0; 27 | } 28 | -------------------------------------------------------------------------------- /src/tests/copy.c: -------------------------------------------------------------------------------- 1 | #define STENT_IMPLEMENTATION 2 | #include 3 | 4 | struct Employee 5 | { 6 | int id; 7 | int salary; 8 | ref(Employee) manager; 9 | }; 10 | 11 | int main() 12 | { 13 | ref(Employee) emp = NULL; 14 | struct Employee e = {0}; 15 | 16 | e.id = 9; 17 | e.salary = 10; 18 | e.manager = NULL; 19 | 20 | emp = allocate(Employee); 21 | _(emp) = e; 22 | 23 | _(emp).id = 9; 24 | 25 | release(emp); 26 | 27 | return 0; 28 | } 29 | -------------------------------------------------------------------------------- /src/tests/vector_oob.c: -------------------------------------------------------------------------------- 1 | #define STENT_IMPLEMENTATION 2 | #include 3 | 4 | struct Employee 5 | { 6 | int id; 7 | }; 8 | 9 | int main() 10 | { 11 | vector(struct Employee) emps = NULL; 12 | struct Employee emp = {0}; 13 | size_t i = 0; 14 | 15 | emps = vector_new(struct Employee); 16 | 17 | for(i = 0; i < 1000; i++) 18 | { 19 | emp.id = i; 20 | vector_push_back(emps, emp); 21 | } 22 | 23 | vector_erase(emps, 500, 100); 24 | vector_at(emps, 900).id = 9; 25 | 26 | vector_delete(emps); 27 | 28 | return 0; 29 | } 30 | -------------------------------------------------------------------------------- /src/tests/invalid_void_cast.c: -------------------------------------------------------------------------------- 1 | #define STENT_IMPLEMENTATION 2 | #include 3 | 4 | struct Employee 5 | { 6 | int id; 7 | }; 8 | 9 | struct Manager 10 | { 11 | int id; 12 | }; 13 | 14 | int main() 15 | { 16 | ref(Employee) emp = NULL; 17 | refvoid vemp = NULL; 18 | ref(Manager) mgr = NULL; 19 | 20 | emp = allocate(Employee); 21 | vemp = void_cast(emp); 22 | 23 | /* 24 | * Error: Attempt to cast Employee into Manager. 25 | */ 26 | mgr = cast(Manager, vemp); 27 | _(mgr).id = 9; 28 | 29 | release(emp); 30 | 31 | return 0; 32 | } 33 | -------------------------------------------------------------------------------- /src/tests/vector.c: -------------------------------------------------------------------------------- 1 | #define STENT_IMPLEMENTATION 2 | #include 3 | 4 | struct Employee 5 | { 6 | int id; 7 | }; 8 | 9 | int main() 10 | { 11 | vector(struct Employee) emps = NULL; 12 | struct Employee emp = {0}; 13 | size_t i = 0; 14 | 15 | emps = vector_new(struct Employee); 16 | 17 | for(i = 0; i < 1000; i++) 18 | { 19 | emp.id = i; 20 | vector_push_back(emps, emp); 21 | } 22 | 23 | vector_erase(emps, 500, 100); 24 | 25 | if(vector_size(emps) != 900) 26 | { 27 | abort(); 28 | } 29 | 30 | if(vector_at(emps, 899).id != 999) 31 | { 32 | abort(); 33 | } 34 | 35 | vector_delete(emps); 36 | 37 | return 0; 38 | } 39 | -------------------------------------------------------------------------------- /src/tests/ref.c: -------------------------------------------------------------------------------- 1 | #define STENT_IMPLEMENTATION 2 | #include 3 | 4 | struct Employee 5 | { 6 | int id; 7 | int wage; 8 | }; 9 | 10 | ref(Employee) EmployeeCreate() 11 | { 12 | ref(Employee) rtn = NULL; 13 | 14 | rtn = allocate(Employee); 15 | _(rtn).id = 9; 16 | 17 | return rtn; 18 | } 19 | 20 | void EmployeeDestroy(ref(Employee) ctx) 21 | { 22 | release(ctx); 23 | } 24 | 25 | void EmployeeAddWage(ref(Employee) ctx, int amount) 26 | { 27 | _(ctx).wage += amount; 28 | } 29 | 30 | int main() 31 | { 32 | ref(Employee) emp = NULL; 33 | 34 | emp = EmployeeCreate(); 35 | EmployeeAddWage(emp, 10); 36 | EmployeeDestroy(emp); 37 | 38 | return 0; 39 | } 40 | -------------------------------------------------------------------------------- /src/tests/vec.c: -------------------------------------------------------------------------------- 1 | #define VEC_IMPLEMENTATION 2 | #include 3 | 4 | struct Employee 5 | { 6 | int id; 7 | }; 8 | 9 | int main() 10 | { 11 | vec(struct Employee) emps = NULL; 12 | vec(int) ids = NULL; 13 | size_t i = 0; 14 | 15 | ids = vec_new(int); 16 | emps = vec_new(struct Employee); 17 | 18 | for(i = 0; i < 1000; i++) 19 | { 20 | struct Employee emp = {0}; 21 | emp.id = i; 22 | vec_push_back(emps, emp); 23 | vec_push_back(ids, i); 24 | } 25 | 26 | vec_erase(emps, 500, 100); 27 | vec_erase(ids, 500, 100); 28 | 29 | if(vec_size(emps) != 900) 30 | { 31 | abort(); 32 | } 33 | 34 | if(vec_size(ids) != 900) 35 | { 36 | abort(); 37 | } 38 | 39 | if(vec_at(emps, 899).id != 999) 40 | { 41 | abort(); 42 | } 43 | 44 | if(vec_at(ids, 899) != 999) 45 | { 46 | abort(); 47 | } 48 | 49 | vec_delete(emps); 50 | vec_delete(ids); 51 | 52 | return 0; 53 | } 54 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.0) 2 | project(STENT C) 3 | 4 | add_definitions(-D_XOPEN_SOURCE) 5 | set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -std=c99 -pedantic -Wall -Werror") 6 | 7 | include_directories( 8 | include 9 | ) 10 | 11 | function(create_test NAME RETURN_ZERO) 12 | add_executable(${NAME} src/tests/${NAME}.c) 13 | 14 | if(RETURN_ZERO) 15 | add_test(${NAME} ./${NAME}) 16 | else() 17 | add_test(${NAME} harness ./${NAME}) 18 | endif() 19 | endfunction() 20 | 21 | add_executable(harness 22 | src/harness/main.c 23 | ) 24 | 25 | enable_testing() 26 | 27 | create_test(ref true) 28 | create_test(ref_copy true) 29 | create_test(copy true) 30 | create_test(zero_initialized true) 31 | create_test(void_cast true) 32 | create_test(vector true) 33 | create_test(fstream true) 34 | 35 | create_test(vec true) 36 | 37 | create_test(dangling_ref false) 38 | create_test(dangling_ref_copy false) 39 | create_test(invalid_cast false) 40 | create_test(invalid_void_cast false) 41 | create_test(use_null false) 42 | create_test(release_null false) 43 | create_test(leak false) 44 | create_test(vector_oob false) 45 | create_test(vector_use_null false) 46 | create_test(vector_dangling_ref false) 47 | # vector_leak 48 | # vector_incorrect_delete 49 | -------------------------------------------------------------------------------- /src/tests/fstream.c: -------------------------------------------------------------------------------- 1 | #define STENT_IMPLEMENTATION 2 | #include 3 | 4 | #include 5 | 6 | int main() 7 | { 8 | ref(ifstream) input = NULL; 9 | ref(sstream) line = NULL; 10 | ref(sstream) total = NULL; 11 | vector(unsigned char) data = NULL; 12 | 13 | input = ifstream_open_cstr("CMakeCache.txt"); 14 | 15 | if(!input) 16 | { 17 | input = ifstream_open_cstr("CMakeLists.txt"); 18 | } 19 | 20 | if(!input) 21 | { 22 | abort(); 23 | } 24 | 25 | line = sstream_new(); 26 | total = sstream_new(); 27 | 28 | while(!ifstream_eof(input)) 29 | { 30 | ifstream_getline(input, line); 31 | printf("Line: [%s]\n", sstream_cstr(line)); 32 | sstream_append(total, line); 33 | } 34 | 35 | printf("Total: [%s]\n", sstream_cstr(total)); 36 | 37 | sstream_delete(total); 38 | sstream_delete(line); 39 | ifstream_close(input); 40 | 41 | input = ifstream_open_cstr("CMakeCache.txt"); 42 | 43 | if(!input) 44 | { 45 | input = ifstream_open_cstr("CMakeLists.txt"); 46 | } 47 | 48 | if(!input) 49 | { 50 | abort(); 51 | } 52 | 53 | data = vector_new(unsigned char); 54 | vector_resize(data, 500); 55 | ifstream_read(input, data); 56 | printf("Read: %i\n", (int)vector_size(data)); 57 | 58 | ifstream_close(input); 59 | vector_delete(data); 60 | 61 | return 0; 62 | } 63 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2019, Karsten Pedersen 2 | All rights reserved. 3 | 4 | Redistribution and use in source and binary forms, with or without 5 | modification, are permitted provided that the following conditions are met: 6 | 7 | * Redistributions of source code must retain the above copyright notice, this 8 | list of conditions and the following disclaimer. 9 | 10 | * Redistributions in binary form must reproduce the above copyright notice, 11 | this list of conditions and the following disclaimer in the documentation 12 | and/or other materials provided with the distribution. 13 | 14 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 15 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 17 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 18 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 20 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 21 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 22 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 23 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 24 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ![Stent Logo](/doc/logo.png) 2 | 3 | Stent 4 | ===== 5 | Completely avoid dangling pointers in C. 6 | 7 | How It Works 8 | ------------ 9 | By borrowing concepts from std::weak_ptr in C++ (or more closely 10 | wxWeakRef from wxWidgets), Memory errors in C can appear in a more 11 | deterministic manner without severely impacting the design of program 12 | code. 13 | 14 | Rather than using a raw pointer, the developer uses a pointer wrapper 15 | which is guaranteed to return NULL as soon as the original data 16 | being pointed to is freed. Rather than return NULL, Stent instead 17 | displays a useful debug error and aborts the program, letting the 18 | developer know that there is an issue with the code. 19 | 20 | **Note:** Stent is intended to be enabled only during debugging. 21 | For release builds, it can be disabled and the program will incur 22 | no additional costs to the performance at runtime. 23 | 24 | Getting Started 25 | --------------- 26 | There are a few considerations that a developer needs to make before 27 | using Stent. The following instructions should provide a brief overview 28 | of the steps required. However, a quick look through the *src/tests/ref.c* 29 | program should also explain these concepts. 30 | 31 | Include the "stent.h" header file. 32 | 33 | #define STENT_IMPLEMENTATION 34 | #include 35 | 36 | **Note:** Exactly one compilation unit must define *STENT_IMPLEMENTATION* 37 | before including the header in order for the implementation of Stent 38 | to be compiled correctly as a header-only library. 39 | 40 | Declare a variable to store the pointer: 41 | 42 | struct SomeStruct *someStruct = NULL; // Standard C 43 | ref(SomeStruct) someStruct = NULL; // Using Stent 44 | 45 | To dereference a pointer: 46 | 47 | someStruct->someData = 6; // Standard C 48 | _(someStruct).someData = 6; // Using Stent 49 | 50 | To allocate dynamic memory: 51 | 52 | someStruct = calloc(1, sizeof(*someStruct)); // Standard C 53 | someStruct = allocate(SomeStruct); // Using Stent 54 | 55 | To free dynamic memory: 56 | 57 | free(someStruct); // Standard C 58 | release(someStruct); // Using Stent 59 | 60 | **Note:** When using Stent, any time you subsequently dereference 61 | the pointer using `_(ptr)`, it will abort since the memory pointed 62 | to is no longer valid. This means that without checking, the program 63 | will reliably crash in the debug build. 64 | 65 | Forward declaring a structure: 66 | 67 | struct SomeStruct; // Same for both Standard C and Stent 68 | 69 | Defining a structure containing a pointer: 70 | 71 | struct Test 72 | { 73 | struct SomeStruct *someStruct; // Standard C 74 | ref(SomeStruct) someStruct; // Using Stent 75 | }; 76 | 77 | Pass pointer into function (Function prototypes): 78 | 79 | void SomeStructDoSomething(struct SomeStruct *someStruct); // Standard C 80 | void SomeStructDoDomething(ref(SomeStruct) someStruct); // Using Stent 81 | 82 | Pass pointer into function (Calling function): 83 | 84 | SomeStructDoSomething(someStruct); // Same for both Standard C and Stent 85 | 86 | Leak Detector 87 | ------------- 88 | There is also basic support for leak detection. Have a look at the 89 | *src/tests/leak.c* program for more information. 90 | 91 | For a simple example, if a leak exists, a message will appear such as: 92 | 93 | ***** Memory Leak ***** 94 | Type: struct Test 95 | File: example/leak/main.c 96 | Line: 14 97 | 98 | This is possible because Stent creates an "atexit" hook that upon 99 | program termination, will scan internal memory for a list of data 100 | yet to be freed. 101 | 102 | -------------------------------------------------------------------------------- /include/vec.h: -------------------------------------------------------------------------------- 1 | #ifndef _VEC_H 2 | #define _VEC_H 3 | 4 | #include 5 | 6 | #define vec(T) \ 7 | T ** 8 | 9 | #define vec_new(T) \ 10 | (vec(T))_vec_new(sizeof(T)) 11 | 12 | #define vec_delete(V) \ 13 | _vec_delete((vec(void))V) 14 | 15 | #define vec_size(V) \ 16 | _vec_size((vec(void))V) 17 | 18 | #define vec_compare(V1, V2) \ 19 | _vec_compare((vec(void))V1, (vec(void))V2) 20 | 21 | #define vec_push_back(V, E) \ 22 | do \ 23 | { \ 24 | _vec_resize((vec(void))V, vec_size(V) + 1); \ 25 | V[0][vec_size(V) - 1] = E; \ 26 | } \ 27 | while(0) 28 | 29 | #define vec_resize(V, S) \ 30 | _vec_resize((vec(void))V, S) 31 | 32 | #define vec_clear(V) \ 33 | _vec_clear((vec(void))V) 34 | 35 | #define vec_at(V, I) \ 36 | (V[0][I]) 37 | 38 | #define vec_erase(V, I, N) \ 39 | _vec_erase((vec(void))V, I, N) 40 | 41 | #define vec_insert(V, B, S, I, N) \ 42 | _vec_insert((vec(void))V, B, (vec(void))S, I, N) 43 | 44 | #define vec_push(V, E) \ 45 | vec_push_back(V, E) 46 | 47 | #define vec_fill(V, S, N, D) \ 48 | do \ 49 | { \ 50 | size_t __start = S; \ 51 | size_t __num = N; \ 52 | size_t vi = 0; \ 53 | vec_at(V, __start) = D; \ 54 | for(vi = __start; vi < __start + __num; ++vi) \ 55 | { \ 56 | vec_at(V, vi) = vec_at(V, __start); \ 57 | } \ 58 | } \ 59 | while(0) 60 | 61 | #define foreach(VAR, VEC, BODY) \ 62 | { \ 63 | size_t __var_i = 0; \ 64 | size_t __var_size = vec_size(VEC); \ 65 | for(; __var_i < __var_size; ++__var_i) \ 66 | { \ 67 | VAR = vec_at(VEC, __var_i); \ 68 | BODY \ 69 | } \ 70 | } 71 | 72 | #ifdef __cplusplus 73 | extern "C" 74 | { 75 | #endif 76 | 77 | vec(void) _vec_new(size_t size); 78 | void _vec_delete(vec(void) ptr); 79 | size_t _vec_size(vec(void) ptr); 80 | int _vec_compare(vec(void) ptr1, vec(void) ptr2); 81 | void _vec_resize(vec(void) ptr, size_t size); 82 | void _vec_clear(vec(void) ptr); 83 | void _vec_erase(vec(void) ptr, size_t idx, size_t num); 84 | 85 | void _vec_insert(vec(void) ptr, size_t before, 86 | vec(void) source, size_t idx, size_t num); 87 | 88 | void panic(const char *message); 89 | 90 | #ifdef __cplusplus 91 | } 92 | #endif 93 | 94 | #ifdef VEC_IMPLEMENTATION 95 | #undef VEC_IMPLEMENTATION 96 | 97 | #include 98 | #include 99 | 100 | /*************************************************** 101 | * Vector 102 | ***************************************************/ 103 | 104 | struct Vec 105 | { 106 | char *data; 107 | size_t size; 108 | size_t allocated; 109 | size_t elementSize; 110 | }; 111 | 112 | #ifdef __cplusplus 113 | extern "C" 114 | { 115 | #endif 116 | 117 | vec(void) _vec_new(size_t size) 118 | { 119 | struct Vec *rtn = calloc(1, sizeof(*rtn)); 120 | if(!rtn) return NULL; 121 | 122 | rtn->elementSize = size; 123 | 124 | return (vec(void))rtn; 125 | } 126 | 127 | void _vec_delete(vec(void) ptr) 128 | { 129 | struct Vec *v = (struct Vec *)ptr; 130 | 131 | free(v->data); 132 | free(v); 133 | } 134 | 135 | size_t _vec_size(vec(void) ptr) 136 | { 137 | struct Vec *v = (struct Vec *)ptr; 138 | 139 | return v->size; 140 | } 141 | 142 | int _vec_compare(vec(void) ptr1, vec(void) ptr2) 143 | { 144 | struct Vec *v1 = (struct Vec *)ptr1; 145 | struct Vec *v2 = (struct Vec *)ptr2; 146 | 147 | /* 148 | * TODO: Check if 1 or -1. 149 | */ 150 | if(v1->size != v2->size) 151 | { 152 | return 1; 153 | } 154 | 155 | return memcmp(v1->data, v2->data, v1->size); 156 | } 157 | 158 | void _vec_clear(vec(void) ptr) 159 | { 160 | struct Vec *v = (struct Vec *)ptr; 161 | 162 | v->size = 0; 163 | } 164 | 165 | void _vec_resize(vec(void) ptr, size_t size) 166 | { 167 | struct Vec *v = (struct Vec *)ptr; 168 | size_t s = 0; 169 | void *d = NULL; 170 | 171 | if(v->allocated >= size) 172 | { 173 | v->size = size; 174 | return; 175 | } 176 | 177 | s = 1; 178 | 179 | while(1) 180 | { 181 | if(s >= size) 182 | { 183 | break; 184 | } 185 | 186 | s = s * 2; 187 | } 188 | 189 | d = calloc(s, v->elementSize); 190 | 191 | if(!d) 192 | { 193 | fprintf(stderr, "Error: Failed to increase vec size\n"); 194 | abort(); 195 | } 196 | 197 | memcpy(d, v->data, v->elementSize * v->size); 198 | free(v->data); 199 | v->data = d; 200 | v->allocated = s; 201 | v->size = size; 202 | } 203 | 204 | size_t _vec_valid(vec(void) ptr, size_t idx) 205 | { 206 | struct Vec *v = (struct Vec *)ptr; 207 | 208 | if(v->size > idx) 209 | { 210 | return idx; 211 | } 212 | 213 | fprintf(stderr, "Error: Index [index=%lu] out of bounds [size=%lu]\n", 214 | (unsigned long)idx, (unsigned long)v->size); 215 | abort(); 216 | } 217 | 218 | void _vec_erase(vec(void) ptr, size_t idx, size_t num) 219 | { 220 | struct Vec *v = (struct Vec *)ptr; 221 | char *dest = NULL; 222 | char *src = NULL; 223 | size_t tm = 0; 224 | 225 | if(idx >= v->size || 226 | idx + num > v->size) 227 | { 228 | fprintf(stderr, "Error: Index out of bounds [size=%i] [index=%i] [num=%i]\n", (int)v->size, (int)idx, (int)num); 229 | abort(); 230 | } 231 | 232 | if(!num) 233 | { 234 | return; 235 | } 236 | 237 | dest = (char *)v->data; 238 | dest += idx * v->elementSize; 239 | 240 | src = dest; 241 | src += num * v->elementSize; 242 | 243 | tm = (v->size - (idx + num)) * v->elementSize; 244 | 245 | if(tm) 246 | { 247 | memmove(dest, src, tm); 248 | } 249 | 250 | v->size -= num; 251 | } 252 | 253 | void _vec_insert(vec(void) ptr, size_t before, 254 | vec(void) source, size_t idx, size_t num) 255 | { 256 | struct Vec *d = (struct Vec *)ptr; 257 | struct Vec *s = (struct Vec *)source; 258 | char *dest = NULL; 259 | char *src = NULL; 260 | size_t tm = 0; 261 | 262 | if(s == d) 263 | { 264 | fprintf(stderr, "Error: Source and desination must not match\n"); 265 | abort(); 266 | } 267 | 268 | if(!num) 269 | { 270 | return; 271 | } 272 | 273 | if(before > d->size) 274 | { 275 | fprintf(stderr, "Error: Invalid index specified. Non contiguous\n"); 276 | abort(); 277 | } 278 | 279 | if(idx >= s->size || 280 | idx + num > s->size) 281 | { 282 | fprintf(stderr, "Error: Index out of bounds on source\n"); 283 | abort(); 284 | } 285 | 286 | tm = (d->size - before) * d->elementSize; 287 | 288 | _vec_resize(ptr, d->size + num); 289 | 290 | src = (char *)d->data; 291 | src += (before * d->elementSize); 292 | dest = src; 293 | dest += (num * d->elementSize); 294 | memmove(dest, src, tm); 295 | 296 | dest = src; 297 | src = (char *)s->data; 298 | src += (idx * d->elementSize); 299 | memcpy(dest, src, num * s->elementSize); 300 | } 301 | 302 | void panic(const char *message) 303 | { 304 | printf("Panic: %s\n", message); 305 | 306 | abort(); 307 | } 308 | 309 | #ifdef __cplusplus 310 | } 311 | #endif 312 | 313 | #endif 314 | 315 | #endif 316 | -------------------------------------------------------------------------------- /include/stent.h: -------------------------------------------------------------------------------- 1 | /***************************************************************************** 2 | * stent.h 3 | * 4 | * TODO 5 | * - Ensure memcpy compile time checks are never actually executed 6 | * - _svalid doesn't really need to return an int? 7 | * - _svalid should ensure passed in pointer is within allocated blocks 8 | * - Can temporaries be supported? 9 | *****************************************************************************/ 10 | #ifdef __cplusplus 11 | extern "C" 12 | { 13 | #endif 14 | 15 | #ifndef STENT_STENT_H 16 | #define STENT_STENT_H 17 | 18 | #include 19 | #include 20 | 21 | /***************************************************************************** 22 | * STENT_ENABLE 23 | * 24 | * Compile time flag to enable the tool. If disabled then dummy operations are 25 | * used instead. Facilities such as vector(T) are always available. 26 | *****************************************************************************/ 27 | #ifndef STENT_DISABLE 28 | #define STENT_ENABLE 29 | #endif 30 | 31 | #define STENT_BLOCKSIZE 1024 32 | 33 | #ifdef STENT_ENABLE 34 | 35 | /***************************************************************************** 36 | * refvoid 37 | * 38 | * MACRO to describe a void reference. This is mostly a convenience type as 39 | * it is used to pass into the generic utility functions for the tool. It is 40 | * similar to the ref(T) MACRO but avoids the struct requirement. 41 | *****************************************************************************/ 42 | #define refvoid \ 43 | char ** 44 | 45 | /***************************************************************************** 46 | * ref(T) 47 | * 48 | * MACRO to describe a pointer tracked by the tool. When enabled this type has 49 | * an extra indirection because it is stored within a structure which can be 50 | * subsequently checked for a deletion flag prior to use. To access the raw 51 | * pointer it needs an additional dereference (via the _(P) MACRO) in order 52 | * check or validity and then to obtain the final pointer location (first 53 | * element of the referenced structure). This is the key component of this 54 | * tool. 55 | *****************************************************************************/ 56 | #define ref(T) \ 57 | struct T ** 58 | 59 | /***************************************************************************** 60 | * _(P) 61 | * 62 | * MACRO to pass the reference into the implementation along with the unit 63 | * file name and line number for debug purposes. The memcmp calls ensure that 64 | * the specified reference is of the correct format for the tool. The returned 65 | * value is the first element of the passed in parameter to ensure type safety 66 | * and avoiding the need to manually cast. 67 | *****************************************************************************/ 68 | #define _(R) \ 69 | ((_assert_ref(R))[0][0]) 70 | 71 | #define _assert_ref(R) \ 72 | ((_svalid((refvoid)R, __FILE__, __LINE__) || \ 73 | memcmp(&R, &R, 0) || \ 74 | /* TODO: Not required */ \ 75 | /* memcmp(&R[0], &R[0], 0) || */ \ 76 | memcpy(&R[0][0], &R[0][0], 0) || \ 77 | 1) ? R : NULL) 78 | 79 | /***************************************************************************** 80 | * allocate(T) 81 | * 82 | * MACRO to obtain the size and name string of specified type. This is passed 83 | * into the utility function which does the actual allocation. The data is 84 | * then explicitly casted to the specified type to ensure that the entire 85 | * operation is type safe. 86 | *****************************************************************************/ 87 | #define allocate(T) \ 88 | (ref(T))_stent_alloc(sizeof(struct T), #T, __FILE__, __LINE__) 89 | 90 | /***************************************************************************** 91 | * release(P) 92 | * 93 | * MACRO to ensure that specified pointer is in the correct format for the 94 | * tool and that it is not a temporary. The specified reference is then casted 95 | * and passed into the implementation along with file name of the unit and the 96 | * line number for debug purposes. 97 | *****************************************************************************/ 98 | #define release(R) \ 99 | _stent_free((refvoid)_assert_ref(R), __FILE__, __LINE__); 100 | 101 | /***************************************************************************** 102 | * cast 103 | * 104 | * MACRO to pass the type string and reference to check into the 105 | * implementation. Also pass through the unit file name and line number for 106 | * debug purposes. The returned refvoid is casted to the requested type if 107 | * the implementation did not cause an incorrect type error. 108 | *****************************************************************************/ 109 | #define cast(T, R) \ 110 | (ref(T))_stent_cast(#T, (refvoid)_assert_ref(R), __FILE__, __LINE__) 111 | 112 | /***************************************************************************** 113 | * void_cast 114 | * 115 | * TODO 116 | *****************************************************************************/ 117 | #define void_cast(R) \ 118 | _stent_cast("void", (refvoid)_assert_ref(R), __FILE__, __LINE__) 119 | 120 | refvoid _stent_alloc(size_t size, const char *type, const char *file, int line); 121 | void _stent_free(refvoid ptr, const char *file, size_t line); 122 | 123 | refvoid _stent_cast(const char *type, refvoid ptr, 124 | const char *file, size_t line); 125 | 126 | int _svalid(refvoid ptr, const char *file, size_t line); 127 | 128 | /*************************************************** 129 | * Array 130 | ***************************************************/ 131 | size_t _array_check(size_t as, size_t es, size_t idx); 132 | 133 | #define array_at(A, I) \ 134 | (A[_array_check(sizeof(A), sizeof(A[0]), I)]) 135 | 136 | /*************************************************** 137 | * Vector 138 | ***************************************************/ 139 | 140 | #define vector(T) \ 141 | T *** 142 | 143 | #define _assert_vector(V) \ 144 | ((_svalid((refvoid)V, __FILE__, __LINE__) || \ 145 | memcmp(&V, &V, 0) || \ 146 | /* TODO: Not required */ \ 147 | /* memcmp(&V[0], &V[0], 0) || */ \ 148 | /* memcpy(&V[0][0], &V[0][0], 0) || */ \ 149 | memcpy(&V[0][0][0], &V[0][0][0], 0) || \ 150 | 1) ? V : NULL) 151 | 152 | #define vector_new(T) \ 153 | (vector(T))_vector_new(sizeof(T), "vector("#T")", __FILE__, __LINE__) 154 | 155 | #define vector_delete(V) \ 156 | _vector_delete((vector(void))_assert_vector(V), __FILE__, __LINE__) 157 | 158 | #define vector_size(V) \ 159 | _vector_size((vector(void))_assert_vector(V)) 160 | 161 | #define vector_compare(V1, V2) \ 162 | _vector_compare( \ 163 | (vector(void))_assert_vector(V1), \ 164 | (vector(void))_assert_vector(V2)) 165 | 166 | #define vector_erase(V, I, N) \ 167 | _vector_erase((vector(void))_assert_vector(V), I, N) 168 | 169 | #define vector_push_back(V, E) \ 170 | do \ 171 | { \ 172 | _vector_resize((vector(void))_assert_vector(V), vector_size(V) + 1); \ 173 | _(V)[vector_size(V) - 1] = E; \ 174 | } \ 175 | while(0) 176 | 177 | #define vector_resize(V, S) \ 178 | do \ 179 | { \ 180 | _vector_resize((vector(void))_assert_vector(V), S); \ 181 | } \ 182 | while(0) 183 | 184 | #define vector_clear(V) \ 185 | do \ 186 | { \ 187 | _vector_clear((vector(void))_assert_vector(V)); \ 188 | } \ 189 | while(0) 190 | 191 | #define vector_at(V, I) \ 192 | (_(V)[_vector_valid((vector(void))_assert_vector(V), I)]) 193 | 194 | #define vector_insert(V, B, S, I, N) \ 195 | _vector_insert((vector(void))_assert_vector(V), B, (vector(void))_assert_vector(S), I, N) 196 | 197 | vector(void) _vector_new(size_t size, const char *type, const char *file, int line); 198 | void _vector_delete(vector(void) ptr, const char *file, size_t line); 199 | size_t _vector_size(vector(void) ptr); 200 | int _vector_compare(vector(void) ptr1, vector(void) ptr2); 201 | void _vector_resize(vector(void) ptr, size_t size); 202 | void _vector_clear(vector(void) ptr); 203 | size_t _vector_valid(vector(void) ptr, size_t idx); 204 | size_t _sstream_valid(vector(void) ptr, size_t idx); 205 | void _vector_erase(vector(void) ptr, size_t idx, size_t num); 206 | 207 | void _vector_insert(vector(void) ptr, size_t before, 208 | vector(void) source, size_t idx, size_t num); 209 | 210 | /* 211 | unsigned char sstream_at(ref(sstream) ctx, size_t idx); 212 | 213 | #define sstream_at(S, I) \ 214 | vector_at(_(S).data, I) 215 | 216 | #define sstream_at(S, I) \ 217 | (_(_(S).data)[_vector_valid((vector(void))_assert_vector(_(S).data), I + 1) - 1]) 218 | */ 219 | 220 | #define sstream_at(S, I) \ 221 | (_(_(S).data)[_sstream_valid((vector(void))_assert_vector(_(S).data), I)]) 222 | 223 | #else 224 | 225 | /*************************************************** 226 | * Dummy 227 | ***************************************************/ 228 | 229 | #define refvoid \ 230 | char * 231 | 232 | #define ref(T) \ 233 | struct T * 234 | 235 | #define allocate(T) \ 236 | (ref(T))calloc(1, sizeof(struct T)) 237 | 238 | #define release(R) \ 239 | free(R) 240 | 241 | #define cast(T, R) \ 242 | (ref(T))R 243 | 244 | #define void_cast(R) \ 245 | (refvoid)R 246 | 247 | #define _(R) \ 248 | R[0] 249 | 250 | #define vector(T) \ 251 | T ** 252 | 253 | #define vector_new(T) \ 254 | (vector(T))_vector_new(sizeof(T)) 255 | 256 | #define vector_delete(V) \ 257 | _vector_delete((vector(void))V) 258 | 259 | #define vector_size(V) \ 260 | _vector_size((vector(void))V) 261 | 262 | #define vector_compare(V1, V2) \ 263 | _vector_compare((vector(void))V1, (vector(void))V2) 264 | 265 | #define vector_push_back(V, E) \ 266 | do \ 267 | { \ 268 | _vector_resize((vector(void))V, vector_size(V) + 1); \ 269 | _(V)[vector_size(V) - 1] = E; \ 270 | } \ 271 | while(0) 272 | 273 | #define vector_resize(V, S) \ 274 | _vector_resize((vector(void))V, S) 275 | 276 | #define vector_clear(V) \ 277 | _vector_clear((vector(void))V) 278 | 279 | #define vector_at(V, I) \ 280 | (_(V)[I]) 281 | 282 | #define vector_erase(V, I, N) \ 283 | _vector_erase((vector(void))V, I, N) 284 | 285 | #define vector_insert(V, B, S, I, N) \ 286 | _vector_insert((vector(void))V, B, (vector(void))S, I, N) 287 | 288 | vector(void) _vector_new(size_t size); 289 | void _vector_delete(vector(void) ptr); 290 | size_t _vector_size(vector(void) ptr); 291 | int _vector_compare(vector(void) ptr1, vector(void) ptr2); 292 | void _vector_resize(vector(void) ptr, size_t size); 293 | void _vector_clear(vector(void) ptr); 294 | /* 295 | size_t _vector_valid(vector(void) ptr, size_t idx); 296 | */ 297 | void _vector_erase(vector(void) ptr, size_t idx, size_t num); 298 | 299 | void _vector_insert(vector(void) ptr, size_t before, 300 | vector(void) source, size_t idx, size_t num); 301 | 302 | #define array_at(A, I) (A[I]) 303 | 304 | #define sstream_at(S, I) \ 305 | (vector_at(_(S).data, I)) 306 | 307 | #endif 308 | 309 | /*************************************************** 310 | * Both 311 | ***************************************************/ 312 | #define foreach(VAR, VEC, BODY) \ 313 | { \ 314 | size_t __var_i = 0; \ 315 | size_t __var_size = vector_size(VEC); \ 316 | for(; __var_i < __var_size; ++__var_i) \ 317 | { \ 318 | VAR = vector_at(VEC, __var_i); \ 319 | BODY \ 320 | } \ 321 | } 322 | 323 | #define vector_push(V, E) \ 324 | vector_push_back(V, E) 325 | 326 | #define vector_fill(V, S, N, D) \ 327 | do \ 328 | { \ 329 | size_t __start = S; \ 330 | size_t __num = N; \ 331 | size_t vi = 0; \ 332 | vector_at(V, __start) = D; \ 333 | for(vi = __start; vi < __start + __num; ++vi) \ 334 | { \ 335 | vector_at(V, vi) = vector_at(V, __start); \ 336 | } \ 337 | } \ 338 | while(0) 339 | 340 | /*************************************************** 341 | * String Stream 342 | ***************************************************/ 343 | 344 | /* 345 | struct sstream; 346 | */ 347 | struct sstream 348 | { 349 | vector(unsigned char) data; 350 | }; 351 | 352 | ref(sstream) sstream_new(); 353 | ref(sstream) sstream_new_str(ref(sstream) other); 354 | ref(sstream) sstream_new_cstr(const char *str); 355 | void sstream_delete(ref(sstream) ctx); 356 | 357 | void sstream_str_cstr(ref(sstream) ctx, const char *str); 358 | void sstream_str(ref(sstream) ctx, ref(sstream) str); 359 | void sstream_append(ref(sstream) ctx, ref(sstream) str); 360 | void sstream_append_char(ref(sstream) ctx, unsigned char c); 361 | void sstream_append_cstr(ref(sstream) ctx, const char *str); 362 | void sstream_append_int(ref(sstream) ctx, int val); 363 | void sstream_append_float(ref(sstream) ctx, float val); 364 | 365 | int sstream_compare_cstr(ref(sstream) ctx, const char *str); 366 | int sstream_compare(ref(sstream) ctx, ref(sstream) other); 367 | 368 | void sstream_split(ref(sstream) ctx, unsigned char c, vector(ref(sstream)) out); 369 | void sstream_split_eol(ref(sstream) ctx, vector(ref(sstream)) out); 370 | char *sstream_cstr(ref(sstream) ctx); 371 | 372 | size_t sstream_length(ref(sstream) ctx); 373 | void sstream_erase(ref(sstream) ctx, size_t idx, size_t num); 374 | void sstream_resize(ref(sstream) ctx, size_t length); 375 | 376 | void sstream_insert(ref(sstream) dest, size_t dbegin, ref(sstream) source, size_t sbegin, size_t count); 377 | 378 | vector(unsigned char) sstream_raw(ref(sstream) ctx); 379 | 380 | int sstream_cmp_cstr(ref(sstream) ctx, const char *str); 381 | 382 | /* TODO: Use sstream_str_cstr */ 383 | /* void sstream_clear(ref(sstream) ctx); */ 384 | 385 | /*************************************************** 386 | * File Stream 387 | ***************************************************/ 388 | 389 | struct ifstream; 390 | 391 | ref(ifstream) ifstream_popen_cstr(const char *cmd); 392 | ref(ifstream) ifstream_popen(ref(sstream) cmd); 393 | ref(ifstream) ifstream_open_cstr(const char *path); 394 | ref(ifstream) ifstream_open(ref(sstream) path); 395 | void ifstream_close(ref(ifstream) ctx); 396 | int ifstream_eof(ref(ifstream) ctx); 397 | void ifstream_getline(ref(ifstream) ctx, ref(sstream) out); 398 | void ifstream_read(ref(ifstream) ctx, vector(unsigned char) out); 399 | 400 | /*************************************************** 401 | * Directory Handling 402 | ***************************************************/ 403 | 404 | struct dir; 405 | 406 | ref(dir) dir_open_cstr(char *path); 407 | ref(dir) dir_open(ref(sstream) path); 408 | void dir_close(ref(dir) ctx); 409 | 410 | /*************************************************** 411 | * Error Handling 412 | ***************************************************/ 413 | 414 | void panic(const char *message); 415 | 416 | #endif 417 | 418 | #ifdef STENT_IMPLEMENTATION 419 | #undef STENT_IMPLEMENTATION 420 | /***************************************************************************** 421 | * STENT_IMPLEMENTATION 422 | * 423 | * This header is a combined definition and implementation. This means that 424 | * the implementation needs to be compiled once. The best practice is to 425 | * define STENT_IMPLEMENTATION and include stent.h once within the same unit 426 | * containing the main function. For the rest of the program, just include 427 | * stent.h as needed. 428 | *****************************************************************************/ 429 | 430 | #include 431 | #include 432 | 433 | #ifdef STENT_ENABLE 434 | 435 | /***************************************************************************** 436 | * Allocation 437 | * 438 | * Structure containing information about individual allocations. The ptr 439 | * must be the first element to allow the additional indirection of the 440 | * type-safe reference to work. The memory pointed to by ptr may get freed 441 | * but the Allocation structure itself persists throughout the lifespan of the 442 | * program. 443 | *****************************************************************************/ 444 | struct Allocation 445 | { 446 | void *ptr; /* Pointer to the native C memory block */ 447 | int expired; /* Track whether allocation has been freed */ 448 | const char *type; /* The specified type for run-time type identification */ 449 | const char *file; /* The source unit file the memory was allocated in */ 450 | int line; /* The line number the memory was allocated in */ 451 | }; 452 | 453 | /***************************************************************************** 454 | * Block 455 | * 456 | * Rather than allocate lots of small Allocation structures, allocations of 457 | * much larger blocks are done to reduce fragmentation. Each one of these 458 | * blocks tracks the count and then the next block in the list once it has 459 | * been filled. 460 | *****************************************************************************/ 461 | struct Block 462 | { 463 | struct Allocation 464 | allocations[STENT_BLOCKSIZE]; /* Individual allocations */ 465 | 466 | size_t count; /* The number of reserved allocations */ 467 | struct Block *next; /* The next block once this is exhausted */ 468 | }; 469 | 470 | /***************************************************************************** 471 | * blocks 472 | * 473 | * The head of the blocks list. This is assigned an initial block during the 474 | * _stent_init function. If an existing head exists, it is attached to the new 475 | * block causing the most recent allocations to be the fastest to find (though 476 | * searching in this way is typically never needed). 477 | *****************************************************************************/ 478 | static struct Block *blocks; 479 | 480 | /***************************************************************************** 481 | * _stent_atexit 482 | * 483 | * This function is called when the program terminates. It iterates through 484 | * all the blocks in the list and then iterates through each of the individual 485 | * allocations. If any of these have not been freed before program exit it 486 | * reports a leak. 487 | *****************************************************************************/ 488 | static void _stent_atexit(void) 489 | { 490 | struct Block *sb = NULL; 491 | size_t ai = 0; 492 | int doAbort = 0; 493 | 494 | sb = blocks; 495 | 496 | while(sb) 497 | { 498 | for(ai = 0; ai < sb->count; ai++) 499 | { 500 | if(!sb->allocations[ai].expired) 501 | { 502 | fprintf(stderr, 503 | "Warning: Allocated memory [%s:%i] persisted after application exit [%s]\n", 504 | sb->allocations[ai].file, 505 | sb->allocations[ai].line, 506 | sb->allocations[ai].type); 507 | 508 | doAbort = 1; 509 | } 510 | } 511 | 512 | sb = sb->next; 513 | } 514 | 515 | if(doAbort) 516 | { 517 | abort(); 518 | } 519 | } 520 | 521 | /***************************************************************************** 522 | * _stent_init 523 | * 524 | * This function is called by every function relating to memory to ensure 525 | * that the initial block is not NULL. It simply allocates a new Block 526 | * structure and reports that this tool is active in the program. 527 | *****************************************************************************/ 528 | static void _stent_init() 529 | { 530 | if(blocks) 531 | { 532 | return; 533 | } 534 | 535 | blocks = (struct Block *)calloc(1, sizeof(*blocks)); 536 | 537 | if(!blocks) 538 | { 539 | fprintf(stderr, "Error: Failed to initialize initial block\n"); 540 | 541 | abort(); 542 | } 543 | 544 | fprintf(stderr, "Warning: Debug memory allocator enabled\n"); 545 | atexit(_stent_atexit); 546 | } 547 | 548 | /***************************************************************************** 549 | * _stent_alloc 550 | * 551 | * Ensure the tool has been initialized and early return if a size of zero 552 | * has been specified. Assign the return value with the memory location of 553 | * the next free allocation. Allocate the native block of memory, if this 554 | * succeeds, increment the current block's allocation count (allocating a new 555 | * block if necessary) because this allocation is now reserved. Set additional 556 | * properties on the allocation such as type and return the value casted to 557 | * refvoid. 558 | *****************************************************************************/ 559 | refvoid _stent_alloc(size_t size, const char *type, const char *file, int line) 560 | { 561 | struct Allocation *rtn = NULL; 562 | struct Block *sb = NULL; 563 | 564 | _stent_init(); 565 | 566 | if(!size) 567 | { 568 | fprintf(stderr, "Warning: Allocation of zero size\n"); 569 | 570 | return NULL; 571 | } 572 | 573 | rtn = &blocks->allocations[blocks->count]; 574 | rtn->ptr = calloc(1, size); 575 | 576 | if(!rtn->ptr) 577 | { 578 | fprintf(stderr, "Error: Failed to allocate %s\n", type); 579 | abort(); 580 | } 581 | 582 | blocks->count++; 583 | 584 | if(blocks->count >= STENT_BLOCKSIZE) 585 | { 586 | fprintf(stderr, "Warning: Adding allocation blocks\n"); 587 | sb = (struct Block *)calloc(1, sizeof(*blocks)); 588 | sb->next = blocks; 589 | blocks = sb; 590 | } 591 | 592 | rtn->type = type; 593 | rtn->file = file; 594 | rtn->line = line; 595 | 596 | return (refvoid)rtn; 597 | } 598 | 599 | /***************************************************************************** 600 | * _stent_free 601 | * 602 | * Ensure the tool has been initialized and that the specified allocation 603 | * referenced by ptr is still valid. Cast this to an Allocation structure 604 | * and free the native memory assigned to it. Finally set the expired flag so 605 | * that it is no longer seen as valid. 606 | *****************************************************************************/ 607 | void _stent_free(refvoid ptr, const char *file, size_t line) 608 | { 609 | struct Allocation *allocation = NULL; 610 | 611 | _svalid(ptr, file, line); 612 | 613 | allocation = (struct Allocation *)ptr; 614 | free(allocation->ptr); 615 | allocation->expired = 1; 616 | } 617 | 618 | /***************************************************************************** 619 | * _stent_cast 620 | * 621 | * Ensure the tool has been initialized and that the specified allocation 622 | * referenced by ptr is still valid. Obtain the allocation and ensure that the 623 | * type string matches the one specified. If it matches then the cast is 624 | * assumed to be safe. 625 | *****************************************************************************/ 626 | refvoid _stent_cast(const char *type, refvoid ptr, 627 | const char *file, size_t line) 628 | { 629 | struct Allocation *allocation = NULL; 630 | 631 | _svalid(ptr, file, line); 632 | 633 | allocation = (struct Allocation *)ptr; 634 | 635 | if(strcmp(type, "void") == 0) 636 | { 637 | return ptr; 638 | } 639 | 640 | if(strcmp(allocation->type, type) != 0) 641 | { 642 | fprintf(stderr, 643 | "Error: Attempt to cast [%s] to incompatible type [%s]\n", 644 | allocation->type, type); 645 | 646 | abort(); 647 | } 648 | 649 | return ptr; 650 | } 651 | 652 | /***************************************************************************** 653 | * _svalid 654 | * 655 | * Ensure the tool has been initialized, check that the specified pointer 656 | * is not NULL and check if the obtained allocation has the expired flag set. 657 | * If the flag has not been set, assume the data is valid and return 1. 658 | *****************************************************************************/ 659 | int _svalid(refvoid ptr, const char *file, size_t line) 660 | { 661 | struct Allocation *allocation = (struct Allocation *)ptr; 662 | 663 | /* TODO: Can ptr be anything but NULL if stent is not initialized? */ 664 | /* _stent_init(); */ 665 | 666 | if(!ptr) 667 | { 668 | fprintf(stderr, "Error: NULL pointer in %s %i\n", 669 | file, (int)line); 670 | 671 | abort(); 672 | } 673 | 674 | /* 675 | * TODO: Check to see if ptr is within a block. 676 | */ 677 | 678 | /* 679 | * TODO: Would allocation->ptr ever be NULL? 680 | */ 681 | 682 | if(allocation->expired) 683 | { 684 | fprintf(stderr, "Error: %s pointer no longer valid in %s %i\n", 685 | allocation->type, file, (int)line); 686 | 687 | abort(); 688 | } 689 | 690 | return 1; 691 | } 692 | 693 | /***************************************************************************** 694 | * _array_check 695 | * 696 | * TODO: Pass through file and line number. 697 | *****************************************************************************/ 698 | size_t _array_check(size_t as, size_t es, size_t idx) 699 | { 700 | if(es * idx >= as) 701 | { 702 | fprintf(stderr, "Error: Index [index=%lu] out of bounds [size=%lu]\n", 703 | (unsigned long)idx, (unsigned long)as / es); 704 | 705 | abort(); 706 | } 707 | 708 | return idx; 709 | } 710 | 711 | #endif 712 | 713 | /*************************************************** 714 | * Vector 715 | ***************************************************/ 716 | 717 | struct _StentVector 718 | { 719 | unsigned char *data; 720 | size_t size; 721 | size_t allocated; 722 | size_t elementSize; 723 | }; 724 | 725 | #ifdef STENT_ENABLE 726 | vector(void) _vector_new(size_t size, const char *type, const char *file, int line) 727 | #else 728 | vector(void) _vector_new(size_t size) 729 | #endif 730 | { 731 | ref(_StentVector) rtn = NULL; 732 | 733 | #ifdef STENT_ENABLE 734 | rtn = (ref(_StentVector))_stent_alloc(sizeof(struct _StentVector), type, file, line); 735 | #else 736 | rtn = allocate(_StentVector); 737 | #endif 738 | 739 | _(rtn).elementSize = size; 740 | 741 | return (vector(void))rtn; 742 | } 743 | 744 | #ifdef STENT_ENABLE 745 | void _vector_delete(vector(void) ptr, const char *file, size_t line) 746 | #else 747 | void _vector_delete(vector(void) ptr) 748 | #endif 749 | { 750 | ref(_StentVector) v = NULL; 751 | 752 | v = (ref(_StentVector))ptr; 753 | free(_(v).data); 754 | 755 | #ifdef STENT_ENABLE 756 | _stent_free((refvoid)ptr, file, line); 757 | #else 758 | release(ptr); 759 | #endif 760 | } 761 | 762 | size_t _vector_size(vector(void) ptr) 763 | { 764 | ref(_StentVector) v = NULL; 765 | 766 | v = (ref(_StentVector))ptr; 767 | 768 | return _(v).size; 769 | } 770 | 771 | int _vector_compare(vector(void) ptr1, vector(void) ptr2) 772 | { 773 | ref(_StentVector) v1 = NULL; 774 | ref(_StentVector) v2 = NULL; 775 | 776 | v1 = (ref(_StentVector))ptr1; 777 | v2 = (ref(_StentVector))ptr2; 778 | 779 | if(_(v1).size != _(v2).size) 780 | { 781 | return 1; 782 | } 783 | 784 | return memcmp(_(v1).data, _(v2).data, _(v1).size); 785 | } 786 | 787 | void _vector_clear(vector(void) ptr) 788 | { 789 | ref(_StentVector) v = NULL; 790 | 791 | v = (ref(_StentVector))ptr; 792 | 793 | _(v).size = 0; 794 | } 795 | 796 | void _vector_resize(vector(void) ptr, size_t size) 797 | { 798 | ref(_StentVector) v = NULL; 799 | size_t s = 0; 800 | unsigned char *d = NULL; 801 | 802 | v = (ref(_StentVector))ptr; 803 | 804 | if(_(v).allocated >= size) 805 | { 806 | #ifdef STENT_ENABLE 807 | /* 808 | * Fill reclaimed space with zero 809 | */ 810 | if(_(v).size < size) 811 | { 812 | memset(_(v).data + _(v).size * _(v).elementSize, 0, (size - _(v).size) * _(v).elementSize); 813 | } 814 | #endif 815 | 816 | _(v).size = size; 817 | return; 818 | } 819 | 820 | s = 1; 821 | 822 | while(1) 823 | { 824 | if(s >= size) 825 | { 826 | break; 827 | } 828 | 829 | s = s * 2; 830 | } 831 | 832 | d = (unsigned char *)calloc(s, _(v).elementSize); 833 | 834 | if(!d) 835 | { 836 | fprintf(stderr, "Error: Failed to increase vector size\n"); 837 | abort(); 838 | } 839 | 840 | memcpy(d, _(v).data, _(v).elementSize * _(v).size); 841 | free(_(v).data); 842 | _(v).data = d; 843 | _(v).allocated = s; 844 | _(v).size = size; 845 | } 846 | 847 | size_t _vector_valid(vector(void) ptr, size_t idx) 848 | { 849 | ref(_StentVector) v = NULL; 850 | 851 | v = (ref(_StentVector))ptr; 852 | 853 | if(_(v).size > idx) 854 | { 855 | return idx; 856 | } 857 | 858 | fprintf(stderr, "Error: Index [index=%lu] out of bounds [size=%lu]\n", 859 | (unsigned long)idx, (unsigned long)_(v).size); 860 | abort(); 861 | } 862 | 863 | size_t _sstream_valid(vector(void) ptr, size_t idx) 864 | { 865 | /* Check we are not overflowing into the '\0' terminator */ 866 | _vector_valid(ptr, idx + 1); 867 | 868 | return _vector_valid(ptr, idx); 869 | } 870 | 871 | void _vector_erase(vector(void) ptr, size_t idx, size_t num) 872 | { 873 | ref(_StentVector) v = NULL; 874 | char *dest = NULL; 875 | char *src = NULL; 876 | size_t tm = 0; 877 | 878 | v = (ref(_StentVector))ptr; 879 | 880 | if(idx >= _(v).size || 881 | idx + num > _(v).size) 882 | { 883 | fprintf(stderr, "Error: Index out of bounds [size=%i] [index=%i] [num=%i]\n", (int)_(v).size, (int)idx, (int)num); 884 | abort(); 885 | } 886 | 887 | if(!num) 888 | { 889 | return; 890 | } 891 | 892 | dest = (char *)_(v).data; 893 | dest += (idx * _(v).elementSize); 894 | 895 | src = dest; 896 | src += (num * _(v).elementSize); 897 | 898 | tm = (_(v).size - (idx + num)) * _(v).elementSize; 899 | 900 | if(tm) 901 | { 902 | memmove(dest, src, tm); 903 | } 904 | 905 | _(v).size -= num; 906 | } 907 | 908 | void _vector_insert(vector(void) ptr, size_t before, 909 | vector(void) source, size_t idx, size_t num) 910 | { 911 | ref(_StentVector) s = NULL; 912 | ref(_StentVector) d = NULL; 913 | char *dest = NULL; 914 | char *src = NULL; 915 | size_t tm = 0; 916 | 917 | s = (ref(_StentVector))source; 918 | d = (ref(_StentVector))ptr; 919 | 920 | if(s == d) 921 | { 922 | fprintf(stderr, "Error: Source and desination must not match\n"); 923 | abort(); 924 | } 925 | 926 | if(!num) 927 | { 928 | return; 929 | } 930 | 931 | if(before > _(d).size) 932 | { 933 | fprintf(stderr, "Error: Invalid index specified. Non contiguous\n"); 934 | abort(); 935 | } 936 | 937 | if(idx >= _(s).size || 938 | idx + num > _(s).size) 939 | { 940 | fprintf(stderr, "Error: Index out of bounds on source\n"); 941 | abort(); 942 | } 943 | 944 | tm = (_(d).size - before) * _(d).elementSize; 945 | 946 | _vector_resize(ptr, _(d).size + num); 947 | 948 | src = (char *)_(d).data; 949 | src += (before * _(d).elementSize); 950 | dest = src; 951 | dest += (num * _(d).elementSize); 952 | memmove(dest, src, tm); 953 | 954 | dest = src; 955 | src = (char *)_(s).data; 956 | src += (idx * _(d).elementSize); 957 | memcpy(dest, src, num * _(s).elementSize); 958 | } 959 | 960 | /*************************************************** 961 | * String Stream 962 | ***************************************************/ 963 | 964 | /* 965 | struct sstream 966 | { 967 | vector(unsigned char) data; 968 | }; 969 | */ 970 | 971 | static char _scratch[256]; 972 | 973 | ref(sstream) sstream_new() 974 | { 975 | ref(sstream) rtn = NULL; 976 | 977 | rtn = allocate(sstream); 978 | _(rtn).data = vector_new(unsigned char); 979 | vector_push_back(_(rtn).data, '\0'); 980 | 981 | return rtn; 982 | } 983 | 984 | ref(sstream) sstream_new_str(ref(sstream) other) 985 | { 986 | ref(sstream) rtn = sstream_new(); 987 | 988 | sstream_str(rtn, other); 989 | 990 | return rtn; 991 | } 992 | 993 | ref(sstream) sstream_new_cstr(const char *str) 994 | { 995 | ref(sstream) rtn = sstream_new(); 996 | 997 | sstream_str_cstr(rtn, str); 998 | 999 | return rtn; 1000 | } 1001 | 1002 | void sstream_delete(ref(sstream) ctx) 1003 | { 1004 | vector_delete(_(ctx).data); 1005 | release(ctx); 1006 | } 1007 | 1008 | int sstream_compare_cstr(ref(sstream) ctx, const char *str) 1009 | { 1010 | return strcmp(sstream_cstr(ctx), str); 1011 | } 1012 | 1013 | int sstream_compare(ref(sstream) ctx, ref(sstream) other) 1014 | { 1015 | return sstream_compare_cstr(ctx, sstream_cstr(other)); 1016 | } 1017 | 1018 | void sstream_append(ref(sstream) ctx, ref(sstream) str) 1019 | { 1020 | size_t len = 0; 1021 | size_t ci = 0; 1022 | 1023 | len = sstream_length(str); 1024 | 1025 | for(ci = 0; ci < len; ci++) 1026 | { 1027 | sstream_append_char(ctx, sstream_at(str, ci)); 1028 | } 1029 | } 1030 | 1031 | void sstream_append_char(ref(sstream) ctx, unsigned char c) 1032 | { 1033 | vector_at(_(ctx).data, vector_size(_(ctx).data) - 1) = c; 1034 | vector_push_back(_(ctx).data, '\0'); 1035 | } 1036 | 1037 | void sstream_append_int(ref(sstream) ctx, int val) 1038 | { 1039 | char str[(CHAR_BIT * sizeof(int) - 1) / 3 + 2] = {0}; 1040 | 1041 | sprintf(str, "%i", val); 1042 | sstream_append_cstr(ctx, str); 1043 | } 1044 | 1045 | void sstream_append_float(ref(sstream) ctx, float val) 1046 | { 1047 | /* TODO: Better size query */ 1048 | sprintf(_scratch, "%f", val); 1049 | sstream_append_cstr(ctx, _scratch); 1050 | } 1051 | 1052 | void sstream_append_cstr(ref(sstream) ctx, const char *str) 1053 | { 1054 | size_t len = 0; 1055 | size_t ci = 0; 1056 | 1057 | len = strlen(str); 1058 | 1059 | for(ci = 0; ci < len; ci++) 1060 | { 1061 | sstream_append_char(ctx, str[ci]); 1062 | } 1063 | } 1064 | 1065 | size_t sstream_length(ref(sstream) ctx) 1066 | { 1067 | return vector_size(_(ctx).data) - 1; 1068 | } 1069 | 1070 | /* 1071 | unsigned char sstream_at(ref(sstream) ctx, size_t idx) 1072 | { 1073 | if(idx >= sstream_length(ctx)) 1074 | { 1075 | panic("Character index out of range"); 1076 | } 1077 | 1078 | return vector_at(_(ctx).data, idx); 1079 | } 1080 | */ 1081 | 1082 | void sstream_split(ref(sstream) ctx, unsigned char c, vector(ref(sstream)) out) 1083 | { 1084 | ref(sstream) curr = NULL; 1085 | size_t i = 0; 1086 | unsigned char ch = 0; 1087 | 1088 | for(i = 0; i < sstream_length(ctx); i++) 1089 | { 1090 | ch = sstream_at(ctx, i); 1091 | 1092 | if(curr == NULL) 1093 | { 1094 | curr = sstream_new(); 1095 | } 1096 | 1097 | if(ch == c) 1098 | { 1099 | vector_push_back(out, curr); 1100 | curr = NULL; 1101 | } 1102 | else 1103 | { 1104 | sstream_append_char(curr, ch); 1105 | } 1106 | } 1107 | 1108 | if(curr != NULL) 1109 | { 1110 | vector_push_back(out, curr); 1111 | } 1112 | } 1113 | 1114 | void sstream_split_eol(ref(sstream) ctx, vector(ref(sstream)) out) 1115 | { 1116 | size_t max = 0; 1117 | size_t i = 0; 1118 | ref(sstream) curr = NULL; 1119 | char ch = 0; 1120 | 1121 | /* 1122 | * If out array already larger than 0, use that existing string 1123 | * after blanking it. 1124 | */ 1125 | if(max >= vector_size(out)) 1126 | { 1127 | curr = sstream_new(); 1128 | } 1129 | else 1130 | { 1131 | curr = vector_at(out, max); 1132 | sstream_str_cstr(curr, ""); 1133 | } 1134 | 1135 | for(i = 0; i < sstream_length(ctx); i++) 1136 | { 1137 | ch = sstream_at(ctx, i); 1138 | 1139 | if(ch == '\n') 1140 | { 1141 | /* 1142 | * Add to out array if not an empty token 1143 | */ 1144 | if(sstream_length(curr) > 0) 1145 | { 1146 | /* 1147 | * String is already in array if max is still smaller than array 1148 | */ 1149 | if(max >= vector_size(out)) 1150 | { 1151 | vector_push_back(out, curr); 1152 | } 1153 | max++; 1154 | 1155 | /* 1156 | * If out array already larger than max, use that existing string 1157 | * after blanking it. 1158 | */ 1159 | if(max >= vector_size(out)) 1160 | { 1161 | curr = sstream_new(); 1162 | } 1163 | else 1164 | { 1165 | curr = vector_at(out, max); 1166 | sstream_str_cstr(curr, ""); 1167 | } 1168 | } 1169 | } 1170 | else if(ch == '\r') 1171 | { 1172 | /* Ignore */ 1173 | } 1174 | else 1175 | { 1176 | sstream_append_char(curr, ch); 1177 | } 1178 | } 1179 | 1180 | /* 1181 | * If remaining curr is not blank. Add it to out array 1182 | * if not already reusing an element. 1183 | */ 1184 | if(sstream_length(curr) > 0) 1185 | { 1186 | if(max >= vector_size(out)) 1187 | { 1188 | vector_push_back(out, curr); 1189 | } 1190 | max++; 1191 | } 1192 | else 1193 | { 1194 | if(max >= vector_size(out)) 1195 | { 1196 | sstream_delete(curr); 1197 | } 1198 | } 1199 | 1200 | /* 1201 | * Erase the remaining elements. They are not needed. 1202 | */ 1203 | while(vector_size(out) > max) 1204 | { 1205 | sstream_delete(vector_at(out, vector_size(out) - 1)); 1206 | vector_erase(out, vector_size(out) - 1, 1); 1207 | } 1208 | } 1209 | 1210 | char *sstream_cstr(ref(sstream) ctx) 1211 | { 1212 | return (char *)&vector_at(_(ctx).data, 0); 1213 | } 1214 | 1215 | void sstream_erase(ref(sstream) ctx, size_t idx, size_t num) 1216 | { 1217 | if(idx + num > sstream_length(ctx)) 1218 | { 1219 | fprintf(stderr, "Error: Selection reaches end of array\n"); 1220 | 1221 | abort(); 1222 | } 1223 | 1224 | vector_erase(_(ctx).data, idx, num); 1225 | } 1226 | 1227 | void sstream_resize(ref(sstream) ctx, size_t length) 1228 | { 1229 | if(length == sstream_length(ctx)) 1230 | { 1231 | return; 1232 | } 1233 | 1234 | vector_resize(_(ctx).data, length + 1); 1235 | vector_at(_(ctx).data, length) = '\0'; 1236 | } 1237 | 1238 | void sstream_str_cstr(ref(sstream) ctx, const char *str) 1239 | { 1240 | vector_clear(_(ctx).data); 1241 | vector_push_back(_(ctx).data, '\0'); 1242 | sstream_append_cstr(ctx, str); 1243 | } 1244 | 1245 | void sstream_str(ref(sstream) ctx, ref(sstream) str) 1246 | { 1247 | vector_clear(_(ctx).data); 1248 | vector_push_back(_(ctx).data, '\0'); 1249 | sstream_append(ctx, str); 1250 | } 1251 | 1252 | vector(unsigned char) sstream_raw(ref(sstream) ctx) 1253 | { 1254 | return _(ctx).data; 1255 | } 1256 | 1257 | void sstream_insert(ref(sstream) dest, size_t dbegin, ref(sstream) source, size_t sbegin, size_t count) 1258 | { 1259 | if(dbegin > sstream_length(dest)) 1260 | { 1261 | fprintf(stderr, "Error: Attempting to insert past end of stream\n"); 1262 | 1263 | abort(); 1264 | } 1265 | 1266 | if(sbegin + count > sstream_length(source)) 1267 | { 1268 | fprintf(stderr, "Error: Selection reaches end of stream\n"); 1269 | 1270 | abort(); 1271 | } 1272 | 1273 | vector_insert(_(dest).data, dbegin, _(source).data, sbegin, count); 1274 | } 1275 | 1276 | int sstream_cmp_cstr(ref(sstream) ctx, const char *str) 1277 | { 1278 | return strcmp(sstream_cstr(ctx), str); 1279 | } 1280 | 1281 | #ifndef _WIN32 1282 | /*************************************************** 1283 | * Directory Handling 1284 | ***************************************************/ 1285 | #include 1286 | 1287 | struct dir 1288 | { 1289 | DIR *dp; 1290 | }; 1291 | 1292 | ref(dir) dir_open_cstr(char *path) 1293 | { 1294 | ref(dir) rtn = NULL; 1295 | DIR *dp = NULL; 1296 | 1297 | dp = opendir(path); 1298 | 1299 | if(!dp) 1300 | { 1301 | return NULL; 1302 | } 1303 | 1304 | rtn = allocate(dir); 1305 | _(rtn).dp = dp; 1306 | 1307 | return rtn; 1308 | } 1309 | 1310 | ref(dir) dir_open(ref(sstream) path) 1311 | { 1312 | return dir_open_cstr(sstream_cstr(path)); 1313 | } 1314 | 1315 | void dir_close(ref(dir) ctx) 1316 | { 1317 | closedir(_(ctx).dp); 1318 | release(ctx); 1319 | } 1320 | #endif 1321 | 1322 | /*************************************************** 1323 | * File Stream 1324 | ***************************************************/ 1325 | 1326 | struct ifstream 1327 | { 1328 | FILE *fp; 1329 | int pipe; 1330 | }; 1331 | 1332 | ref(ifstream) ifstream_open_cstr(const char *path) 1333 | { 1334 | ref(ifstream) rtn = NULL; 1335 | FILE *fp = NULL; 1336 | 1337 | fp = fopen(path, "rb"); 1338 | 1339 | if(!fp) 1340 | { 1341 | return NULL; 1342 | } 1343 | 1344 | rtn = allocate(ifstream); 1345 | _(rtn).fp = fp; 1346 | 1347 | return rtn; 1348 | } 1349 | 1350 | ref(ifstream) ifstream_popen_cstr(const char *cmd) 1351 | { 1352 | ref(ifstream) rtn = NULL; 1353 | FILE *fp = NULL; 1354 | 1355 | #ifdef WIN32 1356 | fp = _popen(cmd, "r"); 1357 | #else 1358 | fp = popen(cmd, "r"); 1359 | #endif 1360 | 1361 | if(!fp) 1362 | { 1363 | return NULL; 1364 | } 1365 | 1366 | rtn = allocate(ifstream); 1367 | _(rtn).fp = fp; 1368 | _(rtn).pipe = 1; 1369 | 1370 | return rtn; 1371 | } 1372 | 1373 | ref(ifstream) ifstream_open(ref(sstream) path) 1374 | { 1375 | return ifstream_open_cstr(sstream_cstr(path)); 1376 | } 1377 | 1378 | ref(ifstream) ifstream_popen(ref(sstream) cmd) 1379 | { 1380 | return ifstream_popen_cstr(sstream_cstr(cmd)); 1381 | } 1382 | 1383 | void ifstream_close(ref(ifstream) ctx) 1384 | { 1385 | if(_(ctx).pipe) 1386 | { 1387 | #ifdef WIN32 1388 | _pclose(_(ctx).fp); 1389 | #else 1390 | pclose(_(ctx).fp); 1391 | #endif 1392 | } 1393 | else 1394 | { 1395 | fclose(_(ctx).fp); 1396 | } 1397 | 1398 | release(ctx); 1399 | } 1400 | 1401 | int ifstream_eof(ref(ifstream) ctx) 1402 | { 1403 | int c = 0; 1404 | 1405 | if(feof(_(ctx).fp)) 1406 | { 1407 | return 1; 1408 | } 1409 | 1410 | c = getc(_(ctx).fp); 1411 | 1412 | if(c == EOF) 1413 | { 1414 | return 1; 1415 | } 1416 | 1417 | ungetc(c, _(ctx).fp); 1418 | 1419 | return 0; 1420 | } 1421 | 1422 | void ifstream_read_contents(ref(ifstream) ctx, vector(unsigned char) out) 1423 | { 1424 | unsigned char buf[1024] = {0}; 1425 | 1426 | vector_clear(out); 1427 | 1428 | while(1) 1429 | { 1430 | size_t ci = 0; 1431 | 1432 | size_t len = fread(buf, sizeof(unsigned char), 1024, _(ctx).fp); 1433 | 1434 | if(!len) 1435 | { 1436 | break; 1437 | } 1438 | 1439 | for(ci = 0; ci < len; ci++) 1440 | { 1441 | vector_push(out, buf[ci]); 1442 | } 1443 | } 1444 | } 1445 | 1446 | void ifstream_read(ref(ifstream) ctx, vector(unsigned char) out) 1447 | { 1448 | size_t len = fread(&vector_at(out, 0), sizeof(unsigned char), vector_size(out), _(ctx).fp); 1449 | 1450 | vector_resize(out, len); 1451 | } 1452 | 1453 | void ifstream_getline(ref(ifstream) ctx, ref(sstream) out) 1454 | { 1455 | char buf[1024] = {0}; 1456 | size_t ci = 0; 1457 | char rc = 0; 1458 | 1459 | sstream_str_cstr(out, ""); 1460 | 1461 | while(1) 1462 | { 1463 | if(!fgets(buf, sizeof(buf), _(ctx).fp)) 1464 | { 1465 | if(sstream_length(out) > 0) 1466 | { 1467 | return; 1468 | } 1469 | 1470 | fprintf(stderr, "Error: Failed to read from input stream\n"); 1471 | abort(); 1472 | } 1473 | 1474 | ci = 0; 1475 | 1476 | while(1) 1477 | { 1478 | rc = buf[ci]; 1479 | 1480 | if(rc == '\n') 1481 | { 1482 | return; 1483 | } 1484 | else if(rc == '\0') 1485 | { 1486 | break; 1487 | } 1488 | else if(rc == '\r') 1489 | { } 1490 | else 1491 | { 1492 | sstream_append_char(out, rc); 1493 | } 1494 | 1495 | ci++; 1496 | } 1497 | } 1498 | } 1499 | 1500 | void panic(const char *message) 1501 | { 1502 | printf("Panic: %s\n", message); 1503 | 1504 | abort(); 1505 | } 1506 | 1507 | #endif 1508 | 1509 | #ifdef __cplusplus 1510 | } 1511 | #endif 1512 | 1513 | --------------------------------------------------------------------------------