use hashmap for compound tag

This commit is contained in:
Freya Murphy 2023-12-16 11:47:59 -05:00
parent 4760ed147e
commit 85ba301f52
No known key found for this signature in database
GPG key ID: 988032A5638EE799
10 changed files with 156 additions and 65 deletions

View file

@ -68,7 +68,7 @@ static bool json_print_long_array(const tagdata_t *data, const stream_t *stream)
} }
static bool json_print_string(const tagdata_t *data, const stream_t *stream) { static bool json_print_string(const tagdata_t *data, const stream_t *stream) {
if (data->string.size > 1) { if (data->string.size > 0) {
if (printi(stream, 0, "\"%.*s\"", data->string.size, data->string.data) == false) if (printi(stream, 0, "\"%.*s\"", data->string.size, data->string.data) == false)
return false; return false;
} else { } else {
@ -81,10 +81,14 @@ static bool json_print_string(const tagdata_t *data, const stream_t *stream) {
static bool json_print_compound(const tagdata_t *data, const stream_t *stream, int depth) { static bool json_print_compound(const tagdata_t *data, const stream_t *stream, int depth) {
if (printi(stream, 0, "{\n") == false) if (printi(stream, 0, "{\n") == false)
return false; return false;
for (int32_t i = 0; i < data->compound.size; i++) { bool first = true;
if (i != 0 && printi(stream, 0, ",\n") == false) for (uint32_t i = 0; i < data->compound.capacity; i++) {
if (data->compound.entries[i].name == NULL)
continue;
if (!first && printi(stream, 0, ",\n") == false)
return false; return false;
if (json_print_impl(&data->compound.tags[i], stream, depth + 1) == false) first = false;
if (json_print_impl(&data->compound.entries[i], stream, depth + 1) == false)
return false; return false;
} }
if (printi(stream, 0, "\n") == false || printi(stream, depth, "}") == false) if (printi(stream, 0, "\n") == false || printi(stream, depth, "}") == false)

View file

@ -499,6 +499,9 @@ static bool json_read_list(tagdata_t *data, const stream_t *stream) {
static bool json_read_compound(tagdata_t *data, const stream_t *stream) { static bool json_read_compound(tagdata_t *data, const stream_t *stream) {
map_t map;
map_init(&map);
token_t next = {0}; token_t next = {0};
if (json_next_token(&next, stream) == false) { if (json_next_token(&next, stream) == false) {
json_token_free(&next); json_token_free(&next);
@ -506,19 +509,14 @@ static bool json_read_compound(tagdata_t *data, const stream_t *stream) {
} }
if (next.type == TOK_RBRACE) { if (next.type == TOK_RBRACE) {
data->compound.tags = NULL; data->compound = map;
data->compound.size = 0;
return true; return true;
} }
int capacity = 8;
int len = 0;
tag_t *tags = xalloc(capacity * sizeof(tag_t));
while (1) { while (1) {
if (next.type != TOK_STRING) { if (next.type != TOK_STRING) {
free(tags); map_free(&map);
json_token_free(&next); json_token_free(&next);
return false; return false;
} }
@ -527,20 +525,20 @@ static bool json_read_compound(tagdata_t *data, const stream_t *stream) {
int name_len = next.data.string.len; int name_len = next.data.string.len;
if (name_len < 1) { if (name_len < 1) {
free(tags); map_free(&map);
free(name); free(name);
return false; return false;
} }
if (json_next_token(&next, stream) == false || next.type != TOK_COLON) { if (json_next_token(&next, stream) == false || next.type != TOK_COLON) {
free(tags); map_free(&map);
free(name); free(name);
return false; return false;
} }
tag_t value; tag_t value;
if (json_read_value(&value, stream, NULL) == false) { if (json_read_value(&value, stream, NULL) == false) {
free(tags); map_free(&map);
free(name); free(name);
return false; return false;
} }
@ -548,42 +546,31 @@ static bool json_read_compound(tagdata_t *data, const stream_t *stream) {
value.name = name; value.name = name;
value.name_len = name_len; value.name_len = name_len;
if (len == capacity) { map_put(&map, &value);
capacity *= 2;
tags = xrealloc(tags, capacity * sizeof(tag_t));
}
tags[len++] = value;
if (json_next_token(&next, stream) == false) { if (json_next_token(&next, stream) == false) {
free(tags); map_free(&map);
free(value.name);
json_token_free(&next); json_token_free(&next);
return false; return false;
} }
if (next.type == TOK_COMMA) { if (next.type == TOK_COMMA) {
if (json_next_token(&next, stream) == false) { if (json_next_token(&next, stream) == false) {
free(tags); map_free(&map);
free(name);
return false; return false;
} }
continue; continue;
} else if (next.type == TOK_RBRACE) { } else if (next.type == TOK_RBRACE) {
break; break;
} else { } else {
free(tags); map_free(&map);
free(value.name);
json_token_free(&next); json_token_free(&next);
return false; return false;
} }
} }
data->compound.tags = xalloc(len * sizeof(tag_t)); data->compound = map;
data->compound.size = len;
memcpy(data->compound.tags, tags, len * sizeof(tag_t));
free(tags);
return true; return true;
} }

View file

@ -3,6 +3,7 @@
#include <stdarg.h> #include <stdarg.h>
#include <stdio.h> #include <stdio.h>
#include <stdlib.h> #include <stdlib.h>
#include <string.h>
__attribute__((__noreturn__)) __attribute__((__noreturn__))
static void die() { static void die() {
@ -36,6 +37,12 @@ void *xalloc(size_t amount) {
return res; return res;
} }
void *xzalloc(size_t amount) {
void *res = xalloc(amount);
memset(res, 0, sizeof(amount));
return res;
}
void *xrealloc(void *ptr, size_t amount) { void *xrealloc(void *ptr, size_t amount) {
void *res = realloc(ptr, amount); void *res = realloc(ptr, amount);
if (res == NULL) if (res == NULL)

View file

@ -9,4 +9,5 @@ __attribute__((__noreturn__, format(printf, 1, 2)))
void perror_and_die(char *format, ...); void perror_and_die(char *format, ...);
void *xalloc(size_t amount); void *xalloc(size_t amount);
void *xzalloc(size_t amount);
void *xrealloc(void *ptr, size_t amount); void *xrealloc(void *ptr, size_t amount);

86
src/map.c Normal file
View file

@ -0,0 +1,86 @@
#include "map.h"
#include "lib.h"
#include "tag.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
void map_init(map_t *map) {
map->len = 0;
map->capacity = 0;
map->entries = NULL;
}
void map_free(map_t *map) {
if (map->entries == NULL)
return;
for (uint32_t i = 0; i < map->capacity; i++) {
if (map->entries[i].name != NULL)
tag_free(&map->entries[i]);
}
free(map->entries);
}
static uint32_t hash(const char *name, uint16_t len) {
uint32_t hash = 2166136261u;
while(len > 0) {
hash ^= (uint8_t)(*name);
hash *= 16777619;
name++;
len--;
}
return hash;
}
static tag_t *map_find(tag_t *entries, uint32_t capacity, tag_t *tag) {
uint32_t index = hash(tag->name, tag->name_len) % capacity;
while(true) {
tag_t *entry = &entries[index];
if (entry->name == NULL) {
return entry;
} else if (
entry->name_len == tag->name_len &&
memcmp(entry->name, tag->name, tag->name_len) == 0
) {
return entry;
}
index += 1;
index %= capacity;
}
}
static void map_grow(map_t *map, uint32_t capacity) {
tag_t *entries = xzalloc(capacity * sizeof(tag_t));
for (uint32_t i = 0; i < capacity; i++) {
entries[i].name = NULL;
entries[i].name_len = 0;
}
map->len = 0;
for (uint32_t i = 0; i < map->capacity; i++) {
tag_t *tag = &map->entries[i];
if (tag->name == NULL) continue;
tag_t *dest = map_find(entries, capacity, tag);
*dest = *tag;
map->len++;
}
free(map->entries);
map->entries = entries;
map->capacity = capacity;
}
void map_put(map_t *map, tag_t *tag) {
if (map->len + 1 > map->capacity * 0.75) {
int capacity = (map->capacity == 0 ? 8 : (2 * map->capacity));
map_grow(map, capacity);
}
tag_t *dest = map_find(map->entries, map->capacity, tag);
if (dest->name == NULL) {
map->len++;
} else {
tag_free(dest);
}
*dest = *tag;
}

15
src/map.h Normal file
View file

@ -0,0 +1,15 @@
#pragma once
#include <stdint.h>
typedef struct tag_t tag_t;
typedef struct {
uint32_t len;
uint32_t capacity;
tag_t *entries;
} map_t;
void map_init(map_t *map);
void map_free(map_t *map);
void map_put(map_t *map, tag_t *tag);

View file

@ -64,9 +64,11 @@ static bool nbt_print_list(const tagdata_t *data, const stream_t *stream) {
} }
static bool nbt_print_compound(const tagdata_t *data, const stream_t *stream) { static bool nbt_print_compound(const tagdata_t *data, const stream_t *stream) {
for (int32_t i = 0; i < data->compound.size; i++) for (uint32_t i = 0; i < data->compound.capacity; i++) {
if (nbt_print(&data->compound.tags[i], stream) == false) if (data->compound.entries[i].name == NULL) continue;
if (nbt_print(&data->compound.entries[i], stream) == false)
return false; return false;
}
if (stream_write_i8(stream, TAG_END) == false) if (stream_write_i8(stream, TAG_END) == false)
return false; return false;
return true; return true;

View file

@ -118,44 +118,36 @@ static bool nbt_read_list(tagdata_t *data, const stream_t *stream) {
} }
static bool nbt_read_compound(tagdata_t *data, const stream_t *stream) { static bool nbt_read_compound(tagdata_t *data, const stream_t *stream) {
int32_t size = 0;
int32_t capacity = 8; map_t map;
tag_t *tags = xalloc(capacity * sizeof(tag_t)); map_init(&map);
while (1) { while (1) {
tag_t tag; tag_t tag;
if (nbt_read_header(&tag, stream, true) == false) { if (nbt_read_header(&tag, stream, true) == false) {
free(tags); map_free(&map);
return false;
}
if (tag.name_len < 1) {
free(tags);
return false; return false;
} }
if (tag.type == TAG_END) if (tag.type == TAG_END)
break; break;
if (nbt_read_data(&tag, stream) == false) { if (tag.name_len < 1) {
free(tags); map_free(&map);
return false; return false;
} }
if (size == capacity) { if (nbt_read_data(&tag, stream) == false) {
capacity *= 2; map_free(&map);
tags = xrealloc(tags, capacity * sizeof(tag_t)); return false;
} }
tags[size++] = tag; map_put(&map, &tag);
} }
data->compound.size = size; data->compound = map;
data->compound.tags = xalloc(size * sizeof(tag_t));
memcpy(data->compound.tags, tags, size * sizeof(tag_t));
free(tags);
return true; return true;
} }

View file

@ -1,4 +1,5 @@
#include "tag.h" #include "tag.h"
#include "map.h"
#include "nbt/nbt.h" #include "nbt/nbt.h"
#include "json/json.h" #include "json/json.h"
@ -28,9 +29,7 @@ void tag_free(tag_t *tag) {
free(tag->data.list.tags); free(tag->data.list.tags);
break; break;
case TAG_COMPOUND: case TAG_COMPOUND:
for (int32_t i = 0; i < tag->data.compound.size; i++) map_free(&tag->data.compound);
tag_free(&tag->data.compound.tags[i]);
free(tag->data.compound.tags);
break; break;
case TAG_INT_ARRAY: case TAG_INT_ARRAY:
free(tag->data.i_arr.data); free(tag->data.i_arr.data);

View file

@ -1,6 +1,7 @@
#pragma once #pragma once
#include "stream.h" #include "stream.h"
#include "map.h"
#include <stdint.h> #include <stdint.h>
#include <stdbool.h> #include <stdbool.h>
@ -46,10 +47,7 @@ typedef union {
int32_t size; int32_t size;
struct tag_t *tags; struct tag_t *tags;
} list; } list;
struct { map_t compound;
int32_t size;
struct tag_t *tags;
} compound;
struct { struct {
int32_t size; int32_t size;
int32_t *data; int32_t *data;