ignore unpaired noteoff's when writing part of a MidiModel to a new source. in realit...
[ardour2.git] / libs / ardour / rdff.c
blobca8e1abf1fd6d07a155ea957c2cca4123188e7b6
1 /*
2 RDFF - RDF in RIFF
3 Copyright 2011 David Robillard <http://drobilla.net>
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.
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.
18 #include <assert.h>
19 #include <errno.h>
20 #include <stdio.h>
21 #include <stdlib.h>
22 #include <string.h>
24 #include "rdff.h"
26 #define CHUNK_ID_LEN 4
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*/
32 struct _RDFF {
33 FILE* fd;
34 uint32_t size;
35 bool write;
38 RDFF
39 rdff_open(const char* path, bool write)
41 FILE* fd = fopen(path, (write ? "w" : "r"));
42 if (!fd) {
43 fprintf(stderr, "%s\n", strerror(errno));
44 return NULL;
47 uint32_t size = 0;
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;
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;
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;
77 RDFF ret = (RDFF)malloc(sizeof(struct _RDFF));
78 ret->fd = fd;
79 ret->size = size;
80 ret->write = write;
81 return ret;
84 #define WRITE(ptr, size, nmemb, stream) \
85 if (fwrite(ptr, size, nmemb, stream) != nmemb) { \
86 return RDFF_STATUS_UNKNOWN_ERROR; \
89 RDFFStatus
90 rdff_write_uri(RDFF file,
91 uint32_t id,
92 uint32_t len,
93 const char* uri)
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 */
103 file->size += 8 + chunk_size;
104 return RDFF_STATUS_OK;
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)
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 */
126 file->size += 8 + chunk_size;
127 return RDFF_STATUS_OK;
130 RDFFStatus
131 rdff_read_chunk(RDFF file,
132 RDFFChunk** buf)
134 if (feof(file->fd))
135 return RDFF_STATUS_EOF;
137 #define READ(ptr, size, nmemb, stream) \
138 if (fread(ptr, size, nmemb, stream) != nmemb) { \
139 return RDFF_STATUS_CORRUPT; \
142 const uint32_t alloc_size = (*buf)->size;
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);
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 */
154 return RDFF_STATUS_OK;
157 bool
158 rdff_chunk_is_uri(RDFFChunk* chunk)
161 return !strncmp(chunk->type, CHUNK_URID, CHUNK_ID_LEN);
164 bool
165 rdff_chunk_is_triple(RDFFChunk* chunk)
167 return !strncmp(chunk->type, CHUNK_TRIP, CHUNK_ID_LEN);
170 void
171 rdff_close(RDFF file)
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");
180 fclose(file->fd);
183 free(file);
186 #ifdef STANDALONE
187 // Test program
189 main(int argc, char** argv)
191 if (argc != 2) {
192 fprintf(stderr, "Usage: %s FILENAME\n", argv[0]);
193 return 1;
196 const char* const filename = argv[1];
198 RDFF file = rdff_open(filename, true);
199 if (!file)
200 goto fail;
202 static const int N_URIS = 16;
203 static const int N_RECORDS = 16;
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);
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,
216 rand() % N_URIS,
218 sizeof(val),
219 val);
222 rdff_close(file);
224 file = rdff_open(filename, false);
225 if (!file)
226 goto fail;
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;
236 RDFFURIChunk* body = (RDFFURIChunk*)chunk->data;
237 printf("URI: %s\n", body->uri);
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;
246 RDFFTripleChunk* body = (RDFFTripleChunk*)chunk->data;
247 printf("KEY %d = %s\n", body->predicate, body->object);
250 free(chunk);
251 rdff_close(file);
253 return 0;
255 fail:
256 rdff_close(file);
257 fprintf(stderr, "Test failed\n");
258 return 1;
260 #endif // STANDALONE