Monday, August 18, 2003

An STL-like array container for JNI

After some tinkering, I tried this for an STL-like container for JNI arrays. Tell me what you think:

// Emacs, this is -*- c++ -*- code.

#ifndef JVECTOR_H_
#define JVECTOR_H_

#include <jni.h>

#include <algorithm>
#include <vector>

#include <stdint.h>

template<typename T>
class jtype;

/**
 * Base class with specializations to hide type differences in the JNI
 * API.
 *
 * XXX - handle exceptions (e.g., NULL return from NewArray).
 *
 * XXX - should switch to Get<Type>ArrayRegion and use a scroll window
 * ala SQL result sets.
 */
template<>
struct jtype<jint>
{
  typedef int32_t value_type; // jint == int32_t
  typedef jintArray array_type;

  mutable JNIEnv* env;

  explicit jtype (JNIEnv* env) : env (env) { }
  jtype (const jtype& that) : env (that.env) { }
  jtype& operator= (const jtype& that)
  { jtype tmp (that); swap (tmp); return *this; }
  ~jtype ( ) { env = 0; }

  /*
   * jsize (JNICALL *GetArrayLength)
   *   (JNIEnv *env, jarray array);
   */
  jsize GetArrayLength (jarray jarr) const
  { return env->GetArrayLength (jarr); }

  /*
   * jintArray (JNICALL *NewIntArray)
   *   (JNIEnv *env, jsize len);
   */
  array_type NewArray (jsize len) const
  { return env->NewIntArray (len); }

  /*
   * jint * (JNICALL *GetIntArrayElements)
   *   (JNIEnv *env, jintArray array, jboolean *isCopy);
   */
  value_type* GetArrayElements (array_type jarr, bool& is_copy)
  {
    jboolean b = is_copy ? JNI_TRUE : JNI_FALSE;
    value_type* arr = env->GetIntArrayElements (jarr, &b);
    is_copy = b == JNI_TRUE ? true : false;
    return arr;
  }

  value_type* GetArrayElements (array_type jarr)
  { return env->GetIntArrayElements (jarr, 0); }

  /*
   * void (JNICALL *ReleaseIntArrayElements)
   *   (JNIEnv *env, jintArray array, jint *elems, jint mode);
   */
  void ReleaseArrayElements (array_type jarr, value_type* arr, jint mode)
  { env->ReleaseIntArrayElements (jarr, arr, mode); }

  void ReleaseArrayElements (array_type jarr, value_type* arr)
    // 0 --> copy back and free element buffer
  { env->ReleaseIntArrayElements (jarr, arr, 0); }

  /*
   * void (JNICALL *GetIntArrayRegion)
   *   (JNIEnv *env, jintArray array, jsize start, jsize len, jint buf);
   */
  void GetArrayRegion (array_type jarr, jsize start, jsize len,
		       value_type* arr)
  { env->GetIntArrayRegion (jarr, start, len, arr); }

  /*
   * void (JNICALL *SetIntArrayRegion)
   *   (JNIEnv *env, jintArray array, jsize start, jsize len, const jint *buf);
   */
  void SetArrayRegion (array_type jarr, jsize start, jsize len,
		       const value_type* arr)
    // Not const.  What gives?  XXX
  { env->SetIntArrayRegion (jarr, start, len, const_cast<value_type*> (arr)); }

  void swap (jtype& that) throw ( ) { std::swap (env, that.env); }
};

template<typename T>
void
std::swap (jtype<T>& lhs, jtype<T>& rhs)
{ lhs.swap (rhs); }

/**
 * jvector<T> is an STL-like wrapper for Java arrays.  Note that Java
 * arrays are immutable although the values can be changed.  Therefore
 * operations like clear, resize, max_size, etc., make no sense are
 * are not included.
 */
template<typename T>
class jvector
  : public jtype<T>
{
public:
  typedef typename jtype<T>::value_type value_type;
  typedef typename jtype<T>::array_type array_type;
  typedef value_type* pointer;
  typedef const value_type* const_pointer;
  typedef value_type& reference;
  typedef const value_type& const_reference;

  // Replace with checked versions.  XXX
  typedef value_type* iterator;
  typedef const value_type* const_iterator;
  typedef jsize size_type;
  typedef std::ptrdiff_t difference_type;
  typedef std::reverse_iterator<const_iterator> const_reverse_iterator;
  typedef std::reverse_iterator<iterator> reverse_iterator;

private:
  array_type jarr;
  size_type len;
  pointer arr; // XXX counted_arr<T>
  bool changed;

public:
  jvector (JNIEnv* env, array_type jarr)
    : jtype<T> (env),
      jarr (jarr),
      len (GetArrayLength (jarr)),
      arr (GetArrayElements (jarr)),
      changed (false)
  { }

  jvector (const jvector& that)
    : jtype<T> (that),
      jarr (that.jarr),
      len (that.len),
      arr (that.arr),
      changed (that.changed)
  { }

  jvector operator= (const jvector& that)
  { jvector tmp (that); swap (tmp); return *this; }

  // Switching to a counted array for arr may remove the memory burden
  // on the JVM for the calls to {Get,Release}IntArrayElements past
  // the first one.  XXX
  ~jvector ( )
  { ReleaseArrayElements (jarr, arr, changed ? 0 : JNI_ABORT); }

  iterator begin ( )
  { changed = true; return arr; }
  const_iterator begin ( ) const
  { return arr; }
  iterator end ( )
  { changed = true; return arr + len; }
  const_iterator end ( ) const
  { return arr + len; }
  reverse_iterator rbegin ( )
  { changed = true; return reverse_iterator (end ( )); }
  const_reverse_iterator rbegin ( ) const
  { return const_reverse_iterator (end ( )); }
  reverse_iterator rend ( )
  { changed = true; return reverse_iterator (begin ( )); }
  const_reverse_iterator rend ( ) const
  { return const_reverse_iterator (begin ( )); }

  size_type size ( ) const
  { return size_type (end ( ) - begin ( )); }

  bool empty ( ) const
  { return begin ( ) == end ( ); }

  reference at (size_type n)
  { check_range (n); changed = true; return (*this)[n]; }
  const_reference at (size_type n) const
  { check_range (n); return (*this)[n]; }
  reference operator[](size_type n)
  { changed = true; return *(begin ( ) + n); }
  const_reference operator[](size_type n) const
  { return *(begin ( ) + n); }

  reference front ( )
  { changed = true; return *begin ( ); }
  const_reference front ( ) const
  { return *begin ( ); }

  reference back ( )
  { changed = true; return *(end ( ) - 1); }
  const_reference back ( ) const
  { return *(end ( ) - 1); }

  void swap (jvector& that)
  {
    std::swap (base, that.base);
    std::swap (jarr, that.jarr);
    std::swap (len, that.len);
    std::swap (arr, that.arr);
    std::swap (changed, that.changed);
  }

private:
  void check_range (size_type n) const
  { if (n > len) throw std::out_of_range ("jvector"); }
};

template<typename T>
void
std::swap (jvector<T>& lhs, jvector<T>& rhs)
{ lhs.swap (rhs); }

#endif // JVECTOR_H_

No comments: