All Downloads are FREE. Search and download functionalities are using the official Maven repository.

uk.co.probablyfine.dirty.Store Maven / Gradle / Ivy

package uk.co.probablyfine.dirty;

import uk.co.probablyfine.dirty.utils.Classes;
import uk.co.probablyfine.dirty.utils.Nio;
import uk.co.probablyfine.dirty.utils.Types;

import java.lang.reflect.Field;
import java.nio.MappedByteBuffer;
import java.nio.channels.FileChannel;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.function.BiConsumer;
import java.util.stream.Collectors;
import java.util.stream.Stream;

import static uk.co.probablyfine.dirty.utils.Exceptions.unchecked;

public class Store {

  private final FileChannel fileChannel;
  private final MappedByteBuffer memoryMappedFile;
  private final int offSet;
  private final List fields;
  private final Class klass;
  private final List> writeObservers = new ArrayList<>();
  private int size;

  public Store(String path, Class klass) {
    this.klass = klass;
    this.fields = Classes.primitiveFields(klass).collect(Collectors.toList());
    this.fileChannel = Nio.fileChannel(path);
    this.offSet = Types.offSetForClass(klass);
    this.memoryMappedFile = Nio.mapFile(fileChannel, 1024 * 1024 * 2);
    this.size = memoryMappedFile.getInt(0);
  }

  public void put(T t) {
    AtomicInteger currentPosition = new AtomicInteger(Types.INT.getSize() + this.size*this.offSet);

    fields.forEach(field -> {
      Object unchecked = unchecked(() -> field.get(t));
      Types fieldType = Types.of(field.getType());
      fieldType.getWriteField().accept(memoryMappedFile, currentPosition.get(), unchecked);
      currentPosition.addAndGet(fieldType.getSize());
    });

    this.writeObservers.forEach(x -> x.accept(t, this.size));
    this.incrementSize();
  }

  private void incrementSize() {
    this.size++;
    this.memoryMappedFile.putInt(0, this.size);
  }

  public Stream all() throws IllegalAccessException {
    Stream.Builder builder = Stream.builder();

    for(int index = 0; index < this.size; index++) {
      builder.add(extractEntry(index));
    }

    return builder.build();
  }

  public Stream reverse() throws IllegalAccessException {
    Stream.Builder builder = Stream.builder();

    for(int index = (this.size-1); index >= 0; index--) {
      builder.add(extractEntry(index));
    }

    return builder.build();
  }

  private T extractEntry(int index) throws IllegalAccessException {
    final AtomicInteger cursor = new AtomicInteger(Types.INT.getSize() + (index * this.offSet));
    T t = unchecked(klass::newInstance);

    fields.forEach(field -> {
      final Types fieldType = Types.of(field.getType());
      final Object apply = fieldType.getReadField().apply(memoryMappedFile, cursor.get());

      unchecked(() -> field.set(t, apply));

      cursor.addAndGet(fieldType.getSize());
    });
    return t;
  }

  public T get(int index) throws IllegalAccessException {
    return extractEntry(index);
  }

  public void observeWrites(BiConsumer observeWriteFunction) {
    this.writeObservers.add(observeWriteFunction);
  }

  public void reset() {
    this.size = 0;
    this.memoryMappedFile.putInt(0, 0);
  }

  public interface WithFile {
    Store from(String path);
  }

  public static  WithFile of(final Class fooClass) {
    return path -> new Store<>(path, fooClass);
  }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy