This grew out of a need to track sequential ctrl+shift+Left/Right-arrow
keypress events on a gtk editor. It is a small stack implementation for boolean values (for LEFT
, RIGHT
) that uses the bits of a predefined array of unsigned
for storage. The amount of storage is determined by the constant STKMAX
which simply specifies the number of unsigned
elements in the array. The reason for using this as opposed to some builtin GtkStack
, etc. was simply for storage efficiency where only 32+ bits were needed, compared to Gtk's traditional over-allocation for minimal data structures.
Here, I'm not that concerned with whether I used unsigned
where uint32_t
would be better, but more whether there is anything inherently wrong with packing bits and indexing the various array elements that make up the stack storage that I may be overlooking. I've thought through it and the logic and implementation seems sound -- but I could have easily missed something that will come back to bite me.
With that, here's the code and a short driver that intentionally attempts to add more than the stack will hold to validate stack-full behavior:
#include <stdio.h>
#ifndef CHAR_BIT
# define CHAR_BIT 8
#endif
enum { LEFT, RIGHT, STKMAX = 4 };
typedef struct {
unsigned int kp[STKMAX], idx; /* array for stack, bit-index */
} selstack;
/** zero the stack values */
void stack_clear (selstack *s)
{
s->idx = 0;
for (int i = 0; i < STKMAX; i++)
s->kp[i] = 0;
}
/** stack_push set bit at index to 0 or 1 based on v.
* increment bit-index after setting bit.
*/
int stack_push (selstack *s, int v)
{
/* total bits, bits per-array-element, array index */
unsigned arrbits = (sizeof s->kp) * CHAR_BIT,
elebits = (sizeof s->idx) * CHAR_BIT,
arridx = s->idx/elebits;
if (s->idx == arrbits) {
printf ("stack full, index: %u\n", s->idx);
return 0;
}
if (v) /* set bit at index */
s->kp[arridx] |= (1u << (s->idx % elebits));
else /* clear bit at index */
s->kp[arridx] &= ~(1u << (s->idx % elebits));
return ++s->idx;
}
/** stack_pop - decrement index, get bit at index, clear bit. */
int stack_pop (selstack *s)
{
if (!s->idx) {
printf ("stack empty, index: %u\n", s->idx);
return -1;
}
/* number of bits per-array-element, decrement index */
unsigned elebits = (sizeof s->idx) * CHAR_BIT,
arridx = --s->idx/elebits,
/* get bit value at index */
v = (s->kp[arridx] >> (s->idx % elebits)) & 1;
/* zero bit at index */
s->kp[arridx] &= ~(1u << (s->idx % elebits));
return v;
}
/** stack_last - get bit for last pushed value. */
int stack_last (selstack *s)
{
if (!s->idx) {
printf ("stack empty, index: %u\n", s->idx);
return -1;
}
/* number of bits per-array-element, computed index */
unsigned elebits = (sizeof s->idx) * CHAR_BIT,
arridx = (s->idx - 1)/elebits;
/* return bit at index */
return (s->kp[arridx] >> ((s->idx - 1) % elebits)) & 1;
}
int main (void) {
int v;
selstack s;
stack_clear (&s);
for (int i = 0; i < 132; i++)
if (i < 32 || (64 <= i && i < 96))
stack_push (&s, RIGHT);
else
stack_push (&s, LEFT);
while ((v = stack_pop (&s)) >= 0)
printf ("popped[%3u] : %s\n", s.idx, v ? "RIGHT" : "LEFT");
return 0;
}
Let me know if there are any gotchas I'm not picking up on.
unsigned
inkp[]
,1u
in1u <<
may need widening. Dirty trick:1u
-->(s->kp[0]&0 + 1)
to get the right width. \$\endgroup\$