Add files for swift over UDP.
[swifty.git] / src / libswift_udp / ext / vod_picker.cpp
diff --git a/src/libswift_udp/ext/vod_picker.cpp b/src/libswift_udp/ext/vod_picker.cpp
new file mode 100644 (file)
index 0000000..690b4dc
--- /dev/null
@@ -0,0 +1,302 @@
+/*
+ *  vod_picker.cpp
+ *  swift
+ *
+ *  Created by Riccardo Petrocco.
+ *  Copyright 2009-2012 Delft University of Technology. All rights reserved.
+ *
+ */
+
+#include "swift.h"
+#include <cassert>
+
+using namespace swift;
+
+#define HIGHPRIORITYWINDOW 45000;      // initial high priority window in bin unit
+#define MIDPRIORITYWINDOW 4;           // proportion of the mid priority window compared to the high pri. one
+
+/** Picks pieces in VoD fashion. The stream is divided in three priority
+ *  sets based on the current playback position. In the high priority set
+ *  bins are selected in order, while on the medium and low priority sets
+ *  in a rarest fist fashion */
+class VodPiecePicker : public PiecePicker {
+
+    binmap_t        ack_hint_out_;
+    tbqueue         hint_out_;
+    FileTransfer*   transfer_;
+    Availability*      avail_;
+    uint64_t        twist_;
+    bin_t           range_;
+    int                                playback_pos_;          // playback position in KB
+    int                                high_pri_window_;
+
+public:
+
+    VodPiecePicker (FileTransfer* file_to_pick_from) : ack_hint_out_(),
+           transfer_(file_to_pick_from), twist_(0), range_(bin_t::ALL)
+    {
+       avail_ = &(transfer_->availability());
+        binmap_t::copy(ack_hint_out_, file().ack_out());
+        playback_pos_ = -1;
+        high_pri_window_ = HIGHPRIORITYWINDOW;
+    }
+
+    virtual ~VodPiecePicker() {}
+
+    HashTree& file() {
+        return transfer_->file();
+    }
+
+    virtual void Randomize (uint64_t twist) {
+       // Arno, 2012-03-21: After consulting with Riccardo, disable
+       // twisting for VOD PP. Randomization of peers over the bitmap
+       // is already guaranteed by the ack_hint_out_ (prevents double requesting)
+       // and rarest first.
+    }
+
+    virtual void LimitRange (bin_t range) {
+        range_ = range;
+    }
+
+
+    bin_t getTopBin(bin_t bin, uint64_t start, uint64_t size)
+    {
+       while (bin.parent().base_length() <= size && bin.parent().base_left() >= bin_t(start))
+               {
+                       bin.to_parent();
+               }
+       return bin;
+    }
+
+
+    bin_t pickUrgent (binmap_t& offer, uint64_t max_width, uint64_t size) {
+
+       bin_t curr = bin_t((playback_pos_+1)<<1); // the base bin will be indexed by the double of the value (bin(4) == bin(0,2))
+       bin_t hint = bin_t::NONE;
+       uint64_t examined = 0;
+               binmap_t binmap;
+
+       // report the first bin we find
+       while (hint.is_none() && examined < size)
+       {
+               curr = getTopBin(curr, (playback_pos_+1)<<1, size-examined);
+               if (!ack_hint_out_.is_filled(curr))
+               {
+                       binmap.fill(offer);
+                               binmap_t::copy(binmap, ack_hint_out_, curr);
+                               hint = binmap_t::find_complement(binmap, offer, twist_);
+                               binmap.clear();
+               }
+               examined += curr.base_length();
+               curr = bin_t(0, curr.base_right().layer_offset()+1 );
+       }
+
+       if (!hint.is_none())
+               while (hint.base_length()>max_width && !hint.is_base()) // Arno,2012-01-17: stop!
+               hint.to_left();
+
+       return hint;
+    }
+
+
+    bin_t pickRarest (binmap_t& offer, uint64_t max_width, uint64_t start, uint64_t size) {
+
+       //fprintf(stderr,"%s #1 Picker -> choosing from mid/low priority \n",tintstr());
+       bin_t curr = bin_t(start<<1);
+               bin_t hint = bin_t::NONE;
+               uint64_t examined = 0;
+               //uint64_t size = end-start;
+               bin_t rarest_hint = bin_t::NONE;
+               // TODO remove..
+               binmap_t binmap;
+
+               // TODO.. this is the dummy version... put some logic in deciding what to DL
+               while (examined < size)
+               {
+                       curr = getTopBin(curr, start<<1, size-examined);
+
+                       if (!ack_hint_out_.is_filled(curr))
+                       {
+                               // remove
+                               //binmap_t::copy(binmap, offer);
+                               //binmap.reset(curr);
+
+                               binmap.fill(offer);
+                               binmap_t::copy(binmap, ack_hint_out_, curr);
+                               //hint = binmap_t::find_complement(ack_hint_out_, offer, curr, twist_);
+                               hint = binmap_t::find_complement(binmap, offer, twist_);
+                               binmap.clear();
+
+                               if (!hint.is_none())
+                               {
+                                       if (avail_->size())
+                                       {
+                                               rarest_hint = avail_->get(rarest_hint) < avail_->get(hint) ? rarest_hint : hint;
+                                       }
+                                       else
+                                       {
+                                               examined = size;
+                                               rarest_hint = hint;
+                                       }
+                               }
+                       }
+
+                       examined += curr.base_length();
+                       curr = bin_t(0, curr.base_right().layer_offset()+1 );
+
+               }
+
+               if (!rarest_hint.is_none())
+               {
+                       if (avail_->size())
+                               rarest_hint = avail_->getRarest(rarest_hint, max_width);
+                       else
+                               while (rarest_hint.base_length()>max_width && !rarest_hint.is_base()) // Arno,2012-01-17: stop!
+                                       rarest_hint.to_left();
+               }
+
+               return rarest_hint;
+    }
+
+
+    virtual bin_t Pick (binmap_t& offer, uint64_t max_width, tint expires)
+    {
+       bin_t hint;
+       bool retry;
+        char tmp[32];
+        char set = 'X';        // TODO remove set var, only used for debug
+
+       // TODO check... the seconds should depend on previous speed of the peer
+        while (hint_out_.size() && hint_out_.front().time<NOW-TINT_SEC*3/2) { // FIXME sec
+            binmap_t::copy(ack_hint_out_, file().ack_out(), hint_out_.front().bin);
+            hint_out_.pop_front();
+        }
+
+        // get the first piece to estimate the size, whoever sends it first
+        if (!file().size()) {
+
+            return bin_t(0,0);
+        }
+
+        do {
+               uint64_t max_size = file().size_in_chunks() - playback_pos_ - 1;
+               max_size = high_pri_window_ < max_size ? high_pri_window_ : max_size;
+
+                       // check the high priority window for data we r missing
+                       hint = pickUrgent(offer, max_width, max_size);
+
+                       // check the mid priority window
+                       uint64_t start = (1 + playback_pos_) + HIGHPRIORITYWINDOW;      // start in KB
+                       if (hint.is_none() && start < file().size_in_chunks())
+                       {
+                               int mid = MIDPRIORITYWINDOW;
+                               int size = mid * HIGHPRIORITYWINDOW;                                            // size of window in KB
+                               // check boundaries
+                               max_size = file().size_in_chunks() - start;
+                               max_size = size < max_size ? size : max_size;
+
+                               hint = pickRarest(offer, max_width, start, max_size);
+
+                               //check low priority
+                               start += max_size;
+                               if (hint.is_none() && start < file().size_in_chunks())
+                               {
+                                       size = file().size_in_chunks() - start;
+                                       hint = pickRarest(offer, max_width, start, size);
+                                       set = 'L';
+                               }
+                               else
+                                       set = 'M';
+                       }
+                       else
+                               set = 'H';
+
+                       // unhinted/late data
+                       if (!file().ack_out().is_empty(hint)) {
+                               binmap_t::copy(ack_hint_out_, file().ack_out(), hint);
+                               retry = true;
+                       }
+                       else
+                               retry = false;
+
+        } while (retry);
+
+
+        if (hint.is_none()) {
+               // TODO, control if we want: check for missing hints (before playback pos.)
+               hint = binmap_t::find_complement(ack_hint_out_, offer, twist_);
+               // TODO: end-game mode
+               if (hint.is_none())
+                       return hint;
+               else
+                               while (hint.base_length()>max_width && !hint.is_base()) // Arno,2012-01-17: stop!
+                                       hint.to_left();
+
+
+        }
+
+        assert(ack_hint_out_.is_empty(hint));
+        ack_hint_out_.set(hint);
+        hint_out_.push_back(tintbin(NOW,hint));
+
+
+        // TODO clean ... printing percentage of completeness for the priority sets
+        //status();
+
+        //fprintf(stderr,"%s #1 Picker -> picked %s\t from %c set\t max width %lu \n",tintstr(), hint.str(tmp), set, max_width );
+        //if (avail_->size())
+        return hint;
+    }
+
+
+    void updatePlaybackPos(int size = 1)
+    {
+       assert(size>-1);
+       if (size< file().size_in_chunks() - playback_pos_ - 2)
+               playback_pos_ += size;
+    }
+
+
+    void status()
+       {
+               int t = 0;
+               int x = HIGHPRIORITYWINDOW;
+               int y = MIDPRIORITYWINDOW;
+               int i = playback_pos_ + 1;
+               int end_high = (x+playback_pos_)<<1;
+               int end_mid = ((x*y)+x+playback_pos_)<<1;
+               int total = 0;
+
+
+               while (i<=end_high)
+               {
+                       if (!file().ack_out().is_empty(bin_t(i)))
+                               t++;
+                       i++;
+               }
+               total = t;
+               t = t*100/((x<<1)-1);
+               fprintf(stderr, "low %u, ", t);
+               t = 0;
+               while (i<=end_mid)
+               {
+                       if (!file().ack_out().is_empty(bin_t(i)))
+                               t++;
+                       i++;
+               }
+               total += t;
+               t = t*100/((x*y)<<1);
+               fprintf(stderr, "mid %u, ", t);
+               t = 0;
+               while (i<=file().size_in_chunks()<<1)
+               {
+                       if (!file().ack_out().is_empty(bin_t(i)))
+                               t++;
+                       i++;
+               }
+               total += t;
+               t = t*100/((file().size_in_chunks()-(x*y+playback_pos_))<<1);
+               fprintf(stderr, "low %u  -> in total: %i\t pp: %i\n", t, total, playback_pos_);
+       }
+
+};