source: trunk/gsdl3/packages/javagdbm/jni/gdbmjava.c@ 10737

Last change on this file since 10737 was 10737, checked in by kjdon, 18 years ago

Java Wrapper for GDBM, from Martin Pool. Original website gone, so added it all in here. I have modified the Makefiles to work in greenstone, and on macs, and added windows makefiles

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