/*
* transfer.cpp
- * p2tp
+ * some transfer-scope code
*
* Created by Victor Grishchenko on 10/6/09.
- * Copyright 2009 Delft Technical University. All rights reserved.
+ * Copyright 2009 Delft University of Technology. All rights reserved.
*
*/
+#include <errno.h>
+#include <string>
+#include <sstream>
+#include "swift.h"
-#include "p2tp.h"
+#include "ext/seq_picker.cpp" // FIXME FIXME FIXME FIXME
+using namespace swift;
-File::File (int _fd) : fd(_fd), status_(DONE), hashes(_fd)
-{
- bin::vec peaks = bin::peaks(hashes.data_size());
- history.insert(history.end(),peaks.begin(),peaks.end());
- for(bin::vec::iterator i=peaks.begin(); i!=peaks.end(); i++)
- ack_out.set(*i);
-}
-
-File::File (Sha1Hash hash, int _fd) : hashes(hash), fd(_fd), status_(EMPTY) {
- // TODO resubmit data
-}
+std::vector<FileTransfer*> FileTransfer::files(20);
-File::~File() {
- if (fd>0) ::close(fd);
-}
+#define BINHASHSIZE (sizeof(bin64_t)+sizeof(Sha1Hash))
+// FIXME: separate Bootstrap() and Download(), then Size(), Progress(), SeqProgress()
-bool File::OfferHash (bin pos, const Sha1Hash& hash) {
- HashTree::hashres_t res = hashes.offer(pos,hash);
- if (res==HashTree::PEAK_ACCEPT) { // file size is finally known
- ftruncate(fd, size());
- LOG(INFO)<<fd<<" file size is set to "<<size();
- history.push_back(0);
- status_ = IN_PROGRESS;
- }
- return res==HashTree::PEAK_ACCEPT || res==HashTree::ACCEPT;
+FileTransfer::FileTransfer (const char* filename, const Sha1Hash& _root_hash) :
+ file_(filename,_root_hash), hs_in_offset_(0), cb_installed(0)
+{
+ if (files.size()<fd()+1)
+ files.resize(fd()+1);
+ files[fd()] = this;
+ picker_ = new SeqPiecePicker(this);
+ picker_->Randomize(rand()&63);
+ init_time_ = Datagram::Time();
}
-File* File::find (const Sha1Hash& hash) {
- for(vector<File*>::iterator i=files.begin(); i!=files.end(); i++)
- if (*i && (*i)->hashes.root==hash)
- return *i;
- return NULL;
+void Channel::CloseTransfer (FileTransfer* trans) {
+ for(int i=0; i<Channel::channels.size(); i++)
+ if (Channel::channels[i] && Channel::channels[i]->transfer_==trans)
+ delete Channel::channels[i];
}
-int p2tp::Open (const char* filename) {
- int fd = ::open(filename,O_RDONLY);
- if (fd<0)
- return -1;
- if (File::files.size()<fd+1)
- File::files.resize(fd+1);
- File::files[fd] = new File(fd);
- return fd;
+void swift::AddProgressCallback (int transfer,ProgressCallback cb,uint8_t agg) {
+ FileTransfer* trans = FileTransfer::file(transfer);
+ if (!trans)
+ return;
+ trans->cb_agg[trans->cb_installed] = agg;
+ trans->callbacks[trans->cb_installed] = cb;
+ trans->cb_installed++;
}
-int p2tp::Open (const Sha1Hash& root_hash, const char* filename) {
- int fd = ::open(filename,O_RDWR|O_CREAT,S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH);
- if (fd<0)
- return -1;
- if (File::files.size()<fd+1)
- File::files.resize(fd+1);
- File::files[fd] = new File(root_hash,fd);
- return fd;
-}
-
-size_t p2tp::file_size (int fd) { return File::file(fd)->size(); }
-void p2tp::Close (int fid) {
- if (!File::files[fid])
- return;
- delete File::files[fid];
- File::files[fid] = NULL;
+void swift::ExternallyRetrieved (int transfer,bin64_t piece) {
+ FileTransfer* trans = FileTransfer::file(transfer);
+ if (!trans)
+ return;
+ trans->ack_out().set(piece); // that easy
}
-Sha1Hash HashTree::deriveRoot () {
- int i = peak_count-1;
- bin64_t p = peaks[i].first;
- Sha1Hash hash = peak_hashes[i].second;
- i--;
- while (p<bin64_t::ALL) {
- if (p.is_left()) {
- p = p.parent();
- hash = Sha1Hash(hash,Sha1Hash::ZERO);
- } else {
- if (i<0 || peaks[i].first!=p.sibling())
- return Sha1Hash::ZERO;
- hash = Sha1Hash(peak_hashes[i].second,hash);
- p = p.parent();
- i--;
- }
- }
- return hash;
+void swift::RemoveProgressCallback (int transfer, ProgressCallback cb) {
+ FileTransfer* trans = FileTransfer::file(transfer);
+ if (!trans)
+ return;
+ for(int i=0; i<trans->cb_installed; i++)
+ if (trans->callbacks[i]==cb)
+ trans->callbacks[i]=trans->callbacks[--trans->cb_installed];
}
-/** Three stages: have file, have root hash, have peaks. */
-HashTree::HashTree (int _datafd, int _hashfd, Sha1Hash _root)
-: datafd(_datafd), hashfd(_hashfd), root(_root)
+
+FileTransfer::~FileTransfer ()
{
- if (root==Sha1Hash::ZERO) { // fresh file; derive the root hash
- struct stat st;
- if (fstat(fd, &st)!=0)
- return;
- resize(st.st_size);
- lseek(datafd,0,SEEK_SET);
- for(bin64_t k=0; k!=toppeak.right(); k=k.dfs_next()) {
- if (k.is_base()) {
- uint8_t data[1024];
- int rd = read(datafd,data,1024);
- if (rd<=0)
- return; // FIXME
- hashes[k] = Sha1Hash(data,rd);
- } else
- hashes[k] = Sha1Hash(hashes[k.left()],hashes[k.right()]);
- }
- // zeros
- root = hashes[toppeak];
- for(bin64_t p=toppeak; p!=bin64_t::ALL; p=p.parent())
- root = Sha1Hash(root,Sha1Hash::ZERO);
- }
- // TODO: THIS MUST BE THE "Transfer"/"File" CLASS
- if (file_size==0) { // hash only, no file, no peak hashes
- if (root==Sha1Hash::ZERO)
- return; // FIXME
- resize(0); // anyway, 1cell for the root, 63 for peaks
- }
-
+ Channel::CloseTransfer(this);
+ files[fd()] = NULL;
+ delete picker_;
}
-bool FileTransfer::acceptData (uint64_t bin, uint8_t* data, size_t len) {
+
+FileTransfer* FileTransfer::Find (const Sha1Hash& root_hash) {
+ for(int i=0; i<files.size(); i++)
+ if (files[i] && files[i]->root_hash()==root_hash)
+ return files[i];
+ return NULL;
}
-HashTree::~HashTree () {
- close(fd);
+int swift:: Find (Sha1Hash hash) {
+ FileTransfer* t = FileTransfer::Find(hash);
+ if (t)
+ return t->fd();
+ return -1;
}
-HashTree::hashres_t HashTree::offerPeak (bin pos, Sha1Hash hash) {
- if (bin(pos+1).layer())
- return REJECT;
- if (bin::all1(pos))
- peaks.clear();
- peaks.push_back(binhash(pos,hash));
- if (deriveRoot()==root) { // bingo
- mass = peaks.back().first;
- length = mass.length();
- status.resize(mass+1);
- bits.resize(mass+1);
- for(int i=0; i<peaks.size(); i++) {
- bits[peaks[i].first] = peaks[i].second;
- status[peaks[i].first] = true;
- }
- return PEAK_ACCEPT;
- } else
- return pos.layer() ? DUNNO : REJECT;
-}
-HashTree::hashres_t HashTree::offer (bin pos, const Sha1Hash& hash) {
- if (!length) // only peak hashes are accepted at this point
- return offerPeak(pos,hash);
- if (pos>mass)
- return REJECT;
- if (status[pos])
- return bits[pos]==hash ? ACCEPT : REJECT;
- bits[pos] = hash;
- // walk to the nearest proven hash
- if (bits[pos.sibling()]==Sha1Hash::ZERO)
- return DUNNO;
- bin p = pos.parent();
- while (!status[p]) {
- bits[p] = Sha1Hash(bits[p.left()],bits[p.right()]);
- p = p.parent();
- }
- if ( bits[p] == Sha1Hash(bits[p.left()],bits[p.right()]) ) {
- for(bin i=pos; i<p; i=i.parent())
- status[i] = status[i.sibling()] = true;
- return ACCEPT;
- } else
- return REJECT;
-
+
+void FileTransfer::OnPexIn (const Address& addr) {
+ for(int i=0; i<hs_in_.size(); i++) {
+ Channel* c = Channel::channel(hs_in_[i]);
+ if (c && c->transfer().fd()==this->fd() && c->peer()==addr)
+ return; // already connected
+ }
+ if (hs_in_.size()<20) {
+ new Channel(this,Datagram::default_socket(),addr);
+ } else {
+ pex_in_.push_back(addr);
+ if (pex_in_.size()>1000)
+ pex_in_.pop_front();
+ }
}
+int FileTransfer::RevealChannel (int& pex_out_) { // FIXME brainfuck
+ pex_out_ -= hs_in_offset_;
+ if (pex_out_<0)
+ pex_out_ = 0;
+ while (pex_out_<hs_in_.size()) {
+ Channel* c = Channel::channel(hs_in_[pex_out_]);
+ if (c && c->transfer().fd()==this->fd()) {
+ if (c->is_established()) {
+ pex_out_ += hs_in_offset_ + 1;
+ return c->id();
+ } else
+ pex_out_++;
+ } else {
+ hs_in_[pex_out_] = hs_in_[0];
+ hs_in_.pop_front();
+ hs_in_offset_++;
+ }
+ }
+ pex_out_ += hs_in_offset_;
+ return -1;
+}