-
Notifications
You must be signed in to change notification settings - Fork 1
/
utils.c
299 lines (277 loc) · 9.65 KB
/
utils.c
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
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
/*
* This source file forms part of sxbp, a library which generates experimental
* 2D spiral-like shapes based on input binary data.
*
* This compilation unit provides the definition of functions for allocating,
* freeing and copying the public data types of sxbp and those for checking
* the error codes returned by certain functions in sxbp.
*
* Copyright (C) Joshua Saxby <joshua.a.saxby@gmail.com> 2018
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/
#include <stdbool.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "sxbp.h"
#include "sxbp_internal.h"
#ifdef __cplusplus
extern "C"{
#endif
bool sxbp_success(sxbp_result_t state) {
// return whether state was 'OK or not'
return state == SXBP_RESULT_OK;
}
bool sxbp_check(sxbp_result_t state, sxbp_result_t* const report_to) {
// return true immediately if the state is 'OK'
if (sxbp_success(state)) {
return true;
} else {
// otherwise, store it in the location at `report_to` if not NULL
if (report_to != NULL) {
*report_to = state;
}
// return false to indicate some kind of error occurred
return false;
}
}
sxbp_buffer_t sxbp_blank_buffer(void) {
return (sxbp_buffer_t){ .size = 0, .bytes = NULL, };
}
sxbp_result_t sxbp_init_buffer(sxbp_buffer_t* const buffer) {
// check buffer isn't NULL
SXBP_RETURN_FAIL_IF_NULL(buffer);
// allocate memory with calloc to make sure all bytes are set to zero
buffer->bytes = calloc(buffer->size, sizeof(uint8_t));
// if bytes is not NULL, then the operation was successful
return buffer->bytes != NULL ? SXBP_RESULT_OK : SXBP_RESULT_FAIL_MEMORY;
}
bool sxbp_free_buffer(sxbp_buffer_t* const buffer) {
// if buffer and bytes are not NULL, assume there's memory to be deallocated
if (buffer != NULL && buffer->bytes != NULL) {
free(buffer->bytes);
// set bytes to NULL (be a good person)
buffer->bytes = NULL;
return true;
} else {
// nothing to deallocate
return false;
}
}
sxbp_result_t sxbp_copy_buffer(
const sxbp_buffer_t* const from,
sxbp_buffer_t* const to
) {
// check both pointers to ensure they're not NULL
SXBP_RETURN_FAIL_IF_NULL(from);
SXBP_RETURN_FAIL_IF_NULL(to);
// before we do anything else, make sure 'to' has been freed
sxbp_free_buffer(to);
// copy across the size
to->size = from->size;
// allocate the 'to' buffer
if (!sxbp_success(sxbp_init_buffer(to))) {
// exit early if allocation failed - this can only be a memory error
return SXBP_RESULT_FAIL_MEMORY;
} else {
// allocation succeeded, so now copy the data
memcpy(to->bytes, from->bytes, to->size);
return SXBP_RESULT_OK;
}
}
/*
* private, works out and returns the size of the file referred to by the given
* file handle
*/
static size_t sxbp_get_file_size(FILE* file_handle) {
// seek to end
// NOTE: This isn't portable due to lack of meaningful support of `SEEK_END`
fseek(file_handle, 0, SEEK_END);
// get size
size_t file_size = (size_t)ftell(file_handle);
// seek to start again
fseek(file_handle, 0, SEEK_SET);
return file_size;
}
sxbp_result_t sxbp_buffer_from_file(
FILE* file_handle,
sxbp_buffer_t* const buffer
) {
// check both pointers to ensure they're not NULL
SXBP_RETURN_FAIL_IF_NULL(file_handle);
SXBP_RETURN_FAIL_IF_NULL(buffer);
// erase buffer
sxbp_free_buffer(buffer);
// get the file's size
buffer->size = sxbp_get_file_size(file_handle);
// allocate the buffer to this size and handle error if this failed
if (!sxbp_success(sxbp_init_buffer(buffer))) {
// allocation failed - this can only be a memory error
return SXBP_RESULT_FAIL_MEMORY;
} else {
// allocation succeeded, so read the file contents into the buffer
size_t bytes_read = fread(
buffer->bytes,
sizeof(uint8_t),
buffer->size,
file_handle
);
/*
* check that the correct number of bytes were read and exit with error
* if it doesn't match the reported file size
*/
if (bytes_read != buffer->size) {
// we didn't read the same number of bytes as the file's size
sxbp_free_buffer(buffer);
// return a file error
return SXBP_RESULT_FAIL_IO;
} else {
// we read the buffer successfully, so return success
return SXBP_RESULT_OK;
}
}
}
sxbp_result_t sxbp_buffer_to_file(
const sxbp_buffer_t* const buffer,
FILE* file_handle
) {
// check both pointers to ensure they're not NULL
SXBP_RETURN_FAIL_IF_NULL(buffer);
SXBP_RETURN_FAIL_IF_NULL(file_handle);
// try and write the file contents
size_t bytes_written = fwrite(
buffer->bytes,
sizeof(uint8_t),
buffer->size,
file_handle
);
// return success/failure if the correct number of bytes were written
return bytes_written == buffer->size ? SXBP_RESULT_OK : SXBP_RESULT_FAIL_IO;
}
sxbp_figure_t sxbp_blank_figure(void) {
return (sxbp_figure_t){ .size = 0, .lines = NULL, .lines_remaining = 0, };
}
sxbp_result_t sxbp_init_figure(sxbp_figure_t* const figure) {
// check figure isn't NULL
SXBP_RETURN_FAIL_IF_NULL(figure);
// allocate the lines, using calloc to set all fields of each one to zero
figure->lines = calloc(figure->size, sizeof(sxbp_line_t));
// if lines is not NULL, then the operation was successful
return figure->lines != NULL ? SXBP_RESULT_OK : SXBP_RESULT_FAIL_MEMORY;
}
bool sxbp_free_figure(sxbp_figure_t* const figure) {
// if figure and lines are not NULL, assume there's memory to be deallocated
if (figure != NULL && figure->lines != NULL) {
free(figure->lines);
// set lines to NULL (be a good person)
figure->lines = NULL;
return true;
} else {
// nothing to deallocate
return false;
}
}
sxbp_result_t sxbp_copy_figure(
const sxbp_figure_t* const from,
sxbp_figure_t* const to
) {
// check both pointers to ensure they're not NULL
SXBP_RETURN_FAIL_IF_NULL(from);
SXBP_RETURN_FAIL_IF_NULL(to);
// before we do anything else, make sure 'to' has been freed
sxbp_free_figure(to);
// copy across the static members
to->size = from->size;
to->lines_remaining = from->lines_remaining;
// allocate the 'to' figure
if (!sxbp_init_figure(to)) {
// exit early if allocation failed - this can only be a memory error
return SXBP_RESULT_FAIL_MEMORY;
} else {
// allocation succeeded, so now copy the lines
memcpy(to->lines, from->lines, to->size);
return SXBP_RESULT_OK;
}
}
sxbp_bitmap_t sxbp_blank_bitmap(void) {
return (sxbp_bitmap_t){ .width = 0, .height = 0, .pixels = NULL, };
}
// allocates memory for one column of a bitmap, returning whether it succeeded
static bool sxbp_init_bitmap_col(bool** col, uint32_t size) {
// allocate col with calloc
*col = calloc(size, sizeof(bool));
// if col is not NULL, then the operation was successful
return *col != NULL;
}
sxbp_result_t sxbp_init_bitmap(sxbp_bitmap_t* const bitmap) {
// check bitmap isn't NULL
SXBP_RETURN_FAIL_IF_NULL(bitmap);
// first allocate pointers for the columns
bitmap->pixels = calloc(bitmap->width, sizeof(bool*));
if (bitmap->pixels == NULL) {
// catch allocation error and exit early
return SXBP_RESULT_FAIL_MEMORY;
} else {
// allocation of col pointers succeeded, now try and allocate each col
for (uint32_t col = 0; col < bitmap->width; col++) {
if (
!sxbp_success(
sxbp_init_bitmap_col(&bitmap->pixels[col], bitmap->height)
)
) {
// allocation of one col failed, so de-allocate the whole bitmap
sxbp_free_bitmap(bitmap);
// indicate allocation failure
return SXBP_RESULT_FAIL_MEMORY;
}
}
return SXBP_RESULT_OK;
}
}
bool sxbp_free_bitmap(sxbp_bitmap_t* const bitmap) {
// if bitmap and pixels aren't NULL, assume there are cols to be deallocated
if (bitmap != NULL && bitmap->pixels != NULL) {
// deallocate each col that needs deallocating first
for (uint32_t col = 0; col < bitmap->width; col++) {
if (bitmap->pixels[col] != NULL) {
free(bitmap->pixels[col]);
}
}
// finally, deallocate the rows pointer
free(bitmap->pixels);
return true;
} else {
return false;
}
}
sxbp_result_t sxbp_copy_bitmap(
const sxbp_bitmap_t* const from,
sxbp_bitmap_t* const to
) {
// check both pointers to ensure they're not NULL
SXBP_RETURN_FAIL_IF_NULL(from);
SXBP_RETURN_FAIL_IF_NULL(to);
// before we do anything else, make sure 'to' has been freed
sxbp_free_bitmap(to);
// copy across width and height
to->width = from->width;
to->height = from->height;
// allocate the 'to' bitmap
if (!sxbp_success(sxbp_init_bitmap(to))) {
// exit early if allocation failed - this can only be a memory error
return SXBP_RESULT_FAIL_MEMORY;
} else {
// allocation succeeded, so now copy the pixels
for (uint32_t col = 0; col < to->width; col++) {
memcpy(to->pixels[col], from->pixels[col], to->height);
}
return SXBP_RESULT_OK;
}
}
#ifdef __cplusplus
} // extern "C"
#endif