1 /*
   2  * Copyright (c) 2012, Oracle and/or its affiliates. All rights reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.  Oracle designates this
   8  * particular file as subject to the "Classpath" exception as provided
   9  * by Oracle in the LICENSE file that accompanied this code.
  10  *
  11  * This code is distributed in the hope that it will be useful, but WITHOUT
  12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  14  * version 2 for more details (a copy is included in the LICENSE file that
  15  * accompanied this code).
  16  *
  17  * You should have received a copy of the GNU General Public License version
  18  * 2 along with this work; if not, write to the Free Software Foundation,
  19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  20  *
  21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  22  * or visit www.oracle.com if you need additional information or have any
  23  * questions.
  24  */
  25 
  26 #include <stdlib.h>
  27 #include <string.h>
  28 
  29 #ifdef _WIN32           /* needed for htonl, Ugh! */
  30 #include <winsock2.h>
  31 #else
  32 #include <netinet/in.h>
  33 #endif
  34 
  35 #include "jni.h"
  36 #include "jlong.h"
  37 #include "jni_util.h"
  38 #include "org_openjdk_jigsaw_PersistentTreeMap.h"
  39 #include "PersistentTreeMap.h"
  40 
  41 static jboolean
  42 error(JNIEnv *env, int rv)
  43 {
  44     if (rv != 0) {
  45         char *m = libdb_db_strerror(rv);
  46         JNU_ThrowIOException(env, m);
  47         return JNI_TRUE;
  48     }
  49     return JNI_FALSE;
  50 }
  51 
  52 static const char *
  53 getutf(JNIEnv *env, jstring s)
  54 {
  55     // TODO: replace with GetStringUTFRegion and use on stack buffer
  56     //       to avoid malloc from GetStringUTFChars???
  57     const char *u = (*env)->GetStringUTFChars(env, s, NULL);
  58     if (u == NULL) {
  59         JNU_ThrowOutOfMemoryError(env, "org.openjdk.jigsaw.PersistentTreeMap.getutf");
  60         return NULL;
  61     }
  62     return u;
  63 }
  64 
  65 static void freeutf(JNIEnv *env, jstring s, const char *su)
  66 {
  67     (*env)->ReleaseStringUTFChars(env, s, su);
  68 }
  69 
  70 JNIEXPORT void JNICALL
  71 Java_org_openjdk_jigsaw_PersistentTreeMap_initialize(JNIEnv *env, jclass cl)
  72 {
  73     if (libdb_db_create == NULL)
  74         loadLibrary(env);
  75 }
  76 
  77 JNIEXPORT jlong JNICALL
  78 Java_org_openjdk_jigsaw_PersistentTreeMap_create0(JNIEnv *env, jclass cl,
  79                                                   jstring path)
  80 {
  81     DB *dbp;
  82     int rv;
  83     const char *pathb = getutf(env, path);
  84     if (pathb == NULL)
  85         return 0;
  86 
  87     rv = libdb_db_create(&dbp, NULL, 0);
  88     if (error(env, rv)) {
  89         freeutf(env, path, pathb);
  90         return 0;
  91     }
  92 
  93     rv = dbp->open(dbp, NULL, pathb, NULL, DB_BTREE,
  94                    DB_CREATE | DB_TRUNCATE | DB_THREAD, 0);
  95 
  96     freeutf(env, path, pathb);
  97     if (error(env, rv)) {
  98         dbp->close(dbp, 0);
  99         return 0;
 100     }
 101     return ptr_to_jlong(dbp);
 102 }
 103 
 104 JNIEXPORT jlong JNICALL
 105 Java_org_openjdk_jigsaw_PersistentTreeMap_open0(JNIEnv *env, jclass cl,
 106                                                 jstring path)
 107 {
 108     DB *dbp;
 109     int rv;
 110     const char *pathb = getutf(env, path);
 111     if (pathb == NULL)
 112         return 0;
 113 
 114     rv = libdb_db_create(&dbp, NULL, 0);
 115     if (error(env, rv)) {
 116         freeutf(env, path, pathb);
 117         return 0;
 118     }
 119 
 120     rv = dbp->open(dbp, NULL, pathb, NULL, DB_BTREE, DB_RDONLY | DB_THREAD, 0);
 121     freeutf(env, path, pathb);
 122     if (error(env, rv)) {
 123         dbp->close(dbp, 0);
 124         return 0;
 125     }
 126     return ptr_to_jlong(dbp);
 127 }
 128 
 129 JNIEXPORT void JNICALL
 130 Java_org_openjdk_jigsaw_PersistentTreeMap_put0(JNIEnv *env, jobject ob,
 131                                                jlong dbl, jstring key,
 132                                                jstring val)
 133 {
 134     DB *dbp = jlong_to_ptr(dbl);
 135     DBT dbkey, dbdata;
 136     int rv;
 137     const char *v;
 138     const char *k = getutf(env, key);
 139     if (k == NULL)
 140         return;
 141     v = getutf(env, val);
 142     if (v == NULL) {
 143         freeutf(env, key, k);
 144         return;
 145     }
 146 
 147     memset(&dbkey, 0, sizeof(DBT));
 148     memset(&dbdata, 0, sizeof(DBT));
 149     dbkey.data = (void*)k;
 150     dbkey.size = dbkey.ulen = strlen(k);
 151     dbdata.data = (void*)v;
 152     dbdata.size = dbdata.ulen = strlen(v);
 153 
 154     rv = dbp->put(dbp, NULL, &dbkey, &dbdata, 0);
 155 
 156     freeutf(env, key, k);
 157     freeutf(env, val, v);
 158     (void)error(env, rv);
 159 }
 160 
 161 JNIEXPORT jstring JNICALL
 162 Java_org_openjdk_jigsaw_PersistentTreeMap_get0(JNIEnv *env, jobject ob,
 163                                                jlong dbl, jstring key)
 164 {
 165     DB *dbp = jlong_to_ptr(dbl);
 166     DBT dbkey, dbdata;
 167     int rv;
 168     char valbuf[1024];     // TODO: 1k limit on data value, increase/malloc?
 169     const char *k = getutf(env, key);
 170     if (k == NULL)
 171         return NULL;
 172 
 173     memset(&dbkey, 0, sizeof(DBT));
 174     memset(&dbdata, 0, sizeof(DBT));
 175     dbkey.data = (void*)k;
 176     dbkey.size = dbkey.ulen = strlen(k);
 177     dbdata.data = valbuf;
 178     dbdata.ulen = 1023;     // leave space for null terminator
 179     dbdata.flags = DB_DBT_USERMEM;
 180 
 181     rv = dbp->get(dbp, NULL, &dbkey, &dbdata, 0);
 182     freeutf(env, key, k);
 183     if (rv == DB_NOTFOUND || error(env, rv) || dbdata.data == NULL)
 184         return NULL;
 185 
 186     // TODO: check for DB_BUFFER_SMALL (possibly in error) and handle
 187     valbuf[dbdata.size] = 0; // null terminate
 188 
 189     return (*env)->NewStringUTF(env, valbuf);
 190 }
 191 
 192 JNIEXPORT void JNICALL
 193 Java_org_openjdk_jigsaw_PersistentTreeMap_put1(JNIEnv *env, jobject ob,
 194                                                jlong dbl, jstring key,
 195                                                jint val)
 196 {
 197     DB *dbp = jlong_to_ptr(dbl);
 198     DBT dbkey, dbdata;
 199     int rv;
 200     int aval = htonl(val);
 201     const char *k = getutf(env, key);
 202     if (k == NULL)
 203         return;
 204 
 205     memset(&dbkey, 0, sizeof(DBT));
 206     memset(&dbdata, 0, sizeof(DBT));
 207     dbkey.data = (void*)k;
 208     dbkey.size = dbkey.ulen = strlen(k);
 209     dbdata.data = &aval;
 210     dbdata.size = dbdata.ulen = sizeof(aval);
 211 
 212     rv = dbp->put(dbp, NULL, &dbkey, &dbdata, 0);
 213     freeutf(env, key, k);
 214     (void)error(env, rv);
 215 }
 216 
 217 JNIEXPORT jint JNICALL
 218 Java_org_openjdk_jigsaw_PersistentTreeMap_get1(JNIEnv *env, jobject ob,
 219                                                jlong dbl, jstring key)
 220 {
 221     DB *dbp = jlong_to_ptr(dbl);
 222     DBT dbkey, dbdata;
 223     int rv;
 224     jint ival;
 225     const char *k = getutf(env, key);
 226     if (k == NULL)
 227         return -1;
 228 
 229     memset(&dbkey, 0, sizeof(DBT));
 230     memset(&dbdata, 0, sizeof(DBT));
 231     dbkey.data = (void*)k;
 232     dbkey.size = dbkey.ulen = strlen(k);
 233     dbdata.data = &ival;
 234     dbdata.ulen = sizeof(ival);
 235     dbdata.flags = DB_DBT_USERMEM;
 236 
 237     rv = dbp->get(dbp, NULL, &dbkey, &dbdata, 0);
 238     freeutf(env, key, k);
 239     if (rv == DB_NOTFOUND || error(env, rv) || dbdata.data == NULL)
 240         return -1;
 241 
 242     return ntohl(ival);
 243 }
 244 
 245 JNIEXPORT void JNICALL
 246 Java_org_openjdk_jigsaw_PersistentTreeMap_put2(JNIEnv *env, jobject ob,
 247                                                jlong dbl, jstring key,
 248                                                jstring sval, jint ival)
 249 {
 250     DB *dbp = jlong_to_ptr(dbl);
 251     DBT dbkey, dbdata;
 252     int rv, slen, sulen, blen;
 253     char *valbuf;
 254     int aval = htonl(ival);
 255     const char *k = getutf(env, key);
 256     if (k == NULL)
 257         return;
 258 
 259     slen = (*env)->GetStringLength(env, sval);
 260     sulen = (*env)->GetStringUTFLength(env, sval);  //TODO; error checking jni fails?
 261     blen = sulen + sizeof(aval);
 262     valbuf = (char*)malloc(blen + 1);    //TODO: on stack buffer if small enough?
 263     memcpy(valbuf, &aval, sizeof(aval));
 264     (*env)->GetStringUTFRegion(env, sval, 0, slen, valbuf + sizeof(aval));
 265 
 266     memset(&dbkey, 0, sizeof(DBT));
 267     memset(&dbdata, 0, sizeof(DBT));
 268     dbkey.data = (void*)k;
 269     dbkey.size = dbkey.ulen = strlen(k);
 270     dbdata.data = (void*)valbuf;
 271     dbdata.size = dbdata.ulen = blen;
 272     dbdata.flags = DB_DBT_USERMEM;
 273 
 274     rv = dbp->put(dbp, NULL, &dbkey, &dbdata, 0);
 275     freeutf(env, key, k);
 276     free(valbuf);
 277     (void)error(env, rv);
 278 }
 279 
 280 JNIEXPORT jboolean JNICALL
 281 Java_org_openjdk_jigsaw_PersistentTreeMap_get2(JNIEnv *env, jobject ob,
 282                                                jlong dbl, jstring key,
 283                                                jobjectArray svala,
 284                                                jintArray ivala)
 285 {
 286     DB *dbp = jlong_to_ptr(dbl);
 287     DBT dbkey, dbdata;
 288     int rv;
 289     char valbuf[1024];
 290     jint ival;
 291     jstring sval;
 292     const char *k = getutf(env, key);
 293     if (k == NULL)
 294         return JNI_FALSE;
 295 
 296     memset(&dbkey, 0, sizeof(DBT));
 297     memset(&dbdata, 0, sizeof(DBT));
 298     dbkey.data = (void*)k;
 299     dbkey.size = dbkey.ulen = strlen(k);
 300     dbdata.data = valbuf;
 301     dbdata.ulen = 1023;     // leave space for null terminator
 302     dbdata.flags = DB_DBT_USERMEM;
 303 
 304     rv = dbp->get(dbp, NULL, &dbkey, &dbdata, 0);
 305     freeutf(env, key, k);
 306     if (rv == DB_NOTFOUND || error(env, rv) ||
 307         dbdata.data == NULL || dbdata.size < sizeof(jint))
 308         return JNI_FALSE;
 309 
 310     // TODO: check for DB_BUFFER_SMALL (possibly in error) and handle
 311     valbuf[dbdata.size] = 0; // null terminate
 312 
 313     ival = ntohl(*((jint *)valbuf));
 314     sval = (*env)->NewStringUTF(env, valbuf + sizeof(ival));
 315     (*env)->SetObjectArrayElement(env, svala, 0, sval);
 316     (*env)->SetIntArrayRegion(env, ivala, 0, 1, &ival);
 317     return JNI_TRUE;
 318 }
 319 
 320 JNIEXPORT void JNICALL
 321 Java_org_openjdk_jigsaw_PersistentTreeMap_close0(JNIEnv *env, jobject ob,
 322                                                  jlong dbl)
 323 {
 324     DB *dbp = jlong_to_ptr(dbl);
 325     (void)error(env, dbp->close(dbp, 0));
 326 }