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

kvd.server.storage.mapdb.BinaryLargeObjectOutputStream Maven / Gradle / Ivy

The newest version!
/*
 * Copyright 2021 Andre Gebers
 *
 * 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 kvd.server.storage.mapdb;

import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;

import javax.annotation.concurrent.NotThreadSafe;

import kvd.common.IOStreamUtils;
import kvd.common.KvdException;
import kvd.server.Key;

@NotThreadSafe
public class BinaryLargeObjectOutputStream extends OutputStream {

  private Key key;

  private ByteBuffer buf;

  private File blobBase;

  private OutputStream blobStream;

  private long blobSize;

  private long blobSplitSize;

  private boolean closed;

  private List blobs = new ArrayList<>();

  private String blobBaseName = UUID.randomUUID().toString();

  public BinaryLargeObjectOutputStream(Key key, File blobBase) {
    this(key, blobBase, 64*1024, Long.MAX_VALUE);
  }

  public BinaryLargeObjectOutputStream(Key key, File blobBase, int blobThreshold) {
    this(key, blobBase, blobThreshold, Long.MAX_VALUE);
  }

  public BinaryLargeObjectOutputStream(Key key, File blobBase,
      int blobThreshold,
      long blobSplitSize) {
    super();
    this.key = key;
    this.blobSplitSize = blobSplitSize;
    buf = ByteBuffer.allocate(blobThreshold);
    this.blobBase = blobBase;
  }

  @Override
  public void write(int b) throws IOException {
    byte[] buf = new byte[1];
    buf[0] = (byte)b;
    write(buf);
  }

  @Override
  public void write(byte[] b, int off, int len) throws IOException {
    if(closed) {
      throw new IOException("stream closed");
    }
    IOStreamUtils.checkFromIndexSize(b, off, len);
    if(blobStream != null) {
      writeToBlob(b, off, len);
    } else {
      if(buf.remaining() < len) {
        writeToBlob(buf.array(), 0, buf.position());
        writeToBlob(b, off, len);
        buf = null;
      } else {
        buf.put(b, off, len);
      }
    }
  }

  private void writeToBlob(byte[] b, int off, int len) throws IOException {
    if(blobStream == null) {
      newBlob();
    }
    if((blobSize+len) > blobSplitSize) {
      // fill up the blob to capacity
      int len2 = (int)(blobSplitSize - blobSize);
      blobStream.write(b, off, len2);
      blobStream.close();
      newBlob();
      // write the remaining into the new blob (might need to split again)
      int len3 = len-len2;
      writeToBlob(b, off+len2, len3);
    } else {
      blobStream.write(b, off, len);
      blobSize += len;
    }
  }

  private void newBlob() throws IOException {
    int index = blobs.size();
    String name = blobBaseName + "." + index;
    File f = new File(blobBase, name);
    blobStream = new BufferedOutputStream(new FileOutputStream(f));
    BlobHeader header = new BlobHeader(index, key);
    blobSize = header.writeToStream(blobStream);
    blobs.add(name);
    if(blobSplitSize < blobSize) {
      // silently increase the split size so we can put some content into the file
      // this should be a edge case when either the split size is tiny or the keys are huge or both
      blobSplitSize = blobSize + 64*1024;
    }
  }

  public Value toValue() {
    if(!closed) {
      throw new KvdException("stream not closed");
    }
    return blobs.isEmpty()?inline():blob();
  }

  private Value inline() {
    buf.flip();
    byte[] bytes = new byte[buf.limit()];
    buf.get(bytes);
    return Value.inline(bytes);
  }

  private Value blob() {
    return Value.blob(blobs);
  }

  @Override
  public void close() throws IOException {
    closed = true;
    if(blobStream != null) {
      blobStream.close();
      blobStream = null;
    }
  }

  Key getKey() {
    return key;
  }

}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy