summaryrefslogtreecommitdiffhomepage
path: root/docs/cvec_api.md
blob: 68e08cb2d14d13eac5c6760b23e3285b25d21dfd (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
# STC [cvec](../include/stc/cvec.h): Vector
![Vector](pics/vector.jpg)

A **cvec** is a sequence container that encapsulates dynamic size arrays.

The storage of the vector is handled automatically, being expanded and contracted as needed. Vectors usually occupy more space than static arrays, because more memory is allocated to handle future growth. This way a vector does not need to reallocate each time an element is inserted, but only when the additional memory is exhausted. The total amount of allocated memory can be queried using *cvec_X_capacity()* function. Extra memory can be returned to the system via a call to *cvec_X_shrink_to_fit()*.

Reallocations are usually costly operations in terms of performance. The *cvec_X_reserve()* function can be used to eliminate reallocations if the number of elements is known beforehand.

See the c++ class [std::vector](https://en.cppreference.com/w/cpp/container/vector) for a functional description.

## Header file and declaration

```c
#define i_type      // full typename of the container
#define i_val       // value: REQUIRED
#define i_cmp       // three-way compare two i_valraw* : REQUIRED IF i_valraw is a non-integral type
#define i_valdrop   // destroy value func - defaults to empty destruct
#define i_valclone  // REQUIRED IF i_valdrop defined

#define i_valraw    // convertion "raw" type - defaults to i_val
#define i_valfrom   // convertion func i_valraw => i_val
#define i_valto     // convertion func i_val* => i_valraw

#define i_tag       // alternative typename: cvec_{i_tag}. i_tag defaults to i_val
#include <stc/cvec.h>
```
`X` should be replaced by the value of `i_tag` in all of the following documentation.

## Methods

```c
cvec_X              cvec_X_init(void);
cvec_X              cvec_X_with_size(intptr_t size, i_val null);
cvec_X              cvec_X_with_capacity(intptr_t size);
cvec_X              cvec_X_clone(cvec_X vec);

void                cvec_X_clear(cvec_X* self);
void                cvec_X_copy(cvec_X* self, const cvec_X* other);
cvec_X_iter         cvec_X_copy_range(cvec_X* self, i_val* pos, const i_val* p1, const i_val* p2);
bool                cvec_X_reserve(cvec_X* self, intptr_t cap);
bool                cvec_X_resize(cvec_X* self, intptr_t size, i_val null);
cvec_X_iter         cvec_X_insert_uninit(cvec_X* self, i_val* pos, intptr_t n); // return pos iter 
void                cvec_X_shrink_to_fit(cvec_X* self);
void                cvec_X_drop(cvec_X* self);                              // destructor

bool                cvec_X_empty(const cvec_X* self);
intptr_t            cvec_X_size(const cvec_X* self);
intptr_t            cvec_X_capacity(const cvec_X* self);

const cvec_X_value* cvec_X_at(const cvec_X* self, intptr_t idx);
const cvec_X_value* cvec_X_get(const cvec_X* self, i_valraw raw);           // return NULL if not found
cvec_X_value*       cvec_X_at_mut(cvec_X* self, intptr_t idx);
cvec_X_value*       cvec_X_get_mut(cvec_X* self, i_valraw raw);             // find mutable value, return value ptr
cvec_X_iter         cvec_X_find(const cvec_X* self, i_valraw raw);
cvec_X_iter         cvec_X_find_in(cvec_X_iter i1, cvec_X_iter i2, i_valraw raw); // return cvec_X_end() if not found
                    // On sorted vectors:
cvec_X_iter         cvec_X_binary_search(const cvec_X* self, i_valraw raw); // at elem == raw, else end
cvec_X_iter         cvec_X_lower_bound(const cvec_X* self, i_valraw raw);   // at first elem >= raw, else end
cvec_X_iter         cvec_X_binary_search_in(cvec_X_iter i1, cvec_X_iter i2,
                                            i_valraw raw, cvec_X_iter* lower_bound);

cvec_X_value*       cvec_X_front(const cvec_X* self);
cvec_X_value*       cvec_X_back(const cvec_X* self);

cvec_X_value*       cvec_X_push(cvec_X* self, i_val value);
cvec_X_value*       cvec_X_emplace(cvec_X* self, i_valraw raw);
cvec_X_value*       cvec_X_push_back(cvec_X* self, i_val value);            // alias for push
cvec_X_value*       cvec_X_emplace_back(cvec_X* self, i_valraw raw);        // alias for emplace

void                cvec_X_pop(cvec_X* self);
void                cvec_X_pop_back(cvec_X* self);                          // alias for pop

cvec_X_iter         cvec_X_insert(cvec_X* self, intptr_t idx, i_val value); // move value 
cvec_X_iter         cvec_X_insert_n(cvec_X* self, intptr_t idx, const i_val[] arr, intptr_t n);  // move n values
cvec_X_iter         cvec_X_insert_at(cvec_X* self, cvec_X_iter it, i_val value);  // move value 
cvec_X_iter         cvec_X_insert_range(cvec_X* self, i_val* pos,
                                        const i_val* p1, const i_val* p2);

cvec_X_iter         cvec_X_emplace_n(cvec_X* self, intptr_t idx, const i_valraw[] arr, intptr_t n); // clone values
cvec_X_iter         cvec_X_emplace_at(cvec_X* self, cvec_X_iter it, i_valraw raw);
cvec_X_iter         cvec_X_emplace_range(cvec_X* self, i_val* pos,
                                         const i_valraw* p1, const i_valraw* p2);

cvec_X_iter         cvec_X_erase_n(cvec_X* self, intptr_t idx, intptr_t n);
cvec_X_iter         cvec_X_erase_at(cvec_X* self, cvec_X_iter it);
cvec_X_iter         cvec_X_erase_range(cvec_X* self, cvec_X_iter it1, cvec_X_iter it2);
cvec_X_iter         cvec_X_erase_range_p(cvec_X* self, i_val* p1, i_val* p2);

void                cvec_X_sort(cvec_X* self);
void                cvec_X_sort_range(cvec_X_iter i1, cvec_X_iter i2,
                                      int(*cmp)(const i_val*, const i_val*));

cvec_X_iter         cvec_X_begin(const cvec_X* self);
cvec_X_iter         cvec_X_end(const cvec_X* self);
void                cvec_X_next(cvec_X_iter* iter);
cvec_X_iter         cvec_X_advance(cvec_X_iter it, size_t n);

cvec_X_raw          cvec_X_value_toraw(cvec_X_value* pval);
cvec_X_value        cvec_X_value_clone(cvec_X_value val);
```

## Types

| Type name          | Type definition                   | Used to represent...   |
|:-------------------|:----------------------------------|:-----------------------|
| `cvec_X`           | `struct { cvec_X_value* data; }`  | The cvec type          |
| `cvec_X_value`     | `i_val`                           | The cvec value type    |
| `cvec_X_raw`       | `i_valraw`                        | The raw value type     |
| `cvec_X_iter`      | `struct { cvec_X_value* ref; }`   | The iterator type      |

## Examples
```c
#define i_val int
#include <stc/cvec.h>

#include <stdio.h>

int main()
{
    // Create a vector containing integers
    c_auto (cvec_int, vec)
    {
        // Add two integers to vector
        cvec_int_push(&vec, 25);
        cvec_int_push(&vec, 13);

        // Append a set of numbers
        c_forlist (i, int, {7, 5, 16, 8})
            cvec_int_push(&vec, *i.ref);

        printf("initial:");
        c_foreach (k, cvec_int, vec) {
            printf(" %d", *k.ref);
        }

        // Sort the vector
        cvec_int_sort(&vec);

        printf("\nsorted:");
        c_foreach (k, cvec_int, vec) {
            printf(" %d", *k.ref);
        }
    }
}
```
Output:
```
initial: 25 13 7 5 16 8
sorted: 5 7 8 13 16 25
```
### Example 2
```c
#include <stc/cstr.h>

#define i_val_str
#include <stc/cvec.h>

int main() {
    cvec_str names = cvec_str_init();

    cvec_str_emplace(&names, "Mary");
    cvec_str_emplace(&names, "Joe");
    cstr_assign(&names.data[1], "Jake"); // replace "Joe".

    cstr tmp = cstr_from_fmt("%d elements so far", cvec_str_size(names));

    // cvec_str_emplace() only accept const char*, so use push():
    cvec_str_push(&names, tmp); // tmp is "moved" to names (must not be dropped).

    printf("%s\n", cstr_str(&names.data[1])); // Access second element

    c_foreach (i, cvec_str, names)
        printf("item: %s\n", cstr_str(i.ref));

    cvec_str_drop(&names);
}
```
Output:
```
Jake
item: Mary
item: Jake
item: 2 elements so far
```
### Example 3

Container with elements of structs:
```c
#include <stc/cstr.h>

typedef struct {
    cstr name; // dynamic string
    int id;
} User;

int User_cmp(const User* a, const User* b) {
    int c = strcmp(cstr_str(&a->name), cstr_str(&b->name));
    return c ? c : a->id - b->id;
}
void User_drop(User* self) {
    cstr_drop(&self->name);
}
User User_clone(User user) {
    user.name = cstr_clone(user.name);
    return user;
}

// Declare a managed, clonable vector of users.
#define i_type UVec
#define i_valclass User // User is a "class" as it has _cmp, _clone and _drop functions.
#include <stc/cvec.h>

int main(void) {
    UVec vec = UVec_init();
    UVec_push(&vec, (User){cstr_lit("mary"), 0});
    UVec_push(&vec, (User){cstr_lit("joe"), 1});
    UVec_push(&vec, (User){cstr_lit("admin"), 2});

    UVec vec2 = UVec_clone(vec);

    c_foreach (i, UVec, vec2)
        printf("%s: %d\n", cstr_str(&i.ref->name), i.ref->id);

    c_drop(UVec, &vec, &vec2); // cleanup
}
```