3 Copyright 2011 David Robillard <http://drobilla.net>
5 Redistribution and use in source and binary forms, with or without
6 modification, are permitted provided that the following conditions are met:
8 1. Redistributions of source code must retain the above copyright notice,
9 this list of conditions and the following disclaimer.
11 2. Redistributions in binary form must reproduce the above copyright
12 notice, this list of conditions and the following disclaimer in the
13 documentation and/or other materials provided with the distribution.
15 THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
16 INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
17 AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
18 AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
19 OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
20 SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
21 INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
22 CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
23 ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
24 THE POSSIBILITY OF SUCH DAMAGE.
35 #define CHUNK_ID_LEN 4
37 static const char FILE_TYPE[CHUNK_ID_LEN] = "RDFF"; /* RDFF File ID */
38 static const char CHUNK_TRIP[CHUNK_ID_LEN] = "trip"; /* Triple Chunk ID */
39 static const char CHUNK_URID[CHUNK_ID_LEN] = "urid"; /* URI-ID Chunk ID*/
48 rdff_open(const char* path, bool write)
50 FILE* fd = fopen(path, (write ? "w" : "r"));
52 fprintf(stderr, "%s\n", strerror(errno));
59 fwrite("RIFF", CHUNK_ID_LEN, 1, fd); /* RIFF chunk ID */
60 fwrite(&size, sizeof(size), 1, fd); /* RIFF chunk size */
61 fwrite(FILE_TYPE, CHUNK_ID_LEN, 1, fd); /* File type */
63 char magic[CHUNK_ID_LEN];
64 if (fread(magic, CHUNK_ID_LEN, 1, fd) != 1
65 || strncmp(magic, "RIFF", CHUNK_ID_LEN)) {
67 fprintf(stderr, "%s: error: not a RIFF file\n", path);
71 if (fread(&size, sizeof(size), 1, fd) != 1) {
73 fprintf(stderr, "%s: error: missing RIFF chunk size\n", path);
77 if (fread(magic, CHUNK_ID_LEN, 1, fd) != 1
78 || strncmp(magic, FILE_TYPE, CHUNK_ID_LEN)) {
80 fprintf(stderr, "%s: error: not an %s RIFF file\n",
86 RDFF ret = (RDFF)malloc(sizeof(struct _RDFF));
93 #define WRITE(ptr, size, nmemb, stream) \
94 if (fwrite(ptr, size, nmemb, stream) != nmemb) { \
95 return RDFF_STATUS_UNKNOWN_ERROR; \
99 rdff_write_uri(RDFF file,
104 const uint32_t chunk_size = sizeof(id) + len + 1;
105 WRITE(CHUNK_URID, CHUNK_ID_LEN, 1, file->fd);
106 WRITE(&chunk_size, sizeof(chunk_size), 1, file->fd);
107 WRITE(&id, sizeof(id), 1, file->fd);
108 WRITE(uri, len + 1, 1, file->fd);
109 if ((chunk_size % 2)) {
110 WRITE("", 1, 1, file->fd); /* pad */
112 file->size += 8 + chunk_size;
113 return RDFF_STATUS_OK;
117 rdff_write_triple(RDFF file,
120 uint32_t object_type,
121 uint32_t object_size,
124 const uint32_t chunk_size = sizeof(RDFFTripleChunk) + object_size;
125 WRITE(CHUNK_TRIP, CHUNK_ID_LEN, 1, file->fd);
126 WRITE(&chunk_size, sizeof(chunk_size), 1, file->fd);
127 WRITE(&subject, sizeof(subject), 1, file->fd);
128 WRITE(&predicate, sizeof(predicate), 1, file->fd);
129 WRITE(&object_type, sizeof(object_type), 1, file->fd);
130 WRITE(&object_size, sizeof(object_size), 1, file->fd);
131 WRITE(object, object_size, 1, file->fd);
132 if ((object_size % 2)) {
133 WRITE("", 1, 1, file->fd); /* write pad */
135 file->size += 8 + chunk_size;
136 return RDFF_STATUS_OK;
140 rdff_read_chunk(RDFF file,
144 return RDFF_STATUS_EOF;
146 #define READ(ptr, size, nmemb, stream) \
147 if (fread(ptr, size, nmemb, stream) != nmemb) { \
148 return RDFF_STATUS_CORRUPT; \
151 const uint32_t alloc_size = (*buf)->size;
153 READ((*buf)->type, sizeof((*buf)->type), 1, file->fd);
154 READ(&(*buf)->size, sizeof((*buf)->size), 1, file->fd);
155 if ((*buf)->size > alloc_size) {
156 *buf = realloc(*buf, sizeof(RDFFChunk) + (*buf)->size);
158 READ((*buf)->data, (*buf)->size, 1, file->fd);
159 if (((*buf)->size % 2)) {
161 READ(&pad, 1, 1, file->fd); /* skip pad */
163 return RDFF_STATUS_OK;
167 rdff_chunk_is_uri(RDFFChunk* chunk)
170 return !strncmp(chunk->type, CHUNK_URID, CHUNK_ID_LEN);
174 rdff_chunk_is_triple(RDFFChunk* chunk)
176 return !strncmp(chunk->type, CHUNK_TRIP, CHUNK_ID_LEN);
180 rdff_close(RDFF file)
184 fseek(file->fd, 4, SEEK_SET);
185 if (fwrite(&file->size, sizeof(file->size), 1, file->fd) != 1) {
186 fprintf(stderr, "failed to write RIFF header size\n");
198 main(int argc, char** argv)
201 fprintf(stderr, "Usage: %s FILENAME\n", argv[0]);
205 const char* const filename = argv[1];
207 RDFF file = rdff_open(filename, true);
211 static const int N_URIS = 16;
212 static const int N_RECORDS = 16;
215 for (int i = 0; i < N_URIS; ++i) {
216 snprintf(uri, sizeof(uri), "http://example.org/uri%02d", i + 1);
217 rdff_write_uri(file, i + 1, strlen(uri), uri);
221 for (int i = 0; i < N_RECORDS; ++i) {
222 snprintf(val, sizeof(val), "VAL%02d", i);
223 rdff_write_triple(file,
233 file = rdff_open(filename, false);
237 RDFFChunk* chunk = malloc(sizeof(RDFFChunk));
239 for (int i = 0; i < N_URIS; ++i) {
240 if (rdff_read_chunk(file, &chunk)
241 || strncmp(chunk->type, CHUNK_URID, 4)) {
242 fprintf(stderr, "error: expected %s chunk\n", CHUNK_URID);
245 RDFFURIChunk* body = (RDFFURIChunk*)chunk->data;
246 printf("URI: %s\n", body->uri);
249 for (int i = 0; i < N_RECORDS; ++i) {
250 if (rdff_read_chunk(file, &chunk)
251 || strncmp(chunk->type, CHUNK_TRIP, 4)) {
252 fprintf(stderr, "error: expected %s chunk\n", CHUNK_TRIP);
255 RDFFTripleChunk* body = (RDFFTripleChunk*)chunk->data;
256 printf("KEY %d = %s\n", body->predicate, body->object);
266 fprintf(stderr, "Test failed\n");