Quellcodebibliothek Statistik Leitseite products/Sources/formale Sprachen/C/Firefox/security/nss/lib/base/   (Browser von der Mozilla Stiftung Version 136.0.1©)  Datei vom 10.2.2025 mit Größe 30 kB image not shown  

Quelle  arena.c   Sprache: C

 
/* 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/. */


/*
 * arena.c
 *
 * This contains the implementation of NSS's thread-safe arenas.
 */


#ifndef BASE_H
#include "base.h"
#endif /* BASE_H */

#ifdef ARENA_THREADMARK
#include "prthread.h"
#endif /* ARENA_THREADMARK */

#include "prlock.h"
#include "plarena.h"

#include <string.h>

/*
 * NSSArena
 *
 * This is based on NSPR's arena code, but it is threadsafe.
 *
 * The public methods relating to this type are:
 *
 *  NSSArena_Create  -- constructor
 *  NSSArena_Destroy
 *  NSS_ZAlloc
 *  NSS_ZRealloc
 *  NSS_ZFreeIf
 *
 * The nonpublic methods relating to this type are:
 *
 *  nssArena_Create  -- constructor
 *  nssArena_Destroy
 *  nssArena_Mark
 *  nssArena_Release
 *  nssArena_Unmark
 *
 *  nss_ZAlloc
 *  nss_ZFreeIf
 *  nss_ZRealloc
 *
 * In debug builds, the following calls are available:
 *
 *  nssArena_verifyPointer
 *  nssArena_registerDestructor
 *  nssArena_deregisterDestructor
 */


struct NSSArenaStr {
    PLArenaPool pool;
    PRLock *lock;
#ifdef ARENA_THREADMARK
    PRThread *marking_thread;
    nssArenaMark *first_mark;
    nssArenaMark *last_mark;
#endif /* ARENA_THREADMARK */
#ifdef ARENA_DESTRUCTOR_LIST
    struct arena_destructor_node *first_destructor;
    struct arena_destructor_node *last_destructor;
#endif /* ARENA_DESTRUCTOR_LIST */
};

/*
 * nssArenaMark
 *
 * This type is used to mark the current state of an NSSArena.
 */


struct nssArenaMarkStr {
    PRUint32 magic;
    void *mark;
#ifdef ARENA_THREADMARK
    nssArenaMark *next;
#endif /* ARENA_THREADMARK */
#ifdef ARENA_DESTRUCTOR_LIST
    struct arena_destructor_node *next_destructor;
    struct arena_destructor_node *prev_destructor;
#endif /* ARENA_DESTRUCTOR_LIST */
};

#define MARK_MAGIC 0x4d41524b /* "MARK" how original */

/*
 * But first, the pointer-tracking code
 */

#ifdef DEBUG
extern const NSSError NSS_ERROR_INTERNAL_ERROR;

static nssPointerTracker arena_pointer_tracker;

static PRStatus
arena_add_pointer(const NSSArena *arena)
{
    PRStatus rv;

    rv = nssPointerTracker_initialize(&arena_pointer_tracker);
    if (PR_SUCCESS != rv) {
        return rv;
    }

    rv = nssPointerTracker_add(&arena_pointer_tracker, arena);
    if (PR_SUCCESS != rv) {
        NSSError e = NSS_GetError();
        if (NSS_ERROR_NO_MEMORY != e) {
            nss_SetError(NSS_ERROR_INTERNAL_ERROR);
        }

        return rv;
    }

    return PR_SUCCESS;
}

static PRStatus
arena_remove_pointer(const NSSArena *arena)
{
    PRStatus rv;

    rv = nssPointerTracker_remove(&arena_pointer_tracker, arena);
    if (PR_SUCCESS != rv) {
        nss_SetError(NSS_ERROR_INTERNAL_ERROR);
    }

    return rv;
}

/*
 * nssArena_verifyPointer
 *
 * This method is only present in debug builds.
 *
 * If the specified pointer is a valid pointer to an NSSArena object,
 * this routine will return PR_SUCCESS.  Otherwise, it will put an
 * error on the error stack and return PR_FAILURE.
 *
 * The error may be one of the following values:
 *  NSS_ERROR_INVALID_ARENA
 *
 * Return value:
 *  PR_SUCCESS if the pointer is valid
 *  PR_FAILURE if it isn't
 */


NSS_IMPLEMENT PRStatus
nssArena_verifyPointer(const NSSArena *arena)
{
    PRStatus rv;

    rv = nssPointerTracker_initialize(&arena_pointer_tracker);
    if (PR_SUCCESS != rv) {
        /*
         * This is a little disingenious.  We have to initialize the
         * tracker, because someone could "legitimately" try to verify
         * an arena pointer before one is ever created.  And this step
         * might fail, due to lack of memory.  But the only way that
         * this step can fail is if it's doing the call_once stuff,
         * (later calls just no-op).  And if it didn't no-op, there
         * aren't any valid arenas.. so the argument certainly isn't one.
         */

        nss_SetError(NSS_ERROR_INVALID_ARENA);
        return PR_FAILURE;
    }

    rv = nssPointerTracker_verify(&arena_pointer_tracker, arena);
    if (PR_SUCCESS != rv) {
        nss_SetError(NSS_ERROR_INVALID_ARENA);
        return PR_FAILURE;
    }

    return PR_SUCCESS;
}
#endif /* DEBUG */

#ifdef ARENA_DESTRUCTOR_LIST

struct arena_destructor_node {
    struct arena_destructor_node *next;
    struct arena_destructor_node *prev;
    void (*destructor)(void *argument);
    void *arg;
};

/*
 * nssArena_registerDestructor
 *
 * This routine stores a pointer to a callback and an arbitrary
 * pointer-sized argument in the arena, at the current point in
 * the mark stack.  If the arena is destroyed, or an "earlier"
 * mark is released, then this destructor will be called at that
 * time.  Note that the destructor will be called with the arena
 * locked, which means the destructor may free memory in that
 * arena, but it may not allocate or cause to be allocated any
 * memory.  This callback facility was included to support our
 * debug-version pointer-tracker feature; overuse runs counter to
 * the the original intent of arenas.  This routine returns a
 * PRStatus value; if successful, it will return PR_SUCCESS.  If
 * unsuccessful, it will set an error on the error stack and
 * return PR_FAILURE.
 *
 * The error may be one of the following values:
 *  NSS_ERROR_INVALID_ARENA
 *  NSS_ERROR_NO_MEMORY
 *
 * Return value:
 *  PR_SUCCESS
 *  PR_FAILURE
 */


NSS_IMPLEMENT PRStatus
nssArena_registerDestructor(NSSArena *arena, void (*destructor)(void *argument),
                            void *arg)
{
    struct arena_destructor_node *it;

#ifdef NSSDEBUG
    if (PR_SUCCESS != nssArena_verifyPointer(arena)) {
        return PR_FAILURE;
    }
#endif /* NSSDEBUG */

    it = nss_ZNEW(arena, struct arena_destructor_node);
    if ((struct arena_destructor_node *)NULL == it) {
        return PR_FAILURE;
    }

    it->prev = arena->last_destructor;
    arena->last_destructor->next = it;
    arena->last_destructor = it;
    it->destructor = destructor;
    it->arg = arg;

    if ((nssArenaMark *)NULL != arena->last_mark) {
        arena->last_mark->prev_destructor = it->prev;
        arena->last_mark->next_destructor = it->next;
    }

    return PR_SUCCESS;
}

NSS_IMPLEMENT PRStatus
nssArena_deregisterDestructor(NSSArena *arena,
                              void (*destructor)(void *argument), void *arg)
{
    struct arena_destructor_node *it;

#ifdef NSSDEBUG
    if (PR_SUCCESS != nssArena_verifyPointer(arena)) {
        return PR_FAILURE;
    }
#endif /* NSSDEBUG */

    for (it = arena->first_destructor; it; it = it->next) {
        if ((it->destructor == destructor) && (it->arg == arg)) {
            break;
        }
    }

    if ((struct arena_destructor_node *)NULL == it) {
        nss_SetError(NSS_ERROR_NOT_FOUND);
        return PR_FAILURE;
    }

    if (it == arena->first_destructor) {
        arena->first_destructor = it->next;
    }

    if (it == arena->last_destructor) {
        arena->last_destructor = it->prev;
    }

    if ((struct arena_destructor_node *)NULL != it->prev) {
        it->prev->next = it->next;
    }

    if ((struct arena_destructor_node *)NULL != it->next) {
        it->next->prev = it->prev;
    }

    {
        nssArenaMark *m;
        for (m = arena->first_mark; m; m = m->next) {
            if (m->next_destructor == it) {
                m->next_destructor = it->next;
            }
            if (m->prev_destructor == it) {
                m->prev_destructor = it->prev;
            }
        }
    }

    nss_ZFreeIf(it);
    return PR_SUCCESS;
}

static void
nss_arena_call_destructor_chain(struct arena_destructor_node *it)
{
    for (; it; it = it->next) {
        (*(it->destructor))(it->arg);
    }
}

#endif /* ARENA_DESTRUCTOR_LIST */

/*
 * NSSArena_Create
 *
 * This routine creates a new memory arena.  This routine may return
 * NULL upon error, in which case it will have created an error stack.
 *
 * The top-level error may be one of the following values:
 *  NSS_ERROR_NO_MEMORY
 *
 * Return value:
 *  NULL upon error
 *  A pointer to an NSSArena upon success
 */


NSS_IMPLEMENT NSSArena *
NSSArena_Create(void)
{
    nss_ClearErrorStack();
    return nssArena_Create();
}

/*
 * nssArena_Create
 *
 * This routine creates a new memory arena.  This routine may return
 * NULL upon error, in which case it will have set an error on the
 * error stack.
 *
 * The error may be one of the following values:
 *  NSS_ERROR_NO_MEMORY
 *
 * Return value:
 *  NULL upon error
 *  A pointer to an NSSArena upon success
 */


NSS_IMPLEMENT NSSArena *
nssArena_Create(void)
{
    NSSArena *rv = (NSSArena *)NULL;

    rv = nss_ZNEW((NSSArena *)NULL, NSSArena);
    if ((NSSArena *)NULL == rv) {
        nss_SetError(NSS_ERROR_NO_MEMORY);
        return (NSSArena *)NULL;
    }

    rv->lock = PR_NewLock();
    if ((PRLock *)NULL == rv->lock) {
        (void)nss_ZFreeIf(rv);
        nss_SetError(NSS_ERROR_NO_MEMORY);
        return (NSSArena *)NULL;
    }

    /*
     * Arena sizes.  The current security code has 229 occurrences of
     * PORT_NewArena.  The default chunksizes specified break down as
     *
     *  Size    Mult.   Specified as
     *   512       1    512
     *  1024       7    1024
     *  2048       5    2048
     *  2048       5    CRMF_DEFAULT_ARENA_SIZE
     *  2048     190    DER_DEFAULT_CHUNKSIZE
     *  2048      20    SEC_ASN1_DEFAULT_ARENA_SIZE
     *  4096       1    4096
     *
     * Obviously this "default chunksize" flexibility isn't very
     * useful to us, so I'll just pick 2048.
     */


    PL_InitArenaPool(&rv->pool, "NSS", 2048, sizeof(double));

#ifdef DEBUG
    {
        PRStatus st;
        st = arena_add_pointer(rv);
        if (PR_SUCCESS != st) {
            PL_FinishArenaPool(&rv->pool);
            PR_DestroyLock(rv->lock);
            (void)nss_ZFreeIf(rv);
            return (NSSArena *)NULL;
        }
    }
#endif /* DEBUG */

    return rv;
}

/*
 * NSSArena_Destroy
 *
 * This routine will destroy the specified arena, freeing all memory
 * allocated from it.  This routine returns a PRStatus value; if
 * successful, it will return PR_SUCCESS.  If unsuccessful, it will
 * create an error stack and return PR_FAILURE.
 *
 * The top-level error may be one of the following values:
 *  NSS_ERROR_INVALID_ARENA
 *
 * Return value:
 *  PR_SUCCESS upon success
 *  PR_FAILURE upon failure
 */


NSS_IMPLEMENT PRStatus
NSSArena_Destroy(NSSArena *arena)
{
    nss_ClearErrorStack();

#ifdef DEBUG
    if (PR_SUCCESS != nssArena_verifyPointer(arena)) {
        return PR_FAILURE;
    }
#endif /* DEBUG */

    return nssArena_Destroy(arena);
}

/*
 * nssArena_Destroy
 *
 * This routine will destroy the specified arena, freeing all memory
 * allocated from it.  This routine returns a PRStatus value; if
 * successful, it will return PR_SUCCESS.  If unsuccessful, it will
 * set an error on the error stack and return PR_FAILURE.
 *
 * The error may be one of the following values:
 *  NSS_ERROR_INVALID_ARENA
 *
 * Return value:
 *  PR_SUCCESS
 *  PR_FAILURE
 */


NSS_IMPLEMENT PRStatus
nssArena_Destroy(NSSArena *arena)
{
    PRLock *lock;

#ifdef NSSDEBUG
    if (PR_SUCCESS != nssArena_verifyPointer(arena)) {
        return PR_FAILURE;
    }
#endif /* NSSDEBUG */

    if ((PRLock *)NULL == arena->lock) {
        /* Just got destroyed */
        nss_SetError(NSS_ERROR_INVALID_ARENA);
        return PR_FAILURE;
    }
    PR_Lock(arena->lock);

#ifdef DEBUG
    if (PR_SUCCESS != arena_remove_pointer(arena)) {
        PR_Unlock(arena->lock);
        return PR_FAILURE;
    }
#endif /* DEBUG */

#ifdef ARENA_DESTRUCTOR_LIST
    /* Note that the arena is locked at this time */
    nss_arena_call_destructor_chain(arena->first_destructor);
#endif /* ARENA_DESTRUCTOR_LIST */

    PL_FinishArenaPool(&arena->pool);
    lock = arena->lock;
    arena->lock = (PRLock *)NULL;
    PR_Unlock(lock);
    PR_DestroyLock(lock);
    (void)nss_ZFreeIf(arena);
    return PR_SUCCESS;
}

static void *nss_zalloc_arena_locked(NSSArena *arena, PRUint32 size);

/*
 * nssArena_Mark
 *
 * This routine "marks" the current state of an arena.  Space
 * allocated after the arena has been marked can be freed by
 * releasing the arena back to the mark with nssArena_Release,
 * or committed by calling nssArena_Unmark.  When successful,
 * this routine returns a valid nssArenaMark pointer.  This
 * routine may return NULL upon error, in which case it will
 * have set an error on the error stack.
 *
 * The error may be one of the following values:
 *  NSS_ERROR_INVALID_ARENA
 *  NSS_ERROR_NO_MEMORY
 *  NSS_ERROR_ARENA_MARKED_BY_ANOTHER_THREAD
 *
 * Return value:
 *  NULL upon failure
 *  An nssArenaMark pointer upon success
 */


NSS_IMPLEMENT nssArenaMark *
nssArena_Mark(NSSArena *arena)
{
    nssArenaMark *rv;
    void *p;

#ifdef NSSDEBUG
    if (PR_SUCCESS != nssArena_verifyPointer(arena)) {
        return (nssArenaMark *)NULL;
    }
#endif /* NSSDEBUG */

    if ((PRLock *)NULL == arena->lock) {
        /* Just got destroyed */
        nss_SetError(NSS_ERROR_INVALID_ARENA);
        return (nssArenaMark *)NULL;
    }
    PR_Lock(arena->lock);

#ifdef ARENA_THREADMARK
    if ((PRThread *)NULL == arena->marking_thread) {
        /* Unmarked.  Store our thread ID */
        arena->marking_thread = PR_GetCurrentThread();
        /* This call never fails. */
    } else {
        /* Marked.  Verify it's the current thread */
        if (PR_GetCurrentThread() != arena->marking_thread) {
            PR_Unlock(arena->lock);
            nss_SetError(NSS_ERROR_ARENA_MARKED_BY_ANOTHER_THREAD);
            return (nssArenaMark *)NULL;
        }
    }
#endif /* ARENA_THREADMARK */

    p = PL_ARENA_MARK(&arena->pool);
    /* No error possible */

    /* Do this after the mark */
    rv = (nssArenaMark *)nss_zalloc_arena_locked(arena, sizeof(nssArenaMark));
    if ((nssArenaMark *)NULL == rv) {
        PR_Unlock(arena->lock);
        nss_SetError(NSS_ERROR_NO_MEMORY);
        return (nssArenaMark *)NULL;
    }

#ifdef ARENA_THREADMARK
    if ((nssArenaMark *)NULL == arena->first_mark) {
        arena->first_mark = rv;
        arena->last_mark = rv;
    } else {
        arena->last_mark->next = rv;
        arena->last_mark = rv;
    }
#endif /* ARENA_THREADMARK */

    rv->mark = p;
    rv->magic = MARK_MAGIC;

#ifdef ARENA_DESTRUCTOR_LIST
    rv->prev_destructor = arena->last_destructor;
#endif /* ARENA_DESTRUCTOR_LIST */

    PR_Unlock(arena->lock);

    return rv;
}

/*
 * nss_arena_unmark_release
 *
 * This static routine implements the routines nssArena_Release
 * ans nssArena_Unmark, which are almost identical.
 */


static PRStatus
nss_arena_unmark_release(NSSArena *arena, nssArenaMark *arenaMark,
                         PRBool release)
{
    void *inner_mark;

#ifdef NSSDEBUG
    if (PR_SUCCESS != nssArena_verifyPointer(arena)) {
        return PR_FAILURE;
    }
#endif /* NSSDEBUG */

    if (MARK_MAGIC != arenaMark->magic) {
        nss_SetError(NSS_ERROR_INVALID_ARENA_MARK);
        return PR_FAILURE;
    }

    if ((PRLock *)NULL == arena->lock) {
        /* Just got destroyed */
        nss_SetError(NSS_ERROR_INVALID_ARENA);
        return PR_FAILURE;
    }
    PR_Lock(arena->lock);

#ifdef ARENA_THREADMARK
    if ((PRThread *)NULL != arena->marking_thread) {
        if (PR_GetCurrentThread() != arena->marking_thread) {
            PR_Unlock(arena->lock);
            nss_SetError(NSS_ERROR_ARENA_MARKED_BY_ANOTHER_THREAD);
            return PR_FAILURE;
        }
    }
#endif /* ARENA_THREADMARK */

    if (MARK_MAGIC != arenaMark->magic) {
        /* Just got released */
        PR_Unlock(arena->lock);
        nss_SetError(NSS_ERROR_INVALID_ARENA_MARK);
        return PR_FAILURE;
    }

    arenaMark->magic = 0;
    inner_mark = arenaMark->mark;

#ifdef ARENA_THREADMARK
    {
        nssArenaMark **pMark = &arena->first_mark;
        nssArenaMark *rest;
        nssArenaMark *last = (nssArenaMark *)NULL;

        /* Find this mark */
        while (*pMark != arenaMark) {
            last = *pMark;
            pMark = &(*pMark)->next;
        }

        /* Remember the pointer, then zero it */
        rest = (*pMark)->next;
        *pMark = (nssArenaMark *)NULL;

        arena->last_mark = last;

        /* Invalidate any later marks being implicitly released */
        for (; (nssArenaMark *)NULL != rest; rest = rest->next) {
            rest->magic = 0;
        }

        /* If we just got rid of the first mark, clear the thread ID */
        if ((nssArenaMark *)NULL == arena->first_mark) {
            arena->marking_thread = (PRThread *)NULL;
        }
    }
#endif /* ARENA_THREADMARK */

    if (release) {
#ifdef ARENA_DESTRUCTOR_LIST
        if ((struct arena_destructor_node *)NULL !=
            arenaMark->prev_destructor) {
            arenaMark->prev_destructor->next =
                (struct arena_destructor_node *)NULL;
        }
        arena->last_destructor = arenaMark->prev_destructor;

        /* Note that the arena is locked at this time */
        nss_arena_call_destructor_chain(arenaMark->next_destructor);
#endif /* ARENA_DESTRUCTOR_LIST */

        PL_ARENA_RELEASE(&arena->pool, inner_mark);
        /* No error return */
    }

    PR_Unlock(arena->lock);
    return PR_SUCCESS;
}

/*
 * nssArena_Release
 *
 * This routine invalidates and releases all memory allocated from
 * the specified arena after the point at which the specified mark
 * was obtained.  This routine returns a PRStatus value; if successful,
 * it will return PR_SUCCESS.  If unsuccessful, it will set an error
 * on the error stack and return PR_FAILURE.
 *
 * The error may be one of the following values:
 *  NSS_ERROR_INVALID_ARENA
 *  NSS_ERROR_INVALID_ARENA_MARK
 *  NSS_ERROR_ARENA_MARKED_BY_ANOTHER_THREAD
 *
 * Return value:
 *  PR_SUCCESS
 *  PR_FAILURE
 */


NSS_IMPLEMENT PRStatus
nssArena_Release(NSSArena *arena, nssArenaMark *arenaMark)
{
    return nss_arena_unmark_release(arena, arenaMark, PR_TRUE);
}

/*
 * nssArena_Unmark
 *
 * This routine "commits" the indicated mark and any marks after
 * it, making them unreleasable.  Note that any earlier marks can
 * still be released, and such a release will invalidate these
 * later unmarked regions.  If an arena is to be safely shared by
 * more than one thread, all marks must be either released or
 * unmarked.  This routine returns a PRStatus value; if successful,
 * it will return PR_SUCCESS.  If unsuccessful, it will set an error
 * on the error stack and return PR_FAILURE.
 *
 * The error may be one of the following values:
 *  NSS_ERROR_INVALID_ARENA
 *  NSS_ERROR_INVALID_ARENA_MARK
 *  NSS_ERROR_ARENA_MARKED_BY_ANOTHER_THREAD
 *
 * Return value:
 *  PR_SUCCESS
 *  PR_FAILURE
 */


NSS_IMPLEMENT PRStatus
nssArena_Unmark(NSSArena *arena, nssArenaMark *arenaMark)
{
    return nss_arena_unmark_release(arena, arenaMark, PR_FALSE);
}

/*
 * We prefix this header to all allocated blocks.  It is a multiple
 * of the alignment size.  Note that this usage of a header may make
 * purify spew bogus warnings about "potentially leaked blocks" of
 * memory; if that gets too annoying we can add in a pointer to the
 * header in the header itself.  There's not a lot of safety here;
 * maybe we should add a magic value?
 */

struct pointer_header {
    NSSArena *arena;
    PRUint32 size;
};

static void *
nss_zalloc_arena_locked(NSSArena *arena, PRUint32 size)
{
    void *p;
    void *rv;
    struct pointer_header *h;
    PRUint32 my_size = size + sizeof(struct pointer_header);
    PL_ARENA_ALLOCATE(p, &arena->pool, my_size);
    if ((void *)NULL == p) {
        nss_SetError(NSS_ERROR_NO_MEMORY);
        return (void *)NULL;
    }
    /*
     * Do this before we unlock.  This way if the user is using
     * an arena in one thread while destroying it in another, he'll
     * fault/FMR in his code, not ours.
     */

    h = (struct pointer_header *)p;
    h->arena = arena;
    h->size = size;
    rv = (void *)((char *)h + sizeof(struct pointer_header));
    (void)nsslibc_memset(rv, 0, size);
    return rv;
}

/*
 * NSS_ZAlloc
 *
 * This routine allocates and zeroes a section of memory of the
 * size, and returns to the caller a pointer to that memory.  If
 * the optional arena argument is non-null, the memory will be
 * obtained from that arena; otherwise, the memory will be obtained
 * from the heap.  This routine may return NULL upon error, in
 * which case it will have set an error upon the error stack.  The
 * value specified for size may be zero; in which case a valid
 * zero-length block of memory will be allocated.  This block may
 * be expanded by calling NSS_ZRealloc.
 *
 * The error may be one of the following values:
 *  NSS_ERROR_INVALID_ARENA
 *  NSS_ERROR_NO_MEMORY
 *  NSS_ERROR_ARENA_MARKED_BY_ANOTHER_THREAD
 *
 * Return value:
 *  NULL upon error
 *  A pointer to the new segment of zeroed memory
 */


NSS_IMPLEMENT void *
NSS_ZAlloc(NSSArena *arenaOpt, PRUint32 size)
{
    return nss_ZAlloc(arenaOpt, size);
}

/*
 * nss_ZAlloc
 *
 * This routine allocates and zeroes a section of memory of the
 * size, and returns to the caller a pointer to that memory.  If
 * the optional arena argument is non-null, the memory will be
 * obtained from that arena; otherwise, the memory will be obtained
 * from the heap.  This routine may return NULL upon error, in
 * which case it will have set an error upon the error stack.  The
 * value specified for size may be zero; in which case a valid
 * zero-length block of memory will be allocated.  This block may
 * be expanded by calling nss_ZRealloc.
 *
 * The error may be one of the following values:
 *  NSS_ERROR_INVALID_ARENA
 *  NSS_ERROR_NO_MEMORY
 *  NSS_ERROR_ARENA_MARKED_BY_ANOTHER_THREAD
 *
 * Return value:
 *  NULL upon error
 *  A pointer to the new segment of zeroed memory
 */


NSS_IMPLEMENT void *
nss_ZAlloc(NSSArena *arenaOpt, PRUint32 size)
{
    struct pointer_header *h;
    PRUint32 my_size = size + sizeof(struct pointer_header);

    if (my_size < sizeof(struct pointer_header)) {
        /* Wrapped */
        nss_SetError(NSS_ERROR_NO_MEMORY);
        return (void *)NULL;
    }

    if ((NSSArena *)NULL == arenaOpt) {
        /* Heap allocation, no locking required. */
        h = (struct pointer_header *)PR_Calloc(1, my_size);
        if ((struct pointer_header *)NULL == h) {
            nss_SetError(NSS_ERROR_NO_MEMORY);
            return (void *)NULL;
        }

        h->arena = (NSSArena *)NULL;
        h->size = size;
        /* We used calloc: it's already zeroed */

        return (void *)((char *)h + sizeof(struct pointer_header));
    } else {
        void *rv;
/* Arena allocation */
#ifdef NSSDEBUG
        if (PR_SUCCESS != nssArena_verifyPointer(arenaOpt)) {
            return (void *)NULL;
        }
#endif /* NSSDEBUG */

        if ((PRLock *)NULL == arenaOpt->lock) {
            /* Just got destroyed */
            nss_SetError(NSS_ERROR_INVALID_ARENA);
            return (void *)NULL;
        }
        PR_Lock(arenaOpt->lock);

#ifdef ARENA_THREADMARK
        if ((PRThread *)NULL != arenaOpt->marking_thread) {
            if (PR_GetCurrentThread() != arenaOpt->marking_thread) {
                nss_SetError(NSS_ERROR_ARENA_MARKED_BY_ANOTHER_THREAD);
                PR_Unlock(arenaOpt->lock);
                return (void *)NULL;
            }
        }
#endif /* ARENA_THREADMARK */

        rv = nss_zalloc_arena_locked(arenaOpt, size);

        PR_Unlock(arenaOpt->lock);
        return rv;
    }
    /*NOTREACHED*/
}

/*
 * NSS_ZFreeIf
 *
 * If the specified pointer is non-null, then the region of memory
 * to which it points -- which must have been allocated with
 * NSS_ZAlloc -- will be zeroed and released.  This routine
 * returns a PRStatus value; if successful, it will return PR_SUCCESS.
 * If unsuccessful, it will set an error on the error stack and return
 * PR_FAILURE.
 *
 * The error may be one of the following values:
 *  NSS_ERROR_INVALID_POINTER
 *
 * Return value:
 *  PR_SUCCESS
 *  PR_FAILURE
 */

NSS_IMPLEMENT PRStatus
NSS_ZFreeIf(void *pointer)
{
    return nss_ZFreeIf(pointer);
}

/*
 * nss_ZFreeIf
 *
 * If the specified pointer is non-null, then the region of memory
 * to which it points -- which must have been allocated with
 * nss_ZAlloc -- will be zeroed and released.  This routine
 * returns a PRStatus value; if successful, it will return PR_SUCCESS.
 * If unsuccessful, it will set an error on the error stack and return
 * PR_FAILURE.
 *
 * The error may be one of the following values:
 *  NSS_ERROR_INVALID_POINTER
 *
 * Return value:
 *  PR_SUCCESS
 *  PR_FAILURE
 */


NSS_IMPLEMENT PRStatus
nss_ZFreeIf(void *pointer)
{
    struct pointer_header *h;

    if ((void *)NULL == pointer) {
        return PR_SUCCESS;
    }

    h = (struct pointer_header *)((char *)pointer -
                                  sizeof(struct pointer_header));

    /* Check any magic here */

    if ((NSSArena *)NULL == h->arena) {
        /* Heap */
        (void)nsslibc_memset(pointer, 0, h->size);
        PR_Free(h);
        return PR_SUCCESS;
    } else {
/* Arena */
#ifdef NSSDEBUG
        if (PR_SUCCESS != nssArena_verifyPointer(h->arena)) {
            return PR_FAILURE;
        }
#endif /* NSSDEBUG */

        if ((PRLock *)NULL == h->arena->lock) {
            /* Just got destroyed.. so this pointer is invalid */
            nss_SetError(NSS_ERROR_INVALID_POINTER);
            return PR_FAILURE;
        }
        PR_Lock(h->arena->lock);

        (void)nsslibc_memset(pointer, 0, h->size);

        /* No way to "free" it within an NSPR arena. */

        PR_Unlock(h->arena->lock);
        return PR_SUCCESS;
    }
    /*NOTREACHED*/
}

/*
 * NSS_ZRealloc
 *
 * This routine reallocates a block of memory obtained by calling
 * nss_ZAlloc or nss_ZRealloc.  The portion of memory
 * between the new and old sizes -- which is either being newly
 * obtained or released -- is in either case zeroed.  This routine
 * may return NULL upon failure, in which case it will have placed
 * an error on the error stack.
 *
 * The error may be one of the following values:
 *  NSS_ERROR_INVALID_POINTER
 *  NSS_ERROR_NO_MEMORY
 *  NSS_ERROR_ARENA_MARKED_BY_ANOTHER_THREAD
 *
 * Return value:
 *  NULL upon error
 *  A pointer to the replacement segment of memory
 */


NSS_EXTERN void *
NSS_ZRealloc(void *pointer, PRUint32 newSize)
{
    return nss_ZRealloc(pointer, newSize);
}

/*
 * nss_ZRealloc
 *
 * This routine reallocates a block of memory obtained by calling
 * nss_ZAlloc or nss_ZRealloc.  The portion of memory
 * between the new and old sizes -- which is either being newly
 * obtained or released -- is in either case zeroed.  This routine
 * may return NULL upon failure, in which case it will have placed
 * an error on the error stack.
 *
 * The error may be one of the following values:
 *  NSS_ERROR_INVALID_POINTER
 *  NSS_ERROR_NO_MEMORY
 *  NSS_ERROR_ARENA_MARKED_BY_ANOTHER_THREAD
 *
 * Return value:
 *  NULL upon error
 *  A pointer to the replacement segment of memory
 */


NSS_EXTERN void *
nss_ZRealloc(void *pointer, PRUint32 newSize)
{
    NSSArena *arena;
    struct pointer_header *h, *new_h;
    PRUint32 my_newSize = newSize + sizeof(struct pointer_header);
    void *rv;

    if (my_newSize < sizeof(struct pointer_header)) {
        /* Wrapped */
        nss_SetError(NSS_ERROR_NO_MEMORY);
        return (void *)NULL;
    }

    if ((void *)NULL == pointer) {
        nss_SetError(NSS_ERROR_INVALID_POINTER);
        return (void *)NULL;
    }

    h = (struct pointer_header *)((char *)pointer -
                                  sizeof(struct pointer_header));

    /* Check any magic here */

    if (newSize == h->size) {
        /* saves thrashing */
        return pointer;
    }

    arena = h->arena;
    if (!arena) {
        /* Heap */
        new_h = (struct pointer_header *)PR_Calloc(1, my_newSize);
        if ((struct pointer_header *)NULL == new_h) {
            nss_SetError(NSS_ERROR_NO_MEMORY);
            return (void *)NULL;
        }

        new_h->arena = (NSSArena *)NULL;
        new_h->size = newSize;
        rv = (void *)((char *)new_h + sizeof(struct pointer_header));

        if (newSize > h->size) {
            (void)nsslibc_memcpy(rv, pointer, h->size);
            (void)nsslibc_memset(&((char *)rv)[h->size], 0,
                                 (newSize - h->size));
        } else {
            (void)nsslibc_memcpy(rv, pointer, newSize);
        }

        (void)nsslibc_memset(pointer, 0, h->size);
        h->size = 0;
        PR_Free(h);

        return rv;
    } else {
        void *p;
/* Arena */
#ifdef NSSDEBUG
        if (PR_SUCCESS != nssArena_verifyPointer(arena)) {
            return (void *)NULL;
        }
#endif /* NSSDEBUG */

        if (!arena->lock) {
            /* Just got destroyed.. so this pointer is invalid */
            nss_SetError(NSS_ERROR_INVALID_POINTER);
            return (void *)NULL;
        }
        PR_Lock(arena->lock);

#ifdef ARENA_THREADMARK
        if (arena->marking_thread) {
            if (PR_GetCurrentThread() != arena->marking_thread) {
                PR_Unlock(arena->lock);
                nss_SetError(NSS_ERROR_ARENA_MARKED_BY_ANOTHER_THREAD);
                return (void *)NULL;
            }
        }
#endif /* ARENA_THREADMARK */

        if (newSize < h->size) {
            /*
             * We have no general way of returning memory to the arena
             * (mark/release doesn't work because things may have been
             * allocated after this object), so the memory is gone
             * anyway.  We might as well just return the same pointer to
             * the user, saying "yeah, uh-hunh, you can only use less of
             * it now."  We'll zero the leftover part, of course.  And
             * in fact we might as well *not* adjust h->size-- this way,
             * if the user reallocs back up to something not greater than
             * the original size, then voila, there's the memory!  This
             * way a thrash big/small/big/small doesn't burn up the arena.
             */

            char *extra = &((char *)pointer)[newSize];
            (void)nsslibc_memset(extra, 0, (h->size - newSize));
            PR_Unlock(arena->lock);
            return pointer;
        }

        PL_ARENA_ALLOCATE(p, &arena->pool, my_newSize);
        if ((void *)NULL == p) {
            PR_Unlock(arena->lock);
            nss_SetError(NSS_ERROR_NO_MEMORY);
            return (void *)NULL;
        }

        new_h = (struct pointer_header *)p;
        new_h->arena = arena;
        new_h->size = newSize;
        rv = (void *)((char *)new_h + sizeof(struct pointer_header));
        if (rv != pointer) {
            (void)nsslibc_memcpy(rv, pointer, h->size);
            (void)nsslibc_memset(pointer, 0, h->size);
        }
        (void)nsslibc_memset(&((char *)rv)[h->size], 0, (newSize - h->size));
        h->arena = (NSSArena *)NULL;
        h->size = 0;
        PR_Unlock(arena->lock);
        return rv;
    }
    /*NOTREACHED*/
}

PRStatus
nssArena_Shutdown(void)
{
    PRStatus rv = PR_SUCCESS;
#ifdef DEBUG
    rv = nssPointerTracker_finalize(&arena_pointer_tracker);
#endif
    return rv;
}

Messung V0.5
C=93 H=97 G=94

¤ Dauer der Verarbeitung: 0.21 Sekunden  (vorverarbeitet)  ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

Beweissystem der NASA

Beweissystem Isabelle

NIST Cobol Testsuite

Cephes Mathematical Library

Wiener Entwicklungsmethode

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.