<commit_before>var Peers = (function() {
  var encoder = new tnetbin.Encoder({arraybuffer: true});
  var decoder = new tnetbin.Decoder();
  var blobDecoder = new tnetbin.Decoder({arraybuffer: true});

  function Peer(id, config) {
    var pc = new RTCPeerConnection({
      iceServers: [{
        // please contact me if you plan to use this server
        url: 'turn:webrtc.monkeypatch.me:6424?transport=udp',
        credential: 'hibuddy',
        username: 'hibuddy'
      }]
    });
    var dc;

    pc.oniceconnectionstatechange = this._onIceStateChange.bind(this);
    pc.onicecandidate = function(event) {
      this.trigger("icecandidate", event);
    }.bind(this);
    dc = pc.createDataChannel("waggle", {id: 0, negotiated: true});
    dc.onopen = this._onDatachannelOpen.bind(this);
    dc.onmessage = this._onMessage.bind(this);
    dc.binaryType = "arraybuffer";

    this.id = id;
    this.pc = pc;
    this.dc = dc;
    this.queue = [];
  }

  Peer.prototype = {
    request: function(chunk) {
      console.log("request chunk #" + chunk.id + " from " + this.id);

      this._send({
        type: "request",
        swarmId: chunk.swarm,
        chunkId: chunk.id
      });
    },

    send: function(chunk) {
      this._send({
        type: "chunk",
        swarmId: chunk.swarm,
        chunkId: chunk.id
      }, chunk.data);
    },

    createOffer: function(callback) {
      console.log("createoffer");
      this.pc.createOffer(function(offer) {
        this.pc.setLocalDescription(offer, function() {
          callback(offer);
        });
      }.bind(this), function() {});
    },

    createAnswer: function(offer, callback) {
      offer = new RTCSessionDescription(offer);
      this.pc.setRemoteDescription(offer, function() {
        this.pc.createAnswer(function(answer) {
          this.pc.setLocalDescription(answer, function() {
            callback(answer)
          });
        }.bind(this), function() {});
      }.bind(this), function() {});
    },

    complete: function(answer, callback) {
      answer = new RTCSessionDescription(answer);
      this.pc.setRemoteDescription(answer, callback);
    },

    addIceCandidate: function(candidate) {
      candidate = new RTCIceCandidate(candidate);
      this.pc.addIceCandidate(candidate);
    },

    isConnected: function() {
      var iceState = this.pc.iceConnectionState;
      if (iceState === "connected" ||
          iceState === "completed")
        return true;

      return false;
    },

    willConnect: function() {
      var iceState = this.pc.iceConnectionState;
      if (iceState === "new" ||
          iceState === "checking")
        return true;

      return false;
    },

    _onIceStateChange: function() {
      // XXX: display an error if the ice connection failed
      console.log("ice: " + this.pc.iceConnectionState);
      if (this.pc.iceConnectionState === "failed") {
        console.error("Something went wrong: the connection failed");
        this.trigger("failure");
      }

      if (this.pc.iceConnectionState === "connected")
        this.trigger("connected");
    },

    _send: function(message, blob) {
      if (this.dc.readyState === "connecting") {
        this.queue.push([message, blob]);
        return;
      }

      message = encoder.encode(message);
      blob = blob ? encoder.encode(blob) : new Uint8Array(0);
      // TODO: handle errors when the datachannel is closed
      this.dc.send(tnetbin.concatArrayBuffers([message, blob]));
    },

    _onDatachannelOpen: function() {
      var item, message, blob;
      while (this.queue.length > 0) {
        item = this.queue.shift();
        message = item[0];
        blob = item[1];
        this._send(message, blob);
      }
    },

    _onMessage: function(event) {
      var payload, message, remain;

      payload = decoder.decode(event.data);
      message = payload.value;<commit_msg>Peers: bubble up disconnections<commit_after>var Peers = (function() {
  var encoder = new tnetbin.Encoder({arraybuffer: true});
  var decoder = new tnetbin.Decoder();
  var blobDecoder = new tnetbin.Decoder({arraybuffer: true});

  function Peer(id, config) {
    var pc = new RTCPeerConnection({
      iceServers: [{
        // please contact me if you plan to use this server
        url: 'turn:webrtc.monkeypatch.me:6424?transport=udp',
        credential: 'hibuddy',
        username: 'hibuddy'
      }]
    });
    var dc;

    pc.oniceconnectionstatechange = this._onIceStateChange.bind(this);
    pc.onicecandidate = function(event) {
      this.trigger("icecandidate", event);
    }.bind(this);
    dc = pc.createDataChannel("waggle", {id: 0, negotiated: true});
    dc.onopen = this._onDatachannelOpen.bind(this);
    dc.onmessage = this._onMessage.bind(this);
    dc.onclose = this.trigger.bind(this, "disconnected");
    dc.binaryType = "arraybuffer";

    this.id = id;
    this.pc = pc;
    this.dc = dc;
    this.queue = [];
  }

  Peer.prototype = {
    request: function(chunk) {
      console.log("request chunk #" + chunk.id + " from " + this.id);

      this._send({
        type: "request",
        swarmId: chunk.swarm,
        chunkId: chunk.id
      });
    },

    send: function(chunk) {
      this._send({
        type: "chunk",
        swarmId: chunk.swarm,
        chunkId: chunk.id
      }, chunk.data);
    },

    createOffer: function(callback) {
      console.log("createoffer");
      this.pc.createOffer(function(offer) {
        this.pc.setLocalDescription(offer, function() {
          callback(offer);
        });
      }.bind(this), function() {});
    },

    createAnswer: function(offer, callback) {
      offer = new RTCSessionDescription(offer);
      this.pc.setRemoteDescription(offer, function() {
        this.pc.createAnswer(function(answer) {
          this.pc.setLocalDescription(answer, function() {
            callback(answer)
          });
        }.bind(this), function() {});
      }.bind(this), function() {});
    },

    complete: function(answer, callback) {
      answer = new RTCSessionDescription(answer);
      this.pc.setRemoteDescription(answer, callback);
    },

    addIceCandidate: function(candidate) {
      candidate = new RTCIceCandidate(candidate);
      this.pc.addIceCandidate(candidate);
    },

    isConnected: function() {
      var iceState = this.pc.iceConnectionState;
      if (iceState === "connected" ||
          iceState === "completed")
        return true;

      return false;
    },

    willConnect: function() {
      var iceState = this.pc.iceConnectionState;
      if (iceState === "new" ||
          iceState === "checking")
        return true;

      return false;
    },

    _onIceStateChange: function() {
      // XXX: display an error if the ice connection failed
      console.log("ice: " + this.pc.iceConnectionState);
      if (this.pc.iceConnectionState === "failed") {
        console.error("Something went wrong: the connection failed");
        this.trigger("failure");
      }

      if (this.pc.iceConnectionState === "connected")
        this.trigger("connected");
      if (this.pc.iceConnectionState === "disconnected" ||
          this.pc.iceConnectionState === "closed")
        this.trigger("diconnected");
    },

    _send: function(message, blob) {
      if (this.dc.readyState === "connecting") {
        this.queue.push([message, blob]);
        return;
      }

      message = encoder.encode(message);
      blob = blob ? encoder.encode(blob) : new Uint8Array(0);
      // TODO: handle errors when the datachannel is closed
      this.dc.send(tnetbin.concatArrayBuffers([message, blob]));
    },

    _onDatachannelOpen: function() {
      var item, message, blob;
      while (this.queue.length > 0) {
        item = this.queue.shift();
        message = item[0];
        blob = item[1];
        this._send(message, blob);
      }
    },

    _onMessage: function(event) {
      var payload, message, remain;

      payload = decoder.decode(event.data);
      message = payload.value;
{'fraction_non_alphanumeric': 0.14028732042473455, 'fraction_numerical': 0.0024984384759525295, 'mean_word_length': 2.4945438673068527, 'pattern_counts': {'":': 0, '<': 3, '<?xml version=': 0, '>': 5, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '3775788', 'n_tokens_mistral': 2484, 'n_tokens_neox': 2369, 'n_words': 493}