fix crash when copy'ing latent plugins
[ardour.git] / libs / ardour / rdff.c
1 /*
2   RDFF - RDF in RIFF
3   Copyright 2011 David Robillard <http://drobilla.net>
4
5   Permission to use, copy, modify, and/or distribute this software for any
6   purpose with or without fee is hereby granted, provided that the above
7   copyright notice and this permission notice appear in all copies.
8
9   THIS SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
10   WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11   MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
12   ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13   WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14   ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
15   OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
16 */
17
18 #include <assert.h>
19 #include <errno.h>
20 #include <stdio.h>
21 #include <stdlib.h>
22 #include <string.h>
23
24 #include "rdff.h"
25
26 #define CHUNK_ID_LEN 4
27
28 static const char FILE_TYPE[CHUNK_ID_LEN]  = "RDFF";  /* RDFF File ID */
29 static const char CHUNK_TRIP[CHUNK_ID_LEN] = "trip";  /* Triple Chunk ID */
30 static const char CHUNK_URID[CHUNK_ID_LEN] = "urid";  /* URI-ID Chunk ID*/
31
32 struct _RDFF {
33         FILE*    fd;
34         uint32_t size;
35         bool     write;
36 };
37
38 RDFF
39 rdff_open(const char* path, bool write)
40 {
41         FILE* fd = fopen(path, (write ? "w" : "r"));
42         if (!fd) {
43                 fprintf(stderr, "%s\n", strerror(errno));
44                 return NULL;
45         }
46
47         uint32_t size = 0;
48
49         if (write) {
50                 fwrite("RIFF", CHUNK_ID_LEN, 1, fd);    /* RIFF chunk ID */
51                 fwrite(&size, sizeof(size), 1, fd);     /* RIFF chunk size */
52                 fwrite(FILE_TYPE, CHUNK_ID_LEN, 1, fd); /* File type */
53         } else {
54                 char magic[CHUNK_ID_LEN];
55                 if (fread(magic, CHUNK_ID_LEN, 1, fd) != 1
56                     || strncmp(magic, "RIFF", CHUNK_ID_LEN)) {
57                         fclose(fd);
58                         fprintf(stderr, "%s: error: not a RIFF file\n", path);
59                         return NULL;
60                 }
61
62                 if (fread(&size, sizeof(size), 1, fd) != 1) {
63                         fclose(fd);
64                         fprintf(stderr, "%s: error: missing RIFF chunk size\n", path);
65                         return NULL;
66                 }
67
68                 if (fread(magic, CHUNK_ID_LEN, 1, fd) != 1
69                     || strncmp(magic, FILE_TYPE, CHUNK_ID_LEN)) {
70                         fclose(fd);
71                         fprintf(stderr, "%s: error: not an %s RIFF file\n",
72                                 FILE_TYPE, path);
73                         return NULL;
74                 }
75         }
76
77         RDFF ret = (RDFF)malloc(sizeof(struct _RDFF));
78         ret->fd    = fd;
79         ret->size  = size;
80         ret->write = write;
81         return ret;
82 }
83
84 #define WRITE(ptr, size, nmemb, stream) \
85         if (fwrite(ptr, size, nmemb, stream) != nmemb) { \
86                 return RDFF_STATUS_UNKNOWN_ERROR; \
87         }
88
89 RDFFStatus
90 rdff_write_uri(RDFF        file,
91                uint32_t    id,
92                uint32_t    len,
93                const char* uri)
94 {
95         const uint32_t chunk_size = sizeof(id) + len + 1;
96         WRITE(CHUNK_URID,  CHUNK_ID_LEN,       1, file->fd);
97         WRITE(&chunk_size, sizeof(chunk_size), 1, file->fd);
98         WRITE(&id,         sizeof(id),         1, file->fd);
99         WRITE(uri,         len + 1,            1, file->fd);
100         if ((chunk_size % 2)) {
101                 WRITE("", 1, 1, file->fd);  /* pad */
102         }
103         file->size += 8 + chunk_size;
104         return RDFF_STATUS_OK;
105 }
106
107 RDFFStatus
108 rdff_write_triple(RDFF        file,
109                   uint32_t    subject,
110                   uint32_t    predicate,
111                   uint32_t    object_type,
112                   uint32_t    object_size,
113                   const void* object)
114 {
115         const uint32_t chunk_size = sizeof(RDFFTripleChunk) + object_size;
116         WRITE(CHUNK_TRIP,   CHUNK_ID_LEN,        1, file->fd);
117         WRITE(&chunk_size,  sizeof(chunk_size),  1, file->fd);
118         WRITE(&subject,     sizeof(subject),     1, file->fd);
119         WRITE(&predicate,   sizeof(predicate),   1, file->fd);
120         WRITE(&object_type, sizeof(object_type), 1, file->fd);
121         WRITE(&object_size, sizeof(object_size), 1, file->fd);
122         WRITE(object,       object_size,         1, file->fd);
123         if ((object_size % 2)) {
124                 WRITE("", 1, 1, file->fd);  /* write pad */
125         }
126         file->size += 8 + chunk_size;
127         return RDFF_STATUS_OK;
128 }
129
130 RDFFStatus
131 rdff_read_chunk(RDFF        file,
132                 RDFFChunk** buf)
133 {
134         if (feof(file->fd))
135                 return RDFF_STATUS_EOF;
136
137 #define READ(ptr, size, nmemb, stream) \
138         if (fread(ptr, size, nmemb, stream) != nmemb) { \
139                 return RDFF_STATUS_CORRUPT; \
140         }
141
142         const uint32_t alloc_size = (*buf)->size;
143
144         READ((*buf)->type,  sizeof((*buf)->type), 1, file->fd);
145         READ(&(*buf)->size, sizeof((*buf)->size), 1, file->fd);
146         if ((*buf)->size > alloc_size) {
147                 *buf = realloc(*buf, sizeof(RDFFChunk) + (*buf)->size);
148         }
149         READ((*buf)->data, (*buf)->size, 1, file->fd);
150         if (((*buf)->size % 2)) {
151                 char pad;
152                 READ(&pad, 1, 1, file->fd);  /* skip pad */
153         }
154         return RDFF_STATUS_OK;
155 }
156
157 bool
158 rdff_chunk_is_uri(RDFFChunk* chunk)
159
160 {
161         return !strncmp(chunk->type, CHUNK_URID, CHUNK_ID_LEN);
162 }
163
164 bool
165 rdff_chunk_is_triple(RDFFChunk* chunk)
166 {
167         return !strncmp(chunk->type, CHUNK_TRIP, CHUNK_ID_LEN);
168 }
169
170 void
171 rdff_close(RDFF file)
172 {
173         if (file) {
174                 if (file->write) {
175                         fseek(file->fd, 4, SEEK_SET);
176                         if (fwrite(&file->size, sizeof(file->size), 1, file->fd) != 1) {
177                                 fprintf(stderr, "failed to write RIFF header size\n");
178                         }
179                 }
180                 fclose(file->fd);
181         }
182
183         free(file);
184 }
185
186 #ifdef STANDALONE
187 // Test program
188 int
189 main(int argc, char** argv)
190 {
191         if (argc != 2) {
192                 fprintf(stderr, "Usage: %s FILENAME\n", argv[0]);
193                 return 1;
194         }
195
196         const char* const filename = argv[1];
197
198         RDFF file = rdff_open(filename, true);
199         if (!file)
200                 goto fail;
201
202         static const int N_URIS    = 16;
203         static const int N_RECORDS = 16;
204
205         char uri[64];
206         for (int i = 0; i < N_URIS; ++i) {
207                 snprintf(uri, sizeof(uri), "http://example.org/uri%02d", i + 1);
208                 rdff_write_uri(file, i + 1, strlen(uri), uri);
209         }
210
211         char val[6];
212         for (int i = 0; i < N_RECORDS; ++i) {
213                 snprintf(val, sizeof(val), "VAL%02d", i);
214                 rdff_write_triple(file,
215                                   0,
216                                   rand() % N_URIS,
217                                   0,
218                                   sizeof(val),
219                                   val);
220         }
221
222         rdff_close(file);
223
224         file = rdff_open(filename, false);
225         if (!file)
226                 goto fail;
227
228         RDFFChunk* chunk = malloc(sizeof(RDFFChunk));
229         chunk->size = 0;
230         for (int i = 0; i < N_URIS; ++i) {
231                 if (rdff_read_chunk(file, &chunk)
232                     || strncmp(chunk->type, CHUNK_URID, 4)) {
233                         fprintf(stderr, "error: expected %s chunk\n", CHUNK_URID);
234                         goto fail;
235                 }
236                 RDFFURIChunk* body = (RDFFURIChunk*)chunk->data;
237                 printf("URI: %s\n", body->uri);
238         }
239
240         for (int i = 0; i < N_RECORDS; ++i) {
241                 if (rdff_read_chunk(file, &chunk)
242                     || strncmp(chunk->type, CHUNK_TRIP, 4)) {
243                         fprintf(stderr, "error: expected %s chunk\n", CHUNK_TRIP);
244                         goto fail;
245                 }
246                 RDFFTripleChunk* body = (RDFFTripleChunk*)chunk->data;
247                 printf("KEY %d = %s\n", body->predicate, body->object);
248         }
249
250         free(chunk);
251         rdff_close(file);
252
253         return 0;
254
255 fail:
256         rdff_close(file);
257         fprintf(stderr, "Test failed\n");
258         return 1;
259 }
260 #endif // STANDALONE