Skip to content

Instantly share code, notes, and snippets.

@th-in-gs
Created May 28, 2023 02:17
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save th-in-gs/7f2104440aa02dd36264ed6bc38ce553 to your computer and use it in GitHub Desktop.
Save th-in-gs/7f2104440aa02dd36264ed6bc38ce553 to your computer and use it in GitHub Desktop.
Packed PROGMEM Strings
#include "packedStrings.h"
const uint8_t str7CharAtIndex(const uint8_t *data, const uint8_t index) {
const size_t bitPosition = index * 7;
const uint8_t startByte = bitPosition / 8;
const uint8_t startBit = bitPosition % 8;
uint8_t ch = pgm_read_byte(&data[startByte]) << startBit;
if(startBit > (8-7)) {
ch |= pgm_read_byte(&data[startByte + 1]) >> (8 - startBit);
}
return ch >> 1;
}
const uint8_t str6CharAtIndex(const uint8_t *data, const uint8_t index) {
const size_t bitPosition = index * 6;
const uint8_t startByte = bitPosition >>3;
const uint8_t startBit = bitPosition % 8;
uint8_t ch = pgm_read_byte(&data[startByte]) << startBit;
if(startBit > (8-6)) {
ch |= pgm_read_byte(&data[startByte + 1]) >> (8 - startBit);
}
ch = 0x20 + (ch >> 2);
if(ch == '^') {
ch = '\n';
}
if(ch == '!') {
ch = '|';
}
if(ch == ';') {
ch = '\0';
}
return ch;
}
#include <stdio.h>
#include <stdint.h>
#include <ctype.h>
#include <string.h>
#include <math.h>
#include <avr/pgmspace.h>
// Define a 6 or 7 bit string like this:
// uint8_t *sixBitString = STR6("6 bit string");
// uint8_t *sevenBitString = STR7("7 bit string");
//
// Access characters from them like this:
//
// str6CharAtIndex(sixBitString, 3) // returns 'B' (not 'b' because there's only uppercase in the six bit character set).
// str7CharAtIndex(sixBitString, 7) // returns 'b'.
//
// They are still NULL_terminated after unpacking.
//
// I didn't need a whole-string-unpacking function (str6cpy(char *unpacked, uint8_t packed)?), so they're an exercise fo the reader.
//
#define STR7(str) (__extension__({ static PROGMEM const PackedStringConstexpr<7, sizeof(str)> __c = PackedStringConstexpr<7, sizeof(str)>(str); __c.data; }))
#define STR6(str) (__extension__({ static PROGMEM const PackedStringConstexpr<6, sizeof(str)> __c = PackedStringConstexpr<6, sizeof(str)>(str); __c.data; }))
const uint8_t str7CharAtIndex(const uint8_t *data, const uint8_t index);
const uint8_t str6CharAtIndex(const uint8_t *data, const uint8_t index);
template <size_t bitsPerChar, size_t length>
struct PackedStringConstexpr {
uint8_t data[((length * bitsPerChar) / 8) + (((length * bitsPerChar) % 8) ? 1 : 0)];
constexpr PackedStringConstexpr(const char (&string)[length]) : data {}
{
size_t outBytePosition = 0;
size_t outBitPosition = 0;
for(size_t inBytePosition = 0; inBytePosition < length; ++inBytePosition) {
uint8_t ch = (uint8_t)string[inBytePosition];
if(bitsPerChar == 6) {
// Custom encoding scheme for a 64-element subset of the ASCII
// set.
if(ch >= 'a' && ch <= 'z') {
ch = (ch - 'a') + 'A';
}
bool wasZero = ch == '\0';
bool wasNewline = ch == '\n';
bool wasPipe = ch == '|';
if(ch < 0x20 || ch > 0x5d) {
ch = '?';
}
if(wasNewline) {
ch = '^';
}
if(wasPipe) {
ch = '!';
}
if(wasZero) {
ch = ';';
}
ch = ch - 0x20;
ch = ch << 2;
} else {
ch = ch << 1;
}
data[outBytePosition] |= ch >> outBitPosition;
outBitPosition += bitsPerChar;
if(outBitPosition >= 8) {
++outBytePosition;
outBitPosition -= 8;
if(outBitPosition > 0) {
data[outBytePosition] |= ch << (bitsPerChar - outBitPosition);
}
}
}
}
};
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment