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

Last change on this file since 10762 was 10762, checked in by kjdon, 19 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
File size: 11.4 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 10762 2005-10-25 02:01:59Z 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#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 repository browser.