X-Git-Url: https://git.distorted.org.uk/u/mdw/catacomb/blobdiff_plain/ba6e6b64033b1f9de49feccb5c9cd438354481f7..0f00dc4c8eb47e67bc0f148c2dd109f73a451e0a:/math/mp-mem.c diff --git a/math/mp-mem.c b/math/mp-mem.c new file mode 100644 index 0000000..e1840ee --- /dev/null +++ b/math/mp-mem.c @@ -0,0 +1,334 @@ +/* -*-c-*- + * + * Memory management for multiprecision numbers + * + * (c) 1999 Straylight/Edgeware + */ + +/*----- Licensing notice --------------------------------------------------* + * + * This file is part of Catacomb. + * + * Catacomb is free software; you can redistribute it and/or modify + * it under the terms of the GNU Library General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * Catacomb is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with Catacomb; if not, write to the Free + * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, + * MA 02111-1307, USA. + */ + +/*----- Header files ------------------------------------------------------*/ + +#include + + +#include "mp.h" + +/*----- Main code ---------------------------------------------------------*/ + +/* --- @mp_new@ --- * + * + * Arguments: @size_t sz@ = size of vector required + * @unsigned f@ = flags to set + * + * Returns: Pointer to a new MP structure. + * + * Use: Allocates a new multiprecision integer. The data space is + * allocated from either the standard global or secret arena, + * depending on the initial flags requested. + */ + +mp *mp_new(size_t sz, unsigned f) +{ + mp *m = CREATE(mp); + m->a = (f & MP_BURN) ? MPARENA_SECURE : MPARENA_GLOBAL; + m->v = mpalloc(m->a, sz); + m->vl = m->v + sz; + m->sz = sz; + m->f = f & ~(MP_CONST | MP_DESTROYED); + m->ref = 1; + return (m); +} + +/* --- @mp_create@ --- * + * + * Arguments: @size_t sz@ = size of vector required + * + * Returns: Pointer to pristine new MP structure with enough memory + * bolted onto it. + * + * Use: Creates a new multiprecision integer, initially zero. The + * integer has a single reference. + */ + +mp *mp_create(size_t sz) +{ + mp *m = CREATE(mp); + m->v = mpalloc(MPARENA_GLOBAL, sz); + m->vl = m->v + sz; + m->sz = sz; + m->a = MPARENA_GLOBAL; + m->f = MP_UNDEF; + m->ref = 1; + return (m); +} + +/* --- @mp_createsecure@ --- * + * + * Arguments: @size_t sz@ = size of vector required + * + * Returns: Pointer to pristine new MP structure with enough memory + * bolted onto it. + * + * Use: Creates a new multiprecision integer with indeterminate + * contents. The integer has a single reference. The integer's + * data space is allocated from the secure arena. Its burn flag + * is set. + */ + +mp *mp_createsecure(size_t sz) +{ + mp *m = CREATE(mp); + m->v = mpalloc(MPARENA_SECURE, sz); + m->vl = m->v + sz; + m->sz = sz; + m->a = MPARENA_SECURE; + m->f = MP_UNDEF | MP_BURN; + m->ref = 1; + return (m); +} + +/* --- @mp_build@ --- * + * + * Arguments: @mp *m@ = pointer to an MP block to fill in + * @mpw *v@ = pointer to a word array + * @mpw *vl@ = pointer just past end of array + * + * Returns: --- + * + * Use: Creates a multiprecision integer representing some smallish + * number. You must provide storage for the number and dispose + * of it when you've finished with it. The number is marked as + * constant while it exists. + */ + +void mp_build(mp *m, mpw *v, mpw *vl) +{ + m->v = v; + m->vl = vl; + m->sz = vl - v; + m->a = MPARENA_GLOBAL; + m->f = MP_CONST; + m->ref = 1; +} + +/* --- @mp_destroy@ --- * + * + * Arguments: @mp *m@ = pointer to a multiprecision integer + * + * Returns: --- + * + * Use: Destroys a multiprecision integer. The reference count isn't + * checked. Don't use this function if you don't know what + * you're doing: use @mp_drop@ instead. + */ + +void mp_destroy(mp *m) +{ + assert(((void)"Destroying a free integer", !(m->f & MP_DESTROYED))); + assert(((void)"Attempted to destroy a constant", !(m->f & MP_CONST))); + if (m->f & MP_BURN) + memset(m->v, 0, MPWS(m->sz)); + mpfree(m->a, m->v); + m->f |= MP_DESTROYED; + DESTROY(m); +} + +/* --- @mp_copy@ --- * + * + * Arguments: @mp *m@ = pointer to a multiprecision integer + * + * Returns: A copy of the given multiprecision integer. + * + * Use: Copies the given integer. In fact you just get another + * reference to the same old one again. + */ + +mp *mp_copy(mp *m) { return MP_COPY(m); } + +/* --- @mp_drop@ --- * + * + * Arguments: @mp *m@ = pointer to a multiprecision integer + * + * Returns: --- + * + * Use: Drops a reference to an integer which isn't wanted any more. + * If there are no more references, the integer is destroyed. + */ + +void mp_drop(mp *m) { if (m) MP_DROP(m); } + +/* --- @mp_split@ --- * + * + * Arguments: @mp *m@ = pointer to a multiprecision integer + * + * Returns: A reference to the same integer, possibly with a different + * address. + * + * Use: Splits off a modifiable version of the integer referred to. + */ + +mp *mp_split(mp *m) { MP_SPLIT(m); return (m); } + +/* --- @mp_resize@ --- * + * + * Arguments: @mp *m@ = pointer to a multiprecision integer + * @size_t sz@ = new size + * + * Returns: --- + * + * Use: Changes an integer's size. The length and value are not + * changed. It is an error to + */ + +void mp_resize(mp *m, size_t sz) { MP_RESIZE(m, sz); } + +/* --- @mp_ensure@ --- * + * + * Arguments: @mp *m@ = pointer to a multiprecision integer + * @size_t sz@ = required length + * + * Returns: --- + * + * Use: Changes an integer's length. If there is not enough space + * allocated for the new length then the size is increased. It + */ + +void mp_ensure(mp *m, size_t sz) { MP_ENSURE(m, sz); } + +/* --- @mp_dest@ --- * + * + * Arguments: @mp *m@ = a suggested destination integer + * @size_t sz@ = size required for result, in digits + * @unsigned f@ = various flags + * + * Returns: A pointer to an appropriate destination. + * + * Use: Converts a suggested destination into a real destination with + * the required properties. If the real destination is @d@, + * then the following properties will hold: + * + * * @d@ will have exactly one reference. + * + * * If @m@ is not @MP_NEW@, then the contents of @m@ will not + * change, unless @f@ has the @MP_UNDEF@ flag set. + * + * * If @m@ is not @MP_NEW@, then he reference count of @m@ on + * entry is equal to the sum of the counts of @d@ and @m@ on + * exit. + * + * * The size of @d@ will be at least @sz@. + * + * * If @f@ has the @MP_BURN@ flag set, then @d@ will be + * allocated from @MPARENA_SECURE@. + * + * Understanding this function is crucial to using Catacomb's + * multiprecision integer library effectively. + */ + +mp *mp_dest(mp *m, size_t sz, unsigned f) +{ + /* --- If no destination, make one --- */ + + if (m == MP_NEWSEC) + m = mp_new(sz, f | MP_UNDEF | MP_BURN); + else if (m == MP_NEW) + m = mp_new(sz, f | MP_UNDEF); + else { + size_t len = MP_LEN(m); + unsigned undef = (m->f | f) & MP_UNDEF; + + /* --- If the value must be preserved, the block can't shrink --- */ + + if (!undef && sz < len) + sz = len; + + /* --- Otherwise check whether the destination is suitable --- */ + + if (m->ref > 1 || (m->f & MP_CONST) || + sz > m->sz || ((f & ~m->f) & MP_BURN)) { + + /* --- No -- allocate a new buffer --- * + * + * The buffer must be secure if (a) the caller requested a secure + * buffer, or (b) the old buffer is secure and I'm not allowed to + * discard the old contents. + */ + + mparena *a; + mpw *v; + + if ((f & MP_BURN) || (!undef && (m->f & MP_BURN))) + a = MPARENA_SECURE; + else + a = MPARENA_GLOBAL; + v = mpalloc(a, sz); + + /* --- Copy the data over --- */ + + if (!undef) { + memcpy(v, m->v, MPWS(len)); + if (sz - len > 0) + memset(v + len, 0, MPWS(sz - len)); + } + + /* --- If @m@ has other references, make a new node --- * + * + * Otherwise dispose of the old buffer. + */ + + if (!(m->f & MP_CONST) && m->ref == 1) { + if (m->f & MP_BURN) + memset(m->v, 0, MPWS(m->sz)); + mpfree(m->a, m->v); + } else { + mp *mm = CREATE(mp); + mm->ref = 1; + mm->f = m->f; + m->ref--; + m = mm; + } + + /* --- Fix up the node --- */ + + m->v = v; + m->vl = v + sz; + m->sz = sz; + m->f = ((m->f & ~(MP_CONST | MP_BURN)) | + (f & (MP_BURN | MP_UNDEF))); + m->a = a; + } + + /* --- If the number is growing in its buffer, fix it up --- */ + + else if (sz > len) { + if (!undef) + memset(m->vl, 0, MPWS(sz - len)); + m->vl = m->v + sz; + } + } + + /* --- Done --- */ + + return (m); +} + +/*----- That's all, folks -------------------------------------------------*/