/*
 * hash - one-way hash routines
 *
 * Copyright (C) 1999-2007,2021-2023,2026  Landon Curt Noll
 *
 * Calc is open software; you can redistribute it and/or modify it under
 * the terms of the version 2.1 of the GNU Lesser General Public License
 * as published by the Free Software Foundation.
 *
 * Calc 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 Lesser General
 * Public License for more details.
 *
 * A copy of version 2.1 of the GNU Lesser General Public License is
 * distributed with calc under the filename COPYING-LGPL.  You should have
 * received a copy with calc; if not, write to Free Software Foundation, Inc.
 * 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
 *
 * Under source code control:   1995/11/23 05:13:11
 * File existed as early as:    1995
 *
 * chongo <was here> /\oo/\     http://www.isthe.com/chongo/
 * Share and enjoy!  :-)        http://www.isthe.com/chongo/tech/comp/calc/
 */

/*
 * important <system> header includes
 */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <stdint.h>
#include <stdbool.h>

/*
 * calc local src includes
 */
#include "value.h"
#include "calc.h"
#include "attribute.h"
#include "errtbl.h"

#include "banned.h" /* include after all other includes */

/*
 * external hash_setup functions
 */
extern void shs_init_state(HASH *);
extern void sha1_init_state(HASH *);
extern void MD5_init_state(HASH *);

/*
 * hash_long can deal with bool's, int's, FLAGS's and LEN's
 */
#define hash_bool(type, val, state) (hash_long((type), (long)(val), (state)))
#define hash_int(type, val, state) (hash_long((type), (long)(val), (state)))
#define hash_flag(type, val, state) (hash_long((type), (long)(val), (state)))
#define hash_len(type, val, state) (hash_long((type), (long)(val), (state)))

/*
 * hash_setup - setup the hash state for a given hash
 */
static struct hash_setup {
    int type;                   /* hash type (see XYZ_HASH_TYPE below) */
    void (*init_state)(HASH *); /* initialize a hash state */
} htbl[] = {
    {SHA1_HASH_TYPE, sha1_init_state}, /* SHA-1 / SHA-1 */
    {-1, NULL}                         /* must be last */
};

/*
 * hash_init - initialize a hash state
 *
 * given:
 *      type    - hash type (see hash.h)
 *      state   - the state to initialize, or NULL to malloc it
 *
 * returns:
 *      initialized state
 */
HASH *
hash_init(int type, HASH *state)
{
    int i;

    /*
     * malloc if needed
     */
    if (state == NULL) {
        state = (HASH *)calloc(1, sizeof(HASH));
        if (state == NULL) {
            math_error("hash_init: cannot malloc HASH");
            not_reached();
        }
    }

    /*
     * clear hash value
     */
    memset((void *)state, 0, sizeof(HASH));
    state->bytes = true;

    /*
     * search for the hash_setup function
     */
    for (i = 0; htbl[i].init_state != NULL; ++i) {

        /* if we found the state that we were looking for */
        if (type == htbl[i].type) {

            /* initialize state and return */
            (htbl[i].init_state)(state);

            /* firewall - MAX_CHUNKSIZE must be >= chunksize */
            if (state->chunksize > MAX_CHUNKSIZE) {
                math_error("internal error: MAX_CHUNKSIZE is too small");
                not_reached();
            }
            return state;
        }
    }

    /*
     * no such hash state
     */
    math_error("internal error: hash type not found in htbl[]");
    return NULL;
}

/*
 * hash_free - free the hash state
 */
void
hash_free(HASH *state)
{
    /*
     * do nothing if state is NULL
     */
    if (state == NULL) {
        return;
    }

    /*
     * free main state and return
     */
    free(state);
    return;
}

/*
 * hash_copy - copy a hash state
 *
 * given:
 *      state   - the state to copy
 *
 * returns:
 *      pointer to copy of state
 */
HASH *
hash_copy(HASH *state)
{
    HASH *hnew; /* copy of state */

    /*
     * malloc new state
     */
    hnew = (HASH *)calloc(1, sizeof(HASH));
    if (hnew == NULL) {
        math_error("hash_init: cannot malloc HASH");
        not_reached();
    }

    /*
     * duplicate state
     */
    memcpy((void *)hnew, (void *)state, sizeof(HASH));
    return hnew;
}

/*
 * hash_cmp - compare hash values
 *
 * given:
 *      a       first hash state
 *      b       second hash state
 *
 * returns:
 *      true => hash states are different
 *      false => hash states are the same
 */
int
hash_cmp(HASH *a, HASH *b)
{
    /*
     * firewall and quick check
     */
    if (a == b) {
        /* pointers to the same object */
        return false;
    }
    if (a == NULL || b == NULL) {
        /* one pointer is NULL, so they differ */
        return true;
    }
    if (a->cmp == NULL || b->cmp == NULL) {
        /* one cmp function is NULL, so they differ */
        return true;
    }

    /*
     * compare hash types
     */
    if (a->hashtype != b->hashtype) {
        /* different hash types are different */
        return true;
    }

    /*
     * perform the hash specific comparison
     */
    return ((a->cmp)(a, b));
}

/*
 * hash_print - print the name and value of a hash
 *
 * given:
 *      state   the hash state to print name and value of
 */
void
hash_print(HASH *state)
{
    /* print the hash */
    (state->print)(state);
    return;
}

/*
 * hash_final - finalize the state of a hash and return a ZVALUE
 *
 * given:
 *      state   the hash state to finalize
 *
 * returns:
 *      hash state as a ZVALUE
 */
ZVALUE
hash_final(HASH *state)
{
    /* return the finalized the hash value */
    return (state->final)(state);
}

/*
 * hash_long - note a long value
 *
 * given:
 *      type    - hash type (see hash.h)
 *      longval - a long value
 *      state   - the state to hash
 *
 * returns:
 *      the new state
 *
 * This function will hash a long value as if it were a 64 bit value.
 * The input is a long.  If a long is smaller than 64 bits, we will
 * hash a final 32 bits of zeros.
 *
 * This function is OK to hash bool's, unsigned long's, unsigned int's
 * signed int's as well as FLAG's and LEN's.
 */
HASH *
hash_long(int type, long longval, HASH *state)
{
    long lval[64 / LONG_BITS]; /* 64 bits of longs */

    /*
     * initialize if state is NULL
     */
    if (state == NULL) {
        state = hash_init(type, NULL);
    }

    /*
     * setup for the hash_long
     */
    (state->chkpt)(state);
    state->bytes = false; /* data to be read as words */

    /*
     * catch the zero numeric value special case
     */
    if (longval == 0) {
        /* note a zero numeric value and return */
        (state->note)(HASH_ZERO(state->base), state);
        return state;
    }

    /*
     * prep for a long value hash
     */
    (state->note)(state->base, state);

    /*
     * hash as if we have a 64 bit value
     */
    memset((char *)lval, 0, sizeof(lval));
    lval[0] = longval;
    (state->update)(state, (uint8_t *)lval, sizeof(lval));

    /*
     * all done
     */
    return state;
}

/*
 * hash_zvalue - hash a ZVALUE
 *
 * given:
 *      type    - hash type (see hash.h)
 *      zval    - the ZVALUE
 *      state   - the state to hash or NULL
 *
 * returns:
 *      the new state
 */
HASH *
hash_zvalue(int type, ZVALUE zval, HASH *state)
{

#if CALC_BYTE_ORDER == BIG_ENDIAN && BASEB == 16
    int full_lim;  /* HALFs in whole chunks in zval */
    int chunkhalf; /* size of half buffer in HALFs */
    int i;
    int j;
#endif
#if BASEB == 16
    HALF half[MAX_CHUNKSIZE]; /* For endian reversal */
#endif

    /*
     * initialize if state is NULL
     */
    if (state == NULL) {
        state = hash_init(type, NULL);
    }

    /*
     * setup for the ZVALUE hash
     */
    (state->chkpt)(state);
    state->bytes = false; /* data to be read as words */

    /*
     * catch the zero numeric value special case
     */
    if (ziszero(zval)) {
        /* note a zero numeric value and return */
        (state->note)(HASH_ZERO(state->base), state);
        return state;
    }

    /*
     * prep for a ZVALUE hash
     */
    (state->note)(HASH_ZVALUE(state->base), state);
    /* note if we have a negative value */
    if (zisneg(zval)) {
        (state->note)(HASH_NEG(state->base), state);
    }

#if CALC_BYTE_ORDER == BIG_ENDIAN && BASEB == 16

    /*
     * hash full chunks
     *
     * We need to convert the array of HALFs into canonical architectural
     * independent form -- 32 bit arrays.  Because we have 16 bit values
     * in Big Endian form, we need to swap 16 bit values so that they
     * appear as 32 bit Big Endian values.
     */
    chunkhalf = state->chunksize / sizeof(HALF);
    full_lim = (zval.len / chunkhalf) * chunkhalf;
    for (i = 0; i < full_lim; i += chunkhalf) {
        /* HALF swap copy a chunk into a data buffer */
        for (j = 0; j < chunkhalf; j += 2) {
            half[j] = zval.v[i + j + 1];
            half[j + 1] = zval.v[i + j];
        }
        (state->update)(state, (uint8_t *)half, state->chunksize);
    }

    /*
     * hash the final partial chunk (if any)
     *
     * We need to convert the array of HALFs into canonical architectural
     * independent form -- 32 bit arrays.  Because we have 16 bit values
     * in Big Endian form, we need to swap 16 bit values so that they
     * appear as 32 bit Big Endian values.
     */
    if (zval.len > full_lim) {
        for (j = 0; j < zval.len - full_lim - 1; j += 2) {
            half[j] = zval.v[full_lim + j + 1];
            half[j + 1] = zval.v[full_lim + j];
        }
        if (j < zval.len - full_lim) {
            half[j] = (HALF)0;
            half[j + 1] = zval.v[zval.len - 1];
            --full_lim;
        }
        (state->update)(state, (uint8_t *)half, (zval.len - full_lim) * sizeof(HALF));
    }

#else

    /*
     * hash the array of HALFs
     *
     * The array of HALFs is equivalent to the canonical architectural
     * independent form.  We either have 32 bit HALFs (in which case
     * we do not case the byte order) or we have 16 bit HALFs in Little
     * Endian order (which happens to be laid out in the same order as
     * 32 bit values).
     */
    (state->update)(state, (uint8_t *)zval.v, zval.len * sizeof(HALF));

#  if BASEB == 16
    if (zval.len & 1) { /* padding to complete word */
        half[0] = 0;
        (state->update)(state, (uint8_t *)half, 2);
    }
#  endif

#endif

    /*
     * all done
     */
    return state;
}

/*
 * hash_number - hash a NUMBER
 *
 * given:
 *      type    - hash type (see hash.h)
 *      n       - the NUMBER
 *      state   - the state to hash or NULL
 *
 * returns:
 *      the new state
 */
HASH *
hash_number(int type, void *n, HASH *state)
{
    NUMBER *number = (NUMBER *)n; /* n as a NUMBER pointer */
    bool sign;                    /* sign of the denominator */

    /*
     * initialize if state is NULL
     */
    if (state == NULL) {
        state = hash_init(type, NULL);
    }

    /*
     * setup for the NUMBER hash
     */
    (state->chkpt)(state);
    state->bytes = false;

    /*
     * process the numerator
     */
    state = hash_zvalue(type, number->num, state);

    /*
     * if the NUMBER is not an integer, process the denominator
     */
    if (qisfrac(number)) {

        /* note the division */
        (state->note)(HASH_DIV(state->base), state);

        /* hash denominator as positive -- just in case */
        sign = number->den.sign;
        number->den.sign = 0;

        /* hash the denominator */
        state = hash_zvalue(type, number->den, state);

        /* restore the sign */
        number->den.sign = sign;
    }

    /*
     * all done
     */
    return state;
}

/*
 * hash_complex - hash a COMPLEX
 *
 * given:
 *      type    - hash type (see hash.h)
 *      c       - the COMPLEX
 *      state   - the state to hash or NULL
 *
 * returns:
 *      the new state
 */
HASH *
hash_complex(int type, void *c, HASH *state)
{
    COMPLEX *complex = (COMPLEX *)c; /* c as a COMPLEX pointer */

    /*
     * initialize if state is NULL
     */
    if (state == NULL) {
        state = hash_init(type, NULL);
    }

    /*
     * setup for the COMPLEX hash
     */
    (state->chkpt)(state);
    state->bytes = false;

    /*
     * catch the zero special case
     */
    if (ciszero(complex)) {
        /* note a zero numeric value and return */
        (state->note)(HASH_ZERO(state->base), state);
        return state;
    }

    /*
     * process the real value if not pure imaginary
     *
     * We will ignore the real part if the value is of the form 0+xi.
     */
    if (!qiszero(complex->real)) {
        state = hash_number(type, complex->real, state);
    }

    /*
     * if the NUMBER is not real, process the imaginary value
     *
     * We will ignore the imaginary part of the value is of the form x+0i.
     */
    if (!cisreal(complex)) {

        /* note the sqrt(-1) */
        (state->note)(HASH_COMPLEX(state->base), state);

        /* hash the imaginary value */
        state = hash_number(type, complex->imag, state);
    }

    /*
     * all done
     */
    return state;
}

/*
 * hash_str - hash a null-terminated string
 *
 * given:
 *      type    - hash type (see hash.h)
 *      str     - the string
 *      state   - the state to hash or NULL
 *
 * returns:
 *      the new state
 */
HASH *
hash_str(int type, char *str, HASH *state)
{
    size_t len; /* string length */

    /*
     * initialize if state is NULL
     */
    if (state == NULL) {
        state = hash_init(type, NULL);
    }

    /*
     * setup for the string hash
     */
    if (!state->bytes) {
        (state->chkpt)(state);
        state->bytes = true;
    }

    len = strlen(str);

    /*
     * hash the string
     */
    (state->update)(state, (uint8_t *)str, len);

    /*
     * all done
     */
    return state;
}

/*
 * hash_STR - hash a STRING
 *
 * given:
 *      type    - hash type (see hash.h)
 *      str     - the STRING
 *      state   - the state to hash or NULL
 *
 * returns:
 *      the new state
 */
HASH *
hash_STR(int type, STRING *str, HASH *state)
{
    /*
     * initialize if state is NULL
     */
    if (state == NULL) {
        state = hash_init(type, NULL);
    }

    /*
     * setup for the string hash
     */
    if (!state->bytes) {
        (state->chkpt)(state);
        state->bytes = true;
    }

    /*
     * hash the string
     */
    (state->update)(state, (uint8_t *)str->s_str, (uint32_t)str->s_len);

    /*
     * all done
     */
    return state;
}

/*
 * hash_usb8 - hash an array of uint8_ts
 *
 * given:
 *      type    - hash type (see hash.h)
 *      byte    - pointer to an array of uint8_ts
 *      len     - number of uint8_ts to hash
 *      state   - the state to hash or NULL
 *
 * returns:
 *      the new state
 */
HASH *
hash_usb8(int type, uint8_t *byte, int len, HASH *state)
{
    /*
     * initialize if state is NULL
     */
    if (state == NULL) {
        state = hash_init(type, NULL);
    }

    /*
     * setup for the string hash
     */
    if (!state->bytes) {
        (state->chkpt)(state);
        state->bytes = true;
    }

    /*
     * hash the array of octets
     */
    (state->update)(state, byte, (uint32_t)len);

    /*
     * all done
     */
    return state;
}

/*
 * hash_value - hash a value
 *
 * given:
 *      type    - hash type (see hash.h)
 *      v       - the value
 *      state   - the state to hash or NULL
 *
 * returns:
 *      the new state
 */
HASH *
hash_value(int type, void *v, HASH *state)
{
    LISTELEM *ep;              /* list element pointer */
    ASSOCELEM **assochead;     /* association chain head */
    ASSOCELEM *aep;            /* current association value */
    ASSOCELEM *nextaep;        /* next association value */
    VALUE *value = (VALUE *)v; /* v cast to a VALUE */
    VALUE *vp;                 /* pointer to next OBJ table value */
    ZVALUE fileval;            /* size, position, dev, inode of a file */
    int i;

    /*
     * initialize if state is NULL
     */
    if (state == NULL) {
        state = hash_init(type, NULL);
    }

    /*
     * process the value type
     */
    switch (value->v_type) {
    case V_NULL:
        (state->chkpt)(state);
        state->bytes = true;
        break;

    case V_INT:
        /* setup for the this value type */
        (state->chkpt)(state);
        (state->type)(value->v_type, state);

        /* hash as if we have a 64 bit value */
        state = hash_int(type, value->v_int, state);
        break;

    case V_NUM:
        /* hash this type */
        state = hash_number(type, value->v_num, state);
        break;

    case V_COM:
        /* setup for the this value type */
        (state->chkpt)(state);
        (state->type)(value->v_type, state);

        /* hash this type */
        state = hash_complex(type, value->v_com, state);
        break;

    case V_ADDR:
        /* there is nothing to setup, simply hash what we point at */
        state = hash_value(type, value->v_addr, state);
        break;

    case V_STR:
        /* strings have no setup */

        /* hash this type */
        state = hash_STR(type, value->v_str, state);
        break;

    case V_MAT:
        /* setup for the this value type */
        (state->chkpt)(state);
        (state->type)(value->v_type, state);
        state->bytes = true;

        /* hash all the elements of the matrix */
        for (i = 0; i < value->v_mat->m_size; ++i) {

            /* hash the next matrix value */
            state = hash_value(type, value->v_mat->m_table + i, state);
            state->bytes = false; /* as if reading words */
        }
        break;

    case V_LIST:
        /* setup for the this value type */
        (state->chkpt)(state);
        (state->type)(value->v_type, state);

        /* hash all the elements of the list */
        for (i = 0, ep = value->v_list->l_first; ep != NULL && i < value->v_list->l_count; ++i, ep = ep->e_next) {

            /* hash the next list value */
            state = hash_value(type, &ep->e_value, state);
            state->bytes = false; /* as if reading words */
        }
        break;

    case V_ASSOC:
        /* setup for the this value type */
        (state->chkpt)(state);
        (state->type)(value->v_type, state);
        state->bytes = true;

        /* hash the association */
        assochead = value->v_assoc->a_table;
        for (i = 0; i < value->v_assoc->a_size; i++) {
            nextaep = *assochead;
            while (nextaep) {
                aep = nextaep;
                nextaep = aep->e_next;

                /* hash the next association value */
                state = hash_value(type, &aep->e_value, state);
                state->bytes = false; /* as if reading words */
            }
            assochead++;
        }
        break;

    case V_OBJ:
        /* setup for the this value type */
        (state->chkpt)(state);
        (state->type)(value->v_type, state);
        state->bytes = true; /* reading bytes */

        /* hash the object name and then the element values */

        state = hash_str(type, objtypename(value->v_obj->o_actions->oa_index), state);
        (state->chkpt)(state);

        for (i = value->v_obj->o_actions->oa_count, vp = value->v_obj->o_table; i-- > 0; vp++) {

            /* hash the next object value */
            state = hash_value(type, vp, state);
            state->bytes = false; /* as if reading words */
        }
        break;

    case V_FILE:
        /* setup for the this value type */
        (state->chkpt)(state);
        (state->type)(value->v_type, state);

        /* hash file length if possible */
        if (getsize(value->v_file, &fileval) == 0) {
            state = hash_zvalue(type, fileval, state);
            zfree(fileval);
        } else {
            /* hash -1 for invalid length */
            state = hash_long(type, (long)-1, state);
        }
        /* hash the file position if possible */
        if (getloc(value->v_file, &fileval) == 0) {
            state = hash_zvalue(type, fileval, state);
            zfree(fileval);
        } else {
            /* hash -1 for invalid location */
            state = hash_long(type, (long)-1, state);
        }
        /* hash the file device if possible */
        if (get_device(value->v_file, &fileval) == 0) {
            state = hash_zvalue(type, fileval, state);
            zfree(fileval);
        } else {
            /* hash -1 for invalid device */
            state = hash_long(type, (long)-1, state);
        }
        /* hash the file inode if possible */
        if (get_inode(value->v_file, &fileval) == 0) {
            state = hash_zvalue(type, fileval, state);
            zfree(fileval);
        } else {
            /* hash -1 for invalid inode */
            state = hash_long(type, (long)-1, state);
        }
        break;

    case V_RAND:
        /* setup for the this value type */
        (state->chkpt)(state);
        (state->type)(value->v_type, state);

        /* hash the RAND state */
        state = hash_int(type, value->v_rand->seeded, state);
        state = hash_int(type, value->v_rand->bits, state);
        (state->update)(state, (uint8_t *)value->v_rand->buffer, SLEN * FULL_BITS / 8);
        state = hash_int(type, value->v_rand->j, state);
        state = hash_int(type, value->v_rand->k, state);
        state = hash_int(type, value->v_rand->need_to_skip, state);
        (state->update)(state, (uint8_t *)value->v_rand->slot, SCNT * FULL_BITS / 8);
        (state->update)(state, (uint8_t *)value->v_rand->shuf, SHUFLEN * FULL_BITS / 8);
        state->bytes = false; /* as if reading words */
        break;

    case V_RANDOM:
        /* setup for the this value type */
        (state->chkpt)(state);
        (state->type)(value->v_type, state);

        /* hash the RANDOM state */
        state = hash_int(type, value->v_random->seeded, state);
        state = hash_int(type, value->v_random->bits, state);
        (state->update)(state, (uint8_t *)&(value->v_random->buffer), BASEB / 8);
        state = hash_zvalue(type, value->v_random->r, state);
        state = hash_zvalue(type, value->v_random->n, state);
        state->bytes = false; /* as if reading words */
        break;

    case V_CONFIG:
        /* setup for the this value type */
        (state->chkpt)(state);
        (state->type)(value->v_type, state);

        /* hash the CONFIG state */
        state = hash_int(type, value->v_config->outmode, state);
        state = hash_int(type, value->v_config->outmode2, state);
        state = hash_long(type, (long)value->v_config->outdigits, state);
        state = hash_number(type, value->v_config->epsilon, state);
        state = hash_long(type, (long)value->v_config->epsilonprec, state);
        state = hash_flag(type, value->v_config->traceflags, state);
        state = hash_long(type, (long)value->v_config->maxprint, state);
        state = hash_len(type, value->v_config->mul2, state);
        state = hash_len(type, value->v_config->sq2, state);
        state = hash_len(type, value->v_config->pow2, state);
        state = hash_len(type, value->v_config->redc2, state);
        state = hash_bool(type, value->v_config->tilde_ok, state);
        state = hash_bool(type, value->v_config->tilde_space, state);
        state = hash_bool(type, value->v_config->tab_ok, state);
        state = hash_long(type, (long)value->v_config->quomod, state);
        state = hash_long(type, (long)value->v_config->quo, state);
        state = hash_long(type, (long)value->v_config->mod, state);
        state = hash_long(type, (long)value->v_config->sqrt, state);
        state = hash_long(type, (long)value->v_config->appr, state);
        state = hash_long(type, (long)value->v_config->cfappr, state);
        state = hash_long(type, (long)value->v_config->cfsim, state);
        state = hash_long(type, (long)value->v_config->outround, state);
        state = hash_long(type, (long)value->v_config->round, state);
        state = hash_long(type, (long)value->v_config->triground, state);
        state = hash_bool(type, value->v_config->leadzero, state);
        state = hash_bool(type, value->v_config->fullzero, state);
        state = hash_long(type, (long)value->v_config->maxscancount, state);
        state = hash_str(type, value->v_config->prompt1, state);
        state->bytes = false; /* as if just read words */
        state = hash_str(type, value->v_config->prompt2, state);
        state->bytes = false; /* as if just read words */
        state = hash_int(type, value->v_config->blkmaxprint, state);
        state = hash_bool(type, value->v_config->blkverbose, state);
        state = hash_int(type, value->v_config->blkbase, state);
        state = hash_int(type, value->v_config->blkfmt, state);
        state = hash_long(type, (long)value->v_config->resource_debug, state);
        state = hash_long(type, (long)value->v_config->calc_debug, state);
        state = hash_long(type, (long)value->v_config->user_debug, state);
        state = hash_bool(type, value->v_config->verbose_quit, state);
        state = hash_int(type, value->v_config->ctrl_d, state);
        state = hash_str(type, value->v_config->program, state);
        state = hash_str(type, value->v_config->base_name, state);
        state = hash_bool(type, value->v_config->windows, state);
        state = hash_bool(type, value->v_config->cygwin, state);
        state = hash_bool(type, value->v_config->compile_custom, state);
        if (value->v_config->allow_custom != NULL && *(value->v_config->allow_custom)) {
            state = hash_bool(type, true, state);
        } else {
            state = hash_bool(type, false, state);
        }
        state = hash_str(type, value->v_config->version, state);
        state = hash_int(type, value->v_config->baseb, state);
        state = hash_bool(type, value->v_config->redecl_warn, state);
        state = hash_bool(type, value->v_config->dupvar_warn, state);
        state = hash_bool(type, value->v_config->fraction_space, state);
        break;

    case V_HASH:
        /* setup for the this value type */
        (state->chkpt)(state);
        (state->type)(value->v_type, state);

        /* hash the HASH state */
        state = hash_int(type, value->v_hash->type, state);
        state = hash_bool(type, value->v_hash->bytes, state);
        state = hash_int(type, value->v_hash->base, state);
        state = hash_int(type, value->v_hash->chunksize, state);
        state = hash_int(type, value->v_hash->unionsize, state);
        (state->update)(state, value->v_hash->h_union.data, state->unionsize);
        state->bytes = false; /* as if reading words */
        break;

    case V_BLOCK:
        /* there is no setup for a BLOCK */

        /* hash the octets in the BLOCK */
        if (value->v_block->datalen > 0) {
            state = hash_usb8(type, value->v_block->data, value->v_block->datalen, state);
        }
        break;

    case V_OCTET:
        /* there is no setup for an OCTET */

        /* hash the OCTET */
        state = hash_usb8(type, value->v_octet, 1, state);
        break;

    case V_NBLOCK:
        /* there is no setup for a NBLOCK */

        /* hash the octets in the NBLOCK */
        if (value->v_nblock->blk->datalen > 0) {
            state = hash_usb8(type, value->v_nblock->blk->data, value->v_nblock->blk->datalen, state);
        }
        break;

    default:
        math_error("hashing an unknown value");
        not_reached();
    }
    return state;
}
