Implement big-endian machines support

master
Yury Kurlykov 2019-12-03 20:00:22 +10:00
parent 8d56569dd5
commit a6a8d43f91
2 changed files with 88 additions and 21 deletions

View File

@ -1,9 +1,57 @@
#pragma once
#include <stdint.h>
#include <endian.h>
/**
* @file macro.h
* @brief The macros used in the engine.
*/
/** @brief Return `err` if `x` is null. */
#define THROW_IF_NULL(x, err) if (!(x)) return (err)
#define THROW_IF_NULL(x, err) if (!(x)) return (err)
/**
* @def TO_LE(x)
* @param x Integer value.
* @brief Convert a value stored in memory to little-endian.
* Passes the value if current architecture is little-endian.
*/
/**
* @def FROM_LE(x)
* @param x Integer value.
* @brief Convert a little-endian value to architecture endian.
* Passes the value if current architecture is little-endian.
*/
/**
* @def REASSIGN_FROM_LE(x)
* @param x Non-const integer variable.
* @brief Reassign a variable with converted stored value.
* Does nothing if current architecture is little-endian.
*/
#if BYTE_ORDER == BIG_ENDIAN
#define TO_LE(x) _Generic((x), \
uint16_t: htole16, \
int16_t: htole16, \
uint32_t: htole32, \
int32_t: htole32, \
uint64_t: htole64, \
int64_t: htole64, \
)(x)
#define FROM_LE(x) _Generic((x), \
uint16_t: le16toh, \
int16_t: le16toh, \
uint32_t: le32toh, \
int32_t: le32toh, \
uint64_t: le64toh, \
int64_t: le64toh, \
)(x)
#define REASSIGN_FROM_LE(x) (x) = FROM_LE((x))
#else
#define TO_LE(x) (x)
#define FROM_LE(x) (x)
#define REASSIGN_FROM_LE(x)
#endif

View File

@ -83,19 +83,25 @@ void ydb_terminate_instance(YDB_Engine *instance) {
static YDB_Error __ydb_read_page(YDB_Engine *inst) {
THROW_IF_NULL(inst, YDB_ERR_INSTANCE_NOT_INITIALIZED);
// Seek to current page
fseek(inst->fd, inst->curr_page_offset, SEEK_SET);
// Read page data
char p_data[YDB_TABLE_PAGE_SIZE];
size_t bytes_read = fread(p_data, 1, YDB_TABLE_PAGE_SIZE, inst->fd);
// Throw error if failed to read exactly a page size.
if (bytes_read != YDB_TABLE_PAGE_SIZE) {
return YDB_ERR_TABLE_DATA_CORRUPTED;
}
// Read page flags, next page offset and row count
YDB_Flags page_flags = p_data[0];
YDB_Offset next;
YDB_PageSize row_count;
memcpy(&next, p_data + v0_page_offsets.next_page_offset, sizeof(next));
REASSIGN_FROM_LE(next);
memcpy(&row_count, p_data + v0_page_offsets.row_count, sizeof(row_count));
REASSIGN_FROM_LE(row_count);
const YDB_PageSize meta_size = v0_page_offsets.meta_end;
const YDB_PageSize data_size = YDB_TABLE_PAGE_SIZE - meta_size;
@ -116,27 +122,29 @@ static YDB_Error __ydb_read_page(YDB_Engine *inst) {
// Moves file position to allocated block.
// Also changes last_free_page_offset.
static YDB_Offset __ydb_allocate_page_and_seek(YDB_Engine *inst) {
// TODO change signature of the function. It should return an error code, I think.
YDB_Offset result = 0;
// If no free pages in the table, then...
if (inst->last_free_page_offset == 0) {
// Return the end of the file where a new page will be allocated
fseek(inst->fd, 0, SEEK_END);
result = ftell(inst->fd);
// Write next page offset in the previous page
fseek(inst->fd, inst->last_page_offset + v0_page_offsets.next_page_offset, SEEK_SET); // Skip page flag
fwrite(&result, sizeof(result), 1, inst->fd);
YDB_Offset allocated_page_offset_le = TO_LE(result);
// Allocate an empty chunk
fseek(inst->fd, 0, SEEK_END);
char* new_page_data = calloc(1, YDB_TABLE_PAGE_SIZE);
fwrite(new_page_data, YDB_TABLE_PAGE_SIZE, 1, inst->fd);
free(new_page_data);
// TODO throw error on fail
// Write next page offset in the previous page
fseek(inst->fd, inst->last_page_offset + v0_page_offsets.next_page_offset, SEEK_SET); // Skip page flag
fwrite(&allocated_page_offset_le, sizeof(YDB_Offset), 1, inst->fd);
// Write last page offset
inst->last_page_offset = result;
fseek(inst->fd, v0_offsets.last_page_offset, SEEK_SET);
fwrite(&inst->last_page_offset, sizeof(YDB_Offset), 1, inst->fd);
fwrite(&allocated_page_offset_le, sizeof(YDB_Offset), 1, inst->fd);
fflush(inst->fd);
} else {
// Return last free page offset
@ -144,7 +152,8 @@ static YDB_Offset __ydb_allocate_page_and_seek(YDB_Engine *inst) {
// Read last free page offset after allocation
fseek(inst->fd, result + v0_page_offsets.next_page_offset, SEEK_SET); // Skip flag
fread(&(inst->last_free_page_offset), sizeof(YDB_Offset), 1, inst->fd);
fread(&inst->last_free_page_offset, sizeof(YDB_Offset), 1, inst->fd);
REASSIGN_FROM_LE(inst->last_free_page_offset);
fseek(inst->fd, -(long)(sizeof(YDB_Offset) + 1), SEEK_CUR);
// Rewrite page header
@ -155,7 +164,8 @@ static YDB_Offset __ydb_allocate_page_and_seek(YDB_Engine *inst) {
// Write last free page offset after allocation in file
fseek(inst->fd, v0_offsets.last_free_page_offset, SEEK_SET);
fwrite(&(inst->last_free_page_offset), sizeof(YDB_Offset), 1, inst->fd);
YDB_Offset lfp_offset_le = TO_LE(inst->last_free_page_offset);
fwrite(&lfp_offset_le, sizeof(YDB_Offset), 1, inst->fd);
}
fflush(inst->fd);
fseek(inst->fd, result, SEEK_SET);
@ -201,9 +211,12 @@ YDB_Error ydb_load_table(YDB_Engine *instance, const char *path) {
return YDB_ERR_TABLE_DATA_CORRUPTED;
}
fread(&(instance->first_page_offset), sizeof(YDB_Offset), 1, instance->fd);
fread(&(instance->last_page_offset), sizeof(YDB_Offset), 1, instance->fd);
fread(&(instance->last_free_page_offset), sizeof(YDB_Offset), 1, instance->fd);
fread(&instance->first_page_offset, sizeof(YDB_Offset), 1, instance->fd);
REASSIGN_FROM_LE(instance->first_page_offset);
fread(&instance->last_page_offset, sizeof(YDB_Offset), 1, instance->fd);
REASSIGN_FROM_LE(instance->last_page_offset);
fread(&instance->last_free_page_offset, sizeof(YDB_Offset), 1, instance->fd);
REASSIGN_FROM_LE(instance->last_free_page_offset);
// TODO check offsets
instance->in_use = -1; // unsigned value overflow to fill all the bits
@ -252,7 +265,6 @@ YDB_Error ydb_create_table(YDB_Engine *instance, const char *path) {
FILE *f = fopen(path, "wb");
// TODO test on other byte ordered archs
char tpl[] = YDB_TABLE_FILE_SIGN
"\x00\x02"
"\x1E\x00\x00\x00\x00\x00\x00\x00"
@ -282,7 +294,7 @@ YDB_Error ydb_next_page(YDB_Engine *instance) {
instance->prev_page_offset = instance->curr_page_offset;
instance->curr_page_offset = instance->next_page_offset;
++(instance->current_page_index);
++instance->current_page_index;
return __ydb_read_page(instance);
}
@ -344,9 +356,11 @@ YDB_Error ydb_append_page(YDB_Engine* instance, YDB_TablePage* page) {
return YDB_ERR_UNKNOWN; // FIXME
}
YDB_PageSize rc_le = TO_LE(rc);
fwrite(&f, sizeof(f), 1, instance->fd);
fwrite(&next, sizeof(next), 1, instance->fd);
fwrite(&rc, sizeof(rc), 1, instance->fd);
fwrite(&rc_le, sizeof(rc), 1, instance->fd);
fwrite(d, sizeof(d), 1, instance->fd);
fflush(instance->fd);
@ -377,7 +391,8 @@ YDB_Error ydb_replace_current_page(YDB_Engine *instance, YDB_TablePage *page) {
// Write row count
YDB_PageSize row_cnt = ydb_page_row_count_get(page);
fwrite(&row_cnt, sizeof(row_cnt), 1, instance->fd);
YDB_PageSize row_cnt_le = TO_LE(row_cnt);
fwrite(&row_cnt_le, sizeof(row_cnt), 1, instance->fd);
// Write data
char page_data[YDB_TABLE_PAGE_SIZE - v0_page_offsets.meta_end];
@ -412,27 +427,31 @@ YDB_Error ydb_delete_current_page(YDB_Engine *instance) {
fputc(YDB_TABLE_PAGE_FLAG_DELETED, instance->fd);
// Write last_free_page_offset as the next page for current one
fwrite(&(instance->last_free_page_offset), sizeof(YDB_Offset), 1, instance->fd);
YDB_Offset lfp_le = TO_LE(instance->last_free_page_offset);
fwrite(&lfp_le, sizeof(YDB_Offset), 1, instance->fd);
// Link the previous page with next one (could be null ptr)
fseek(instance->fd, instance->prev_page_offset + 1, SEEK_SET); // Skip flags
fwrite(&(instance->next_page_offset), sizeof(YDB_Offset), 1, instance->fd);
YDB_Offset np_le = TO_LE(instance->next_page_offset);
fwrite(&np_le, sizeof(YDB_Offset), 1, instance->fd);
// If it was the last page, rewrite last_page_offset with prev_page_offset
if (instance->next_page_offset == 0) {
fseek(instance->fd, 14, SEEK_SET);
fwrite(&(instance->prev_page_offset), sizeof(YDB_Offset), 1, instance->fd);
YDB_Offset pp_le = TO_LE(instance->prev_page_offset);
fwrite(&pp_le, sizeof(YDB_Offset), 1, instance->fd);
}
// Rewrite last_free_page_offset with current offset
fseek(instance->fd, 22, SEEK_SET);
fwrite(&(instance->curr_page_offset), sizeof(YDB_Offset), 1, instance->fd);
YDB_Offset cp_le = TO_LE(instance->curr_page_offset);
fwrite(&cp_le, sizeof(YDB_Offset), 1, instance->fd);
// Flush buffer
fflush(instance->fd);
// Set previous page
int64_t ind = --(instance->current_page_index);
int64_t ind = --instance->current_page_index;
if (ydb_seek_page(instance, ind + 1)) {
return YDB_ERR_UNKNOWN;
}