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

Revision 26674, 12.1 KB (checked in by davidb, 7 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
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$
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 browser.