/* * bitlet - Simple bittorrent library * Copyright (C) 2008 Alessandro Bahgat Shehata, Daniele Castagna * * 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 org.bitlet.wetorrent.disk; import java.io.File; import java.io.IOException; import java.io.RandomAccessFile; import java.nio.ByteBuffer; import java.util.ArrayList; import java.util.Iterator; import java.util.LinkedList; import java.util.List; import java.util.Map; import org.bitlet.wetorrent.Metafile; public class PlainFileSystemTorrentDisk implements TorrentDisk { Metafile metafile; private List<Piece> pieces = new ArrayList<Piece>(); List<RandomAccessFile> files = new LinkedList<RandomAccessFile>(); File saveDirectory; public PlainFileSystemTorrentDisk(Metafile metafile, File saveDirectory) { this.metafile = metafile; this.saveDirectory = saveDirectory; } public synchronized void resume() throws IOException { resume(null); } public synchronized void resume(ResumeListener rl) throws IOException { long completed = 0; long resumed = 0; for (Piece p : pieces) { /*pretend that the piece is already downloaded and check the hash*/ if (rl != null) { rl.percent(completed, resumed); } p.addPieceBlock(0, p.getLength()); if (!p.checkSha1()) { p.clear(); } else { resumed += p.getLength(); } completed += p.getLength(); } if (rl != null) { rl.percent(completed, resumed); } } public synchronized boolean init() throws IOException { boolean resume = false; /* create pieces */ Long pieceNumber = 0l; for (Object elem : metafile.getPieces()) { byte[] sha1 = (byte[]) elem; Piece piece = new Piece(sha1); if (pieceNumber < metafile.getPieces().size() - 1 && (metafile.getLength() % metafile.getPieceLength()) > 0) { piece.setLength(metafile.getPieceLength().intValue()); } else { piece.setLength(new Long(metafile.getLength() % metafile.getPieceLength()).intValue()); } pieces.add(piece); pieceNumber++; } saveDirectory.mkdirs(); /*create files*/ Long fileLength = 0l; if (metafile.isSingleFile()) { File persistentFile = new File(saveDirectory, metafile.getName()); if (persistentFile.exists()) { resume = true; } RandomAccessFile raf = new RandomAccessFile(persistentFile, "rw"); raf.setLength(metafile.getLength()); files.add(raf); } else { if (!saveDirectory.getName().equals(metafile.getName())) { saveDirectory = new File(saveDirectory, metafile.getName()); saveDirectory.mkdir(); } for (Object elem : metafile.getFiles()) { Map file = (Map) elem; List path = (List) file.get(ByteBuffer.wrap("path".getBytes())); String pathName = ""; Iterator pathIterator = path.iterator(); while (pathIterator.hasNext()) { byte[] pathElem = ((ByteBuffer) pathIterator.next()).array(); pathName += "/" + new String(pathElem); if (pathIterator.hasNext()) { new File(saveDirectory, pathName).mkdir(); } } /* for (Object pathElem : path) pathName += "/" + new String((byte[])pathElem );*/ Long length = (Long) file.get(ByteBuffer.wrap("length".getBytes())); File persistentFile = new File(saveDirectory.getAbsolutePath() + pathName); if (persistentFile.exists()) { resume = true; } RandomAccessFile raf = new RandomAccessFile(persistentFile, "rw"); raf.setLength(length); fileLength += length; files.add(raf); } } /*Associate pieces to files*/ Iterator<RandomAccessFile> fileIterator = files.iterator(); Iterator<Piece> pieceIterator = pieces.iterator(); Long fileOffset = 0l; Long pieceOffset = 0l; Piece piece = pieceIterator.next(); RandomAccessFile file = fileIterator.next(); while (piece != null && file != null) { piece.addFilePointer(new FilePieceMapper(file, fileOffset, pieceOffset)); Long pieceFreeBytes = piece.getLength() - pieceOffset; Long fileMissingBytes = file.length() - fileOffset; if (pieceFreeBytes < fileMissingBytes) { fileOffset += pieceFreeBytes; if (pieceIterator.hasNext()) { piece = pieceIterator.next(); } else { piece = null; } pieceOffset = 0l; } else if (pieceFreeBytes > fileMissingBytes) { pieceOffset += fileMissingBytes; if (fileIterator.hasNext()) { file = fileIterator.next(); } else { file = null; } fileOffset = 0l; } else /* == */ { fileOffset = 0l; pieceOffset = 0l; if (fileIterator.hasNext()) { file = fileIterator.next(); } else { file = null; } if (pieceIterator.hasNext()) { piece = pieceIterator.next(); } else { piece = null; } } } return resume; } public synchronized byte[] getBitfieldCopy() { byte[] bitField = new byte[(pieces.size() >> 3) + ((pieces.size() & 0x7) != 0 ? 1 : 0)]; for (int i = 0; i < pieces.size(); i++) { bitField[i >> 3] |= (pieces.get(i).isCompleted() ? 0x80 : 0) >> (i & 0x7); } return bitField; } public synchronized void write(int index, int begin, byte[] block) throws IOException { Piece piece = pieces.get(index); if (!piece.isCompleted()) { piece.write(begin, block); } } public synchronized byte[] read(int index, int begin, int length) throws IOException { Piece piece = pieces.get(index); return piece.read(begin, length); } public synchronized Long getCompleted() { Long completed = 0l; for (Piece p : pieces) { completed += p.getCompleted(); } return completed; } public synchronized boolean isCompleted(int index) { return pieces.get(index).isCompleted(); } public synchronized int getDownloaded(int index) { return pieces.get(index).getCompleted(); } public synchronized boolean isAvailable(int index, int begin, int length) { return pieces.get(index).isAvaiable(begin, length); } public synchronized int getLength(int index) { return pieces.get(index).getLength(); } public synchronized int getFirstMissingByte(int index) { return pieces.get(index).getFirstMissingByte(); } public synchronized void close() { for (RandomAccessFile file : files) { try { file.close(); } catch (IOException ex) { } } } public synchronized long available(int index, int begin) { return available(index, begin, Long.MAX_VALUE); } public synchronized long available(int index, int begin, long length) { int pieceLength = pieces.get(0).getLength(); boolean goOn = true; long avaiable = 0; while (goOn) { Piece p = pieces.get(index); int pieceAvaiable = p.available(begin); avaiable += pieceAvaiable; index++; begin = 0; if (pieceAvaiable != p.getLength() - begin || pieceAvaiable >= length) { goOn = false; } } return avaiable; } }