mirror of
https://git.huckle.dev/Huckles-Minecraft-Archive/jpexs-decompiler.git
synced 2026-05-25 00:46:50 +00:00
File cache improvement, deleting old temp files
This commit is contained in:
313
libsrc/ffdec_lib/src/com/jpexs/helpers/FileHashMap.java
Normal file
313
libsrc/ffdec_lib/src/com/jpexs/helpers/FileHashMap.java
Normal file
@@ -0,0 +1,313 @@
|
||||
package com.jpexs.helpers;
|
||||
|
||||
import com.jpexs.decompiler.flash.helpers.Freed;
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.File;
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.IOException;
|
||||
import java.io.ObjectInputStream;
|
||||
import java.io.ObjectOutputStream;
|
||||
import java.io.RandomAccessFile;
|
||||
import java.util.AbstractMap;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.Iterator;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.TreeSet;
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author JPEXS
|
||||
*/
|
||||
public class FileHashMap<K, V> extends AbstractMap<K, V> implements Freed {
|
||||
|
||||
private final Map<K, Integer> lengths = new HashMap<>();
|
||||
private final Map<K, Long> offsets = new HashMap<>();
|
||||
private long fileLen = 0;
|
||||
private final RandomAccessFile file;
|
||||
private final File fileName;
|
||||
private final Set<Gap> gaps = new TreeSet<>();
|
||||
private int maxGapLen = 0;
|
||||
private boolean deleted = false;
|
||||
|
||||
|
||||
|
||||
private static class Gap implements Comparable<Gap> {
|
||||
|
||||
public long offset;
|
||||
public int length;
|
||||
|
||||
public Gap(long offset, int length) {
|
||||
this.offset = offset;
|
||||
this.length = length;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int compareTo(Gap o) {
|
||||
return o.length - length;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
int hash = 7;
|
||||
hash = 97 * hash + (int) (this.offset ^ (this.offset >>> 32));
|
||||
return hash;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (obj == null) {
|
||||
return false;
|
||||
}
|
||||
if (getClass() != obj.getClass()) {
|
||||
return false;
|
||||
}
|
||||
final Gap other = (Gap) obj;
|
||||
if (this.offset != other.offset) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public static class FileEntry<K, V> implements Map.Entry<K, V> {
|
||||
|
||||
private FileHashMap<K, V> parent;
|
||||
private K key;
|
||||
|
||||
public FileEntry(FileHashMap<K, V> parent, K key) {
|
||||
this.parent = parent;
|
||||
this.key = key;
|
||||
}
|
||||
|
||||
@Override
|
||||
public K getKey() {
|
||||
return key;
|
||||
}
|
||||
|
||||
@Override
|
||||
public V getValue() {
|
||||
return parent.get(key);
|
||||
}
|
||||
|
||||
@Override
|
||||
public V setValue(V value) {
|
||||
return parent.put(key, value);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public FileHashMap(File file) throws IOException {
|
||||
this.file = new RandomAccessFile(file, "rw");
|
||||
this.file.setLength(0);
|
||||
this.fileName = file;
|
||||
file.deleteOnExit();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean containsKey(Object key) {
|
||||
if(deleted){
|
||||
throw new NullPointerException();
|
||||
}
|
||||
return offsets.containsKey(key);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<K> keySet() {
|
||||
if(deleted){
|
||||
throw new NullPointerException();
|
||||
}
|
||||
return offsets.keySet();
|
||||
}
|
||||
|
||||
@Override
|
||||
public V get(Object key) {
|
||||
if(deleted){
|
||||
throw new NullPointerException();
|
||||
}
|
||||
try {
|
||||
if (!offsets.containsKey(key)) {
|
||||
return null;
|
||||
}
|
||||
long ofs = offsets.get(key);
|
||||
int len = lengths.get(key);
|
||||
file.seek(ofs);
|
||||
byte data[] = new byte[len];
|
||||
file.readFully(data);
|
||||
ObjectInputStream ois = new ObjectInputStream(new ByteArrayInputStream(data));
|
||||
try {
|
||||
@SuppressWarnings("unchecked")
|
||||
V ret = (V) ois.readObject();
|
||||
return ret;
|
||||
} catch (ClassNotFoundException ex) {
|
||||
Logger.getLogger(FileHashMap.class.getName()).log(Level.SEVERE, null, ex);
|
||||
return null;
|
||||
}
|
||||
} catch (IOException ex) {
|
||||
Logger.getLogger(FileHashMap.class.getName()).log(Level.SEVERE, null, ex);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized V put(K key, V value) {
|
||||
if(deleted){
|
||||
throw new NullPointerException();
|
||||
}
|
||||
ObjectOutputStream oos = null;
|
||||
try {
|
||||
ByteArrayOutputStream baos = new ByteArrayOutputStream();
|
||||
oos = new ObjectOutputStream(baos);
|
||||
oos.writeObject(value);
|
||||
oos.flush();
|
||||
byte data[] = baos.toByteArray();
|
||||
if (offsets.containsKey(key)) {
|
||||
long origOffset = offsets.get(key);
|
||||
int origLen = lengths.get(key);
|
||||
if (data.length <= origLen) {
|
||||
file.seek(origOffset);
|
||||
file.write(data);
|
||||
lengths.put(key, data.length);
|
||||
if (data.length < origLen) {
|
||||
Gap g = new Gap(origOffset + data.length, origLen - data.length);
|
||||
if (g.length > maxGapLen) {
|
||||
maxGapLen = g.length;
|
||||
}
|
||||
gaps.add(g);
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
}
|
||||
if (data.length <= maxGapLen) {
|
||||
|
||||
for (Iterator<Gap> i = gaps.iterator(); i.hasNext();) {
|
||||
Gap g = i.next();
|
||||
if (g.length >= data.length) {
|
||||
file.seek(g.offset);
|
||||
file.write(data);
|
||||
offsets.put(key, g.offset);
|
||||
lengths.put(key, g.length);
|
||||
if (g.length > data.length) {
|
||||
g.offset = g.offset + data.length;
|
||||
g.length = g.length - data.length;
|
||||
} else {
|
||||
i.remove();
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
file.seek(fileLen);
|
||||
file.write(data);
|
||||
offsets.put(key, fileLen);
|
||||
lengths.put(key, data.length);
|
||||
fileLen += data.length;
|
||||
}
|
||||
} catch (IOException ex) {
|
||||
Logger.getLogger(FileHashMap.class.getName()).log(Level.SEVERE, null, ex);
|
||||
} finally {
|
||||
try {
|
||||
oos.close();
|
||||
} catch (IOException ex) {
|
||||
//ignore
|
||||
}
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
@Override
|
||||
public V remove(Object objKey) {
|
||||
if(deleted){
|
||||
throw new NullPointerException();
|
||||
}
|
||||
@SuppressWarnings("unchecked")
|
||||
K key=(K)objKey;
|
||||
if (!containsKey(key)) {
|
||||
return null;
|
||||
}
|
||||
V val = get((K) key);
|
||||
Gap g = new Gap(offsets.get(key), lengths.get(key));
|
||||
offsets.remove(key);
|
||||
lengths.remove(key);
|
||||
if (g.offset + g.length == fileLen) {
|
||||
fileLen -= g.length;
|
||||
} else {
|
||||
if (g.length > maxGapLen) {
|
||||
maxGapLen = g.length;
|
||||
}
|
||||
gaps.add(g);
|
||||
}
|
||||
return val;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<Entry<K, V>> entrySet() {
|
||||
if(deleted){
|
||||
throw new NullPointerException();
|
||||
}
|
||||
Set<Entry<K,V>> ret=new HashSet<Entry<K, V>>();
|
||||
for(K key:keySet()){
|
||||
ret.add(new FileEntry<K,V>(this,key));
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void clear() {
|
||||
if(deleted){
|
||||
throw new NullPointerException();
|
||||
}
|
||||
offsets.clear();
|
||||
lengths.clear();
|
||||
fileLen = 0;
|
||||
maxGapLen = 0;
|
||||
try {
|
||||
file.setLength(0);
|
||||
} catch (IOException ex) {
|
||||
Logger.getLogger(FileHashMap.class.getName()).log(Level.SEVERE, null, ex);
|
||||
}
|
||||
}
|
||||
|
||||
public void delete() {
|
||||
if(deleted){
|
||||
throw new NullPointerException();
|
||||
}
|
||||
try {
|
||||
file.close();
|
||||
} catch (IOException ex) {
|
||||
Logger.getLogger(FileHashMap.class.getName()).log(Level.SEVERE, null, ex);
|
||||
}
|
||||
fileName.delete();
|
||||
deleted = true;
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isFreeing() {
|
||||
return !deleted;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void free() {
|
||||
if(!deleted){
|
||||
delete();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isEmpty() {
|
||||
return offsets.isEmpty();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int size() {
|
||||
return offsets.size();
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user