prettified, Windows build broken
authorvictor <victor@e16421f0-f15b-0410-abcd-98678b794739>
Mon, 9 Nov 2009 13:34:57 +0000 (13:34 +0000)
committervictor <victor@e16421f0-f15b-0410-abcd-98678b794739>
Mon, 9 Nov 2009 13:34:57 +0000 (13:34 +0000)
git-svn-id: https://ttuki.vtt.fi/svn/p2p-next/TUD/p2tp/trunk@511 e16421f0-f15b-0410-abcd-98678b794739

15 files changed:
bins.cpp
bins.h
compat/util.cpp
datagram.cpp
datagram.h
exec/leecher.cpp
hashtree.cpp
hashtree.h
p2tp.cpp
p2tp.h
sendrecv.cpp
tests/binstest2.cpp
tests/hashtest.cpp
tests/transfertest.cpp
transfer.cpp

index 3be3d68..ca3616f 100644 (file)
--- a/bins.cpp
+++ b/bins.cpp
@@ -37,7 +37,8 @@ bins::bins() :  height(4), blocks_allocated(0), cells(NULL),
 }
 
 void bins::twist (uint64_t mask) {
-    assert( (1<<height) > mask );
+    while ( (1<<height) <= mask )
+        extend_range();
     twist_mask = mask;
 }
 
@@ -394,6 +395,19 @@ void        bins::copy_range (bins& origin, bin64_t range) {
     }
 }
 
+uint64_t    bins::seq_length () {
+    iterator i(this);
+    if (!i.deep() && *i==FILLED)
+        return i.pos.width();
+    while (!i.pos.is_base()) {
+        if (i.deep() || *i!=FILLED) 
+            i.left();
+        else
+            i.sibling();
+    }
+    return i.pos.base_offset() + (*i==FILLED ? 1 : 0);
+}
+
 binheap::binheap() {
     size_ = 32;
     heap_ = (bin64_t*) malloc(size_*sizeof(bin64_t));
diff --git a/bins.h b/bins.h
index 8fa3f9b..6d6bd40 100644 (file)
--- a/bins.h
+++ b/bins.h
@@ -41,6 +41,8 @@ public:
 
     uint32_t    size() { return cells_allocated; }
     
+    uint64_t    seq_length ();
+    
     bin64_t     cover(bin64_t val);
 
     uint64_t    mass ();
index c772a20..281dfac 100644 (file)
@@ -33,14 +33,6 @@ std::string gettmpdir(void)
 }\r
 \r
 \r
-    void print_error(const char* msg) {\r
-        perror(msg);\r
-#ifdef _WIN32\r
-        int e = WSAGetLastError();\r
-        if (e)\r
-            fprintf(stderr,"network error #%i\n",e);\r
-#endif\r
-    }\r
     \r
     \r
 }; // namespace\r
index 0c60113..cd8da64 100644 (file)
@@ -15,6 +15,7 @@
     #include <arpa/inet.h>
 #endif
 #include "datagram.h"
+#include "compat.h"
 
 namespace p2tp {
 
@@ -126,10 +127,10 @@ SOCKET Datagram::Wait (int sockcnt, SOCKET* sockets, tint usec) {
 }
 
 tint Datagram::Time () {
-       HiResTimeOfDay* tod = HiResTimeOfDay::Instance();
-       tint ret = tod->getTimeUSec();
+       //HiResTimeOfDay* tod = HiResTimeOfDay::Instance();
+       //tint ret = tod->getTimeUSec();
        //DLOG(INFO)<<"now is "<<ret;
-       return now=ret;
+       return now = usec_time();
 }
 
 SOCKET Datagram::Bind (Address addr_) {
index 5d8ba05..c05cf38 100644 (file)
@@ -170,7 +170,7 @@ struct Datagram {
                length+=8;
        }
        void    PushHash (const Sha1Hash& hash) {
-               Push(hash.bits, Sha1Hash::SIZE);
+               Push((uint8_t*)hash.bits, Sha1Hash::SIZE);
        }
 
        uint8_t Pull8() {
index cee17e4..e4f8f55 100644 (file)
@@ -30,14 +30,16 @@ int main (int argn, char** args) {
     
     char* filename = args[2];
     
-    Address tracker(args[3]), bindaddr(args[4]);
+    Address tracker(args[3]), bindaddr;
     
     if (tracker==Address()) {
         fprintf(stderr,"Tracker address format: [1.2.3.4:]12345\n");
         return -2;
     }
-    if (bindaddr==Address()) 
-        bindaddr = Address(rand()%10000+7000);
+    if (argn>=5) 
+        bindaddr = Address(args[4]);
+    else
+        bindaddr = Address(INADDR_ANY,rand()%10000+7000);
     
     assert(0<p2tp::Listen(bindaddr));
     
index b98cb9a..06095d2 100644 (file)
 #include "hashtree.h"
 #include <openssl/sha.h>
 #include <string.h>
-#ifndef _WIN32
-#include <sys/mman.h>
-#endif
-#include <sys/stat.h>
 #include <stdlib.h>
 #include <fcntl.h>
+#include "compat.h"
 
-using namespace std;
+using namespace p2tp;
 
 #define HASHSZ 20
 const size_t Sha1Hash::SIZE = HASHSZ;
 const Sha1Hash Sha1Hash::ZERO = Sha1Hash();
 
 Sha1Hash::Sha1Hash(const Sha1Hash& left, const Sha1Hash& right) {
-       uint8_t data[HASHSZ*2];
+       char data[HASHSZ*2];
        memcpy(data,left.bits,SIZE);
        memcpy(data+SIZE,right.bits,SIZE);
-       SHA1(data,SIZE*2,bits);
+       SHA1((unsigned char*)data,SIZE*2,bits);
 }
 
-Sha1Hash::Sha1Hash(const uint8_t* data, size_t length) {
-       SHA1(data,length,bits);
+Sha1Hash::Sha1Hash(const char* data, size_t length) {
+    if (length==-1)
+        length = strlen(data);
+       SHA1((unsigned char*)data,length,bits);
 }
 
-Sha1Hash::Sha1Hash(const char* str) {
-       SHA1((const unsigned char*)str,strlen(str),bits);
+Sha1Hash::Sha1Hash(const uint8_t* data, size_t length) {
+       SHA1(data,length,bits);
 }
 
 Sha1Hash::Sha1Hash(bool hex, const char* hash) {
@@ -47,129 +46,275 @@ Sha1Hash::Sha1Hash(bool hex, const char* hash) {
             sscanf(hx, "%x", &val);
             bits[i] = val;
         }
-        assert(this->hex()==string(hash));
+        assert(this->hex()==std::string(hash));
     } else
         memcpy(bits,hash,SIZE);
 }
 
-string Sha1Hash::hex() const {
+std::string    Sha1Hash::hex() const {
        char hex[HASHSZ*2+1];
        for(int i=0; i<HASHSZ; i++)
-               sprintf(hex+i*2, "%02x", bits[i]);
-       return string(hex,HASHSZ*2);
+               sprintf(hex+i*2, "%02x", (int)(unsigned char)bits[i]);
+       return std::string(hex,HASHSZ*2);
+}
+
+
+
+/**     H a s h   t r e e       */
+
+
+HashTree::HashTree (const char* filename, const Sha1Hash& root_hash, const char* hash_filename) :
+root_hash_(root_hash), fd_(0), hash_fd_(0), data_recheck_(false),
+peak_count_(0), hashes_(NULL), size_(0), sizek_(0),
+complete_(0), completek_(0)
+{
+       fd_ = open(filename,O_RDWR|O_CREAT,S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH);
+       if (fd_<0)
+        return;
+    char hfn[1024] = "";
+    if (!hash_filename) {
+        strcat(hfn, filename);
+        strcat(hfn, ".mhash");
+    } else
+        strcpy(hfn,hash_filename);
+    hash_fd_ = open(hfn,O_RDWR|O_CREAT,S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH);
+    if (hash_fd_<0)
+        return;
+    if (root_hash_==Sha1Hash::ZERO) { // fresh submit, hash it
+        assert(file_size(fd_));
+        Submit();
+    } else {
+        RecoverProgress();
+    } // else  LoadComplete()
+}
+
+
+void            HashTree::Submit () {
+    size_ = file_size(fd_);
+       sizek_ = (size_>>10) + ((size_&1023) ? 1 : 0);
+    peak_count_ = bin64_t::peaks(sizek_,peaks_);
+    int hashes_size = Sha1Hash::SIZE*sizek_*2;
+    file_resize(hash_fd_,hashes_size);
+    hashes_ = (Sha1Hash*) memory_map(hash_fd_,hashes_size);
+    if (!hashes_) {
+        size_ = sizek_ = complete_ = completek_ = 0;
+        print_error("mmap failed");
+        return;
+    }
+    for (size_t i=0; i<sizek_; i++) {
+        char kilo[1<<10];
+        size_t rd = read(fd_,kilo,1<<10);
+        if (rd<(1<<10) && i!=sizek_-1) {
+            free(hashes_);
+            hashes_=NULL;
+            return;
+        }
+        bin64_t pos(0,i);
+        hashes_[pos] = Sha1Hash(kilo,rd);
+        ack_out_.set(pos);
+        complete_+=rd;
+        completek_++;
+    }
+    for (int p=0; p<peak_count_; p++) {
+        if (!peaks_[p].is_base())
+            for(bin64_t b=peaks_[p].left_foot().parent(); b.within(peaks_[p]); b=b.next_dfsio(1))
+                hashes_[b] = Sha1Hash(hashes_[b.left()],hashes_[b.right()]);
+        peak_hashes_[p] = hashes_[peaks_[p]];
+    }
+
+    root_hash_ = DeriveRoot();
+    
 }
 
 
+/** Basically, simulated receiving every single packet, except
+ for some optimizations. */
+void            HashTree::RecoverProgress () {
+    size_t size = file_size(fd_);
+       size_t sizek = (size>>10) + ((size&1023) ? 1 : 0);
+    bin64_t peaks[64];
+    int peak_count = bin64_t::peaks(sizek,peaks);
+    for(int i=0; i<peak_count; i++) {
+        Sha1Hash peak_hash;
+        file_seek(hash_fd_,peaks[i]*sizeof(Sha1Hash));
+        if (read(hash_fd_,&peak_hash,sizeof(Sha1Hash))!=sizeof(Sha1Hash))
+            return;
+        OfferPeakHash(peaks[i], peak_hash);
+    }
+    if (!this->size())
+        return; // if no valid peak hashes found
+    // at this point, we may use mmapd hashes already
+    // so, lets verify hashes and the data we've got
+    char zeros[1<<10];
+    memset(zeros, 0, 1<<10);
+    Sha1Hash kilo_zero(zeros,1<<10);
+    for(int p=0; p<size_kilo(); p++) {
+        char buf[1<<10];
+        bin64_t pos(0,p);
+        if (hashes_[pos]==Sha1Hash::ZERO)
+            continue;
+        size_t rd = read(fd_,buf,1<<10);
+        assert(rd==(1<<10) || p==size_kilo()-1);
+        if (rd==(1<<10) && !memcmp(buf, zeros, rd) && hashes_[pos]!=kilo_zero)
+            continue;
+        if ( data_recheck_ && !OfferHash(pos, Sha1Hash(buf,rd)) )
+            continue;
+        ack_out_.set(pos);
+        completek_++;
+        complete_+=rd;
+    }
+}
+
 
-/*void HashTree::expand (bin tolen) {
-       if (bits)
-               munmap(bits,length*HASHSZ);
-       length = tolen;
-       status.resize(length);
-       bits = (Sha1Hash*) mmap(NULL,length*HASHSZ,PROT_READ|PROT_WRITE,MAP_SHARED,fd,0);
-}*/
+bool            HashTree::OfferPeakHash (bin64_t pos, const Sha1Hash& hash) {
+    assert(!size_);
+    if (peak_count_) {
+        bin64_t last_peak = peaks_[peak_count_-1];
+        if ( pos.layer()>=last_peak.layer() ||
+            pos.base_offset()!=last_peak.base_offset()+last_peak.width() )
+            peak_count_ = 0;
+    }
+    peaks_[peak_count_] = pos;
+    peak_hashes_[peak_count_] = hash;
+    peak_count_++;
+    // check whether peak hash candidates add up to the root hash
+    Sha1Hash mustbe_root = DeriveRoot();
+    if (mustbe_root!=root_hash_)
+        return false;
+    for(int i=0; i<peak_count_; i++)
+        sizek_ += peaks_[i].width();
+    
+    // bingo, we now know the file size (rounded up to a KByte)
+    
+    size_ = sizek_<<10;
+    completek_ = complete_ = 0;
+       sizek_ = (size_>>10) + ((size_&1023) ? 1 : 0);
+    
+    // FIXME check tail here
+    if (file_size(fd_)!=size_)
+        if (file_resize(fd_, size_)) {
+            print_error("cannot set file size\n");
+            size_=0; // remain in the 0-state
+            return false;
+        }
+    
+    // mmap the hash file into memory
+    size_t expected_size = sizeof(Sha1Hash)*sizek_*2;
+    if ( file_size(hash_fd_) != expected_size )
+        file_resize (hash_fd_, expected_size);
+    
+    hashes_ = (Sha1Hash*) memory_map(hash_fd_,expected_size);
+    if (!hashes_) {
+        size_ = sizek_ = complete_ = completek_ = 0;
+        print_error("mmap failed");
+        return false;
+    }
+    
+    for(int i=0; i<peak_count_; i++)
+        hashes_[peaks_[i]] = peak_hashes_[i];
+    return true;
+}
 
 
-/*Sha1Hash HashTree::deriveRoot () {
-       int i = peaks.size()-1;
-       bin p = peaks[i].first;
-       Sha1Hash hash = peaks[i].second;
-       i--;
-       while (p<bin::ALL) {
+Sha1Hash        HashTree::DeriveRoot () {
+       int c = peak_count_-1;
+       bin64_t p = peaks_[c];
+       Sha1Hash hash = peak_hashes_[c];
+       c--;
+       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())
+                       if (c<0 || peaks_[c]!=p.sibling())
                                return Sha1Hash::ZERO;
-                       hash = Sha1Hash(peaks[i].second,hash);
+                       hash = Sha1Hash(peak_hashes_[c],hash);
                        p = p.parent();
-                       i--;
+                       c--;
                }
+        //dprintf("p %lli %s\n",(uint64_t)p,hash.hex().c_str());
        }
-       return hash;
+    return hash;
 }
 
-HashTree::HashTree (int fd)  {
-       //fd = open(filename,O_RDWR|O_CREAT,S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH);
-       if (fd<0) return;
-       struct stat st;
-       fstat(fd, &st);
-       length = (st.st_size>>10) + (st.st_size%1024 ? 1 : 0);
-       mass = bin::lenpeak(length); // incorrect
-       bits.resize(mass+1);
-       status.resize(mass+1);
-       uint8_t buf[1024];
-       for(bin i=1; i<=mass; i++)
-               if (i.layer()) {
-                       bits[i] = Sha1Hash(bits[i.left()],bits[i.right()]);
-               } else {
-                       int len = pread(fd,buf,1024,i.offset()<<10);
-                       bits[i] = Sha1Hash(buf,len);
-               }
-       //close(fd);
-       bin::vec p = bin::peaks(length);
-       while(p.size()) {
-               peaks.push_back(binhash(p.front(),bits[p.front()]));
-               p.pop_front();
-       }
-       root = deriveRoot();
+
+/** For live streaming: appends the data, adjusts the tree.
+    @ return the number of fresh (tail) peak hashes */
+int         HashTree::AppendData (char* data, int length) {
+    return 0;
 }
 
-HashTree::HashTree (const Sha1Hash& with_root) : root(with_root), length(0), mass(0) {
-       // recover the partially filled hash file
-       // first, size
-       // then, peaks
-       // works? then offer the rest
+
+bin64_t         HashTree::peak_for (bin64_t pos) const {
+    int pi=0;
+    while (pi<peak_count_ && !pos.within(peaks_[pi]))
+        pi++;
+    return pi==peak_count_ ? bin64_t(bin64_t::NONE) : peaks_[pi];
 }
 
-HashTree::~HashTree () {
-       close(fd);
+
+bool            HashTree::OfferHash (bin64_t pos, const Sha1Hash& hash) {
+       if (!size_)  // only peak hashes are accepted at this point
+               return OfferPeakHash(pos,hash);
+    bin64_t peak = peak_for(pos);
+    if (peak==bin64_t::NONE)
+        return false;
+    if (peak==pos)
+        return hash == hashes_[pos];
+    if (ack_out_.get(pos.parent())!=bins::EMPTY)
+        return hash==hashes_[pos]; // have this hash already, even accptd data
+       hashes_[pos] = hash;
+    if (!pos.is_base())
+        return false; // who cares?
+    bin64_t p = pos;
+    Sha1Hash uphash = hash;
+    while ( p!=peak && ack_out_.get(p)==bins::EMPTY ) {
+        hashes_[p] = uphash;
+        p = p.parent();
+        uphash = Sha1Hash(hashes_[p.left()],hashes_[p.right()]) ;
+    }// walk to the nearest proven hash
+    return uphash==hashes_[p];
 }
 
-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;
+
+bool            HashTree::OfferData (bin64_t pos, const char* data, size_t length) {
+    if (!pos.is_base())
+        return false;
+    if (length<1024 && pos!=bin64_t(0,sizek_-1))
+        return false;
+    if (ack_out_.get(pos)==bins::FILLED)
+        return true; // to set data_in_
+    bin64_t peak = peak_for(pos);
+    if (peak==bin64_t::NONE)
+        return false;
+
+    if (!OfferHash(pos, Sha1Hash(data,length)))
+        return false;
+    
+    //printf("g %lli %s\n",(uint64_t)pos,hash.hex().c_str());
+    ack_out_.set(pos,bins::FILLED);
+    pwrite(fd_,data,length,pos.base_offset()<<10);
+    complete_ += length;
+    completek_++;
+    if (length<1024 && !(size_&1023)) {
+        size_ -= 1024 - length;
+        file_resize(fd_, size_);
+    }
+    return true;
 }
 
-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;
 
+uint64_t      HashTree::seq_complete () {
+    uint64_t seqk = ack_out_.seq_length();
+    if (seqk==sizek_)
+        return size_;
+    else
+        return seqk<<10;
 }
 
-*/
+HashTree::~HashTree () {
+    if (hashes_)
+        memory_unmap(hash_fd_, hashes_, sizek_*2*sizeof(Sha1Hash));
+    if (fd_)
+        close(fd_);
+}
\ No newline at end of file
index fe6aac6..b24c75c 100644 (file)
@@ -8,19 +8,21 @@
  */
 #ifndef P2TP_SHA1_HASH_TREE_H
 #define P2TP_SHA1_HASH_TREE_H
-#include "bin.h"
+#include "bin64.h"
+#include "bins.h"
 #include <string.h>
 #include <string>
-#include <vector>
+
+namespace p2tp {
+
 
 struct Sha1Hash {
        uint8_t bits[20];
 
        Sha1Hash() { memset(bits,0,20); }
        Sha1Hash(const Sha1Hash& left, const Sha1Hash& right);
-       Sha1Hash(const uint8_t* bits, size_t length);
-       /***/
-       Sha1Hash(const char* bits);
+       Sha1Hash(const char* str, size_t length=-1);
+       Sha1Hash(const uint8_t* data, size_t length);
        Sha1Hash(bool hex, const char* hash);
        
        std::string     hex() const;
@@ -33,42 +35,72 @@ struct Sha1Hash {
        const static size_t SIZE;
 };
 
-/*
-typedef std::pair<bin,Sha1Hash> binhash;
 
-struct HashTree {
-       Sha1Hash        root;
-       int                     fd;
-       bin                     mass;
-       uint32_t        length;
-       std::vector<bool> status;
-       std::vector<Sha1Hash> bits;
-       std::vector<binhash> peaks;
-       typedef enum { ACCEPT, DUNNO, PEAK_ACCEPT, REJECT } hashres_t;
+class HashTree {
+
+    /** Merkle hash tree: root */
+       Sha1Hash        root_hash_;
+    Sha1Hash        *hashes_;
+    /** Merkle hash tree: peak hashes */
+    Sha1Hash        peak_hashes_[64];
+    bin64_t         peaks_[64];
+    int             peak_count_;
+    /** File descriptor to put hashes to */
+       int             fd_;
+    int             hash_fd_;
+    /** Whether to re-hash files. */
+    bool            data_recheck_;
+    /** Base size, as derived from the hashes. */
+    size_t          size_;
+    size_t          sizek_;
+    /**        Part of the tree currently checked. */
+    size_t          complete_;
+    size_t          completek_;
+    bins            ack_out_;
+    
 protected:
-       Sha1Hash                deriveRoot();
-       hashres_t               offerPeak (bin pos, Sha1Hash hash);
+    
+    void            Submit();
+    void            RecoverProgress();
+    Sha1Hash        DeriveRoot();
+    bool            OfferPeakHash (bin64_t pos, const Sha1Hash& hash);
+    
 public:
        
-       HashTree (int fd);
-       HashTree (const Sha1Hash& root);
+       HashTree (const char* file_name, const Sha1Hash& root=Sha1Hash::ZERO, 
+              const char* hash_filename=NULL);
+    
+    /** Offer a hash; returns true if it verified; false otherwise.
+     Once it cannot be verified (no sibling or parent), the hash
+     is remembered, while returning false. */
+    bool            OfferHash (bin64_t pos, const Sha1Hash& hash);
+    /** Offer data; the behavior is the same as with a hash:
+     accept or remember or drop. Returns true => ACK is sent. */
+    bool            OfferData (bin64_t bin, const char* data, size_t length);
+    /** Not implemented yet. */
+    int             AppendData (char* data, int length) ;
+    
+    int             file_descriptor () const { return fd_; }
+    int             peak_count () const { return peak_count_; }
+    bin64_t         peak (int i) const { return peaks_[i]; }
+    const Sha1Hash& peak_hash (int i) const { return peak_hashes_[i]; }
+    bin64_t         peak_for (bin64_t pos) const;
+    const Sha1Hash& hash (bin64_t pos) const {return hashes_[pos];}
+    const Sha1Hash& root_hash () const { return root_hash_; }
+    uint64_t        size () const { return size_; }
+    uint64_t        size_kilo () const { return sizek_; }
+    uint64_t        complete () const { return complete_; }
+    uint64_t        complete_kilo () const { return completek_; }
+    uint64_t        seq_complete () ;
+    bool            is_complete () 
+        { return size_ && seq_complete()==size_; }
+    bins&           ack_out () { return ack_out_; }
        
        ~HashTree ();
 
-       hashres_t               offer (bin pos, const Sha1Hash& hash);
-       
-       bool                    rooted () const { return length>0; }
-       
-       const Sha1Hash& operator [] (bin i) { 
-               return i<=mass ? bits[i] : Sha1Hash::ZERO; 
-       }
        
-       uint32_t                data_size () const { return length; }
-       
-       bin                             data_mass () const { return mass; }
-       
-       const std::vector<binhash>& peak_hashes() const { return peaks; }
-       
-};*/
+};
+
+}
 
 #endif
index fea73a6..70a9b21 100644 (file)
--- a/p2tp.cpp
+++ b/p2tp.cpp
@@ -34,8 +34,8 @@ tbqueue Channel::send_queue;
 #include "ext/simple_selector.cpp"
 PeerSelector* Channel::peer_selector = new SimpleSelector();
 
-Channel::Channel       (FileTransfer* file, int socket, Address peer_addr) :
-       file_(file), peer_(peer_addr), peer_channel_id_(0), pex_out_(0),
+Channel::Channel       (FileTransfer* transfer, int socket, Address peer_addr) :
+       transfer_(transfer), peer_(peer_addr), peer_channel_id_(0), pex_out_(0),
     socket_(socket==-1?sockets[0]:socket), // FIXME
     own_id_mentioned_(false), next_send_time_(0), last_send_time_(0),
     last_recv_time_(0), rtt_avg_(TINT_SEC), dev_avg_(0), dip_avg_(TINT_SEC)
@@ -91,7 +91,7 @@ void    p2tp::Loop (tint till) {
 
 int      p2tp::Open (const char* filename, const Sha1Hash& hash) {
     FileTransfer* ft = new FileTransfer(filename, hash);
-    int fdes = ft->file_descriptor();
+    int fdes = ft->file().file_descriptor();
     if (fdes>0) {
         
         /*if (FileTransfer::files.size()<fdes)  // FIXME duplication
@@ -124,7 +124,7 @@ void    p2tp::AddPeer (Address address, const Sha1Hash& root) {
 
 size_t  p2tp::Size (int fdes) {
     if (FileTransfer::files.size()>fdes && FileTransfer::files[fdes])
-        return FileTransfer::files[fdes]->size();
+        return FileTransfer::files[fdes]->file().size();
     else
         return 0;
 }
@@ -132,7 +132,7 @@ size_t  p2tp::Size (int fdes) {
 
 size_t  p2tp::Complete (int fdes) {
     if (FileTransfer::files.size()>fdes && FileTransfer::files[fdes])
-        return FileTransfer::files[fdes]->is_complete();
+        return FileTransfer::files[fdes]->file().is_complete();
     else
         return 0;
 }
@@ -140,7 +140,7 @@ size_t  p2tp::Complete (int fdes) {
 
 size_t  p2tp::SeqComplete (int fdes) {
     if (FileTransfer::files.size()>fdes && FileTransfer::files[fdes])
-        return FileTransfer::files[fdes]->seq_complete();
+        return FileTransfer::files[fdes]->file().seq_complete();
     else
         return 0;
 }
@@ -150,7 +150,7 @@ const Sha1Hash& p2tp::RootMerkleHash (int file) {
     FileTransfer* trans = FileTransfer::file(file);
     if (!trans)
         return Sha1Hash::ZERO;
-    return trans->root_hash();
+    return trans->file().root_hash();
 }
 
 
diff --git a/p2tp.h b/p2tp.h
index 589a7e5..5e8b5c7 100644 (file)
--- a/p2tp.h
+++ b/p2tp.h
@@ -105,13 +105,6 @@ namespace p2tp {
                /**     Close everything. */
                ~FileTransfer();
 
-        /** Offer a hash; returns true if it verified; false otherwise.
-         Once it cannot be verified (no sibling or parent), the hash
-         is remembered, while returning false. */
-               void            OfferHash (bin64_t pos, const Sha1Hash& hash);
-        /** Offer data; the behavior is the same as with a hash:
-            accept or remember or drop. Returns true => ACK is sent. */
-        bool            OfferData (bin64_t bin, const uint8_t* data, size_t length);
 
         /** While we need to feed ACKs to every peer, we try (1) avoid
             unnecessary duplication and (2) keep minimum state. Thus,
@@ -125,83 +118,33 @@ namespace p2tp {
             return fd<files.size() ? files[fd] : NULL;
         }
 
-        int             peak_count () const { return peak_count_; }
-        bin64_t         peak (int i) const { return peaks_[i]; }
-        const Sha1Hash& peak_hash (int i) const { return peak_hashes_[i]; }
-        bin64_t         peak_for (bin64_t pos) const;
-        const Sha1Hash& hash (bin64_t pos) const {
-            assert(pos<sizek_*2);
-            return hashes_[pos];
-        }
-        const Sha1Hash& root_hash () const { return root_hash_; }
-        uint64_t        size () const { return size_; }
-        uint64_t        size_kilo () const { return sizek_; }
-        uint64_t        complete () const { return complete_; }
-        uint64_t        complete_kilo () const { return completek_; }
-        uint64_t        seq_complete () const { return seq_complete_; }
-        bool            is_complete () const
-            { return size_ && seq_complete_==size_; }
-        bins&           ack_out ()  { return ack_out_; }
-        int             file_descriptor () const { return fd_; }
+        bins&           ack_out ()  { return file_.ack_out(); }
         PiecePicker&    picker () { return *picker_; }
         int             channel_count () const { return hs_in_.size(); }
+        HashTree&       file() { return file_; }
+        int             fd () const { return file_.file_descriptor(); }
+        const Sha1Hash& root_hash () const { return file_.root_hash(); }
 
         static int instance; // FIXME this smells
 
     private:
 
                static std::vector<FileTransfer*> files;
-               static std::string GetTempFilename(Sha1Hash& root_hash, int instance, std::string postfix);
-
-               /**     file descriptor. */
-               int                             fd_;
-        /** File size, as derived from the hashes. */
-        size_t          size_;
-        size_t          sizek_;
-               /**     Part the file currently downloaded. */
-               size_t          complete_;
-               size_t          completek_;
-               size_t          seq_complete_;
-               /**     A binmap for all packets obtained and succesfully checked. */
-               bins                    ack_out_;
-               /**     History of bin retrieval. */
-               //binqueue              data_in_;
-        //int             data_in_off_;
+
+        HashTree        file_;
+        
         /** Piece picker strategy. */
         PiecePicker*    picker_;
-               /** File for keeping the Merkle hash tree. */
-        int             hashfd_;
-#ifdef _WIN32
-        HANDLE                 hashmaphandle_;
-#endif
-        /** Merkle hash tree: root */
-        Sha1Hash        root_hash_;
-        /** Merkle hash tree: peak hashes */
-        Sha1Hash        peak_hashes_[64];
-        bin64_t         peaks_[64];
-        int             peak_count_;
-        /** Merkle hash tree: the tree, as a bin64_t-indexed array */
-        Sha1Hash*       hashes_;
-        /** for recovering saved state */
-        bool            dry_run_;
-        /** Error encountered */
-        char*           error_;
-
+        
         /** Channels working for this transfer. */
         binqueue        hs_in_;
         int             hs_in_offset_;
         std::deque<Address>        pex_in_;
+        
         /** Messages we are accepting.    */
         uint64_t        cap_out_;
 
     protected:
-        void            SetSize(size_t bytes);
-        void            Submit();
-        void            RecoverProgress();
-        void            OfferPeak (bin64_t pos, const Sha1Hash& hash);
-        Sha1Hash        DeriveRoot();
-        void            SavePeaks();
-        void            LoadPeaks();
         void            OnDataIn (bin64_t pos);
         void            OnPexIn (const Address& addr);
 
@@ -277,7 +220,8 @@ namespace p2tp {
         const std::string id_string () const;
         /** A channel is "established" if had already sent and received packets. */
         bool        is_established () { return peer_channel_id_ && own_id_mentioned_; }
-        FileTransfer& file() { return *file_; }
+        FileTransfer& transfer() { return *transfer_; }
+        HashTree&   file () { return transfer_->file(); }
         const Address& peer() const { return peer_; }
         
                static int DecodeID(int scrambled);
@@ -294,7 +238,7 @@ namespace p2tp {
                /**     The UDP socket fd. */
                int                     socket_;
                /**     Descriptor of the file in question. */
-               FileTransfer*   file_;
+               FileTransfer*   transfer_;
                /**     Peer channel id; zero if we are trying to open a channel. */
                uint32_t        peer_channel_id_;
         bool        own_id_mentioned_;
index a01d853..a3ca654 100644 (file)
@@ -173,10 +173,10 @@ void      Channel::AddHint (Datagram& dgram) {
     if ( 4*peer_cwnd > hinted ) { //hinted*1024 < peer_cwnd*4 ) {
         
         uint8_t layer = 2; // actually, enough
-        bin64_t hint = file().picker().Pick(ack_in_,layer);
+        bin64_t hint = transfer().picker().Pick(ack_in_,layer);
         // FIXME FIXME FIXME: any layer
         if (hint==bin64_t::NONE)
-            hint = file().picker().Pick(ack_in_,0);
+            hint = transfer().picker().Pick(ack_in_,0);
         
         if (hint!=bin64_t::NONE) {
             hint_out_.push_back(hint);
@@ -256,7 +256,7 @@ void        Channel::Recv (Datagram& dgram) {
         rtt_avg_ = NOW - last_send_time_;
         dev_avg_ = rtt_avg_;
         dip_avg_ = rtt_avg_;
-        file_->hs_in_.push_back(id);
+        transfer().hs_in_.push_back(id);
         dprintf("%s #%i rtt init %lli\n",tintstr(),id,rtt_avg_);
     }
     bin64_t data = dgram.size() ? bin64_t::NONE : bin64_t::ALL;
@@ -295,7 +295,7 @@ bin64_t Channel::OnData (Datagram& dgram) {
        bin64_t pos = dgram.Pull32();
     uint8_t *data;
     int length = dgram.Pull(&data,1024);
-    bool ok = file().OfferData(pos, data, length) ;
+    bool ok = file().OfferData(pos, (char*)data, length) ;
     dprintf("%s #%i %cdata (%lli)\n",tintstr(),id,ok?'-':'!',pos.offset());
     if (ok) {
         data_in_ = tintbin(NOW,pos);
@@ -365,12 +365,12 @@ void Channel::OnPex (Datagram& dgram) {
     uint16_t port = dgram.Pull16();
     Address addr(ipv4,port);
     dprintf("%s #%i -pex %s\n",tintstr(),id,addr.str().c_str());
-    file_->OnPexIn(addr);
+    transfer().OnPexIn(addr);
 }
 
 
 void    Channel::AddPex (Datagram& dgram) {
-    int chid = file_->RevealChannel(pex_out_);
+    int chid = transfer().RevealChannel(pex_out_);
     if (chid==-1 || chid==id)
         return;
     Address a = channels[chid]->peer();
@@ -395,7 +395,7 @@ void        Channel::Recv (int socket) {
                uint8_t hashid = data.Pull8();
                if (hashid!=P2TP_HASH) 
                        RETLOG ("no hash in the initial handshake");
-               bin pos = data.Pull32();
+               bin64_t pos = data.Pull32();
                if (pos!=bin64_t::ALL32) 
                        RETLOG ("that is not the root hash");
                hash = data.PullHash();
index cb3950c..99b7078 100755 (executable)
@@ -303,6 +303,15 @@ TEST(BinsTest,Twist) {
     EXPECT_EQ(bins::EMPTY,b.get(bin64_t(3,3)));
 }
 
+TEST(BinsTest,SeqLength) {
+    bins b;
+    b.set(bin64_t(3,0));
+    b.set(bin64_t(1,4));
+    b.set(bin64_t(0,10));
+    b.set(bin64_t(3,2));
+    EXPECT_EQ(11,b.seq_length());
+}
+
 TEST(BinheapTest,Eat) {
     
     binheap b;
index a0d16ae..d0812e6 100644 (file)
@@ -7,29 +7,48 @@
  *
  */
 #include <fcntl.h>
-#include "bin.h"
+#include "bin64.h"
 #include <gtest/gtest.h>
 #include "hashtree.h"
 
+using namespace p2tp;
+
 char hash123[] = "a8fdc205a9f19cc1c7507a60c4f01b13d11d7fd0";
-//char roothash123[] = "84e1e5f4b549fe072d803709eeb06143cd2ad736";
+char rooth123[] = "d0bdb8ba28076d84d2b3a0e62521b998e42349a1";
 
 TEST(Sha1HashTest,Trivial) {
        Sha1Hash hash("123\n");
        EXPECT_STREQ(hash123,hash.hex().c_str());
 }
 
-/*
-TEST(Sha1HashTest,HashTreeTest) {
-       Sha1Hash roothash123(hash123);
-       for(bin pos=1; pos<bin::ALL; pos=pos.parent())
+
+TEST(Sha1HashTest,OfferDataTest) {
+       Sha1Hash roothash123(true,hash123);
+       for(bin64_t pos(0,0); pos!=bin64_t::ALL; pos=pos.parent())
                roothash123 = Sha1Hash(roothash123,Sha1Hash::ZERO);
-       HashTree tree = HashTree(roothash123);
-       ASSERT_EQ(HashTree::PEAK_ACCEPT, tree.offer(bin(1),hash123));
-       ASSERT_TRUE(tree.rooted());
+    unlink("123");
+    EXPECT_STREQ(rooth123,roothash123.hex().c_str());
+       HashTree tree("123",roothash123);
+    tree.OfferHash(bin64_t(0,0),Sha1Hash(true,hash123));
+       ASSERT_EQ(1,tree.size_kilo());
+    ASSERT_TRUE(tree.OfferData(bin64_t(0,0), "123\n", 4));
+    unlink("123");
+       ASSERT_EQ(4,tree.size());
+}
+
+
+TEST(Sha1HashTest,SubmitTest) {
+    FILE* f123 = fopen("123","w+");
+    fprintf(f123, "123\n");
+    fclose(f123);
+    HashTree ht123("123");
+    EXPECT_STREQ(hash123,ht123.hash(bin64_t(0,0)).hex().c_str());
+    EXPECT_STREQ(rooth123,ht123.root_hash().hex().c_str());
+    EXPECT_EQ(4,ht123.size());
 }
 
-TEST(Sha1HashTest,HashFileTest) {
+
+/*TEST(Sha1HashTest,HashFileTest) {
        uint8_t a [1024], b[1024], c[1024];
        memset(a,'a',1024);
        memset(b,'b',1024);
@@ -59,8 +78,9 @@ TEST(Sha1HashTest,HashFileTest) {
        EXPECT_TRUE ( bootstree.bits[1]==aaahash );
        EXPECT_TRUE ( bootstree.bits[2]==bbbhash );
        EXPECT_FALSE ( bootstree.bits[2]==aaahash );
-}
-*/
+}*/
+
+
 int main (int argc, char** argv) {
        //bin::init();
        
index 432f990..1c74cc4 100644 (file)
@@ -9,14 +9,11 @@
 //#include <gtest/gtest.h>
 //#include <glog/logging.h>
 #include "p2tp.h"
-#include "compat/util.h"
-#ifdef _MSC_VER
-#include "compat/unixio.h"
-#endif
+#include "compat.h"
 
 using namespace p2tp;
 
-const char* BTF = "big_test_file";
+const char* BTF = "test_file";
 
 Sha1Hash A,B,C,D,E,AB,CD,ABCD,E0,E000,ABCDE000,ROOT;
 
@@ -34,9 +31,11 @@ TEST(TransferTest,TransferFile) {
         ROOT = Sha1Hash(ROOT,Sha1Hash::ZERO);
         //printf("m %lli %s\n",(uint64_t)pos.parent(),ROOT.hex().c_str());
     }
-
-    // submit a new file
-    FileTransfer* seed = new FileTransfer(BTF);
+    
+    // now, submit a new file
+    
+    FileTransfer* seed_transfer = new FileTransfer(BTF);
+    HashTree* seed = & seed_transfer->file();
     EXPECT_TRUE(A==seed->hash(0));
     EXPECT_TRUE(E==seed->hash(bin64_t(0,4)));
     EXPECT_TRUE(ABCD==seed->hash(bin64_t(2,0)));
@@ -52,8 +51,9 @@ TEST(TransferTest,TransferFile) {
     // retrieve it
     unlink("copy");
     FileTransfer::instance = 1;
-    FileTransfer* leech = new FileTransfer("copy",seed->root_hash());
-    leech->picker().Randomize(0);
+    FileTransfer* leech_transfer = new FileTransfer("copy",seed->root_hash());
+    HashTree* leech = & leech_transfer->file();
+    leech_transfer->picker().Randomize(0);
     // transfer peak hashes
     for(int i=0; i<seed->peak_count(); i++)
         leech->OfferHash(seed->peak(i),seed->peak_hash(i));
@@ -68,13 +68,14 @@ TEST(TransferTest,TransferFile) {
     leech->OfferHash(bin64_t(1,1), seed->hash(bin64_t(1,1)));
     for (int i=0; i<5; i++) {
         if (i==2) { // now: stop, save, start
-            delete leech;
+            delete leech_transfer;
             FileTransfer::instance = 1;
-            leech = new FileTransfer("copy",seed->root_hash());
-            leech->picker().Randomize(0);
+            leech_transfer = new FileTransfer("copy",seed->root_hash());
+            leech = & leech_transfer->file();
+            leech_transfer->picker().Randomize(0);
             EXPECT_EQ(2,leech->complete_kilo());
         }
-        bin64_t next = leech->picker().Pick(seed->ack_out(),0);
+        bin64_t next = leech_transfer->picker().Pick(seed->ack_out(),0);
         ASSERT_NE(bin64_t::NONE,next);
         ASSERT_TRUE(next.base_offset()<5);
         uint8_t buf[1024];         //size_t len = seed->storer->ReadData(next,&buf);
@@ -84,9 +85,9 @@ TEST(TransferTest,TransferFile) {
             leech->OfferHash(sibling, seed->hash(sibling));
         uint8_t memo = *buf;
         *buf = 'z';
-        EXPECT_FALSE(leech->OfferData(next, buf, len));
+        EXPECT_FALSE(leech->OfferData(next, (char*)buf, len));
         *buf = memo;
-        EXPECT_TRUE(leech->OfferData(next, buf, len));
+        EXPECT_TRUE(leech->OfferData(next, (char*)buf, len));
     }
     EXPECT_EQ(4100,leech->size());
     EXPECT_EQ(5,leech->size_kilo());
@@ -101,16 +102,10 @@ TEST(TransferTest,TransferFile) {
 
 int main (int argc, char** argv) {
 
-       std::string tempdir = gettmpdir();
-    unlink((tempdir + std::string(".70196e6065a42835b1f08227ac3e2fb419cf78c8.0.hashes")).c_str());
-    unlink((tempdir + std::string(".70196e6065a42835b1f08227ac3e2fb419cf78c8.0.peaks")).c_str());
-    unlink((tempdir + std::string(".70196e6065a42835b1f08227ac3e2fb419cf78c8.1.hashes")).c_str());
-    unlink((tempdir + std::string(".70196e6065a42835b1f08227ac3e2fb419cf78c8.1.peaks")).c_str());
-    unlink((tempdir + std::string(".70196e6065a42835b1f08227ac3e2fb419cf78c8.2.hashes")).c_str());
-    unlink((tempdir + std::string(".70196e6065a42835b1f08227ac3e2fb419cf78c8.2.peaks")).c_str());
-
-    unlink(BTF);
+    unlink("test_file");
     unlink("copy");
+    unlink("test_file.mhash");
+    unlink("copy.mhash");
 
        int f = open(BTF,O_RDWR|O_CREAT|O_TRUNC,S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH);
        if (f < 0)
index 4ae31b8..c26cef6 100644 (file)
@@ -29,306 +29,22 @@ int FileTransfer::instance = 0;
 // FIXME: separate Bootstrap() and Download(), then Size(), Progress(), SeqProgress()
 
 FileTransfer::FileTransfer (const char* filename, const Sha1Hash& _root_hash) :
-    root_hash_(_root_hash), fd_(0), hashfd_(0), dry_run_(false),
-    peak_count_(0), hashes_(NULL), error_(NULL), size_(0), sizek_(0),
-    complete_(0), completek_(0), seq_complete_(0), hs_in_offset_(0)
+    file_(filename,_root_hash), hs_in_offset_(0)
 {
-       fd_ = open(filename,O_RDWR|O_CREAT,S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH);
-       if (fd_<0)
-        return;
-    if (files.size()<fd_+1)
-        files.resize(fd_+1);
-    files[fd_] = this;
-    if (root_hash_==Sha1Hash::ZERO) // fresh submit, hash it
-        Submit();
-    else
-        RecoverProgress();
+    if (files.size()<fd()+1)
+        files.resize(fd()+1);
+    files[fd()] = this;
     picker_ = new SeqPiecePicker(this);
+    if (file_.size())
+        picker_->Randomize(rand()&31&(file_.peak(0).width()-1));
 }
 
 
-void FileTransfer::LoadPeaks () {
-    std::string file_name = GetTempFilename(root_hash_,instance,std::string(".peaks"));
-    int peakfd = open(file_name.c_str(),O_RDONLY,0);
-    if (peakfd<0)
-        return;
-    bin64_t peak;
-    char hash[128];
-    while (sizeof(bin64_t)==read(peakfd,&peak,sizeof(bin64_t))) {
-        read(peakfd,hash,Sha1Hash::SIZE);
-        OfferPeak(peak, Sha1Hash(false,hash));
-    }
-    close(peakfd);
-}
-
-
-/** Basically, simulated receiving every single packet, except
-    for some optimizations. */
-void            FileTransfer::RecoverProgress () {
-    dry_run_ = true;
-    LoadPeaks();
-    if (!size())
-        return;
-    // at this point, we may use mmapd hashes already
-    // so, lets verify hashes and the data we've got
-    lseek(fd_,0,SEEK_SET);
-    for(int p=0; p<size_kilo(); p++) {
-        uint8_t buf[1<<10];
-        size_t rd = read(fd_,buf,1<<10);
-        OfferData(bin64_t(0,p), buf, rd);
-        if (rd<(1<<10))
-            break;
-    }
-    dry_run_ = false;
-}
-
-
-void    FileTransfer::SavePeaks () {
-    std::string file_name = GetTempFilename(root_hash_,instance,std::string(".peaks"));
-    int peakfd = open(file_name.c_str(),O_RDWR|O_CREAT,S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH);
-    for(int i=0; i<peak_count(); i++) {
-        write(peakfd,&(peaks_[i]),sizeof(bin64_t));
-        write(peakfd,*peak_hashes_[i],Sha1Hash::SIZE);
-    }
-    close(peakfd);
-}
-
-
-void FileTransfer::SetSize (size_t bytes) { // peaks/root must be already set
-    size_ = bytes;
-    completek_ = complete_ = seq_complete_ = 0;
-       sizek_ = (size_>>10) + ((size_&1023) ? 1 : 0);
-
-       struct stat st;
-       fstat(fd_, &st);
-    if (st.st_size!=bytes)
-        if (ftruncate(fd_, bytes))
-            return; // remain in the 0-state
-    // mmap the hash file into memory
-    std::string file_name = GetTempFilename(root_hash_,instance,std::string(".hashes"));
-       hashfd_ = open(file_name.c_str(),O_RDWR|O_CREAT,S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH);
-    size_t expected_size_ = Sha1Hash::SIZE * sizek_ * 2;
-       struct stat hash_file_st;
-       fstat(hashfd_, &hash_file_st);
-    if ( hash_file_st.st_size != expected_size_ )
-        ftruncate(hashfd_, expected_size_);
-#ifdef _WIN32
-    HANDLE hashhandle = (HANDLE)_get_osfhandle(hashfd_);
-    hashmaphandle_ = CreateFileMapping(hashhandle,
-                                             NULL,
-                                             PAGE_READWRITE,
-                                             0,
-                                             0,
-                                             NULL);
-       if (hashmaphandle_ != NULL)
-       {
-               hashes_ = (Sha1Hash*)MapViewOfFile(hashmaphandle_,
-                                                        FILE_MAP_WRITE,
-                                                    0,
-                                                    0,
-                                                    0);
-
-       }
-       if (hashmaphandle_ == NULL || hashes_ == NULL)
-#else
-    hashes_ = (Sha1Hash*) mmap (NULL, expected_size_, PROT_READ|PROT_WRITE,
-                               MAP_SHARED, hashfd_, 0);
-    if (hashes_==MAP_FAILED)
-#endif
-    {
-        hashes_ = NULL;
-        size_ = sizek_ = complete_ = completek_ = seq_complete_ = 0;
-        error_ = strerror(errno); // FIXME dprintf()
-        perror("hash tree mmap failed");
-        return;
-    }
-    for(int i=0; i<peak_count_; i++)
-        hashes_[peaks_[i]] = peak_hashes_[i];
-    picker_->Randomize(rand()&31&(sizek_-1));
-}
-
-
-void            FileTransfer::Submit () {
-       struct stat st; // TODO:   AppendData()   and   streaming
-       fstat(fd_, &st);
-    size_ = st.st_size;
-       sizek_ = (size_>>10) + ((size_&1023) ? 1 : 0);
-    hashes_ = (Sha1Hash*) malloc(Sha1Hash::SIZE*sizek_*2);
-    peak_count_ = bin64_t::peaks(sizek_,peaks_);
-    for (int p=0; p<peak_count_; p++) {
-        for(bin64_t b=peaks_[p].left_foot(); b.within(peaks_[p]); b=b.next_dfsio(0))
-            if (b.is_base()) {
-                uint8_t kilo[1<<10];
-                size_t rd = pread(fd_,kilo,1<<10,b.base_offset()<<10);
-                hashes_[b] = Sha1Hash(kilo,rd);
-            } else
-                hashes_[b] = Sha1Hash(hashes_[b.left()],hashes_[b.right()]);
-        peak_hashes_[p] = hashes_[peaks_[p]];
-        //ack_out_.set(peaks_[p],bins::FILLED);
-        OnDataIn(peaks_[p]);
-    }
-    root_hash_ = DeriveRoot();
-    Sha1Hash *hash_tmp = hashes_;
-    SetSize(st.st_size);
-    SavePeaks();
-    seq_complete_ = complete_ = size_;
-    completek_ = sizek_;
-    memcpy(hashes_,hash_tmp,sizek_*Sha1Hash::SIZE*2);
-    free(hash_tmp);
-}
-
-
-bin64_t         FileTransfer::peak_for (bin64_t pos) const {
-    int pi=0;
-    while (pi<peak_count_ && !pos.within(peaks_[pi]))
-        pi++;
-    return pi==peak_count_ ? bin64_t(bin64_t::NONE) : peaks_[pi];
-}
-
-
-void            FileTransfer::OfferHash (bin64_t pos, const Sha1Hash& hash) {
-       if (!size_)  // only peak hashes are accepted at this point
-               return OfferPeak(pos,hash);
-    int pi=0;
-    while (pi<peak_count_ && !pos.within(peaks_[pi]))
-        pi++;
-    if (pi==peak_count_)
-        return;
-    if (pos==peaks_[pi] && hash!=peak_hashes_[pi])
-        return;
-    else if (ack_out_.get(pos.parent())!=bins::EMPTY)
-        return; // have this hash already, even accptd data
-       hashes_[pos] = hash;
-}
-
-
-bool            FileTransfer::OfferData (bin64_t pos, const uint8_t* data, size_t length) {
-    if (!pos.is_base())
-        return false;
-    if (length<1024 && pos!=bin64_t(0,sizek_-1))
-        return false;
-    if (ack_out_.get(pos)==bins::FILLED)
-        return true; // ???
-    bin64_t peak = peak_for(pos);
-    if (peak==bin64_t::NONE)
-        return false;
-
-    Sha1Hash hash(data,length);
-    bin64_t p = pos;
-    while ( p!=peak && ack_out_.get(p)==bins::EMPTY ) {
-        hashes_[p] = hash;
-        p = p.parent();
-        hash = Sha1Hash(hashes_[p.left()],hashes_[p.right()]) ;
-    }
-    if (hash!=hashes_[p])
-        return false;
-
-    //printf("g %lli %s\n",(uint64_t)pos,hash.hex().c_str());
-       // walk to the nearest proven hash   FIXME 0-layer peak
-    OnDataIn(pos);
-    pwrite(fd_,data,length,pos.base_offset()<<10);
-    complete_ += length;
-    completek_++;
-    if (length<1024) {
-        size_ -= 1024 - length;
-        ftruncate(fd_, size_);
-    }
-    while (ack_out_.get(bin64_t(0,seq_complete_>>10))==bins::FILLED)
-        seq_complete_+=1024;
-    if (seq_complete_>size_)
-        seq_complete_ = size_;
-    return true;
-}
-
-
-/*bin64_t         FileTransfer::RevealAck (uint64_t& offset) {
-    if (offset<data_in_off_)
-        offset = data_in_off_;
-    for(int off=offset-data_in_off_; off<data_in_.size(); off++) {
-        offset++;
-        if (data_in_[off]!=bin64_t::NONE) {
-            bin64_t parent = data_in_[off].parent();
-            if (ack_out_.get(parent)!=bins::FILLED)
-                return data_in_[off];
-            else
-                data_in_[off] = bin64_t::NONE;
-        }
-    }
-    return bin64_t::NONE;
-}*/
-
-
-void            FileTransfer::OnDataIn (bin64_t pos) {
-    ack_out_.set(pos,bins::FILLED);
-    /*bin64_t closed = pos;
-    while (ack_out_.get(closed.parent())==bins::FILLED) // TODO optimize
-        closed = closed.parent();
-    data_in_.push_back(closed);
-    // rotating the queue
-    bin64_t parent = data_in_.front().parent();
-    if (ack_out_.get(parent)!=bins::FILLED)
-        data_in_.push_back(data_in_.front());
-    data_in_.front() = bin64_t::NONE;
-    while ( !data_in_.empty() && data_in_.front()==bin64_t::NONE) {
-        data_in_.pop_front();
-        data_in_off_++;
-    }*/
-}
-
-
-Sha1Hash        FileTransfer::DeriveRoot () {
-       int c = peak_count_-1;
-       bin64_t p = peaks_[c];
-       Sha1Hash hash = peak_hashes_[c];
-       c--;
-       while (p!=bin64_t::ALL) {
-               if (p.is_left()) {
-                       p = p.parent();
-                       hash = Sha1Hash(hash,Sha1Hash::ZERO);
-               } else {
-                       if (c<0 || peaks_[c]!=p.sibling())
-                               return Sha1Hash::ZERO;
-                       hash = Sha1Hash(peak_hashes_[c],hash);
-                       p = p.parent();
-                       c--;
-               }
-        //printf("p %lli %s\n",(uint64_t)p,hash.hex().c_str());
-       }
-    return hash;
-}
-
-
-void            FileTransfer::OfferPeak (bin64_t pos, const Sha1Hash& hash) {
-    assert(!size_);
-    if (peak_count_) {
-        bin64_t last_peak = peaks_[peak_count_-1];
-        if ( pos.layer()>=last_peak.layer() ||
-             pos.base_offset()!=last_peak.base_offset()+last_peak.width() )
-            peak_count_ = 0;
-    }
-    peaks_[peak_count_] = pos;
-    peak_hashes_[peak_count_++] = hash;
-    // check whether peak hash candidates add up to the root hash
-    Sha1Hash mustbe_root = DeriveRoot();
-    if (mustbe_root!=root_hash_)
-        return;
-    // bingo, we now know the file size (rounded up to a KByte)
-    SetSize( (pos.base_offset()+pos.width()) << 10              );
-    SavePeaks();
-}
-
 
 FileTransfer::~FileTransfer ()
 {
-#ifdef _WIN32
-       UnmapViewOfFile(hashes_);
-       CloseHandle(hashmaphandle_);
-#else
-    munmap(hashes_,sizek_*2*Sha1Hash::SIZE);
-    close(hashfd_);
-    close(fd_);
-    files[fd_] = NULL;
-#endif
+
+    files[fd()] = NULL;
 }
 
 
@@ -340,35 +56,10 @@ FileTransfer* FileTransfer::Find (const Sha1Hash& root_hash) {
 }
 
 
-std::string FileTransfer::GetTempFilename(Sha1Hash& root_hash, int instance, std::string postfix)
-{
-       std::string tempfile = gettmpdir();
-       std::stringstream ss;
-       ss << instance;
-       tempfile += std::string(".") + root_hash.hex() + std::string(".") + ss.str() + postfix;
-       return tempfile;
-}
-
-
-/*int      p2tp::Open (const char* filename, const Sha1Hash& hash) {
-    FileTransfer* ft = new FileTransfer(filename, hash);
-    int fdes = ft->file_descriptor();
-    if (fdes>0) {
-        if (FileTransfer::files.size()<fdes)
-            FileTransfer::files.resize(fdes);
-        FileTransfer::files[fdes] = ft;
-        return fdes;
-    } else {
-        delete ft;
-        return -1;
-    }
-}*/
-
-
 void            FileTransfer::OnPexIn (const Address& addr) {
     for(int i=0; i<hs_in_.size(); i++) {
         Channel* c = Channel::channels[hs_in_[i]];
-        if (c && c->file_==this && c->peer_==addr)
+        if (c && c->transfer().fd()==this->fd() && c->peer_==addr)
             return; // already connected
     }
     if (hs_in_.size()<20) {
@@ -387,7 +78,7 @@ int        FileTransfer::RevealChannel (int& pex_out_) {
         pex_out_ = 0;
     while (pex_out_<hs_in_.size()) {
         Channel* c = Channel::channels[hs_in_[pex_out_]];
-        if (c && c->file_==this) {
+        if (c && c->transfer().fd()==this->fd()) {
             pex_out_ += hs_in_offset_ + 1;
             return c->id;
         } else {
@@ -400,65 +91,3 @@ int        FileTransfer::RevealChannel (int& pex_out_) {
     return -1;
 }
 
-
-/*
- for(int i=0; i<peak_hash_count; i++) {
- bin64_t x = peaks[i], end = x.sibling();
- do {
- while (!x.layer()>10) {
- OfferHash(x.right(), hashes[x.right()]);
- if ( ! OfferHash(x.left(), hashes[x.left()]) )
- break;
- x = x.left();
- }
-
- if (x.layer()==10) {
- if (recheck_data) {
- uint8_t data[1024];
- size_t rd = pread(fd,data,2<<10,x.base_offset());
- if (hashes[x]==Sha1Hash(data,rd))
- ack_out->set(x,bins::FILLED);
- // may avoid hashing by checking whether it is zero
- // and whether the hash matches hash of zero
- } else {
- ack_out->set(x,bins::FILLED);
- }
- }
-
- while (x.is_right() && x!=peaks[i])
- x = x.parent();
- x = x.sibling();
- } while (x!=end);
- }
-
-
-
-
- // open file
- if ( hash_file_st.st_size < (sizeof(bin64_t)+Sha1Hash::SIZE)*64 )
- return;
- // read root hash
- char hashbuf[128];
- uint64_t binbuf;
- lseek(hashfd_,0,SEEK_SET);
- read(hashfd_,&binbuf,sizeof(bin64_t));
- read(hashfd_,hashbuf,Sha1Hash::SIZE);
- Sha1Hash mustberoot(false,(const char*)hashbuf);
- if ( binbuf!=bin64_t::ALL || mustberoot != this->root_hash ) {
- ftruncate(hashfd_,Sha1Hash::SIZE*64);
- return;
- }
- // read peak hashes
- for(int i=1; i<64 && !this->size; i++){
- read(hashfd_,&binbuf,sizeof(bin64_t));
- read(hashfd_,hashbuf,Sha1Hash::SIZE);
- Sha1Hash mustbepeak(false,(const char*)hashbuf);
- if (mustbepeak==Sha1Hash::ZERO)
- break;
- OfferPeak(binbuf,mustbepeak);
- }
- if (!size)
- return;
-
-
- */