Пример #1
0
KHMEXP void
perf_set_thread_desc(const char * file, int line,
                     const wchar_t * name, const wchar_t * creator) {
    thread_info * t;
    char * fn_copy;

    perf_once();

    t = malloc(sizeof(*t));
    ZeroMemory(t, sizeof(*t));

#ifdef _WIN32
    t->thread = GetCurrentThreadId();
#else
#error Unsupported platform
#endif

    StringCbCopy(t->name, sizeof(t->name), name);
    if (creator)
        StringCbCopy(t->creator, sizeof(t->creator), creator);

    if (file[0] == '.' && file[1] == '\\')
        file += 2;

    EnterCriticalSection(&cs_alloc);

    fn_copy = hash_lookup(&fn_hash, file);
    if (fn_copy == NULL) {
        size_t cblen = 0;
        if (FAILED(StringCbLengthA(file, MAX_PATH * sizeof(char),
                                   &cblen)))
            fn_copy = NULL;
        else {
            fn_copy = malloc(cblen + sizeof(char));
            if (fn_copy) {
                hash_bin * b;
                int hv;

                StringCbCopyA(fn_copy, cblen + sizeof(char), file);

                hv = fn_hash.hash(fn_copy) % fn_hash.n;

                b = malloc(sizeof(*b));
                b->data = fn_copy;
                b->key = fn_copy;
                LINIT(b);
                LPUSH(&fn_hash.bins[hv], b);
            }
        }
    }

    t->file = fn_copy;
    t->line = line;

    LPUSH(&threads, t);
    LeaveCriticalSection(&cs_alloc);
}
Пример #2
0
KHMEXP void *
perf_realloc(const char * file, int line, void * data, size_t s) {
    void * n_data;
    allocation * a;
    size_t h;

    if (data == NULL)
        return perf_malloc(file, line, s);

    perf_once();
    h = HASHPTR(data);

    n_data = realloc(data, s);

    assert(n_data);

    EnterCriticalSection(&cs_alloc);
    for (a = ht[h]; a; a = LNEXT(a)) {
        if (a->ptr == data)
            break;
    }

    assert(a);

    LDELETE(&ht[h], a);

    a->size = s;
    a->ptr = n_data;

    h = HASHPTR(n_data);
    LPUSH(&ht[h], a);
    LeaveCriticalSection(&cs_alloc);

    return n_data;
}
Пример #3
0
/*! \internal
    \brief Adds a subscription to a message type
    \note Obtains ::cs_kmq_types
    */
void kmqint_msg_type_add_sub(int t, kmq_msg_subscription *s) {
    kmq_msg_subscription * ts;

    if(t < 0 || t > KMQ_MSG_TYPE_MAX)
        return;

    if(!msg_types[t])
        kmqint_msg_type_create(t);

    EnterCriticalSection(&cs_kmq_types);
    s->type = t;
    /* check if we already have this subscription */
    ts = msg_types[t]->subs;
    while(ts) {
        if((ts->rcpt_type == s->rcpt_type) &&
            (((ts->rcpt_type == KMQ_RCPTTYPE_CB) && (ts->recipient.cb == s->recipient.cb)) ||
             ((ts->rcpt_type == KMQ_RCPTTYPE_HWND) && (ts->recipient.hwnd == s->recipient.hwnd))))
            break;
        ts = LNEXT(ts);
    }
    /* add it if we didn't find it */
    if(!ts) {
        LPUSH(&msg_types[t]->subs, s);
    }
    LeaveCriticalSection(&cs_kmq_types);
}
Пример #4
0
/*! \internal
    \brief Frees a message object
    \note called with ::cs_kmq_msg held
    */
void
kmqint_put_message(kmq_message *m) {
    int queued;
    /* we can only free a message if the refcount is zero.
       Otherwise we have to wait until the call is freed. */
    if(m->refcount == 0) {
        LDELETE(&msg_active, m);
        LeaveCriticalSection(&cs_kmq_msg);
        queued = kmqint_notify_msg_completion(m);
        EnterCriticalSection(&cs_kmq_msg);
        if (!queued) {
            if(m->err_ctx) {
                kherr_release_context(m->err_ctx);
                m->err_ctx = NULL;
            }
            if(m->wait_o) {
                CloseHandle(m->wait_o);
                m->wait_o = NULL;
            }
            LPUSH(&msg_free,m);
        }
    } else if(m->wait_o) {
        SetEvent(m->wait_o);
    }
}
Пример #5
0
/*! \internal
    \brief Preps a thread for use with kmq
    \note Obtains ::cs_kmq_global
    */
void kmqint_attach_this_thread(void) {
    kmq_queue * q;

    EnterCriticalSection(&cs_kmq_global);

    q = (kmq_queue *) TlsGetValue(kmq_tls_queue);
    if(!q) {
        q = PMALLOC(sizeof(kmq_queue));

        InitializeCriticalSection(&q->cs);
        q->thread = GetCurrentThreadId();
        QINIT(q);
        LINIT(q);
        q->wait_o = CreateEvent(NULL, FALSE, FALSE, NULL);
        q->load = 0;
        q->last_post = 0;
        q->flags = 0;

        LPUSH(&queues, q);

        TlsSetValue(kmq_tls_queue, (LPVOID) q);
    }

    LeaveCriticalSection(&cs_kmq_global);
}
Пример #6
0
kmm_module_i * kmmint_get_module_i(wchar_t * name)
{
    kmm_module_i * m;
    size_t sz;

    if(FAILED(StringCbLength(name, KMM_MAXCB_NAME, &sz)))
        return NULL;
    sz += sizeof(wchar_t);

    EnterCriticalSection(&cs_kmm);
    m = (kmm_module_i *) hash_lookup(hash_modules, (void *) name);

    if(m == NULL) {
        m = PMALLOC(sizeof(kmm_module_i));
        ZeroMemory(m, sizeof(kmm_module_i));

        m->magic = KMM_MODULE_MAGIC;
        m->name = PMALLOC(sz);
        StringCbCopy(m->name, sz, name);
        m->state = KMM_MODULE_STATE_NONE;

        hash_add(hash_modules, (void *) m->name, (void *) m);
        LPUSH(&kmm_all_modules, m);
    }
    LeaveCriticalSection(&cs_kmm);

    return m;
}
Пример #7
0
static void
identpro_mark_for_deletion(kcdb_identpro_i * p)
{
    khm_boolean is_default = FALSE;

    if (!kcdb_is_identpro(p))
        return;

    EnterCriticalSection(&cs_identpro);
    if (DEFAULT_PROVIDER == p)
        is_default = TRUE;

    if (!(p->flags & KCDB_IDENTPRO_FLAG_DELETED)) {
        QDEL(&id_providers, p);
        n_id_providers--;
        LPUSH(&deleted_id_providers, p);
        p->flags |= KCDB_IDENTPRO_FLAG_DELETED;
	p->flags &= ~KCDB_IDENTPRO_FLAG_READY;

        kcdbint_identpro_post_message(KCDB_OP_DELETE, p);
    }

    if (p->refcount == 0)
        identpro_check_and_deleteL(p);

    if (is_default)
        identpro_check_and_set_default_provider();

    LeaveCriticalSection(&cs_identpro);
}
Пример #8
0
static void
free_event(kherr_event * e)
{
    EnterCriticalSection(&cs_error);

    assert(IS_KHERR_EVENT(e));
#ifdef DEBUG
    assert(LNEXT(e) == NULL);
    assert(LPREV(e) == NULL);
#endif

    if(e->flags & KHERR_RF_FREE_SHORT_DESC) {
        assert(e->short_desc);
        PFREE((void *) e->short_desc);
    }
    if(e->flags & KHERR_RF_FREE_LONG_DESC) {
        assert(e->long_desc);
        PFREE((void *) e->long_desc);
    }
    if(e->flags & KHERR_RF_FREE_SUGGEST) {
        assert(e->suggestion);
        PFREE((void *) e->suggestion);
    }

    free_event_params(e);

    ZeroMemory(e, sizeof(*e));

    LPUSH(&evt_free_list, e);
    LeaveCriticalSection(&cs_error);
}
Пример #9
0
KHMEXP khm_int32 KHMAPI 
khui_alert_create_empty(khui_alert ** result)
{
    khui_alert * a;

    a = PMALLOC(sizeof(*a));
    ZeroMemory(a, sizeof(*a));

    a->magic = KHUI_ALERT_MAGIC;

    /* set defaults */
    a->severity = KHERR_INFO;
    a->flags = KHUI_ALERT_FLAG_FREE_STRUCT | KHUI_ALERT_FLAG_MODIFIED;
    a->alert_type = KHUI_ALERTTYPE_NONE;
    khui_context_create(&a->ctx, KHUI_SCOPE_NONE,
                        NULL, KCDB_CREDTYPE_INVALID,
                        NULL);

    khui_alert_hold(a);
    EnterCriticalSection(&cs_alerts);
    LPUSH(&kh_alerts, a);
    LeaveCriticalSection(&cs_alerts);

    *result = a;

    return KHM_ERROR_SUCCESS;
}
Пример #10
0
/*! \internal
    \brief Free a message ref object
    \note called with cs_kmq_msg_ref and cs_kmq_msg held */
void kmqint_put_message_ref(kmq_message_ref * r) {
    if(!r)
        return;
    if(r->msg) {
        r->msg->refcount--;
        r->msg = NULL;
    }
    LPUSH(&kmq_msg_ref_free, r);
}
Пример #11
0
/* the plugin must be delisted before calling this */
void
kmmint_list_plugin(kmm_plugin_i * p)
{
    EnterCriticalSection(&cs_kmm);
    if((p->flags & KMM_PLUGIN_FLAG_IN_MODLIST) ||
        (p->flags & KMM_PLUGIN_FLAG_IN_LIST))
    {
        RaiseException(2, EXCEPTION_NONCONTINUABLE, 0, NULL);
    }
    p->flags |= KMM_PLUGIN_FLAG_IN_LIST;
    LPUSH(&kmm_listed_plugins, p);
    LeaveCriticalSection(&cs_kmm);
}
Пример #12
0
/*! \internal
    \brief Get a message object
    \note called with ::cs_kmq_msg held */
kmq_message *
kmqint_get_message(void) {
    kmq_message * m;

    LPOP(&msg_free,&m);
    if(!m) {
        /* allocate one */
        m = PMALLOC(sizeof(kmq_message));
    }
    ZeroMemory((void*)m, sizeof(kmq_message));

    LPUSH(&msg_active, m);

    return m;
}
Пример #13
0
/*! \internal
    \brief Create a message type
    \note Obtains ::cs_kmq_types
    */
void kmqint_msg_type_create(int t) {
    if(t < 0 || t > KMQ_MSG_TYPE_MAX)
        return;

    EnterCriticalSection(&cs_kmq_types);
    if(!msg_types[t]) {
        kmq_msg_type * mt;
        mt = PMALLOC(sizeof(kmq_msg_type));
        ZeroMemory(mt, sizeof(kmq_msg_type));
        mt->id = t;
        LINIT(mt);
        mt->subs = NULL;
        msg_types[t] = mt;

        LPUSH(&all_msg_types, mt);
    }
    LeaveCriticalSection(&cs_kmq_types);
}
Пример #14
0
KHMEXP void
perf_free  (void * b) {
    size_t h;
    allocation * a;

    perf_once();
    h = HASHPTR(b);

    EnterCriticalSection(&cs_alloc);
    for(a = ht[h]; a; a = LNEXT(a)) {
        if (a->ptr == b)
            break;
    }

    assert(a);

    LDELETE(&ht[h], a);
    LPUSH(&free_alloc, a);
    LeaveCriticalSection(&cs_alloc);
}
Пример #15
0
/*! \internal
    \note Obtains ::cs_kmq_global
*/
KHMEXP khm_int32 KHMAPI kmq_create_subscription(kmq_callback_t cb,
                                                khm_handle * result)
{
    kmq_msg_subscription * s;

    s = PMALLOC(sizeof(kmq_msg_subscription));
    ZeroMemory(s, sizeof(*s));
    s->magic = KMQ_MSG_SUB_MAGIC;
    LINIT(s);
    s->queue = kmqint_get_thread_queue();
    s->rcpt_type = KMQ_RCPTTYPE_CB;
    s->recipient.cb = cb;

    EnterCriticalSection(&cs_kmq_global);
    LPUSH(&kmq_adhoc_subs, s);
    LeaveCriticalSection(&cs_kmq_global);

    *result = (khm_handle) s;

    return KHM_ERROR_SUCCESS;
}
Пример #16
0
KHMEXP khm_int32 KHMAPI kmq_create_hwnd_subscription(HWND hw,
                                                     khm_handle * result)
{
    kmq_msg_subscription * s;

    s = PMALLOC(sizeof(kmq_msg_subscription));
    ZeroMemory(s, sizeof(*s));
    s->magic = KMQ_MSG_SUB_MAGIC;
    LINIT(s);
    s->queue = NULL;
    s->rcpt_type = KMQ_RCPTTYPE_HWND;
    s->recipient.hwnd = hw;

    EnterCriticalSection(&cs_kmq_global);
    LPUSH(&kmq_adhoc_subs, s);
    LeaveCriticalSection(&cs_kmq_global);

    *result = (khm_handle) s;

    return KHM_ERROR_SUCCESS;
}
Пример #17
0
/* Assumes that the context has been deleted from all relevant
   lists */
static void
free_context(kherr_context * c)
{
    kherr_context * ch;
    kherr_event * e;

    assert(IS_KHERR_CTX(c));

#ifdef DEBUG_CONTEXT
    if (IsDebuggerPresent())
        kherr_debug_printf(L"Freeing context 0x%x\n", c);
#endif

    EnterCriticalSection(&cs_error);

    if (c->desc_event)
        free_event(c->desc_event);
    c->desc_event = NULL;

    TPOPCHILD(c, &ch);
    while(ch) {
        free_context(ch);
        TPOPCHILD(c, &ch);
    }
    QGET(c, &e);
    while(e) {
        free_event(e);
        QGET(c, &e);
    }

    c->serial = 0;

    LPUSH(&ctx_free_list,c);
    LeaveCriticalSection(&cs_error);

#ifdef DEBUG_CONTEXT
    if (IsDebuggerPresent())
        kherr_debug_printf(L"Done with context 0x%x\n", c);
#endif
}
Пример #18
0
KHMEXP khm_int32 KHMAPI 
kcdb_credset_create(khm_handle * result)
{
    kcdb_credset * cs;

    cs = PMALLOC(sizeof(kcdb_credset));
    ZeroMemory(cs, sizeof(kcdb_credset));

    cs->magic = KCDB_CREDSET_MAGIC;
    InitializeCriticalSection(&(cs->cs));
    LINIT(cs);
    kcdb_credset_buf_new(cs);
    cs->version = 0;
    cs->seal_count = 0;

    EnterCriticalSection(&cs_credset);
    LPUSH(&kcdb_credsets, cs);
    LeaveCriticalSection(&cs_credset);

    *result = (khm_handle) cs;

    return KHM_ERROR_SUCCESS;
}
Пример #19
0
static kherr_context *
get_empty_context(void)
{
    kherr_context * c;

    EnterCriticalSection(&cs_error);
    if(ctx_free_list) {
        LPOP(&ctx_free_list, &c);
    } else {
        c = PMALLOC(sizeof(kherr_context));
    }

    ZeroMemory(c,sizeof(*c));
    c->severity = KHERR_NONE;
    c->flags = KHERR_CF_UNBOUND;
    c->magic = KHERR_CONTEXT_MAGIC;
    c->serial = ++ctx_serial;

    LPUSH(&ctx_root_list, c);

    LeaveCriticalSection(&cs_error);

    return c;
}
Пример #20
0
KHMEXP khm_int32 KHMAPI
kcdb_credtype_register(const kcdb_credtype * type, khm_int32 * new_id)
{
    khm_int32 id;
    kcdb_credtype_i * ict;
    size_t cb_name;
    size_t cb_short_desc;
    size_t cb_long_desc;
    int i;

    if(!type)
        return KHM_ERROR_INVALID_PARAM;

    if(type->id > KCDB_CREDTYPE_MAX_ID)
        return KHM_ERROR_INVALID_PARAM;

    if(type->name) {
        if(FAILED(StringCbLength(type->name, KCDB_MAXCB_NAME, &cb_name)))
            return KHM_ERROR_TOO_LONG;
        cb_name += sizeof(wchar_t);
    } else
        return KHM_ERROR_INVALID_PARAM;

    if(type->short_desc) {
        if(FAILED(StringCbLength(type->short_desc, KCDB_MAXCB_SHORT_DESC, &cb_short_desc)))
            return KHM_ERROR_TOO_LONG;
        cb_short_desc += sizeof(wchar_t);
    } else
        cb_short_desc = 0;

    if(type->long_desc) {
        if(FAILED(StringCbLength(type->long_desc, KCDB_MAXCB_LONG_DESC, &cb_long_desc)))
            return KHM_ERROR_TOO_LONG;
        cb_long_desc += sizeof(wchar_t);
    } else
        cb_long_desc = 0;

    if(type->sub == NULL)
        return KHM_ERROR_INVALID_PARAM;

    EnterCriticalSection(&cs_credtype);

    if(type->id <= 0) {
        if(KHM_FAILED(kcdb_credtype_get_next_free_id(&id))) {
            LeaveCriticalSection(&cs_credtype);
            return KHM_ERROR_NO_RESOURCES;
        }
    }
    else
        id = type->id;

    if(kcdb_credtype_tbl[id]) {
        LeaveCriticalSection(&cs_credtype);
        return KHM_ERROR_DUPLICATE;
    }

    for (i=1; i <= KCDB_CREDTYPE_MAX_ID; i++) {
        if(kcdb_credtype_tbl[i] && !wcscmp(kcdb_credtype_tbl[i]->ct.name, type->name)) {
            LeaveCriticalSection(&cs_credtype);
            return KHM_ERROR_DUPLICATE;
        }
    }

    ict = PMALLOC(sizeof(kcdb_credtype_i));
    ZeroMemory(ict, sizeof(kcdb_credtype_i));

    ict->ct.name = PMALLOC(cb_name);
    StringCbCopy(ict->ct.name, cb_name, type->name);

    if(cb_short_desc > 0) {
        ict->ct.short_desc = PMALLOC(cb_short_desc);
        StringCbCopy(ict->ct.short_desc, cb_short_desc, type->short_desc);
    }

    if(cb_long_desc > 0) {
        ict->ct.long_desc = PMALLOC(cb_long_desc);
        StringCbCopy(ict->ct.long_desc, cb_long_desc, type->long_desc);
    }

    ict->ct.id = id;

    ict->ct.icon = type->icon;

    ict->ct.sub = type->sub;

    ict->ct.is_equal = type->is_equal;

    kcdb_credtype_tbl[id] = ict;

    LPUSH(&kcdb_credtypes, ict);

    LeaveCriticalSection(&cs_credtype);

    kcdb_credtype_post_message(KCDB_OP_INSERT, &(ict->ct));

    if (new_id)
        *new_id = id;

    return KHM_ERROR_SUCCESS;
}
Пример #21
0
KHMEXP khm_int32   KHMAPI
kmm_provide_plugin(kmm_module module, kmm_plugin_reg * plugin)
{
    kmm_module_i * m;
    kmm_plugin_i * p;
    size_t cb_name = 0;
    size_t cb_desc = 0;
    size_t cb_dep = 0;

    m = kmm_module_from_handle(module);

    /* can only called when handing init_module() */
    if(m->state != KMM_MODULE_STATE_INIT)
        return KHM_ERROR_INVALID_OPERATION;

    if(!plugin ||
       FAILED(StringCbLength(plugin->name, KMM_MAXCB_NAME - sizeof(wchar_t),
                             &cb_name)) ||
       (plugin->description &&
        FAILED(StringCbLength(plugin->description,
                              KMM_MAXCB_DESC - sizeof(wchar_t),
                              &cb_desc))) ||
       (plugin->dependencies &&
        KHM_FAILED(multi_string_length_cb(plugin->dependencies,
                                          KMM_MAXCB_DEPS, &cb_dep)))) {
        return KHM_ERROR_INVALID_PARAM;
    }

    cb_name += sizeof(wchar_t);
    cb_desc += sizeof(wchar_t);

    p = kmmint_get_plugin_i(plugin->name);

    /* released below or in kmmint_init_module() */
    kmm_hold_plugin(kmm_handle_from_plugin(p));

    if(p->state != KMM_PLUGIN_STATE_NONE &&
        p->state != KMM_PLUGIN_STATE_PLACEHOLDER)
    {
        kmm_release_plugin(kmm_handle_from_plugin(p));
        return KHM_ERROR_DUPLICATE;
    }

    /* released when the plugin quits */
    kmm_hold_module(module);

    p->module = m;
    p->p.flags = plugin->flags;
    p->p.msg_proc = plugin->msg_proc;
    p->p.type = plugin->type;

    if(plugin->description) {
        p->p.description = PMALLOC(cb_desc);
        StringCbCopy(p->p.description, cb_desc, plugin->description);
    } else
        p->p.description = NULL;

    if(plugin->dependencies) {
        p->p.dependencies = PMALLOC(cb_dep);
        multi_string_copy_cb(p->p.dependencies, cb_dep, plugin->dependencies);
    } else
        p->p.dependencies = NULL;

    p->p.module = p->module->name;

    p->p.icon = plugin->icon;

    p->state = KMM_PLUGIN_STATE_REG;

    kmmint_delist_plugin(p);
    EnterCriticalSection(&cs_kmm);
    LPUSH(&(m->plugins), p);
    p->flags |= KMM_PLUGIN_FLAG_IN_MODLIST;
    LeaveCriticalSection(&cs_kmm);

    /* leave the plugin held because it is in the module's plugin list */
    return KHM_ERROR_SUCCESS;
}
Пример #22
0
KHMEXP khm_int32 KHMAPI kcdb_type_register(const kcdb_type * type, khm_int32 * new_id)
{
    kcdb_type_i *t;
    size_t cbsize;
    khm_int32 type_id;

    if(!type ||
        !type->comp ||
        !type->dup ||
        !type->isValid ||
        !type->toString ||
        !type->name)
        return KHM_ERROR_INVALID_PARAM;

    if((type->flags & KCDB_TYPE_FLAG_CB_MIN) &&
        (type->cb_min < 0 || type->cb_min > KCDB_TYPE_MAXCB))
    {
        return KHM_ERROR_INVALID_PARAM;
    }

    if((type->flags & KCDB_TYPE_FLAG_CB_MAX) &&
        (type->cb_max < 0 || type->cb_max > KCDB_TYPE_MAXCB))
    {
        return KHM_ERROR_INVALID_PARAM;
    }

    if((type->flags & KCDB_TYPE_FLAG_CB_MIN) &&
        (type->flags & KCDB_TYPE_FLAG_CB_MAX) &&
        (type->cb_max < type->cb_min))
    {
        return KHM_ERROR_INVALID_PARAM;
    }

    if(FAILED(StringCbLength(type->name, KCDB_MAXCB_NAME, &cbsize)))
        return KHM_ERROR_TOO_LONG;

    cbsize += sizeof(wchar_t);

    EnterCriticalSection(&cs_type);
    if(type->id == KCDB_TYPE_INVALID) {
        kcdb_type_get_next_free(&type_id);
    } else if(type->id < 0 || type->id > KCDB_TYPE_MAX_ID) {
        LeaveCriticalSection(&cs_type);
        return KHM_ERROR_INVALID_PARAM;
    } else if(kcdb_type_tbl[type->id]) {
        LeaveCriticalSection(&cs_type);
        return KHM_ERROR_DUPLICATE;
    } else {
        type_id = type->id;
    }

    if(type_id == KCDB_TYPE_INVALID) {
        LeaveCriticalSection(&cs_type);
        return KHM_ERROR_NO_RESOURCES;
    }

    t = PMALLOC(sizeof(kcdb_type_i));
    ZeroMemory(t, sizeof(kcdb_type_i));

    t->type.name = PMALLOC(cbsize);
    StringCbCopy(t->type.name, cbsize, type->name);

    t->type.comp = type->comp;
    t->type.dup = type->dup;
    t->type.flags = type->flags;
    t->type.id = type_id;
    t->type.isValid = type->isValid;
    t->type.toString = type->toString;

    LINIT(t);

    kcdb_type_tbl[type_id] = t;
    LPUSH(&kcdb_types, t);

    hash_add(kcdb_type_namemap, (void *) t->type.name, (void *) t);

    LeaveCriticalSection(&cs_type);

    if(new_id)
        *new_id = type_id;

    kcdb_type_post_message(KCDB_OP_INSERT, t);

    return KHM_ERROR_SUCCESS;
}
Пример #23
0
KHMEXP void * 
perf_malloc(const char * file, int line, size_t s) {
    allocation * a;
    void * ptr;
    size_t h;
    char * fn_copy = NULL;

    perf_once();

    assert(s > 0);

    EnterCriticalSection(&cs_alloc);
    a = get_allocation();

    ptr = malloc(s);

    assert(ptr);                /* TODO: handle this gracefully */

    if (file[0] == '.' && file[1] == '\\')
        file += 2;

    fn_copy = hash_lookup(&fn_hash, file);
    if (fn_copy == NULL) {

        size_t cblen = 0;
        if (FAILED(StringCbLengthA(file, MAX_PATH * sizeof(char),
                                   &cblen)))
            fn_copy = NULL;
        else {
            fn_copy = malloc(cblen + sizeof(char));
            if (fn_copy) {
                hash_bin * b;
                int hv;

                StringCbCopyA(fn_copy, cblen + sizeof(char), file);

                hv = fn_hash.hash(fn_copy) % fn_hash.n;

                b = malloc(sizeof(*b));
                b->data = fn_copy;
                b->key = fn_copy;
                LINIT(b);
                LPUSH(&fn_hash.bins[hv], b);
            }
        }
    }

    a->file = fn_copy;
    a->line = line;
    a->size = s;
    a->ptr = ptr;
#ifdef _WIN32
    a->thread = GetCurrentThreadId();
#endif

    h = HASHPTR(ptr);

    LPUSH(&ht[h], a);
    LeaveCriticalSection(&cs_alloc);

    return ptr;
}