now it builds
authorvictor <victor@e16421f0-f15b-0410-abcd-98678b794739>
Sat, 10 Oct 2009 17:46:25 +0000 (17:46 +0000)
committervictor <victor@e16421f0-f15b-0410-abcd-98678b794739>
Sat, 10 Oct 2009 17:46:25 +0000 (17:46 +0000)
git-svn-id: https://ttuki.vtt.fi/svn/p2p-next/TUD/p2tp/trunk@396 e16421f0-f15b-0410-abcd-98678b794739

16 files changed:
bin64.cpp [new file with mode: 0644]
bin64.h
bins.cpp
bins.h
datagram.h
ext/ledbat_controller.cpp [moved from ledbat_controller.cpp with 100% similarity]
ext/mmap_storer.cpp [new file with mode: 0644]
ext/seq_picker.cpp [moved from seq_picker.cpp with 53% similarity]
ext/simple_selector.cpp [moved from simple_selector.cpp with 100% similarity]
hashtree.cpp
hashtree.h
p2tp.cpp
p2tp.h
sendrecv.cpp
tests/bin64test.cpp
transfer.cpp

diff --git a/bin64.cpp b/bin64.cpp
new file mode 100644 (file)
index 0000000..90524d5
--- /dev/null
+++ b/bin64.cpp
@@ -0,0 +1,46 @@
+/*
+ *  bin64.cpp
+ *  p2tp
+ *
+ *  Created by Victor Grishchenko on 10/10/09.
+ *  Copyright 2009 Delft Technical University. All rights reserved.
+ *
+ */
+#include "bin64.h"
+
+const uint64_t bin64_t::NONE = 0xffffffffffffffffULL;
+const uint64_t bin64_t::ALL = 0x7fffffffffffffffULL;
+
+bin64_t bin64_t::next_dfsio (uint8_t floor) {
+    /*while (ret.is_right())
+        ret = ret.parent();
+    ret = ret.sibling();
+    while (ret.layer()>floor)
+        ret = ret.left();*/
+    if (is_right()) {
+        return parent();
+    } else {
+        bin64_t ret = sibling();
+        while (ret.layer()>floor)
+            ret = ret.left();
+        return ret;
+    }
+}
+
+int bin64_t::peaks (uint64_t length, bin64_t* peaks) {
+    int pp=0;
+    uint8_t layer = 0;
+    while (length) {
+        if (length&1) 
+            peaks[pp++] = bin64_t(layer,length^1);
+        length>>=1;
+        layer++;
+    }
+    for(int i=0; i<(pp>>1); i++) {
+        uint64_t memo = peaks[pp-1-i];
+        peaks[pp-1-i] = peaks[i];
+        peaks[i] = memo;
+    }
+    peaks[pp] = NONE;
+    return pp;
+}
diff --git a/bin64.h b/bin64.h
index e619468..d867545 100644 (file)
--- a/bin64.h
+++ b/bin64.h
@@ -3,7 +3,7 @@
 #include <assert.h>
 #include <stdint.h>
 
-#include <stdio.h>
+//#include <stdio.h>
 
 /** Bin numbers in the tail111 encoding: meaningless
     bits in the tail are set to 0111...11, while the
@@ -11,8 +11,8 @@
     at layer 1, offset 3 (i.e. fourth). */
 struct bin64_t {
     uint64_t v;
-    static const uint64_t NONE = 0xffffffffffffffffULL;
-    static const uint64_t ALL = 0x7fffffffffffffffULL;
+    static const uint64_t NONE;
+    static const uint64_t ALL;
 
     bin64_t() : v(NONE) {}
     bin64_t(const bin64_t&b) : v(b.v) {}
@@ -107,21 +107,25 @@ struct bin64_t {
     }
     bool is_right() const { return !is_left(); }
 
+    bin64_t left_foot () const {
+        return bin64_t(0,base_offset());
+    }
+    
+    bool    is_base () const {
+        return !(v & 1);
+    }
+    
+    bin64_t next_dfsio (uint8_t floor);
+    
+    bin64_t width () const {
+        return (tail_bits()+1)>>1;
+    }
+    
     /** The array must have 64 cells, as it is the max
-     number of peaks possible (and there are no reason
+     number of peaks possible +1 (and there are no reason
      to assume there will be less in any given case. */
-    static void GetPeaks(uint64_t length, bin64_t* peaks) {
-        int pp=0;
-        uint8_t layer = 0;
-        while (length) {
-            if (length&1) 
-                peaks[pp++] = bin64_t(layer,length^1);
-            length>>=1;
-            layer++;
-        }
-        peaks[pp] = NONE;
-    }
-
+    static int peaks (uint64_t length, bin64_t* peaks) ;
+    
 };
 
 
index e6d580f..c1c7436 100644 (file)
--- a/bins.cpp
+++ b/bins.cpp
@@ -35,6 +35,13 @@ bins::bins() :  height(4), blocks_allocated(0), cells(NULL),
     assert(!alloc_cell());
 }
 
+bins::bins (const bins& b) : height(b.height), ap(b.ap),
+blocks_allocated(b.blocks_allocated), cells_allocated(b.cells_allocated) {
+    size_t memsz = blocks_allocated*16*32;
+    cells = (uint32_t*) malloc(memsz);
+    memcpy(cells,b.cells,memsz);
+}
+
 void bins::dump (const char* note) {
     printf("%s\t",note);
     for(int i=0; i<(blocks_allocated<<5); i++) {
@@ -212,12 +219,13 @@ void iterator::parent () {
 }
 
 
-bin64_t bins::find (const bin64_t range, const uint8_t layer) {
+bin64_t bins::find (const bin64_t range, const uint8_t layer, fill_t seek) {
     iterator i(this,range,true);
+    fill_t stop = seek==EMPTY ? FILLED : EMPTY;
     while (true) {
-        while ( i.bin().layer()>layer && (i.deep() || *i!=FILLED) )
+        while ( i.bin().layer()>layer && (i.deep() || *i!=stop) )
             i.left();
-        if (i.bin().layer()==layer && !i.deep() && *i==EMPTY)
+        if (i.bin().layer()==layer && !i.deep() && *i==seek)
             return i.bin();
         while (i.bin().is_right() && i.bin()!=range)
             i.parent();
@@ -288,3 +296,22 @@ uint64_t*   bins::get_stripes (int& count) {
     
     return stripes;
 }
+
+
+void    bins::remove (bins& b) {
+    uint8_t start_lr = b.height>height ? b.height : height;
+    bin64_t top(start_lr,0);
+    iterator zis(this,top), zat(&b,top);
+    while (!zis.end()) {
+        while (zis.deep() || zat.deep()) {
+            zis.left(); zat.left();
+        }
+        
+        *zis &= ~*zat;
+        
+        while (zis.pos.is_right()) {
+            zis.parent(); zat.parent();
+        }
+        zis.sibling(); zat.sibling();
+    }
+}
\ No newline at end of file
diff --git a/bins.h b/bins.h
index ca226df..299ece6 100644 (file)
--- a/bins.h
+++ b/bins.h
@@ -20,11 +20,15 @@ public:
     
     bins();
     
+    bins(const bins& b);
+    
     uint16_t get (bin64_t bin); 
     
     void set (bin64_t bin, fill_t val=FILLED); 
     
-    bin64_t find (const bin64_t range, const uint8_t layer) ;
+    bin64_t find (const bin64_t range, const uint8_t layer, fill_t seek=EMPTY) ;
+    
+    void remove (bins& b);
     
     void dump(const char* note);
 
index 5ff689b..dd2f5dd 100644 (file)
 namespace p2tp {
 
 typedef int64_t tint;
-#define SEC ((tint)1000000)
-#define MSEC ((tint)1000)
-#define uSEC ((tint)1)
-#define NEVER ((tint)0x7fffffffffffffffLL)
+#define TINT_SEC ((tint)1000000)
+#define TINT_MSEC ((tint)1000)
+#define TINT_uSEC ((tint)1)
+#define TINT_NEVER ((tint)0x7fffffffffffffffLL)
 #define MAXDGRAMSZ 1400
 
 struct Datagram {
diff --git a/ext/mmap_storer.cpp b/ext/mmap_storer.cpp
new file mode 100644 (file)
index 0000000..49d07c7
--- /dev/null
@@ -0,0 +1,25 @@
+/*
+ *  mmap_storer.cpp
+ *  p2tp
+ *
+ *  Created by Victor Grishchenko on 10/7/09.
+ *  Copyright 2009 Delft Technical University. All rights reserved.
+ *
+ */
+#include "p2tp.h"
+
+class MMappedStorer : public DataStorer {
+public:
+    
+    DataStorer (Sha1Hash id, size_t size) {
+    }
+    
+    virtual size_t    ReadData (bin64_t pos,uint8_t** buf) {
+    }
+    
+    virtual size_t    WriteData (bin64_t pos, uint8_t* buf, size_t len) {
+    }
+    
+    virtual Sha1Hash* LoadHashes();
+    
+};
\ No newline at end of file
similarity index 53%
rename from seq_picker.cpp
rename to ext/seq_picker.cpp
index 4b20e01..dbb7e83 100644 (file)
@@ -19,28 +19,28 @@ class SeqPiecePicker : public PiecePicker {
 public:
     
     SeqPiecePicker (FileTransfer* file_) : file(file_) {
-        diho(file->ack_out);
     }
     
     virtual bin64_t Pick (bins& from, uint8_t layer) {
-        bins may_pick = ~ file->ack_out;
-        may_pick &= from;
-        may_pick -= hint_out;
-        bin64_t pick = may_pick.find(file->top,bins::FILLED);
-        if ( pick==bin64_t::NONE || pick.right_foot() > file->size() )
-            if (layer)
-                return Pick(from,layer-1);
-            else
-                return bin64_t::NONE;
-        return pick;
+        bins may_pick = from;
+        may_pick.remove (file->ack_out);
+        may_pick.remove (hint_out);
+        for (int l=layer; l>=0; l--) {
+            for(int i=0; i<file->peak_count; i++) {
+                bin64_t pick = may_pick.find(file->peaks[i],l,bins::FILLED);
+                if (pick!=bin64_t::NONE)
+                    return pick;
+            }
+        }
+        return bin64_t::NONE;
     }
     
     virtual void    Received (bin64_t b) {
-        diho.set(b,bins::FILLED);
+        hint_out.set(b,bins::EMPTY);
     }
     
     virtual void    Snubbed (bin64_t b) {
-        diho.set(b,bins::EMPTY);
+        hint_out.set(b,bins::EMPTY);
     }
     
 };
\ No newline at end of file
similarity index 100%
rename from simple_selector.cpp
rename to ext/simple_selector.cpp
index 6c17892..4e4d201 100644 (file)
@@ -59,7 +59,7 @@ string        Sha1Hash::hex() {
 }*/
 
 
-Sha1Hash HashTree::deriveRoot () {
+/*Sha1Hash HashTree::deriveRoot () {
        int i = peaks.size()-1;
        bin p = peaks[i].first;
        Sha1Hash hash = peaks[i].second;
@@ -161,4 +161,4 @@ HashTree::hashres_t HashTree::offer (bin pos, const Sha1Hash& hash) {
        
 }
 
-
+*/
index f4bdb4e..611919b 100644 (file)
@@ -27,11 +27,13 @@ struct Sha1Hash {
        bool    operator == (const Sha1Hash& b) const
                { return 0==memcmp(bits,b.bits,SIZE); }
        bool    operator != (const Sha1Hash& b) const { return !(*this==b); }
+    const char* operator * () const { return (char*) bits; }
        
        const static Sha1Hash ZERO;
        const static size_t SIZE;
 };
 
+/*
 typedef std::pair<bin,Sha1Hash> binhash;
 
 struct HashTree {
@@ -67,6 +69,6 @@ public:
        
        const std::vector<binhash>& peak_hashes() const { return peaks; }
        
-};
+};*/
 
 #endif
index fe540bc..1bf4bc9 100644 (file)
--- a/p2tp.cpp
+++ b/p2tp.cpp
 #include <unistd.h>
 #include <glog/logging.h>
 #include "p2tp.h"
+#include "datagram.h"
 
 using namespace std;
 using namespace p2tp;
 
 p2tp::tint Channel::last_tick = 0;
 int Channel::MAX_REORDERING = 4;
-p2tp::tint Channel::TIMEOUT = TINT_1SEC*60;
+p2tp::tint Channel::TIMEOUT = TINT_SEC*60;
 std::vector<Channel*> Channel::channels(1);
 std::vector<File*> File::files(4);
 int* Channel::sockets_ = (int*)malloc(40);
diff --git a/p2tp.h b/p2tp.h
index a5ac644..7286975 100644 (file)
--- a/p2tp.h
+++ b/p2tp.h
@@ -84,43 +84,40 @@ namespace p2tp {
                
     public:
                
+               /**     Open/submit/retrieve a file.    */
+        FileTransfer(const Sha1Hash& _root_hash, const char *file_name);
+        
+               /**     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. */
-               bool            OfferHash (bin64_t pos, const Sha1Hash& hash);
+               void            OfferHash (bin64_t pos, const Sha1Hash& hash);
         /** Offer data; the behavior is the same as with a hash:
-            accept or remember or drop. */
-        bool            OfferData (bin64_t bin, uint8_t* data);
+            accept or remember or drop. Returns true => ACK is sent. */
+        bool            OfferData (bin64_t bin, uint8_t* data, size_t length);
         
-               const Sha1Hash& root_hash () const { return *root_hash; }
-        
-               size_t          size () const;
-               size_t          packet_size () const;
-        size_t          size_complete () const;
-        size_t          packets_complete () const;
-               
-               static FileTransfer* find (const Sha1Hash& hash);
+               static FileTransfer* Find (const Sha1Hash& hash);
                static FileTransfer* file (int fd) { return fd<files.size() ? files[fd] : NULL; }
         
                friend int      Open (const char* filename);
                friend int      Open (const Sha1Hash& hash, const char* filename);
                friend void     Close (int fdes);
         
-    private:
+    public:
 
                static std::vector<FileTransfer*> files;
-        
-               /**     Open/submit/retrieve a file.    */
-               FileTransfer (Sha1Hash hash, char* file_name);
-               /**     Close everything. */
-               ~FileTransfer();
-               
+                       
                /**     file descriptor. */
                int                             fd;
         /** File size, as derived from the hashes. */
+        size_t          size;
         size_t          sizek;
-               /**     Whether the file is completely downloaded. */
-               bool            complete;
+               /**     Part the file currently downloaded. */
+               size_t          complete;
+               size_t          completek;
+               size_t          seq_complete;
                /**     A map for all packets obtained and succesfully checked. */
                bins                    ack_out;
                /**     History of bin retrieval. */
@@ -130,20 +127,29 @@ namespace p2tp {
                /** File for keeping the Merkle hash tree. */
         int             hashfd;
         /** Merkle hash tree: root */
-        Sha1Hash*       root_hash;
+        Sha1Hash        root_hash;
         /** Merkle hash tree: peak hashes */
-        Sha1Hash*       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;
+        
     protected:
-        void            Resize(size_t bytes);
+        void            SetSize(size_t bytes);
+        void            Submit();
+        void            RecoverProgress();
+        void            OfferPeak (bin64_t pos, const Sha1Hash& hash);
+        Sha1Hash        DeriveRoot();
 
                friend class Channel;
        };
        
        
        int             Open (const char* filename) ;
-       int             Open (const Sha1Hash& root_hash, const char* filename); 
+    int     Open (const Sha1Hash& hash, const char* filename) ;
        void    Close (int fid) ;
     void    Loop (tint till);
     int     Bind (int port);
@@ -157,10 +163,10 @@ namespace p2tp {
         tint    dev_avg;
         int     cwnd;
         int     peer_cwnd;
-        virtual void OnDataSent(bin64_t b) = 0;
-        virtual void OnDataRecvd(bin64_t b) = 0;
-        virtual void OnAckRcvd(bin64_t b, tint peer_stamp) = 0;
-               virtual ~CongestionControl() = 0;
+        virtual void    OnDataSent(bin64_t b) = 0;
+        virtual void    OnDataRecvd(bin64_t b) = 0;
+        virtual void    OnAckRcvd(bin64_t b, tint peer_stamp) = 0;
+               virtual         ~CongestionController() = 0;
        };
     
     class PiecePicker {
@@ -172,8 +178,16 @@ namespace p2tp {
     
     class PeerSelector {
     public:
-        virtual void PeerKnown (const Sha1Hash& root, struct sockaddr_in& addr) = 0;
-        virtual sockaddr_in GetPeer (const Sha1Hash& for_root) = 0;
+        virtual void    PeerKnown (const Sha1Hash& root, struct sockaddr_in& addr) = 0;
+        virtual struct sockaddr_in 
+                        GetPeer (const Sha1Hash& for_root) = 0;
+    };
+    
+    class DataStorer {
+    public:
+        DataStorer (const Sha1Hash& id, size_t size);
+        virtual size_t    ReadData (bin64_t pos,uint8_t** buf) = 0;
+        virtual size_t    WriteData (bin64_t pos, uint8_t* buf, size_t len) = 0;
     };
        
 
@@ -195,8 +209,6 @@ namespace p2tp {
                tint            SendSomething ();
                void            SendHandshake ();
 
-               FileTransfer&           file () { return *FileTransfer::files[fd]; }
-       
                void            OnAck (Datagram& dgram);
                void            OnData (Datagram& dgram);
                void            OnHint (Datagram& dgram);
@@ -211,8 +223,6 @@ namespace p2tp {
 
                void            CleanStaleHints();
                
-               state_t         state () const;
-
                static int DecodeID(int scrambled);
                static int EncodeID(int unscrambled);
                static Channel* channel(int i) {return i<channels.size()?channels[i]:NULL;}
index a222843..b2a16aa 100644 (file)
@@ -303,7 +303,7 @@ void Channel::OnData (Datagram& dgram) {
 
 
 void   Channel::OnAck (Datagram& dgram) {
-       
+       // FIXME check whether it is in the range
        bin pos = dgram.Pull32();
     DLOG(INFO)<<"#"<<id<<" .ACK"<<pos;
        if (file().hashes.data_mass() && pos>file().hashes.data_mass()) {
index 71db670..3a2bb44 100644 (file)
@@ -9,7 +9,7 @@
 #include "bin64.h"
 #include <gtest/gtest.h>
 
-TEST(BinTest,InitGet) {
+TEST(Bin64Test,InitGet) {
 
     EXPECT_EQ(0x1,bin64_t(1,0));
     EXPECT_EQ(0xB,bin64_t(2,1));
@@ -23,7 +23,7 @@ TEST(BinTest,InitGet) {
 
 }
 
-TEST(BinTest,Navigation) {
+TEST(Bin64Test,Navigation) {
 
     bin64_t mid(4,18);
     EXPECT_EQ(bin64_t(5,9),mid.parent());
@@ -35,7 +35,7 @@ TEST(BinTest,Navigation) {
 
 }
 
-TEST(BinTest,Overflows) {
+TEST(Bin64Test,Overflows) {
     
     /*EXPECT_EQ(bin64_t::NONE.parent(),bin64_t::NONE);
     EXPECT_EQ(bin64_t::NONE.left(),bin64_t::NONE);
@@ -45,6 +45,37 @@ TEST(BinTest,Overflows) {
 */
 }
 
+TEST(Bin64Test, Advanced) {
+    
+    EXPECT_EQ(4,bin64_t(2,3).width());
+    EXPECT_FALSE(bin64_t(1,1234).is_base());
+    EXPECT_TRUE(bin64_t(0,12345).is_base());
+    EXPECT_EQ(bin64_t(0,2),bin64_t(1,1).left_foot());
+    bin64_t peaks[64];
+    int peak_count = bin64_t::peaks(7,peaks);
+    EXPECT_EQ(3,peak_count);
+    EXPECT_EQ(bin64_t(2,0),peaks[0]);
+    EXPECT_EQ(bin64_t(1,2),peaks[1]);
+    EXPECT_EQ(bin64_t(0,6),peaks[2]);
+    
+}
+
+TEST(Bin64Test, Iteration) {
+    bin64_t i(1,0);
+    i = i.next_dfsio(1);
+    EXPECT_EQ(bin64_t(1,1),i);
+    i = i.next_dfsio(1);
+    EXPECT_EQ(bin64_t(2,0),i);
+    i = i.next_dfsio(1);
+    EXPECT_EQ(bin64_t(1,2),i);
+    i = i.next_dfsio(1);
+    EXPECT_EQ(bin64_t(1,3),i);
+    i = i.next_dfsio(1);
+    EXPECT_EQ(bin64_t(2,1),i);
+    i = i.next_dfsio(1);
+    EXPECT_EQ(bin64_t(3,0),i);
+}
+
 int main (int argc, char** argv) {
        
        testing::InitGoogleTest(&argc, argv);
index f268f84..8bb74c9 100644 (file)
  *  Copyright 2009 Delft Technical University. All rights reserved.
  *
  */
-
 #include "p2tp.h"
+#include <sys/mman.h>
 
+using namespace p2tp;
 
-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
-}
-
-File::~File() {
-       if (fd>0) ::close(fd);
-}
+std::vector<FileTransfer*> FileTransfer::files(20);
 
 
-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 Sha1Hash& _root_hash, const char* filename) :
+    root_hash(_root_hash), fd(0), hashfd(0), dry_run(false), peak_count(0), hashes(NULL)
+{
+       fd = open(filename,O_RDWR|O_CREAT,S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH);
+       hashfd = open("/tmp/hahs",O_RDWR|O_CREAT,S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH); // FIXME
+       if (fd<0 || hashfd<0)
+        return;
+    if (root_hash==Sha1Hash::ZERO) // fresh submit, hash it
+        Submit();
+    else
+        RecoverProgress();    
 }
 
 
-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;
+/** Basically, simulated receiving every single packet, except
+    for some optimizations. */
+void            FileTransfer::RecoverProgress () {
+    // open file
+       struct stat hash_file_st;
+       fstat(fd, &hash_file_st);
+    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;
+    // at this point, we may use mmapd hashes already
+    // so, lets verify hashes and the data we've got
+    dry_run = true;
+    lseek(fd,0,SEEK_SET);
+    for(int p=0; p<sizek; p++) {
+        uint8_t buf[1<<10];
+        size_t rd = read(fd,buf,1<<10);
+        OfferData(bin64_t(10,p), buf, rd);
+        if (rd<(1<<10))
+            break;
+    }
+    dry_run = false;
 }
 
 
-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 FileTransfer::SetSize (size_t bytes) {
+       struct stat st;
+       fstat(fd, &st);
+    if (st.st_size!=bytes)
+        if (ftruncate(fd, bytes))
+            return; // remain in the 0-state
+    complete = size = bytes;
+       completek = sizek = (size>>10) + (size%1023 ? 1 : 0);
+    peak_count = bin64_t::peaks(sizek,peaks);
+    ftruncate( hashfd, sizek*2*Sha1Hash::SIZE + 
+               (sizeof(bin64_t)+Sha1Hash::SIZE)*64 );
+    lseek(hashfd,0,SEEK_SET);
+    write(hashfd,&bin64_t::ALL,sizeof(bin64_t));
+    write(hashfd,*root_hash,Sha1Hash::SIZE);
+    for(int i=0; i<peak_count; i++) {
+        write(hashfd,&(peaks[i]),sizeof(bin64_t));
+        write(hashfd,*(peak_hashes[i]),Sha1Hash::SIZE);
+    }
+    uint8_t zeros[256];
+    memset(zeros,0,256);
+    for(int i=peak_count; i<63; i++) 
+        write(hashfd,zeros,Sha1Hash::SIZE+sizeof(bin64_t));
+    hashes = (Sha1Hash*) mmap (NULL, sizek*2*sizeof(Sha1Hash), PROT_READ|PROT_WRITE, MAP_FILE,
+                   hashfd, (sizeof(bin64_t)+sizeof(Sha1Hash))*64 );
 }
 
-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;
+void            FileTransfer::Submit () {
+       struct stat st;
+       fstat(fd, &st);
+    SetSize(st.st_size);
+    if (!size)
+        return;
+    for (int p=0; p<peak_count; p++) {
+        for(bin64_t b=peaks[p].left_foot(); b.within(peaks[p]); b=b.next_dfsio(10)) 
+            if (b.is_base()) {
+                uint8_t kilo[1<<10];
+                size_t rd = pread(fd,kilo,1<<10,b.base_offset());
+                hashes[b] = Sha1Hash(kilo,rd);
+            } else
+                hashes[b] = Sha1Hash(hashes[b.left()],hashes[b.right()]);
+        peak_hashes[peaks[p]] = hashes[peaks[p]];
+    }
+    root_hash = DeriveRoot();
 }
 
-size_t p2tp::file_size (int fd) { return File::file(fd)->size(); }
 
-void p2tp::Close (int fid) {
-       if (!File::files[fid])
+void            FileTransfer::OfferHash (bin64_t pos, const Sha1Hash& hash) {    
+       if (!size)  // only peak hashes are accepted at this point
+               return OfferPeak(pos,hash);
+       if (pos.base_offset()>=sizek)
                return;
-       delete File::files[fid];
-       File::files[fid] = NULL;
+    if (ack_out.get(pos)!=bins::EMPTY)
+        return; // have this hash already, even accptd data
+       hashes[pos] = hash;
 }
 
+bool            FileTransfer::OfferData (bin64_t pos, uint8_t* data, size_t length) {
+    if (pos.layer()!=10)
+        return false;
+    if (ack_out.get(pos)==bins::FILLED)
+        return true; // ???
+    int peak=0;
+    while (peak<peak_count && !pos.within(peaks[peak]))
+        peak++;
+    if (peak==peak_count)
+        return false;
+    Sha1Hash hash(data,length);
+    hashes[pos] = hash;
+       // walk to the nearest proven hash
+    for(bin64_t p = pos.parent(); p.within(peaks[peak]) && ack_out.get(p)==bins::EMPTY; p=p.parent())
+        if (hashes[p]!=Sha1Hash(hashes[p.left()],hashes[p.right()]))
+            return false; // hash mismatch
+    ack_out.set(pos,bins::FILLED);
+    pwrite(fd,data,length,pos.base_offset());
+    return true;
+}
 
-Sha1Hash HashTree::deriveRoot () {
-       int i = peak_count-1;
-       bin64_t p = peaks[i].first;
-       Sha1Hash hash = peak_hashes[i].second;
-       i--;
+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 (i<0 || peaks[i].first!=p.sibling())
+                       if (c<0 || peaks[c]!=p.sibling())
                                return Sha1Hash::ZERO;
-                       hash = Sha1Hash(peak_hashes[i].second,hash);
+                       hash = Sha1Hash(peak_hashes[c],hash);
                        p = p.parent();
-                       i--;
+                       c--;
                }
        }
-       return hash;
+    return hash;
 }
 
-/** Three stages: have file, have root hash, have peaks. */
-HashTree::HashTree (int _datafd, int _hashfd, Sha1Hash _root)
-: datafd(_datafd), hashfd(_hashfd), root(_root) 
-{
-    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
+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 (hash!=root_hash)
+        return;
+    // bingo, we now know the file size (rounded up to a KByte)
+    SetSize(pos.base_offset()+pos.width());
 }
 
-bool FileTransfer::acceptData (uint64_t bin, uint8_t* data, size_t len) {
+FileTransfer::~FileTransfer () {
+    munmap(hashes,sizek*2*Sha1Hash::SIZE);
+    close(hashfd);
+    close(fd);
+}
+                           
+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      p2tp::Open (const char* filename) {
+    return Open(Sha1Hash::ZERO,filename);
 }
 
-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;
+int      p2tp::Open (const Sha1Hash& hash, const char* filename) {
+    FileTransfer* ft = new FileTransfer(hash, filename);
+    if (ft->fd>0) {
+        if (FileTransfer::files.size()<ft->fd)
+            FileTransfer::files.resize(ft->fd);
+        FileTransfer::files[ft->fd] = ft;
+        return ft->fd;
+    } else {
+        delete ft;
+        return -1;
+    }
 }
 
-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     Close (int fdes) {
+    // FIXME delete all channels
+    delete FileTransfer::files[fdes];
+    FileTransfer::files[fdes] = NULL;
 }
 
 
 
+/*
+ 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);
+ }
+ */