1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216
|
#include "graphfile.h"
#include <sys/types.h>
#include <unistd.h>
#include <string.h>
#include <stdio.h>
#include <stdint.h>
#define IF_ERR_RETURN(result) do { if(-1 == (result)) return -1; } while(0)
static int writen(FILE *f, const void *buffer, size_t buffer_size)
{
/* TODO: Really writen here... */
if(buffer_size != fwrite(buffer, 1, buffer_size, f)) {
return -1;
}
return 0;
}
static int readn(FILE *f, void *buffer, size_t buffer_size)
{
/* TODO: Really readn here... */
if(buffer_size != fread(buffer, 1, buffer_size, f)) {
return -1;
}
return 0;
}
#define GNUMBER_BARKER_SIZE (3)
#define GNUMBER_BARKER ((1UL<<(8*GNUMBER_BARKER_SIZE))-1)
static unsigned char gnumber_barker[GNUMBER_BARKER_SIZE] = { 0xFF, 0xFF, 0xFF };
static graphfile_size_t write_gnumber(FILE *f, uint64_t number64)
{
/* Endianness is crap :-(
To overcome it, lets do this: */
if(number64 < GNUMBER_BARKER) {
unsigned char gnumber[GNUMBER_BARKER_SIZE] = {
(uint8_t)(number64 >> 0),
(uint8_t)(number64 >> 8),
(uint8_t)(number64 >> 16)
};
IF_ERR_RETURN(writen(f, gnumber, sizeof gnumber));
return sizeof gnumber;
}
IF_ERR_RETURN(writen(f, gnumber_barker, sizeof gnumber_barker));
IF_ERR_RETURN(writen(f, &number64, sizeof number64));
return sizeof gnumber_barker + sizeof number64;
}
static graphfile_size_t read_gnumber(FILE *f, uint64_t *p_number64)
{
unsigned char gnumber[GNUMBER_BARKER_SIZE];
IF_ERR_RETURN(readn(f, gnumber, sizeof gnumber));
if(0 != memcmp(gnumber, gnumber_barker, GNUMBER_BARKER_SIZE)) {
(*p_number64) = ((((uint64_t)gnumber[0]) << 0) +
(((uint64_t)gnumber[1]) << 8) +
(((uint64_t)gnumber[2]) << 16));
return sizeof gnumber;
}
IF_ERR_RETURN(readn(f, p_number64, sizeof *p_number64));
return sizeof gnumber + sizeof *p_number64;
}
static int safe_fileno(FILE *f) {
/* Must fflush before messing with the fd of a (FILE*) */
if(0 != fflush(f)) {
return -1;
}
/* May return -1 */
return fileno(f);
}
static int seek(FILE *f, graphfile_offset_t offset) {
int fd = safe_fileno(f);
IF_ERR_RETURN(fd);
if(((graphfile_offset_t)-1) == graphfile_seek(fd, offset, SEEK_SET)) {
return -1;
}
return 0;
}
static graphfile_offset_t tell(FILE *f) {
int fd = safe_fileno(f);
IF_ERR_RETURN(fd);
return graphfile_seek(fd, 0, SEEK_CUR);
}
int graphfile_writer_init(graphfile_writer_t *graphfile_writer, FILE *file)
{
graphfile_offset_t offset;
graphfile_writer->file = file;
if(-1 == fseek(file, 0, SEEK_END)) {
/* A seekable file must be used */
return -1;
}
offset = tell(file);
if(((graphfile_offset_t)-1 == offset) || (offset > 0)) {
/* An empty file must be used */
return -1;
}
/* POSIX allows seeking to beyond the end of the file */
IF_ERR_RETURN(seek(file, sizeof(graphfile_linkable_t)));
graphfile_writer->offset = sizeof(graphfile_linkable_t);
return 0;
}
int graphfile_writer_set_root(graphfile_writer_t *graphfile_writer,
graphfile_linkable_t *root)
{
FILE *file = graphfile_writer->file;
IF_ERR_RETURN(seek(file, 0));
IF_ERR_RETURN(writen(file, root, sizeof *root));
if(0 != fseek(file, 0, SEEK_END)) {
return -1;
}
return 0;
}
void graphfile_writer_fini(graphfile_writer_t *graphfile_writer)
{
/* Not much to do here */
}
int graphfile_writer_write(graphfile_writer_t *graphfile_writer,
char *buffer, graphfile_size_t buffer_length,
graphfile_linkable_t linkables[], graphfile_size_t linkable_count,
graphfile_linkable_t *result_linkable)
{
graphfile_size_t i;
graphfile_size_t size;
FILE *file = graphfile_writer->file;
graphfile_offset_t offset = graphfile_writer->offset;
/* TODO: Clean up all this code duplication */
IF_ERR_RETURN(size = write_gnumber(file, buffer_length));
graphfile_writer->offset += size;
IF_ERR_RETURN(writen(file, buffer, buffer_length));
graphfile_writer->offset += buffer_length;
IF_ERR_RETURN(size = write_gnumber(file, linkable_count));
graphfile_writer->offset += size;
for(i = 0; i < linkable_count; ++i) {
IF_ERR_RETURN(size = write_gnumber(file, offset - linkables[i].offset));
graphfile_writer->offset += size;
}
result_linkable->offset = offset;
return 0;
}
int graphfile_reader_init(graphfile_reader_t *graphfile_reader, FILE *file,
graphfile_linkable_t *result_root)
{
graphfile_reader->file = file;
/* A seekable file must be used */
IF_ERR_RETURN(seek(file, 0));
/* A readable, coherent file must be used, so it must have a
* readable root. */
IF_ERR_RETURN(readn(file, result_root, sizeof *result_root));
if(0 == result_root->offset) {
/* Root cannot be 0. If it is 0, it means that the file was
* never set_root'd properly, and is corrupt. */
return -1;
}
return 0;
}
void graphfile_reader_fini(graphfile_reader_t *graphfile_reader)
{
/* Nothing to do here */
}
#define UNSAFE_MIN(a, b) (((a) <= (b)) ? (a) : (b))
int graphfile_reader_read(graphfile_reader_t *graphfile_reader,
graphfile_linkable_t *node,
char *result_buffer, graphfile_size_t max_buffer_length,
graphfile_size_t *result_buffer_length,
graphfile_linkable_t result_linkables[], graphfile_size_t max_linkable_count,
graphfile_size_t *result_linkables_count)
{
graphfile_size_t i;
graphfile_size_t min_linkable_count;
uint64_t relative_offset;
uint64_t buffer_length;
uint64_t linkables_count;
graphfile_size_t size;
FILE *file = graphfile_reader->file;
IF_ERR_RETURN(seek(file, node->offset));
IF_ERR_RETURN(size = read_gnumber(file, &buffer_length));
IF_ERR_RETURN(readn(file, result_buffer, UNSAFE_MIN(max_buffer_length, buffer_length)));
IF_ERR_RETURN(seek(file, node->offset + size + buffer_length));
IF_ERR_RETURN(read_gnumber(file, &linkables_count));
min_linkable_count = UNSAFE_MIN(max_linkable_count, linkables_count);
for(i = 0; i < min_linkable_count; ++i) {
IF_ERR_RETURN(read_gnumber(file, &relative_offset));
result_linkables[i].offset = node->offset - relative_offset;
}
(*result_linkables_count) = linkables_count;
(*result_buffer_length) = buffer_length;
return 0;
}
|