in progress
authorvictor <victor@e16421f0-f15b-0410-abcd-98678b794739>
Wed, 7 Oct 2009 11:20:22 +0000 (11:20 +0000)
committervictor <victor@e16421f0-f15b-0410-abcd-98678b794739>
Wed, 7 Oct 2009 11:20:22 +0000 (11:20 +0000)
git-svn-id: https://ttuki.vtt.fi/svn/p2p-next/TUD/p2tp/trunk@393 e16421f0-f15b-0410-abcd-98678b794739

12 files changed:
bin64.h
bins.cpp
bins.h
collisions.cpp [deleted file]
congctrl.cpp [deleted file]
ledbat_controller.cpp [new file with mode: 0644]
p2tp.h
seq_picker.cpp [new file with mode: 0644]
simple_selector.cpp [new file with mode: 0644]
tests/SConscript
tests/freemap.cpp [new file with mode: 0755]
transfer.cpp [new file with mode: 0644]

diff --git a/bin64.h b/bin64.h
index da45678..e619468 100644 (file)
--- a/bin64.h
+++ b/bin64.h
@@ -22,6 +22,9 @@ struct bin64_t {
     operator uint64_t () const { return v; }
     bool operator == (bin64_t& b) const { return v==b.v; }
 
+    static bin64_t none () { return NONE; }
+    static bin64_t all () { return ALL; }
+
     uint64_t tail_bits () const {
         return v ^ (v+1);
     }
@@ -30,6 +33,10 @@ struct bin64_t {
         return (tail_bits()+1)>>1;
     }
 
+    bin64_t sibling () const {
+        // if (v==ALL) return NONE; 
+        return bin64_t(v^(tail_bit()<<1));
+    }
 
     int layer () const {
         int r = 0;
@@ -49,22 +56,44 @@ struct bin64_t {
     }
 
     uint64_t base_offset () const {
-        return v&~(tail_bits());
+        return (v&~(tail_bits()))>>1;
     }
 
     uint64_t offset () const {
         return v >> (layer()+1);
     }
 
+    bin64_t to (bool right) const {
+        if (!(v&1))
+            return NONE;
+        uint64_t tb = tail_bit()>>1;
+        if (right)
+            tb |= (tb<<1);
+        return bin64_t(v^tb);
+    }
+
     bin64_t left () const {
-        assert(layer());
-        return bin64_t( v ^ (tail_bit()>>1) );
+        return to(false);
     }
 
     bin64_t right () const {
-        assert(layer());
-        uint64_t tb = tail_bit();
-        return bin64_t( v ^ (tb|(tb>>1)) );
+        return to(true);
+    }
+
+    bool    within (bin64_t maybe_asc) {
+        uint64_t short_tail = maybe_asc.tail_bits();
+        if (tail_bits()>short_tail)
+            return false;
+        return (v&~short_tail) == (maybe_asc.v&~short_tail) ;
+    }
+
+    bin64_t towards (bin64_t desc) const {
+        if (!desc.within(*this))
+            return NONE;
+        if (desc.within(left()))
+            return left();
+        else
+            return right();
     }
 
     bin64_t parent () const {
@@ -76,6 +105,7 @@ struct bin64_t {
         uint64_t tb = tail_bit();
         return !(v&(tb<<1));
     }
+    bool is_right() const { return !is_left(); }
 
     /** The array must have 64 cells, as it is the max
      number of peaks possible (and there are no reason
@@ -96,3 +126,18 @@ struct bin64_t {
 
 
 #endif
+
+/**
+                 00111
+       0011                    1011
+  001      101         1001          1101
+0   10  100  110    1000  1010   1100   1110
+
+                  7
+      3                         11
+  1        5             9             13
+0   2    4    6       8    10      12     14
+
+once we have peak hashes, this struture is more natural than bin-v1
+
+*/
index 4dd1e3d..e6d580f 100644 (file)
--- a/bins.cpp
+++ b/bins.cpp
  *
  */
 #include "bins.h"
+#include <string.h>
 
-uint16_t               bins::SPLIT[256];
-uint8_t                        bins::JOIN[256];
-uint16_t               bins::MASK1000[32];
-
-bin64_t bins::find (bin64_t range, int layer) {
-    // fall to left border
-    // do
-    //  if up && has 0 success
-    //  if lower && range-shift-trick
-    //      success
-    // while (next && within range)
-    // return fail
+// make it work piece by piece
+
+const uint8_t  bins::SPLIT[16] = 
+{0, 3, 12, 15, 48, 51, 60, 63, 192, 195, 204, 207, 240, 243, 252, 255};
+const uint8_t    bins::JOIN[16] =
+{0, 1, 4, 5, 2, 3, 6, 7, 8, 9, 12, 13, 10, 11, 14, 15};
+const int bins::NOJOIN = 0x10000;
+
+
+void bins::extend () {
+    uint16_t nblocks = blocks_allocated ? (blocks_allocated<<1) : 1;
+    size_t had_bytes = blocks_allocated<<6;
+    size_t need_bytes = nblocks<<6;
+    cells = (uint32_t*) realloc(cells,need_bytes);
+    memset(((char*)cells)+had_bytes,0,need_bytes-had_bytes);
+    for(int b=blocks_allocated; b<nblocks; b++)
+        cells[(b<<4)|0xf] = 0x55555555; // cells are free
+    blocks_allocated = nblocks;
+}
+
+bins::bins() :  height(4), blocks_allocated(0), cells(NULL), 
+                ap(0), cells_allocated(0) {
+    extend();
+    assert(!alloc_cell());
+}
+
+void bins::dump (const char* note) {
+    printf("%s\t",note);
+    for(int i=0; i<(blocks_allocated<<5); i++) {
+        if ( (i&0x1f)>29 )
+            printf("|%x ",halves[i]);
+        else if (deep(i))
+            printf(">%i ",halves[i]);
+        else
+            printf("%x ",halves[i]);
+        if (i&1)
+            printf(" ");
+    }
+    printf("\n");
+}
+
+uint32_t bins::split16to32(uint16_t halfval) {
+    uint32_t nval = 0;
+    for(int i=0; i<4; i++) {
+        nval >>= 8;
+        nval |= (SPLIT[halfval&0xf])<<24;
+        halfval >>= 4;
+    }
+    return nval;
+}
+
+
+int bins::join32to16(uint32_t cval) {
+    union { uint32_t i; uint8_t a[4]; } uvar;
+    uvar.i = cval & (cval>>1) & 0x55555555;
+    if ( (uvar.i|(uvar.i<<1)) != cval )
+        return NOJOIN;
+    uvar.i = (uvar.i&0x05050505) | ((uvar.i&0x50505050U)>>3);
+    uint16_t res = 0;
+    for(int i=3; i>=0; i--) {
+        res <<= 4;
+        res |= JOIN[uvar.a[i]];
+    }
+    return res;
+}
+
+
+void        bins::split (uint32_t half) {
+    if (deep(half))
+        return;
+    uint32_t cell = alloc_cell(), left=cell<<1, right=left+1;
+    mark(half); //cells[(half>>1)|0xf] |= 1<<(half&0x1f);
+    uint16_t halfval = halves[half];
+    uint32_t nval = split16to32(halfval);
+    halves[left] = nval&0xffff;
+    halves[right] = nval>>16;
+    halves[half] = cell;
+}
+
+
+bool        bins::join (uint32_t half) {
+    uint32_t cellno = halves[half];
+    int left = cellno<<1, right=left+1;
+    if (deep(left) || deep(right))
+        return false;
+    int res = join32to16(cells[cellno]);
+    if (res>0xffff)
+        return false;
+    halves[half] = (uint16_t)res;
+    unmark(half);
+    free_cell(cellno);
+    //cells[(half>>1)|0xf] &= ~(1<<(half&0x1f));
+    //(*childdeepcell) &= 0xffff>>1; // clean the full bit
+    return true;
+}
+
+void    bins::free_cell (uint16_t cell) {
+    cells[cell] = 0;
+    int left = cell<<1, right=left+1;
+    mark(left);
+    unmark(right);
+    if (ap==cell+1)
+        ap--;
+    cells_allocated--;
+}
+
+/** Get a free cell. */
+uint16_t    bins::alloc_cell () {
+    uint16_t ap1 = ap;
+    cells_allocated++;
+    for(; ap<(blocks_allocated<<4); ap++) {
+        if ((ap&0xf)==0xf)
+            continue;
+        if (!cells[ap] && deep(ap<<1)) {
+            unmark(ap<<1);
+            return ap++;
+        }
+    }
+    if (ap1)
+        ap = 0;
+    else
+        extend();
+    cells_allocated--;
+    return alloc_cell();
+}
+
+
+bin64_t iterator::next (bool need_solid) {
+    assert(!deep());
+    while (pos.is_right())
+        parent();
+    //parent();
+    //if (need_solid ? !solid() : deep())
+    //    right();
+    sibling();
+    while (need_solid ? !solid() : deep())
+        left();
+    return pos;
+}
+
+
+iterator::iterator(bins* host_, bin64_t start, bool split) { 
+    host = host_;
+    half = 0;
+    for(int i=0; i<64; i++)
+        history[i] = 1;
+    pos = bin64_t(host->height,0);
+    while (!start.within(pos))
+        parent();
+    while (pos!=start && (deep() || split))
+        towards(start);
+}
+
+
+iterator::~iterator () {
+    while (half>1 && !deep())
+        parent();
+    // PROBLEM: may hang in the air if two iters
+    // exist simultaneously
+    // FIX: iterators are not exposed (protected)
+}
+
+
+void iterator::to (bool right) {
+    if (!deep())
+        host->split(half);
+    history[pos.layer()] = half; // FIXME
+    pos = pos.to(right);
+    half = (host->halves[half]<<1) + right;
+    //host->dump("/\\ ");
+}
+
+
+void bins::extend_range () {
+    assert(height<62);
+    height++;
+    uint16_t newroot = alloc_cell();
+    int left = newroot<<1, right = left+1;
+    cells[newroot] = cells[0];
+    halves[0] = newroot;
+    halves[1] = 0;
+    if (deep(0))
+        mark(left);
+    else
+        mark(0);            
+    if (deep(1)) {
+        mark(right);
+        unmark(1);
+    }        
+}
+
+void iterator::parent () {
+    if (!half) {
+        host->extend_range();
+        history[pos.layer()+1] = 0;
+    }
+    pos = pos.parent();
+    half = history[pos.layer()];
+    host->join(half);
+    //host->dump("| ");
+}
+
+
+bin64_t bins::find (const bin64_t range, const uint8_t layer) {
+    iterator i(this,range,true);
+    while (true) {
+        while ( i.bin().layer()>layer && (i.deep() || *i!=FILLED) )
+            i.left();
+        if (i.bin().layer()==layer && !i.deep() && *i==EMPTY)
+            return i.bin();
+        while (i.bin().is_right() && i.bin()!=range)
+            i.parent();
+        if (i.bin()==range)
+            break;
+        i.parent();
+        i.right();
+    }
+    return bin64_t::NONE;
+}
+
+
+uint16_t bins::get (bin64_t bin) {
+    iterator i(this,bin,true);
+    //while ( i.pos!=bin && 
+    //        (i.deep() || (*i!=BIN_FULL && *i!=BIN_EMPTY)) )
+    //    i.towards(bin);
+    //printf("at %i ",i.half);
+    //dump("get made");
+    return *i; // deep cell is never 0xffff or 0x0000
+}
+
+
+void bins::set (bin64_t bin, fill_t val) {
+    assert(val==FILLED || val==EMPTY);
+    iterator i(this,bin,false);
+    while (i.bin()!=bin && (i.deep() || *i!=val))
+        i.towards(bin);
+    if (!i.deep() && *i==val)
+        return;
+    while (i.deep()) 
+        i.left();
+    do {
+        *i = val;
+        i.next();
+    } while (i.bin().within(bin));
+    // dump("just set");
+}
+
+
+uint64_t*   bins::get_stripes (int& count) {
+    int size = 32;
+    uint64_t *stripes = (uint64_t*) malloc(32*8);
+    count = 0;
+    uint16_t cur = bins::EMPTY;
+    stripes[count++] = 0;
+    iterator i(this,0,false);
+    while (!i.solid())
+        i.left();
+
+    while (!i.end()) {
+
+        if (cur!=*i) { // new stripe
+            cur = *i;
+            stripes[count++] = i.bin().base_offset();
+            if (count==size) {
+                size <<= 1;
+                stripes = (uint64_t*) realloc(stripes,size*8);
+            }
+        }
+
+        i.next(true);
+
+    }
+
+    if ( !(count&1) )
+        stripes[count++] = i.bin().base_offset();
+    
+    return stripes;
 }
diff --git a/bins.h b/bins.h
index d95406f..ca226df 100644 (file)
--- a/bins.h
+++ b/bins.h
  *  Copyright 2009 Delft Technical University. All rights reserved.
  *
  */
-#ifndef SERP_SBIT_H
-#define SERP_SBIT_H
+#ifndef BINS_H
+#define BINS_H
 #include "bin64.h"
+#include <gtest/gtest.h>
 
-class bins64 {
+/**  A binmap covering 2^64 range. Complexity limit: 100+200LoC  */
+class bins {
+    
+public:
+    typedef enum { FILLED=0xffff, EMPTY=0x0000 } fill_t;
+    static const int NOJOIN;
+    
+    bins();
+    
+    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) ;
+    
+    void dump(const char* note);
 
-    private:
+    uint64_t*   get_stripes (int& count);
 
-        uint32_t    *bits;
-        uint32_t    alloc_block;
-
-    protected:
-
-        bool        join(uint32_t half) {
-            uint32_t cellno = bits[half]>>(half&1?16:0);
-
-            if (deep(left) || deep(right)) // some half is deep
-                return false;
-            uint8_t b1=JOIN[cell&0xf],
-                    b2=JOIN[(cell>>8)&0xf],
-                    b3=JOIN[(cell>>16)&0xf],
-                    b4=JOIN[cell>>24];
-            if (b1==0xff || b2==0xff || b3==0xff || b4==0xff)
-                return false;
-            bits[half] = b1 | (b2<<4) | (b3<<8) | (b4<<12) ;
-            (*parentdeepcell) ^= 1<<(halfno&32);
-            (*childdeepcell) &= 0xffff>>1; // clean the full bit
-        }
-
-        bool        split(uint32_t half) {
-            if (deep(half))
-                return false;
-            uint32_t cell = alloc_cell(), left=cell<<1, right=left+1;
-            bits[half|0xf] |= 1<<(half&0xf);
-            bits[left] = SPLIT[bits[half]>>8];
-            bits[right] = SPLIT[bits[half&0xff]];
-            bits[half] = cell;
-            return true;
-        }
-
-        uint32_t    alloc_cell () {
-            do{
-                for(int block=alloc_block; bits[block]&(1<<32); block+=32);
-                for(int off=0; bits[block+off]==0 && off<31; off++);
-                if (off==31) 
-                    bits[block] |= 1<<32;
-                if (block&(1<<31)) {
-                    bits = realloc(bits,block*2);
-                    memset();
-                }
-            } while (off==31);
-            alloc_block = block;
-            return block+off;
-        }
-
-    public:
-
-        class iterator {
-            bins64_t    *host;
-            uint32_t    back[32];
-            uint32_t    half;
-            bin64_t     top;
-            bin64_t     focus;
-            bin16_t     mask;
-            public:
-            void left();
-            void right();
-            void parent();
-            bin64_t next();
-            bool defined();
-            uint16_t& operator* ();
-        };
-        friend class iterator;
-
-        bool get (uint64_t bin);
-
-        void set (uint64_t bin);
-
-        bin64_t find (bin64_t range, int layer);
+    uint32_t    size() { return cells_allocated; }
+    // TODO: bitwise operators
+    
+private:
+    
+    /** Every 16th uint32 is a flag field denoting whether
+     previous 30 halves (in 15 cells) are deep or not.
+     The last bit is used as a fill-flag.
+     Free cells have a value of 0; neither deep nor flat
+     cell could have a value of 0 except for the root
+     cell in case the binmap is all-0. */
+    union {
+        uint32_t    *cells;
+        uint16_t    *halves;
+    };
+    uint32_t    blocks_allocated;
+    uint32_t    cells_allocated;
+    int         height;
+    uint32_t    ap;
+    
+    void extend();
+    
+    static const uint8_t    SPLIT[16];
+    static const uint8_t    JOIN[16];
+    
+    bool        deep(uint32_t half) const {
+        return cells[(half>>1)|0xf] & (1<<(half&0x1f));
+    }
+    void        mark(uint32_t half) {
+        cells[(half>>1)|0xf] |= (1<<(half&0x1f));
+    }
+    void        unmark(uint32_t half) {
+        cells[(half>>1)|0xf] &= ~(1<<(half&0x1f));
+    }
+    
+    void extend_range();
+    
+    uint16_t    alloc_cell ();
+    void        free_cell (uint16_t cell);
+    
+    /** Join the cell this half is pointing to
+     (in other words, flatten the half). */
+    bool        join(uint32_t half) ;
+    
+    /** Make the half deep. */
+    void        split(uint32_t half) ;
+    
+    static uint32_t split16to32(uint16_t half);
+    static int join32to16(uint32_t cell);
+    
+    friend class iterator;
+    FRIEND_TEST(BinsTest,Routines);
+};
 
-        // TODO: bitwise operators
 
+/** Iterates over bins; for deep halves, bin==half.
+ For flat halves, bin is a range of bits in a half.
+ Iterator may split cells if needed.
+ Value is either undefined (deep cell, mixed cell)
+ or FILLED/EMPTY. */
+class iterator {
+public: // rm this
+    bins        *host;
+    uint32_t    history[64];
+    uint32_t    half;
+    bin64_t     pos;  // TODO: half[] layer bin
+public:
+    iterator(bins* host, bin64_t start=0, bool split=false);
+    ~iterator();
+    bool deep () { return host->deep(half); }
+    bool solid () { 
+        return !deep() && (host->halves[half]==bins::FILLED || 
+                host->halves[half]==bins::EMPTY); 
+    }
+    void sibling () { half^=1; pos=pos.sibling(); }
+    bool end () { return half==1; }
+    void to (bool right);
+    void left() {to(0);}
+    void right() {to(1);}
+    /** Move to the next defined (non-deep, flat) cell.
+        If solid==true, move to a solid (0xffff/0x0) cell. */
+    bin64_t next (bool solid=false);
+    bin64_t bin() { return pos; }
+    void towards(bin64_t bin) {
+        bin64_t next = pos.towards(bin);
+        assert(next!=bin64_t::NONE);
+        to(next.is_right());
+    }
+    void parent() ;
+    bool defined() { return !host->deep(half); }
+    uint16_t& operator* () { return host->halves[half]; }
 };
+
+#endif
diff --git a/collisions.cpp b/collisions.cpp
deleted file mode 100644 (file)
index 24c3173..0000000
+++ /dev/null
@@ -1,145 +0,0 @@
-/*
- *  collisions.cpp
- *  serp++
- *
- *  Created by Victor Grishchenko on 3/15/09.
- *  Copyright 2009 Delft Technical University. All rights reserved.
- *
- */
-#include "bin.h"
-#include "sbit.h"
-#include <deque>
-#include <cmath>
-
-using namespace std;
-
-#define NUMPEERS 40
-#define QUEUE_LEN 32
-#define WIDTH  20
-#define TOP (bin(WIDTH,0))
-
-sbit   bigpic; // common knowledge
-
-struct Peer {
-       deque<bin>      packets; // packets in flight & unacknowledged
-       feye            map;
-       int peerno;
-       bin                     focus;
-       Peer () : map(bigpic,bin(0,rand()%(1<<WIDTH))) {
-               focus = map.focus;
-       }
-       void jump () {
-               bin oldfoc = focus;
-               
-               map = feye(bigpic,focus);
-               for(int i=0; i<packets.size(); i++)
-                       map|=packets[i];
-               
-               while (map.get(focus) && focus<TOP)
-                       focus = focus.parent();
-               if (focus==TOP) {
-                       printf("DONE\n");
-                       packets.push_back(0);
-                       return;
-               }
-               //if (focus!=bin(WIDTH,0))
-               //      focus = focus.parent();
-               while (focus.layer()) {
-                       bin left = focus.left(), right = focus.right();
-                       bool lfull = map.get(left);
-                       bool rfull = map.get(right);
-                       if (lfull)
-                               focus = right;
-                       else if (rfull)
-                               focus = left;
-                       else
-                               focus = rand()%2 ? left : right;
-               }
-               if (map.focus.commonParent(focus).layer()>6)
-                       map.refocus(focus);
-               if (labs(map.focus-focus)>=129)
-                       printf("bred!\n");
-               map |= focus;
-               // sanity check
-               if (bigpic.get(focus))
-                       printf("zhopa: peer %i redid %x after jumped %i\n",
-                                  peerno,focus.offset(),oldfoc.commonParent(focus).layer());
-               assert(focus.layer()==0 && focus<TOP);
-               packets.push_back(focus);
-       }
-       void hint (bin newfoc) {
-               printf("peer %i hinted at %x\n",
-                          peerno,newfoc.offset());
-               focus = newfoc;
-               //map.refocus(newfoc);  // preserve recent sends if possible
-               //map |= feye(bigpic,newfoc); // update with the big picture
-               map = feye(bigpic,newfoc);
-       }
-       void ack (bin packet) {
-               feye a(bigpic,packet);
-               a.refocus(map.focus);
-               map |= a;
-       }
-};
-
-
-bin random_descend() {
-       bin ret = bin(WIDTH,0);
-       while (ret.layer()) {
-               bin left = ret.left(), right = ret.right();
-               bool lfull = bigpic.get(left);
-               bool rfull = bigpic.get(right);
-               if (lfull)
-                       ret = right;
-               else if (rfull)
-                       ret = left;
-               else
-                       ret = rand()%2 ? left : right;
-       }
-       return ret;
-}
-
-
-int    main (int argc, char** args) {
-       bin::init();
-       sbit::init();
-       freopen( "log", "w", stdout );
-       Peer peers[NUMPEERS];
-       int numpack = 0;
-       int coll_layer[32] = {  0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
-                                                       0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 };
-       for(int i=0; i<NUMPEERS; i++) 
-               peers[i].peerno = i;
-       while ( ! bigpic.get(bin(WIDTH,0)) ) {
-               for(int i=0; i<NUMPEERS; i++) 
-                       peers[i].jump();
-               for(int i=0; i<NUMPEERS; i++)
-                       if (peers[i].packets.size()>=QUEUE_LEN) { // round trip, acks reach senders
-                               bin packet = peers[i].packets.front();
-                               peers[i].packets.pop_front();
-                               if (packet==0) continue;
-                               bool collision = bigpic.get(packet);
-                               bin round = bigpic.set(packet);
-                               printf("peer %i arrived %x filled %i coll %i\n",
-                                          i,packet.offset(),round.layer(),collision);
-                               for(int j=0; j<NUMPEERS; j++)
-                                       peers[j].map |= round;
-                               peers[i].ack(packet);
-                               numpack++;
-                               if (collision) {
-                                       peers[i].hint(random_descend());
-                                       coll_layer[round.layer()]++;
-                               }
-                               /*{     // update with the big picture
-                                       feye update(bigpic,packet); // current focus is unknown
-                                       update.refocus(peers[i].focus);
-                                       peers[i].map |= update;
-                               }*/
-                       }
-       }
-       printf("%i useful, %i total plus the tail\n",1<<WIDTH,numpack);
-       for(int l=0;l<32; l++)
-               printf("%i ",coll_layer[l]);
-       printf("\n");
-}
-
diff --git a/congctrl.cpp b/congctrl.cpp
deleted file mode 100644 (file)
index 451832d..0000000
+++ /dev/null
@@ -1,84 +0,0 @@
-/*
- *  congctrl.cpp
- *  p2tp
- *
- *  Created by Victor Grishchenko on 4/14/09.
- *  Copyright 2009 Delft Technical University. All rights reserved.
- *
- */
-#include "p2tp.h"
-#include <glog/logging.h>
-
-using namespace p2tp;
-
-CongestionControl::CongestionControl () {
-       state_ = SLOW_START_STATE;
-       cwnd_ = 1;
-       cainc_ = 0;
-       ssthresh_ = 100;
-       rtt_avg_ = 0;
-       dev_avg_ = 0;
-       peer_cwnd_ = 0;
-       data_ins_ = 0;
-       last_arrival_ = 0;
-       rate_ = TINT_1SEC/10;
-}
-
-
-void CongestionControl::RttSample (tint rtt) {
-    if (rtt_avg_>0) {
-        rtt_avg_ = (rtt_avg_*7 + rtt) >> 3; // affected by reordering
-        dev_avg_ = (dev_avg_*7 + ::abs(rtt-rtt_avg_)) >> 3;
-    } else {
-        rtt_avg_ = rtt;
-        dev_avg_ = rtt>>3;
-    }
-    DLOG(INFO)<<"sample "<<rtt<<" rtt "<<rtt_avg_<<" dev "<<dev_avg_;
-}
-
-
-void   CongestionControl::OnCongestionEvent (CongCtrlEvents ev) {
-       switch (ev) {
-               case LOSS_EV:   
-                       cwnd_ /= 2;
-                       state_ = CONG_AVOID_STATE;
-                       break;
-               case ACK_EV:
-                       if (state_==SLOW_START_STATE) {
-                               cwnd_++;
-                               if (cwnd_>=ssthresh_)
-                                       state_ = CONG_AVOID_STATE;
-                       } else if (state_==CONG_AVOID_STATE) {
-                               cainc_++;
-                               if (cainc_>=cwnd_) {
-                                       cainc_ = 0;
-                                       cwnd_++;
-                               }
-                       }
-                       break;
-               case DATA_EV:
-                       tint interarrival = last_arrival_ ? 
-                               Datagram::now - last_arrival_   :
-                               rtt_avg_;       // starting est. corresp. cwnd==1
-                       last_arrival_ = Datagram::now;
-                       if (rate_)
-                               rate_ = ( rate_*3 + interarrival ) / 4;
-                       else
-                               rate_ = interarrival;
-                       break;
-       }
-       DLOG(INFO)<<"peer irr "<<rate_<<" pcwnd "<<peer_cwnd();
-}
-
-int            CongestionControl::peer_cwnd () const {
-       if (!rate_)
-               return 0;
-       int pc = rtt_avg_ / rate_;
-       if ( rtt_avg_%rate_ > rate_/2 ) // too many /
-               pc++;
-       return pc;
-}
-
-int            CongestionControl::peer_bps () const {
-       return 1024 * TINT_1SEC / rate_;
-}
diff --git a/ledbat_controller.cpp b/ledbat_controller.cpp
new file mode 100644 (file)
index 0000000..87a705d
--- /dev/null
@@ -0,0 +1,35 @@
+/*
+ *  ledbat_controller.cpp
+ *  p2tp
+ *
+ *  Created by Victor Grishchenko on 10/6/09.
+ *  Copyright 2009 Delft Technical University. All rights reserved.
+ *
+ */
+
+#include "p2tp.h"
+
+using namespace p2tp;
+
+class LedbatController : public CongestionController {
+public:
+    /*tint    rtt_avg;
+    tint    dev_avg;
+    int     cwnd;
+    int     peer_cwnd;*/
+    virtual void OnTimeout () {
+    }
+    
+    virtual void OnDataSent(bin64_t b) {
+    }
+    
+    virtual void OnDataRecvd(bin64_t b) {
+    }
+    
+    virtual void OnAckRcvd(bin64_t b, tint peer_stamp) {
+    }
+    
+    virtual ~CongestionControl() {
+    }
+    
+};
diff --git a/p2tp.h b/p2tp.h
index 45a2ea6..a5ac644 100644 (file)
--- a/p2tp.h
+++ b/p2tp.h
@@ -17,14 +17,15 @@ Messages
  initial handshake packet also has the root hash
  (a HASH message).
  
- DATA          01, bin, buffer
+ DATA          01, bin_32, buffer
  1K of data.
  
- ACK           02, bin
+ ACK           02, bin_32
+ ACKTS      08, bin_32, timestamp_32
  Confirms successfull delivery of data. Used for
  congestion control, as well.
- HINT          03, bin
+  
+ HINT          03, bin_32
  Practical value of "hints" is to avoid overlap, mostly.
  Hints might be lost in the network or ignored.
  Peer might send out data without a hint.
@@ -33,153 +34,156 @@ Messages
  As peers cant pick randomly kilobyte here and there,
  they send out "long hints" for non-base bins.
  
- HASH          04, bin, sha1hash
+ HASH          04, bin_32, sha1hash
  SHA1 hash tree hashes for data verification. The
  connection to a fresh peer starts with bootstrapping
  him with peak hashes. Later, before sending out
  any data, a peer sends the necessary uncle hashes.
  
- PEX+/PEX-     05/06, ip, port
+ PEX+/PEX-     05/06, ipv4 addr, port
  Peer exchange messages; reports all connected and
  disconected peers. Might has special meaning (as
  in the case with swarm supervisors).
  
- --8X---- maybe (give2get)
- CRED          07, scrchid
- Communicated the public channel id at the sender
- side. Any grandchildren's credits will go to this
- channel.
- CRED1/2       08/09, ip, port, scrchid
- Second-step and trird-step credits.
- ACK1/2                10/11, scrchid, packets
- Grandchildren's acknowledgements of data being
- received from a child; the public channel id
- is mentioned.
 */
 #ifndef P2TP_H
 #define P2TP_H
 #include <stdint.h>
-#include <iostream>
 #include <vector>
 #include <deque>
-#include "bin.h"
-#include "sbit.h"
+#include "bin64.h"
+#include "bins.h"
 #include "datagram.h"
 #include "hashtree.h"
 
 namespace p2tp {
 
-       /* 64-bit time counter, microseconds since epoch
-       typedef int64_t tint;
-       static const tint TINT_1SEC = 1000000;
-    static const tint TINT_1MSEC = 1000;
-       static const tint TINT_INFINITY = 0x7fffffffffffffffULL;
-    */
-       struct tintbin {
-               tint time;
-               bin pos;
-               tintbin(tint t, bin p) : time(t), pos(p) {}
-       };
-       typedef std::deque<tintbin> tbinvec;
+    typedef std::pair<tint,bin64_t> tintbin;
+    
+       typedef std::deque<tintbin> tbqueue;
+    typedef std::deque<bin64_t> binqueue;
 
        typedef enum { 
                P2TP_HANDSHAKE = 0, 
                P2TP_DATA = 1, 
                P2TP_ACK = 2, 
+               P2TP_ACKTS = 8, 
                P2TP_HINT = 3, 
                P2TP_HASH = 4, 
                P2TP_PEX_ADD = 5,
                P2TP_PEX_RM = 6,
                P2TP_MESSAGE_COUNT = 7
        } messageid_t;
+    
+    class PiecePicker;
+    class CongestionController;
+    class PeerSelector;
 
        
-       struct  File {
-               
-               typedef enum {EMPTY,IN_PROGRESS,DONE} status_t;
-
-               static std::vector<File*> files;
+       class   FileTransfer {
                
-               /**     File descriptor. */
-               int                             fd;
-               /**     Whether the file is completely downloaded. */
-               status_t                status_;
-               
-               //      A map for all packets obtained and succesfully checked.
-               bins                    ack_out;
-               //      Hinted packets.
-               bins                    hint_out;
+    public:
                
-               HashTree                hashes;
-               //      History of bin retrieval.
-               bin::vec                history;
+        /** 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. */
+        bool            OfferData (bin64_t bin, uint8_t* data);
+        
+               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;
                
-               // TBD
-               uint64_t                options;
+               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:
 
-               /**     Submit a fresh file. */
-               File (int fd);
-               /**     Retrieve a file.        */
-               File (Sha1Hash hash, int fd);
-               /**     Placeholder. */
-               File () : fd(-1), hashes(Sha1Hash::ZERO) {}
+               static std::vector<FileTransfer*> files;
+        
+               /**     Open/submit/retrieve a file.    */
+               FileTransfer (Sha1Hash hash, char* file_name);
                /**     Close everything. */
-               ~File();
+               ~FileTransfer();
                
-               bool OfferHash (bin pos, const Sha1Hash& hash);
-               const Sha1Hash& root_hash () const { return hashes.root; }
-               size_t          size () const { return hashes.data_size()<<10; }
-               size_t          packet_size () const { return hashes.data_size(); }
-               status_t status () const { return status_; }
-               
-               static File* find (const Sha1Hash& hash);
-               static File* file (int fd) { return fd<files.size() ? files[fd] : NULL; }
+               /**     file descriptor. */
+               int                             fd;
+        /** File size, as derived from the hashes. */
+        size_t          sizek;
+               /**     Whether the file is completely downloaded. */
+               bool            complete;
+               /**     A map for all packets obtained and succesfully checked. */
+               bins                    ack_out;
+               /**     History of bin retrieval. */
+               binqueue                ack_history;
+        /** Piece picker strategy. */
+        PiecePicker*    picker;
+               /** File for keeping the Merkle hash tree. */
+        int             hashfd;
+        /** Merkle hash tree: root */
+        Sha1Hash*       root_hash;
+        /** Merkle hash tree: peak hashes */
+        Sha1Hash*       peak_hashes;
+        /** Merkle hash tree: the tree, as a bin64_t-indexed array */
+        Sha1Hash*       hashes;
+    protected:
+        void            Resize(size_t bytes);
 
-               friend int Open (const char* filename);
-               friend int Open (const Sha1Hash& hash, const char* filename);
-               friend void Close (int fdes);
                friend class Channel;
        };
        
        
        int             Open (const char* filename) ;
        int             Open (const Sha1Hash& root_hash, const char* filename); 
-       size_t  file_size (int fd);     
        void    Close (int fid) ;
+    void    Loop (tint till);
+    int     Bind (int port);
+    void    Shutdown (int fd);
+    void    HeardOfPeer (const Sha1Hash& root, struct sockaddr_in address);
        
 
-       struct CongestionControl {
-               typedef enum {SLOW_START_STATE,CONG_AVOID_STATE} stdtcpstate_t;
-               typedef enum { DATA_EV, ACK_EV, LOSS_EV } CongCtrlEvents;
-               
-               stdtcpstate_t state_;
-               tint rtt_avg_, dev_avg_, last_arrival_, rate_;
-               int cwnd_, cainc_, ssthresh_;
-               int peer_cwnd_, data_ins_;
-               
-               CongestionControl();
-               virtual ~CongestionControl() {}
-               virtual void    OnCongestionEvent (CongCtrlEvents ev);
-               void    RttSample (tint rtt);
-               
-               tint    avg_rtt() const { return rtt_avg_; }
-               tint    safe_avg_rtt() const { return avg_rtt() + avg_rtt_dev()*8; }
-               tint    avg_rtt_dev() const { return dev_avg_; }
-               int             cwnd () const {  return cwnd_; }
-               int             peer_cwnd () const;
-               int             peer_bps () const;
-               tint    data_in_rate () const { return rate_; }
+       class CongestionController {
+    public:
+        tint    rtt_avg;
+        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;
        };
-       
+    
+    class PiecePicker {
+    public:
+        virtual bin64_t Pick (bins& from, uint8_t layer) = 0;
+        virtual void    Received (bin64_t b) = 0;
+        virtual void    Snubbed (bin64_t b) = 0;
+    };
+    
+    class PeerSelector {
+    public:
+        virtual void PeerKnown (const Sha1Hash& root, struct sockaddr_in& addr) = 0;
+        virtual sockaddr_in GetPeer (const Sha1Hash& for_root) = 0;
+    };
        
 
-       /**     P2TP "control block". */
+       /**     P2TP channel's "control block"; channels loosely correspond to TCP
+        connections or FTP sessions; one channel is created for one file
+        being transferred between two peers. As we don't need buffers and
+        lots of other TCP stuff, sizeof(Channel+members) must be below 1K.
+        (There was a seductive idea to remove channels, just put the root
+        hash or a fragment of it into every datagram.) */
        class Channel {
-
        public:
                Channel (int filedes, int socket, struct sockaddr_in peer,
                                 uint32_t peer_channel, uint64_t supports=0);
@@ -187,15 +191,11 @@ namespace p2tp {
                
                static void     Recv (int socket);
                void            Recv (Datagram& dgram);
-               void            Send ();
-               void            SendSomething ();
+               tint            Send ();
+               tint            SendSomething ();
                void            SendHandshake ();
-               void            Tick ();
 
-               typedef enum {HS_REQ_OUT,HS_RES_OUT,HS_DONE} state_t;
-               File&           file () { return *File::files[fd]; }
-               
-               
+               FileTransfer&           file () { return *FileTransfer::files[fd]; }
        
                void            OnAck (Datagram& dgram);
                void            OnData (Datagram& dgram);
@@ -209,75 +209,57 @@ namespace p2tp {
                void            AddUncleHashes (Datagram& dgram, bin pos);
                void            AddPeakHashes (Datagram& dgram);
 
-               bin                     SenderPiecePick () ;
-               bin                     ReceiverPiecePick (int sizelim) ;
-               void            CleanStaleDataOut(bin ack_pos);
-               void            CleanStaleHintOut();
-               void            CleanStaleHintIn();
+               void            CleanStaleHints();
                
                state_t         state () const;
-               File::status_t  peer_status() const { return peer_status_; }
-               const CongestionControl& congestion_control() const { return cc_; }
 
                static int DecodeID(int scrambled);
                static int EncodeID(int unscrambled);
-               static void Loop (tint time);
-               static Channel* channel(int ind) {return ind<channels.size()?channels[ind]:NULL;}
-               static int MAX_REORDERING;
-               static tint TIMEOUT;
-               static std::vector<Channel*> channels;
-               static int* sockets_;
-               static int sock_count_;
-               static tint last_tick;
-               //static int socket;
+               static Channel* channel(int i) {return i<channels.size()?channels[i]:NULL;}
                
-               friend int Connect (int fd, int sock, 
+               /*friend int Connect (int fd, int sock, 
                                                        const struct sockaddr_in& addr, uint32_t peerch=0);
                friend int Init (int portno);
-               friend std::ostream& operator << (std::ostream& os, const Channel& s);
+               friend std::ostream& operator << (std::ostream& os, const Channel& s);*/
                
        private:
                
-               // index in the channel array
-               int                     id;
-               //      Socket address of the peer.
+               /** Channel id: index in the channel array. */
+               uint32_t        id;
+               /**     Socket address of the peer. */
                struct sockaddr_in      peer;
-               //      The UDP socket fd.
-               int                     socket_;
-               File::status_t  peer_status_;
-               //      Descriptor of the file in question
-               int                     fd;
-               //      Zero if we are trying to open a channel.
+               /**     The UDP socket fd. */
+               int                     socket;
+               /**     Descriptor of the file in question. */
+               FileTransfer*   file;
+               /**     Peer channel id; zero if we are trying to open a channel. */
                uint32_t        peer_channel_id;
-               //      Temporarily, a pointer to a composed/parsed datagram.
-               //Datagram*     dgram;
-               
-               //      Peer's retrieved pieces.
+               /**     Peer's progress, based on acknowledgements. */
                bins            ack_in;
-               //      Progress of piece acknowledgement to the peer.
-               bin                     data_in_;
+               /**     Last data received; needs to be acked immediately. */
+               bin64_t         data_in;
+        /** Index in the history array. */
                int                     ack_out;
-               //      Transmit schedule: in most cases filled with the peer's hints
-               tbinvec         hint_in;
-               //  Hints sent (to detect and reschedule ignored hints).
-               tbinvec         hint_out;
-               //      Uncle hash send pos; in the case both data and hashes do not fit
-               //      into a single datagram, we set this.
-               bin                     hash_out;
-               //      Send history: all cwnd pieces.
-               tbinvec         data_out; 
-               
-               CongestionControl       cc_;
-               tint            last_send_time;
+               /**     Transmit schedule: in most cases filled with the peer's hints */
+               tbqueue         hint_in;
+               /** Hints sent (to detect and reschedule ignored hints). */
+               tbqueue         hint_out;
+               /** The congestion control strategy. */
+               CongestionController    *cc;
+        /** For repeats. */
+               tint            last_send_time, last_recv_time;
+        
+        static PeerSelector* peer_selector;
+        
+               static int  MAX_REORDERING;
+               static tint TIMEOUT;
+               static std::vector<Channel*> channels;
+               static tint last_tick;
+        
        };
 
        
-       //int           connect (int fd, const struct sockaddr_in& addr, uint32_t peerch=0) ;
-       void    Loop (tint time=0) ;
-       int             Init (int portno) ;
-       void    Shutdown (int portno);
-       
-       uint32_t Width (const tbinvec& v);
+       //uint32_t Width (const tbinvec& v);
 }
 
 #define RETLOG(str) { LOG(WARNING)<<str; return; }
diff --git a/seq_picker.cpp b/seq_picker.cpp
new file mode 100644 (file)
index 0000000..4b20e01
--- /dev/null
@@ -0,0 +1,46 @@
+/*
+ *  seq_picker.cpp
+ *  p2tp
+ *
+ *  Created by Victor Grishchenko on 10/6/09.
+ *  Copyright 2009 Delft Technical University. All rights reserved.
+ *
+ */
+
+#include "p2tp.h"
+
+using namespace p2tp;
+
+class SeqPiecePicker : public PiecePicker {
+    
+    bins hint_out;
+    FileTransfer* file;
+    
+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;
+    }
+    
+    virtual void    Received (bin64_t b) {
+        diho.set(b,bins::FILLED);
+    }
+    
+    virtual void    Snubbed (bin64_t b) {
+        diho.set(b,bins::EMPTY);
+    }
+    
+};
\ No newline at end of file
diff --git a/simple_selector.cpp b/simple_selector.cpp
new file mode 100644 (file)
index 0000000..e0d54e8
--- /dev/null
@@ -0,0 +1,34 @@
+/*
+ *  simple_selector.cpp
+ *  p2tp
+ *
+ *  Created by Victor Grishchenko on 10/6/09.
+ *  Copyright 2009 Delft Technical University. All rights reserved.
+ *
+ */
+
+#include "p2tp.h"
+
+using namespace p2tp;
+
+class SimpleSelector : public PeerSelector {
+    typedef std::pair<sockaddr_in,uint32_t> memo_t;
+    std::queue<memo_t>  peers;
+public:
+    virtual void PeerKnown (const Sha1Hash& root, struct sockaddr_in& addr) {
+        peers.push_front(memo_t(addr,root.fingerprint()));
+    }
+    virtual sockaddr_in GetPeer (const Sha1Hash& for_root) {
+        uint32_t fp = for_root.fingerprint();
+        for(std::queue<memo_t>::iterator i=peers.begin(); i!=peers.end(); i++)
+            if (i->second==fp) {
+                i->second = 0;
+                sockaddr_in ret = i->first;
+                while (peers.begin()->second==0)
+                    peers.pop_front();
+                return ret;
+            }
+    }
+};
+
+static Channel::peer_selector = new SimpleSelector();
\ No newline at end of file
index 9bce647..8a64f7f 100644 (file)
@@ -42,6 +42,13 @@ env.Program(
     LIBS=['p2tp','stdc++','gtest','glog','crypto'],
     LIBPATH='..' )
 
+env.Program( 
+    target='freemap',
+    source=['freemap.cpp'],
+    CPPPATH=['..'],
+    LIBS=['p2tp','stdc++','gtest','glog'],
+    LIBPATH='..' )
+
 env.Program( 
     target='bin64test',
     source=['bin64test.cpp','../bin64.h'],
diff --git a/tests/freemap.cpp b/tests/freemap.cpp
new file mode 100755 (executable)
index 0000000..f98b33a
--- /dev/null
@@ -0,0 +1,74 @@
+/*
+ *  binstest.cpp
+ *  serp++
+ *
+ *  Created by Victor Grishchenko on 3/22/09.
+ *  Copyright 2009 Delft Technical University. All rights reserved.
+ *
+ */
+#include <time.h>
+#include <gtest/gtest.h>
+#include <set>
+#include "bins.h"
+
+int bins_stripe_count (bins& b) {
+    int stripe_count;
+    uint64_t * stripes = b.get_stripes(stripe_count);
+    free(stripes);
+    return stripe_count;
+}
+
+uint8_t rand_norm (uint8_t lim) {
+    long rnd = random() & ((1<<lim)-1);
+    uint8_t bits = 0;
+    while (rnd) {
+        bits += rnd&1;
+        rnd >>= 1;
+    }
+    return bits;
+}
+
+TEST(FreemapTest,Freemap) {
+    bins space;
+    const bin64_t top(30,0);
+    space.set(top,bins::EMPTY);
+    typedef std::pair<int,bin64_t> timebin_t;
+    typedef std::set<timebin_t> ts_t;
+    ts_t to_free;
+    for (int t=0; t<1000000; t++) {
+        if (t<500000 || t>504000) {
+            uint8_t lr = rand_norm(28);
+            bin64_t alloc = space.find(top,lr);
+            ASSERT_NE(0ULL,~alloc);
+            EXPECT_EQ(bins::EMPTY, space.get(alloc));
+            space.set(alloc,bins::FILLED);
+            long dealloc_time = 1<<rand_norm(22);
+            printf("alloc 2**%i starting at %lli for %li ticks\n",
+                (int)lr,(uint64_t)alloc,dealloc_time);
+            dealloc_time += t;
+            to_free.insert(timebin_t(dealloc_time,alloc));
+        }
+        // now, the red-black tree
+        while (to_free.begin()->first<=t) {
+            bin64_t freebin = to_free.begin()->second;
+            to_free.erase(to_free.begin());
+            space.set(freebin,bins::EMPTY);
+            printf("freed at %lli\n",
+                (uint64_t)freebin);
+       }
+        // log: space taken, gaps, binmap cells, tree cells
+        int cells = space.size();
+        int intervals = bins_stripe_count(space);
+        printf("time %i cells used %i intervals %i blocks %i\n",
+                t,cells,intervals,(int)to_free.size());
+        //space.dump("space");
+    }
+    for(ts_t::iterator i=to_free.begin(); i!=to_free.end(); i++)
+        space.set(i->second,bins::EMPTY);
+    EXPECT_EQ(bins::EMPTY,space.get(top));
+}
+
+int main (int argc, char** argv) {
+       testing::InitGoogleTest(&argc, argv);
+       return RUN_ALL_TESTS();
+}
diff --git a/transfer.cpp b/transfer.cpp
new file mode 100644 (file)
index 0000000..f268f84
--- /dev/null
@@ -0,0 +1,188 @@
+/*
+ *  transfer.cpp
+ *  p2tp
+ *
+ *  Created by Victor Grishchenko on 10/6/09.
+ *  Copyright 2009 Delft Technical University. All rights reserved.
+ *
+ */
+
+#include "p2tp.h"
+
+
+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);
+}
+
+
+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;
+}
+
+
+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;
+}
+
+
+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;
+}
+
+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;
+}
+
+
+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;
+}
+
+/** 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
+    }
+    
+}
+
+bool FileTransfer::acceptData (uint64_t bin, uint8_t* data, size_t len) {
+}
+
+
+HashTree::~HashTree () {
+       close(fd);
+}
+
+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;
+       
+}
+
+
+