+/*
+ * bin64.h
+ * bin numbers (binaty tree enumeration/navigation)
+ *
+ * Created by Victor Grishchenko on ??/09/09 in Karlovy Vary
+ * Copyright 2009 Delft University of Technology. All rights reserved.
+ *
+ */
#ifndef BIN64_H
#define BIN64_H
#include <assert.h>
Bin numbers in the tail111 encoding: meaningless
bits in the tail are set to 0111...11, while the
head denotes the offset. Thus, 1101 is the bin
- at layer 1, offset 3 (i.e. fourth). */
+ at layer 1, offset 3 (i.e. fourth).
+ Obviously, bins form a binary tree. All navigation
+ is made in terms of binary trees: left, right,
+ sibling, parent, etc.
+ */
struct bin64_t {
uint64_t v;
static const uint64_t NONE;
return (tail_bits()+1)>>1;
}
+ /** Get the sibling interval in the binary tree. */
bin64_t sibling () const {
// if (v==ALL) return NONE;
return bin64_t(v^(tail_bit()<<1));
return r;
}
+ /** Get the bin's offset in base units, i.e. 4 for (1,2). */
uint64_t base_offset () const {
return (v&~(tail_bits()))>>1;
}
+ /** Get the bin's offset at its own layer, e.g. 2 for (1,2). */
uint64_t offset () const {
return v >> (layer()+1);
}
+ /** Get a child bin; either right(true) or left(false). */
bin64_t to (bool right) const {
if (!(v&1))
return NONE;
return bin64_t(v^tb);
}
+ /** Get the left child bin. */
bin64_t left () const {
return to(false);
}
+ /** Get the right child bin. */
bin64_t right () const {
return to(true);
}
+ /** Check whether this bin is within the specified bin. */
bool within (bin64_t maybe_asc) {
if (maybe_asc==bin64_t::NONE)
return false;
return right();
}
+ /** Twist/untwist a bin number according to the mask. */
bin64_t twisted (uint64_t mask) const {
return bin64_t( v ^ ((mask<<1)&~tail_bits()) );
}
+ /** Get the paretn bin. */
bin64_t parent () const {
uint64_t tbs = tail_bits(), ntbs = (tbs+1)|tbs;
return bin64_t( (v&~ntbs) | tbs );
}
+ /** Check whether this bin is the left sibling. */
bool is_left () const {
uint64_t tb = tail_bit();
return !(v&(tb<<1));
}
+
+ /** Check whether this bin is the right sibling. */
bool is_right() const { return !is_left(); }
+ /** Get the leftmost basic bin within this bin. */
bin64_t left_foot () const {
if (v==NONE)
return NONE;
/** Depth-first in-order binary tree traversal. */
bin64_t next_dfsio (uint8_t floor);
+ /** Return the number of basci bins within this bin. */
bin64_t width () const {
return (tail_bits()+1)>>1;
}
+ /** Get the standard-form null-terminated string
+ representation of this bin, e.g. "(2,1)".
+ The string is statically allocated, must
+ not be reused or released. */
const char* str () const;
/** The array must have 64 cells, as it is the max
/*
* sbit.cpp
- * serp++
+ * binmap, a hybrid of bitmap and binary tree
*
* Created by Victor Grishchenko on 3/28/09.
* Copyright 2009 Delft University of Technology. All rights reserved.
#define BINS_H
#include "bin64.h"
-/** A binmap covering 2^64 range. Complexity limit: 100+200LoC */
+/** A binmap covering 2^64 range. Binmap is a hybrid of a bitmap (aka
+ bit vector) and a binary tree. The key ability of a binmap is
+ the aggregation of solid (all-0 or all-1) ranges. */
class bins {
public:
+ /** Need a 3-valued logic as a range might be either all-0 or all-1
+ or some mix. In fact, any value different from 0x0 or 0xffff
+ must be interpreted as MIXED.
+ All read/write operations on a binmap are made in terms of
+ aligned binary intervals (bins).
+ */
typedef enum { FILLED=0xffff, EMPTY=0x0000, MIXED=0x5555 } fill_t;
static const int NOJOIN;
bins();
+ /** Copying constructor. */
bins(const bins& b);
+ /** Get value for the bin. */
uint16_t get (bin64_t bin);
+ /** Set value for the bin. */
void set (bin64_t bin, fill_t val=FILLED);
+ /** Copy a range from another binmap. */
void copy_range (bins& origin, bin64_t range);
+ /** Find the leftmost bin within the specified range which is
+ either filled or empty. */
bin64_t find (const bin64_t range, fill_t seek=EMPTY) ;
+ /** Find the leftmost bin within the specified range which is
+ either filled or empty. Bins set to 1 in the filter binmap cannot
+ be returned. In fact, this is an incremental bitwise op. */
bin64_t find_filtered
(bins& filter, bin64_t range, fill_t seek=EMPTY) ;
- void remove (bins& b);
+ /** Bitwise SUB; any bins set to one in the filter binmap should
+ be set to 0 in this binmap. */
+ void remove (bins& filter);
void dump(const char* note);
+ /** Represent the binmap as a sequence of 0 and 1 stripes; for each
+ new stripe only the starting offset is given. The first stripe
+ is supposed to be empty (if the (0,0) bin is actually filled,
+ the next stripe will also start at 0). */
uint64_t* get_stripes (int& count);
+ /** Return the number of cells allocated in the binmap. */
uint32_t size() { return cells_allocated; }
uint64_t seq_length ();
+ /** Return the topmost solid bin which covers the specified bin. */
bin64_t cover(bin64_t val);
uint64_t mass ();
+ /** Return true if the range is solid (either all-0 or 1). If val is
+ specified, the interval must be both solid and filled/empty,
+ depending on the value. */
bool is_solid (bin64_t range=bin64_t::ALL, fill_t val=MIXED) ;
+ /** Whether range/bin is empty. */
bool is_empty (bin64_t range=bin64_t::ALL) { return is_solid(range,EMPTY); }
+ /** Whether range/bin is filled. */
bool is_filled (bin64_t range=bin64_t::ALL) { return is_solid(range,FILLED); }
+ /** Clear everything, empty all bins. */
void clear ();
+ /** Returns whether the int is mixed (not all-1 or all-0). */
static bool is_mixed (uint16_t val) { return val!=EMPTY && val!=FILLED; }
+ /** Returns whether the int is solid (0x0 or 0xffff). */
static bool is_solid (uint16_t val) { return val==EMPTY || val==FILLED; }
+ /** Twisting is some generalization of XOR. For every 1-bit in the mask,
+ the respective layer of the binary tree is flipped, i.e. left and
+ right change places. Twisting is mostly needed for randomization. */
void twist (uint64_t mask);
private:
/*
* compat.h
- * p2tp
+ * compatibility wrappers
*
* Created by Arno Bakker, Victor Grishchenko
* Copyright 2009 Delft University of Technology. All rights reserved.
namespace p2tp {
+/** tint is the time integer type; microsecond-precise. */
typedef int64_t tint;
#define TINT_HOUR ((tint)1000000*60*60)
#define TINT_MIN ((tint)1000000*60)
/*
* datagram.h
- * serp++
+ * nice IPv4 UDP wrappers
*
* Created by Victor Grishchenko, Arno Bakker on 3/9/09.
* Copyright 2009 Delft University of Technology. All rights reserved.
#endif
+/** IPv4 address, just a nice wrapping around struct sockaddr_in. */
struct Address {
struct sockaddr_in addr;
static uint32_t LOCALHOST;
};
-struct Datagram {
+/** UDP datagram class, a nice wrapping around sendto/recvfrom/select.
+ Reading/writing from/to a datagram is done in a FIFO (deque) fashion:
+ written data is appended to the tail (push) while read data is
+ taken from the "head" of the buffer. */
+class Datagram {
Address addr;
SOCKET sock;
int offset, length;
uint8_t buf[MAXDGRAMSZ*2];
+public:
+
+ /** bind to the address */
static SOCKET Bind(Address address);
+ /** close the port */
static void Close(int port);
+ /** the current time */
static tint Time();
+ /** wait till one of the sockets has some io to do; usec is the timeout */
static SOCKET Wait (int sockcnt, SOCKET* sockets, tint usec=0);
static tint now, epoch, start;
static uint64_t dgrams_up, dgrams_down, bytes_up, bytes_down;
+ /** This constructor is normally used to SEND something to the address. */
Datagram (SOCKET socket, const Address addr_) : addr(addr_), offset(0),
length(0), sock(socket) {}
+ /** This constructor is normally used to RECEIVE something at the socket. */
Datagram (SOCKET socket) : offset(0), length(0), sock(socket) {
}
+ /** space remaining */
int space () const { return MAXDGRAMSZ-length; }
+ /** size of the data (not counting UDP etc headers) */
int size() const { return length-offset; }
std::string str() const { return std::string((char*)buf+offset,size()); }
const uint8_t* operator * () const { return buf+offset; }
-
+ const Address& address () const { return addr; }
+ /** Append some data at the back */
int Push (const uint8_t* data, int l) { // scatter-gather one day
int toc = l<space() ? l : space();
memcpy(buf+length,data,toc);
length += toc;
return toc;
}
+ /** Read something from the front of the datagram */
int Pull (uint8_t** data, int l) {
int toc = l<size() ? l : size();
//memcpy(data,buf+offset,toc);
int Send ();
int Recv ();
- const Address& address() const { return addr; }
+
void Clear() { offset=length=0; }
void PushString (std::string str) {
/*
* hashtree.h
- * serp++
+ * hashing, Merkle hash trees and data integrity
*
* Created by Victor Grishchenko on 3/6/09.
* Copyright 2009 Delft University of Technology. All rights reserved.
namespace p2tp {
+/** SHA-1 hash, 20 bytes of data */
struct Sha1Hash {
uint8_t bits[20];
Sha1Hash() { memset(bits,0,20); }
+ /** Make a hash of two hashes (for building Merkle hash trees). */
Sha1Hash(const Sha1Hash& left, const Sha1Hash& right);
+ /** Hash an old plain string. */
Sha1Hash(const char* str, size_t length=-1);
Sha1Hash(const uint8_t* data, size_t length);
+ /** Either parse hash from hex representation of read in raw format. */
Sha1Hash(bool hex, const char* hash);
std::string hex() const;
};
+/** This class controls data integrity of some file; hash tree is put to
+ an auxilliary file next to it. The hash tree file is mmap'd for
+ performance reasons. Actually, I'd like the data file itself to be
+ mmap'd, but 32-bit platforms do not allow that for bigger files.
+
+ There are two variants of the general workflow: either a HashTree
+ is initialized with a root hash and the rest of hashes and data is
+ spoon-fed later, OR a HashTree is initialized with a data file, so
+ the hash tree is derived, including the root hash.
+ */
class HashTree {
/** Merkle hash tree: root */
/** Offer data; the behavior is the same as with a hash:
accept or remember or drop. Returns true => ACK is sent. */
bool OfferData (bin64_t bin, const char* data, size_t length);
- /** Not implemented yet. */
+ /** For live streaming. Not implemented yet. */
int AppendData (char* data, int length) ;
int file_descriptor () const { return fd_; }
+ /** Returns the number of peaks (read on peak hashes). */
int peak_count () const { return peak_count_; }
+ /** Returns the i-th peak's bin number. */
bin64_t peak (int i) const { return peaks_[i]; }
+ /** Returns peak hash #i. */
const Sha1Hash& peak_hash (int i) const { return peak_hashes_[i]; }
+ /** Return the peak bin the given bin belongs to. */
bin64_t peak_for (bin64_t pos) const;
+ /** Return a (Merkle) hash for the given bin. */
const Sha1Hash& hash (bin64_t pos) const {return hashes_[pos];}
+ /** Give the root hash, which is effectively an identifier of this file. */
const Sha1Hash& root_hash () const { return root_hash_; }
+ /** Get file size, in bytes. */
uint64_t size () const { return size_; }
+ /** Get file size in packets (in kilobytes, rounded up). */
uint64_t packet_size () const { return sizek_; }
+ /** Number of bytes retrieved and checked. */
uint64_t complete () const { return complete_; }
+ /** Number of packets retrieved and checked. */
uint64_t packets_complete () const { return completek_; }
+ /** The number of bytes completed sequentially, i.e. from the beginning of
+ the file, uninterrupted. */
uint64_t seq_complete () ;
+ /** Whether the file is complete. */
bool is_complete ()
{ return size_ && complete_==size_; }
+ /** The binmap of complete packets. */
bins& ack_out () { return ack_out_; }
~HashTree ();
/*
* p2tp.h
- * serp++
+ * the main header file for libswift, normally you should only read this one
*
* Created by Victor Grishchenko on 3/6/09.
* Copyright 2009 Delft University of Technology. All rights reserved.
namespace p2tp {
#define NOW Datagram::now
+
+ /** tintbin is basically a pair<tint,bin64_t> plus some nice operators.
+ Most frequently used in different queues (acknowledgements, requests,
+ etc). */
struct tintbin {
tint time;
bin64_t bin;
typedef std::deque<bin64_t> binqueue;
typedef Address Address;
+ /** A heap (priority queue) for timestamped bin numbers (tintbins). */
class tbheap {
tbqueue data_;
public:
}
};
+ /** swift protocol message types; these are used on the wire. */
typedef enum {
P2TP_HANDSHAKE = 0,
P2TP_DATA = 1,
class PeerSelector;
+ /** A class representing single file transfer. */
class FileTransfer {
public:
/*
* send_control.cpp
- * p2tp
+ * congestion control logic for the swift protocol
*
* Created by Victor Grishchenko on 12/10/09.
* Copyright 2009 Delft University of Technology. All rights reserved.
/*
- * datasendrecv.cpp
- * serp++
+ * sendrecv.cpp
+ * most of the swift's state machine
*
* Created by Victor Grishchenko on 3/6/09.
* Copyright 2009 Delft University of Technology. All rights reserved.
Channel* Channel::RecvDatagram (int socket) {
Datagram data(socket);
data.Recv();
- Address& addr = data.addr;
+ const Address& addr = data.address();
#define return_log(...) { eprintf(__VA_ARGS__); return NULL; }
if (data.size()<4)
return_log("datagram shorter than 4 bytes %s\n",addr.str());
return_log ("hash %s unknown, no such file %s\n",hash.hex().c_str(),addr.str());
dprintf("%s #0 -hash ALL %s\n",tintstr(),hash.hex().c_str());
for(binqueue::iterator i=file->hs_in_.begin(); i!=file->hs_in_.end(); i++)
- if (channels[*i] && channels[*i]->peer_==data.addr &&
+ if (channels[*i] && channels[*i]->peer_==data.address() &&
channels[*i]->last_recv_time_>NOW-TINT_SEC*2)
return_log("have a channel already to %s\n",addr.str());
channel = new Channel(file, socket, data.address());
+// licensed under the GPL v2 as part of the git project http://git-scm.com/
/*
* SHA1 routine optimized to do word accesses rather than byte accesses,
* and to avoid unnecessary copies into the context array.
+// licensed under the GPL v2 as part of the git project http://git-scm.com/
/*
* SHA1 routine optimized to do word accesses rather than byte accesses,
* and to avoid unnecessary copies into the context array.
/*
* transfer.cpp
- * p2tp
+ * some transfer-scope code
*
* Created by Victor Grishchenko on 10/6/09.
* Copyright 2009 Delft University of Technology. All rights reserved.