|  |  |  | @ -24,8 +24,6 @@ struct __YDB_Engine { | 
		
	
		
			
				|  |  |  |  |   YDB_Offset curr_page_offset; /**< A location of current page in file. */ | 
		
	
		
			
				|  |  |  |  |   YDB_Offset next_page_offset; /**< A location of next page in file. */ | 
		
	
		
			
				|  |  |  |  | 
 | 
		
	
		
			
				|  |  |  |  |   int64_t current_page_index; /**< Current page index in range [0, page_count). */ | 
		
	
		
			
				|  |  |  |  | 
 | 
		
	
		
			
				|  |  |  |  |   YDB_TablePage *curr_page; /**< A pointer to the current page. */ | 
		
	
		
			
				|  |  |  |  | 
 | 
		
	
		
			
				|  |  |  |  |   uint8_t in_use; /**< "In use" flag. */ | 
		
	
	
		
			
				
					|  |  |  | @ -46,8 +44,7 @@ void ydb_terminate_instance(YDB_Engine *instance) { | 
		
	
		
			
				|  |  |  |  | } | 
		
	
		
			
				|  |  |  |  | 
 | 
		
	
		
			
				|  |  |  |  | // Internal usage only!
 | 
		
	
		
			
				|  |  |  |  | // Its only purpose to read current page data and set next_page offset.
 | 
		
	
		
			
				|  |  |  |  | // You should write prev_page offset on your own.
 | 
		
	
		
			
				|  |  |  |  | // Its only purpose to read current page data and set next_page and prev_page offsets.
 | 
		
	
		
			
				|  |  |  |  | static YDB_Error __ydb_read_page(YDB_Engine *inst) { | 
		
	
		
			
				|  |  |  |  |   THROW_IF_NULL(inst, YDB_ERR_INSTANCE_NOT_INITIALIZED); | 
		
	
		
			
				|  |  |  |  | 
 | 
		
	
	
		
			
				
					|  |  |  | @ -62,16 +59,19 @@ static YDB_Error __ydb_read_page(YDB_Engine *inst) { | 
		
	
		
			
				|  |  |  |  |     return YDB_ERR_TABLE_DATA_CORRUPTED; | 
		
	
		
			
				|  |  |  |  |   } | 
		
	
		
			
				|  |  |  |  | 
 | 
		
	
		
			
				|  |  |  |  |   // Read page flags, next page offset and row count
 | 
		
	
		
			
				|  |  |  |  |   // Read page flags, next and prev page offset and row count
 | 
		
	
		
			
				|  |  |  |  |   YDB_Flags page_flags = p_data[0]; | 
		
	
		
			
				|  |  |  |  |   YDB_Offset next; | 
		
	
		
			
				|  |  |  |  |   YDB_Offset prev; | 
		
	
		
			
				|  |  |  |  |   YDB_PageSize row_count; | 
		
	
		
			
				|  |  |  |  |   memcpy(&next, p_data + YDB_v0_page_next_offset, sizeof(next)); | 
		
	
		
			
				|  |  |  |  |   memcpy(&next, p_data + YDB_v1_page_next_offset, sizeof(next)); | 
		
	
		
			
				|  |  |  |  |   REASSIGN_FROM_LE(next); | 
		
	
		
			
				|  |  |  |  |   memcpy(&row_count, p_data + YDB_v0_page_row_count_offset, sizeof(row_count)); | 
		
	
		
			
				|  |  |  |  |   memcpy(&prev, p_data + YDB_v1_page_prev_offset, sizeof(prev)); | 
		
	
		
			
				|  |  |  |  |   REASSIGN_FROM_LE(prev); | 
		
	
		
			
				|  |  |  |  |   memcpy(&row_count, p_data + YDB_v1_page_row_count_offset, sizeof(row_count)); | 
		
	
		
			
				|  |  |  |  |   REASSIGN_FROM_LE(row_count); | 
		
	
		
			
				|  |  |  |  | 
 | 
		
	
		
			
				|  |  |  |  |   const YDB_PageSize meta_size = YDB_v0_page_data_offset; | 
		
	
		
			
				|  |  |  |  |   const YDB_PageSize meta_size = YDB_v1_page_data_offset; | 
		
	
		
			
				|  |  |  |  |   const YDB_PageSize data_size = YDB_TABLE_PAGE_SIZE - meta_size; | 
		
	
		
			
				|  |  |  |  | 
 | 
		
	
		
			
				|  |  |  |  |   YDB_TablePage *p = ydb_page_alloc(data_size); | 
		
	
	
		
			
				
					|  |  |  | @ -83,6 +83,7 @@ static YDB_Error __ydb_read_page(YDB_Engine *inst) { | 
		
	
		
			
				|  |  |  |  |   if (write_status) return write_status; | 
		
	
		
			
				|  |  |  |  | 
 | 
		
	
		
			
				|  |  |  |  |   inst->curr_page = p; | 
		
	
		
			
				|  |  |  |  |   inst->prev_page_offset = prev; | 
		
	
		
			
				|  |  |  |  |   inst->next_page_offset = next; | 
		
	
		
			
				|  |  |  |  | 
 | 
		
	
		
			
				|  |  |  |  |   return YDB_ERR_SUCCESS; | 
		
	
	
		
			
				
					|  |  |  | @ -101,17 +102,21 @@ static YDB_Offset __ydb_allocate_page_and_seek(YDB_Engine *inst) { | 
		
	
		
			
				|  |  |  |  | 
 | 
		
	
		
			
				|  |  |  |  |     // Allocate an empty chunk
 | 
		
	
		
			
				|  |  |  |  |     char* new_page_data = calloc(1, YDB_TABLE_PAGE_SIZE); | 
		
	
		
			
				|  |  |  |  |     // Write last page offset as prev page offset in this page
 | 
		
	
		
			
				|  |  |  |  |     YDB_Offset last_page_offset_le = TO_LE(inst->last_page_offset); | 
		
	
		
			
				|  |  |  |  |     memcpy(new_page_data + YDB_v1_page_prev_offset, &last_page_offset_le, sizeof(YDB_Offset)); | 
		
	
		
			
				|  |  |  |  |     // Write it to file
 | 
		
	
		
			
				|  |  |  |  |     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 + YDB_v0_page_next_offset, SEEK_SET); // Skip page flag
 | 
		
	
		
			
				|  |  |  |  |     // Write next page offset in the previous (last) page
 | 
		
	
		
			
				|  |  |  |  |     fseek(inst->fd, inst->last_page_offset + YDB_v1_page_next_offset, SEEK_SET); | 
		
	
		
			
				|  |  |  |  |     fwrite(&allocated_page_offset_le, sizeof(YDB_Offset), 1, inst->fd); | 
		
	
		
			
				|  |  |  |  | 
 | 
		
	
		
			
				|  |  |  |  |     // Write last page offset
 | 
		
	
		
			
				|  |  |  |  |     inst->last_page_offset = result; | 
		
	
		
			
				|  |  |  |  |     fseek(inst->fd, YDB_v0_last_page_offset, SEEK_SET); | 
		
	
		
			
				|  |  |  |  |     fseek(inst->fd, YDB_v1_last_page_offset, SEEK_SET); | 
		
	
		
			
				|  |  |  |  |     fwrite(&allocated_page_offset_le, sizeof(YDB_Offset), 1, inst->fd); | 
		
	
		
			
				|  |  |  |  |     fflush(inst->fd); | 
		
	
		
			
				|  |  |  |  |   } else { | 
		
	
	
		
			
				
					|  |  |  | @ -119,19 +124,22 @@ static YDB_Offset __ydb_allocate_page_and_seek(YDB_Engine *inst) { | 
		
	
		
			
				|  |  |  |  |     result = inst->last_free_page_offset; | 
		
	
		
			
				|  |  |  |  | 
 | 
		
	
		
			
				|  |  |  |  |     // Read last free page offset after allocation
 | 
		
	
		
			
				|  |  |  |  |     fseek(inst->fd, result + YDB_v0_page_next_offset, SEEK_SET); // Skip flag
 | 
		
	
		
			
				|  |  |  |  |     fseek(inst->fd, result + YDB_v1_page_next_offset, SEEK_SET); // Skip flag
 | 
		
	
		
			
				|  |  |  |  |     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); //FIXME magic
 | 
		
	
		
			
				|  |  |  |  | 
 | 
		
	
		
			
				|  |  |  |  |     // Rewrite page header
 | 
		
	
		
			
				|  |  |  |  |     char page_header[] = "\x00" // No flags
 | 
		
	
		
			
				|  |  |  |  |         "\x00\x00\x00\x00\x00\x00\x00\x00" // No next page
 | 
		
	
		
			
				|  |  |  |  |         "\x00\x00"; // No rows
 | 
		
	
		
			
				|  |  |  |  |     fwrite(page_header, sizeof(page_header)-1, 1, inst->fd); | 
		
	
		
			
				|  |  |  |  |     // Go back to our almost allocated page
 | 
		
	
		
			
				|  |  |  |  |     fseek(inst->fd, result, SEEK_SET); | 
		
	
		
			
				|  |  |  |  | 
 | 
		
	
		
			
				|  |  |  |  |     // Rewrite page header with last page offset as previous page
 | 
		
	
		
			
				|  |  |  |  |     char* page_header = calloc(1, YDB_v1_data_offset); | 
		
	
		
			
				|  |  |  |  |     YDB_Offset last_page_offset_le = TO_LE(inst->last_page_offset); | 
		
	
		
			
				|  |  |  |  |     memcpy(page_header + YDB_v1_page_prev_offset, &last_page_offset_le, sizeof(YDB_Offset)); | 
		
	
		
			
				|  |  |  |  |     fwrite(page_header, YDB_v1_data_offset, 1, inst->fd); | 
		
	
		
			
				|  |  |  |  |     free(page_header); | 
		
	
		
			
				|  |  |  |  | 
 | 
		
	
		
			
				|  |  |  |  |     // Write last free page offset after allocation in file
 | 
		
	
		
			
				|  |  |  |  |     fseek(inst->fd, YDB_v0_last_free_page_offset, SEEK_SET); | 
		
	
		
			
				|  |  |  |  |     fseek(inst->fd, YDB_v1_last_free_page_offset, SEEK_SET); | 
		
	
		
			
				|  |  |  |  |     YDB_Offset lfp_offset_le = TO_LE(inst->last_free_page_offset); | 
		
	
		
			
				|  |  |  |  |     fwrite(&lfp_offset_le, sizeof(YDB_Offset), 1, inst->fd); | 
		
	
		
			
				|  |  |  |  |   } | 
		
	
	
		
			
				
					|  |  |  | @ -152,9 +160,9 @@ YDB_Error ydb_load_table(YDB_Engine *instance, const char *path) { | 
		
	
		
			
				|  |  |  |  |   instance->fd = fopen(path, "rb+"); | 
		
	
		
			
				|  |  |  |  |   THROW_IF_NULL(instance->fd, YDB_ERR_UNKNOWN); // TODO file open error
 | 
		
	
		
			
				|  |  |  |  | 
 | 
		
	
		
			
				|  |  |  |  |   // FIXME magic
 | 
		
	
		
			
				|  |  |  |  |   char signature[4]; | 
		
	
		
			
				|  |  |  |  |   fread(signature, 1, 4, instance->fd); | 
		
	
		
			
				|  |  |  |  |   // Check file signature
 | 
		
	
		
			
				|  |  |  |  |   char signature[YDB_TABLE_FILE_SIGN_SIZE]; | 
		
	
		
			
				|  |  |  |  |   fread(signature, 1, YDB_TABLE_FILE_SIGN_SIZE, instance->fd); | 
		
	
		
			
				|  |  |  |  |   int signature_match = memcmp(signature, YDB_TABLE_FILE_SIGN, 3) == 0; | 
		
	
		
			
				|  |  |  |  |   if (!signature_match) { | 
		
	
		
			
				|  |  |  |  |     return YDB_ERR_TABLE_DATA_CORRUPTED; | 
		
	
	
		
			
				
					|  |  |  | @ -163,18 +171,18 @@ YDB_Error ydb_load_table(YDB_Engine *instance, const char *path) { | 
		
	
		
			
				|  |  |  |  |   fread(&(instance->ver_major), sizeof(instance->ver_major), 1, instance->fd); | 
		
	
		
			
				|  |  |  |  |   fread(&(instance->ver_minor), sizeof(instance->ver_minor), 1, instance->fd); | 
		
	
		
			
				|  |  |  |  | 
 | 
		
	
		
			
				|  |  |  |  |   if (instance->ver_major != 0) { // TODO proper version check
 | 
		
	
		
			
				|  |  |  |  |   if (instance->ver_major != 1) { // TODO proper version check!
 | 
		
	
		
			
				|  |  |  |  |     return YDB_ERR_TABLE_DATA_VERSION_MISMATCH; | 
		
	
		
			
				|  |  |  |  |   } | 
		
	
		
			
				|  |  |  |  | 
 | 
		
	
		
			
				|  |  |  |  |   // Check consistency of a page
 | 
		
	
		
			
				|  |  |  |  |   switch (signature[3]) { | 
		
	
		
			
				|  |  |  |  |     case '!': | 
		
	
		
			
				|  |  |  |  |       break; | 
		
	
		
			
				|  |  |  |  |     case '?': | 
		
	
		
			
				|  |  |  |  |       if (instance->ver_minor < 2) { | 
		
	
		
			
				|  |  |  |  |         return YDB_ERR_TABLE_DATA_CORRUPTED; | 
		
	
		
			
				|  |  |  |  |       } | 
		
	
		
			
				|  |  |  |  |       // TODO mark table as possibly corrupted and start rollback from journal
 | 
		
	
		
			
				|  |  |  |  |       // FIXME
 | 
		
	
		
			
				|  |  |  |  |       return YDB_ERR_TABLE_DATA_CORRUPTED; | 
		
	
		
			
				|  |  |  |  |       break; | 
		
	
		
			
				|  |  |  |  |     default: | 
		
	
		
			
				|  |  |  |  |       return YDB_ERR_TABLE_DATA_CORRUPTED; | 
		
	
	
		
			
				
					|  |  |  | @ -210,7 +218,6 @@ YDB_Error ydb_unload_table(YDB_Engine *instance) { | 
		
	
		
			
				|  |  |  |  |   i->prev_page_offset = 0; | 
		
	
		
			
				|  |  |  |  |   i->curr_page_offset = 0; | 
		
	
		
			
				|  |  |  |  |   i->next_page_offset = 0; | 
		
	
		
			
				|  |  |  |  |   i->current_page_index = 0; | 
		
	
		
			
				|  |  |  |  |   ydb_page_free(i->curr_page); | 
		
	
		
			
				|  |  |  |  |   i->curr_page = NULL; | 
		
	
		
			
				|  |  |  |  |   free(i->filename); | 
		
	
	
		
			
				
					|  |  |  | @ -226,7 +233,7 @@ YDB_Error ydb_create_table(YDB_Engine *instance, const char *path) { | 
		
	
		
			
				|  |  |  |  |   THROW_IF_NULL(instance, YDB_ERR_INSTANCE_NOT_INITIALIZED); | 
		
	
		
			
				|  |  |  |  |   THROW_IF_NULL(!instance->in_use, YDB_ERR_INSTANCE_IN_USE); | 
		
	
		
			
				|  |  |  |  | 
 | 
		
	
		
			
				|  |  |  |  |   if (access(instance->filename, F_OK) != -1) { | 
		
	
		
			
				|  |  |  |  |   if (access(path, F_OK) != -1) { | 
		
	
		
			
				|  |  |  |  |     // TODO: Windows does not check W_OK correctly, use other methods.
 | 
		
	
		
			
				|  |  |  |  |     // TODO: if can't read/write, throw other error
 | 
		
	
		
			
				|  |  |  |  |     return YDB_ERR_TABLE_EXIST; | 
		
	
	
		
			
				
					|  |  |  | @ -234,11 +241,11 @@ YDB_Error ydb_create_table(YDB_Engine *instance, const char *path) { | 
		
	
		
			
				|  |  |  |  | 
 | 
		
	
		
			
				|  |  |  |  |   FILE *f = fopen(path, "wb"); | 
		
	
		
			
				|  |  |  |  | 
 | 
		
	
		
			
				|  |  |  |  |   char tpl[] = YDB_TABLE_FILE_SIGN | 
		
	
		
			
				|  |  |  |  |                "\x00\x02" | 
		
	
		
			
				|  |  |  |  |                "\x1E\x00\x00\x00\x00\x00\x00\x00" | 
		
	
		
			
				|  |  |  |  |                "\x1E\x00\x00\x00\x00\x00\x00\x00" | 
		
	
		
			
				|  |  |  |  |                "\x00\x00\x00\x00\x00\x00\x00\x00"; | 
		
	
		
			
				|  |  |  |  |   char tpl[] = YDB_TABLE_FILE_SIGN                 // File signature
 | 
		
	
		
			
				|  |  |  |  |                "\x01\x00"                          // File version FIXME magic
 | 
		
	
		
			
				|  |  |  |  |                "\x1E\x00\x00\x00\x00\x00\x00\x00"  // First page offset
 | 
		
	
		
			
				|  |  |  |  |                "\x1E\x00\x00\x00\x00\x00\x00\x00"  // Last page offset
 | 
		
	
		
			
				|  |  |  |  |                "\x00\x00\x00\x00\x00\x00\x00\x00"; // Last free page offset
 | 
		
	
		
			
				|  |  |  |  |   fwrite(tpl, sizeof(tpl)-1, 1, f); | 
		
	
		
			
				|  |  |  |  | 
 | 
		
	
		
			
				|  |  |  |  |   char *first_page = calloc(1, YDB_TABLE_PAGE_SIZE); | 
		
	
	
		
			
				
					|  |  |  | @ -250,10 +257,13 @@ YDB_Error ydb_create_table(YDB_Engine *instance, const char *path) { | 
		
	
		
			
				|  |  |  |  |   return ydb_load_table(instance, path); | 
		
	
		
			
				|  |  |  |  | } | 
		
	
		
			
				|  |  |  |  | 
 | 
		
	
		
			
				|  |  |  |  | int64_t ydb_tell_page(YDB_Engine *instance) { | 
		
	
		
			
				|  |  |  |  | YDB_Error ydb_prev_page(YDB_Engine *instance) { | 
		
	
		
			
				|  |  |  |  |   THROW_IF_NULL(instance, YDB_ERR_INSTANCE_NOT_INITIALIZED); | 
		
	
		
			
				|  |  |  |  |   THROW_IF_NULL(instance->in_use, YDB_ERR_INSTANCE_NOT_IN_USE); | 
		
	
		
			
				|  |  |  |  |   return instance->current_page_index; | 
		
	
		
			
				|  |  |  |  |   THROW_IF_NULL(instance->prev_page_offset, YDB_ERR_NO_MORE_PAGES); | 
		
	
		
			
				|  |  |  |  | 
 | 
		
	
		
			
				|  |  |  |  |   instance->curr_page_offset = instance->prev_page_offset; | 
		
	
		
			
				|  |  |  |  |   return __ydb_read_page(instance); | 
		
	
		
			
				|  |  |  |  | } | 
		
	
		
			
				|  |  |  |  | 
 | 
		
	
		
			
				|  |  |  |  | YDB_Error ydb_next_page(YDB_Engine *instance) { | 
		
	
	
		
			
				
					|  |  |  | @ -261,48 +271,10 @@ YDB_Error ydb_next_page(YDB_Engine *instance) { | 
		
	
		
			
				|  |  |  |  |   THROW_IF_NULL(instance->in_use, YDB_ERR_INSTANCE_NOT_IN_USE); | 
		
	
		
			
				|  |  |  |  |   THROW_IF_NULL(instance->next_page_offset, YDB_ERR_NO_MORE_PAGES); | 
		
	
		
			
				|  |  |  |  | 
 | 
		
	
		
			
				|  |  |  |  |   instance->prev_page_offset = instance->curr_page_offset; | 
		
	
		
			
				|  |  |  |  |   instance->curr_page_offset = instance->next_page_offset; | 
		
	
		
			
				|  |  |  |  |   ++instance->current_page_index; | 
		
	
		
			
				|  |  |  |  |   return __ydb_read_page(instance); | 
		
	
		
			
				|  |  |  |  | } | 
		
	
		
			
				|  |  |  |  | 
 | 
		
	
		
			
				|  |  |  |  | YDB_Error ydb_seek_page(YDB_Engine *instance, int64_t index) { | 
		
	
		
			
				|  |  |  |  |   THROW_IF_NULL(instance, YDB_ERR_INSTANCE_NOT_INITIALIZED); | 
		
	
		
			
				|  |  |  |  |   THROW_IF_NULL(instance->in_use, YDB_ERR_INSTANCE_NOT_IN_USE); | 
		
	
		
			
				|  |  |  |  |   if (index < 0) { | 
		
	
		
			
				|  |  |  |  |     return YDB_ERR_PAGE_INDEX_OUT_OF_RANGE; | 
		
	
		
			
				|  |  |  |  |   } | 
		
	
		
			
				|  |  |  |  | 
 | 
		
	
		
			
				|  |  |  |  |   // Save current state
 | 
		
	
		
			
				|  |  |  |  |   YDB_Offset prev_page_offset = instance->prev_page_offset; | 
		
	
		
			
				|  |  |  |  |   YDB_Offset curr_page_offset = instance->curr_page_offset; | 
		
	
		
			
				|  |  |  |  |   YDB_Offset next_page_offset = instance->next_page_offset; | 
		
	
		
			
				|  |  |  |  |   int64_t curr_page_index = instance->current_page_index; | 
		
	
		
			
				|  |  |  |  | 
 | 
		
	
		
			
				|  |  |  |  |   if (index < instance->current_page_index) { | 
		
	
		
			
				|  |  |  |  |     instance->prev_page_offset = 0; | 
		
	
		
			
				|  |  |  |  |     instance->curr_page_offset = instance->first_page_offset; | 
		
	
		
			
				|  |  |  |  |     instance->current_page_index = 0; | 
		
	
		
			
				|  |  |  |  |     __ydb_read_page(instance); | 
		
	
		
			
				|  |  |  |  |   } | 
		
	
		
			
				|  |  |  |  | 
 | 
		
	
		
			
				|  |  |  |  |   while (instance->current_page_index != index) { | 
		
	
		
			
				|  |  |  |  |     YDB_Error status = ydb_next_page(instance); | 
		
	
		
			
				|  |  |  |  | 
 | 
		
	
		
			
				|  |  |  |  |     if (status != YDB_ERR_SUCCESS) { | 
		
	
		
			
				|  |  |  |  |       instance->prev_page_offset = prev_page_offset; | 
		
	
		
			
				|  |  |  |  |       instance->curr_page_offset = curr_page_offset; | 
		
	
		
			
				|  |  |  |  |       instance->next_page_offset = next_page_offset; | 
		
	
		
			
				|  |  |  |  |       instance->current_page_index = curr_page_index; | 
		
	
		
			
				|  |  |  |  |       __ydb_read_page(instance); | 
		
	
		
			
				|  |  |  |  |       return status; | 
		
	
		
			
				|  |  |  |  |     } | 
		
	
		
			
				|  |  |  |  |   } | 
		
	
		
			
				|  |  |  |  | 
 | 
		
	
		
			
				|  |  |  |  |   return YDB_ERR_SUCCESS; | 
		
	
		
			
				|  |  |  |  | } | 
		
	
		
			
				|  |  |  |  | 
 | 
		
	
		
			
				|  |  |  |  | YDB_TablePage* ydb_get_current_page(YDB_Engine *instance) { | 
		
	
		
			
				|  |  |  |  |   THROW_IF_NULL(instance, NULL); | 
		
	
		
			
				|  |  |  |  |   THROW_IF_NULL(instance->in_use, NULL); | 
		
	
	
		
			
				
					|  |  |  | @ -314,21 +286,20 @@ YDB_Error ydb_append_page(YDB_Engine* instance, YDB_TablePage* page) { | 
		
	
		
			
				|  |  |  |  |   THROW_IF_NULL(instance, YDB_ERR_INSTANCE_NOT_INITIALIZED); | 
		
	
		
			
				|  |  |  |  |   THROW_IF_NULL(instance->in_use, YDB_ERR_INSTANCE_NOT_IN_USE); | 
		
	
		
			
				|  |  |  |  | 
 | 
		
	
		
			
				|  |  |  |  |   __ydb_allocate_page_and_seek(instance); | 
		
	
		
			
				|  |  |  |  |   YDB_Offset new_page_offset = __ydb_allocate_page_and_seek(instance); | 
		
	
		
			
				|  |  |  |  | 
 | 
		
	
		
			
				|  |  |  |  |   YDB_PageSize rc = ydb_page_row_count_get(page); | 
		
	
		
			
				|  |  |  |  |   YDB_PageSize rc_le = TO_LE(rc); | 
		
	
		
			
				|  |  |  |  |   YDB_Flags f = ydb_page_flags_get(page); | 
		
	
		
			
				|  |  |  |  |   YDB_Offset next = 0; | 
		
	
		
			
				|  |  |  |  |   char d[YDB_TABLE_PAGE_SIZE - YDB_v0_page_data_offset]; | 
		
	
		
			
				|  |  |  |  | 
 | 
		
	
		
			
				|  |  |  |  |   char d[YDB_TABLE_PAGE_SIZE - YDB_v1_page_data_offset]; | 
		
	
		
			
				|  |  |  |  |   ydb_page_data_seek(page, 0); | 
		
	
		
			
				|  |  |  |  |   if (ydb_page_data_read(page, d, sizeof(d))) { | 
		
	
		
			
				|  |  |  |  |     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); | 
		
	
		
			
				|  |  |  |  |   fseek(instance->fd, new_page_offset + YDB_v1_page_row_count_offset, SEEK_SET); | 
		
	
		
			
				|  |  |  |  |   fwrite(&rc_le, sizeof(rc), 1, instance->fd); | 
		
	
		
			
				|  |  |  |  |   fwrite(d, sizeof(d), 1, instance->fd); | 
		
	
		
			
				|  |  |  |  | 
 | 
		
	
	
		
			
				
					|  |  |  | @ -355,8 +326,8 @@ YDB_Error ydb_replace_current_page(YDB_Engine *instance, YDB_TablePage *page) { | 
		
	
		
			
				|  |  |  |  |   YDB_Flags page_flags = ydb_page_flags_get(page); | 
		
	
		
			
				|  |  |  |  |   fwrite(&page_flags, sizeof(YDB_Flags), 1, instance->fd); | 
		
	
		
			
				|  |  |  |  | 
 | 
		
	
		
			
				|  |  |  |  |   // Skip next page offset
 | 
		
	
		
			
				|  |  |  |  |   fseek(instance->fd, sizeof(YDB_Offset), SEEK_CUR); | 
		
	
		
			
				|  |  |  |  |   // Skip next and prev page offsets
 | 
		
	
		
			
				|  |  |  |  |   fseek(instance->fd, YDB_v1_page_next_size + YDB_v1_page_prev_size, SEEK_CUR); | 
		
	
		
			
				|  |  |  |  | 
 | 
		
	
		
			
				|  |  |  |  |   // Write row count
 | 
		
	
		
			
				|  |  |  |  |   YDB_PageSize row_cnt = ydb_page_row_count_get(page); | 
		
	
	
		
			
				
					|  |  |  | @ -364,7 +335,7 @@ YDB_Error ydb_replace_current_page(YDB_Engine *instance, YDB_TablePage *page) { | 
		
	
		
			
				|  |  |  |  |   fwrite(&row_cnt_le, sizeof(row_cnt), 1, instance->fd); | 
		
	
		
			
				|  |  |  |  | 
 | 
		
	
		
			
				|  |  |  |  |   // Write data
 | 
		
	
		
			
				|  |  |  |  |   char page_data[YDB_TABLE_PAGE_SIZE - YDB_v0_page_data_offset]; | 
		
	
		
			
				|  |  |  |  |   char page_data[YDB_TABLE_PAGE_SIZE - YDB_v1_page_data_offset]; | 
		
	
		
			
				|  |  |  |  |   ydb_page_data_seek(page, 0); | 
		
	
		
			
				|  |  |  |  |   if (ydb_page_data_read(page, page_data, sizeof(page_data))) { | 
		
	
		
			
				|  |  |  |  |     return YDB_ERR_UNKNOWN; // FIXME
 | 
		
	
	
		
			
				
					|  |  |  | @ -385,9 +356,11 @@ YDB_Error ydb_delete_current_page(YDB_Engine *instance) { | 
		
	
		
			
				|  |  |  |  |   THROW_IF_NULL(instance->in_use, YDB_ERR_INSTANCE_NOT_IN_USE); | 
		
	
		
			
				|  |  |  |  | 
 | 
		
	
		
			
				|  |  |  |  |   if (instance->prev_page_offset == 0 && instance->next_page_offset == 0) { | 
		
	
		
			
				|  |  |  |  |     fseek(instance->fd, instance->curr_page_offset + 9, SEEK_SET); // Skip page flags + next ptr
 | 
		
	
		
			
				|  |  |  |  |     YDB_PageSize tmp = 0; | 
		
	
		
			
				|  |  |  |  |     fwrite(&tmp, sizeof(YDB_PageSize), 1, instance->fd); | 
		
	
		
			
				|  |  |  |  |     // Just clear page header. That's all.
 | 
		
	
		
			
				|  |  |  |  |     fseek(instance->fd, instance->curr_page_offset, SEEK_SET); | 
		
	
		
			
				|  |  |  |  |     char* empty_header = calloc(1, YDB_v1_page_data_offset); | 
		
	
		
			
				|  |  |  |  |     fwrite(empty_header, YDB_v1_page_data_offset, 1, instance->fd); | 
		
	
		
			
				|  |  |  |  |     free(empty_header); | 
		
	
		
			
				|  |  |  |  |     return YDB_ERR_SUCCESS; | 
		
	
		
			
				|  |  |  |  |   } | 
		
	
		
			
				|  |  |  |  | 
 | 
		
	
	
		
			
				
					|  |  |  | @ -400,32 +373,51 @@ YDB_Error ydb_delete_current_page(YDB_Engine *instance) { | 
		
	
		
			
				|  |  |  |  |   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
 | 
		
	
		
			
				|  |  |  |  |   if (instance->prev_page_offset != 0) { | 
		
	
		
			
				|  |  |  |  |     fseek(instance->fd, instance->prev_page_offset + YDB_v1_page_next_offset, SEEK_SET); | 
		
	
		
			
				|  |  |  |  |   } else { | 
		
	
		
			
				|  |  |  |  |     // If it was the first page, rewrite first_page_offset with next_page_offset
 | 
		
	
		
			
				|  |  |  |  |     fseek(instance->fd, YDB_v1_first_page_offset, SEEK_SET); | 
		
	
		
			
				|  |  |  |  |   } | 
		
	
		
			
				|  |  |  |  |   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); | 
		
	
		
			
				|  |  |  |  |     YDB_Offset pp_le = TO_LE(instance->prev_page_offset); | 
		
	
		
			
				|  |  |  |  |     fwrite(&pp_le, sizeof(YDB_Offset), 1, instance->fd); | 
		
	
		
			
				|  |  |  |  |   // Link the next page with previous one (could be null ptr)
 | 
		
	
		
			
				|  |  |  |  |   if (instance->next_page_offset != 0) { | 
		
	
		
			
				|  |  |  |  |     fseek(instance->fd, instance->next_page_offset + YDB_v1_page_prev_offset, SEEK_SET); | 
		
	
		
			
				|  |  |  |  |   } else { | 
		
	
		
			
				|  |  |  |  |     // If it was the last page, rewrite last_page_offset with prev_page_offset
 | 
		
	
		
			
				|  |  |  |  |     fseek(instance->fd, YDB_v1_last_page_offset, SEEK_SET); | 
		
	
		
			
				|  |  |  |  |   } | 
		
	
		
			
				|  |  |  |  |   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); | 
		
	
		
			
				|  |  |  |  |   fseek(instance->fd, YDB_v1_last_free_page_offset, SEEK_SET); | 
		
	
		
			
				|  |  |  |  |   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; | 
		
	
		
			
				|  |  |  |  |   if (ydb_seek_page(instance, ind + 1)) { | 
		
	
		
			
				|  |  |  |  |     return YDB_ERR_UNKNOWN; | 
		
	
		
			
				|  |  |  |  |   // Seek to the next page if it's not the last, else seek to the previous one
 | 
		
	
		
			
				|  |  |  |  |   if (instance->next_page_offset != 0) { | 
		
	
		
			
				|  |  |  |  |     instance->curr_page_offset = instance->next_page_offset; | 
		
	
		
			
				|  |  |  |  |   } else { | 
		
	
		
			
				|  |  |  |  |     instance->curr_page_offset = instance->prev_page_offset; | 
		
	
		
			
				|  |  |  |  |   } | 
		
	
		
			
				|  |  |  |  |   return __ydb_read_page(instance); | 
		
	
		
			
				|  |  |  |  | } | 
		
	
		
			
				|  |  |  |  | 
 | 
		
	
		
			
				|  |  |  |  |   return YDB_ERR_SUCCESS; | 
		
	
		
			
				|  |  |  |  | YDB_Error ydb_seek_to_end(YDB_Engine *instance) { | 
		
	
		
			
				|  |  |  |  |   THROW_IF_NULL(instance, YDB_ERR_INSTANCE_NOT_INITIALIZED); | 
		
	
		
			
				|  |  |  |  |   THROW_IF_NULL(instance->in_use, YDB_ERR_INSTANCE_NOT_IN_USE); | 
		
	
		
			
				|  |  |  |  | 
 | 
		
	
		
			
				|  |  |  |  |   if (instance->next_page_offset == 0) | 
		
	
		
			
				|  |  |  |  |     return YDB_ERR_SUCCESS; | 
		
	
		
			
				|  |  |  |  | 
 | 
		
	
		
			
				|  |  |  |  |   instance->curr_page_offset = instance->next_page_offset; | 
		
	
		
			
				|  |  |  |  |   return __ydb_read_page(instance); | 
		
	
		
			
				|  |  |  |  | } | 
		
	
		
			
				|  |  |  |  | 
 | 
		
	
		
			
				|  |  |  |  | #ifdef __cplusplus | 
		
	
	
		
			
				
					|  |  |  | 
 |