static void int_thread_del_item(const ERR_STATE *d)
	{
	ERR_STATE *p;
	LHASH *hash;

	err_fns_check();
	hash = ERRFN(thread_get)(0);
	if (!hash)
		return;

	CRYPTO_w_lock(CRYPTO_LOCK_ERR);
	p = (ERR_STATE *)lh_delete(hash, d);
	/* make sure we don't leak memory */
	if (int_thread_hash_references == 1
		&& int_thread_hash && (lh_num_items(int_thread_hash) == 0))
		{
		lh_free(int_thread_hash);
		int_thread_hash = NULL;
		}
	CRYPTO_w_unlock(CRYPTO_LOCK_ERR);

	ERRFN(thread_release)(&hash);
	if (p)
		ERR_STATE_free(p);
	}
void ERR_load_strings(const ERR_STRING_DATA *str) {
  err_fns_check();

  while (str->error) {
    ERRFN(set_item)(str);
    str++;
  }
}
const char *ERR_lib_error_string(unsigned long e)
	{
	ERR_STRING_DATA d,*p;
	unsigned long l;

	err_fns_check();
	l=ERR_GET_LIB(e);
	d.error=ERR_PACK(l,0,0);
	p=ERRFN(err_get_item)(&d);
	return((p == NULL)?NULL:p->string);
	}
/* err_component_error_string returns the error string associated with
 * |packed_error|, which must be of a special form matching the keys inserted
 * into the error hash table. */
static const char *err_component_error_string(uint32_t packed_error) {
  ERR_STRING_DATA *p;

  err_fns_check();
  p = ERRFN(get_item)(packed_error);

  if (p == NULL) {
    return NULL;
  }
  return p->string;
}
void ERR_load_ERR_strings(void)
	{
	err_fns_check();
#ifndef OPENSSL_NO_ERR
	err_load_strings(0,ERR_str_libraries);
	err_load_strings(0,ERR_str_reasons);
	err_load_strings(ERR_LIB_SYS,ERR_str_functs);
	build_SYS_str_reasons();
	err_load_strings(ERR_LIB_SYS,SYS_str_reasons);
#endif
	}
void ERR_remove_state(unsigned long pid)
	{
	ERR_STATE tmp;

	err_fns_check();
	if (pid == 0)
		pid=(unsigned long)CRYPTO_thread_id();
	tmp.pid=pid;
	/* thread_del_item automatically destroys the LHASH if the number of
	 * items reaches zero. */
	ERRFN(thread_del_item)(&tmp);
	}
static void err_load_strings(void) {
  unsigned i, j = 0;

  err_fns_check();

  /* This loop loads strings for the libraries for the ERR_R_*_LIB
   * reasons. */
  for (i = ERR_LIB_NONE; i < ERR_NUM_LIBS; i++) {
    ERR_STRING_DATA *data = &kStaticErrors[j++];
    data->string = kLibraryNames[i];
    data->error = ERR_PACK(i, 0, 0);
    ERRFN(set_item)(data);

    data = &kStaticErrors[j++];
    data->string = kLibraryNames[i];
    data->error = ERR_PACK(0, 0, i);
    ERRFN(set_item)(data);
  }

  for (i = 1; i < 1 + NUM_SYS_ERRNOS; i++) {
    /* The "SYS" library sets errno values as the reason for its errors.
     * Thus we load the first |NUM_SYS_ERRNOS| errno strings as the
     * reason strings for that library. */

    ERR_STRING_DATA *data = &kStaticErrors[j++];
    data->string = strerror(i);
    data->error = ERR_PACK(ERR_LIB_SYS, 0, i);
    ERRFN(set_item)(data);
  }

  ERR_load_strings(kGlobalErrors);

  ERR_load_strings(ASN1_error_string_data);
  ERR_load_strings(BIO_error_string_data);
  ERR_load_strings(BN_error_string_data);
  ERR_load_strings(BUF_error_string_data);
  ERR_load_strings(CIPHER_error_string_data);
  ERR_load_strings(CONF_error_string_data);
  ERR_load_strings(CRYPTO_error_string_data);
  ERR_load_strings(DH_error_string_data);
  ERR_load_strings(DIGEST_error_string_data);
  ERR_load_strings(DSA_error_string_data);
  ERR_load_strings(ECDH_error_string_data);
  ERR_load_strings(ECDSA_error_string_data);
  ERR_load_strings(EC_error_string_data);
  ERR_load_strings(EVP_error_string_data);
  ERR_load_strings(OBJ_error_string_data);
  ERR_load_strings(PEM_error_string_data);
  ERR_load_strings(RSA_error_string_data);
  ERR_load_strings(X509V3_error_string_data);
  ERR_load_strings(X509_error_string_data);
}
static ERR_STRING_DATA *int_err_del_item(ERR_STRING_DATA *d)
	{
	ERR_STRING_DATA *p;
	LHASH *hash;

	err_fns_check();
	hash = ERRFN(err_get)(0);
	if (!hash)
		return NULL;

	CRYPTO_w_lock(CRYPTO_LOCK_ERR);
	p = (ERR_STRING_DATA *)lh_delete(hash, d);
	CRYPTO_w_unlock(CRYPTO_LOCK_ERR);

	return p;
	}
static ERR_STATE *int_thread_get_item(const ERR_STATE *d)
	{
	ERR_STATE *p;
	LHASH *hash;

	err_fns_check();
	hash = ERRFN(thread_get)(0);
	if (!hash)
		return NULL;

	CRYPTO_r_lock(CRYPTO_LOCK_ERR);
	p = (ERR_STATE *)lh_retrieve(hash, d);
	CRYPTO_r_unlock(CRYPTO_LOCK_ERR);

	ERRFN(thread_release)(&hash);
	return p;
	}
const char *ERR_reason_error_string(unsigned long e)
	{
	ERR_STRING_DATA d,*p=NULL;
	unsigned long l,r;

	err_fns_check();
	l=ERR_GET_LIB(e);
	r=ERR_GET_REASON(e);
	d.error=ERR_PACK(l,0,r);
	p=ERRFN(err_get_item)(&d);
	if (!p)
		{
		d.error=ERR_PACK(0,0,r);
		p=ERRFN(err_get_item)(&d);
		}
	return((p == NULL)?NULL:p->string);
	}
static ERR_STATE *int_thread_set_item(ERR_STATE *d)
	{
	ERR_STATE *p;
	LHASH *hash;

	err_fns_check();
	hash = ERRFN(thread_get)(1);
	if (!hash)
		return NULL;

	CRYPTO_w_lock(CRYPTO_LOCK_ERR);
	p = (ERR_STATE *)lh_insert(hash, d);
	CRYPTO_w_unlock(CRYPTO_LOCK_ERR);

	ERRFN(thread_release)(&hash);
	return p;
	}
void ERR_remove_thread_state(const CRYPTO_THREADID *tid) {
  CRYPTO_THREADID current;
  ERR_STATE *state;
  unsigned i;

  if (tid == NULL) {
    CRYPTO_THREADID_current(&current);
    tid = &current;
  }

  err_fns_check();
  state = ERRFN(release_state)(tid);
  if (state == NULL) {
    return;
  }

  for (i = 0; i < ERR_NUM_ERRORS; i++) {
    err_clear(&state->errors[i]);
  }

  OPENSSL_free(state);
}
ERR_STATE *ERR_get_state(void)
	{
	static ERR_STATE fallback;
	ERR_STATE *ret,tmp,*tmpp=NULL;
	int i;
	unsigned long pid;

	err_fns_check();
	pid=(unsigned long)CRYPTO_thread_id();
	tmp.pid=pid;
	ret=ERRFN(thread_get_item)(&tmp);

	/* ret == the error state, if NULL, make a new one */
	if (ret == NULL)
		{
		ret=(ERR_STATE *)OPENSSL_malloc(sizeof(ERR_STATE));
		if (ret == NULL) return(&fallback);
		ret->pid=pid;
		ret->top=0;
		ret->bottom=0;
		for (i=0; i<ERR_NUM_ERRORS; i++)
			{
			ret->err_data[i]=NULL;
			ret->err_data_flags[i]=0;
			}
		tmpp = ERRFN(thread_set_item)(ret);
		/* To check if insertion failed, do a get. */
		if (ERRFN(thread_get_item)(ret) != ret)
			{
			ERR_STATE_free(ret); /* could not insert it */
			return(&fallback);
			}
		/* If a race occured in this function and we came second, tmpp
		 * is the first one that we just replaced. */
		if (tmpp)
			ERR_STATE_free(tmpp);
		}
	return ret;
	}
/* err_get_state gets the ERR_STATE object for the current thread. */
static ERR_STATE *err_get_state(void) {
  err_fns_check();
  return ERRFN(get_state)();
}
int ERR_get_next_error_library(void)
	{
	err_fns_check();
	return ERRFN(get_next_lib)();
	}
void ERR_free_strings() {
  err_fns_check();
  ERRFN(shutdown)();
}
const ERR_FNS *ERR_get_implementation(void)
	{
	err_fns_check();
	return err_fns;
	}
void ERR_release_err_state_table(LHASH **hash)
	{
	err_fns_check();
	ERRFN(thread_release)(hash);
	}
LHASH *ERR_get_err_state_table(void)
	{
	err_fns_check();
	return ERRFN(thread_get)(0);
	}
LHASH *ERR_get_string_table(void)
	{
	err_fns_check();
	return ERRFN(err_get)(0);
	}
void ERR_free_strings(void)
	{
	err_fns_check();
	ERRFN(err_del)();
	}