source: main/trunk/greenstone3/src/packages/javagdbm/jni/gdbmjava.c

Last change on this file was 35559, checked in by davidb, 3 years ago

ifdef needs to be lowercase

  • Property svn:keywords set to Author Date Id Revision
File size: 14.7 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 35559 2021-10-12 05:25:51Z davidb $
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#ifdef __MINGW32__
35#define MSDOS
36#endif
37
38#if defined (_MSC_VER)
39#include <gdbmdefs.h>
40#include <gdbmerrno.h>
41#define GDBM_FILE gdbm_file_info *
42extern char* gdbm_version;
43extern char *gdbm_strerror(gdbm_error);
44#else
45#include <gdbm.h>
46#endif
47
48#include <jni.h>
49
50#ifdef __MINGW32__
51
52/* (Cross) compiling for Windows Want the type definitions in *win32*
53 version of jni_md.h but with the mingw c compiler this then leads
54 to C-mangled style functions (param/num args encoded in function
55 name) which we *don't* want. The following achieves this */
56
57#undef JNIEXPORT
58#undef JNIIMPORT
59#undef JNICALL
60
61#define JNIEXPORT
62#define JNIIMPORT
63#define JNICALL
64#endif
65
66#include <GdbmFile.h>
67
68#ifdef DEBUG
69#define ASSERT(x) if (!(x)) { \
70 fprintf(stderr, "%s:%d: assertion failed\n", __FILE__, __LINE__); \
71 abort(); }
72#else /* !DEBUG */
73#define ASSERT(x)
74#endif /* !DEBUG */
75
76/* The Java class within which the native methods are declared */
77#define JAVA_CLASS "au.com.pharos.gdbm.GdbmFile"
78
79#define NULL_PTR_EXCEPTION(env) nullPtrException(env, __FILE__, __LINE__)
80#define GDBM_EXCEPTION(env) gdbmException(env, __FILE__, __LINE__)
81
82#define CHECK_NOT_NULL(ptr, env) if (!ptr) { NULL_PTR_EXCEPTION(env); return 0; }
83#define CHECK_NOT_NULL_VOID(ptr, env) if (!ptr) { NULL_PTR_EXCEPTION(env); return; }
84
85void gdbmException(JNIEnv *env, const char *file, int line);
86void nullPtrException(JNIEnv *env, const char *file, int line);
87
88void releaseArray(JNIEnv *env, jbyteArray array, datum *fromDatum);
89void releaseArrayAbort(JNIEnv *env, jbyteArray array, datum *fromDatum);
90int arrayToDatum(JNIEnv *env, jbyteArray fromArray, datum *toDatum);
91jbyteArray datumToArray(JNIEnv *env, datum *fromDatum);
92
93/*
94 The macros defined below for DBF_TO_JLONG(x) and JLONG_TO_DBF(x)
95 expect SIZEOF_VOID_P and SIZEOF_LONG to be defined.
96
97 It's possible for autoconf to determine these, but:
98 (a) This currently doesn't appear to be bound into JAVAGDBM
99 compilation sequence (config.h would need to be #included)
100 (b) Even if this was done for Unix system, there would still
101 need to be a way to do this for Windows: both 32-bit and
102 64-bit as the values would be differnt
103
104 In the event SIZEOF_VOID_P and SIZEON_LONG are not already set,
105 the solution taken here is to test for various #ifdef values
106 known to be set by Unix/Windows *compilers* that indicate
107 the type sizes they use for int, long, pointer, and
108 from that information explicitly set the values used in
109 the macro definition tests below.
110
111 For more details on what these sizes are, two useful references are:
112
113 https://en.cppreference.com/w/cpp/language/types
114 https://unix.org/version2/whatsnew/lp64_wp.html
115*/
116
117#ifndef SIZEOF_VOID_P
118# if defined ( _MSC_VER ) /* i.e., Microsoft Visual Studio, not a Mingw based GCC */
119# if defined( _WIN64 )
120# define SIZEOF_VOID_P 8
121
122# elif defined( _WIN32 )
123# define SIZEOF_VOID_P 4
124
125# else
126#error "VisualStudio C compile neither 32-bit nor 64-bit. SIZEOF_VOID_P not specified"
127# endif
128
129# elif defined ( __GNUC__ ) /* Presumably Unix or Unix-like platform */
130
131# if defined( __i386__ ) /* i.e., ILP32: int, long and pointer are all 32-bits */
132# define SIZEOF_VOID_P 4
133
134# elif defined( __x86_64__ ) /* i.e., LP64: long, and pointer are both 64-bits */
135# define SIZEOF_VOID_P 8
136
137# else
138#error "GCC 'sizeof' model not matched as either ILP32 (32-bit) or LP64 (64-bit). SIZEOF_VOID_P not specified"
139# endif
140
141# else
142#error "Unrecognized compiler: SIZEOF_LONG not specified"
143
144# endif
145#endif
146
147
148
149#ifndef SIZEOF_LONG
150# if defined ( _MSC_VER ) /* i.e., Microsoft Visual Studio, not a Mingw based GCC */
151# if defined( _WIN64 )
152# define SIZEOF_LONG 4 /* but note that SIZEOF_VOID_P above is 8 ! */
153
154# elif defined( _WIN32 )
155# define SIZEOF_LONG 4
156
157# else
158#error "VisualStudio C compiler neither 32-bit nor 64-bit. SIZEOF_LONG not specified"
159# endif
160
161# elif __GNUC__ /* Presumably Unix or Unix-like platform */
162# if defined( __i386__ )
163# define SIZEOF_LONG 4
164
165# elif defined( __x86_64__ )
166# define SIZEOF_LONG 8
167
168# else
169#error "GCC 'sizeof' model not matched as either ILP32 (32-bit) or LP64 (64-bit_. SIZEOF_LONG not specified"
170# endif
171
172# else
173#error "Unrecognized compiler: SIZEOF_LONG not specified"
174# endif
175#endif
176
177
178
179/* Convert between a jlong and a void ptr using a well-defined cast.
180 * (Casting between a pointer and an integer of different sizes spooks
181 * both gcc and mbp. */
182
183#if (SIZEOF_VOID_P == SIZEOF_LONG)
184# define DBF_TO_JLONG(x) ((jlong)((long) x))
185# define JLONG_TO_DBF(x) ((GDBM_FILE)((long) x))
186#elif (SIZEOF_VOID_P == SIZEOF_INT)
187# define DBF_TO_JLONG(x) ((jlong)((int) (x)))
188# define JLONG_TO_DBF(x) ((GDBM_FILE)((int) (x)))
189#else
190# define DBF_TO_JLONG(x) ((jlong)(x))
191# define JLONG_TO_DBF(x) ((GDBM_FILE)(x))
192#endif
193
194
195JNIEXPORT jlong JNICALL
196Java_au_com_pharos_gdbm_GdbmFile_gdbm_1open
197 (JNIEnv *env, jobject obj, jstring fileName, jint flags)
198{
199 GDBM_FILE dbf;
200 const char *utfFileName;
201
202 utfFileName = (*env)->GetStringUTFChars(env, fileName, 0);
203 if (!utfFileName)
204 return 0;
205
206 setbuf(stderr, 0);
207
208 /* XXX: Should we let the caller specify the file mode? I think
209 * not -- Java is above the level of octal file modes. [mbp] */
210 /* Couldn't get it to work properly on Windows without the 0 here -
211 wouldn't allow mulitple READs on a single file [kjdon] */
212#if defined (__WIN32__)
213 dbf = gdbm_open((char *) utfFileName, 0, flags, 0660, NULL, 0);
214#else
215 dbf = gdbm_open((char *) utfFileName, 0, flags, 0660, NULL);
216#endif
217 if (utfFileName)
218 (*env)->ReleaseStringUTFChars(env, fileName, utfFileName);
219
220 if (!dbf) {
221 GDBM_EXCEPTION(env);
222 return 0;
223 }
224
225 return DBF_TO_JLONG(dbf);
226}
227
228
229
230
231
232JNIEXPORT jbyteArray JNICALL
233Java_au_com_pharos_gdbm_GdbmFile_gdbm_1firstkey
234(JNIEnv *env, jobject obj, jlong dbf)
235{
236 datum keyDatum;
237 jbyteArray keyArray;
238
239 CHECK_NOT_NULL(dbf, env);
240
241 keyDatum = gdbm_firstkey(JLONG_TO_DBF(dbf));
242 if ( gdbm_errno != GDBM_NO_ERROR ) {
243 GDBM_EXCEPTION(env);
244 return 0;
245 }
246 if ( !keyDatum.dptr )
247 return 0;
248
249 keyArray = datumToArray(env, &keyDatum);
250 free(keyDatum.dptr);
251
252 return keyArray;
253}
254
255
256
257
258
259JNIEXPORT jbyteArray JNICALL
260Java_au_com_pharos_gdbm_GdbmFile_gdbm_1nextkey
261(JNIEnv *env, jobject this, jlong dbf, jbyteArray keyArray)
262{
263 datum keyDatum;
264 datum nextDatum;
265 jbyteArray nextArray;
266
267 CHECK_NOT_NULL(dbf, env);
268
269 if (!arrayToDatum(env, keyArray, &keyDatum)) {
270 NULL_PTR_EXCEPTION(env);
271 return 0;
272 }
273
274 nextDatum = gdbm_nextkey(JLONG_TO_DBF(dbf), keyDatum);
275 releaseArrayAbort(env, keyArray, &keyDatum);
276
277 if ( gdbm_errno != GDBM_NO_ERROR ) {
278 GDBM_EXCEPTION(env);
279 return 0;
280 }
281 if ( !nextDatum.dptr )
282 return 0;
283
284 nextArray = datumToArray(env, &nextDatum);
285 free(nextDatum.dptr);
286
287 return nextArray;
288}
289
290
291
292
293
294
295JNIEXPORT jbyteArray JNICALL
296Java_au_com_pharos_gdbm_GdbmFile_gdbm_1fetch
297 (JNIEnv *env, jobject this, jlong dbf, jbyteArray keyArray)
298{
299 datum keyDatum;
300 datum valueDatum;
301 jbyteArray valueArray;
302
303 CHECK_NOT_NULL(dbf, env);
304
305 if (!arrayToDatum(env, keyArray, &keyDatum)) {
306 NULL_PTR_EXCEPTION(env);
307 return 0;
308 }
309
310 valueDatum = gdbm_fetch(JLONG_TO_DBF(dbf), keyDatum);
311 releaseArrayAbort(env, keyArray, &keyDatum);
312
313 if ( !valueDatum.dptr )
314 return 0;
315 if ( gdbm_errno != GDBM_NO_ERROR ) {
316 GDBM_EXCEPTION(env);
317 return 0;
318 }
319
320 valueArray = datumToArray(env, &valueDatum);
321 free(valueDatum.dptr);
322
323 return valueArray;
324}
325
326
327
328JNIEXPORT jboolean JNICALL
329Java_au_com_pharos_gdbm_GdbmFile_gdbm_1exists
330 (JNIEnv *env, jobject obj, jlong dbf, jbyteArray keyArray)
331{
332 datum keyDatum;
333 int result;
334
335 CHECK_NOT_NULL(dbf, env);
336 if (!arrayToDatum(env, keyArray, &keyDatum)) {
337 NULL_PTR_EXCEPTION(env);
338 return JNI_FALSE;
339 }
340 result = gdbm_exists(JLONG_TO_DBF(dbf), keyDatum);
341 if ( gdbm_errno != GDBM_NO_ERROR ) {
342 GDBM_EXCEPTION(env);
343 return 0;
344 }
345 releaseArrayAbort(env, keyArray, &keyDatum);
346 return result ? JNI_TRUE : JNI_FALSE;
347}
348
349
350
351
352
353JNIEXPORT void JNICALL
354Java_au_com_pharos_gdbm_GdbmFile_gdbm_1store
355 (JNIEnv *env, jobject obj, jlong dbf,
356 jbyteArray keyArray, jbyteArray valueArray, jboolean replace)
357{
358 datum keyDatum;
359 datum valueDatum;
360
361 CHECK_NOT_NULL_VOID(dbf, env);
362
363 if ( !arrayToDatum(env, keyArray, &keyDatum)
364 || !arrayToDatum(env, valueArray, &valueDatum) ) {
365 NULL_PTR_EXCEPTION(env);
366 return;
367 }
368
369 gdbm_store(JLONG_TO_DBF(dbf), keyDatum, valueDatum,
370 replace ? GDBM_REPLACE : 0);
371
372 releaseArrayAbort(env, keyArray, &keyDatum);
373 releaseArrayAbort(env, valueArray, &valueDatum);
374
375 if ( gdbm_errno != GDBM_NO_ERROR )
376 GDBM_EXCEPTION(env);
377}
378
379
380
381JNIEXPORT void JNICALL
382Java_au_com_pharos_gdbm_GdbmFile_gdbm_1delete
383 (JNIEnv *env, jobject obj, jlong dbf, jbyteArray keyArray)
384{
385 datum keyDatum;
386
387 CHECK_NOT_NULL_VOID(dbf, env);
388
389 if (!arrayToDatum(env, keyArray, &keyDatum)) {
390 NULL_PTR_EXCEPTION(env);
391 return;
392 }
393
394 gdbm_delete(JLONG_TO_DBF(dbf), keyDatum);
395
396 releaseArrayAbort(env, keyArray, &keyDatum);
397
398 if ( gdbm_errno != GDBM_NO_ERROR )
399 GDBM_EXCEPTION(env);
400}
401
402
403
404JNIEXPORT jstring JNICALL
405Java_au_com_pharos_gdbm_GdbmFile_gdbm_1getversion
406 (JNIEnv *env, jclass cls)
407{
408 return (*env)->NewStringUTF(env, gdbm_version);
409}
410
411
412JNIEXPORT jstring JNICALL
413Java_au_com_pharos_gdbm_GdbmFile_gdbm_1wrapperVersion
414 (JNIEnv *env, jclass cls)
415{
416 return (*env)->NewStringUTF(env, "JavaGDBM release 0005 built " __DATE__);
417}
418
419
420
421
422JNIEXPORT void JNICALL
423Java_au_com_pharos_gdbm_GdbmFile_gdbm_1reorganize
424 (JNIEnv *env, jobject obj, jlong dbf)
425{
426 CHECK_NOT_NULL_VOID(dbf, env);
427
428 gdbm_reorganize(JLONG_TO_DBF(dbf));
429
430 if ( gdbm_errno != GDBM_NO_ERROR )
431 GDBM_EXCEPTION(env);
432}
433
434
435
436JNIEXPORT void JNICALL
437Java_au_com_pharos_gdbm_GdbmFile_gdbm_1close
438 (JNIEnv *env, jobject obj, jlong dbf)
439{
440 CHECK_NOT_NULL_VOID(dbf, env);
441
442 gdbm_close(JLONG_TO_DBF(dbf));
443
444 if ( gdbm_errno != GDBM_NO_ERROR )
445 GDBM_EXCEPTION(env);
446}
447
448
449
450JNIEXPORT void JNICALL
451Java_au_com_pharos_gdbm_GdbmFile_gdbm_1sync
452 (JNIEnv *env, jobject obj, jlong dbf)
453{
454 CHECK_NOT_NULL_VOID(dbf, env);
455
456 gdbm_sync(JLONG_TO_DBF(dbf));
457
458 if ( gdbm_errno != GDBM_NO_ERROR )
459 GDBM_EXCEPTION(env);
460}
461
462
463
464
465/**********************************************************************
466 *
467 * Following are support functions which aid in interfacing C to
468 * Java. */
469
470/** Create a new Java byte array from a GDBM datum, and return a
471 * pointer thereto. */
472
473jbyteArray datumToArray(JNIEnv *env, datum *fromDatum)
474{
475 jbyteArray toArray;
476
477 if (!fromDatum || !fromDatum->dptr)
478 return 0;
479
480 toArray = (*env)->NewByteArray(env, fromDatum->dsize);
481 ASSERT(toArray);
482 (*env)->SetByteArrayRegion(env, toArray,
483 0, fromDatum->dsize, fromDatum->dptr);
484
485 return toArray;
486}
487
488/** Convert a Java byte array to a GDBM datum.
489 *
490 * The Java array is pinned or copied for use in the datum, and must
491 * be released after use by releaseBytes.
492 *
493 * Returns true if the array is non-null and could be pinned. Otherwise,
494 * returns false.
495 */
496int arrayToDatum(JNIEnv *env, jbyteArray fromArray, datum *toDatum)
497{
498 if (fromArray) {
499 toDatum->dptr = (*env)->GetByteArrayElements(env, fromArray, 0);
500 toDatum->dsize = (*env)->GetArrayLength(env, fromArray);
501 return (int) toDatum->dptr;
502 }
503 else
504 return 0;
505}
506
507/** Release a byte array pinned or copied for use in a datum. */
508void releaseArray(JNIEnv *env, jbyteArray array, datum *fromDatum)
509{
510 ASSERT(fromDatum->dptr);
511 (*env)->ReleaseByteArrayElements(env, array, fromDatum->dptr, 0);
512 fromDatum->dptr = 0; /* no longer valid */
513}
514
515/** Release a byte array pinned or copied for use in a datum, aborting
516 * any changes. This potentially saves the runtime from having to
517 * copy back an unchanged array. */
518void releaseArrayAbort(JNIEnv *env, jbyteArray array, datum *fromDatum)
519{
520 ASSERT(fromDatum->dptr);
521 (*env)->ReleaseByteArrayElements(env, array, fromDatum->dptr,
522 JNI_ABORT);
523 fromDatum->dptr = 0; /* no longer valid */
524}
525
526
527/** Throw a null pointer exception. */
528void nullPtrException(JNIEnv *env, const char *file, int line)
529{
530 jclass exClass;
531 char reason[1024];
532 sprintf(reason, "Null pointer exception in GDBM wrapper (%s:%d)",
533 file, line);
534
535 exClass = (*env)->FindClass(env, "java/lang/NullPointerException");
536 ASSERT(exClass);
537
538 (*env)->ThrowNew(env, exClass, reason);
539}
540
541/** Translate the GDBM error into throw a Java exception, and throw
542 * same.
543 *
544 * TODO: Throw different classes of exceptions depending on what the
545 * underlying error is.
546 */
547void gdbmException(JNIEnv *env, const char *file, int line) {
548 jclass exClass;
549 static char reason[1500];
550 static char srcLocation[500];
551
552 exClass = (*env)->FindClass(env, "au/com/pharos/gdbm/GdbmException");
553 ASSERT(exClass);
554
555 strncpy(reason, gdbm_strerror(gdbm_errno), 500);
556 sprintf(srcLocation, " (%s:%d)", file, line);
557
558 /* If the error code suggests that an OS or stdio error may have occurred,
559 * include supplementary information from errno. */
560 switch (gdbm_errno) {
561 case GDBM_FILE_OPEN_ERROR:
562 case GDBM_FILE_WRITE_ERROR:
563 case GDBM_FILE_SEEK_ERROR:
564 case GDBM_FILE_READ_ERROR:
565 case GDBM_MALLOC_ERROR:
566 case GDBM_REORGANIZE_FAILED:
567 strcat(reason, ": ");
568 strncat(reason, strerror(errno), 490);
569 strcat(reason, "?");
570 default:
571 /* errno is probably not useful */
572 ;
573 }
574
575 strncat(reason, srcLocation, 495);
576 gdbm_errno = GDBM_NO_ERROR; /* this one has been handled */
577 (*env)->ThrowNew(env, exClass, reason);
578}
579
580/*
581 * Local variables:
582 * c-basic-offset: 4
583 * End:
584 */
Note: See TracBrowser for help on using the repository browser.