A zero-allocation C99 library for encoding a standard deck of playing cards into a compressed 39-byte binary format using 6-bit-per-card packing.
- Compact encoding — 52 cards in exactly 39 bytes (6 bits/card)
- Zero dynamic allocation — all structures fit on the stack
- Namespaced API — all symbols prefixed with
deck39_to avoid collisions - Joker support — optional 54-card mode (41 bytes packed)
- O(1) draw/peek/reset — stack-based deck with constant-time operations
- Fisher-Yates shuffle — cryptographically unbiased permutation in O(N)
make # Builds library, runs tests, runs example
make lib # Builds libdeck39.a (static) and libdeck39.so (shared) only
make test # Builds and runs the test suite
make clean # Removes all build artifacts#include "deck.h"
int main(void) {
Deck39_Deck deck;
/* Initialize a standard 52-card deck (no jokers) */
deck39_init(&deck, false);
/* Shuffle (caller must seed PRNG first: srand(time(NULL))) */
deck39_shuffle(&deck);
/* Draw the top card */
Deck39_Card card = deck39_draw(&deck);
/* Print it */
char buf[DECK39_CARD_STRING_SIZE];
deck39_card_to_string(card, buf, sizeof(buf));
/* Encode 52 cards into 39 bytes */
uint8_t packed[DECK39_PACKED_52];
deck39_encode(deck.cards, deck.size, packed);
/* Decode back */
Deck39_Card recovered[52];
deck39_decode(packed, 52, recovered);
return 0;
}Compile:
gcc -std=c99 -Iapp_build your_program.c libdeck39.a -o your_programEach card occupies exactly 6 bits within a uint8_t:
Byte layout: [ 0 0 | S S | R R R R ]
↑ ↑ ↑
unused Suit Rank
(7-6) (5-4) (3-0)
| Field | Bits | Range | Values |
|---|---|---|---|
| Suit | 5–4 | 0–3 | 0 = Clubs, 1 = Diamonds, 2 = Hearts, 3 = Spades |
| Rank | 3–0 | 1–13 | 1 = Ace, 2–10 = Pip, 11 = Jack, 12 = Queen, 13 = King |
| Joker | all | 0 | 0x00 = Joker sentinel (all 6 bits zero) |
Packing ratio: 4 cards × 6 bits = 24 bits = 3 bytes → 52 cards = 39 bytes.
| Type | Description |
|---|---|
Deck39_Card |
Single card — uint8_t data with 6-bit encoding |
Deck39_Deck |
Stack-based deck holding up to 54 cards |
Deck39_Suit |
Enum: DECK39_SUIT_CLUBS .. DECK39_SUIT_SPADES |
Deck39_Rank |
Enum: DECK39_RANK_ACE .. DECK39_RANK_KING |
| Macro | Value | Description |
|---|---|---|
DECK39_JOKER |
0 |
Sentinel value for joker cards |
DECK39_PACKED_52 |
39 |
Packed size for 52 cards |
DECK39_PACKED_54 |
41 |
Packed size for 54 cards |
DECK39_PACKED_SIZE(n) |
ceil(n×6/8) |
Packed size for arbitrary N |
DECK39_CARD_STRING_SIZE |
32 |
Minimum buffer for deck39_card_to_string |
| Function | Time | Description |
|---|---|---|
deck39_init(deck, with_jokers) |
O(N) | Initialize 52 or 54 cards |
deck39_shuffle(deck) |
O(N) | Fisher-Yates shuffle of undrawn cards |
deck39_draw(deck) |
O(1) | Draw and remove top card |
deck39_peek(deck) |
O(1) | View top card without removal |
deck39_cards_remaining(deck) |
O(1) | Count of undrawn cards |
deck39_is_empty(deck) |
O(1) | Check if all cards drawn |
deck39_reset(deck) |
O(1) | Reset draw pointer (preserves order) |
deck39_card_to_string(card, buf, size) |
O(1) | Format card as human-readable string |
deck39_encode(cards, n, packed) |
O(N) | Pack cards into 6-bit byte stream |
deck39_decode(packed, n, cards) |
O(N) | Unpack byte stream into cards |
| Operation | Time | Space | Notes |
|---|---|---|---|
| Init | O(N) | O(1) | N = 52 or 54; fills pre-allocated array |
| Shuffle | O(N) | O(1) | Fisher-Yates; single temp variable |
| Draw / Peek | O(1) | O(1) | Index increment only |
| Encode / Decode | O(N) | O(1) | Sequential bitwise pass; no allocation |
| Reset | O(1) | O(1) | Pointer reset |
libdeck39/
├── app_build/
│ ├── deck.h # Public API header (Source of Truth)
│ └── deck.c # Library implementation
├── tests/
│ └── test_deck.c # Assertion-based test suite
├── examples/
│ └── example_usage.c # Usage demonstration
├── documentation/
│ ├── Theory.md # Encoding theory and design rationale
│ ├── Algorithms.md # Algorithm analysis and complexity
│ └── Runtime.md # Build, execution, and integration guide
├── Makefile # Build system (static + shared lib)
└── README.md # This file
See LICENSE.