]> git.sesse.net Git - greproxy/blobdiff - rsencoder.cpp
Add FEC.
[greproxy] / rsencoder.cpp
diff --git a/rsencoder.cpp b/rsencoder.cpp
new file mode 100644 (file)
index 0000000..3026dd4
--- /dev/null
@@ -0,0 +1,92 @@
+#include <string.h>
+#include <arpa/inet.h>
+#include <netinet/in.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/select.h>
+#include <sys/socket.h>
+extern "C" {
+#include <fec.h>
+}
+
+#include "reorderer.h"
+#include "rsencoder.h"
+#include "rs_parm.h"
+
+#include <algorithm>
+
+using namespace std;
+
+void RSEncoder::send_packet(uint16_t proto, const std::string &data, int incoming_seq)
+{
+       if (!packet_history.empty() &&
+           incoming_seq <= packet_history.back().seq) {
+               // Reorderer should have done this for us.
+               return;
+       }
+       if (!packet_history.empty() &&
+           incoming_seq / RS_PAYLOAD_SIZE !=
+               packet_history.back().seq / RS_PAYLOAD_SIZE) {
+               // Received an unfinished group.
+               packet_history.clear();
+       }
+       sender->send_packet(proto, data, incoming_seq);
+       packet_history.emplace_back(GREPacket{incoming_seq, proto, data});
+       if (packet_history.size() == RS_PAYLOAD_SIZE) {
+               finish_group();
+       }
+}
+
+void RSEncoder::finish_group()
+{
+       // Our RS packets need to have the same max length as the longest one.
+       int max_length = 0;
+       for (int i = 0; i < packet_history.size(); ++i) {
+               max_length = max<int>(max_length, packet_history[i].data.size());
+       }
+
+       vector<string> padded_packets;
+       for (int i = 0; i < packet_history.size(); ++i) {
+               string p;
+               p.resize(max_length + 4);
+               memset(&p[0], 0, max_length + 4);
+               uint16_t proto_be = htons(packet_history[i].proto);
+               memcpy(&p[0], &proto_be, sizeof(uint16_t));
+               uint16_t len_be = htons(packet_history[i].data.size());
+               memcpy(&p[2], &len_be, sizeof(uint16_t));
+               memcpy(&p[4], packet_history[i].data.data(), packet_history[i].data.size());
+               padded_packets.push_back(p);
+       }
+
+       // Now construct RS packets.
+       vector<string> rs_packets;
+       for (int i = 0; i < RS_PARITY_SIZE; ++i) {
+               string p;
+               p.resize(max_length + 4);
+               memset(&p[0], 0, max_length + 4);
+               rs_packets.push_back(p);
+       }
+       string data, parity;
+       data.resize(RS_PAYLOAD_SIZE);
+       parity.resize(RS_PARITY_SIZE);
+       for (int i = 0; i < max_length + 4; ++i) {
+               for (int j = 0; j < packet_history.size(); ++j) {
+                       data[j] = packet_history[j].data[i];
+               }
+               encode_rs_8(reinterpret_cast<unsigned char *>(&data[0]),
+                           reinterpret_cast<unsigned char *>(&parity[0]),
+                           RS_PAD);
+               for (int j = 0; j < RS_PARITY_SIZE; ++j) {
+                       rs_packets[j][i] = parity[j];
+               }
+       }
+
+       // Actually send the RS packets.
+       int start_seq = packet_history[0].seq - 1;
+       for (int i = 0; i < RS_PARITY_SIZE; ++i) {
+               sender->send_packet(0xffff, rs_packets[i], start_seq - i);
+       }
+       
+       packet_history.clear();
+}
+