This file is indexed.

/usr/include/ableton/link/Peers.hpp is in ableton-link-dev 1.0.0+dfsg-2.

This file is owned by root:root, with mode 0o644.

The actual contents of the file can be viewed below.

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
/* Copyright 2016, Ableton AG, Berlin. All rights reserved.
 *
 *  This program is free software: you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation, either version 2 of the License, or
 *  (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
 *
 *  If you would like to incorporate Link into a proprietary software application,
 *  please contact <link-devs@ableton.com>.
 */

#pragma once

#include <ableton/link/PeerState.hpp>
#include <ableton/util/Injected.hpp>
#include <cassert>

namespace ableton
{
namespace link
{

// SessionMembershipCallback is invoked when any change to session
// membership occurs (when any peer joins or leaves a session)
//
// SessionTimelineCallback is invoked with a session id and a timeline
// whenever a new combination of these values is seen

template <typename IoContext,
  typename SessionMembershipCallback,
  typename SessionTimelineCallback>
class Peers
{
  // non-movable private implementation type
  struct Impl;

public:
  using Peer = std::pair<PeerState, asio::ip::address>;

  Peers(util::Injected<IoContext> io,
    SessionMembershipCallback membership,
    SessionTimelineCallback timeline)
    : mpImpl(
        std::make_shared<Impl>(std::move(io), std::move(membership), std::move(timeline)))
  {
  }

  // The set of peers for a given session, ordered by (peerId, addr).
  // The result will possibly contain multiple entries for the same
  // peer if it is visible through multiple gateways.
  std::vector<Peer> sessionPeers(const SessionId& sid) const
  {
    using namespace std;
    vector<Peer> result;
    auto& peerVec = mpImpl->mPeers;
    copy_if(begin(peerVec), end(peerVec), back_inserter(result), SessionMemberPred{sid});
    return result;
  }

  // Number of individual for a given session.
  std::size_t uniqueSessionPeerCount(const SessionId& sid) const
  {
    using namespace std;
    auto peerVec = sessionPeers(sid);
    auto last = unique(begin(peerVec), end(peerVec),
      [](const Peer& a, const Peer& b) { return a.first.ident() == b.first.ident(); });
    return static_cast<size_t>(distance(begin(peerVec), last));
  }

  void setSessionTimeline(const SessionId& sid, const Timeline& tl)
  {
    // Set the cached timeline for all peers to a new client-specified
    // timeline. When we make a timeline change, we do so
    // optimistically and clients assume that all peers in a session
    // have adopted the newly specified timeline. We must represent
    // this in our cache or else we risk failing to notify about a
    // higher-priority peer timeline that was already seen.
    for (auto& peer : mpImpl->mPeers)
    {
      if (peer.first.sessionId() == sid)
      {
        peer.first.nodeState.timeline = tl;
      }
    }
  }

  // Purge all cached peers that are members of the given session
  void forgetSession(const SessionId& sid)
  {
    using namespace std;
    auto& peerVec = mpImpl->mPeers;
    peerVec.erase(
      remove_if(begin(peerVec), end(peerVec), SessionMemberPred{sid}), end(peerVec));
  }

  void resetPeers()
  {
    mpImpl->mPeers.clear();
  }

  // Observer type that monitors peer discovery on a particular
  // gateway and relays the information to a Peers instance.
  // Models the PeerObserver concept from the discovery module.
  struct GatewayObserver
  {
    using GatewayObserverNodeState = PeerState;
    using GatewayObserverNodeId = NodeId;

    GatewayObserver(std::shared_ptr<Impl> pImpl, asio::ip::address addr)
      : mpImpl(std::move(pImpl))
      , mAddr(std::move(addr))
    {
    }
    GatewayObserver(const GatewayObserver&) = delete;

    GatewayObserver(GatewayObserver&& rhs)
      : mpImpl(std::move(rhs.mpImpl))
      , mAddr(std::move(rhs.mAddr))
    {
    }

    ~GatewayObserver()
    {
      // Check to handle the moved from case
      if (mpImpl)
      {
        auto& io = *mpImpl->mIo;
        io.async(Deleter{*this});
      }
    }

    // model the PeerObserver concept from discovery
    friend void sawPeer(GatewayObserver& observer, const PeerState& state)
    {
      auto pImpl = observer.mpImpl;
      auto addr = observer.mAddr;
      assert(pImpl);
      pImpl->mIo->async([pImpl, addr, state] {
        pImpl->sawPeerOnGateway(std::move(state), std::move(addr));
      });
    }

    friend void peerLeft(GatewayObserver& observer, const NodeId& id)
    {
      auto pImpl = observer.mpImpl;
      auto addr = observer.mAddr;
      pImpl->mIo->async(
        [pImpl, addr, id] { pImpl->peerLeftGateway(std::move(id), std::move(addr)); });
    }

    friend void peerTimedOut(GatewayObserver& observer, const NodeId& id)
    {
      auto pImpl = observer.mpImpl;
      auto addr = observer.mAddr;
      pImpl->mIo->async(
        [pImpl, addr, id] { pImpl->peerLeftGateway(std::move(id), std::move(addr)); });
    }

    struct Deleter
    {
      Deleter(GatewayObserver& observer)
        : mpImpl(std::move(observer.mpImpl))
        , mAddr(std::move(observer.mAddr))
      {
      }

      void operator()()
      {
        mpImpl->gatewayClosed(mAddr);
      }

      std::shared_ptr<Impl> mpImpl;
      asio::ip::address mAddr;
    };

    std::shared_ptr<Impl> mpImpl;
    asio::ip::address mAddr;
  };

  // Factory function for the gateway observer
  friend GatewayObserver makeGatewayObserver(Peers& peers, asio::ip::address addr)
  {
    return GatewayObserver{peers.mpImpl, std::move(addr)};
  }

private:
  struct Impl
  {
    Impl(util::Injected<IoContext> io,
      SessionMembershipCallback membership,
      SessionTimelineCallback timeline)
      : mIo(std::move(io))
      , mSessionMembershipCallback(std::move(membership))
      , mSessionTimelineCallback(std::move(timeline))
    {
    }

    void sawPeerOnGateway(PeerState peerState, asio::ip::address gatewayAddr)
    {
      using namespace std;

      const auto peerSession = peerState.sessionId();
      const auto peerTimeline = peerState.timeline();
      bool isNewSessionTimeline = false;
      bool didSessionMembershipChange = false;
      {
        isNewSessionTimeline = !sessionTimelineExists(peerSession, peerTimeline);

        auto peer = make_pair(move(peerState), move(gatewayAddr));
        const auto idRange = equal_range(begin(mPeers), end(mPeers), peer, PeerIdComp{});

        if (idRange.first == idRange.second)
        {
          // This peer is not currently known on any gateway
          didSessionMembershipChange = true;
          mPeers.insert(move(idRange.first), move(peer));
        }
        else
        {
          // We've seen this peer before... does it have a new session?
          didSessionMembershipChange =
            all_of(idRange.first, idRange.second, [&peerSession](const Peer& test) {
              return test.first.sessionId() != peerSession;
            });

          // was it on this gateway?
          const auto addrRange =
            equal_range(idRange.first, idRange.second, peer, AddrComp{});

          if (addrRange.first == addrRange.second)
          {
            // First time on this gateway, add it
            mPeers.insert(move(addrRange.first), move(peer));
          }
          else
          {
            // We have an entry for this peer on this gateway, update it
            *addrRange.first = move(peer);
          }
        }
      } // end lock

      // Invoke callbacks outside the critical section
      if (isNewSessionTimeline)
      {
        mSessionTimelineCallback(peerSession, peerTimeline);
      }

      if (didSessionMembershipChange)
      {
        mSessionMembershipCallback();
      }
    }

    void peerLeftGateway(const NodeId& nodeId, const asio::ip::address& gatewayAddr)
    {
      using namespace std;

      bool didSessionMembershipChange = false;
      {
        auto it = find_if(begin(mPeers), end(mPeers), [&](const Peer& peer) {
          return peer.first.ident() == nodeId && peer.second == gatewayAddr;
        });

        if (it != end(mPeers))
        {
          mPeers.erase(move(it));
          didSessionMembershipChange = true;
        }
      } // end lock

      if (didSessionMembershipChange)
      {
        mSessionMembershipCallback();
      }
    }

    void gatewayClosed(const asio::ip::address& gatewayAddr)
    {
      using namespace std;

      {
        mPeers.erase(
          remove_if(begin(mPeers), end(mPeers),
            [&gatewayAddr](const Peer& peer) { return peer.second == gatewayAddr; }),
          end(mPeers));
      } // end lock

      mSessionMembershipCallback();
    }

    bool sessionTimelineExists(const SessionId& session, const Timeline& tl)
    {
      using namespace std;
      return find_if(begin(mPeers), end(mPeers),
               [&](const Peer& peer) {
                 return peer.first.sessionId() == session && peer.first.timeline() == tl;
               })
             != end(mPeers);
    }

    struct PeerIdComp
    {
      bool operator()(const Peer& lhs, const Peer& rhs) const
      {
        return lhs.first.ident() < rhs.first.ident();
      }
    };

    struct AddrComp
    {
      bool operator()(const Peer& lhs, const Peer& rhs) const
      {
        return lhs.second < rhs.second;
      }
    };

    util::Injected<IoContext> mIo;
    SessionMembershipCallback mSessionMembershipCallback;
    SessionTimelineCallback mSessionTimelineCallback;
    std::vector<Peer> mPeers; // sorted by peerId, unique by (peerId, addr)
  };

  struct SessionMemberPred
  {
    bool operator()(const Peer& peer) const
    {
      return peer.first.sessionId() == sid;
    }

    const SessionId& sid;
  };

  std::shared_ptr<Impl> mpImpl;
};

template <typename Io,
  typename SessionMembershipCallback,
  typename SessionTimelineCallback>
Peers<Io, SessionMembershipCallback, SessionTimelineCallback> makePeers(
  util::Injected<Io> io,
  SessionMembershipCallback membershipCallback,
  SessionTimelineCallback timelineCallback)
{
  return {std::move(io), std::move(membershipCallback), std::move(timelineCallback)};
}

} // namespace link
} // namespace ableton