diff --git a/examples/aoc2023/day10/part1.jou b/examples/aoc2023/day10/part1.jou index 5cf3106c..ed0884fb 100644 --- a/examples/aoc2023/day10/part1.jou +++ b/examples/aoc2023/day10/part1.jou @@ -1,32 +1,6 @@ import "stdlib/io.jou" import "stdlib/mem.jou" -import "stdlib/str.jou" - - -class Input: - width: int - height: int - data: byte* - - def is_in_bounds(self, point: int[2]) -> bool: - x = point[0] - y = point[1] - return 0 <= x and x < self->width and 0 <= y and y < self->height - - def get(self, point: int[2]) -> byte: - assert self->is_in_bounds(point) - x = point[0] - y = point[1] - return self->data[(self->width + 1)*y + x] - - -def find_S(input: Input*) -> int[2]: - for x = 0; x < input->width; x++: - for y = 0; y < input->height; y++: - if input->get([x, y]) == 'S': - return [x, y] - - assert False +import "../grid.jou" def get_dirs(c: byte) -> int[2][2]: @@ -50,14 +24,14 @@ def eq(a: int[2], b: int[2]) -> bool: return a[0] == b[0] and a[1] == b[1] -def find_initial_direction(input: Input*, S: int[2]) -> int[2]: +def find_initial_direction(grid: Grid*, S: int[2]) -> int[2]: directions = [[0,1], [0,-1], [1,0], [-1,0]] for d = &directions[0]; d < &directions[4]; d++: S_to_neighbor = *d neighbor = [S[0] + S_to_neighbor[0], S[1] + S_to_neighbor[1]] - if input->is_in_bounds(neighbor) and input->get(neighbor) != '.': - dirs = get_dirs(input->get(neighbor)) + if grid->is_in_bounds(neighbor) and grid->get(neighbor) != '.': + dirs = get_dirs(grid->get(neighbor)) if eq(dirs[0], S_to_neighbor) or eq(dirs[1], S_to_neighbor): return S_to_neighbor @@ -65,28 +39,22 @@ def find_initial_direction(input: Input*, S: int[2]) -> int[2]: def main() -> int: - max_len = 100000 - input = Input{data = calloc(1, max_len+1)} - f = fopen("sampleinput.txt", "r") assert f != NULL - fread(input.data, 1, max_len, f) + grid = read_grid_from_file(f) fclose(f) - input.width = strcspn(input.data, "\n") as int - input.height = (strlen(input.data) as int) / (input.width + 1) - - point = find_S(&input) + point = grid.find_first('S') # take first step away from S - dir = find_initial_direction(&input, point) + dir = find_initial_direction(&grid, point) point[0] += dir[0] point[1] += dir[1] loop_length = 1 - while input.get(point) != 'S': + while grid.get(point) != 'S': came_from = [-dir[0], -dir[1]] - dirs = get_dirs(input.get(point)) + dirs = get_dirs(grid.get(point)) assert eq(came_from, dirs[0]) or eq(came_from, dirs[1]) if eq(came_from, dirs[0]): dir = dirs[1] @@ -99,5 +67,5 @@ def main() -> int: assert loop_length % 2 == 0 printf("%d\n", loop_length / 2) # Output: 4 - free(input.data) + free(grid.data) return 0 diff --git a/examples/aoc2023/day10/part2.jou b/examples/aoc2023/day10/part2.jou index 1319c025..21d9d824 100644 --- a/examples/aoc2023/day10/part2.jou +++ b/examples/aoc2023/day10/part2.jou @@ -1,33 +1,7 @@ import "stdlib/io.jou" import "stdlib/mem.jou" -import "stdlib/str.jou" import "stdlib/math.jou" - - -class Input: - width: int - height: int - data: byte* - - def is_in_bounds(self, point: int[2]) -> bool: - x = point[0] - y = point[1] - return 0 <= x and x < self->width and 0 <= y and y < self->height - - def get(self, point: int[2]) -> byte: - assert self->is_in_bounds(point) - x = point[0] - y = point[1] - return self->data[(self->width + 1)*y + x] - - -def find_S(input: Input*) -> int[2]: - for x = 0; x < input->width; x++: - for y = 0; y < input->height; y++: - if input->get([x, y]) == 'S': - return [x, y] - - assert False +import "../grid.jou" def get_dirs(c: byte) -> int[2][2]: @@ -51,14 +25,14 @@ def eq(a: int[2], b: int[2]) -> bool: return a[0] == b[0] and a[1] == b[1] -def find_initial_direction(input: Input*, S: int[2]) -> int[2]: +def find_initial_direction(grid: Grid*, S: int[2]) -> int[2]: directions = [[0,1], [0,-1], [1,0], [-1,0]] for d = &directions[0]; d < &directions[4]; d++: S_to_neighbor = *d neighbor = [S[0] + S_to_neighbor[0], S[1] + S_to_neighbor[1]] - if input->is_in_bounds(neighbor) and input->get(neighbor) != '.': - dirs = get_dirs(input->get(neighbor)) + if grid->is_in_bounds(neighbor) and grid->get(neighbor) != '.': + dirs = get_dirs(grid->get(neighbor)) if eq(dirs[0], S_to_neighbor) or eq(dirs[1], S_to_neighbor): return S_to_neighbor @@ -107,31 +81,25 @@ def polygon_area(corners: int[2]*, ncorners: int) -> int: def main() -> int: - max_len = 100000 - input = Input{data = calloc(1, max_len+1)} - f = fopen("sampleinput2.txt", "r") assert f != NULL - fread(input.data, 1, max_len, f) + grid = read_grid_from_file(f) fclose(f) - input.width = strcspn(input.data, "\n") as int - input.height = (strlen(input.data) as int) / (input.width + 1) - - point = find_S(&input) + point = grid.find_first('S') - loop: int[2]* = malloc(input.width * input.height * sizeof(loop[0])) + loop: int[2]* = malloc(grid.width * grid.height * sizeof(loop[0])) loop[0] = point # take first step away from S - dir = find_initial_direction(&input, point) + dir = find_initial_direction(&grid, point) point[0] += dir[0] point[1] += dir[1] loop_length = 1 - while input.get(point) != 'S': + while grid.get(point) != 'S': came_from = [-dir[0], -dir[1]] - dirs = get_dirs(input.get(point)) + dirs = get_dirs(grid.get(point)) assert eq(came_from, dirs[0]) or eq(came_from, dirs[1]) if eq(came_from, dirs[0]): dir = dirs[1] @@ -161,6 +129,6 @@ def main() -> int: # towards the inside. printf("%d\n", area_along_middle_of_path + 1 - loop_length/2) # Output: 4 - free(input.data) + free(grid.data) free(loop) return 0 diff --git a/examples/aoc2023/day11/part1.jou b/examples/aoc2023/day11/part1.jou index ea39cd6f..6c42cb25 100644 --- a/examples/aoc2023/day11/part1.jou +++ b/examples/aoc2023/day11/part1.jou @@ -2,54 +2,24 @@ import "stdlib/io.jou" import "stdlib/mem.jou" import "stdlib/str.jou" import "stdlib/math.jou" +import "../grid.jou" -class Input: - width: int - height: int - data: byte* +def duplicate_blank_lines(grid: Grid*) -> None: + grid->data = realloc(grid->data, 2*strlen(grid->data) + 1) + assert grid->data != NULL - def duplicate_blank_lines(self) -> None: - self->data = realloc(self->data, 2*strlen(self->data) + 1) - assert self->data != NULL - - y = 0 - while y < self->height: - if strspn(&self->data[(self->width + 1)*y], ".") == self->width: - # duplicate row - blank_start = &self->data[(self->width + 1)*y] - next_start = &self->data[(self->width + 1)*(y+1)] - memmove(next_start, blank_start, strlen(blank_start) + 1) - y += 2 - self->height++ - else: - y++ - - def transpose(self) -> None: - old_content = strdup(self->data) - old_width = self->width - old_height = self->height - self->width = old_height - self->height = old_width - - memset(self->data, '\n', strlen(self->data)) - for x = 0; x < self->width; x++: - for y = 0; y < self->height; y++: - self->data[(self->width + 1)*y + x] = old_content[(old_width + 1)*x + y] - - free(old_content) - - # array is terminated by [-1, -1] - def get_hashtag_coords(self) -> int[2]*: - result: int[2]* = malloc(sizeof(result[0]) * (self->width * self->height + 1)) - result_len = 0 - for y = 0; y < self->height; y++: - for x = 0; x < self->width; x++: - if self->data[(self->width + 1)*y + x] == '#': - result[result_len++] = [x, y] - - result[result_len] = [-1, -1] - return result + y = 0 + while y < grid->height: + if strspn(&grid->data[(grid->width + 1)*y], ".") == grid->width: + # duplicate row + blank_start = &grid->data[(grid->width + 1)*y] + next_start = &grid->data[(grid->width + 1)*(y+1)] + memmove(next_start, blank_start, strlen(blank_start) + 1) + y += 2 + grid->height++ + else: + y++ def manhattan_distance(a: int[2], b: int[2]) -> int: @@ -57,23 +27,17 @@ def manhattan_distance(a: int[2], b: int[2]) -> int: def main() -> int: - max_len = 100000 - input = Input{data = calloc(1, max_len+1)} - f = fopen("sampleinput.txt", "r") assert f != NULL - fread(input.data, 1, max_len, f) + grid = read_grid_from_file(f) fclose(f) - input.width = strcspn(input.data, "\n") as int - input.height = (strlen(input.data) as int) / (input.width + 1) - - input.duplicate_blank_lines() - input.transpose() - input.duplicate_blank_lines() - input.transpose() + duplicate_blank_lines(&grid) + grid.transpose() + duplicate_blank_lines(&grid) + grid.transpose() - coords = input.get_hashtag_coords() + coords = grid.find_all('#') sum = 0 for i = 0; coords[i][0] != -1; i++: @@ -81,6 +45,6 @@ def main() -> int: sum += manhattan_distance(coords[i], coords[k]) printf("%d\n", sum) # Output: 374 - free(input.data) + free(grid.data) free(coords) return 0 diff --git a/examples/aoc2023/day11/part2.jou b/examples/aoc2023/day11/part2.jou index b05ace4b..f33373db 100644 --- a/examples/aoc2023/day11/part2.jou +++ b/examples/aoc2023/day11/part2.jou @@ -1,59 +1,29 @@ import "stdlib/io.jou" import "stdlib/mem.jou" import "stdlib/str.jou" +import "../grid.jou" -class Input: - width: int - height: int - data: byte* +# returned array is terminated by -1 +def find_blank_lines(grid: Grid*) -> int*: + result: int* = malloc(sizeof(result[0]) * (grid->height + 1)) + result_len = 0 - # returned array is terminated by -1 - def find_blank_lines(self) -> int*: - result: int* = malloc(sizeof(result[0]) * (self->height + 1)) - result_len = 0 - - for y = 0; y < self->height; y++: - if strspn(&self->data[(self->width + 1)*y], ".") == self->width: - result[result_len++] = y - result[result_len] = -1 - return result - - def transpose(self) -> None: - old_content = strdup(self->data) - old_width = self->width - old_height = self->height - self->width = old_height - self->height = old_width - - memset(self->data, '\n', strlen(self->data)) - for x = 0; x < self->width; x++: - for y = 0; y < self->height; y++: - self->data[(self->width + 1)*y + x] = old_content[(old_width + 1)*x + y] - - free(old_content) - - # returned array is terminated by -1 - def get_hashtag_y_coords(self) -> int*: - result: int* = malloc(sizeof(result[0]) * (self->width * self->height + 1)) - result_len = 0 - for y = 0; y < self->height; y++: - for x = 0; x < self->width; x++: - if self->data[(self->width + 1)*y + x] == '#': - result[result_len++] = y - - result[result_len] = -1 - return result + for y = 0; y < grid->height; y++: + if strspn(&grid->data[(grid->width + 1)*y], ".") == grid->width: + result[result_len++] = y + result[result_len] = -1 + return result -def sum_vertical_distances(input: Input*) -> long: - hashtag_y_coords = input->get_hashtag_y_coords() - blank_lines = input->find_blank_lines() +def sum_vertical_distances(grid: Grid*) -> long: + hashtags = grid->find_all('#') + blank_lines = find_blank_lines(grid) result = 0L - for end = hashtag_y_coords; *end != -1; end++: - for start = hashtag_y_coords; start < end; start++: - for y = *start; y < *end; y++: + for end = hashtags; (*end)[0] != -1 and (*end)[1] != -1; end++: + for start = hashtags; start < end; start++: + for y = (*start)[1]; y < (*end)[1]; y++: y_is_blank = False for p = blank_lines; *p != -1; p++: if *p == y: @@ -66,27 +36,21 @@ def sum_vertical_distances(input: Input*) -> long: else: result += 1 - free(hashtag_y_coords) + free(hashtags) free(blank_lines) return result def main() -> int: - max_len = 100000 - input = Input{data = calloc(1, max_len+1)} - f = fopen("sampleinput.txt", "r") assert f != NULL - fread(input.data, 1, max_len, f) + grid = read_grid_from_file(f) fclose(f) - input.width = strcspn(input.data, "\n") as int - input.height = (strlen(input.data) as int) / (input.width + 1) - - result = sum_vertical_distances(&input) - input.transpose() - result += sum_vertical_distances(&input) + result = sum_vertical_distances(&grid) + grid.transpose() + result += sum_vertical_distances(&grid) printf("%lld\n", result) # Output: 8410 - free(input.data) + free(grid.data) return 0 diff --git a/examples/aoc2023/day13/part1.jou b/examples/aoc2023/day13/part1.jou index 1b495f2a..86783354 100644 --- a/examples/aoc2023/day13/part1.jou +++ b/examples/aoc2023/day13/part1.jou @@ -1,24 +1,6 @@ import "stdlib/io.jou" -import "stdlib/str.jou" import "stdlib/mem.jou" -import "stdlib/ascii.jou" - - -class Grid: - width: int - height: int - data: byte* - - def is_in_bounds(self, point: int[2]) -> bool: - x = point[0] - y = point[1] - return 0 <= x and x < self->width and 0 <= y and y < self->height - - def get(self, point: int[2]) -> byte: - assert self->is_in_bounds(point) - x = point[0] - y = point[1] - return self->data[(self->width + 1)*y + x] +import "../grid.jou" def find_h_reflection(g: Grid*) -> int: @@ -38,58 +20,19 @@ def find_h_reflection(g: Grid*) -> int: return -1 -def find_v_reflection(g: Grid*) -> int: - for reflect = 1; reflect < g->height; reflect++: - top = reflect-1 - bottom = reflect - matches = True - while top >= 0 and bottom < g->height and matches: - for x = 0; x < g->width; x++: - if g->get([x, top]) != g->get([x, bottom]): - matches = False - break - top-- - bottom++ - if matches: - return reflect - return -1 - - def main() -> int: - f = fopen("sampleinput.txt", "r") - assert f != NULL - - line: byte[200] - max_size = 10000 - - grid = Grid{data = malloc(max_size)} result = 0 - while True: - grid.width = 0 - grid.height = 0 - grid.data[0] = '\0' - - while fgets(line, sizeof(line) as int, f) != NULL: - trim_ascii_whitespace(line) - if line[0] == '\0': - break - - if grid.height == 0: # set width on first round - grid.width = strlen(line) as int - assert grid.width == strlen(line) - grid.height++ - - assert grid.width * grid.height < max_size - strcat(grid.data, line) - strcat(grid.data, "\n") - - if grid.height == 0: - # end of input - break + f = fopen("sampleinput.txt", "r") + assert f != NULL + while feof(f) == 0: + grid = read_grid_from_file(f) h = find_h_reflection(&grid) - v = find_v_reflection(&grid) + grid.transpose() + v = find_h_reflection(&grid) + free(grid.data) + assert h == -1 or v == -1 # does not have both if h != -1: result += h @@ -98,7 +41,6 @@ def main() -> int: else: assert False - free(grid.data) fclose(f) printf("%d\n", result) # Output: 405 return 0 diff --git a/examples/aoc2023/day13/part2.jou b/examples/aoc2023/day13/part2.jou index 6ec1f8dc..baa17ad6 100644 --- a/examples/aoc2023/day13/part2.jou +++ b/examples/aoc2023/day13/part2.jou @@ -1,24 +1,6 @@ import "stdlib/io.jou" -import "stdlib/str.jou" import "stdlib/mem.jou" -import "stdlib/ascii.jou" - - -class Grid: - width: int - height: int - data: byte* - - def is_in_bounds(self, point: int[2]) -> bool: - x = point[0] - y = point[1] - return 0 <= x and x < self->width and 0 <= y and y < self->height - - def get(self, point: int[2]) -> byte: - assert self->is_in_bounds(point) - x = point[0] - y = point[1] - return self->data[(self->width + 1)*y + x] +import "../grid.jou" def find_h_reflection(g: Grid*, mismatch_count: int) -> int: @@ -40,60 +22,19 @@ def find_h_reflection(g: Grid*, mismatch_count: int) -> int: return -1 -def find_v_reflection(g: Grid*, mismatch_count: int) -> int: - for reflect = 1; reflect < g->height; reflect++: - top = reflect-1 - bottom = reflect - n = 0 - - while top >= 0 and bottom < g->height: - for x = 0; x < g->width; x++: - if g->get([x, top]) != g->get([x, bottom]): - n++ - top-- - bottom++ - - if n == mismatch_count: - return reflect - - return -1 - - def main() -> int: - f = fopen("sampleinput.txt", "r") - assert f != NULL - - line: byte[200] - max_size = 10000 - - grid = Grid{data = malloc(max_size)} result = 0 - while True: - grid.width = 0 - grid.height = 0 - grid.data[0] = '\0' - - while fgets(line, sizeof(line) as int, f) != NULL: - trim_ascii_whitespace(line) - if line[0] == '\0': - break - - if grid.height == 0: # set width on first round - grid.width = strlen(line) as int - assert grid.width == strlen(line) - grid.height++ - - assert grid.width * grid.height < max_size - strcat(grid.data, line) - strcat(grid.data, "\n") - - if grid.height == 0: - # end of input - break + f = fopen("sampleinput.txt", "r") + assert f != NULL + while feof(f) == 0: + grid = read_grid_from_file(f) h = find_h_reflection(&grid, 1) - v = find_v_reflection(&grid, 1) + grid.transpose() + v = find_h_reflection(&grid, 1) + free(grid.data) + assert h == -1 or v == -1 # does not have both if h != -1: result += h @@ -102,7 +43,6 @@ def main() -> int: else: assert False - free(grid.data) fclose(f) printf("%d\n", result) # Output: 400 return 0 diff --git a/examples/aoc2023/day14/part1.jou b/examples/aoc2023/day14/part1.jou index 7373bcdd..6b3c095c 100644 --- a/examples/aoc2023/day14/part1.jou +++ b/examples/aoc2023/day14/part1.jou @@ -1,30 +1,6 @@ import "stdlib/io.jou" import "stdlib/mem.jou" -import "stdlib/str.jou" -import "stdlib/ascii.jou" - - -class Grid: - width: int - height: int - data: byte* - - def is_in_bounds(self, point: int[2]) -> bool: - x = point[0] - y = point[1] - return 0 <= x and x < self->width and 0 <= y and y < self->height - - def get(self, point: int[2]) -> byte: - assert self->is_in_bounds(point) - x = point[0] - y = point[1] - return self->data[(self->width + 1)*y + x] - - def set(self, point: int[2], value: byte) -> None: - assert self->is_in_bounds(point) - x = point[0] - y = point[1] - self->data[(self->width + 1)*y + x] = value +import "../grid.jou" def roll_north(grid: Grid*) -> None: @@ -54,28 +30,10 @@ def calculate_load(g: Grid*) -> int: def main() -> int: f = fopen("sampleinput.txt", "r") assert f != NULL - - line: byte[200] - max_size = 100000 - - grid = Grid{data = malloc(max_size)} - grid.data[0] = '\0' - - while fgets(line, sizeof(line) as int, f) != NULL: - trim_ascii_whitespace(line) - if grid.height == 0: # set width on first round - grid.width = strlen(line) as int - assert grid.width == strlen(line) - grid.height++ - - assert grid.width * grid.height < max_size - strcat(grid.data, line) - strcat(grid.data, "\n") - + grid = read_grid_from_file(f) fclose(f) roll_north(&grid) printf("%d\n", calculate_load(&grid)) # Output: 136 - free(grid.data) return 0 diff --git a/examples/aoc2023/day14/part2.jou b/examples/aoc2023/day14/part2.jou index 70ef0a39..e229d672 100644 --- a/examples/aoc2023/day14/part2.jou +++ b/examples/aoc2023/day14/part2.jou @@ -1,39 +1,8 @@ import "stdlib/io.jou" import "stdlib/mem.jou" import "stdlib/str.jou" -import "stdlib/ascii.jou" import "stdlib/math.jou" - - -class Grid: - width: int - height: int - data: byte* - - def is_in_bounds(self, point: int[2]) -> bool: - x = point[0] - y = point[1] - return 0 <= x and x < self->width and 0 <= y and y < self->height - - def get(self, point: int[2]) -> byte: - assert self->is_in_bounds(point) - x = point[0] - y = point[1] - return self->data[(self->width + 1)*y + x] - - def set(self, point: int[2], value: byte) -> None: - assert self->is_in_bounds(point) - x = point[0] - y = point[1] - self->data[(self->width + 1)*y + x] = value - - def swap(self, a: int[2], b: int[2]) -> None: - old_a = self->get(a) - self->set(a, self->get(b)) - self->set(b, old_a) - - def copy(self) -> Grid: - return Grid{width = self->width, height = self->height, data = strdup(self->data)} +import "../grid.jou" def can_roll(grid: Grid*, rock: int[2], dir: int[2], n: int) -> bool: @@ -92,24 +61,7 @@ def calculate_load(g: Grid*) -> int: def main() -> int: f = fopen("sampleinput.txt", "r") assert f != NULL - - line: byte[200] - max_size = 100000 - - grid = Grid{data = malloc(max_size)} - grid.data[0] = '\0' - - while fgets(line, sizeof(line) as int, f) != NULL: - trim_ascii_whitespace(line) - if grid.height == 0: # set width on first round - grid.width = strlen(line) as int - assert grid.width == strlen(line) - grid.height++ - - assert grid.width * grid.height < max_size - strcat(grid.data, line) - strcat(grid.data, "\n") - + grid = read_grid_from_file(f) fclose(f) previous_states: Grid* = NULL diff --git a/examples/aoc2023/grid.jou b/examples/aoc2023/grid.jou new file mode 100644 index 00000000..c402c2b7 --- /dev/null +++ b/examples/aoc2023/grid.jou @@ -0,0 +1,99 @@ +# This file contains a utility class for AoC solutions. +# It is not in the standard library because it feels too AoC-specific to me. + +import "stdlib/ascii.jou" +import "stdlib/str.jou" +import "stdlib/mem.jou" +import "stdlib/io.jou" + + +class Grid: + width: int + height: int + data: byte* + + def is_in_bounds(self, point: int[2]) -> bool: + x = point[0] + y = point[1] + return 0 <= x and x < self->width and 0 <= y and y < self->height + + def get(self, point: int[2]) -> byte: + assert self->is_in_bounds(point) + x = point[0] + y = point[1] + return self->data[(self->width + 1)*y + x] + + def set(self, point: int[2], value: byte) -> None: + assert self->is_in_bounds(point) + x = point[0] + y = point[1] + self->data[(self->width + 1)*y + x] = value + + def swap(self, a: int[2], b: int[2]) -> None: + old_a = self->get(a) + self->set(a, self->get(b)) + self->set(b, old_a) + + def copy(self) -> Grid: + return Grid{width = self->width, height = self->height, data = strdup(self->data)} + + def transpose(self) -> None: + old = self->copy() + memset(self->data, '\n', strlen(self->data)) + self->width = old.height + self->height = old.width + + for x = 0; x < self->width; x++: + for y = 0; y < self->height; y++: + self->set([x, y], old.get([y, x])) + free(old.data) + + # returned array is terminated by [-1, -1] + def find_all(self, b: byte) -> int[2]*: + result: int[2]* = malloc(sizeof(result[0]) * (self->width * self->height + 1)) + result_len = 0 + + for y = 0; y < self->height; y++: + for x = 0; x < self->width; x++: + if self->get([x, y]) == b: + result[result_len++] = [x, y] + + result = realloc(result, sizeof(result[0]) * (result_len + 1)) + result[result_len] = [-1, -1] + return result + + def find_first(self, b: byte) -> int[2]: + for y = 0; y < self->height; y++: + for x = 0; x < self->width; x++: + if self->get([x, y]) == b: + return [x, y] + assert False + + +# Reading stops on end of file or newline, so you can call this repeatedly +# to read multiple blank-line separated grids. +def read_grid_from_file(f: FILE*) -> Grid: + line: byte[5000] + max_size = 10000000 # 10 MB + + result = Grid{data = malloc(max_size)} + result.data[0] = '\0' + + while fgets(line, sizeof(line) as int, f) != NULL: + trim_ascii_whitespace(line) + if line[0] == '\0': + break + + if result.height == 0: # set width on first round + result.width = strlen(line) as int + assert result.width == strlen(line) + result.height++ + + assert result.width * result.height < max_size + strcat(result.data, line) + strcat(result.data, "\n") + + assert result.width != 0 and result.height != 0 + assert strlen(result.data) == (result.width + 1)*result.height + result.data = realloc(result.data, strlen(result.data) + 1) + return result