/**
* This file is part of the Kompics P2P Framework.
*
* Copyright (C) 2009 Swedish Institute of Computer Science (SICS)
* Copyright (C) 2009 Royal Institute of Technology (KTH)
*
* Kompics 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 2
* of the License, or (at your option) any later version.
*
* This program 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 this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
package se.sics.gvod.system.storage;
import se.sics.gvod.system.vod.PieceInTransit;
import se.sics.gvod.common.BitField;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Random;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import se.sics.gvod.common.UtilityVod;
import se.sics.gvod.common.VodDescriptor;
/**
*
* @author gautier
*/
public class Stats {
private Logger logger = LoggerFactory.getLogger(Stats.class);
private List<PieceStats> stats;
private Map<Integer, PieceStats> statsMap;
protected Random random;
private int seed;
private Map<VodDescriptor, Integer> interestingNode = new HashMap<VodDescriptor, Integer>();
public Stats(UtilityVod utility, int seed) {
stats = new ArrayList<PieceStats>();
this.seed = seed;
random = new Random(seed);
statsMap = new HashMap<Integer, PieceStats>();
for (int i = utility.getChunk(); i < utility.getChunk() + utility.getOffset(); i++) {
for (int j = i * BitField.NUM_PIECES_PER_CHUNK;
j < i * BitField.NUM_PIECES_PER_CHUNK + BitField.NUM_PIECES_PER_CHUNK;
j++) {
initiateStats(j);
}
}
}
public int getUpperPiece(Map<Integer, PieceInTransit> outstandingPieces, int readingPos, int readingWindow) {
List<PieceStats> piecesList;
piecesList = stats;
if (piecesList.isEmpty()) {
// System.out.println("BUUUUUUUUUUUUUG stats");
return -1;
}
Collections.sort(piecesList, PieceStats.statsOrder);
int i = 0;
int temp;
List<PieceStats> list = new ArrayList<PieceStats>();
List<PieceStats> priorityList = new ArrayList<PieceStats>();
do {
temp = piecesList.get(i).getNumNodesWithPiece();
if (temp == 0 && !outstandingPieces.containsKey(piecesList.get(i).getPiece())) {
if (piecesList.get(i).getPiece() < readingPos + readingWindow) {
priorityList.add(piecesList.get(i));
} else {
list.add(piecesList.get(i));
}
} else if (temp > 0) {
break;
}
i++;
} while (i < piecesList.size() && temp == 0);
if (priorityList.size() > 0) {
return priorityList.get(random.nextInt(priorityList.size())).getPiece();
}
if (list.size() > 0) {
return list.get(random.nextInt(list.size())).getPiece();
} else {
return -1;
}
}
public int pieceToDownload(VodDescriptor node,
Map<Integer, PieceInTransit> outstandingPieces,
int readingPos, int readingWindow) {
List<PieceStats> piecesList;
List<PieceStats> priorityList = new ArrayList<PieceStats>();
piecesList = stats;
for (Integer i : statsMap.keySet()) {
if (i <= readingPos + readingWindow
&& statsMap.get(i).getPeers().contains(node)
&& !outstandingPieces.containsKey(statsMap.get(i).getPiece())) {
priorityList.add(statsMap.get(i));
}
}
if (piecesList.isEmpty() || !interestingNode.containsKey(node)) {
if (piecesList.isEmpty()) {
logger.warn("Pieces list is empty");
} else {
logger.warn("Node not interesting: " + node.getVodAddress().getId());
}
return -1;
}
if (priorityList.size() > 0) {
return priorityList.get(random.nextInt(priorityList.size())).getPiece();
}
Collections.sort(piecesList, PieceStats.statsOrder);
int i = 0;
int temp;
do {
temp = piecesList.get(i).getNumNodesWithPiece();
i++;
} while (i < piecesList.size() && temp == 0);
i--;
if (i >= piecesList.size()) {
return -1;
}
do {
if (!outstandingPieces.containsKey(piecesList.get(i).getPiece())
&& piecesList.get(i).getPeers().contains(node)) {
return piecesList.get(i).getPiece();
}
i++;
} while (i < piecesList.size());
logger.warn("Stats didn't find an interesting piece.");
return -1;
}
public int pieceToDownloadFromUpper(VodDescriptor node,
Map<Integer, PieceInTransit> outstandingPieces, int readingPos, int readingWindow) {
List<PieceStats> piecesList;
List<PieceStats> priorityList = new ArrayList<PieceStats>();
piecesList = stats;
for (Integer i : statsMap.keySet()) {
if (i <= readingPos + readingWindow
&& statsMap.get(i).getPeers().contains(node)
&& !outstandingPieces.containsKey(statsMap.get(i).getPiece())) {
priorityList.add(statsMap.get(i));
}
}
if (piecesList.isEmpty()) {
return -1;
}
if (priorityList.size() > 0) {
return priorityList.get(random.nextInt(priorityList.size())).getPiece();
}
Collections.sort(piecesList, PieceStats.statsOrder);
int i = 0;
do {
if (!outstandingPieces.containsKey(piecesList.get(i).getPiece())
&& piecesList.get(i).getPeers().contains(node)) {
return piecesList.get(i).getPiece();
}
i++;
} while (i < piecesList.size());
return -1;
}
public List<Integer> getEligible(List<Integer> outstandingPieces, VodDescriptor node) {
List<Integer> result = new ArrayList<Integer>();
for (Integer piece : outstandingPieces) {
if ((statsMap.containsKey(piece) && statsMap.get(piece).getPeers().contains(node)) /*|| (idxMap.containsKey(piece) && idxMap.get(piece).getPeers().contains(node))*/) {
result.add(piece);
}
}
return result;
}
private void addStats(int k, VodDescriptor peer) {
if (statsMap.containsKey(k)) {
statsMap.get(k).add(peer);
if (interestingNode.containsKey(peer)) {
interestingNode.put(peer, interestingNode.get(peer) + 1);
} else {
interestingNode.put(peer, 1);
}
return;
}
}
private void initiateStats(int k) {
PieceStats pieceStats = new PieceStats(k, seed);
statsMap.put(k, pieceStats);
stats.add(pieceStats);
}
public void changeUtility(int oldUtility, UtilityVod newUtility, int max, BitField bitField) {
if (newUtility.isSeeder()) {
return;
}
if (newUtility.getChunk() - oldUtility >= newUtility.getOffset()
|| newUtility.getChunk() < oldUtility) {
stats = new ArrayList<PieceStats>();
statsMap = new HashMap<Integer, PieceStats>();
int lim = newUtility.getChunk() + newUtility.getOffset();
if (lim > bitField.getChunkFieldSize()) {
lim = bitField.getChunkFieldSize();
}
for (int i = newUtility.getChunk();
i < lim; i++) {
if (!bitField.hasChunk(i)) {
for (int j = i * BitField.NUM_PIECES_PER_CHUNK;
j < i * BitField.NUM_PIECES_PER_CHUNK + BitField.NUM_PIECES_PER_CHUNK;
j++) {
if (j >= max) {
break;
}
if (!bitField.getPiece(j)) {
initiateStats(j);
}
}
}
}
} else {
for (int i = oldUtility; i < newUtility.getChunk(); i++) {
for (int j = i * BitField.NUM_PIECES_PER_CHUNK;
j < i * BitField.NUM_PIECES_PER_CHUNK+ BitField.NUM_PIECES_PER_CHUNK;
j++) {
PieceStats pieceStats = statsMap.get(j);
stats.remove(pieceStats);
statsMap.remove(j);
}
}
int lim = newUtility.getChunk() + newUtility.getOffset();
if (lim > bitField.getChunkFieldSize()) {
lim = bitField.getChunkFieldSize();
}
for (int i = oldUtility + newUtility.getOffset();
i < lim; i++) {
if (!bitField.hasChunk(i)) {
for (int j = i * BitField.NUM_PIECES_PER_CHUNK;
j < i * BitField.NUM_PIECES_PER_CHUNK + BitField.NUM_PIECES_PER_CHUNK;
j++) {
if (j >= max) {
break;
}
if (!bitField.getPiece(j)) {
initiateStats(j);
}
}
}
}
}
}
public boolean removePieceFromStats(int piece) {
if (statsMap.containsKey(piece)) {
for (VodDescriptor node : statsMap.get(piece).getPeers()) {
interestingNode.put(node, interestingNode.get(node) - 1);
if (interestingNode.get(node) == 0) {
interestingNode.remove(node);
}
}
boolean result = stats.remove(statsMap.get(piece));
statsMap.remove(piece);
return result;
} else {
return false;
}
}
public void updateStats(VodDescriptor peer, byte[][] availablePieces,
byte[] availableChunk, UtilityVod peerUtility, UtilityVod utility) {
if (utility.isSeeder()) {
return;
}
if (peerUtility.isSeeder()) {
for (int j = 0; j < utility.getChunk() + utility.getOffset(); j++) {
for (int k = (utility.getChunk() + j) * BitField.NUM_PIECES_PER_CHUNK;
k < (utility.getChunk() + j) * BitField.NUM_PIECES_PER_CHUNK +
BitField.NUM_PIECES_PER_CHUNK;
k++) {
addStats(k, peer);
}
}
return;
}
if (availablePieces == null) {
return;
}
int i = 0;
if (peerUtility.getChunk() >= utility.getChunk()) {
for (int j = 0; j < peerUtility.getChunk() - utility.getChunk(); j++) {
int mask = BitField.NUM_PIECES_PER_CHUNK >> (j % 8);
if ((availableChunk[j / 8] & mask) != 0) {
for (int k = (utility.getChunk() + j) * BitField.NUM_PIECES_PER_CHUNK;
k < (utility.getChunk() + j) * BitField.NUM_PIECES_PER_CHUNK + BitField.NUM_PIECES_PER_CHUNK; k++) {
addStats(k, peer);
}
}
i++;
}
for (int j = 0; j < utility.getOffset() - i; j++) {
for (int k = 0; k < BitField.NUM_PIECES_PER_CHUNK; k++) {
int mask = BitField.NUM_PIECES_PER_CHUNK >> (k % 8);
if ((availablePieces[j][k / 8] & mask) != 0) {
addStats((peerUtility.getChunk() + j) * BitField.NUM_PIECES_PER_CHUNK + k, peer);
}
}
}
} else {
for (int j = 0; j < utility.getChunk() - peerUtility.getChunk(); j++) {
i++;
}
for (int j = i; j < utility.getOffset(); j++) {
for (int k = 0; k < BitField.NUM_PIECES_PER_CHUNK; k++) {
int mask = BitField.NUM_PIECES_PER_CHUNK >> (k % 8);
if ((availablePieces[j][k / 8] & mask) != 0) {
addStats((peerUtility.getChunk() + j) * BitField.NUM_PIECES_PER_CHUNK + k, peer);
}
}
}
}
}
public VodDescriptor getRandomNodeWithPiece(int piece,
Collection<VodDescriptor> usedNodes) {
List<VodDescriptor> list = new ArrayList<VodDescriptor>();
if (statsMap.get(piece) == null) {
return null;
}
list.addAll(statsMap.get(piece).getPeers());
if (usedNodes != null) {
list.removeAll(usedNodes);
}
if (list.isEmpty()) {
return null;
} else {
return list.get(random.nextInt(list.size()));
}
}
public void removeNode(VodDescriptor node) {
for (PieceStats st : stats) {
st.removeNode(node);
}
interestingNode.remove(node);
}
}