Home Reference Source

lib/arcstore.js

'use strict'

const ELiving = require('./entries/eliving.js')

const ExSocketNotFound = require('./exceptions/exsocketnotfound.js')

/**
 * Table that contains all living sockets that might be in use. Peers can
 * comprise multiples protocols that may use multiple times an arc.
 */
class ArcStore {
  constructor () {
    this.store = new Map()
    this.tid = new Map()
  }

  /**
     * add another arc leading to peerId in the store
     * @param {string} peerId The identifier of the peer reachable through the
     * socket.
     * @param {object} socket The WebRTC socket. Can be null if the socket to
     * peerId is known to be in the store.
     * @param {string} tid TemporyId of a set of offer that we store in order to say if the already see a set of offers. (trickle true problems)
     * @return {Boolean} Return true if increment was done, false otherwise
     */
  insert (peerId, socket, tid) {
    // #1 make sure the peerId exists if the socket is not set.
    if (socket === null && !this.store.has(peerId)) {
      throw new ExSocketNotFound(
        'arcStore',
        peerId,
        'Try to add an arc to a peer that does not exists')
    }

    // #2 the peerId is not known yet, create the entry
    if (!this.store.has(peerId)) {
      let entry = new ELiving(peerId, socket)
      this.store.set(peerId, entry)
      return true
    } else {
      // check if we already see the tid (fix the problem of multiple offers, trickle true!)
      if (!this.tid.has(tid)) {
        this.tid.set(tid, true)
        // #3 increment the number of arcs of protocolId
        this.store.get(peerId).increment()
        return true
      } else {
        return false
      }
    }
  }

  /**
     * ProtocolId removes an arc to the peerId.
     * @param {string} peerId The identifier of the peer accessible through the
     * arc to delete.
     * @returns {ELiving} The entry if no protocol is using the socket, null
     * otherwise.
     */
  remove (peerId) {
    let unusedSocket = null
    // #1 check if a socket to the arc exists
    if (!this.store.has(peerId)) {
      throw new ExSocketNotFound(
        'arcStore',
        peerId,
        'Try to remove an arc to a peer that does not exists')
    }
    this.store.get(peerId).decrement()
    // #3 remove the entry if no protocol use it
    if (this.store.get(peerId).counter <= 0) {
      unusedSocket = this.store.get(peerId)
      this.store.delete(peerId)
    }
    return unusedSocket
  }

  /**
     * removes all arcs.
     * @returns {object[]} Objects comprising {peer, socket, occ}; peer being
     * the identifier of the peer reachable through the socket, socket being a
     * WebRTC connection that is not used by any protocols, null if protocols
     * still use it, occ being the number of arcs removed by protocolId.
     */
  removeAll () {
    let result = []
    this.store.forEach((v, k) => {
      let entry = {
        peer: v.peer,
        socket: null,
        occ: v.counter
      }
      for (let i = 0; i < entry.occ; ++i) {
        let unusedSocket = this.remove(k)
        if (unusedSocket !== null) {
          entry.socket = unusedSocket.socket
          result.push(entry)
        }
      }
    })
    return result
  }

  /**
     * Remove all arcs leading to peerId.
     * @param {string} peerId The identifier of the peer reachable by a WebRTC
     * connection to remove.
     * @returns {Map} Map where key is the identifier of the protocol that sees
     * its arcs being removed, and value is the number of arcs removed;
     */
  removePeer (peerId) {
    let result
    if (this.store.has(peerId)) {
      result = this.store.get(peerId)
      this.store.delete(peerId)
    } else {
      result = new Map()
    }
    return result
  };
  /**
     * Check if the store has at least one occurrence of the peer.
     * @param {string} peerId The identifier of the peer to check.
     * @returns {boolean} true if it exists, false otherwise.
     */
  contains (peerId) {
    return this.store.has(peerId)
  }

  /**
     * Get the entry of the arc leading to PeerId
     * @param {string} peerId The identifier of the remote peer
     * @returns {ELiving} The entry containing peerId, null if it does not
     * exists.
     */
  get (peerId) {
    let entry = null
    if (this.contains(peerId)) {
      entry = this.store.get(peerId)
    }
    return entry
  }
}

module.exports = ArcStore