The Perl Toolchain Summit needs more sponsors. If your company depends on Perl, please support this very important event.
#include "EXTERN.h"
#include "perl.h"
#include "XSUB.h"
/*#include "INLINE.h"*/

#define STATES_INITIAL_SIZE 10

struct _iterator_state {
    I32  riter;
    HE*  eiter;
};
typedef struct _iterator_state iterator_state;

static int module_initialized = 0;
iterator_state **STATES;
int STATES_size;

void initialize()
{
    int i;
    if (module_initialized) return;
    STATES = malloc(STATES_INITIAL_SIZE*sizeof(iterator_state *));
    STATES_size = STATES_INITIAL_SIZE;
    for (i=0; i<STATES_size; i++) {
	STATES[i] = (iterator_state*) 0;
    }
    module_initialized = 1;
}

void resize_STATES()
{
    int i;
    int new_size = STATES_size * 2;
    iterator_state **new_STATES = malloc(new_size*sizeof(iterator_state*));
    for (i=0; i<STATES_size; i++) {
	new_STATES[i] = STATES[i];
    }
    for (; i<new_size; i++) {
	new_STATES[i] = (iterator_state*) 0;
    }
    free(STATES);
    STATES = new_STATES;
    STATES_size = new_size;
}

int save_iterator_state(HV* hv)
{
    int i;
    iterator_state *state = malloc(sizeof(iterator_state));
    initialize();
    if (hv == (HV*) 0) {
	/* warn */
	return -1;
    }

    for (i=0; i<STATES_size; i++) {
	if (STATES[i] == (iterator_state*) 0) {
	    break;
	}
    }
    if (i >= STATES_size) {
	resize_STATES();
	i = STATES_size;
    }

    state->riter = HvRITER(hv);
    state->eiter = HvEITER(hv);
    STATES[i] = state;
    hv_iterinit(hv);
    return i;
}

void restore_iterator_state(HV* hv, int i)
{
    iterator_state *state = STATES[i];
    initialize();
    if (i < 0 || i >= STATES_size) {
	/* warn */
	return;
    }
    if (state != (iterator_state*) 0) {
	HvRITER(hv) = state->riter;
	HvEITER(hv) = state->eiter;
	free(state);
    } else {
	/* warn */
    }
    STATES[i] = (iterator_state*) 0;
}


MODULE = Hash::SafeKeys		PACKAGE = Hash::SafeKeys	

int
save_iterator_state (hv)
	HV *	hv

void
restore_iterator_state (hv, i)
	HV *	hv
	int	i
	PREINIT:
	I32* temp;
	PPCODE:
	temp = PL_markstack_ptr++;
	restore_iterator_state(hv, i);
	if (PL_markstack_ptr != temp) {
          /* truly void, because dXSARGS not invoked */
	  PL_markstack_ptr = temp;
	  XSRETURN_EMPTY; /* return empty stack */
        }
        /* must have used dXSARGS; list context implied */
	return; /* assume stack size is correct */