/* * Copyright (c) 2018 Magnus Lind. * * This software is provided 'as-is', without any express or implied warranty. * In no event will the authors be held liable for any damages arising from * the use of this software. * * Permission is granted to anyone to use this software, alter it and re- * distribute it freely for any non-commercial, non-profit purpose subject to * the following restrictions: * * 1. The origin of this software must not be misrepresented; you must not * claim that you wrote the original software. If you use this software in a * product, an acknowledgment in the product documentation would be * appreciated but is not required. * * 2. Altered source versions must be plainly marked as such, and must not * be misrepresented as being the original software. * * 3. This notice may not be removed or altered from any distribution. * * 4. The names of this software and/or it's copyright holders may not be * used to endorse or promote products derived from this software without * specific prior written permission. * */ #include "areatrace.h" void areatrace_init(struct areatrace *at) { vec_init(&at->areas, sizeof(u16)); buf_reserve(&at->areas.buf, 64); } void areatrace_free(struct areatrace *at) { vec_free(&at->areas, NULL); } void areatrace_merge_overlapping(struct areatrace *at) { struct vec *areas = &at->areas; int i = 1; int size = vec_size(areas) - 1; while (i < size) { u16 *end = vec_get(areas, i); u16 *start = vec_get(areas, i + 1); if (*start == *end) { /* merge */ vec_remove(areas, i); vec_remove(areas, i); size -= 2; } else { i += 2; } } } void areatrace_get_largest(const struct areatrace *at, int *startp, int *endp) { const struct vec *areas = &at->areas; struct vec_iterator i; u16 *pstart; u16 *pend; int start = 0; int end = 0; int size = -1; for (vec_get_iterator(areas, &i); (pstart = vec_iterator_next(&i)) != NULL;) { pend = vec_iterator_next(&i); if (size < *pend - *pstart) { size = *pend - *pstart; start = *pstart; end = *pend + 1; } } if (startp != NULL) { *startp = start; } if (endp != NULL) { *endp = end; } } static int areatrace_addr_cb_cmp(const void *a, const void *b) { u16 ua = *(u16*)a; u16 ub = *(u16*)b; if (ua < ub) { return -1; } else if (ub < ua) { return 1; } else { return 0; } } static int areas_next_start_pos(struct vec *areas, int pos, u16 address) { int result = -1; for (pos = pos & ~1; pos < vec_size(areas); pos += 2) { u16 *next_addr = vec_get(areas, pos); if (*next_addr > address + 1) { break; } if (*next_addr == address + 1) { result = pos; break; } } return result; } static int areas_prev_end_pos(struct vec *areas, int pos, u16 address) { int result = -1; for (pos = (pos - 2) | 1; pos >= 0; pos -= 2) { u16 *prev_addr = vec_get(areas, pos); if (*prev_addr < address - 1) { break; } if (*prev_addr == address - 1) { result = pos; break; } } return result; } void areatrace_access(struct areatrace *at, u16 address) { struct vec *areas = &at->areas; int pos; int orig_pos; int next_start_pos; int prev_end_pos; orig_pos = vec_find(areas, areatrace_addr_cb_cmp, &address); pos = orig_pos; if (pos < 0) { pos = -(pos + 2); } next_start_pos = areas_next_start_pos(areas, pos, address); prev_end_pos = areas_prev_end_pos(areas, pos, address); if (next_start_pos != -1) { vec_set(areas, next_start_pos, &address); if (prev_end_pos == -1) { u16 *addrp; addrp = vec_get(areas, next_start_pos - 1); if (addrp != NULL && *addrp >= address) { u16 addr = address - 1; /* shrink */ vec_set(areas, next_start_pos - 1, &addr); addrp = vec_get(areas, next_start_pos - 2); if (*addrp == address) { vec_remove(areas, next_start_pos - 2); vec_remove(areas, next_start_pos - 2); } } } } if (prev_end_pos != -1) { vec_set(areas, prev_end_pos, &address); if (next_start_pos == -1) { u16 *addrp; addrp = vec_get(areas, prev_end_pos + 1); if (addrp != NULL && *addrp <= address) { u16 addr = address + 1; /* shrink */ vec_set(areas, prev_end_pos + 1, &addr); addrp = vec_get(areas, prev_end_pos + 2); if (*addrp == address) { vec_remove(areas, prev_end_pos + 1); vec_remove(areas, prev_end_pos + 1); } } } } if (orig_pos < 0 && (pos & 1) == 0 && next_start_pos == -1 && prev_end_pos == -1) { vec_insert(areas, pos, &address); vec_insert(areas, pos, &address); } }