Still haven't properly tested MetaTree. But have all of the current features working together.

This commit is contained in:
2022-09-08 19:35:16 +10:00
parent abc9cf6a61
commit 392e226013
5 changed files with 195 additions and 94 deletions

View File

@@ -39,8 +39,7 @@ void asserts(void);
// Tree walkers
char *walk_concat(
char *data,
size_t size,
struct sized_buf tree,
struct walker_callbacks cb,
void *from_parent
);
@@ -88,7 +87,7 @@ void decompress_pre(
void decompress_post(
Node n,
void *data,
void **carry_in
void *carry_in
);
void create_tree_cb(
Node n,
@@ -97,6 +96,7 @@ void create_tree_cb(
void *from_parent,
void **to_children
);
void serialize_cb(MetaNode *m, void *data);
//
// === FUNCTIONS ===
@@ -118,22 +118,21 @@ void asserts(void)
}
void espr_walk(
char *data,
size_t size,
struct sized_buf esp,
struct walker_callbacks cb,
void *from_parent
) {
// check assertions that cannot be checked at compile time
asserts();
char *data_start = data;
char *data_start = esp.data;
// check that we are at the start of the file
const Type4 type = *(const Type4 *)data;
const Type4 type = *(const Type4 *)esp.data;
assert(type.uint == rt[TES4]);
data = walk_concat(data, size, cb, from_parent);
assert(data == data_start + size);
esp.data = walk_concat(esp, cb, from_parent);
assert(esp.data == data_start + esp.size);
}
/* Unknown data will be some concatenation of groups and records.
@@ -142,27 +141,26 @@ void espr_walk(
* for each segment of unknown data in this concatenation.
*/
char *walk_concat(
char *data,
size_t size,
struct sized_buf tree,
struct walker_callbacks cb,
void *from_parent
) {
const char *end = data + size;
while (data != end) {
assert(data < end);
const char *end = tree.data + tree.size;
while (tree.data != end) {
assert(tree.data < end);
const Type4 *type = (Type4 *)data;
const Type4 *type = (Type4 *)tree.data;
// check valid type
assert(rt[rt_hash(type->uint)] == type->uint);
// only need to distinguish between groups and records
if (type->uint == rt[GRUP])
data = walk_group(data, cb, from_parent);
tree.data = walk_group(tree.data, cb, from_parent);
else
data = walk_record(data, cb, from_parent);
tree.data = walk_record(tree.data, cb, from_parent);
}
return data;
return tree.data;
}
/* Walk a group record. Group records are containers for any other type of
@@ -193,7 +191,8 @@ char *walk_group(char *data, struct walker_callbacks cb, void *from_parent)
cb.pre(n, cb.data, &carry, from_parent, &to_children);
// Walk through the concatenation of data inside the group.
data = walk_concat(data_start, data_size, cb, to_children);
struct sized_buf tree = { data_start, data_size };
data = walk_concat(tree, cb, to_children);
assert(data == data_end);
// Post-walk callback
@@ -237,10 +236,10 @@ char *walk_record(char *data, struct walker_callbacks cb, void *from_parent)
return data;
}
void espr_print(char *data, size_t size)
void espr_print(struct sized_buf esp)
{
struct walker_callbacks cb = { .pre = print_cb };
espr_walk(data, size, cb, NULL);
espr_walk(esp, cb, NULL);
}
void print_cb(
@@ -266,11 +265,11 @@ void print_cb(
}
}
struct esp_stats espr_stats(char *data, size_t size)
struct esp_stats espr_stats(struct sized_buf esp)
{
struct esp_stats stats = { 0 };
struct walker_callbacks cb = { .pre = stats_cb, .data = &stats };
espr_walk(data, size, cb, NULL);
espr_walk(esp, cb, NULL);
return stats;
}
@@ -311,20 +310,14 @@ void stats_cb(
}
}
struct decom {
char *buf;
size_t remaining;
};
void espr_decompress(char *data, size_t size, char *buf, size_t buf_size)
void espr_decompress(struct sized_buf esp, struct sized_buf decom)
{
struct decom s = { .buf = buf, .remaining = buf_size };
struct walker_callbacks cb = {
.pre = decompress_pre,
.post = decompress_post,
.data = &s
.data = &decom
};
espr_walk(data, size, cb, NULL);
espr_walk(esp, cb, NULL);
}
/* Handles the copying of groups and records, and the decompression of
@@ -349,21 +342,20 @@ void decompress_pre(
(void)from_parent;
(void)to_children;
struct decom *d = decom_ptr;
struct sized_buf *d = decom_ptr;
switch (n.type) {
case NT_RECORD:
// compressed record
if (n.header.record->flags & COMPRESSED_FLAG) {
// copy header
memcpy(d->buf, n.header.record, sizeof(Record));
memcpy(d->data, n.header.record, sizeof(Record));
// copied header reference
Record *header = (Record *)d->buf;
Record *header = (Record *)d->data;
// update decom struct
d->remaining -= sizeof(Record);
d->buf += sizeof(Record);
sized_buf_update(d, sizeof(Record));
// decompress directly into buffer
// first 4 bytes are the decompressed size
@@ -373,7 +365,7 @@ void decompress_pre(
n.header.record->size - sizeof(uint32_t);
char *data_start = n.data + sizeof(uint32_t);
int ret = uncompress(
(Bytef *)d->buf,
(Bytef *)d->data,
(uLongf *)&to_copy,
(Bytef *)data_start,
(uLong)cur_size
@@ -382,8 +374,7 @@ void decompress_pre(
assert(to_copy == dc_size);
// update decom struct
d->remaining -= dc_size;
d->buf += dc_size;
sized_buf_update(d, dc_size);
// update header data size
header->size = dc_size;
@@ -393,23 +384,21 @@ void decompress_pre(
} else {
// copy record
size_t record_size = sizeof(Record) + n.header.record->size;
memcpy(d->buf, n.header.record, record_size);
memcpy(d->data, n.header.record, record_size);
// update decom
d->remaining -= record_size;
d->buf += record_size;
sized_buf_update(d, record_size);
}
break;
case NT_GROUP:
// copy header, contents will be copied while walking
memcpy(d->buf, n.header.group, sizeof(Group));
memcpy(d->data, n.header.group, sizeof(Group));
// save copied header location for post-walk group size recalc
*carry_out = (void *)d->buf;
*carry_out = (void *)d->data;
// update decom
d->buf += sizeof(Group);
d->remaining -= sizeof(Group);
sized_buf_update(d, sizeof(Group));
break;
default:
@@ -423,14 +412,14 @@ void decompress_pre(
* based on the difference between the current destination pointer and the
* group header pointer.
*/
void decompress_post(Node n, void *decom_ptr, void **carry_in)
void decompress_post(Node n, void *decom_ptr, void *carry_in)
{
struct decom *d = decom_ptr;
struct sized_buf *d = decom_ptr;
// only need to handle group resize
if (n.type == NT_GROUP) {
Group *g = (Group *)(*carry_in);
uint32_t new_size = (uint32_t)((char *)d->buf - (char *)g);
Group *g = (Group *)carry_in;
uint32_t new_size = (uint32_t)((char *)d->data - (char *)g);
g->size = new_size;
}
}
@@ -641,7 +630,7 @@ Timestamp convert_ts(uint16_t ts)
return (Timestamp) { year, month, day };
}
MetaNode *espr_create_tree(struct sized_buf in, struct sized_buf tree)
MetaTree espr_create_tree(struct sized_buf in, struct sized_buf tree)
{
// create root node
MetaNode *root = (MetaNode *)tree.data;
@@ -652,9 +641,9 @@ MetaNode *espr_create_tree(struct sized_buf in, struct sized_buf tree)
// walk
struct walker_callbacks cb = { .pre = create_tree_cb, .data = &tree };
espr_walk(in.data, in.size, cb, root);
espr_walk(in, cb, root);
return root;
return (MetaTree) { .root = root, .size = in.size };
}
void create_tree_cb(
@@ -690,3 +679,47 @@ void create_tree_cb(
*to_children = m;
}
void espr_meta_walk(MetaTree tree, struct meta_callbacks cb) {
espr_meta_node_walk(tree.root, cb);
}
void espr_meta_node_walk(MetaNode *m, struct meta_callbacks cb) {
cb.pre(m, cb.data);
MetaNode *child = m->first_child;
while (child) {
espr_meta_node_walk(child, cb);
child = child->next;
}
}
void espr_serialize(MetaTree tree, struct sized_buf out) {
struct meta_callbacks cb = { .pre = serialize_cb, .data = &out };
espr_meta_walk(tree, cb);
}
void serialize_cb(MetaNode *m, void *data) {
struct sized_buf *out = data;
// exit on empty node
if (!m->n.data)
return;
switch (m->n.type) {
case NT_GROUP:
// only serialize the header of groups
assert(out->size >= sizeof(Group));
memcpy(out->data, m->n.header.group, sizeof(Group));
sized_buf_update(out, sizeof(Group));
break;
case NT_RECORD:
size_t data_size = m->n.header.record->size;
assert(out->size >= data_size + sizeof(Record));
// serialize header and data separately as they may be
// discontiguous
memcpy(out->data, m->n.header.record, sizeof(Record));
sized_buf_update(out, sizeof(Record));
memcpy(out->data, m->n.data, data_size);
sized_buf_update(out, data_size);
}
}