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_KVAL[CHUNK_ID_LEN] = "KVAL"; /* Key/Value 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_value(RDFF file,
123 const uint32_t chunk_size = sizeof(key) + sizeof(type) + sizeof(size) + size;
124 WRITE(CHUNK_KVAL, CHUNK_ID_LEN, 1, file->fd);
125 WRITE(&chunk_size, sizeof(chunk_size), 1, file->fd);
126 WRITE(&key, sizeof(key), 1, file->fd);
127 WRITE(&type, sizeof(type), 1, file->fd);
128 WRITE(&size, sizeof(size), 1, file->fd);
129 WRITE(value, size, 1, file->fd);
131 WRITE("", 1, 1, file->fd); /* write pad */
133 file->size += 8 + chunk_size;
134 return RDFF_STATUS_OK;
138 rdff_read_chunk(RDFF file,
142 return RDFF_STATUS_EOF;
144 #define READ(ptr, size, nmemb, stream) \
145 if (fread(ptr, size, nmemb, stream) != nmemb) { \
146 return RDFF_STATUS_CORRUPT; \
149 const uint32_t alloc_size = (*buf)->size;
151 READ((*buf)->type, sizeof((*buf)->type), 1, file->fd);
152 READ(&(*buf)->size, sizeof((*buf)->size), 1, file->fd);
153 if ((*buf)->size > alloc_size) {
154 *buf = realloc(*buf, sizeof(RDFFChunk) + (*buf)->size);
156 READ((*buf)->data, (*buf)->size, 1, file->fd);
157 if (((*buf)->size % 2)) {
159 READ(&pad, 1, 1, file->fd); /* skip pad */
161 return RDFF_STATUS_OK;
165 rdff_close(RDFF file)
169 fseek(file->fd, 4, SEEK_SET);
170 if (fwrite(&file->size, sizeof(file->size), 1, file->fd) != 1) {
171 fprintf(stderr, "failed to write RIFF header size\n");
183 main(int argc, char** argv)
186 fprintf(stderr, "Usage: %s FILENAME\n", argv[0]);
190 const char* const filename = argv[1];
192 RDFF file = rdff_open(filename, true);
196 static const int N_URIS = 16;
197 static const int N_RECORDS = 16;
200 for (int i = 0; i < N_URIS; ++i) {
201 snprintf(uri, sizeof(uri), "http://example.org/uri%02d", i + 1);
202 rdff_write_uri(file, i + 1, uri, strlen(uri) + 1);
206 for (int i = 0; i < N_RECORDS; ++i) {
207 snprintf(val, sizeof(val), "VAL%02d", i);
208 rdff_write_value(file,
217 file = rdff_open(filename, false);
221 RDFFChunk* chunk = malloc(sizeof(RDFFChunk));
223 for (int i = 0; i < N_URIS; ++i) {
224 if (rdff_read_chunk(file, &chunk)
225 || strncmp(chunk->type, "URID", 4)) {
226 fprintf(stderr, "error: expected URID chunk\n");
229 RDFFURIChunk* body = (RDFFURIChunk*)chunk->data;
230 printf("URI: %s\n", body->uri);
233 for (int i = 0; i < N_RECORDS; ++i) {
234 if (rdff_read_chunk(file, &chunk)
235 || strncmp(chunk->type, "KVAL", 4)) {
236 fprintf(stderr, "error: expected KVAL chunk\n");
239 RDFFValueChunk* body = (RDFFValueChunk*)chunk->data;
240 printf("KEY %d = %s\n", body->key, body->value);
250 fprintf(stderr, "Test failed\n");