Compare commits

..

9 Commits

Author SHA1 Message Date
Mid
e0716abc81 Handle indefinite elements 2025-10-07 12:27:21 +03:00
Mid
8b0fbfdcf7 Create Makefile.. finally 2025-10-07 11:00:39 +03:00
Mid
75c0ac1997 Bugfix 2025-10-07 10:57:38 +03:00
Mid
cca8f02870 Bug in README.md 2025-09-27 20:56:40 +03:00
Mid
d0ccaff719 C++-friendly headers 2025-09-27 20:56:05 +03:00
Mid
75d2efbe4c Bug fixes 2025-03-30 23:20:47 +03:00
Mid
629a8fdf55 Forgot something 2025-01-04 10:53:47 +02:00
Mid
fb97037243 Writer example 2025-01-04 10:45:25 +02:00
Mid
1476dbdd57 Complete writer interface 2025-01-04 10:44:33 +02:00
7 changed files with 159 additions and 22 deletions

7
Makefile Normal file
View File

@@ -0,0 +1,7 @@
libeebie.so:
$(CC) -shared -g -O2 -o libeebie.so eebie/reader.c eebie/writer.c
install:
install -d /usr/local/include/eebie
install eebie/*.h /usr/local/include/eebie
install libeebie.so /usr/local/lib

View File

@@ -4,6 +4,8 @@ Eebie is a very small C99 library for reading and writing EBML files, namely Mat
The library is meant to be embedded directly into another project. The library is meant to be embedded directly into another project.
TODO: signed integer writing
## Reading example ## Reading example
#include"eebie/reader.h" #include"eebie/reader.h"
@@ -39,7 +41,7 @@ The library is meant to be embedded directly into another project.
} }
static void on_exit_element(EBMLReader *reader) { static void on_exit_element(EBMLReader *reader) {
// If needed, the current element ID can be found at `reader->inside.id`. // If needed, the exitee element ID can be found at `reader->idStack[reader->currentDepth - 1]`.
// Use accumulated buffer here. // Use accumulated buffer here.
@@ -97,7 +99,45 @@ The library is meant to be embedded directly into another project.
## Writing example ## Writing example
TBA #include"eebie/writer.h"
#include<stdio.h>
#include<stdlib.h>
static void *on_alloc(EBMLWriter *w, void *p, size_t sz) {
return realloc(p, sz);
}
static size_t on_write(EBMLWriter *w, const void *p, size_t sz) {
FILE *fp = w->ud;
return fwrite(p, 1, sz, fp);
}
int main() {
EBMLWriter w;
ebml_writer_init(&w);
FILE *output = fopen("test.ebml", "wb");
w.ud = output;
w.eventWrite = on_write;
w.eventAlloc = on_alloc;
ebml_writer_push(&w, 0x1A45DFA3);
ebml_writer_put(&w, 0x4286, EBML_UNSIGNED_INTEGER, (EBMLPrimitive) {.uInt = 1});
ebml_writer_put(&w, 0x4282, EBML_STRING, (EBMLPrimitive) {.string = "excrement"});
ebml_writer_pop(&w);
// Unknown-sized element must not have corresponding pop
ebml_writer_push_ind(&w, 0x18538067);
ebml_writer_put(&w, 0x4123, EBML_STRING, (EBMLPrimitive) {.string = "yodel"});
while(!ebml_writer_flush(&w));
fclose(output);
}
## `schemagen` ## `schemagen`

View File

@@ -2,6 +2,7 @@
#define EEBIE_H #define EEBIE_H
#include<stdint.h> #include<stdint.h>
#include<stddef.h>
typedef enum EBMLElementType { typedef enum EBMLElementType {
EBML_SIGNED_INTEGER, EBML_SIGNED_INTEGER,
@@ -15,13 +16,18 @@ typedef enum EBMLElementType {
EBML_BINARY, EBML_BINARY,
} EBMLElementType; } EBMLElementType;
typedef struct EBMLBinary {
uint8_t *ptr;
size_t length;
} EBMLBinary;
typedef union EBMLPrimitive { typedef union EBMLPrimitive {
int64_t sInt; int64_t sInt;
uint64_t uInt; uint64_t uInt;
float flt4; float flt4;
double flt8; double flt8;
const char *string; const char *string;
const uint8_t *binary; const EBMLBinary binary;
uint64_t date; uint64_t date;
} EBMLPrimitive; } EBMLPrimitive;

View File

@@ -9,6 +9,8 @@
#include"reader.h" #include"reader.h"
#define is_max(u) ((u) == 0xFFFFFFFFFFFFFFFFUL)
void ebml_reader_init(EBMLReader *this) { void ebml_reader_init(EBMLReader *this) {
memset(this, 0, sizeof(*this)); memset(this, 0, sizeof(*this));
this->state = EBMLRS_WAITING_FOR_ELEMENT_ID; this->state = EBMLRS_WAITING_FOR_ELEMENT_ID;
@@ -89,6 +91,10 @@ static int get_varint(const uint8_t *data, size_t length, uint64_t *result) {
if(ret >= 0) { if(ret >= 0) {
*result &= ~VARINT_MASKS[ret]; *result &= ~VARINT_MASKS[ret];
if(*result == (1UL << (ret * 7)) - 1) {
*result = 0xFFFFFFFFFFFFFFFFUL;
}
} }
return ret; return ret;
@@ -137,7 +143,7 @@ int ebml_reader_feed(EBMLReader *this, const uint8_t *data, size_t length) {
} }
this->idStack[this->currentDepth] = this->inside.id; this->idStack[this->currentDepth] = this->inside.id;
this->stack[this->currentDepth] = elLength + status; this->stack[this->currentDepth] = is_max(elLength) ? elLength : elLength + status;
this->currentDepth++; this->currentDepth++;
@@ -156,7 +162,9 @@ int ebml_reader_feed(EBMLReader *this, const uint8_t *data, size_t length) {
} }
for(int i = 0; i < this->currentDepth; i++) { for(int i = 0; i < this->currentDepth; i++) {
this->stack[i] -= eaten; if(!is_max(this->stack[i])) {
this->stack[i] -= eaten;
}
} }
while(this->currentDepth > 0 && this->stack[this->currentDepth - 1] == 0) { while(this->currentDepth > 0 && this->stack[this->currentDepth - 1] == 0) {
@@ -173,3 +181,12 @@ int ebml_reader_feed(EBMLReader *this, const uint8_t *data, size_t length) {
return eaten; return eaten;
} }
void ebml_reader_leave(EBMLReader *this, uint64_t id) {
for(int i = 0; i < this->currentDepth; i++) {
if(this->idStack[i] == id) {
this->currentDepth = i;
return;
}
}
}

View File

@@ -5,6 +5,10 @@
#include"ebml.h" #include"ebml.h"
#ifdef __cplusplus
extern "C" {
#endif
#include<stddef.h> #include<stddef.h>
#include<stdint.h> #include<stdint.h>
@@ -42,7 +46,16 @@ typedef struct EBMLReader {
void *ud; void *ud;
} EBMLReader; } EBMLReader;
void ebml_reader_init(EBMLReader *this); void ebml_reader_init(EBMLReader*);
int ebml_reader_feed(EBMLReader *this, const uint8_t *data, size_t length); int ebml_reader_feed(EBMLReader*, const uint8_t *data, size_t length);
// Because Eebie is schemaless, it must be told when we have left an
// indefinite element, otherwise the stack may grow indefinitely and
// even overflow. The user may call this from eventEnterElement.
void ebml_reader_leave(EBMLReader*, uint64_t id);
#ifdef __cplusplus
}
#endif
#endif #endif

View File

@@ -2,9 +2,10 @@
#include<string.h> #include<string.h>
#include<endian.h> #include<endian.h>
#include<stdlib.h> #include<stddef.h>
#include<assert.h>
static uint64_t VARINT_MASKS[] = {0, 0x80L, 0xC000L, 0xE00000L, 0xF0000000L, 0xF800000000L, 0xFC0000000000L, 0xFE000000000000L, 0L}; static uint64_t VARINT_MASKS[] = {0x0UL, 0x80UL, 0x4000UL, 0x200000UL, 0x10000000UL, 0x0800000000UL, 0x040000000000UL, 0x02000000000000UL, 0x0100000000000000UL};
static int byte_length(uint64_t i) { static int byte_length(uint64_t i) {
if(i == 0) return 1; if(i == 0) return 1;
@@ -14,6 +15,7 @@ static int byte_length(uint64_t i) {
i >>= 8; i >>= 8;
n++; n++;
} }
return n; return n;
} }
@@ -30,7 +32,8 @@ static int bit_length(uint64_t i) {
} }
static uint64_t encode_int(uint64_t i) { static uint64_t encode_int(uint64_t i) {
return i | VARINT_MASKS[(bit_length(i) + 6) / 7]; // bit_length(i + 1) over bit_length(i) because 0xFF... lengths are interpreted as indefinite
return i | VARINT_MASKS[(bit_length(i + 1) + 6) / 7];
} }
static void advance(EBMLWriter *this, uint64_t amount) { static void advance(EBMLWriter *this, uint64_t amount) {
@@ -41,7 +44,7 @@ static void advance(EBMLWriter *this, uint64_t amount) {
static void add(EBMLWriter *this, const void *data, size_t length) { static void add(EBMLWriter *this, const void *data, size_t length) {
if(this->bufferLen + length > this->bufferCapacity) { if(this->bufferLen + length > this->bufferCapacity) {
this->buffer = realloc(this->buffer, (this->bufferLen + length + 1023) & ~1023); this->buffer = this->eventAlloc(this, this->buffer, this->bufferCapacity = ((this->bufferLen + length + 1023) & ~1023));
} }
memcpy(this->buffer + this->bufferLen, data, length); memcpy(this->buffer + this->bufferLen, data, length);
@@ -61,19 +64,47 @@ static void add_varint(EBMLWriter *this, uint64_t i) {
void ebml_writer_init(EBMLWriter *this) { void ebml_writer_init(EBMLWriter *this) {
memset(this, 0, sizeof(EBMLWriter)); memset(this, 0, sizeof(EBMLWriter));
}
void ebml_writer_free(EBMLWriter *this) {
this->eventAlloc(this, this->buffer, 0);
}
void ebml_writer_push_ind(EBMLWriter *this, uint64_t id) {
add_varint(this, id);
this->buffer = calloc(this->bufferCapacity = 1024, 1); add(this, &(uint64_t) {htobe64(0x01FFFFFFFFFFFFFFUL)}, sizeof(uint64_t)); // UNKNOWN-SIZED ELEMENT
while(!ebml_writer_flush(this));
} }
void ebml_writer_push(EBMLWriter *this, uint64_t id) { void ebml_writer_push(EBMLWriter *this, uint64_t id) {
add_varint(this, id); add_varint(this, id);
add(this, &(uint64_t) {htobe64(0x0100000000000000L)}, sizeof(uint64_t)); // LENGTH OF MAX SIZE (BECAUSE UNKNOWN) add(this, &(uint64_t) {htobe64(0x0100000000000000UL)}, sizeof(uint64_t)); // LENGTH OF MAX SIZE (BECAUSE UNKNOWN)
this->stack[this->currentDepth++] = 0; this->stack[this->currentDepth++] = 0;
} }
void ebml_writer_pop(EBMLWriter *this) { void ebml_writer_pop(EBMLWriter *this) {
this->currentDepth--;
uint64_t newSize = this->stack[this->currentDepth];
uint64_t *s = (uint64_t*) &this->buffer[this->bufferLen - newSize - 8];
*s = htobe64(0x0100000000000000UL | newSize);
if(this->currentDepth == 0) {
while(!ebml_writer_flush(this));
}
}
bool ebml_writer_flush(EBMLWriter *this) {
size_t written = this->eventWrite(this, this->buffer, this->bufferLen);
memmove(this->buffer, this->buffer + written, this->bufferLen - written);
this->bufferLen -= written;
return this->bufferLen == 0;
} }
void ebml_writer_put(EBMLWriter *this, uint64_t id, EBMLElementType type, EBMLPrimitive primitive) { void ebml_writer_put(EBMLWriter *this, uint64_t id, EBMLElementType type, EBMLPrimitive primitive) {
@@ -89,9 +120,11 @@ void ebml_writer_put(EBMLWriter *this, uint64_t id, EBMLElementType type, EBMLPr
len = 8; len = 8;
} else if(type == EBML_STRING) { } else if(type == EBML_STRING) {
len = strlen(primitive.string); len = strlen(primitive.string);
} else if(type == EBML_BINARY) {
len = primitive.binary.length;
} else if(type == EBML_DATE) { } else if(type == EBML_DATE) {
len = 8; len = 8;
} else abort(); } else assert(0);
add_varint(this, encode_int(len)); add_varint(this, encode_int(len));
@@ -100,13 +133,17 @@ void ebml_writer_put(EBMLWriter *this, uint64_t id, EBMLElementType type, EBMLPr
add(this, (uint8_t*) &primitive.uInt + 8 - len, len); add(this, (uint8_t*) &primitive.uInt + 8 - len, len);
} else if(type == EBML_FLOAT4) { } else if(type == EBML_FLOAT4) {
uint32_t fl = *(uint32_t*) &primitive.flt4; uint32_t fl = *(uint32_t*) &primitive.flt4;
fl = htobe32(fl);
add(this, &fl, 4); add(this, &fl, 4);
} else if(type == EBML_FLOAT8) { } else if(type == EBML_FLOAT8) {
uint64_t fl = *(uint64_t*) &primitive.flt8; uint64_t fl = *(uint64_t*) &primitive.flt8;
fl = htobe64(fl);
add(this, &fl, 8); add(this, &fl, 8);
} else if(type == EBML_STRING) { } else if(type == EBML_STRING) {
add(this, primitive.string, len); add(this, primitive.string, len);
} else if(type == EBML_BINARY) {
add(this, primitive.binary.ptr, primitive.binary.length);
} else if(type == EBML_DATE) { } else if(type == EBML_DATE) {
add(this, &primitive.date, 8); add(this, &primitive.date, 8);
} else abort(); } else assert(0);
} }

View File

@@ -4,16 +4,25 @@
#include"ebml.h" #include"ebml.h"
#include<stddef.h> #include<stddef.h>
#include<stdbool.h>
#ifdef __cplusplus
extern "C" {
#endif
#define EBML_WRITE_MAXIMUM_DEPTH 128 #define EBML_WRITE_MAXIMUM_DEPTH 128
typedef size_t(*EBMLWriteCallback)(void *ud, const void *data, size_t length); struct EBMLWriter;
typedef struct { typedef size_t(*EBMLEventWrite)(struct EBMLWriter*, const void *data, size_t length);
typedef void*(*EBMLEventAlloc)(struct EBMLWriter*, void *data, size_t length);
typedef struct EBMLWriter {
int currentDepth; int currentDepth;
uint64_t stack[EBML_WRITE_MAXIMUM_DEPTH]; uint64_t stack[EBML_WRITE_MAXIMUM_DEPTH];
EBMLWriteCallback callbackWrite; EBMLEventWrite eventWrite;
EBMLEventAlloc eventAlloc;
uint8_t *buffer; uint8_t *buffer;
size_t bufferLen; size_t bufferLen;
@@ -22,11 +31,19 @@ typedef struct {
void *ud; void *ud;
} EBMLWriter; } EBMLWriter;
void ebml_writer_init(EBMLWriter *this); void ebml_writer_init(EBMLWriter*);
void ebml_writer_free(EBMLWriter*);
void ebml_writer_push(EBMLWriter *this, uint64_t id); void ebml_writer_push_ind(EBMLWriter*, uint64_t id);
void ebml_writer_pop(EBMLWriter *this); void ebml_writer_push(EBMLWriter*, uint64_t id);
void ebml_writer_pop(EBMLWriter*);
void ebml_writer_put(EBMLWriter *this, uint64_t id, EBMLElementType type, EBMLPrimitive primitive); bool ebml_writer_flush(EBMLWriter*);
void ebml_writer_put(EBMLWriter*, uint64_t id, EBMLElementType type, EBMLPrimitive primitive);
#ifdef __cplusplus
}
#endif
#endif #endif