--- /dev/null
+/*
+ * 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;
+}
#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
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) {}
}
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) ;
+
};
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++) {
}
-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();
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
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);
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 {
--- /dev/null
+/*
+ * 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
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
}*/
-Sha1Hash HashTree::deriveRoot () {
+/*Sha1Hash HashTree::deriveRoot () {
int i = peaks.size()-1;
bin p = peaks[i].first;
Sha1Hash hash = peaks[i].second;
}
-
+*/
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 {
const std::vector<binhash>& peak_hashes() const { return peaks; }
-};
+};*/
#endif
#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);
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. */
/** 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);
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 {
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;
};
tint SendSomething ();
void SendHandshake ();
- FileTransfer& file () { return *FileTransfer::files[fd]; }
-
void OnAck (Datagram& dgram);
void OnData (Datagram& dgram);
void OnHint (Datagram& dgram);
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;}
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()) {
#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));
}
-TEST(BinTest,Navigation) {
+TEST(Bin64Test,Navigation) {
bin64_t mid(4,18);
EXPECT_EQ(bin64_t(5,9),mid.parent());
}
-TEST(BinTest,Overflows) {
+TEST(Bin64Test,Overflows) {
/*EXPECT_EQ(bin64_t::NONE.parent(),bin64_t::NONE);
EXPECT_EQ(bin64_t::NONE.left(),bin64_t::NONE);
*/
}
+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);
* 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);
+ }
+
+ */