// Path: src/main/java/org/lmdbjava/ByteBufferProxy.java
// public static final BufferProxy<ByteBuffer> PROXY_OPTIMAL;
// 
// Path: src/main/java/org/lmdbjava/DirectBufferProxy.java
// public static final BufferProxy<DirectBuffer> PROXY_DB
//     = new DirectBufferProxy();
// 
// Path: src/main/java/org/lmdbjava/Env.java
// public static Builder<ByteBuffer> create() {
//   return new Builder<>(PROXY_OPTIMAL);
// }
// 
// Path: src/main/java/org/lmdbjava/CursorIterable.java
// public static final class KeyVal<T> {
// 
//   private T k;
//   private T v;
// 
//   /**
//    * The key.
//    *
//    * @return key
//    */
//   public T key() {
//     return k;
//   }
// 
//   /**
//    * The value.
//    *
//    * @return value
//    */
//   public T val() {
//     return v;
//   }
// 
//   void setK(final T key) {
//     this.k = key;
//   }
// 
//   void setV(final T val) {
//     this.v = val;
//   }
// 
// }


import static java.nio.ByteBuffer.allocateDirect;
import static java.nio.charset.StandardCharsets.UTF_8;
import static java.util.concurrent.Executors.newCachedThreadPool;
import static java.util.concurrent.TimeUnit.SECONDS;
import static org.hamcrest.CoreMatchers.is;
import static org.hamcrest.CoreMatchers.notNullValue;
import static org.hamcrest.CoreMatchers.startsWith;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.lmdbjava.ByteBufferProxy.PROXY_OPTIMAL;
import static org.lmdbjava.DbiFlags.MDB_CREATE;
import static org.lmdbjava.DbiFlags.MDB_DUPSORT;
import static org.lmdbjava.DirectBufferProxy.PROXY_DB;
import static org.lmdbjava.Env.create;
import static org.lmdbjava.GetOp.MDB_SET;
import static org.lmdbjava.SeekOp.MDB_FIRST;
import static org.lmdbjava.SeekOp.MDB_LAST;
import static org.lmdbjava.SeekOp.MDB_PREV;
import java.io.File;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.util.concurrent.ExecutorService;
import org.agrona.DirectBuffer;
import org.agrona.MutableDirectBuffer;
import org.agrona.concurrent.UnsafeBuffer;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.TemporaryFolder;
import org.lmdbjava.CursorIterable.KeyVal;
  /**
   * In this fourth tutorial we'll take a quick look at the iterators. These are
   * a more Java idiomatic form of using the Cursors we looked at in tutorial 3.
   *
   * @throws IOException if a path was unavailable for memory mapping
   */
  @Test
  @SuppressWarnings("ConvertToTryWithResources")
  public void tutorial4() throws IOException {
    final Env<ByteBuffer> env = createSimpleEnv(tmp.newFolder());
    final Dbi<ByteBuffer> db = env.openDbi(DB_NAME, MDB_CREATE);

    try (Txn<ByteBuffer> txn = env.txnWrite()) {
      final ByteBuffer key = allocateDirect(env.getMaxKeySize());
      final ByteBuffer val = allocateDirect(700);

      // Insert some data. Note that ByteBuffer order defaults to Big Endian.
      // LMDB does not persist the byte order, but it's critical to sort keys.
      // If your numeric keys don't sort as expected, review buffer byte order.
      val.putInt(100);
      key.putInt(1);
      db.put(txn, key, val);
      key.clear();
      key.putInt(2);
      db.put(txn, key, val);
      key.clear();

      // Each iterable uses a cursor and must be closed when finished. Iterate
      // forward in terms of key ordering starting with the first key.
      try (CursorIterable<ByteBuffer> ci = db.iterate(txn, KeyRange.all())) {
// Path: src/main/java/org/lmdbjava/ByteBufferProxy.java
// public static final BufferProxy<ByteBuffer> PROXY_OPTIMAL;
// 
// Path: src/main/java/org/lmdbjava/DirectBufferProxy.java
// public static final BufferProxy<DirectBuffer> PROXY_DB
//     = new DirectBufferProxy();
// 
// Path: src/main/java/org/lmdbjava/Env.java
// public static Builder<ByteBuffer> create() {
//   return new Builder<>(PROXY_OPTIMAL);
// }
// 
// Path: src/main/java/org/lmdbjava/CursorIterable.java
// public static final class KeyVal<T> {
// 
//   private T k;
//   private T v;
// 
//   /**
//    * The key.
//    *
//    * @return key
//    */
//   public T key() {
//     return k;
//   }
// 
//   /**
//    * The value.
//    *
//    * @return value
//    */
//   public T val() {
//     return v;
//   }
// 
//   void setK(final T key) {
//     this.k = key;
//   }
// 
//   void setV(final T val) {
//     this.v = val;
//   }
// 
// }


// Path: src/test/java/org/lmdbjava/TutorialTest.java
import static java.nio.ByteBuffer.allocateDirect;
import static java.nio.charset.StandardCharsets.UTF_8;
import static java.util.concurrent.Executors.newCachedThreadPool;
import static java.util.concurrent.TimeUnit.SECONDS;
import static org.hamcrest.CoreMatchers.is;
import static org.hamcrest.CoreMatchers.notNullValue;
import static org.hamcrest.CoreMatchers.startsWith;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.lmdbjava.ByteBufferProxy.PROXY_OPTIMAL;
import static org.lmdbjava.DbiFlags.MDB_CREATE;
import static org.lmdbjava.DbiFlags.MDB_DUPSORT;
import static org.lmdbjava.DirectBufferProxy.PROXY_DB;
import static org.lmdbjava.Env.create;
import static org.lmdbjava.GetOp.MDB_SET;
import static org.lmdbjava.SeekOp.MDB_FIRST;
import static org.lmdbjava.SeekOp.MDB_LAST;
import static org.lmdbjava.SeekOp.MDB_PREV;
import java.io.File;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.util.concurrent.ExecutorService;
import org.agrona.DirectBuffer;
import org.agrona.MutableDirectBuffer;
import org.agrona.concurrent.UnsafeBuffer;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.TemporaryFolder;
import org.lmdbjava.CursorIterable.KeyVal;

  /**
   * In this fourth tutorial we'll take a quick look at the iterators. These are
   * a more Java idiomatic form of using the Cursors we looked at in tutorial 3.
   *
   * @throws IOException if a path was unavailable for memory mapping
   */
  @Test
  @SuppressWarnings("ConvertToTryWithResources")
  public void tutorial4() throws IOException {
    final Env<ByteBuffer> env = createSimpleEnv(tmp.newFolder());
    final Dbi<ByteBuffer> db = env.openDbi(DB_NAME, MDB_CREATE);

    try (Txn<ByteBuffer> txn = env.txnWrite()) {
      final ByteBuffer key = allocateDirect(env.getMaxKeySize());
      final ByteBuffer val = allocateDirect(700);

      // Insert some data. Note that ByteBuffer order defaults to Big Endian.
      // LMDB does not persist the byte order, but it's critical to sort keys.
      // If your numeric keys don't sort as expected, review buffer byte order.
      val.putInt(100);
      key.putInt(1);
      db.put(txn, key, val);
      key.clear();
      key.putInt(2);
      db.put(txn, key, val);
      key.clear();

      // Each iterable uses a cursor and must be closed when finished. Iterate
      // forward in terms of key ordering starting with the first key.
      try (CursorIterable<ByteBuffer> ci = db.iterate(txn, KeyRange.all())) {
        for (final KeyVal<ByteBuffer> kv : ci) {