+struct loc_database_enumerator {
+ struct loc_ctx* ctx;
+ struct loc_database* db;
+ int refcount;
+
+ // Search string
+ char* string;
+
+ // Index of the AS we are looking at
+ unsigned int as_index;
+};
+
+static int loc_database_read_magic(struct loc_database* db, FILE* f) {
+ struct loc_database_magic magic;
+
+ // Read from file
+ size_t bytes_read = fread(&magic, 1, sizeof(magic), f);
+
+ // Check if we have been able to read enough data
+ if (bytes_read < sizeof(magic)) {
+ ERROR(db->ctx, "Could not read enough data to validate magic bytes\n");
+ DEBUG(db->ctx, "Read %zu bytes, but needed %zu\n", bytes_read, sizeof(magic));
+ return -ENOMSG;
+ }
+
+ // Compare magic bytes
+ if (memcmp(LOC_DATABASE_MAGIC, magic.magic, strlen(LOC_DATABASE_MAGIC)) == 0) {
+ DEBUG(db->ctx, "Magic value matches\n");
+
+ // Parse version
+ db->version = be16toh(magic.version);
+ DEBUG(db->ctx, "Database version is %u\n", db->version);
+
+ return 0;
+ }
+
+ ERROR(db->ctx, "Database format is not compatible\n");
+
+ // Return an error
+ return 1;
+}
+
+static int loc_database_read_as_section_v0(struct loc_database* db,
+ FILE* f, const struct loc_database_header_v0* header) {
+ off_t as_offset = be32toh(header->as_offset);
+ size_t as_length = be32toh(header->as_length);
+
+ DEBUG(db->ctx, "Reading AS section from %jd (%zu bytes)\n", as_offset, as_length);
+
+ if (as_length > 0) {
+ db->as_v0 = mmap(NULL, as_length, PROT_READ,
+ MAP_SHARED, fileno(f), as_offset);
+
+ if (db->as_v0 == MAP_FAILED)
+ return -errno;
+ }
+
+ db->as_count = as_length / sizeof(*db->as_v0);
+
+ INFO(db->ctx, "Read %zu ASes from the database\n", db->as_count);
+
+ return 0;
+}
+
+static int loc_database_read_network_nodes_section_v0(struct loc_database* db,
+ FILE* f, const struct loc_database_header_v0* header) {
+ off_t network_nodes_offset = be32toh(header->network_tree_offset);
+ size_t network_nodes_length = be32toh(header->network_tree_length);
+
+ DEBUG(db->ctx, "Reading network nodes section from %jd (%zu bytes)\n",
+ network_nodes_offset, network_nodes_length);
+
+ if (network_nodes_length > 0) {
+ db->network_nodes_v0 = mmap(NULL, network_nodes_length, PROT_READ,
+ MAP_SHARED, fileno(f), network_nodes_offset);
+
+ if (db->network_nodes_v0 == MAP_FAILED)
+ return -errno;
+ }
+
+ db->network_nodes_count = network_nodes_length / sizeof(*db->network_nodes_v0);
+
+ INFO(db->ctx, "Read %zu network nodes from the database\n", db->network_nodes_count);
+
+ return 0;
+}
+
+static int loc_database_read_networks_section_v0(struct loc_database* db,
+ FILE* f, const struct loc_database_header_v0* header) {
+ off_t networks_offset = be32toh(header->network_data_offset);
+ size_t networks_length = be32toh(header->network_data_length);
+
+ DEBUG(db->ctx, "Reading networks section from %jd (%zu bytes)\n",
+ networks_offset, networks_length);
+
+ if (networks_length > 0) {
+ db->networks_v0 = mmap(NULL, networks_length, PROT_READ,
+ MAP_SHARED, fileno(f), networks_offset);
+
+ if (db->networks_v0 == MAP_FAILED)
+ return -errno;
+ }
+
+ db->networks_count = networks_length / sizeof(*db->networks_v0);
+
+ INFO(db->ctx, "Read %zu networks from the database\n", db->networks_count);
+
+ return 0;
+}
+
+static int loc_database_read_header_v0(struct loc_database* db, FILE* f) {
+ struct loc_database_header_v0 header;
+
+ // Read from file
+ size_t size = fread(&header, 1, sizeof(header), f);
+
+ if (size < sizeof(header)) {
+ ERROR(db->ctx, "Could not read enough data for header\n");
+ return -ENOMSG;
+ }
+
+ // Copy over data
+ db->created_at = be64toh(header.created_at);
+ db->vendor = be32toh(header.vendor);
+ db->description = be32toh(header.description);
+ db->license = be32toh(header.license);
+
+ // Open pool
+ off_t pool_offset = be32toh(header.pool_offset);
+ size_t pool_length = be32toh(header.pool_length);
+
+ int r = loc_stringpool_open(db->ctx, &db->pool,
+ f, pool_length, pool_offset);
+ if (r)
+ return r;
+
+ // AS section
+ r = loc_database_read_as_section_v0(db, f, &header);
+ if (r)
+ return r;
+
+ // Network Nodes
+ r = loc_database_read_network_nodes_section_v0(db, f, &header);
+ if (r)
+ return r;
+
+ // Networks
+ r = loc_database_read_networks_section_v0(db, f, &header);
+ if (r)
+ return r;
+
+ return 0;
+}
+
+static int loc_database_read_header(struct loc_database* db, FILE* f) {
+ switch (db->version) {
+ case 0:
+ return loc_database_read_header_v0(db, f);
+
+ default:
+ ERROR(db->ctx, "Incompatible database version: %u\n", db->version);
+ return 1;
+ }
+}
+
+static int loc_database_read(struct loc_database* db, FILE* f) {
+ clock_t start = clock();
+
+ // Read magic bytes
+ int r = loc_database_read_magic(db, f);
+ if (r)
+ return r;
+
+ // Read the header
+ r = loc_database_read_header(db, f);
+ if (r)
+ return r;
+
+ clock_t end = clock();
+
+ INFO(db->ctx, "Opened database in %.8fs\n",
+ (double)(end - start) / CLOCKS_PER_SEC);
+
+ return 0;
+}
+
+LOC_EXPORT int loc_database_new(struct loc_ctx* ctx, struct loc_database** database, FILE* f) {
+ // Fail on invalid file handle
+ if (!f)
+ return -EINVAL;
+