/*
This file is part of jpcsp.
Jpcsp is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Jpcsp is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Jpcsp. If not, see <http://www.gnu.org/licenses/>.
*/
package jpcsp.HLE.kernel.managers;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.ListIterator;
import jpcsp.Emulator;
import jpcsp.HLE.kernel.types.SceUid;
/**
*
* @author hli, gid15
*/
public class SceUidManager {
// UID is a unique identifier across all purposes
private static HashMap<Integer, SceUid> uidMap = new HashMap<Integer, SceUid>();
private static int uidNext = 0x1; // LocoRoco expects UID to be 8bit
public static final int INVALID_ID = Integer.MIN_VALUE;
// ID is an identifier only unique for the same purpose.
// Different purposes can share the save ID values.
// An ID has always a range of valid values, e.g. [0..255]
private static HashMap<Object, LinkedList<Integer>> freeIdsMap = new HashMap<Object, LinkedList<Integer>>();
static public void reset() {
uidMap.clear();
freeIdsMap.clear();
uidNext = 1;
}
/** classes should call getUid to get a new unique SceUID */
static public int getNewUid(Object purpose) {
SceUid uid = new SceUid(purpose, uidNext++);
uidMap.put(uid.getUid(), uid);
return uid.getUid();
}
/** classes should call checkUidPurpose before using a SceUID
* @return true is the uid is ok. */
static public boolean checkUidPurpose(int uid, Object purpose, boolean allowUnknown) {
SceUid found = uidMap.get(uid);
if (found == null) {
if (!allowUnknown) {
Emulator.log.warn("Attempt to use unknown SceUID (purpose='" + purpose.toString() + "')");
return false;
}
} else if (!purpose.equals(found.getPurpose())) {
Emulator.log.error("Attempt to use SceUID for different purpose (purpose='" + purpose.toString() + "',original='" + found.getPurpose().toString() + "')");
return false;
}
return true;
}
/** classes should call releaseUid when they are finished with a SceUID
* @return true on success. */
static public boolean releaseUid(int uid, Object purpose) {
SceUid found = uidMap.get(uid);
if (found == null) {
Emulator.log.warn("Attempt to release unknown SceUID (purpose='" + purpose.toString() + "')");
return false;
}
if (purpose.equals(found.getPurpose())) {
uidMap.remove(uid);
} else {
Emulator.log.error("Attempt to release SceUID for different purpose (purpose='" + purpose.toString() + "',original='" + found.getPurpose().toString() + "')");
return false;
}
return true;
}
/**
* Return a new ID for the given purpose.
* The ID will be unique for the given purpose but will not be unique
* across different purposes.
* The ID will be higher of equal to minimumId, and lower or equal to
* maximumId, i.e. in the range [minimumId..maximumId].
* The ID will be lowest possible free ID.
*
* @param purpose The ID will be unique for this purpose
* @param minimumId The lowest possible value for the ID
* @param maximumId The highest possible value for the ID
* @return The lowest possible free ID for the given purpose
*/
static public int getNewId(Object purpose, int minimumId, int maximumId) {
LinkedList<Integer> freeIds = freeIdsMap.get(purpose);
if (freeIds == null) {
freeIds = new LinkedList<Integer>();
for (int id = minimumId; id <= maximumId; id++) {
freeIds.add(id);
}
freeIdsMap.put(purpose, freeIds);
}
// No more free IDs?
if (freeIds.size() <= 0) {
// Return an invalid ID
return INVALID_ID;
}
// Return the lowest free ID
return freeIds.remove();
}
static public void resetIds(Object purpose) {
freeIdsMap.remove(purpose);
}
/**
* Release an ID for a given purpose. The ID had to be created first
* by getNewId().
* After release, the ID is marked as being free and can be returned
* again by getNewId().
*
* @param id The ID to be released
* @param purpose The ID will be releases for this purpose.
* @return true if the ID was successfully released
* false if the ID could not be released
* (because the purpose was not exiting or
* the ID was already released)
*/
static public boolean releaseId(int id, Object purpose) {
LinkedList<Integer> freeIds = freeIdsMap.get(purpose);
if (freeIds == null) {
Emulator.log.warn(String.format("Attempt to release ID=%d with unknown purpose='%s'", id, purpose));
return false;
}
// Add the id back to the freeIds list,
// and keep the id's ordered (lowest first).
for (ListIterator<Integer> lit = freeIds.listIterator(); lit.hasNext(); ) {
int currentId = lit.next();
if (currentId == id) {
Emulator.log.warn(String.format("Attempt to release free ID=%d with purpose='%s'", id, purpose));
return false;
}
if (currentId > id) {
// Insert the id before the currentId
lit.set(id);
lit.add(currentId);
return true;
}
}
freeIds.add(id);
return true;
}
}