Compare commits
9 Commits
251adf906b
...
master
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
e0716abc81 | ||
|
|
8b0fbfdcf7 | ||
|
|
75c0ac1997 | ||
|
|
cca8f02870 | ||
|
|
d0ccaff719 | ||
|
|
75d2efbe4c | ||
|
|
629a8fdf55 | ||
|
|
fb97037243 | ||
|
|
1476dbdd57 |
7
Makefile
Normal file
7
Makefile
Normal 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
|
||||||
44
README.md
44
README.md
@@ -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`
|
||||||
|
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|
||||||
|
|||||||
@@ -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,8 +162,10 @@ 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++) {
|
||||||
|
if(!is_max(this->stack[i])) {
|
||||||
this->stack[i] -= eaten;
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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));
|
||||||
|
}
|
||||||
|
|
||||||
this->buffer = calloc(this->bufferCapacity = 1024, 1);
|
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);
|
||||||
|
|
||||||
|
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);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
Reference in New Issue
Block a user