static ph_result_t do_register(ph_hook_point_t *hp, ph_hook_func func, void *closure, int8_t pri, ph_hook_unreg_func unreg) { ph_hook_point_head_t *old_head, *new_head; ph_result_t res = PH_ERR; uint16_t num_items = 0; ck_rwlock_write_lock(&rwlock); { old_head = hp->head; if (old_head) { num_items = old_head->nitems; } // num_items is 1-less than the number we want, but the head struct // has 1 element embedded, so we're fine to multiply it out here new_head = ph_mem_alloc_size(mt.head, sizeof(*new_head) + (sizeof(ph_hook_item_t) * (num_items))); if (!new_head) { goto done; } new_head->nitems = num_items + 1; // Copy old data in if (old_head) { memcpy(new_head->items, old_head->items, old_head->nitems * sizeof(ph_hook_item_t)); } new_head->items[num_items].closure = closure; new_head->items[num_items].func = func; new_head->items[num_items].pri = pri; new_head->items[num_items].unreg = unreg; qsort(new_head->items, new_head->nitems, sizeof(ph_hook_item_t), compare_item); hp->head = new_head; if (old_head) { ph_thread_epoch_defer(&old_head->entry, free_head); } res = PH_OK; } done: ck_rwlock_write_unlock(&rwlock); return res; }
char *ph_mem_strdup(ph_memtype_t memtype, const char *str) { uint32_t len = strlen(str) + 1; char *d; d = ph_mem_alloc_size(memtype, len); if (!d) { return NULL; } memcpy(d, str, len); return d; }
static struct ph_dns_query_response *make_aaaa_resp(unsigned char *abuf, int alen) { int ancount = DNS_HEADER_ANCOUNT(abuf); struct ph_dns_query_response *resp; uint32_t size; struct ares_addr6ttl *ttls = NULL; int res; struct hostent *host; int nttls; int i; char *name; ttls = malloc(ancount * sizeof(*ttls)); if (!ttls) { return NULL; } nttls = ancount; res = ares_parse_aaaa_reply(abuf, alen, &host, ttls, &nttls); if (res) { free(ttls); return NULL; } size = sizeof(*resp) + ((nttls - 1) * sizeof(resp->answer[0])); resp = ph_mem_alloc_size(mt.aresp, size + strlen(host->h_name) + 1); if (!resp) { free(ttls); return NULL; } // this is where we'll stash the name name = ((char*)resp) + size; strcpy(name, host->h_name); resp->num_answers = ancount; resp->name = name; for (i = 0; i < nttls; i++) { resp->answer[i].name = name; resp->answer[i].ttl = ttls[i].ttl; resp->answer[i].addr.family = AF_INET6; resp->answer[i].addr.sa.v6.sin6_family = AF_INET6; memcpy(&resp->answer[i].addr.sa.v6.sin6_addr, &ttls[i].ip6addr, sizeof(ttls[i].ip6addr)); } free(ttls); ares_free_hostent(host); return resp; }
ph_string_t *ph_string_make_empty(ph_memtype_t mt, uint32_t size) { char *buf = ph_mem_alloc_size(mt, size); ph_string_t *str; if (!buf) { return NULL; } str = ph_string_make_claim(mt, buf, 0, size); if (!str) { ph_mem_free(mt, buf); } return str; }
ph_result_t ph_string_append_buf(ph_string_t *str, const char *buf, uint32_t len) { if (len + str->len > str->alloc) { // Not enough room if (str->mt == PH_STRING_STATIC) { // Just clamp to the available space len = str->alloc - str->len; } else { // Grow it uint32_t nsize = ph_power_2(str->len + len); char *nbuf; // Negative memtypes encode the desired memtype as the negative // value. Allocate a buffer from scratch using the desired memtype if (str->mt < 0) { nbuf = ph_mem_alloc_size(-str->mt, nsize); } else { nbuf = ph_mem_realloc(str->mt, str->buf, nsize); } if (nbuf == NULL) { return PH_NOMEM; } if (str->mt < 0) { // Promote from static growable to heap allocated growable memcpy(nbuf, str->buf, str->len); str->mt = -str->mt; } str->buf = nbuf; str->alloc = nsize; } } memcpy(str->buf + str->len, buf, len); str->len += len; return PH_OK; }
ph_variant_t *ph_var_array(uint32_t nelems) { ph_variant_t *var; var = ph_mem_alloc(mt.var); if (!var) { return NULL; } var->ref = 1; var->type = PH_VAR_ARRAY; var->u.aval.len = 0; var->u.aval.alloc = nelems; var->u.aval.arr = ph_mem_alloc_size(mt.arr, nelems * sizeof(ph_variant_t*)); if (!var->u.aval.arr) { ph_mem_free(mt.var, var); return NULL; } return var; }
static struct ph_dns_query_response *make_srv_resp(unsigned char *abuf, int alen) { struct ph_dns_query_response *resp; uint32_t size; struct ares_srv_reply *srv, *tmp; uint32_t nres = 0, i; if (ares_parse_srv_reply(abuf, alen, &srv) != ARES_SUCCESS) { return NULL; } for (nres = 0, tmp = srv; tmp; tmp = tmp->next) { nres++; } size = sizeof(*resp) + ((nres - 1) * sizeof(resp->answer[0])); resp = ph_mem_alloc_size(mt.aresp, size); if (!resp) { ares_free_data(srv); return NULL; } resp->num_answers = nres; for (tmp = srv, i = 0; tmp; tmp = tmp->next, i++) { resp->answer[i].name = ph_mem_strdup(mt.string, tmp->host); resp->answer[i].priority = tmp->priority; resp->answer[i].weight = tmp->weight; resp->answer[i].port = tmp->port; } // Sort in priority order qsort(resp->answer, nres, sizeof(resp->answer[0]), compare_mx_ent); ares_free_data(srv); return resp; }
void *ph_mem_realloc(ph_memtype_t mt, void *ptr, uint64_t size) { struct mem_type *mem_type; ph_counter_block_t *block; static const uint8_t slots[2] = { SLOT_BYTES, SLOT_REALLOC }; int64_t values[3]; struct sized_header *hdr; uint64_t orig_size; void *new_ptr; if (size == 0) { ph_mem_free(mt, ptr); return NULL; } if (ptr == NULL) { return ph_mem_alloc_size(mt, size); } mem_type = resolve_mt(mt); if (mem_type->def.item_size) { memory_panic( "mem_type %s is not vsize and cannot be used with ph_mem_realloc", mem_type->def.name); return NULL; } hdr = ptr; hdr--; ptr = hdr; if (hdr->mt != mt) { memory_panic("ph_mem_realloc: hdr->mt %d != caller provided mt %d %s", hdr->mt, mt, mem_type->def.name); } orig_size = hdr->size; if (orig_size == size) { return ptr; } hdr = realloc(ptr, size + HEADER_RESERVATION); if (!hdr) { ph_counter_scope_add(mem_type->scope, mem_type->first_slot + SLOT_OOM, 1); if (mem_type->def.flags & PH_MEM_FLAGS_PANIC) { ph_panic("OOM while allocating %" PRIu64 " bytes of %s/%s memory", size + HEADER_RESERVATION, mem_type->def.facility, mem_type->def.name); } return NULL; } new_ptr = hdr + 1; hdr->size = size; block = ph_counter_block_open(mem_type->scope); values[0] = size - orig_size; values[1] = 1; ph_counter_block_bulk_add(block, 2, slots, values); ph_counter_block_delref(block); if (size > orig_size && mem_type->def.flags & PH_MEM_FLAGS_ZERO) { memset((char*)new_ptr + orig_size, 0, size - orig_size); } return new_ptr; }
static ph_result_t do_unregister(ph_hook_point_t *hp, ph_hook_func func, void *closure) { ph_hook_point_head_t *old_head, *new_head; ph_result_t res = PH_ERR; uint16_t off = 0; bool found = false; struct ph_hook_item_free *unreg = 0; ck_rwlock_write_lock(&rwlock); { old_head = hp->head; if (!old_head) { goto done; } for (off = 0; off < old_head->nitems; off++) { if (old_head->items[off].func == func && old_head->items[off].closure == closure) { found = true; break; } } if (!found) { goto done; } new_head = ph_mem_alloc_size(mt.head, sizeof(*new_head) + (sizeof(ph_hook_item_t) * (old_head->nitems-1))); if (!new_head) { goto done; } if (old_head->items[off].unreg) { unreg = ph_mem_alloc(mt.unreg); if (!unreg) { ph_mem_free(mt.head, new_head); goto done; } unreg->closure = old_head->items[off].closure; unreg->func = old_head->items[off].func; unreg->unreg = old_head->items[off].unreg; } new_head->nitems = old_head->nitems - 1; // Copy before the item if (off) { memcpy(new_head->items, old_head->items, off * sizeof(ph_hook_item_t)); } // Copy after the item if (off + 1 <= old_head->nitems) { memcpy(new_head->items + off, old_head->items + off + 1, (old_head->nitems - (off+1)) * sizeof(ph_hook_item_t)); } // Don't need to re-sort, since we simply removed that item hp->head = new_head; ph_thread_epoch_defer(&old_head->entry, free_head); // Arrange to unregister if (unreg) { ph_thread_epoch_defer(&unreg->entry, call_unreg); } res = PH_OK; } done: ck_rwlock_write_unlock(&rwlock); return res; }