Program: 15868
Project: 15868
Buggy source:
/** 
 * Compact the store by moving all chunks next to each other, if there is free space between chunks. This might temporarily double the file size. Chunks are overwritten irrespective of the current retention time. Before overwriting chunks and before resizing the file, syncFile() is called.
 * @return if anything was written
 */
public synchronized boolean compactMoveChunks(){
  checkOpen();
  if (chunks.size() == 0) {
    return false;
  }
  int oldRetentionTime=retentionTime;
  retentionTime=0;
  long time=getTime();
  ArrayList<Chunk> free=New.arrayList();
  for (  Chunk c : chunks.values()) {
    if (c.maxLengthLive == 0) {
      if (canOverwriteChunk(c,time)) {
        free.add(c);
      }
    }
  }
  for (  Chunk c : free) {
    chunks.remove(c.id);
    markMetaChanged();
    meta.remove("String_Node_Str" + c.id);
    int length=MathUtils.roundUpInt(c.length,BLOCK_SIZE) + BLOCK_SIZE;
    fileStore.free(c.start,length);
  }
  if (fileStore.getFillRate() == 100) {
    return false;
  }
  long firstFree=fileStore.getFirstFree();
  ArrayList<Chunk> move=New.arrayList();
  for (  Chunk c : chunks.values()) {
    if (c.start > firstFree) {
      move.add(c);
    }
  }
  for (  Chunk c : move) {
    WriteBuffer buff=getWriteBuffer();
    int length=MathUtils.roundUpInt(c.length,BLOCK_SIZE) + BLOCK_SIZE;
    buff.limit(length);
    ByteBuffer buff2=fileStore.readFully(c.start,length);
    buff.put(buff2);
    long end=getEndPosition();
    fileStore.markUsed(end,length);
    fileStore.free(c.start,length);
    c.start=end;
    buff.position(0);
    c.writeHeader(buff);
    buff.position(buff.limit() - BLOCK_SIZE);
    byte[] header=getStoreHeaderBytes();
    buff.put(header);
    buff.put(new byte[BLOCK_SIZE - header.length]);
    buff.position(0);
    fileStore.writeFully(end,buff.getBuffer());
    releaseWriteBuffer(buff);
    markMetaChanged();
    meta.put("String_Node_Str" + c.id,c.asString());
  }
  boolean oldReuse=reuseSpace;
  reuseSpace=false;
  commitAndSave();
  sync();
  reuseSpace=true;
  for (  Chunk c : move) {
    if (!chunks.containsKey(c.id)) {
      continue;
    }
    WriteBuffer buff=getWriteBuffer();
    int length=MathUtils.roundUpInt(c.length,BLOCK_SIZE) + BLOCK_SIZE;
    buff.limit(length);
    ByteBuffer buff2=fileStore.readFully(c.start,length);
    buff.put(buff2);
    long pos=fileStore.allocate(length);
    fileStore.free(c.start,length);
    buff.position(0);
    c.start=pos;
    c.writeHeader(buff);
    buff.position(buff.limit() - BLOCK_SIZE);
    byte[] header=getStoreHeaderBytes();
    buff.put(header);
    buff.put(new byte[BLOCK_SIZE - header.length]);
    buff.position(0);
    fileStore.writeFully(pos,buff.getBuffer());
    releaseWriteBuffer(buff);
    markMetaChanged();
    meta.put("String_Node_Str" + c.id,c.asString());
  }
  commitAndSave();
  sync();
  shrinkFileIfPossible(0);
  reuseSpace=oldReuse;
  retentionTime=oldRetentionTime;
  return true;
}

Fixed version (ground truth):
/** 
 * Compact the store by moving all chunks next to each other, if there is free space between chunks. This might temporarily double the file size. Chunks are overwritten irrespective of the current retention time. Before overwriting chunks and before resizing the file, syncFile() is called.
 * @return if anything was written
 */
public synchronized boolean compactMoveChunks(){
  checkOpen();
  if (chunks.size() == 0) {
    return false;
  }
  int oldRetentionTime=retentionTime;
  retentionTime=0;
  long time=getTime();
  ArrayList<Chunk> free=New.arrayList();
  for (  Chunk c : chunks.values()) {
    if (c.maxLengthLive == 0) {
      if (canOverwriteChunk(c,time)) {
        free.add(c);
      }
    }
  }
  for (  Chunk c : free) {
    chunks.remove(c.id);
    markMetaChanged();
    meta.remove("String_Node_Str" + c.id);
    int length=MathUtils.roundUpInt(c.length,BLOCK_SIZE) + BLOCK_SIZE;
    fileStore.free(c.start,length);
  }
  if (fileStore.getFillRate() == 100) {
    return false;
  }
  long firstFree=fileStore.getFirstFree();
  ArrayList<Chunk> move=New.arrayList();
  for (  Chunk c : chunks.values()) {
    if (c.start > firstFree) {
      move.add(c);
    }
  }
  for (  Chunk c : move) {
    WriteBuffer buff=getWriteBuffer();
    int length=MathUtils.roundUpInt(c.length,BLOCK_SIZE) + BLOCK_SIZE;
    buff.limit(length);
    ByteBuffer buff2=fileStore.readFully(c.start,length);
    buff.put(buff2);
    long end=getFileLengthInUse();
    fileStore.markUsed(end,length);
    fileStore.free(c.start,length);
    c.start=end;
    buff.position(0);
    c.writeHeader(buff);
    buff.position(buff.limit() - BLOCK_SIZE);
    byte[] header=getStoreHeaderBytes();
    buff.put(header);
    buff.put(new byte[BLOCK_SIZE - header.length]);
    buff.position(0);
    fileStore.writeFully(end,buff.getBuffer());
    releaseWriteBuffer(buff);
    markMetaChanged();
    meta.put("String_Node_Str" + c.id,c.asString());
  }
  boolean oldReuse=reuseSpace;
  reuseSpace=false;
  commitAndSave();
  sync();
  reuseSpace=true;
  for (  Chunk c : move) {
    if (!chunks.containsKey(c.id)) {
      continue;
    }
    WriteBuffer buff=getWriteBuffer();
    int length=MathUtils.roundUpInt(c.length,BLOCK_SIZE) + BLOCK_SIZE;
    buff.limit(length);
    ByteBuffer buff2=fileStore.readFully(c.start,length);
    buff.put(buff2);
    long pos=fileStore.allocate(length);
    fileStore.free(c.start,length);
    buff.position(0);
    c.start=pos;
    c.writeHeader(buff);
    buff.position(buff.limit() - BLOCK_SIZE);
    byte[] header=getStoreHeaderBytes();
    buff.put(header);
    buff.put(new byte[BLOCK_SIZE - header.length]);
    buff.position(0);
    fileStore.writeFully(pos,buff.getBuffer());
    releaseWriteBuffer(buff);
    markMetaChanged();
    meta.put("String_Node_Str" + c.id,c.asString());
  }
  commitAndSave();
  sync();
  shrinkFileIfPossible(0);
  reuseSpace=oldReuse;
  retentionTime=oldRetentionTime;
  return true;
}