v4k-git-backup/tools/labs/meta_dna.c

329 lines
10 KiB
C

// @todo fsave: fputs(DNA), then fwrite
// @todo fread: fgets(DNA), abort if DNA != read; then fread
#include <stdio.h>
// load/save whole struct acording to its DNA structure
int fload(FILE *infile, void *structure, const char *dna);
int fsave(FILE *outfile, const void *structure, const char *dna);
// set alignment for next fload/fsave call. resets back to 0 after every fload/fsave call.
// value: 0 (auto, default), 1,2,4,8,16 bytes [...]
void falign(unsigned alignment);
// override default DNA handlers and switch to a different schema and/or serialization format
typedef int (*size_operator)(char fmt, void *addr, int readmode);
typedef int (*call_operator)(void *out, void *addr, char fmt, int count, int readmode);
void foverride(size_operator size, call_operator call);
#if 0
Example Usage
-------------
struct fat_bootsector {
uint8_t jump_instruction[3];
uint8_t oem_name[8];
uint16_t bytes_per_sector;
uint8_t sectors_per_cluster;
uint16_t reserved_sectors;
uint8_t fat_copies;
uint16_t max_dirs;
uint16_t sector_count;
uint8_t media_descriptor;
uint16_t sectors_per_fat;
uint16_t sectors_per_head;
uint16_t heads;
uint32_t hidden_sectors;
uint32_t sector_count2;
} fat_struct;
Now we can read a binary image of the MBR into this structure:
mreadf(mbr, "i3c8chchchhchhhdd", &fat_struct);
Directives
----------
Supported directives:
regex meaning
(blank) ignored
b read / write uint8_t
w read / write uint16_t (uppercase: use vli encoding; as uint8? )
u read / write uint32_t (uppercase: use vli encoding; as uint8,16? )
q read / write uint64_t (uppercase: use vli encoding; as uint8,16,32?)
m read / write micro
h read / write half (uppercase: use smallest representation; as micro?)
f read / write float (uppercase: use smallest representation; as micro,half?)
d read / write double (uppercase: use smallest representation; as micro,half,float?)
s read / write string (uppercase: use smallest representation; quarks?)
[] read / write buffer
< switch to Intel (little endian) byte order
> switch to Motorola (big endian) byte order
( begin of tight group
) end of tight group
[0-9]+ next item repeated n times
z skip one byte of input / emit \0
* consume next structure item but do not read / write
#endif
// ----------------------------------------------------------------------------
#include <stdint.h>
#include <stdio.h>
#include <string.h>
#ifndef __thread
#define __thread __declspec(thread)
#endif
static __thread int pragma_pack_alignment = 0;
void falign(unsigned alignment) {
pragma_pack_alignment = alignment;
}
static
int size_fn(char fmt, uint8_t* addr8, int rd) {
if(addr8) {
// sizeof pointee data & align operator
/**/ if(fmt == 'c' || fmt == 'b') return 1;
else if(fmt == 'w' ) return 2;
else if(fmt == 'i' || fmt == 'u') return 4;
else if(fmt == 'l' || fmt == 'q') return 8;
else if(fmt == 'f' ) return 4;
else if(fmt == 'd' ) return 8;
else if(fmt == 's' ) return !*(char**)addr8 ? 0+1 : !rd ? strlen(*(char**)addr8) + 1 : strcspn(*(char**)addr8,"\x1") + 1;
return -1;
} else {
// sizeof member
/**/ if(fmt == 'w' ) return 2;
else if(fmt == 'i' || fmt == 'u') return 4;
else if(fmt == 'l' || fmt == 'q') return 8;
else if(fmt == 'f' ) return 4;
else if(fmt == 'd' ) return 8;
else if(fmt == 's' ) return sizeof(void*);
return 1;
}
}
static
int call_fn(void *out, uint8_t *addr8, char fmt, int count, int rd) { // rd/wr operator
FILE *fp = (FILE*)out;
int iterated_bytes = 0, processed_bytes = 0;
while( --count >= 0 ) {
int slot = size_fn(fmt, 0, rd);
int size = size_fn(fmt, addr8, rd);
if(rd)
switch (fmt) {
default: return -1;
break; case 'c': case 'b': fscanf(fp, "%c,", (uint8_t*)addr8);
break; case 'w': fscanf(fp, "%#04llx,", (uint16_t*)addr8);
break; case 'i': case 'u': fscanf(fp, "%#08llx,", (uint32_t*)addr8);
break; case 'l': case 'q': fscanf(fp, "%#16llx,", (uint64_t*)addr8);
break; case 'f': fscanf(fp, "%f,", (float*)addr8);
break; case 'd': fscanf(fp, "%llf,", (double*)addr8);
break; case 's': fscanf(fp, "%[^\x1],", (char*)addr8);
}
else
switch(fmt) {
default: return -1;
break; case 'c': case 'b': fprintf(fp, "%c,", (int)*(uint8_t*)addr8);
break; case 'w': fprintf(fp, "%#04llx,", (uint64_t)*(uint16_t*)addr8);
break; case 'i': case 'u': fprintf(fp, "%#08llx,", (uint64_t)*(uint32_t*)addr8);
break; case 'l': case 'q': fprintf(fp, "%#16llx,", (uint64_t)*(uint64_t*)addr8);
break; case 'f': fprintf(fp, "%f,", (float)*(float*)addr8);
break; case 'd': fprintf(fp, "%f,", (double)*(double*)addr8);
break; case 's': fprintf(fp, "%s\x1,", *(char**)addr8);
}
addr8 += slot;
iterated_bytes += slot;
processed_bytes += size;
}
return iterated_bytes;
}
static size_operator fsize_fn = size_fn;
static call_operator fcall_fn = call_fn;
void foverride(size_operator size, call_operator call) {
fsize_fn = size;
fcall_fn = call;
}
int fdump(FILE *out, void *addr, const char *dna, int rd) {
unsigned pragma_pack = pragma_pack_alignment; pragma_pack_alignment = 0; // reset alignment
uint8_t *addr8 = (uint8_t*)addr;
int size = 0, count = 1, skip = 0;
int last_type = 0;
int written = 0;
int align = 0;
for( int i = 0; dna[i]; ++i) {
char fmt = dna[i];
/**/ if(fmt <= 32) continue;
else if(fmt >= '0' && fmt <= '9') continue;
else if(fmt == 'z') skip = 1;
else {
// member alignment
if( last_type != fmt ) { // check if next struct member was found (may need re-alignment)
if( pragma_pack != 1 ) { // forced (>1) or auto-alignment (0)?
//printf("*%p ->", addr8);
align = pragma_pack == 0 ? fsize_fn(fmt, 0, rd) : pragma_pack;
// Round up to N-byte boundary
addr8 = (uint8_t*)(((uintptr_t)(addr8) + ((align) - 1)) & -(align));
//printf(" %p\n", addr8);
}
}
last_type = fmt;
size = fsize_fn(fmt, addr8, rd);
if( size < 0 ) {
fprintf(stderr, "parse error, unknown dna sequence '%c'!\n", fmt);
return -i-1;
}
}
if( skip ) { skip = 0; continue; }
char next = dna[i+1];
if( next >= '0' && next <= '9' ) {
count = next - '0';
}
int bytes = skip || count == 0 ? 0 : call_fn(out, addr8, dna[i], count, rd);
if( bytes < 0 ) {
fprintf(stderr, "stream fail. rc: %d\n", bytes);
return bytes;
}
written += bytes;
addr8 += bytes;
count = 1;
fprintf(out, "\n");
}
return written;
}
int fsave(FILE *out, const void *structure, const char *dna) {
return fdump(out, (void*)structure, dna, 0);
}
int fload(FILE *inf, void *structure, const char *dna) {
return fdump(inf, structure, dna, 1);
}
int fsavefile(const char *outfile, const void *structure, const char *dna) {
FILE *fp = fopen(outfile, "wb");
if( !fp ) return 0;
int bytes = fdump(fp, (void*)structure, dna, 0);
fclose(fp);
return bytes;
}
int floadfile(const char *infile, void *structure, const char *dna) {
FILE *fp = fopen(infile, "rb");
if( !fp ) return 0;
int bytes = fdump(fp, (void*)structure, dna, 1);
fclose(fp);
return bytes;
}
// ---
#include <assert.h>
// #pragma pack(1)
struct fat_mbr {
uint8_t jmp[3]; // b3
uint8_t oem[8]; // b8
const char* str; // s
uint16_t bytes_per_sector; // w
uint8_t sectors_per_cluster; // b
uint16_t reserved_sectors; // w
uint8_t fat_copies; // b
uint16_t max_dirs; // w
uint16_t sector_count; // w
uint8_t media_descriptor; // b
uint16_t sectors_per_fat; // w
uint16_t sectors_per_head; // w
uint16_t heads; // w
uint32_t hidden_sectors; // u
uint32_t sector_count2; // u
float pi;
char break_alignment; // b
double phi;
};
#define FAT_MBR_DNA "b3b8s wb wb w wb www uu fbd"
// #pragma pack(pop)
int main() {
// foverride(size_fn, write_fn);
struct fat_mbr mbr = {
{'a','b','c'},
{'d','e','f','g','h','i','j','k'},
"hello 'escaped' \"world\"",
0x100,'l',
0x101,'m',
0x102,
0x103,'n',
0x104,
0x105,
0x106,
0x01234567,
0x89abcdef,
3.14159f,'o',
1.6069,
};
// fdump(stdout, &mbr, FAT_MBR_DNA); exit(0);
// printf("%p\n", &mbr.jmp);
// printf("%p\n", &mbr.oem);
// printf("%p\n", &mbr.str);
// printf("%p\n", &mbr.sector_count);
// falign(0);
int bytes = fsave(stdout, &mbr, FAT_MBR_DNA);
printf("%d bytes written\n", bytes);
typedef struct entitystate {
struct pos {
short trTime;
float trBase[3];
} pos;
} entitystate;
entitystate e = { 123, 3.14159f, 4.14159f, 5.14159f };
bytes = fsave(stdout, &e, "wfff");
printf("%d bytes written\n", bytes);
// exit(0);
struct fat_mbr zero = {0};
struct fat_mbr src = mbr;
struct fat_mbr dst = zero;
dst = src;
assert(0 == memcmp(&src,&dst,sizeof(struct fat_mbr)));
dst = zero;
assert(0 != memcmp(&src,&dst,sizeof(struct fat_mbr)));
int sbytes = fsavefile(".temp", &src, FAT_MBR_DNA);
int lbytes = floadfile(".temp", &dst, FAT_MBR_DNA);
assert( sbytes == lbytes );
assert(0 != memcmp(&src,&dst,sizeof(struct fat_mbr)));
assert(!puts("Ok"));
}