mas01cr@239
|
1 #include "audioDB.h"
|
mas01cr@239
|
2
|
mas01cr@239
|
3 #if defined(O2_DEBUG)
|
mas01cr@239
|
4 void sigterm_action(int signal, siginfo_t *info, void *context) {
|
mas01cr@239
|
5 exit(128+signal);
|
mas01cr@239
|
6 }
|
mas01cr@239
|
7
|
mas01cr@239
|
8 void sighup_action(int signal, siginfo_t *info, void *context) {
|
mas01cr@239
|
9 // FIXME: reread any configuration files
|
mas01cr@239
|
10 }
|
mas01cr@239
|
11 #endif
|
mas01cr@239
|
12
|
mas01cr@385
|
13 int acquire_lock(int fd, bool exclusive) {
|
mas01cr@239
|
14 struct flock lock;
|
mas01cr@239
|
15 int status;
|
mas01cr@239
|
16
|
mas01cr@239
|
17 lock.l_type = exclusive ? F_WRLCK : F_RDLCK;
|
mas01cr@239
|
18 lock.l_whence = SEEK_SET;
|
mas01cr@239
|
19 lock.l_start = 0;
|
mas01cr@239
|
20 lock.l_len = 0; /* "the whole file" */
|
mas01cr@239
|
21
|
mas01cr@239
|
22 retry:
|
mas01cr@239
|
23 do {
|
mas01cr@239
|
24 status = fcntl(fd, F_SETLKW, &lock);
|
mas01cr@239
|
25 } while (status != 0 && errno == EINTR);
|
mas01cr@239
|
26
|
mas01cr@239
|
27 if (status) {
|
mas01cr@239
|
28 if (errno == EAGAIN) {
|
mas01cr@239
|
29 sleep(1);
|
mas01cr@239
|
30 goto retry;
|
mas01cr@239
|
31 } else {
|
mas01cr@385
|
32 return status;
|
mas01cr@239
|
33 }
|
mas01cr@239
|
34 }
|
mas01cr@385
|
35 return 0;
|
mas01cr@239
|
36 }
|
mas01cr@239
|
37
|
mas01cr@385
|
38 int divest_lock(int fd) {
|
mas01cr@239
|
39 struct flock lock;
|
mas01cr@239
|
40
|
mas01cr@239
|
41 lock.l_type = F_UNLCK;
|
mas01cr@239
|
42 lock.l_whence = SEEK_SET;
|
mas01cr@239
|
43 lock.l_start = 0;
|
mas01cr@239
|
44 lock.l_len = 0;
|
mas01cr@239
|
45
|
mas01cr@385
|
46 return fcntl(fd, F_SETLKW, &lock);
|
mas01cr@385
|
47 }
|
mas01cr@239
|
48
|
mas01cr@385
|
49 void audioDB::get_lock(int fd, bool exclusive) {
|
mas01cr@385
|
50 if(acquire_lock(fd, exclusive)) {
|
mas01cr@385
|
51 error("fcntl lock error", "", "fcntl");
|
mas01cr@385
|
52 }
|
mas01cr@385
|
53 }
|
mas01cr@385
|
54
|
mas01cr@385
|
55 void audioDB::release_lock(int fd) {
|
mas01cr@385
|
56 if (divest_lock(fd)) {
|
mas01cr@239
|
57 error("fcntl unlock error", "", "fcntl");
|
mas01cr@385
|
58 }
|
mas01cr@239
|
59 }
|
mas01cr@239
|
60
|
mas01cr@239
|
61 void audioDB::error(const char* a, const char* b, const char *sysFunc) {
|
mas01ik@355
|
62
|
mas01ik@355
|
63
|
mas01ik@355
|
64 if(isServer) {
|
mas01cr@370
|
65 /* FIXME: I think this is leaky -- we never delete err.
|
mas01cr@370
|
66 actually deleting it is tricky, though; it gets placed into
|
mas01cr@370
|
67 some soap-internal struct with uncertain extent... -- CSR,
|
mas01cr@370
|
68 2007-10-01 */
|
mas01cr@370
|
69 char *err = new char[256]; /* FIXME: overflows */
|
mas01cr@370
|
70 snprintf(err, 255, "%s: %s\n%s", a, b, sysFunc ? strerror(errno) : "");
|
mas01cr@370
|
71 /* FIXME: actually we could usefully do with a properly
|
mas01cr@370
|
72 structured type, so that we can throw separate faultstring
|
mas01cr@370
|
73 and details. -- CSR, 2007-10-01 */
|
mas01ik@355
|
74 throw(err);
|
mas01ik@355
|
75 } else if (UseApiError){
|
mas01ik@355
|
76 apierrortemp=-1;
|
mas01ik@355
|
77 throw(apierrortemp);
|
mas01ik@355
|
78 } else {
|
mas01ik@355
|
79 std::cerr << a << ": " << b << std::endl;
|
mas01ik@355
|
80 if (sysFunc) {
|
mas01ik@355
|
81 perror(sysFunc);
|
mas01ik@355
|
82 }
|
mas01ik@355
|
83 exit(1);
|
mas01cr@239
|
84 }
|
mas01ik@355
|
85
|
mas01cr@239
|
86 }
|
mas01cr@239
|
87
|
mas01cr@284
|
88 void audioDB::initRNG() {
|
mas01cr@284
|
89 rng = gsl_rng_alloc(gsl_rng_mt19937);
|
mas01cr@284
|
90 if(!rng) {
|
mas01cr@284
|
91 error("could not allocate Random Number Generator");
|
mas01cr@284
|
92 }
|
mas01cr@284
|
93 /* FIXME: maybe we should use a real source of entropy? */
|
mas01cr@284
|
94 gsl_rng_set(rng, time(NULL));
|
mas01cr@284
|
95 }
|
mas01cr@284
|
96
|
mas01cr@239
|
97 void audioDB::initDBHeader(const char* dbName) {
|
mas01cr@239
|
98 if ((dbfid = open(dbName, forWrite ? O_RDWR : O_RDONLY)) < 0) {
|
mas01cr@239
|
99 error("Can't open database file", dbName, "open");
|
mas01cr@239
|
100 }
|
mas01cr@239
|
101
|
mas01cr@239
|
102 get_lock(dbfid, forWrite);
|
mas01cr@239
|
103 // Get the database header info
|
mas01cr@239
|
104 dbH = new dbTableHeaderT();
|
mas01cr@239
|
105 assert(dbH);
|
mas01cr@239
|
106
|
mas01cr@239
|
107 if(read(dbfid, (char *) dbH, O2_HEADERSIZE) != O2_HEADERSIZE) {
|
mas01cr@239
|
108 error("error reading db header", dbName, "read");
|
mas01cr@239
|
109 }
|
mas01cr@239
|
110
|
mas01cr@239
|
111 if(dbH->magic == O2_OLD_MAGIC) {
|
mas01cr@239
|
112 // FIXME: if anyone ever complains, write the program to convert
|
mas01cr@239
|
113 // from the old audioDB format to the new...
|
mas01cr@239
|
114 error("database file has old O2 header", dbName);
|
mas01cr@239
|
115 }
|
mas01cr@239
|
116
|
mas01cr@239
|
117 if(dbH->magic != O2_MAGIC) {
|
mas01cr@239
|
118 std::cerr << "expected: " << O2_MAGIC << ", got: " << dbH->magic << std::endl;
|
mas01cr@239
|
119 error("database file has incorrect header", dbName);
|
mas01cr@239
|
120 }
|
mas01cr@239
|
121
|
mas01cr@239
|
122 if(dbH->version != O2_FORMAT_VERSION) {
|
mas01cr@239
|
123 error("database file has incorrect version", dbName);
|
mas01cr@239
|
124 }
|
mas01cr@239
|
125
|
mas01cr@239
|
126 if(dbH->headerSize != O2_HEADERSIZE) {
|
mas01cr@239
|
127 error("sizeof(dbTableHeader) unexpected: platform ABI mismatch?", dbName);
|
mas01cr@239
|
128 }
|
mas01cr@239
|
129
|
mas01cr@239
|
130 CHECKED_MMAP(char *, db, 0, getpagesize());
|
mas01cr@239
|
131
|
mas01cr@239
|
132 // Make some handy tables with correct types
|
mas01cr@239
|
133 if(forWrite || (dbH->length > 0)) {
|
mas01cr@239
|
134 if(forWrite) {
|
mas01cr@239
|
135 fileTableLength = dbH->trackTableOffset - dbH->fileTableOffset;
|
mas01cr@239
|
136 trackTableLength = dbH->dataOffset - dbH->trackTableOffset;
|
mas01cr@239
|
137 dataBufLength = dbH->timesTableOffset - dbH->dataOffset;
|
mas01cr@239
|
138 timesTableLength = dbH->powerTableOffset - dbH->timesTableOffset;
|
mas01cr@239
|
139 powerTableLength = dbH->l2normTableOffset - dbH->powerTableOffset;
|
mas01cr@239
|
140 l2normTableLength = dbH->dbSize - dbH->l2normTableOffset;
|
mas01cr@239
|
141 } else {
|
mas01cr@256
|
142 fileTableLength = ALIGN_PAGE_UP(dbH->numFiles * O2_FILETABLE_ENTRY_SIZE);
|
mas01cr@256
|
143 trackTableLength = ALIGN_PAGE_UP(dbH->numFiles * O2_TRACKTABLE_ENTRY_SIZE);
|
mas01mc@324
|
144 if( dbH->flags & O2_FLAG_LARGE_ADB ){
|
mas01mc@324
|
145 dataBufLength = ALIGN_PAGE_UP(dbH->numFiles * O2_FILETABLE_ENTRY_SIZE);
|
mas01mc@324
|
146 timesTableLength = ALIGN_PAGE_UP(dbH->numFiles * O2_FILETABLE_ENTRY_SIZE);
|
mas01mc@324
|
147 powerTableLength = ALIGN_PAGE_UP(dbH->numFiles * O2_FILETABLE_ENTRY_SIZE);
|
mas01mc@324
|
148 l2normTableLength = 0;
|
mas01mc@324
|
149 }
|
mas01mc@324
|
150 else{
|
mas01mc@324
|
151 dataBufLength = ALIGN_PAGE_UP(dbH->length);
|
mas01mc@324
|
152 timesTableLength = ALIGN_PAGE_UP(2*(dbH->length / dbH->dim));
|
mas01mc@324
|
153 powerTableLength = ALIGN_PAGE_UP(dbH->length / dbH->dim);
|
mas01mc@324
|
154 l2normTableLength = ALIGN_PAGE_UP(dbH->length / dbH->dim);
|
mas01mc@324
|
155 }
|
mas01cr@239
|
156 }
|
mas01cr@239
|
157 CHECKED_MMAP(char *, fileTable, dbH->fileTableOffset, fileTableLength);
|
mas01cr@239
|
158 CHECKED_MMAP(unsigned *, trackTable, dbH->trackTableOffset, trackTableLength);
|
mas01cr@239
|
159 /*
|
mas01cr@239
|
160 * No more mmap() for dataBuf
|
mas01cr@239
|
161 *
|
mas01cr@239
|
162 * FIXME: Actually we do do the mmap() in the two cases where it's
|
mas01cr@239
|
163 * still "needed": in pointQuery and in l2norm if dbH->length is
|
mas01cr@239
|
164 * non-zero. Removing those cases too (and deleting the dataBuf
|
mas01cr@239
|
165 * variable completely) would be cool. -- CSR, 2007-11-19
|
mas01cr@239
|
166 *
|
mas01cr@239
|
167 * CHECKED_MMAP(double *, dataBuf, dbH->dataOffset, dataBufLength);
|
mas01cr@239
|
168 */
|
mas01mc@324
|
169 if( dbH->flags & O2_FLAG_LARGE_ADB ){
|
mas01mc@324
|
170 CHECKED_MMAP(char *, featureFileNameTable, dbH->dataOffset, fileTableLength);
|
mas01mc@324
|
171 if( dbH->flags & O2_FLAG_TIMES )
|
mas01mc@324
|
172 CHECKED_MMAP(char *, timesFileNameTable, dbH->timesTableOffset, fileTableLength);
|
mas01mc@324
|
173 if( dbH->flags & O2_FLAG_POWER )
|
mas01mc@324
|
174 CHECKED_MMAP(char *, powerFileNameTable, dbH->powerTableOffset, fileTableLength);
|
mas01mc@324
|
175 }
|
mas01mc@324
|
176 else{
|
mas01mc@324
|
177 CHECKED_MMAP(double *, timesTable, dbH->timesTableOffset, timesTableLength);
|
mas01mc@324
|
178 CHECKED_MMAP(double *, powerTable, dbH->powerTableOffset, powerTableLength);
|
mas01mc@324
|
179 CHECKED_MMAP(double *, l2normTable, dbH->l2normTableOffset, l2normTableLength);
|
mas01mc@324
|
180 }
|
mas01cr@239
|
181 }
|
mas01mc@292
|
182
|
mas01mc@292
|
183 // build track offset table
|
mas01mc@292
|
184 trackOffsetTable = new off_t[dbH->numFiles];
|
mas01mc@292
|
185 Uns32T cumTrack=0;
|
mas01mc@292
|
186 for(Uns32T k = 0; k < dbH->numFiles; k++){
|
mas01mc@292
|
187 trackOffsetTable[k] = cumTrack;
|
mas01mc@292
|
188 cumTrack += trackTable[k] * dbH->dim;
|
mas01mc@324
|
189 }
|
mas01mc@324
|
190
|
mas01mc@324
|
191 // Assign correct number of point bits per track in LSH indexing / retrieval
|
mas01mc@324
|
192 lsh_n_point_bits = dbH->flags >> 28;
|
mas01mc@324
|
193 if( !lsh_n_point_bits )
|
mas01mc@324
|
194 lsh_n_point_bits = O2_DEFAULT_LSH_N_POINT_BITS;
|
mas01cr@239
|
195 }
|
mas01cr@239
|
196
|
mas01mc@324
|
197 void audioDB::initInputFile (const char *inFile, bool loadData) {
|
mas01cr@239
|
198 if (inFile) {
|
mas01cr@239
|
199 if ((infid = open(inFile, O_RDONLY)) < 0) {
|
mas01cr@239
|
200 error("can't open input file for reading", inFile, "open");
|
mas01cr@239
|
201 }
|
mas01cr@239
|
202
|
mas01cr@239
|
203 if (fstat(infid, &statbuf) < 0) {
|
mas01cr@239
|
204 error("fstat error finding size of input", inFile, "fstat");
|
mas01cr@239
|
205 }
|
mas01cr@239
|
206
|
mas01cr@239
|
207 if(dbH->dim == 0 && dbH->length == 0) { // empty database
|
mas01cr@239
|
208 // initialize with input dimensionality
|
mas01cr@239
|
209 if(read(infid, &dbH->dim, sizeof(unsigned)) != sizeof(unsigned)) {
|
mas01cr@239
|
210 error("short read of input file", inFile);
|
mas01cr@239
|
211 }
|
mas01cr@239
|
212 if(dbH->dim == 0) {
|
mas01cr@239
|
213 error("dimensionality of zero in input file", inFile);
|
mas01cr@239
|
214 }
|
mas01cr@239
|
215 } else {
|
mas01cr@239
|
216 unsigned test;
|
mas01cr@239
|
217 if(read(infid, &test, sizeof(unsigned)) != sizeof(unsigned)) {
|
mas01cr@239
|
218 error("short read of input file", inFile);
|
mas01cr@239
|
219 }
|
mas01cr@239
|
220 if(dbH->dim == 0) {
|
mas01cr@239
|
221 error("dimensionality of zero in input file", inFile);
|
mas01cr@239
|
222 }
|
mas01cr@239
|
223 if(dbH->dim != test) {
|
mas01cr@239
|
224 std::cerr << "error: expected dimension: " << dbH->dim << ", got : " << test <<std::endl;
|
mas01cr@239
|
225 error("feature dimensions do not match database table dimensions", inFile);
|
mas01cr@239
|
226 }
|
mas01cr@239
|
227 }
|
mas01cr@239
|
228
|
mas01mc@324
|
229 if (loadData && ((indata = (char *) mmap(0, statbuf.st_size, PROT_READ, MAP_SHARED, infid, 0)) == (caddr_t) -1)) {
|
mas01cr@239
|
230 error("mmap error for input", inFile, "mmap");
|
mas01cr@239
|
231 }
|
mas01cr@239
|
232 }
|
mas01cr@239
|
233 }
|
mas01cr@239
|
234
|
mas01mc@292
|
235 void audioDB::initTables(const char* dbName, const char* inFile) {
|
mas01cr@284
|
236 /* FIXME: initRNG() really logically belongs in the audioDB
|
mas01cr@284
|
237 contructor. However, there are of the order of four constructors
|
mas01cr@284
|
238 at the moment, and more to come from API implementation. Given
|
mas01cr@284
|
239 that duplication, I think this is the least worst place to put
|
mas01cr@284
|
240 it; the assumption is that nothing which doesn't look at a
|
mas01cr@284
|
241 database will need an RNG. -- CSR, 2008-07-02 */
|
mas01cr@284
|
242 initRNG();
|
mas01cr@239
|
243 initDBHeader(dbName);
|
mas01mc@292
|
244 if(inFile)
|
mas01mc@292
|
245 initInputFile(inFile);
|
mas01cr@239
|
246 }
|
mas01mc@292
|
247
|
mas01mc@324
|
248 // If name is relative path, side effect name with prefix/name
|
mas01mc@324
|
249 // Do not free original pointer
|
mas01mc@324
|
250 void audioDB::prefix_name(char** const name, const char* prefix){
|
mas01mc@324
|
251 // No prefix if prefix is empty
|
mas01mc@324
|
252 if(!prefix)
|
mas01mc@324
|
253 return;
|
mas01mc@324
|
254 // Allocate new memory, keep old memory
|
mas01mc@324
|
255 assert(name && *name);
|
mas01mc@324
|
256 if (strlen(*name) + strlen(prefix) + 1 > O2_MAXFILESTR)
|
mas01mc@324
|
257 error("error: path prefix + filename too long",prefix);
|
mas01mc@324
|
258 // Do not prefix absolute path+filename
|
mas01mc@324
|
259 if(**name=='/')
|
mas01mc@324
|
260 return;
|
mas01mc@324
|
261 // OK to prefix relative path+filename
|
mas01mc@324
|
262 char* prefixedName = (char*) malloc(O2_MAXFILESTR);
|
mas01mc@324
|
263 sprintf(prefixedName, "%s/%s", prefix, *name);
|
mas01mc@324
|
264 *name = prefixedName; // side effect new name to old name
|
mas01mc@324
|
265 }
|