FlowStat: Replace btree_map with flat map which is inlined in small case
Inline storage for size <= 2 Size = 1 is ~90% Size = 2 is ~9% Size >=3 is ~1% and gets a separate allocation
This commit is contained in:
@@ -663,10 +663,10 @@ bool VehicleCargoList::Stage(bool accepted, StationID current_station, StationID
|
|||||||
FlowStat new_shares = *flow_it;
|
FlowStat new_shares = *flow_it;
|
||||||
new_shares.ChangeShare(current_station, INT_MIN);
|
new_shares.ChangeShare(current_station, INT_MIN);
|
||||||
StationIDStack excluded = next_station;
|
StationIDStack excluded = next_station;
|
||||||
while (!excluded.IsEmpty() && !new_shares.GetShares()->empty()) {
|
while (!excluded.IsEmpty() && !new_shares.empty()) {
|
||||||
new_shares.ChangeShare(excluded.Pop(), INT_MIN);
|
new_shares.ChangeShare(excluded.Pop(), INT_MIN);
|
||||||
}
|
}
|
||||||
if (new_shares.GetShares()->empty()) {
|
if (new_shares.empty()) {
|
||||||
cargo_next = INVALID_STATION;
|
cargo_next = INVALID_STATION;
|
||||||
} else {
|
} else {
|
||||||
cargo_next = new_shares.GetVia();
|
cargo_next = new_shares.GetVia();
|
||||||
|
@@ -155,8 +155,8 @@ void LinkGraphJob::FinaliseJob()
|
|||||||
FlowStat shares(INVALID_STATION, INVALID_STATION, 1);
|
FlowStat shares(INVALID_STATION, INVALID_STATION, 1);
|
||||||
it->SwapShares(shares);
|
it->SwapShares(shares);
|
||||||
it = ge.flows.erase(it);
|
it = ge.flows.erase(it);
|
||||||
for (FlowStat::SharesMap::const_iterator shares_it(shares.GetShares()->begin());
|
for (FlowStat::const_iterator shares_it(shares.begin());
|
||||||
shares_it != shares.GetShares()->end(); ++shares_it) {
|
shares_it != shares.end(); ++shares_it) {
|
||||||
RerouteCargo(st, this->Cargo(), shares_it->second, st->index);
|
RerouteCargo(st, this->Cargo(), shares_it->second, st->index);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -156,10 +156,10 @@ private:
|
|||||||
std::vector<NodeID> station_to_node;
|
std::vector<NodeID> station_to_node;
|
||||||
|
|
||||||
/** Current iterator in the shares map. */
|
/** Current iterator in the shares map. */
|
||||||
FlowStat::SharesMap::const_iterator it;
|
FlowStat::const_iterator it;
|
||||||
|
|
||||||
/** End of the shares map. */
|
/** End of the shares map. */
|
||||||
FlowStat::SharesMap::const_iterator end;
|
FlowStat::const_iterator end;
|
||||||
public:
|
public:
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -187,11 +187,11 @@ public:
|
|||||||
const FlowStatMap &flows = this->job[node].Flows();
|
const FlowStatMap &flows = this->job[node].Flows();
|
||||||
FlowStatMap::const_iterator it = flows.find(this->job[source].Station());
|
FlowStatMap::const_iterator it = flows.find(this->job[source].Station());
|
||||||
if (it != flows.end()) {
|
if (it != flows.end()) {
|
||||||
this->it = it->GetShares()->begin();
|
this->it = it->begin();
|
||||||
this->end = it->GetShares()->end();
|
this->end = it->end();
|
||||||
} else {
|
} else {
|
||||||
this->it = FlowStat::empty_sharesmap.begin();
|
this->it = nullptr;
|
||||||
this->end = FlowStat::empty_sharesmap.end();
|
this->end = nullptr;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -523,15 +523,16 @@ static void RealSave_STNN(BaseStation *bst)
|
|||||||
_num_dests = (uint32)st->goods[i].cargo.Packets()->MapSize();
|
_num_dests = (uint32)st->goods[i].cargo.Packets()->MapSize();
|
||||||
_num_flows = 0;
|
_num_flows = 0;
|
||||||
for (FlowStatMap::const_iterator it(st->goods[i].flows.begin()); it != st->goods[i].flows.end(); ++it) {
|
for (FlowStatMap::const_iterator it(st->goods[i].flows.begin()); it != st->goods[i].flows.end(); ++it) {
|
||||||
_num_flows += (uint32)it->GetShares()->size();
|
_num_flows += (uint32)it->size();
|
||||||
}
|
}
|
||||||
SlObjectSaveFiltered(&st->goods[i], _filtered_goods_desc.data());
|
SlObjectSaveFiltered(&st->goods[i], _filtered_goods_desc.data());
|
||||||
for (FlowStatMap::const_iterator outer_it(st->goods[i].flows.begin()); outer_it != st->goods[i].flows.end(); ++outer_it) {
|
for (FlowStatMap::const_iterator outer_it(st->goods[i].flows.begin()); outer_it != st->goods[i].flows.end(); ++outer_it) {
|
||||||
const FlowStat::SharesMap *shares = outer_it->GetShares();
|
|
||||||
uint32 sum_shares = 0;
|
uint32 sum_shares = 0;
|
||||||
FlowSaveLoad flow;
|
FlowSaveLoad flow;
|
||||||
flow.source = outer_it->GetOrigin();
|
flow.source = outer_it->GetOrigin();
|
||||||
for (FlowStat::SharesMap::const_iterator inner_it(shares->begin()); inner_it != shares->end(); ++inner_it) {
|
FlowStat::const_iterator inner_it(outer_it->begin());
|
||||||
|
const FlowStat::const_iterator end(outer_it->end());
|
||||||
|
for (; inner_it != end; ++inner_it) {
|
||||||
flow.via = inner_it->second;
|
flow.via = inner_it->second;
|
||||||
flow.share = inner_it->first - sum_shares;
|
flow.share = inner_it->first - sum_shares;
|
||||||
flow.restricted = inner_it->first > outer_it->GetUnrestricted();
|
flow.restricted = inner_it->first > outer_it->GetUnrestricted();
|
||||||
|
@@ -195,10 +195,9 @@ void ScriptStationList_CargoPlanned::Add(StationID station_id, CargoID cargo, St
|
|||||||
FlowStatMap::const_iterator iter = collector.GE()->flows.begin();
|
FlowStatMap::const_iterator iter = collector.GE()->flows.begin();
|
||||||
FlowStatMap::const_iterator end = collector.GE()->flows.end();
|
FlowStatMap::const_iterator end = collector.GE()->flows.end();
|
||||||
for (; iter != end; ++iter) {
|
for (; iter != end; ++iter) {
|
||||||
const FlowStat::SharesMap *shares = iter->GetShares();
|
|
||||||
uint prev = 0;
|
uint prev = 0;
|
||||||
for (FlowStat::SharesMap::const_iterator flow_iter = shares->begin();
|
for (FlowStat::const_iterator flow_iter = iter->begin();
|
||||||
flow_iter != shares->end(); ++flow_iter) {
|
flow_iter != iter->end(); ++flow_iter) {
|
||||||
collector.Update<Tselector>(iter->GetOrigin(), flow_iter->second, flow_iter->first - prev);
|
collector.Update<Tselector>(iter->GetOrigin(), flow_iter->second, flow_iter->first - prev);
|
||||||
prev = flow_iter->first;
|
prev = flow_iter->first;
|
||||||
}
|
}
|
||||||
@@ -265,10 +264,9 @@ ScriptStationList_CargoPlannedFromByVia::ScriptStationList_CargoPlannedFromByVia
|
|||||||
|
|
||||||
FlowStatMap::const_iterator iter = collector.GE()->flows.find(from);
|
FlowStatMap::const_iterator iter = collector.GE()->flows.find(from);
|
||||||
if (iter == collector.GE()->flows.end()) return;
|
if (iter == collector.GE()->flows.end()) return;
|
||||||
const FlowStat::SharesMap *shares = iter->GetShares();
|
|
||||||
uint prev = 0;
|
uint prev = 0;
|
||||||
for (FlowStat::SharesMap::const_iterator flow_iter = shares->begin();
|
for (FlowStat::const_iterator flow_iter = iter->begin();
|
||||||
flow_iter != shares->end(); ++flow_iter) {
|
flow_iter != iter->end(); ++flow_iter) {
|
||||||
collector.Update<CS_FROM_BY_VIA>(iter->GetOrigin(), flow_iter->second, flow_iter->first - prev);
|
collector.Update<CS_FROM_BY_VIA>(iter->GetOrigin(), flow_iter->second, flow_iter->first - prev);
|
||||||
prev = flow_iter->first;
|
prev = flow_iter->first;
|
||||||
}
|
}
|
||||||
|
@@ -22,10 +22,12 @@
|
|||||||
#include "3rdparty/cpp-btree/btree_map.h"
|
#include "3rdparty/cpp-btree/btree_map.h"
|
||||||
#include "3rdparty/cpp-btree/btree_set.h"
|
#include "3rdparty/cpp-btree/btree_set.h"
|
||||||
#include "bitmap_type.h"
|
#include "bitmap_type.h"
|
||||||
|
#include "core/endian_type.hpp"
|
||||||
#include <map>
|
#include <map>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
#include <iterator>
|
#include <iterator>
|
||||||
#include <functional>
|
#include <functional>
|
||||||
|
#include <algorithm>
|
||||||
|
|
||||||
typedef Pool<BaseStation, StationID, 32, 64000> StationPool;
|
typedef Pool<BaseStation, StationID, 32, 64000> StationPool;
|
||||||
extern StationPool _station_pool;
|
extern StationPool _station_pool;
|
||||||
@@ -44,9 +46,28 @@ class FlowStatMap;
|
|||||||
class FlowStat {
|
class FlowStat {
|
||||||
friend FlowStatMap;
|
friend FlowStatMap;
|
||||||
public:
|
public:
|
||||||
typedef btree::btree_map<uint32, StationID> SharesMap;
|
struct ShareEntry {
|
||||||
|
#if OTTD_ALIGNMENT == 0
|
||||||
|
unaligned_uint32 first;
|
||||||
|
#else
|
||||||
|
uint32 first;
|
||||||
|
#endif
|
||||||
|
StationID second;
|
||||||
|
};
|
||||||
|
static_assert(sizeof(ShareEntry) == 6, "");
|
||||||
|
|
||||||
static const SharesMap empty_sharesmap;
|
friend bool operator<(const ShareEntry &a, const ShareEntry &b) noexcept
|
||||||
|
{
|
||||||
|
return a.first < b.first;
|
||||||
|
}
|
||||||
|
|
||||||
|
friend bool operator<(uint a, const ShareEntry &b) noexcept
|
||||||
|
{
|
||||||
|
return a < b.first;
|
||||||
|
}
|
||||||
|
|
||||||
|
typedef ShareEntry* iterator;
|
||||||
|
typedef const ShareEntry* const_iterator;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Invalid constructor. This can't be called as a FlowStat must not be
|
* Invalid constructor. This can't be called as a FlowStat must not be
|
||||||
@@ -65,11 +86,92 @@ public:
|
|||||||
inline FlowStat(StationID origin, StationID via, uint flow, bool restricted = false)
|
inline FlowStat(StationID origin, StationID via, uint flow, bool restricted = false)
|
||||||
{
|
{
|
||||||
assert(flow > 0);
|
assert(flow > 0);
|
||||||
this->shares[flow] = via;
|
this->storage.inline_shares[0].first = flow;
|
||||||
|
this->storage.inline_shares[0].second = via;
|
||||||
this->unrestricted = restricted ? 0 : flow;
|
this->unrestricted = restricted ? 0 : flow;
|
||||||
|
this->count = 1;
|
||||||
this->origin = origin;
|
this->origin = origin;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
inline bool inline_mode() const
|
||||||
|
{
|
||||||
|
return this->count <= 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline const ShareEntry *data() const
|
||||||
|
{
|
||||||
|
return this->inline_mode() ? this->storage.inline_shares : this->storage.ptr_shares.buffer;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline ShareEntry *data()
|
||||||
|
{
|
||||||
|
return const_cast<ShareEntry *>(const_cast<const FlowStat*>(this)->data());
|
||||||
|
}
|
||||||
|
|
||||||
|
inline void clear()
|
||||||
|
{
|
||||||
|
if (!inline_mode()) {
|
||||||
|
free(this->storage.ptr_shares.buffer);
|
||||||
|
}
|
||||||
|
this->count = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
iterator erase_item(iterator iter, uint flow_reduction);
|
||||||
|
|
||||||
|
inline void CopyCommon(const FlowStat &other)
|
||||||
|
{
|
||||||
|
this->count = other.count;
|
||||||
|
if (!other.inline_mode()) {
|
||||||
|
this->storage.ptr_shares.elem_capacity = other.storage.ptr_shares.elem_capacity;
|
||||||
|
this->storage.ptr_shares.buffer = MallocT<ShareEntry>(other.storage.ptr_shares.elem_capacity);
|
||||||
|
}
|
||||||
|
MemCpyT(this->data(), other.data(), this->count);
|
||||||
|
this->unrestricted = other.unrestricted;
|
||||||
|
this->origin = other.origin;
|
||||||
|
}
|
||||||
|
|
||||||
|
public:
|
||||||
|
inline FlowStat(const FlowStat &other)
|
||||||
|
{
|
||||||
|
this->CopyCommon(other);
|
||||||
|
}
|
||||||
|
|
||||||
|
inline FlowStat(FlowStat &&other) noexcept
|
||||||
|
{
|
||||||
|
this->count = 0;
|
||||||
|
this->SwapShares(other);
|
||||||
|
this->origin = other.origin;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline ~FlowStat()
|
||||||
|
{
|
||||||
|
this->clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
inline FlowStat &operator=(const FlowStat &other)
|
||||||
|
{
|
||||||
|
this->clear();
|
||||||
|
this->CopyCommon(other);
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline FlowStat &operator=(FlowStat &&other) noexcept
|
||||||
|
{
|
||||||
|
this->SwapShares(other);
|
||||||
|
this->origin = other.origin;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline size_t size() const { return this->count; }
|
||||||
|
inline bool empty() const { return this->count == 0; }
|
||||||
|
inline iterator begin() { return this->data(); }
|
||||||
|
inline const_iterator begin() const { return this->data(); }
|
||||||
|
inline iterator end() { return this->data() + this->count; }
|
||||||
|
inline const_iterator end() const { return this->data() + this->count; }
|
||||||
|
inline iterator upper_bound(uint32 key) { return std::upper_bound(this->begin(), this->end(), key); }
|
||||||
|
inline const_iterator upper_bound(uint32 key) const { return std::upper_bound(this->begin(), this->end(), key); }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Add some flow to the end of the shares map. Only do that if you know
|
* Add some flow to the end of the shares map. Only do that if you know
|
||||||
* that the station isn't in the map yet. Anything else may lead to
|
* that the station isn't in the map yet. Anything else may lead to
|
||||||
@@ -81,8 +183,26 @@ public:
|
|||||||
inline void AppendShare(StationID st, uint flow, bool restricted = false)
|
inline void AppendShare(StationID st, uint flow, bool restricted = false)
|
||||||
{
|
{
|
||||||
assert(flow > 0);
|
assert(flow > 0);
|
||||||
uint32 key = (--this->shares.end())->first + flow;
|
uint32 key = this->GetLastKey() + flow;
|
||||||
this->shares.insert(this->shares.end(), std::make_pair(key, st));
|
if (unlikely(this->count >= 2)) {
|
||||||
|
if (this->count == 2) {
|
||||||
|
// convert inline buffer to ptr
|
||||||
|
ShareEntry *ptr = MallocT<ShareEntry>(4);
|
||||||
|
ptr[0] = this->storage.inline_shares[0];
|
||||||
|
ptr[1] = this->storage.inline_shares[1];
|
||||||
|
this->storage.ptr_shares.buffer = ptr;
|
||||||
|
this->storage.ptr_shares.elem_capacity = 4;
|
||||||
|
} else if (this->count == this->storage.ptr_shares.elem_capacity) {
|
||||||
|
// grow buffer
|
||||||
|
uint16 new_size = this->storage.ptr_shares.elem_capacity * 2;
|
||||||
|
this->storage.ptr_shares.buffer = ReallocT<ShareEntry>(this->storage.ptr_shares.buffer, new_size);
|
||||||
|
this->storage.ptr_shares.elem_capacity = new_size;
|
||||||
|
}
|
||||||
|
this->storage.ptr_shares.buffer[this->count] = { key, st };
|
||||||
|
} else {
|
||||||
|
this->storage.inline_shares[this->count] = { key, st };
|
||||||
|
}
|
||||||
|
this->count++;
|
||||||
if (!restricted) this->unrestricted += flow;
|
if (!restricted) this->unrestricted += flow;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -96,13 +216,6 @@ public:
|
|||||||
|
|
||||||
void ScaleToMonthly(uint runtime);
|
void ScaleToMonthly(uint runtime);
|
||||||
|
|
||||||
/**
|
|
||||||
* Get the actual shares as a const pointer so that they can be iterated
|
|
||||||
* over.
|
|
||||||
* @return Actual shares.
|
|
||||||
*/
|
|
||||||
inline const SharesMap *GetShares() const { return &this->shares; }
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Return total amount of unrestricted shares.
|
* Return total amount of unrestricted shares.
|
||||||
* @return Amount of unrestricted shares.
|
* @return Amount of unrestricted shares.
|
||||||
@@ -116,8 +229,9 @@ public:
|
|||||||
*/
|
*/
|
||||||
inline void SwapShares(FlowStat &other)
|
inline void SwapShares(FlowStat &other)
|
||||||
{
|
{
|
||||||
this->shares.swap(other.shares);
|
std::swap(this->storage, other.storage);
|
||||||
Swap(this->unrestricted, other.unrestricted);
|
std::swap(this->unrestricted, other.unrestricted);
|
||||||
|
std::swap(this->count, other.count);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -130,10 +244,10 @@ public:
|
|||||||
*/
|
*/
|
||||||
inline StationID GetViaWithRestricted(bool &is_restricted) const
|
inline StationID GetViaWithRestricted(bool &is_restricted) const
|
||||||
{
|
{
|
||||||
assert(!this->shares.empty());
|
assert(!this->empty());
|
||||||
uint rand = RandomRange((--this->shares.end())->first);
|
uint rand = RandomRange(this->GetLastKey());
|
||||||
is_restricted = rand >= this->unrestricted;
|
is_restricted = rand >= this->unrestricted;
|
||||||
return this->shares.upper_bound(rand)->second;
|
return this->upper_bound(rand)->second;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -145,9 +259,9 @@ public:
|
|||||||
*/
|
*/
|
||||||
inline StationID GetVia() const
|
inline StationID GetVia() const
|
||||||
{
|
{
|
||||||
assert(!this->shares.empty());
|
assert(!this->empty());
|
||||||
return this->unrestricted > 0 ?
|
return this->unrestricted > 0 ?
|
||||||
this->shares.upper_bound(RandomRange(this->unrestricted))->second :
|
this->upper_bound(RandomRange(this->unrestricted))->second :
|
||||||
INVALID_STATION;
|
INVALID_STATION;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -161,11 +275,36 @@ public:
|
|||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
SharesMap shares; ///< Shares of flow to be sent via specified station (or consumed locally).
|
uint32 GetLastKey() const
|
||||||
|
{
|
||||||
|
return this->data()[this->count - 1].first;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct ptr_buffer {
|
||||||
|
ShareEntry *buffer;
|
||||||
|
uint16 elem_capacity;
|
||||||
|
}
|
||||||
|
#if OTTD_ALIGNMENT == 0 && (defined(__GNUC__) || defined(__clang__))
|
||||||
|
__attribute__((packed, aligned(4)))
|
||||||
|
#endif
|
||||||
|
;
|
||||||
|
union storage_union {
|
||||||
|
ShareEntry inline_shares[2]; ///< Small buffer optimisation: size = 1 is ~90%, size = 2 is ~9%, size >= 3 is ~1%
|
||||||
|
ptr_buffer ptr_shares;
|
||||||
|
|
||||||
|
// Actual construction/destruction done by class FlowStat
|
||||||
|
storage_union() {}
|
||||||
|
~storage_union() {}
|
||||||
|
};
|
||||||
|
storage_union storage; ///< Shares of flow to be sent via specified station (or consumed locally).
|
||||||
uint unrestricted; ///< Limit for unrestricted shares.
|
uint unrestricted; ///< Limit for unrestricted shares.
|
||||||
|
uint16 count;
|
||||||
StationID origin;
|
StationID origin;
|
||||||
};
|
};
|
||||||
static_assert(std::is_nothrow_move_constructible<FlowStat>::value, "FlowStat must be nothrow move constructible");
|
static_assert(std::is_nothrow_move_constructible<FlowStat>::value, "FlowStat must be nothrow move constructible");
|
||||||
|
#if OTTD_ALIGNMENT == 0 && (defined(__GNUC__) || defined(__clang__))
|
||||||
|
static_assert(sizeof(FlowStat) == 20, "");
|
||||||
|
#endif
|
||||||
|
|
||||||
template<typename cv_value, typename cv_container, typename cv_index_iter>
|
template<typename cv_value, typename cv_container, typename cv_index_iter>
|
||||||
class FlowStatMapIterator
|
class FlowStatMapIterator
|
||||||
@@ -224,7 +363,6 @@ public:
|
|||||||
void PassOnFlow(StationID origin, StationID via, uint amount);
|
void PassOnFlow(StationID origin, StationID via, uint amount);
|
||||||
StationIDStack DeleteFlows(StationID via);
|
StationIDStack DeleteFlows(StationID via);
|
||||||
void RestrictFlows(StationID via);
|
void RestrictFlows(StationID via);
|
||||||
void ReleaseFlows(StationID via);
|
|
||||||
void FinalizeLocalConsumption(StationID self);
|
void FinalizeLocalConsumption(StationID self);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
@@ -63,13 +63,6 @@
|
|||||||
|
|
||||||
#include "safeguards.h"
|
#include "safeguards.h"
|
||||||
|
|
||||||
/**
|
|
||||||
* Static instance of FlowStat::SharesMap.
|
|
||||||
* Note: This instance is created on task start.
|
|
||||||
* Lazy creation on first usage results in a data race between the CDist threads.
|
|
||||||
*/
|
|
||||||
/* static */ const FlowStat::SharesMap FlowStat::empty_sharesmap;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Check whether the given tile is a hangar.
|
* Check whether the given tile is a hangar.
|
||||||
* @param t the tile to of whether it is a hangar.
|
* @param t the tile to of whether it is a hangar.
|
||||||
@@ -4629,6 +4622,25 @@ static CommandCost TerraformTile_Station(TileIndex tile, DoCommandFlag flags, in
|
|||||||
return DoCommand(tile, 0, 0, flags, CMD_LANDSCAPE_CLEAR);
|
return DoCommand(tile, 0, 0, flags, CMD_LANDSCAPE_CLEAR);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
FlowStat::iterator FlowStat::erase_item(FlowStat::iterator iter, uint flow_reduction)
|
||||||
|
{
|
||||||
|
assert(!this->empty());
|
||||||
|
const uint offset = iter - this->begin();
|
||||||
|
const iterator last = this->end() - 1;
|
||||||
|
for (; iter < last; ++iter) {
|
||||||
|
*iter = { (iter + 1)->first - flow_reduction, (iter + 1)->second };
|
||||||
|
}
|
||||||
|
--this->count;
|
||||||
|
if (this->count == 2) {
|
||||||
|
// transition from external to internal storage
|
||||||
|
ShareEntry *ptr = this->storage.ptr_shares.buffer;
|
||||||
|
this->storage.inline_shares[0] = ptr[0];
|
||||||
|
this->storage.inline_shares[1] = ptr[1];
|
||||||
|
free(ptr);
|
||||||
|
}
|
||||||
|
return this->begin() + offset;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get flow for a station.
|
* Get flow for a station.
|
||||||
* @param st Station to get flow for.
|
* @param st Station to get flow for.
|
||||||
@@ -4637,7 +4649,7 @@ static CommandCost TerraformTile_Station(TileIndex tile, DoCommandFlag flags, in
|
|||||||
uint FlowStat::GetShare(StationID st) const
|
uint FlowStat::GetShare(StationID st) const
|
||||||
{
|
{
|
||||||
uint32 prev = 0;
|
uint32 prev = 0;
|
||||||
for (SharesMap::const_iterator it = this->shares.begin(); it != this->shares.end(); ++it) {
|
for (const_iterator it = this->begin(); it != this->end(); ++it) {
|
||||||
if (it->second == st) {
|
if (it->second == st) {
|
||||||
return it->first - prev;
|
return it->first - prev;
|
||||||
} else {
|
} else {
|
||||||
@@ -4656,30 +4668,30 @@ uint FlowStat::GetShare(StationID st) const
|
|||||||
StationID FlowStat::GetVia(StationID excluded, StationID excluded2) const
|
StationID FlowStat::GetVia(StationID excluded, StationID excluded2) const
|
||||||
{
|
{
|
||||||
if (this->unrestricted == 0) return INVALID_STATION;
|
if (this->unrestricted == 0) return INVALID_STATION;
|
||||||
assert(!this->shares.empty());
|
assert(!this->empty());
|
||||||
SharesMap::const_iterator it = this->shares.upper_bound(RandomRange(this->unrestricted));
|
const_iterator it = std::upper_bound(this->data(), this->data() + this->count, RandomRange(this->unrestricted));
|
||||||
assert(it != this->shares.end() && it->first <= this->unrestricted);
|
assert(it != this->end() && it->first <= this->unrestricted);
|
||||||
if (it->second != excluded && it->second != excluded2) return it->second;
|
if (it->second != excluded && it->second != excluded2) return it->second;
|
||||||
|
|
||||||
/* We've hit one of the excluded stations.
|
/* We've hit one of the excluded stations.
|
||||||
* Draw another share, from outside its range. */
|
* Draw another share, from outside its range. */
|
||||||
|
|
||||||
uint end = it->first;
|
uint end = it->first;
|
||||||
uint begin = (it == this->shares.begin() ? 0 : (--it)->first);
|
uint begin = (it == this->begin() ? 0 : (--it)->first);
|
||||||
uint interval = end - begin;
|
uint interval = end - begin;
|
||||||
if (interval >= this->unrestricted) return INVALID_STATION; // Only one station in the map.
|
if (interval >= this->unrestricted) return INVALID_STATION; // Only one station in the map.
|
||||||
uint new_max = this->unrestricted - interval;
|
uint new_max = this->unrestricted - interval;
|
||||||
uint rand = RandomRange(new_max);
|
uint rand = RandomRange(new_max);
|
||||||
SharesMap::const_iterator it2 = (rand < begin) ? this->shares.upper_bound(rand) :
|
const_iterator it2 = (rand < begin) ? this->upper_bound(rand) :
|
||||||
this->shares.upper_bound(rand + interval);
|
this->upper_bound(rand + interval);
|
||||||
assert(it2 != this->shares.end() && it2->first <= this->unrestricted);
|
assert(it2 != this->end() && it2->first <= this->unrestricted);
|
||||||
if (it2->second != excluded && it2->second != excluded2) return it2->second;
|
if (it2->second != excluded && it2->second != excluded2) return it2->second;
|
||||||
|
|
||||||
/* We've hit the second excluded station.
|
/* We've hit the second excluded station.
|
||||||
* Same as before, only a bit more complicated. */
|
* Same as before, only a bit more complicated. */
|
||||||
|
|
||||||
uint end2 = it2->first;
|
uint end2 = it2->first;
|
||||||
uint begin2 = (it2 == this->shares.begin() ? 0 : (--it2)->first);
|
uint begin2 = (it2 == this->begin() ? 0 : (--it2)->first);
|
||||||
uint interval2 = end2 - begin2;
|
uint interval2 = end2 - begin2;
|
||||||
if (interval2 >= new_max) return INVALID_STATION; // Only the two excluded stations in the map.
|
if (interval2 >= new_max) return INVALID_STATION; // Only the two excluded stations in the map.
|
||||||
new_max -= interval2;
|
new_max -= interval2;
|
||||||
@@ -4689,15 +4701,15 @@ StationID FlowStat::GetVia(StationID excluded, StationID excluded2) const
|
|||||||
Swap(interval, interval2);
|
Swap(interval, interval2);
|
||||||
}
|
}
|
||||||
rand = RandomRange(new_max);
|
rand = RandomRange(new_max);
|
||||||
SharesMap::const_iterator it3 = this->shares.upper_bound(this->unrestricted);
|
const_iterator it3 = this->upper_bound(this->unrestricted);
|
||||||
if (rand < begin) {
|
if (rand < begin) {
|
||||||
it3 = this->shares.upper_bound(rand);
|
it3 = this->upper_bound(rand);
|
||||||
} else if (rand < begin2 - interval) {
|
} else if (rand < begin2 - interval) {
|
||||||
it3 = this->shares.upper_bound(rand + interval);
|
it3 = this->upper_bound(rand + interval);
|
||||||
} else {
|
} else {
|
||||||
it3 = this->shares.upper_bound(rand + interval + interval2);
|
it3 = this->upper_bound(rand + interval + interval2);
|
||||||
}
|
}
|
||||||
assert(it3 != this->shares.end() && it3->first <= this->unrestricted);
|
assert(it3 != this->end() && it3->first <= this->unrestricted);
|
||||||
return it3->second;
|
return it3->second;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -4708,15 +4720,13 @@ StationID FlowStat::GetVia(StationID excluded, StationID excluded2) const
|
|||||||
*/
|
*/
|
||||||
void FlowStat::Invalidate()
|
void FlowStat::Invalidate()
|
||||||
{
|
{
|
||||||
assert(!this->shares.empty());
|
assert(!this->empty());
|
||||||
SharesMap new_shares;
|
|
||||||
uint i = 0;
|
uint i = 0;
|
||||||
for (SharesMap::iterator it(this->shares.begin()); it != this->shares.end(); ++it) {
|
for (iterator it(this->begin()); it != this->end(); ++it) {
|
||||||
new_shares[++i] = it->second;
|
if (it->first == this->unrestricted) this->unrestricted = i + 1;
|
||||||
if (it->first == this->unrestricted) this->unrestricted = i;
|
it->first = ++i;
|
||||||
}
|
}
|
||||||
this->shares.swap(new_shares);
|
assert(!this->empty() && this->unrestricted <= (this->end() - 1)->first);
|
||||||
assert(!this->shares.empty() && this->unrestricted <= (--this->shares.end())->first);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -4729,45 +4739,38 @@ void FlowStat::ChangeShare(StationID st, int flow)
|
|||||||
{
|
{
|
||||||
/* We assert only before changing as afterwards the shares can actually
|
/* We assert only before changing as afterwards the shares can actually
|
||||||
* be empty. In that case the whole flow stat must be deleted then. */
|
* be empty. In that case the whole flow stat must be deleted then. */
|
||||||
assert(!this->shares.empty());
|
assert(!this->empty());
|
||||||
|
|
||||||
uint removed_shares = 0;
|
|
||||||
uint added_shares = 0;
|
|
||||||
uint last_share = 0;
|
uint last_share = 0;
|
||||||
SharesMap new_shares;
|
for (iterator it(this->begin()); it != this->end(); ++it) {
|
||||||
for (SharesMap::iterator it(this->shares.begin()); it != this->shares.end(); ++it) {
|
|
||||||
if (it->second == st) {
|
if (it->second == st) {
|
||||||
if (flow < 0) {
|
|
||||||
uint share = it->first - last_share;
|
uint share = it->first - last_share;
|
||||||
if (flow == INT_MIN || (uint)(-flow) >= share) {
|
if (flow < 0 && (flow == INT_MIN || (uint)(-flow) >= share)) {
|
||||||
removed_shares += share;
|
|
||||||
if (it->first <= this->unrestricted) this->unrestricted -= share;
|
if (it->first <= this->unrestricted) this->unrestricted -= share;
|
||||||
if (flow != INT_MIN) flow += share;
|
this->erase_item(it, share);
|
||||||
last_share = it->first;
|
break; // remove the whole share
|
||||||
continue; // remove the whole share
|
|
||||||
}
|
|
||||||
removed_shares += (uint)(-flow);
|
|
||||||
} else {
|
|
||||||
added_shares += (uint)(flow);
|
|
||||||
}
|
}
|
||||||
if (it->first <= this->unrestricted) this->unrestricted += flow;
|
if (it->first <= this->unrestricted) this->unrestricted += flow;
|
||||||
|
for (; it != this->end(); ++it) {
|
||||||
/* If we don't continue above the whole flow has been added or
|
it->first += flow;
|
||||||
* removed. */
|
}
|
||||||
flow = 0;
|
flow = 0;
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
new_shares[it->first + added_shares - removed_shares] = it->second;
|
|
||||||
last_share = it->first;
|
last_share = it->first;
|
||||||
}
|
}
|
||||||
if (flow > 0) {
|
if (flow > 0) {
|
||||||
new_shares[last_share + (uint)flow] = st;
|
// must be non-empty here
|
||||||
|
last_share = (this->end() - 1)->first;
|
||||||
|
this->AppendShare(st, (uint)flow, true); // true to avoid changing this->unrestricted, which we fixup below
|
||||||
if (this->unrestricted < last_share) {
|
if (this->unrestricted < last_share) {
|
||||||
|
// Move to front to unrestrict
|
||||||
this->ReleaseShare(st);
|
this->ReleaseShare(st);
|
||||||
} else {
|
} else {
|
||||||
|
// First restricted item, so bump unrestricted count
|
||||||
this->unrestricted += flow;
|
this->unrestricted += flow;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
this->shares.swap(new_shares);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -4777,28 +4780,25 @@ void FlowStat::ChangeShare(StationID st, int flow)
|
|||||||
*/
|
*/
|
||||||
void FlowStat::RestrictShare(StationID st)
|
void FlowStat::RestrictShare(StationID st)
|
||||||
{
|
{
|
||||||
assert(!this->shares.empty());
|
assert(!this->empty());
|
||||||
uint flow = 0;
|
iterator it = this->begin();
|
||||||
|
const iterator end = this->end();
|
||||||
uint last_share = 0;
|
uint last_share = 0;
|
||||||
SharesMap new_shares;
|
for (; it != end; ++it) {
|
||||||
for (SharesMap::iterator it(this->shares.begin()); it != this->shares.end(); ++it) {
|
|
||||||
if (flow == 0) {
|
|
||||||
if (it->first > this->unrestricted) return; // Not present or already restricted.
|
if (it->first > this->unrestricted) return; // Not present or already restricted.
|
||||||
if (it->second == st) {
|
if (it->second == st) {
|
||||||
flow = it->first - last_share;
|
uint flow = it->first - last_share;
|
||||||
this->unrestricted -= flow;
|
this->unrestricted -= flow;
|
||||||
} else {
|
if (this->unrestricted == last_share) return; // No further action required
|
||||||
new_shares[it->first] = it->second;
|
const iterator last = end - 1;
|
||||||
|
for (iterator jt = it; jt != last; ++jt) {
|
||||||
|
*jt = { (jt + 1)->first - flow, (jt + 1)->second };
|
||||||
}
|
}
|
||||||
} else {
|
*last = { flow + (last - 1)->first, st };
|
||||||
new_shares[it->first - flow] = it->second;
|
return;
|
||||||
}
|
}
|
||||||
last_share = it->first;
|
last_share = it->first;
|
||||||
}
|
}
|
||||||
if (flow == 0) return;
|
|
||||||
new_shares[last_share + flow] = st;
|
|
||||||
this->shares.swap(new_shares);
|
|
||||||
assert(!this->shares.empty());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -4808,34 +4808,27 @@ void FlowStat::RestrictShare(StationID st)
|
|||||||
*/
|
*/
|
||||||
void FlowStat::ReleaseShare(StationID st)
|
void FlowStat::ReleaseShare(StationID st)
|
||||||
{
|
{
|
||||||
assert(!this->shares.empty());
|
assert(!this->empty());
|
||||||
uint flow = 0;
|
iterator it = this->end() - 1;
|
||||||
uint next_share = 0;
|
const iterator start = this->begin();
|
||||||
bool found = false;
|
for (; it >= start; --it) {
|
||||||
for (SharesMap::reverse_iterator it(this->shares.rbegin()); it != this->shares.rend(); ++it) {
|
if (it->first < this->unrestricted) return; // Already unrestricted
|
||||||
if (it->first < this->unrestricted) return; // Note: not <= as the share may hit the limit.
|
if (it->second == st) {
|
||||||
if (found) {
|
if (it - 1 >= start) {
|
||||||
flow = next_share - it->first;
|
uint flow = it->first - (it - 1)->first;
|
||||||
this->unrestricted += flow;
|
this->unrestricted += flow;
|
||||||
break;
|
if (it->first == this->unrestricted) return; // No further action required
|
||||||
|
for (iterator jt = it; jt != start; --jt) {
|
||||||
|
*jt = { (jt - 1)->first + flow, (jt - 1)->second };
|
||||||
|
}
|
||||||
|
*start = { flow, st };
|
||||||
} else {
|
} else {
|
||||||
if (it->first == this->unrestricted) return; // !found -> Limit not hit.
|
// already at start
|
||||||
if (it->second == st) found = true;
|
this->unrestricted = it->first;
|
||||||
}
|
}
|
||||||
next_share = it->first;
|
return;
|
||||||
}
|
|
||||||
if (flow == 0) return;
|
|
||||||
SharesMap new_shares;
|
|
||||||
new_shares[flow] = st;
|
|
||||||
for (SharesMap::iterator it(this->shares.begin()); it != this->shares.end(); ++it) {
|
|
||||||
if (it->second != st) {
|
|
||||||
new_shares[flow + it->first] = it->second;
|
|
||||||
} else {
|
|
||||||
flow = 0;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
this->shares.swap(new_shares);
|
|
||||||
assert(!this->shares.empty());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -4846,14 +4839,12 @@ void FlowStat::ReleaseShare(StationID st)
|
|||||||
void FlowStat::ScaleToMonthly(uint runtime)
|
void FlowStat::ScaleToMonthly(uint runtime)
|
||||||
{
|
{
|
||||||
assert(runtime > 0);
|
assert(runtime > 0);
|
||||||
SharesMap new_shares;
|
|
||||||
uint share = 0;
|
uint share = 0;
|
||||||
for (SharesMap::iterator i = this->shares.begin(); i != this->shares.end(); ++i) {
|
for (iterator i = this->begin(); i != this->end(); ++i) {
|
||||||
share = max(share + 1, i->first * 30 / runtime);
|
share = max(share + 1, i->first * 30 / runtime);
|
||||||
new_shares[share] = i->second;
|
|
||||||
if (this->unrestricted == i->first) this->unrestricted = share;
|
if (this->unrestricted == i->first) this->unrestricted = share;
|
||||||
|
i->first = share;
|
||||||
}
|
}
|
||||||
this->shares.swap(new_shares);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -4869,7 +4860,7 @@ void FlowStatMap::AddFlow(StationID origin, StationID via, uint flow)
|
|||||||
this->insert(FlowStat(origin, via, flow));
|
this->insert(FlowStat(origin, via, flow));
|
||||||
} else {
|
} else {
|
||||||
origin_it->ChangeShare(via, flow);
|
origin_it->ChangeShare(via, flow);
|
||||||
assert(!origin_it->GetShares()->empty());
|
assert(!origin_it->empty());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -4891,7 +4882,7 @@ void FlowStatMap::PassOnFlow(StationID origin, StationID via, uint flow)
|
|||||||
} else {
|
} else {
|
||||||
prev_it->ChangeShare(via, flow);
|
prev_it->ChangeShare(via, flow);
|
||||||
prev_it->ChangeShare(INVALID_STATION, flow);
|
prev_it->ChangeShare(INVALID_STATION, flow);
|
||||||
assert(!prev_it->GetShares()->empty());
|
assert(!prev_it->empty());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -4914,7 +4905,7 @@ void FlowStatMap::FinalizeLocalConsumption(StationID self)
|
|||||||
|
|
||||||
/* If the local share is used up there must be a share for some
|
/* If the local share is used up there must be a share for some
|
||||||
* remote station. */
|
* remote station. */
|
||||||
assert(!fs.GetShares()->empty());
|
assert(!fs.empty());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -4930,7 +4921,7 @@ StationIDStack FlowStatMap::DeleteFlows(StationID via)
|
|||||||
for (FlowStatMap::iterator f_it = this->begin(); f_it != this->end();) {
|
for (FlowStatMap::iterator f_it = this->begin(); f_it != this->end();) {
|
||||||
FlowStat &s_flows = *f_it;
|
FlowStat &s_flows = *f_it;
|
||||||
s_flows.ChangeShare(via, INT_MIN);
|
s_flows.ChangeShare(via, INT_MIN);
|
||||||
if (s_flows.GetShares()->empty()) {
|
if (s_flows.empty()) {
|
||||||
ret.Push(f_it->GetOrigin());
|
ret.Push(f_it->GetOrigin());
|
||||||
f_it = this->erase(f_it);
|
f_it = this->erase(f_it);
|
||||||
} else {
|
} else {
|
||||||
@@ -4951,17 +4942,6 @@ void FlowStatMap::RestrictFlows(StationID via)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Release all flows at a station for specific cargo and destination.
|
|
||||||
* @param via Remote station of flows to be released.
|
|
||||||
*/
|
|
||||||
void FlowStatMap::ReleaseFlows(StationID via)
|
|
||||||
{
|
|
||||||
for (FlowStatMap::iterator it = this->begin(); it != this->end(); ++it) {
|
|
||||||
it->ReleaseShare(via);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the sum of all flows from this FlowStatMap.
|
* Get the sum of all flows from this FlowStatMap.
|
||||||
* @return sum of all flows.
|
* @return sum of all flows.
|
||||||
@@ -4970,7 +4950,7 @@ uint FlowStatMap::GetFlow() const
|
|||||||
{
|
{
|
||||||
uint ret = 0;
|
uint ret = 0;
|
||||||
for (FlowStatMap::const_iterator i = this->begin(); i != this->end(); ++i) {
|
for (FlowStatMap::const_iterator i = this->begin(); i != this->end(); ++i) {
|
||||||
ret += (--(i->GetShares()->end()))->first;
|
ret += (i->end() - 1)->first;
|
||||||
}
|
}
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
@@ -4998,7 +4978,7 @@ uint FlowStatMap::GetFlowFrom(StationID from) const
|
|||||||
{
|
{
|
||||||
FlowStatMap::const_iterator i = this->find(from);
|
FlowStatMap::const_iterator i = this->find(from);
|
||||||
if (i == this->end()) return 0;
|
if (i == this->end()) return 0;
|
||||||
return (--(i->GetShares()->end()))->first;
|
return (i->end() - 1)->first;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@@ -1515,9 +1515,8 @@ struct StationViewWindow : public Window {
|
|||||||
for (FlowStatMap::const_iterator it = flows.begin(); it != flows.end(); ++it) {
|
for (FlowStatMap::const_iterator it = flows.begin(); it != flows.end(); ++it) {
|
||||||
StationID from = it->GetOrigin();
|
StationID from = it->GetOrigin();
|
||||||
CargoDataEntry *source_entry = cargo_entry->InsertOrRetrieve(from);
|
CargoDataEntry *source_entry = cargo_entry->InsertOrRetrieve(from);
|
||||||
const FlowStat::SharesMap *shares = it->GetShares();
|
|
||||||
uint32 prev_count = 0;
|
uint32 prev_count = 0;
|
||||||
for (FlowStat::SharesMap::const_iterator flow_it = shares->begin(); flow_it != shares->end(); ++flow_it) {
|
for (FlowStat::const_iterator flow_it = it->begin(); flow_it != it->end(); ++flow_it) {
|
||||||
StationID via = flow_it->second;
|
StationID via = flow_it->second;
|
||||||
CargoDataEntry *via_entry = source_entry->InsertOrRetrieve(via);
|
CargoDataEntry *via_entry = source_entry->InsertOrRetrieve(via);
|
||||||
if (via == this->window_number) {
|
if (via == this->window_number) {
|
||||||
@@ -1546,9 +1545,8 @@ struct StationViewWindow : public Window {
|
|||||||
const FlowStatMap &flowmap = Station::Get(next)->goods[cargo].flows;
|
const FlowStatMap &flowmap = Station::Get(next)->goods[cargo].flows;
|
||||||
FlowStatMap::const_iterator map_it = flowmap.find(source);
|
FlowStatMap::const_iterator map_it = flowmap.find(source);
|
||||||
if (map_it != flowmap.end()) {
|
if (map_it != flowmap.end()) {
|
||||||
const FlowStat::SharesMap *shares = map_it->GetShares();
|
|
||||||
uint32 prev_count = 0;
|
uint32 prev_count = 0;
|
||||||
for (FlowStat::SharesMap::const_iterator i = shares->begin(); i != shares->end(); ++i) {
|
for (FlowStat::const_iterator i = map_it->begin(); i != map_it->end(); ++i) {
|
||||||
tmp.InsertOrRetrieve(i->second)->Update(i->first - prev_count);
|
tmp.InsertOrRetrieve(i->second)->Update(i->first - prev_count);
|
||||||
prev_count = i->first;
|
prev_count = i->first;
|
||||||
}
|
}
|
||||||
@@ -1598,8 +1596,7 @@ struct StationViewWindow : public Window {
|
|||||||
for (FlowStatMap::const_iterator it = flows.begin(); it != flows.end(); ++it) {
|
for (FlowStatMap::const_iterator it = flows.begin(); it != flows.end(); ++it) {
|
||||||
StationID from = it->GetOrigin();
|
StationID from = it->GetOrigin();
|
||||||
const CargoDataEntry *source_entry = source_dest->Retrieve(from);
|
const CargoDataEntry *source_entry = source_dest->Retrieve(from);
|
||||||
const FlowStat::SharesMap *shares = it->GetShares();
|
for (FlowStat::const_iterator flow_it = it->begin(); flow_it != it->end(); ++flow_it) {
|
||||||
for (FlowStat::SharesMap::const_iterator flow_it = shares->begin(); flow_it != shares->end(); ++flow_it) {
|
|
||||||
const CargoDataEntry *via_entry = source_entry->Retrieve(flow_it->second);
|
const CargoDataEntry *via_entry = source_entry->Retrieve(flow_it->second);
|
||||||
for (CargoDataSet::iterator dest_it = via_entry->Begin(); dest_it != via_entry->End(); ++dest_it) {
|
for (CargoDataSet::iterator dest_it = via_entry->Begin(); dest_it != via_entry->End(); ++dest_it) {
|
||||||
CargoDataEntry *dest_entry = *dest_it;
|
CargoDataEntry *dest_entry = *dest_it;
|
||||||
|
Reference in New Issue
Block a user