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

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

While it was discovered that JAVAGDBM had some macros defined to help with type-casting between pointers and "int/long/jlong" where the pointer is handled 'numerically' as a memory location, it was found during testing under Windows 64-bit that the actual #ifdef that picks out the macro to use wasn't 'firing' correctly as SIZEOF_VOID_P and SIZEOF_LONG were not defined AT ALLsvn status The code change here takes a conservative line: it tests to see if these values are defined, and if not, resorts to a sequence of further #ifdef tests using values that gcc and VisualStudio set based on the 'size of' model for int, long, and pointers, to correcty determine what values they should be

  • Property svn:keywords set to Author Date Id Revision
File size: 14.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 35554 2021-10-12 01:55:23Z 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 Windows OS neither 32-bit nor 64-bit. SIZEOF_VOID_P not specified
127# endif
128
129# else /* Presumably Unix */
130# if defined( __ILP32 ) /* i.e., int, long and pointer are all 32-bits */
131# define SIZEOF_VOID_P 4
132
133# elif defined( __LP64 ) /* i.e., long, and pointer are both 64-bits */
134# define SIZEOF_VOID_P 8
135
136# else
137# error Compiler's 'sizeof' model not matched as either IILP32 or LP64. SIZEOF_VOID_P not specified
138# endif
139# endif
140#endif
141
142
143
144#ifndef SIZEOF_LONG
145# if defined ( _MSC_VER ) /* i.e., Microsoft Visual Studio, not a Mingw based GCC */
146# if defined( _WIN64 )
147# define SIZEOF_LONG 4 /* but note that SIZEOF_VOID_P above is 8 ! */
148
149# elif defined( _WIN32 )
150# define SIZEOF_LONG 4
151
152# else
153# error Windows OS neither 32-bit nor 64-bit. SIZEOF_LONG not specified
154# endif
155
156# else /* Presumably Unix */
157# if defined( __ILP32 )
158# define SIZEOF_LONG 4
159
160# elif defined( __LP64 )
161# define SIZEOF_LONG 8
162
163# else
164# error Compiler's 'sizeof' model not matched as either IILP32 or LP64. SIZEOF_LONG not specified
165# endif
166# endif
167#endif
168
169
170
171/* Convert between a jlong and a void ptr using a well-defined cast.
172 * (Casting between a pointer and an integer of different sizes spooks
173 * both gcc and mbp. */
174
175#if (SIZEOF_VOID_P == SIZEOF_LONG)
176# define DBF_TO_JLONG(x) ((jlong)((long) x))
177# define JLONG_TO_DBF(x) ((GDBM_FILE)((long) x))
178#elif (SIZEOF_VOID_P == SIZEOF_INT)
179# define DBF_TO_JLONG(x) ((jlong)((int) (x)))
180# define JLONG_TO_DBF(x) ((GDBM_FILE)((int) (x)))
181#else
182# define DBF_TO_JLONG(x) ((jlong)(x))
183# define JLONG_TO_DBF(x) ((GDBM_FILE)(x))
184#endif
185
186
187JNIEXPORT jlong JNICALL
188Java_au_com_pharos_gdbm_GdbmFile_gdbm_1open
189 (JNIEnv *env, jobject obj, jstring fileName, jint flags)
190{
191 GDBM_FILE dbf;
192 const char *utfFileName;
193
194 utfFileName = (*env)->GetStringUTFChars(env, fileName, 0);
195 if (!utfFileName)
196 return 0;
197
198 setbuf(stderr, 0);
199
200 /* XXX: Should we let the caller specify the file mode? I think
201 * not -- Java is above the level of octal file modes. [mbp] */
202 /* Couldn't get it to work properly on Windows without the 0 here -
203 wouldn't allow mulitple READs on a single file [kjdon] */
204#if defined (__WIN32__)
205 dbf = gdbm_open((char *) utfFileName, 0, flags, 0660, NULL, 0);
206#else
207 dbf = gdbm_open((char *) utfFileName, 0, flags, 0660, NULL);
208#endif
209 if (utfFileName)
210 (*env)->ReleaseStringUTFChars(env, fileName, utfFileName);
211
212 if (!dbf) {
213 GDBM_EXCEPTION(env);
214 return 0;
215 }
216
217 return DBF_TO_JLONG(dbf);
218}
219
220
221
222
223
224JNIEXPORT jbyteArray JNICALL
225Java_au_com_pharos_gdbm_GdbmFile_gdbm_1firstkey
226(JNIEnv *env, jobject obj, jlong dbf)
227{
228 datum keyDatum;
229 jbyteArray keyArray;
230
231 CHECK_NOT_NULL(dbf, env);
232
233 keyDatum = gdbm_firstkey(JLONG_TO_DBF(dbf));
234 if ( gdbm_errno != GDBM_NO_ERROR ) {
235 GDBM_EXCEPTION(env);
236 return 0;
237 }
238 if ( !keyDatum.dptr )
239 return 0;
240
241 keyArray = datumToArray(env, &keyDatum);
242 free(keyDatum.dptr);
243
244 return keyArray;
245}
246
247
248
249
250
251JNIEXPORT jbyteArray JNICALL
252Java_au_com_pharos_gdbm_GdbmFile_gdbm_1nextkey
253(JNIEnv *env, jobject this, jlong dbf, jbyteArray keyArray)
254{
255 datum keyDatum;
256 datum nextDatum;
257 jbyteArray nextArray;
258
259 CHECK_NOT_NULL(dbf, env);
260
261 if (!arrayToDatum(env, keyArray, &keyDatum)) {
262 NULL_PTR_EXCEPTION(env);
263 return 0;
264 }
265
266 nextDatum = gdbm_nextkey(JLONG_TO_DBF(dbf), keyDatum);
267 releaseArrayAbort(env, keyArray, &keyDatum);
268
269 if ( gdbm_errno != GDBM_NO_ERROR ) {
270 GDBM_EXCEPTION(env);
271 return 0;
272 }
273 if ( !nextDatum.dptr )
274 return 0;
275
276 nextArray = datumToArray(env, &nextDatum);
277 free(nextDatum.dptr);
278
279 return nextArray;
280}
281
282
283
284
285
286
287JNIEXPORT jbyteArray JNICALL
288Java_au_com_pharos_gdbm_GdbmFile_gdbm_1fetch
289 (JNIEnv *env, jobject this, jlong dbf, jbyteArray keyArray)
290{
291 datum keyDatum;
292 datum valueDatum;
293 jbyteArray valueArray;
294
295 CHECK_NOT_NULL(dbf, env);
296
297 if (!arrayToDatum(env, keyArray, &keyDatum)) {
298 NULL_PTR_EXCEPTION(env);
299 return 0;
300 }
301
302 valueDatum = gdbm_fetch(JLONG_TO_DBF(dbf), keyDatum);
303 releaseArrayAbort(env, keyArray, &keyDatum);
304
305 if ( !valueDatum.dptr )
306 return 0;
307 if ( gdbm_errno != GDBM_NO_ERROR ) {
308 GDBM_EXCEPTION(env);
309 return 0;
310 }
311
312 valueArray = datumToArray(env, &valueDatum);
313 free(valueDatum.dptr);
314
315 return valueArray;
316}
317
318
319
320JNIEXPORT jboolean JNICALL
321Java_au_com_pharos_gdbm_GdbmFile_gdbm_1exists
322 (JNIEnv *env, jobject obj, jlong dbf, jbyteArray keyArray)
323{
324 datum keyDatum;
325 int result;
326
327 CHECK_NOT_NULL(dbf, env);
328 if (!arrayToDatum(env, keyArray, &keyDatum)) {
329 NULL_PTR_EXCEPTION(env);
330 return JNI_FALSE;
331 }
332 result = gdbm_exists(JLONG_TO_DBF(dbf), keyDatum);
333 if ( gdbm_errno != GDBM_NO_ERROR ) {
334 GDBM_EXCEPTION(env);
335 return 0;
336 }
337 releaseArrayAbort(env, keyArray, &keyDatum);
338 return result ? JNI_TRUE : JNI_FALSE;
339}
340
341
342
343
344
345JNIEXPORT void JNICALL
346Java_au_com_pharos_gdbm_GdbmFile_gdbm_1store
347 (JNIEnv *env, jobject obj, jlong dbf,
348 jbyteArray keyArray, jbyteArray valueArray, jboolean replace)
349{
350 datum keyDatum;
351 datum valueDatum;
352
353 CHECK_NOT_NULL_VOID(dbf, env);
354
355 if ( !arrayToDatum(env, keyArray, &keyDatum)
356 || !arrayToDatum(env, valueArray, &valueDatum) ) {
357 NULL_PTR_EXCEPTION(env);
358 return;
359 }
360
361 gdbm_store(JLONG_TO_DBF(dbf), keyDatum, valueDatum,
362 replace ? GDBM_REPLACE : 0);
363
364 releaseArrayAbort(env, keyArray, &keyDatum);
365 releaseArrayAbort(env, valueArray, &valueDatum);
366
367 if ( gdbm_errno != GDBM_NO_ERROR )
368 GDBM_EXCEPTION(env);
369}
370
371
372
373JNIEXPORT void JNICALL
374Java_au_com_pharos_gdbm_GdbmFile_gdbm_1delete
375 (JNIEnv *env, jobject obj, jlong dbf, jbyteArray keyArray)
376{
377 datum keyDatum;
378
379 CHECK_NOT_NULL_VOID(dbf, env);
380
381 if (!arrayToDatum(env, keyArray, &keyDatum)) {
382 NULL_PTR_EXCEPTION(env);
383 return;
384 }
385
386 gdbm_delete(JLONG_TO_DBF(dbf), keyDatum);
387
388 releaseArrayAbort(env, keyArray, &keyDatum);
389
390 if ( gdbm_errno != GDBM_NO_ERROR )
391 GDBM_EXCEPTION(env);
392}
393
394
395
396JNIEXPORT jstring JNICALL
397Java_au_com_pharos_gdbm_GdbmFile_gdbm_1getversion
398 (JNIEnv *env, jclass cls)
399{
400 return (*env)->NewStringUTF(env, gdbm_version);
401}
402
403
404JNIEXPORT jstring JNICALL
405Java_au_com_pharos_gdbm_GdbmFile_gdbm_1wrapperVersion
406 (JNIEnv *env, jclass cls)
407{
408 return (*env)->NewStringUTF(env, "JavaGDBM release 0005 built " __DATE__);
409}
410
411
412
413
414JNIEXPORT void JNICALL
415Java_au_com_pharos_gdbm_GdbmFile_gdbm_1reorganize
416 (JNIEnv *env, jobject obj, jlong dbf)
417{
418 CHECK_NOT_NULL_VOID(dbf, env);
419
420 gdbm_reorganize(JLONG_TO_DBF(dbf));
421
422 if ( gdbm_errno != GDBM_NO_ERROR )
423 GDBM_EXCEPTION(env);
424}
425
426
427
428JNIEXPORT void JNICALL
429Java_au_com_pharos_gdbm_GdbmFile_gdbm_1close
430 (JNIEnv *env, jobject obj, jlong dbf)
431{
432 CHECK_NOT_NULL_VOID(dbf, env);
433
434 gdbm_close(JLONG_TO_DBF(dbf));
435
436 if ( gdbm_errno != GDBM_NO_ERROR )
437 GDBM_EXCEPTION(env);
438}
439
440
441
442JNIEXPORT void JNICALL
443Java_au_com_pharos_gdbm_GdbmFile_gdbm_1sync
444 (JNIEnv *env, jobject obj, jlong dbf)
445{
446 CHECK_NOT_NULL_VOID(dbf, env);
447
448 gdbm_sync(JLONG_TO_DBF(dbf));
449
450 if ( gdbm_errno != GDBM_NO_ERROR )
451 GDBM_EXCEPTION(env);
452}
453
454
455
456
457/**********************************************************************
458 *
459 * Following are support functions which aid in interfacing C to
460 * Java. */
461
462/** Create a new Java byte array from a GDBM datum, and return a
463 * pointer thereto. */
464
465jbyteArray datumToArray(JNIEnv *env, datum *fromDatum)
466{
467 jbyteArray toArray;
468
469 if (!fromDatum || !fromDatum->dptr)
470 return 0;
471
472 toArray = (*env)->NewByteArray(env, fromDatum->dsize);
473 ASSERT(toArray);
474 (*env)->SetByteArrayRegion(env, toArray,
475 0, fromDatum->dsize, fromDatum->dptr);
476
477 return toArray;
478}
479
480/** Convert a Java byte array to a GDBM datum.
481 *
482 * The Java array is pinned or copied for use in the datum, and must
483 * be released after use by releaseBytes.
484 *
485 * Returns true if the array is non-null and could be pinned. Otherwise,
486 * returns false.
487 */
488int arrayToDatum(JNIEnv *env, jbyteArray fromArray, datum *toDatum)
489{
490 if (fromArray) {
491 toDatum->dptr = (*env)->GetByteArrayElements(env, fromArray, 0);
492 toDatum->dsize = (*env)->GetArrayLength(env, fromArray);
493 return (int) toDatum->dptr;
494 }
495 else
496 return 0;
497}
498
499/** Release a byte array pinned or copied for use in a datum. */
500void releaseArray(JNIEnv *env, jbyteArray array, datum *fromDatum)
501{
502 ASSERT(fromDatum->dptr);
503 (*env)->ReleaseByteArrayElements(env, array, fromDatum->dptr, 0);
504 fromDatum->dptr = 0; /* no longer valid */
505}
506
507/** Release a byte array pinned or copied for use in a datum, aborting
508 * any changes. This potentially saves the runtime from having to
509 * copy back an unchanged array. */
510void releaseArrayAbort(JNIEnv *env, jbyteArray array, datum *fromDatum)
511{
512 ASSERT(fromDatum->dptr);
513 (*env)->ReleaseByteArrayElements(env, array, fromDatum->dptr,
514 JNI_ABORT);
515 fromDatum->dptr = 0; /* no longer valid */
516}
517
518
519/** Throw a null pointer exception. */
520void nullPtrException(JNIEnv *env, const char *file, int line)
521{
522 jclass exClass;
523 char reason[1024];
524 sprintf(reason, "Null pointer exception in GDBM wrapper (%s:%d)",
525 file, line);
526
527 exClass = (*env)->FindClass(env, "java/lang/NullPointerException");
528 ASSERT(exClass);
529
530 (*env)->ThrowNew(env, exClass, reason);
531}
532
533/** Translate the GDBM error into throw a Java exception, and throw
534 * same.
535 *
536 * TODO: Throw different classes of exceptions depending on what the
537 * underlying error is.
538 */
539void gdbmException(JNIEnv *env, const char *file, int line) {
540 jclass exClass;
541 static char reason[1500];
542 static char srcLocation[500];
543
544 exClass = (*env)->FindClass(env, "au/com/pharos/gdbm/GdbmException");
545 ASSERT(exClass);
546
547 strncpy(reason, gdbm_strerror(gdbm_errno), 500);
548 sprintf(srcLocation, " (%s:%d)", file, line);
549
550 /* If the error code suggests that an OS or stdio error may have occurred,
551 * include supplementary information from errno. */
552 switch (gdbm_errno) {
553 case GDBM_FILE_OPEN_ERROR:
554 case GDBM_FILE_WRITE_ERROR:
555 case GDBM_FILE_SEEK_ERROR:
556 case GDBM_FILE_READ_ERROR:
557 case GDBM_MALLOC_ERROR:
558 case GDBM_REORGANIZE_FAILED:
559 strcat(reason, ": ");
560 strncat(reason, strerror(errno), 490);
561 strcat(reason, "?");
562 default:
563 /* errno is probably not useful */
564 ;
565 }
566
567 strncat(reason, srcLocation, 495);
568 gdbm_errno = GDBM_NO_ERROR; /* this one has been handled */
569 (*env)->ThrowNew(env, exClass, reason);
570}
571
572/*
573 * Local variables:
574 * c-basic-offset: 4
575 * End:
576 */
Note: See TracBrowser for help on using the repository browser.