<commit_before>/*
 * Copyright 2016 The LmdbJava Project, http://lmdbjava.org/
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.lmdbjava;

import static java.util.Objects.requireNonNull;
import static jnr.ffi.Memory.allocateDirect;
import static jnr.ffi.NativeType.ADDRESS;
import jnr.ffi.Pointer;
import jnr.ffi.provider.MemoryManager;
import static org.lmdbjava.BufferProxy.MDB_VAL_STRUCT_SIZE;
import org.lmdbjava.Env.NotOpenException;
import static org.lmdbjava.Library.LIB;
import static org.lmdbjava.Library.RUNTIME;
import static org.lmdbjava.MaskedFlag.isSet;
import static org.lmdbjava.MaskedFlag.mask;
import static org.lmdbjava.ResultCodeMapper.checkRc;
import static org.lmdbjava.TxnFlags.MDB_RDONLY;

/**
 * LMDB transaction.
 *
 * @param <T> buffer type
 */
public final class Txn<T> implements AutoCloseable {

  private static final MemoryManager MEM_MGR = RUNTIME.getMemoryManager();
  private boolean committed = false;
  private final Env<T> env;
  private final T key;
  private final Txn<T> parent;
  private BufferProxy<T> proxy;
  private final long ptrKeyAddr;
  private final long ptrValAddr;
  private final boolean readOnly;
  private boolean reset = false;
  private final T val;
  final Pointer ptr;
  final Pointer ptrKey;
  final Pointer ptrVal;

  Txn(final Env<T> env, final Txn<T> parent, final BufferProxy<T> proxy,
      final TxnFlags... flags)
      throws NotOpenException, IncompatibleParent, LmdbNativeException {
    requireNonNull(env);
    requireNonNull(proxy);
    if (!env.isOpen() || env.isClosed()) {
      throw new NotOpenException();
    }
    this.env = env;
    this.proxy = proxy;
    final int flagsMask = mask(flags);
    this.readOnly = isSet(flagsMask, MDB_RDONLY);
    this.parent = parent;
    if (parent != null) {
      if ((parent.readOnly && !this.readOnly)
              || (!parent.readOnly && this.readOnly)) {
        throw new IncompatibleParent();
      }
    }
    final Pointer txnPtr = allocateDirect(RUNTIME, ADDRESS);
    final Pointer txnParentPtr = parent == null ? null : parent.ptr;
    checkRc(LIB.mdb_txn_begin(env.ptr, txnParentPtr, flagsMask, txnPtr));
    ptr = txnPtr.getPointer(0);

    this.key = proxy.allocate();
    this.val = proxy.allocate();
    ptrKey = MEM_MGR.allocateTemporary(MDB_VAL_STRUCT_SIZE, false);
    ptrKeyAddr = ptrKey.address();
    ptrVal = MEM_MGR.allocateTemporary(MDB_VAL_STRUCT_SIZE, false);
    ptrValAddr = ptrVal.address();
  }

  /**
   * Aborts this transaction.
   */
  public void abort() {
    if (committed) {
      throw new CommittedException();
    }
    LIB.mdb_txn_abort(ptr);
    committed = true;
  }

  /**
   * Closes this transaction by aborting if not already committed.
   * <p>
   * Closing the transaction will invoke
   * {@link BufferProxy#deallocate(java.lang.Object)} for each read-only buffer
   * (ie the key and value).
   */
  @Override
  public void close() {
    if (committed) {
      return;
    }
    proxy.deallocate(key);
    proxy.deallocate(val);
    LIB.mdb_txn_abort(ptr);
    committed = true;
  }

  /**
   * Commits this transaction.
   */
  public void commit() {
    if (committed) {
      throw new CommittedException();
    }
    checkRc(LIB.mdb_txn_commit(ptr));
    committed = true;
  }

  /**
   * Return the transaction's ID.
   *
   * @return A transaction ID, valid if input is an active transaction
   */
  public long getId() {
    return LIB.mdb_txn_id(ptr);
  }

  /**
   * Obtains this transaction's parent.
   *
   * @return the parent transaction (may be null)
   */
  public Txn<T> getParent() {
    return parent;
  }

  /**
   * Whether this transaction has been committed.
   *
   * @return true if committed
   */
  public boolean isCommitted() {
    return committed;
  }

  /**
   * Whether this transaction is read-only.
   *
   * @return if read-only
   */
  public boolean isReadOnly() {
    return readOnly;
  }

  /**
   * Whether this transaction has been {@link #reset()}.
   *
   * @return if reset
   */
  public boolean isReset() {
    return reset;
  }

  /**
   * Fetch the buffer which holds a read-only view of the LMDI allocated memory.
   * Any use of this buffer must comply with the standard LMDB C "mdb_get"
   * contract (ie do not modify, do not attempt to release the memory, do not
   * use once the transaction or cursor closes, do not use after a write etc).
   *
   * @return the key buffer (never null)
   */
  public T key() {
    return key;
  }

  /**
   * Renews a read-only transaction previously released by {@link #reset()}.
   */
  public void renew() {
    if (!reset) {
      throw new NotResetException();
    }
    reset = false;
    checkRc(LIB.mdb_txn_renew(ptr));
  }

  /**
   * Aborts this read-only transaction and resets the transaction handle so it
   * can be reused upon calling {@link #renew()}.
   */
  public void reset() {
    if (!isReadOnly()) {
      throw new ReadOnlyRequiredException();
    }
    if (reset) {
      throw new ResetException();
    }
    LIB.mdb_txn_reset(ptr);
    reset = true;
  }

  /**
   * Fetch the buffer which holds a read-only view of the LMDI allocated memory.
   * Any use of this buffer must comply with the standard LMDB C "mdb_get"
   * contract (ie do not modify, do not attempt to release the memory, do not
   * use once the transaction or cursor closes, do not use after a write etc).
   *
   * @return the value buffer (never null)
   */
  public T val() {
    return val;
  }

  void checkNotCommitted() throws CommittedException {
    if (committed) {
      throw new CommittedException();
    }
  }

  void checkReadOnly() throws ReadOnlyRequiredException {
    if (!readOnly) {
      throw new ReadOnlyRequiredException();
    }
  }

  void checkWritesAllowed() throws ReadWriteRequiredException {
    if (readOnly) {
      throw new ReadWriteRequiredException();
    }
  }

  void keyIn(final T key) {
    proxy.in(key, ptrKey, ptrKeyAddr);
  }

  void keyOut() {
    proxy.out(key, ptrKey, ptrKeyAddr);
  }

  void valIn(final T val) {
    proxy.in(val, ptrVal, ptrValAddr);
  }

  void valIn(final int size) {
    proxy.in(val, size, ptrVal, ptrValAddr);
  }

  void valOut() {
    proxy.out(val, ptrVal, ptrValAddr);
  }

  void valOut(final T val) {
    proxy.out(val, ptrVal, ptrValAddr);
  }

  /**
   * Transaction must abort, has a child, or is invalid.
   */
  public static final class BadException extends LmdbNativeException {
<commit_msg>Remove Txn.valOut(T) (not required; not tested)<commit_after>/*
 * Copyright 2016 The LmdbJava Project, http://lmdbjava.org/
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.lmdbjava;

import static java.util.Objects.requireNonNull;
import static jnr.ffi.Memory.allocateDirect;
import static jnr.ffi.NativeType.ADDRESS;
import jnr.ffi.Pointer;
import jnr.ffi.provider.MemoryManager;
import static org.lmdbjava.BufferProxy.MDB_VAL_STRUCT_SIZE;
import org.lmdbjava.Env.NotOpenException;
import static org.lmdbjava.Library.LIB;
import static org.lmdbjava.Library.RUNTIME;
import static org.lmdbjava.MaskedFlag.isSet;
import static org.lmdbjava.MaskedFlag.mask;
import static org.lmdbjava.ResultCodeMapper.checkRc;
import static org.lmdbjava.TxnFlags.MDB_RDONLY;

/**
 * LMDB transaction.
 *
 * @param <T> buffer type
 */
public final class Txn<T> implements AutoCloseable {

  private static final MemoryManager MEM_MGR = RUNTIME.getMemoryManager();
  private boolean committed = false;
  private final Env<T> env;
  private final T key;
  private final Txn<T> parent;
  private BufferProxy<T> proxy;
  private final long ptrKeyAddr;
  private final long ptrValAddr;
  private final boolean readOnly;
  private boolean reset = false;
  private final T val;
  final Pointer ptr;
  final Pointer ptrKey;
  final Pointer ptrVal;

  Txn(final Env<T> env, final Txn<T> parent, final BufferProxy<T> proxy,
      final TxnFlags... flags)
      throws NotOpenException, IncompatibleParent, LmdbNativeException {
    requireNonNull(env);
    requireNonNull(proxy);
    if (!env.isOpen() || env.isClosed()) {
      throw new NotOpenException();
    }
    this.env = env;
    this.proxy = proxy;
    final int flagsMask = mask(flags);
    this.readOnly = isSet(flagsMask, MDB_RDONLY);
    this.parent = parent;
    if (parent != null) {
      if ((parent.readOnly && !this.readOnly)
              || (!parent.readOnly && this.readOnly)) {
        throw new IncompatibleParent();
      }
    }
    final Pointer txnPtr = allocateDirect(RUNTIME, ADDRESS);
    final Pointer txnParentPtr = parent == null ? null : parent.ptr;
    checkRc(LIB.mdb_txn_begin(env.ptr, txnParentPtr, flagsMask, txnPtr));
    ptr = txnPtr.getPointer(0);

    this.key = proxy.allocate();
    this.val = proxy.allocate();
    ptrKey = MEM_MGR.allocateTemporary(MDB_VAL_STRUCT_SIZE, false);
    ptrKeyAddr = ptrKey.address();
    ptrVal = MEM_MGR.allocateTemporary(MDB_VAL_STRUCT_SIZE, false);
    ptrValAddr = ptrVal.address();
  }

  /**
   * Aborts this transaction.
   */
  public void abort() {
    if (committed) {
      throw new CommittedException();
    }
    LIB.mdb_txn_abort(ptr);
    committed = true;
  }

  /**
   * Closes this transaction by aborting if not already committed.
   * <p>
   * Closing the transaction will invoke
   * {@link BufferProxy#deallocate(java.lang.Object)} for each read-only buffer
   * (ie the key and value).
   */
  @Override
  public void close() {
    if (committed) {
      return;
    }
    proxy.deallocate(key);
    proxy.deallocate(val);
    LIB.mdb_txn_abort(ptr);
    committed = true;
  }

  /**
   * Commits this transaction.
   */
  public void commit() {
    if (committed) {
      throw new CommittedException();
    }
    checkRc(LIB.mdb_txn_commit(ptr));
    committed = true;
  }

  /**
   * Return the transaction's ID.
   *
   * @return A transaction ID, valid if input is an active transaction
   */
  public long getId() {
    return LIB.mdb_txn_id(ptr);
  }

  /**
   * Obtains this transaction's parent.
   *
   * @return the parent transaction (may be null)
   */
  public Txn<T> getParent() {
    return parent;
  }

  /**
   * Whether this transaction has been committed.
   *
   * @return true if committed
   */
  public boolean isCommitted() {
    return committed;
  }

  /**
   * Whether this transaction is read-only.
   *
   * @return if read-only
   */
  public boolean isReadOnly() {
    return readOnly;
  }

  /**
   * Whether this transaction has been {@link #reset()}.
   *
   * @return if reset
   */
  public boolean isReset() {
    return reset;
  }

  /**
   * Fetch the buffer which holds a read-only view of the LMDI allocated memory.
   * Any use of this buffer must comply with the standard LMDB C "mdb_get"
   * contract (ie do not modify, do not attempt to release the memory, do not
   * use once the transaction or cursor closes, do not use after a write etc).
   *
   * @return the key buffer (never null)
   */
  public T key() {
    return key;
  }

  /**
   * Renews a read-only transaction previously released by {@link #reset()}.
   */
  public void renew() {
    if (!reset) {
      throw new NotResetException();
    }
    reset = false;
    checkRc(LIB.mdb_txn_renew(ptr));
  }

  /**
   * Aborts this read-only transaction and resets the transaction handle so it
   * can be reused upon calling {@link #renew()}.
   */
  public void reset() {
    if (!isReadOnly()) {
      throw new ReadOnlyRequiredException();
    }
    if (reset) {
      throw new ResetException();
    }
    LIB.mdb_txn_reset(ptr);
    reset = true;
  }

  /**
   * Fetch the buffer which holds a read-only view of the LMDI allocated memory.
   * Any use of this buffer must comply with the standard LMDB C "mdb_get"
   * contract (ie do not modify, do not attempt to release the memory, do not
   * use once the transaction or cursor closes, do not use after a write etc).
   *
   * @return the value buffer (never null)
   */
  public T val() {
    return val;
  }

  void checkNotCommitted() throws CommittedException {
    if (committed) {
      throw new CommittedException();
    }
  }

  void checkReadOnly() throws ReadOnlyRequiredException {
    if (!readOnly) {
      throw new ReadOnlyRequiredException();
    }
  }

  void checkWritesAllowed() throws ReadWriteRequiredException {
    if (readOnly) {
      throw new ReadWriteRequiredException();
    }
  }

  void keyIn(final T key) {
    proxy.in(key, ptrKey, ptrKeyAddr);
  }

  void keyOut() {
    proxy.out(key, ptrKey, ptrKeyAddr);
  }

  void valIn(final T val) {
    proxy.in(val, ptrVal, ptrValAddr);
  }

  void valIn(final int size) {
    proxy.in(val, size, ptrVal, ptrValAddr);
  }

  void valOut() {
    proxy.out(val, ptrVal, ptrValAddr);
  }

  /**
   * Transaction must abort, has a child, or is invalid.
   */
  public static final class BadException extends LmdbNativeException {

{'fraction_non_alphanumeric': 0.10407957330258037, 'fraction_numerical': 0.0012973908029407526, 'mean_word_length': 3.0475495915986, 'pattern_counts': {'":': 0, '<': 23, '<?xml version=': 0, '>': 23, 'https://': 0, 'lorem ipsum': 0, 'www.': 2, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '5307002', 'n_tokens_mistral': 4431, 'n_tokens_neox': 4332, 'n_words': 1358}