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

Last change on this file since 10765 was 10765, checked in by kjdon, 16 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
File size: 11.6 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 10765 2005-10-25 02:52:00Z 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 /* 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 repository browser.