root/trunk/gsdl3/src/packages/javagdbm/jni/gdbmjava.c @ 10765

Revision 10765, 11.6 KB (checked in by kjdon, 14 years ago)

made the previous change only for windows (if defined WIN32) as gdbm_open on linux doesn't have the final argument

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