source: main/trunk/greenstone3/src/packages/javagdbm/jni/gdbmjava.c@ 26674

Last change on this file since 26674 was 26674, checked in by davidb, 8 years ago

Changes to support cross-compiling using mingw under Linux to produce Windows native binaries/dlls. See comment at the top of the file for more details

  • Property svn:keywords set to Author Date Id Revision
File size: 12.1 KB
Line 
1/*
2 * module: pip/jni/gdbm -- A Java interface to the GDBM library
3 * file: gdbmjava.c -- Native parts of the au.com.pharos.gdbm.GdbmFile
4 * Java class
5 *
6 * Copyright (C) 1997 Pharos IP Pty Ltd
7 *
8 * $Id: gdbmjava.c 26674 2013-01-10 03:30:25Z davidb $
9 *
10 * This program is free software; you can redistribute it and/or modify
11 * it under the terms of the GNU General Public License as published by
12 * the Free Software Foundation; either version 2 of the License, or
13 * (at your option) any later version.
14 *
15 * This program is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 * GNU General Public License for more details.
19 *
20 * You should have received a copy of the GNU General Public License
21 * along with this program; if not, write to the Free Software
22 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
23 */
24
25/* NOTE ON MEMORY MANAGEMENT: GDBM always mallocs memory and expects
26 * the caller to free it. Java requires native functions to interact
27 * with it to lock and unlock buffers. */
28
29#include <errno.h>
30#include <stdio.h>
31#include <stdlib.h>
32#include <string.h>
33
34#ifdef __MINGW32__
35#define MSDOS
36#endif
37
38#if defined (_MSC_VER)
39#include <gdbmdefs.h>
40#include <gdbmerrno.h>
41#define GDBM_FILE gdbm_file_info *
42extern char* gdbm_version;
43#else
44#include <gdbm.h>
45#endif
46
47#include <jni.h>
48
49#ifdef __MINGW32__
50
51/* (Cross) compiling for Windows Want the type definitions in *win32*
52 version of jni_md.h but with the mingw c compiler this then leads
53 to C-mangled style functions (param/num args encoded in function
54 name) which we *don't* want. The following achieves this */
55
56#undef JNIEXPORT
57#undef JNIIMPORT
58#undef JNICALL
59
60#define JNIEXPORT
61#define JNIIMPORT
62#define JNICALL
63#endif
64
65#include <GdbmFile.h>
66
67#ifdef DEBUG
68#define ASSERT(x) if (!(x)) { \
69 fprintf(stderr, "%s:%d: assertion failed\n", __FILE__, __LINE__); \
70 abort(); }
71#else /* !DEBUG */
72#define ASSERT(x)
73#endif /* !DEBUG */
74
75/* The Java class within which the native methods are declared */
76#define JAVA_CLASS "au.com.pharos.gdbm.GdbmFile"
77
78#define NULL_PTR_EXCEPTION(env) nullPtrException(env, __FILE__, __LINE__)
79#define GDBM_EXCEPTION(env) gdbmException(env, __FILE__, __LINE__)
80
81#define CHECK_NOT_NULL(ptr, env) if (!ptr) { NULL_PTR_EXCEPTION(env); return 0; }
82#define CHECK_NOT_NULL_VOID(ptr, env) if (!ptr) { NULL_PTR_EXCEPTION(env); return; }
83
84void gdbmException(JNIEnv *env, const char *file, int line);
85void nullPtrException(JNIEnv *env, const char *file, int line);
86
87void releaseArray(JNIEnv *env, jbyteArray array, datum *fromDatum);
88void releaseArrayAbort(JNIEnv *env, jbyteArray array, datum *fromDatum);
89int arrayToDatum(JNIEnv *env, jbyteArray fromArray, datum *toDatum);
90jbyteArray datumToArray(JNIEnv *env, datum *fromDatum);
91
92/* Convert between a jlong and a void ptr using a well-defined cast.
93 * (Casting between a pointer and an integer of different sizes spooks
94 * both gcc and mbp. */
95#if (SIZEOF_VOID_P == SIZEOF_LONG)
96# define DBF_TO_JLONG(x) ((jlong)((long) x))
97# define JLONG_TO_DBF(x) ((GDBM_FILE)((long) x))
98#elif (SIZEOF_VOID_P == SIZEOF_INT)
99# define DBF_TO_JLONG(x) ((jlong)((int) (x)))
100# define JLONG_TO_DBF(x) ((GDBM_FILE)((int) (x)))
101#else
102# define DBF_TO_JLONG(x) ((jlong)(x))
103# define JLONG_TO_DBF(x) ((GDBM_FILE)(x))
104#endif
105
106
107JNIEXPORT jlong JNICALL
108Java_au_com_pharos_gdbm_GdbmFile_gdbm_1open
109 (JNIEnv *env, jobject obj, jstring fileName, jint flags)
110{
111 GDBM_FILE dbf;
112 const char *utfFileName;
113
114 utfFileName = (*env)->GetStringUTFChars(env, fileName, 0);
115 if (!utfFileName)
116 return 0;
117
118 setbuf(stderr, 0);
119
120 /* XXX: Should we let the caller specify the file mode? I think
121 * not -- Java is above the level of octal file modes. [mbp] */
122 /* Couldn't get it to work properly on Windows without the 0 here -
123 wouldn't allow mulitple READs on a single file [kjdon] */
124#if defined (__WIN32__)
125 dbf = gdbm_open((char *) utfFileName, 0, flags, 0660, NULL, 0);
126#else
127 dbf = gdbm_open((char *) utfFileName, 0, flags, 0660, NULL);
128#endif
129 if (utfFileName)
130 (*env)->ReleaseStringUTFChars(env, fileName, utfFileName);
131
132 if (!dbf) {
133 GDBM_EXCEPTION(env);
134 return 0;
135 }
136
137 return DBF_TO_JLONG(dbf);
138}
139
140
141
142
143
144JNIEXPORT jbyteArray JNICALL
145Java_au_com_pharos_gdbm_GdbmFile_gdbm_1firstkey
146(JNIEnv *env, jobject obj, jlong dbf)
147{
148 datum keyDatum;
149 jbyteArray keyArray;
150
151 CHECK_NOT_NULL(dbf, env);
152
153 keyDatum = gdbm_firstkey(JLONG_TO_DBF(dbf));
154 if ( gdbm_errno != GDBM_NO_ERROR ) {
155 GDBM_EXCEPTION(env);
156 return 0;
157 }
158 if ( !keyDatum.dptr )
159 return 0;
160
161 keyArray = datumToArray(env, &keyDatum);
162 free(keyDatum.dptr);
163
164 return keyArray;
165}
166
167
168
169
170
171JNIEXPORT jbyteArray JNICALL
172Java_au_com_pharos_gdbm_GdbmFile_gdbm_1nextkey
173(JNIEnv *env, jobject this, jlong dbf, jbyteArray keyArray)
174{
175 datum keyDatum;
176 datum nextDatum;
177 jbyteArray nextArray;
178
179 CHECK_NOT_NULL(dbf, env);
180
181 if (!arrayToDatum(env, keyArray, &keyDatum)) {
182 NULL_PTR_EXCEPTION(env);
183 return 0;
184 }
185
186 nextDatum = gdbm_nextkey(JLONG_TO_DBF(dbf), keyDatum);
187 releaseArrayAbort(env, keyArray, &keyDatum);
188
189 if ( gdbm_errno != GDBM_NO_ERROR ) {
190 GDBM_EXCEPTION(env);
191 return 0;
192 }
193 if ( !nextDatum.dptr )
194 return 0;
195
196 nextArray = datumToArray(env, &nextDatum);
197 free(nextDatum.dptr);
198
199 return nextArray;
200}
201
202
203
204
205
206
207JNIEXPORT jbyteArray JNICALL
208Java_au_com_pharos_gdbm_GdbmFile_gdbm_1fetch
209 (JNIEnv *env, jobject this, jlong dbf, jbyteArray keyArray)
210{
211 datum keyDatum;
212 datum valueDatum;
213 jbyteArray valueArray;
214
215 CHECK_NOT_NULL(dbf, env);
216
217 if (!arrayToDatum(env, keyArray, &keyDatum)) {
218 NULL_PTR_EXCEPTION(env);
219 return 0;
220 }
221
222 valueDatum = gdbm_fetch(JLONG_TO_DBF(dbf), keyDatum);
223 releaseArrayAbort(env, keyArray, &keyDatum);
224
225 if ( !valueDatum.dptr )
226 return 0;
227 if ( gdbm_errno != GDBM_NO_ERROR ) {
228 GDBM_EXCEPTION(env);
229 return 0;
230 }
231
232 valueArray = datumToArray(env, &valueDatum);
233 free(valueDatum.dptr);
234
235 return valueArray;
236}
237
238
239
240JNIEXPORT jboolean JNICALL
241Java_au_com_pharos_gdbm_GdbmFile_gdbm_1exists
242 (JNIEnv *env, jobject obj, jlong dbf, jbyteArray keyArray)
243{
244 datum keyDatum;
245 int result;
246
247 CHECK_NOT_NULL(dbf, env);
248 if (!arrayToDatum(env, keyArray, &keyDatum)) {
249 NULL_PTR_EXCEPTION(env);
250 return JNI_FALSE;
251 }
252 result = gdbm_exists(JLONG_TO_DBF(dbf), keyDatum);
253 if ( gdbm_errno != GDBM_NO_ERROR ) {
254 GDBM_EXCEPTION(env);
255 return 0;
256 }
257 releaseArrayAbort(env, keyArray, &keyDatum);
258 return result ? JNI_TRUE : JNI_FALSE;
259}
260
261
262
263
264
265JNIEXPORT void JNICALL
266Java_au_com_pharos_gdbm_GdbmFile_gdbm_1store
267 (JNIEnv *env, jobject obj, jlong dbf,
268 jbyteArray keyArray, jbyteArray valueArray, jboolean replace)
269{
270 datum keyDatum;
271 datum valueDatum;
272
273 CHECK_NOT_NULL_VOID(dbf, env);
274
275 if ( !arrayToDatum(env, keyArray, &keyDatum)
276 || !arrayToDatum(env, valueArray, &valueDatum) ) {
277 NULL_PTR_EXCEPTION(env);
278 return;
279 }
280
281 gdbm_store(JLONG_TO_DBF(dbf), keyDatum, valueDatum,
282 replace ? GDBM_REPLACE : 0);
283
284 releaseArrayAbort(env, keyArray, &keyDatum);
285 releaseArrayAbort(env, valueArray, &valueDatum);
286
287 if ( gdbm_errno != GDBM_NO_ERROR )
288 GDBM_EXCEPTION(env);
289}
290
291
292
293JNIEXPORT void JNICALL
294Java_au_com_pharos_gdbm_GdbmFile_gdbm_1delete
295 (JNIEnv *env, jobject obj, jlong dbf, jbyteArray keyArray)
296{
297 datum keyDatum;
298
299 CHECK_NOT_NULL_VOID(dbf, env);
300
301 if (!arrayToDatum(env, keyArray, &keyDatum)) {
302 NULL_PTR_EXCEPTION(env);
303 return;
304 }
305
306 gdbm_delete(JLONG_TO_DBF(dbf), keyDatum);
307
308 releaseArrayAbort(env, keyArray, &keyDatum);
309
310 if ( gdbm_errno != GDBM_NO_ERROR )
311 GDBM_EXCEPTION(env);
312}
313
314
315
316JNIEXPORT jstring JNICALL
317Java_au_com_pharos_gdbm_GdbmFile_gdbm_1getversion
318 (JNIEnv *env, jclass cls)
319{
320 return (*env)->NewStringUTF(env, gdbm_version);
321}
322
323
324JNIEXPORT jstring JNICALL
325Java_au_com_pharos_gdbm_GdbmFile_gdbm_1wrapperVersion
326 (JNIEnv *env, jclass cls)
327{
328 return (*env)->NewStringUTF(env, "JavaGDBM release 0005 built " __DATE__);
329}
330
331
332
333
334JNIEXPORT void JNICALL
335Java_au_com_pharos_gdbm_GdbmFile_gdbm_1reorganize
336 (JNIEnv *env, jobject obj, jlong dbf)
337{
338 CHECK_NOT_NULL_VOID(dbf, env);
339
340 gdbm_reorganize(JLONG_TO_DBF(dbf));
341
342 if ( gdbm_errno != GDBM_NO_ERROR )
343 GDBM_EXCEPTION(env);
344}
345
346
347
348JNIEXPORT void JNICALL
349Java_au_com_pharos_gdbm_GdbmFile_gdbm_1close
350 (JNIEnv *env, jobject obj, jlong dbf)
351{
352 CHECK_NOT_NULL_VOID(dbf, env);
353
354 gdbm_close(JLONG_TO_DBF(dbf));
355
356 if ( gdbm_errno != GDBM_NO_ERROR )
357 GDBM_EXCEPTION(env);
358}
359
360
361
362JNIEXPORT void JNICALL
363Java_au_com_pharos_gdbm_GdbmFile_gdbm_1sync
364 (JNIEnv *env, jobject obj, jlong dbf)
365{
366 CHECK_NOT_NULL_VOID(dbf, env);
367
368 gdbm_sync(JLONG_TO_DBF(dbf));
369
370 if ( gdbm_errno != GDBM_NO_ERROR )
371 GDBM_EXCEPTION(env);
372}
373
374
375
376
377/**********************************************************************
378 *
379 * Following are support functions which aid in interfacing C to
380 * Java. */
381
382/** Create a new Java byte array from a GDBM datum, and return a
383 * pointer thereto. */
384
385jbyteArray datumToArray(JNIEnv *env, datum *fromDatum)
386{
387 jbyteArray toArray;
388
389 if (!fromDatum || !fromDatum->dptr)
390 return 0;
391
392 toArray = (*env)->NewByteArray(env, fromDatum->dsize);
393 ASSERT(toArray);
394 (*env)->SetByteArrayRegion(env, toArray,
395 0, fromDatum->dsize, fromDatum->dptr);
396
397 return toArray;
398}
399
400/** Convert a Java byte array to a GDBM datum.
401 *
402 * The Java array is pinned or copied for use in the datum, and must
403 * be released after use by releaseBytes.
404 *
405 * Returns true if the array is non-null and could be pinned. Otherwise,
406 * returns false.
407 */
408int arrayToDatum(JNIEnv *env, jbyteArray fromArray, datum *toDatum)
409{
410 if (fromArray) {
411 toDatum->dptr = (*env)->GetByteArrayElements(env, fromArray, 0);
412 toDatum->dsize = (*env)->GetArrayLength(env, fromArray);
413 return (int) toDatum->dptr;
414 }
415 else
416 return 0;
417}
418
419/** Release a byte array pinned or copied for use in a datum. */
420void releaseArray(JNIEnv *env, jbyteArray array, datum *fromDatum)
421{
422 ASSERT(fromDatum->dptr);
423 (*env)->ReleaseByteArrayElements(env, array, fromDatum->dptr, 0);
424 fromDatum->dptr = 0; /* no longer valid */
425}
426
427/** Release a byte array pinned or copied for use in a datum, aborting
428 * any changes. This potentially saves the runtime from having to
429 * copy back an unchanged array. */
430void releaseArrayAbort(JNIEnv *env, jbyteArray array, datum *fromDatum)
431{
432 ASSERT(fromDatum->dptr);
433 (*env)->ReleaseByteArrayElements(env, array, fromDatum->dptr,
434 JNI_ABORT);
435 fromDatum->dptr = 0; /* no longer valid */
436}
437
438
439/** Throw a null pointer exception. */
440void nullPtrException(JNIEnv *env, const char *file, int line)
441{
442 jclass exClass;
443 char reason[1024];
444 sprintf(reason, "Null pointer exception in GDBM wrapper (%s:%d)",
445 file, line);
446
447 exClass = (*env)->FindClass(env, "java/lang/NullPointerException");
448 ASSERT(exClass);
449
450 (*env)->ThrowNew(env, exClass, reason);
451}
452
453/** Translate the GDBM error into throw a Java exception, and throw
454 * same.
455 *
456 * TODO: Throw different classes of exceptions depending on what the
457 * underlying error is.
458 */
459void gdbmException(JNIEnv *env, const char *file, int line) {
460 jclass exClass;
461 static char reason[1500];
462 static char srcLocation[500];
463
464 exClass = (*env)->FindClass(env, "au/com/pharos/gdbm/GdbmException");
465 ASSERT(exClass);
466
467 strncpy(reason, gdbm_strerror(gdbm_errno), 500);
468 sprintf(srcLocation, " (%s:%d)", file, line);
469
470 /* If the error code suggests that an OS or stdio error may have occurred,
471 * include supplementary information from errno. */
472 switch (gdbm_errno) {
473 case GDBM_FILE_OPEN_ERROR:
474 case GDBM_FILE_WRITE_ERROR:
475 case GDBM_FILE_SEEK_ERROR:
476 case GDBM_FILE_READ_ERROR:
477 case GDBM_MALLOC_ERROR:
478 case GDBM_REORGANIZE_FAILED:
479 strcat(reason, ": ");
480 strncat(reason, strerror(errno), 490);
481 strcat(reason, "?");
482 default:
483 /* errno is probably not useful */
484 ;
485 }
486
487 strncat(reason, srcLocation, 495);
488 gdbm_errno = GDBM_NO_ERROR; /* this one has been handled */
489 (*env)->ThrowNew(env, exClass, reason);
490}
491
492/*
493 * Local variables:
494 * c-basic-offset: 4
495 * End:
496 */
Note: See TracBrowser for help on using the repository browser.