root/trunk/gsdl3/packages/javagdbm/jni/gdbmjava.c @ 10762

Revision 10762, 11.4 KB (checked in by kjdon, 14 years ago)

added a 0 as teh last parameter to gdbm_open, line 102. this seems to be needed to make it work under windows. hope it still works under linux :-)

  • 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#if defined (__WIN32__)
35#include <gdbmdefs.h>
36#include <gdbmerrno.h>
37#define GDBM_FILE gdbm_file_info *
38extern char* gdbm_version;
39#else
40#include <gdbm.h>
41#endif
42
43#include <jni.h>
44
45#include <GdbmFile.h>
46
47#ifdef DEBUG
48#define ASSERT(x) if (!(x)) { \
49  fprintf(stderr, "%s:%d: assertion failed\n", __FILE__, __LINE__); \
50  abort(); }
51#else /* !DEBUG */
52#define ASSERT(x)
53#endif /* !DEBUG */
54
55/* The Java class within which the native methods are declared */
56#define JAVA_CLASS "au.com.pharos.gdbm.GdbmFile"
57
58#define NULL_PTR_EXCEPTION(env) nullPtrException(env, __FILE__, __LINE__)
59#define GDBM_EXCEPTION(env) gdbmException(env, __FILE__, __LINE__)
60
61#define CHECK_NOT_NULL(ptr, env) if (!ptr) { NULL_PTR_EXCEPTION(env); return 0; }
62#define CHECK_NOT_NULL_VOID(ptr, env) if (!ptr) { NULL_PTR_EXCEPTION(env); return; }
63
64void gdbmException(JNIEnv *env, const char *file, int line);
65void nullPtrException(JNIEnv *env, const char *file, int line);
66
67void releaseArray(JNIEnv *env, jbyteArray array, datum *fromDatum);
68void releaseArrayAbort(JNIEnv *env, jbyteArray array, datum *fromDatum);
69int arrayToDatum(JNIEnv *env, jbyteArray fromArray, datum *toDatum);
70jbyteArray datumToArray(JNIEnv *env, datum *fromDatum);
71
72/* Convert between a jlong and a void ptr using a well-defined cast.
73 * (Casting between a pointer and an integer of different sizes spooks
74 * both gcc and mbp. */
75#if (SIZEOF_VOID_P == SIZEOF_LONG)
76#  define DBF_TO_JLONG(x) ((jlong)((long) x))
77#  define JLONG_TO_DBF(x) ((GDBM_FILE)((long) x))
78#elif (SIZEOF_VOID_P == SIZEOF_INT)
79#  define DBF_TO_JLONG(x) ((jlong)((int) (x)))
80#  define JLONG_TO_DBF(x) ((GDBM_FILE)((int) (x)))
81#else
82#  define DBF_TO_JLONG(x) ((jlong)(x))
83#  define JLONG_TO_DBF(x) ((GDBM_FILE)(x))
84#endif
85
86
87JNIEXPORT jlong JNICALL
88Java_au_com_pharos_gdbm_GdbmFile_gdbm_1open
89    (JNIEnv *env, jobject obj, jstring fileName, jint flags)
90{
91    GDBM_FILE dbf;
92    const char *utfFileName;
93   
94    utfFileName = (*env)->GetStringUTFChars(env, fileName, 0);
95    if (!utfFileName)
96    return 0;
97   
98    setbuf(stderr, 0);
99   
100    /* XXX: Should we let the caller specify the file mode?  I think
101     * not -- Java is above the level of octal file modes. [mbp] */
102    dbf = gdbm_open((char *) utfFileName, 0, flags, 0660, NULL, 0);
103   
104    if (utfFileName)
105    (*env)->ReleaseStringUTFChars(env, fileName, utfFileName);
106
107    if (!dbf) {
108    GDBM_EXCEPTION(env);
109    return 0;
110    }   
111   
112    return DBF_TO_JLONG(dbf);
113}
114
115
116
117
118
119JNIEXPORT jbyteArray JNICALL
120Java_au_com_pharos_gdbm_GdbmFile_gdbm_1firstkey
121(JNIEnv *env, jobject obj, jlong dbf)
122{
123    datum   keyDatum;
124    jbyteArray  keyArray;
125
126    CHECK_NOT_NULL(dbf, env);
127
128    keyDatum = gdbm_firstkey(JLONG_TO_DBF(dbf));
129    if ( gdbm_errno != GDBM_NO_ERROR ) {
130    GDBM_EXCEPTION(env);
131    return 0;
132    }
133    if ( !keyDatum.dptr )
134    return 0;
135
136    keyArray = datumToArray(env, &keyDatum);
137    free(keyDatum.dptr);
138
139    return keyArray;
140}
141
142
143
144
145
146JNIEXPORT jbyteArray JNICALL
147Java_au_com_pharos_gdbm_GdbmFile_gdbm_1nextkey
148(JNIEnv *env, jobject this, jlong dbf, jbyteArray keyArray)
149{
150    datum   keyDatum;
151    datum   nextDatum;
152    jbyteArray  nextArray;
153
154    CHECK_NOT_NULL(dbf, env);
155
156    if (!arrayToDatum(env, keyArray, &keyDatum)) {
157    NULL_PTR_EXCEPTION(env);
158    return 0;
159    }
160   
161    nextDatum = gdbm_nextkey(JLONG_TO_DBF(dbf), keyDatum);
162    releaseArrayAbort(env, keyArray, &keyDatum);
163
164    if ( gdbm_errno != GDBM_NO_ERROR ) {
165    GDBM_EXCEPTION(env);
166    return 0;
167    }
168    if ( !nextDatum.dptr )
169    return 0;
170
171    nextArray = datumToArray(env, &nextDatum);
172    free(nextDatum.dptr);
173   
174    return nextArray;
175}
176
177
178
179
180
181
182JNIEXPORT jbyteArray JNICALL
183Java_au_com_pharos_gdbm_GdbmFile_gdbm_1fetch
184  (JNIEnv *env, jobject this, jlong dbf, jbyteArray keyArray)
185{
186    datum   keyDatum;
187    datum   valueDatum;
188    jbyteArray  valueArray;
189
190    CHECK_NOT_NULL(dbf, env);
191
192    if (!arrayToDatum(env, keyArray, &keyDatum)) {
193    NULL_PTR_EXCEPTION(env);
194    return 0;
195    }
196
197    valueDatum = gdbm_fetch(JLONG_TO_DBF(dbf), keyDatum);
198    releaseArrayAbort(env, keyArray, &keyDatum);
199
200    if ( !valueDatum.dptr )
201    return 0;
202    if ( gdbm_errno != GDBM_NO_ERROR ) {
203    GDBM_EXCEPTION(env);
204    return 0;
205    }
206
207    valueArray = datumToArray(env, &valueDatum);
208    free(valueDatum.dptr);
209
210    return valueArray;
211}
212
213
214
215JNIEXPORT jboolean JNICALL
216Java_au_com_pharos_gdbm_GdbmFile_gdbm_1exists
217  (JNIEnv *env, jobject obj, jlong dbf, jbyteArray keyArray)
218{
219    datum   keyDatum;
220    int     result;
221
222    CHECK_NOT_NULL(dbf, env);
223    if (!arrayToDatum(env, keyArray, &keyDatum)) {
224    NULL_PTR_EXCEPTION(env);
225    return JNI_FALSE;
226    }
227    result = gdbm_exists(JLONG_TO_DBF(dbf), keyDatum);
228    if ( gdbm_errno != GDBM_NO_ERROR ) {
229    GDBM_EXCEPTION(env);
230    return 0;
231    }
232    releaseArrayAbort(env, keyArray, &keyDatum);
233    return result ? JNI_TRUE : JNI_FALSE;
234}
235
236
237
238
239
240JNIEXPORT void JNICALL
241Java_au_com_pharos_gdbm_GdbmFile_gdbm_1store
242  (JNIEnv *env, jobject obj, jlong dbf,
243   jbyteArray keyArray, jbyteArray valueArray, jboolean replace)
244{
245    datum   keyDatum;
246    datum   valueDatum;
247   
248    CHECK_NOT_NULL_VOID(dbf, env);
249   
250    if ( !arrayToDatum(env, keyArray, &keyDatum)
251     || !arrayToDatum(env, valueArray, &valueDatum) ) {
252    NULL_PTR_EXCEPTION(env);
253    return;
254    }
255
256    gdbm_store(JLONG_TO_DBF(dbf), keyDatum, valueDatum,
257           replace ? GDBM_REPLACE : 0);
258
259    releaseArrayAbort(env, keyArray, &keyDatum);
260    releaseArrayAbort(env, valueArray, &valueDatum);
261
262    if ( gdbm_errno != GDBM_NO_ERROR )
263    GDBM_EXCEPTION(env);
264}
265
266
267
268JNIEXPORT void JNICALL
269Java_au_com_pharos_gdbm_GdbmFile_gdbm_1delete
270   (JNIEnv *env, jobject obj, jlong dbf, jbyteArray keyArray)
271{
272    datum   keyDatum;
273
274    CHECK_NOT_NULL_VOID(dbf, env);
275
276    if (!arrayToDatum(env, keyArray, &keyDatum)) {
277    NULL_PTR_EXCEPTION(env);
278    return;
279    }
280
281    gdbm_delete(JLONG_TO_DBF(dbf), keyDatum);
282
283    releaseArrayAbort(env, keyArray, &keyDatum);
284   
285    if ( gdbm_errno != GDBM_NO_ERROR )
286    GDBM_EXCEPTION(env);
287}
288
289
290
291JNIEXPORT jstring JNICALL
292Java_au_com_pharos_gdbm_GdbmFile_gdbm_1getversion
293  (JNIEnv *env, jclass cls)
294{
295    return (*env)->NewStringUTF(env, gdbm_version);
296}
297
298
299JNIEXPORT jstring JNICALL
300Java_au_com_pharos_gdbm_GdbmFile_gdbm_1wrapperVersion
301  (JNIEnv *env, jclass cls)
302{
303    return (*env)->NewStringUTF(env, "JavaGDBM release 0005 built " __DATE__);
304}
305               
306
307
308
309JNIEXPORT void JNICALL
310Java_au_com_pharos_gdbm_GdbmFile_gdbm_1reorganize
311  (JNIEnv *env, jobject obj, jlong dbf)
312{
313    CHECK_NOT_NULL_VOID(dbf, env);
314
315    gdbm_reorganize(JLONG_TO_DBF(dbf));
316
317    if ( gdbm_errno != GDBM_NO_ERROR )
318    GDBM_EXCEPTION(env);
319}
320
321
322
323JNIEXPORT void JNICALL
324Java_au_com_pharos_gdbm_GdbmFile_gdbm_1close
325  (JNIEnv *env, jobject obj, jlong dbf)
326{
327    CHECK_NOT_NULL_VOID(dbf, env);
328
329    gdbm_close(JLONG_TO_DBF(dbf));
330
331    if ( gdbm_errno != GDBM_NO_ERROR )
332    GDBM_EXCEPTION(env);
333}
334
335
336
337JNIEXPORT void JNICALL
338Java_au_com_pharos_gdbm_GdbmFile_gdbm_1sync
339  (JNIEnv *env, jobject obj, jlong dbf)
340{
341    CHECK_NOT_NULL_VOID(dbf, env);
342
343    gdbm_sync(JLONG_TO_DBF(dbf));
344
345    if ( gdbm_errno != GDBM_NO_ERROR )
346    GDBM_EXCEPTION(env);
347}
348
349
350
351
352/**********************************************************************
353 *
354 * Following are support functions which aid in interfacing C to
355 * Java. */
356
357/** Create a new Java byte array from a GDBM datum, and return a
358 * pointer thereto.  */
359
360jbyteArray datumToArray(JNIEnv *env, datum *fromDatum)
361{
362    jbyteArray toArray;
363
364    if (!fromDatum || !fromDatum->dptr)
365    return 0;
366
367    toArray = (*env)->NewByteArray(env, fromDatum->dsize);
368    ASSERT(toArray);
369    (*env)->SetByteArrayRegion(env, toArray,
370                   0, fromDatum->dsize, fromDatum->dptr);
371   
372    return toArray;
373}
374
375/** Convert a Java byte array to a GDBM datum.
376 *
377 * The Java array is pinned or copied for use in the datum, and must
378 * be released after use by releaseBytes.
379 *
380 * Returns true if the array is non-null and could be pinned.  Otherwise,
381 * returns false.
382 */
383int arrayToDatum(JNIEnv *env, jbyteArray fromArray, datum *toDatum)
384{
385    if (fromArray) {
386    toDatum->dptr = (*env)->GetByteArrayElements(env, fromArray, 0);
387    toDatum->dsize = (*env)->GetArrayLength(env, fromArray);
388    return (int) toDatum->dptr;
389    }
390    else
391    return 0;
392}
393
394/** Release a byte array pinned or copied for use in a datum. */
395void releaseArray(JNIEnv *env, jbyteArray array, datum *fromDatum)
396{
397    ASSERT(fromDatum->dptr);
398    (*env)->ReleaseByteArrayElements(env, array, fromDatum->dptr, 0);
399    fromDatum->dptr = 0;    /* no longer valid */
400}
401
402/** Release a byte array pinned or copied for use in a datum, aborting
403 * any changes.  This potentially saves the runtime from having to
404 * copy back an unchanged array. */
405void releaseArrayAbort(JNIEnv *env, jbyteArray array, datum *fromDatum)
406{
407    ASSERT(fromDatum->dptr);
408    (*env)->ReleaseByteArrayElements(env, array, fromDatum->dptr,
409                     JNI_ABORT);
410    fromDatum->dptr = 0;    /* no longer valid */
411}
412
413
414/** Throw a null pointer exception. */
415void nullPtrException(JNIEnv *env, const char *file, int line)
416{
417    jclass exClass;
418    char reason[1024];
419    sprintf(reason, "Null pointer exception in GDBM wrapper (%s:%d)",
420        file, line);
421   
422    exClass = (*env)->FindClass(env, "java/lang/NullPointerException");
423    ASSERT(exClass);
424
425    (*env)->ThrowNew(env, exClass, reason);
426}
427
428/** Translate the GDBM error into throw a Java exception, and throw
429 * same.
430 *
431 * TODO: Throw different classes of exceptions depending on what the
432 * underlying error is.
433 */
434void gdbmException(JNIEnv *env, const char *file, int line) {
435    jclass exClass;
436    static char reason[1500];
437    static char srcLocation[500];
438
439    exClass = (*env)->FindClass(env, "au/com/pharos/gdbm/GdbmException");
440    ASSERT(exClass);
441
442    strncpy(reason, gdbm_strerror(gdbm_errno), 500);
443    sprintf(srcLocation, " (%s:%d)", file, line);
444
445    /* If the error code suggests that an OS or stdio error may have occurred,
446     * include supplementary information from errno. */
447    switch (gdbm_errno) {
448    case GDBM_FILE_OPEN_ERROR:
449    case GDBM_FILE_WRITE_ERROR:
450    case GDBM_FILE_SEEK_ERROR:
451    case GDBM_FILE_READ_ERROR:
452    case GDBM_MALLOC_ERROR:
453    case GDBM_REORGANIZE_FAILED:
454    strcat(reason, ": ");
455    strncat(reason, strerror(errno), 490);
456    strcat(reason, "?");
457    default:
458    /* errno is probably not useful */
459    ;
460    }
461   
462    strncat(reason, srcLocation, 495);
463    gdbm_errno = GDBM_NO_ERROR; /* this one has been handled */
464    (*env)->ThrowNew(env, exClass, reason);   
465}
466
467/*
468 * Local variables:
469 * c-basic-offset: 4
470 * End:
471 */
Note: See TracBrowser for help on using the browser.