Codechange: Store accepted and produced cargo in vector instead of array.

Most industries do not use the full 16 slots, so this can save a little memory and iteration time.
This commit is contained in:
Peter Nelson 2023-07-14 11:49:11 +01:00 committed by Peter Nelson
parent 00e0021e3a
commit 3de8853e29
5 changed files with 99 additions and 58 deletions

View File

@ -90,14 +90,14 @@ struct Industry : IndustryPool::PoolItem<&_industry_pool> {
TimerGameEconomy::Date last_accepted; ///< Last day cargo was accepted by this industry
};
using ProducedCargoArray = std::array<ProducedCargo, INDUSTRY_NUM_OUTPUTS>;
using AcceptedCargoArray = std::array<AcceptedCargo, INDUSTRY_NUM_INPUTS>;
using ProducedCargoes = std::vector<ProducedCargo>;
using AcceptedCargoes = std::vector<AcceptedCargo>;
TileArea location; ///< Location of the industry
Town *town; ///< Nearest town
Station *neutral_station; ///< Associated neutral station
ProducedCargoArray produced; ///< INDUSTRY_NUM_OUTPUTS production cargo slots
AcceptedCargoArray accepted; ///< INDUSTRY_NUM_INPUTS input cargo slots
ProducedCargoes produced; ///< produced cargo slots
AcceptedCargoes accepted; ///< accepted cargo slots
uint8_t prod_level; ///< general production level
uint16_t counter; ///< used for animation and/or production (if available cargo)
@ -166,7 +166,7 @@ struct Industry : IndustryPool::PoolItem<&_industry_pool> {
* @param cargo CargoID to find.
* @return Iterator pointing to produced cargo slot if it exists, or the end iterator.
*/
inline ProducedCargoArray::iterator GetCargoProduced(CargoID cargo)
inline ProducedCargoes::iterator GetCargoProduced(CargoID cargo)
{
if (!IsValidCargoID(cargo)) return std::end(this->produced);
return std::find_if(std::begin(this->produced), std::end(this->produced), [&cargo](const auto &p) { return p.cargo == cargo; });
@ -177,7 +177,7 @@ struct Industry : IndustryPool::PoolItem<&_industry_pool> {
* @param cargo CargoID to find.
* @return Iterator pointing to produced cargo slot if it exists, or the end iterator.
*/
inline ProducedCargoArray::const_iterator GetCargoProduced(CargoID cargo) const
inline ProducedCargoes::const_iterator GetCargoProduced(CargoID cargo) const
{
if (!IsValidCargoID(cargo)) return std::end(this->produced);
return std::find_if(std::begin(this->produced), std::end(this->produced), [&cargo](const auto &p) { return p.cargo == cargo; });
@ -188,7 +188,7 @@ struct Industry : IndustryPool::PoolItem<&_industry_pool> {
* @param cargo CargoID to find.
* @return Iterator pointing to accepted cargo slot if it exists, or the end iterator.
*/
inline AcceptedCargoArray::iterator GetCargoAccepted(CargoID cargo)
inline AcceptedCargoes::iterator GetCargoAccepted(CargoID cargo)
{
if (!IsValidCargoID(cargo)) return std::end(this->accepted);
return std::find_if(std::begin(this->accepted), std::end(this->accepted), [&cargo](const auto &a) { return a.cargo == cargo; });
@ -332,4 +332,6 @@ enum IndustryDirectoryInvalidateWindowData {
IDIWD_FORCE_RESORT,
};
void TrimIndustryAcceptedProduced(Industry *ind);
#endif /* INDUSTRY_H */

View File

@ -1788,15 +1788,19 @@ static void DoCreateNewIndustry(Industry *i, TileIndex tile, IndustryType type,
i->type = type;
Industry::IncIndustryTypeCount(type);
for (auto it = std::begin(i->produced); it != std::end(i->produced); ++it) {
size_t index = it - std::begin(i->produced);
it->cargo = indspec->produced_cargo[index];
it->rate = indspec->production_rate[index];
for (size_t index = 0; index < lengthof(indspec->produced_cargo); ++index) {
if (!IsValidCargoID(indspec->produced_cargo[index])) break;
Industry::ProducedCargo &p = i->produced.emplace_back();
p.cargo = indspec->produced_cargo[index];
p.rate = indspec->production_rate[index];
}
for (auto it = std::begin(i->accepted); it != std::end(i->accepted); ++it) {
size_t index = it - std::begin(i->accepted);
it->cargo = indspec->accepts_cargo[index];
for (size_t index = 0; index < lengthof(indspec->accepts_cargo); ++index) {
if (!IsValidCargoID(indspec->accepts_cargo[index])) break;
Industry::AcceptedCargo &a = i->accepted.emplace_back();
a.cargo = indspec->accepts_cargo[index];
}
/* Randomize inital production if non-original economy is used and there are no production related callbacks. */
@ -1870,7 +1874,7 @@ static void DoCreateNewIndustry(Industry *i, TileIndex tile, IndustryType type,
if (HasBit(indspec->callback_mask, CBM_IND_INPUT_CARGO_TYPES)) {
/* Clear all input cargo types */
for (auto &a : i->accepted) a.cargo = INVALID_CARGO;
i->accepted.clear();
/* Query actual types */
uint maxcargoes = (indspec->behaviour & INDUSTRYBEH_CARGOTYPES_UNLIMITED) ? static_cast<uint>(i->accepted.size()) : 3;
for (uint j = 0; j < maxcargoes; j++) {
@ -1884,7 +1888,12 @@ static void DoCreateNewIndustry(Industry *i, TileIndex tile, IndustryType type,
/* Industries without "unlimited" cargo types support depend on the specific order/slots of cargo types.
* They need to be able to blank out specific slots without aborting the callback sequence,
* and solve this by returning undefined cargo indexes. Skip these. */
if (!IsValidCargoID(cargo) && !(indspec->behaviour & INDUSTRYBEH_CARGOTYPES_UNLIMITED)) continue;
if (!IsValidCargoID(cargo) && !(indspec->behaviour & INDUSTRYBEH_CARGOTYPES_UNLIMITED)) {
/* As slots are allocated as needed now, this means we do need to add a slot for the invalid cargo. */
Industry::AcceptedCargo &a = i->accepted.emplace_back();
a.cargo = INVALID_CARGO;
continue;
}
/* Verify valid cargo */
if (std::find(indspec->accepts_cargo, endof(indspec->accepts_cargo), cargo) == endof(indspec->accepts_cargo)) {
/* Cargo not in spec, error in NewGRF */
@ -1896,13 +1905,14 @@ static void DoCreateNewIndustry(Industry *i, TileIndex tile, IndustryType type,
ErrorUnknownCallbackResult(indspec->grf_prop.grffile->grfid, CBID_INDUSTRY_INPUT_CARGO_TYPES, res);
break;
}
i->accepted[j].cargo = cargo;
Industry::AcceptedCargo &a = i->accepted.emplace_back();
a.cargo = cargo;
}
}
if (HasBit(indspec->callback_mask, CBM_IND_OUTPUT_CARGO_TYPES)) {
/* Clear all output cargo types */
for (auto &p : i->produced) p.cargo = INVALID_CARGO;
i->produced.clear();
/* Query actual types */
uint maxcargoes = (indspec->behaviour & INDUSTRYBEH_CARGOTYPES_UNLIMITED) ? static_cast<uint>(i->produced.size()) : 2;
for (uint j = 0; j < maxcargoes; j++) {
@ -1914,7 +1924,12 @@ static void DoCreateNewIndustry(Industry *i, TileIndex tile, IndustryType type,
}
CargoID cargo = GetCargoTranslation(GB(res, 0, 8), indspec->grf_prop.grffile);
/* Allow older GRFs to skip slots. */
if (!IsValidCargoID(cargo) && !(indspec->behaviour & INDUSTRYBEH_CARGOTYPES_UNLIMITED)) continue;
if (!IsValidCargoID(cargo) && !(indspec->behaviour & INDUSTRYBEH_CARGOTYPES_UNLIMITED)) {
/* As slots are allocated as needed now, this means we do need to add a slot for the invalid cargo. */
Industry::ProducedCargo &p = i->produced.emplace_back();
p.cargo = INVALID_CARGO;
continue;
}
/* Verify valid cargo */
if (std::find(indspec->produced_cargo, endof(indspec->produced_cargo), cargo) == endof(indspec->produced_cargo)) {
/* Cargo not in spec, error in NewGRF */
@ -1926,7 +1941,8 @@ static void DoCreateNewIndustry(Industry *i, TileIndex tile, IndustryType type,
ErrorUnknownCallbackResult(indspec->grf_prop.grffile->grfid, CBID_INDUSTRY_OUTPUT_CARGO_TYPES, res);
break;
}
i->produced[j].cargo = cargo;
Industry::ProducedCargo &p = i->produced.emplace_back();
p.cargo = cargo;
}
}
@ -3212,3 +3228,18 @@ bool IndustryCompare::operator() (const IndustryListEntry &lhs, const IndustryLi
/* Compare by distance first and use index as a tiebreaker. */
return std::tie(lhs.distance, lhs.industry->index) < std::tie(rhs.distance, rhs.industry->index);
}
/**
* Remove unused industry accepted/produced slots -- entries after the last slot with valid cargo.
* @param ind Industry to trim slots.
*/
void TrimIndustryAcceptedProduced(Industry *ind)
{
auto ita = std::find_if(std::rbegin(ind->accepted), std::rend(ind->accepted), [](const auto &a) { return IsValidCargoID(a.cargo); });
ind->accepted.erase(ita.base(), std::end(ind->accepted));
ind->accepted.shrink_to_fit();
auto itp = std::find_if(std::rbegin(ind->produced), std::rend(ind->produced), [](const auto &p) { return IsValidCargoID(p.cargo); });
ind->produced.erase(itp.base(), std::end(ind->produced));
ind->produced.shrink_to_fit();
}

View File

@ -3044,16 +3044,6 @@ bool AfterLoadGame()
if (IsSavegameVersionBefore(SLV_EXTEND_INDUSTRY_CARGO_SLOTS)) {
/* Make sure added industry cargo slots are cleared */
for (Industry *i : Industry::Iterate()) {
for (auto it = std::begin(i->produced) + 2; it != std::end(i->produced); ++it) {
it->cargo = INVALID_CARGO;
it->waiting = 0;
it->rate = 0;
it->history = {};
}
for (auto it = std::begin(i->accepted) + 3; it != std::end(i->accepted); ++it) {
it->cargo = INVALID_CARGO;
it->waiting = 0;
}
/* Make sure last_cargo_accepted_at is copied to elements for every valid input cargo.
* The loading routine should put the original singular value into the first array element. */
for (auto &a : i->accepted) {

View File

@ -39,11 +39,12 @@ public:
void Load(Industry *i) const override
{
size_t len = SlGetStructListLength(i->accepted.size());
size_t len = SlGetStructListLength(INDUSTRY_NUM_INPUTS);
for (auto &a : i->accepted) {
if (--len > i->accepted.size()) break; // unsigned so wraps after hitting zero.
SlObject(&a, this->GetDescription());
i->accepted.reserve(len);
for (size_t index = 0; index < len; ++index) {
auto &a = i->accepted.emplace_back();
SlObject(&a, this->GetLoadDescription());
}
}
@ -115,11 +116,12 @@ public:
void Load(Industry *i) const override
{
size_t len = SlGetStructListLength(i->produced.size());
size_t len = SlGetStructListLength(INDUSTRY_NUM_OUTPUTS);
for (auto &p : i->produced) {
if (--len > i->produced.size()) break; // unsigned so wraps after hitting zero.
SlObject(&p, this->GetDescription());
i->produced.reserve(len);
for (size_t index = 0; index < len; ++index) {
auto &p = i->produced.emplace_back();
SlObject(&p, this->GetLoadDescription());
}
}
@ -214,17 +216,19 @@ struct INDYChunkHandler : ChunkHandler {
}
}
void LoadMoveAcceptsProduced(Industry *i) const
void LoadMoveAcceptsProduced(Industry *i, uint inputs, uint outputs) const
{
for (uint j = 0; j != INDUSTRY_NUM_INPUTS; ++j) {
auto &a = i->accepted[j];
i->accepted.reserve(inputs);
for (uint j = 0; j != inputs; ++j) {
auto &a = i->accepted.emplace_back();
a.cargo = SlIndustryAccepted::old_cargo[j];
a.waiting = SlIndustryAccepted::old_waiting[j];
a.last_accepted = SlIndustryAccepted::old_last_accepted[j];
}
for (uint j = 0; j != INDUSTRY_NUM_OUTPUTS; ++j) {
auto &p = i->produced[j];
i->produced.reserve(outputs);
for (uint j = 0; j != outputs; ++j) {
auto &p = i->produced.emplace_back();
p.cargo = SlIndustryProduced::old_cargo[j];
p.waiting = SlIndustryProduced::old_waiting[j];
p.rate = SlIndustryProduced::old_rate[j];
@ -256,8 +260,13 @@ struct INDYChunkHandler : ChunkHandler {
i->psa = new PersistentStorage(0, 0, 0);
std::copy(std::begin(_old_ind_persistent_storage.storage), std::end(_old_ind_persistent_storage.storage), std::begin(i->psa->storage));
}
if (IsSavegameVersionBefore(SLV_INDUSTRY_CARGO_REORGANISE)) LoadMoveAcceptsProduced(i);
if (IsSavegameVersionBefore(SLV_EXTEND_INDUSTRY_CARGO_SLOTS)) {
LoadMoveAcceptsProduced(i, 3, 2);
} else if (IsSavegameVersionBefore(SLV_INDUSTRY_CARGO_REORGANISE)) {
LoadMoveAcceptsProduced(i, INDUSTRY_NUM_INPUTS, INDUSTRY_NUM_OUTPUTS);
}
Industry::IncIndustryTypeCount(i->type);
TrimIndustryAcceptedProduced(i);
}
}

View File

@ -804,6 +804,10 @@ static bool LoadOldStation(LoadgameState *ls, int num)
return true;
}
/* Old save games always have 3 input and 2 output slots per industry. */
static std::array<Industry::AcceptedCargo, 3> _old_accepted{};
static std::array<Industry::ProducedCargo, 2> _old_produced{};
static const OldChunks industry_chunk[] = {
OCL_SVAR( OC_TILE, Industry, location.tile ),
OCL_VAR ( OC_UINT32, 1, &_old_town_index ),
@ -811,29 +815,29 @@ static const OldChunks industry_chunk[] = {
OCL_SVAR( OC_FILE_U8 | OC_VAR_U16, Industry, location.h ),
OCL_NULL( 2 ), ///< used to be industry's produced_cargo
OCL_SVAR( OC_TTD | OC_UINT16, Industry, produced[0].waiting ),
OCL_SVAR( OC_TTD | OC_UINT16, Industry, produced[1].waiting ),
OCL_SVAR( OC_TTO | OC_FILE_U8 | OC_VAR_U16, Industry, produced[0].waiting ),
OCL_SVAR( OC_TTO | OC_FILE_U8 | OC_VAR_U16, Industry, produced[1].waiting ),
OCL_VAR( OC_TTD | OC_UINT16, 1, &_old_produced[0].waiting ),
OCL_VAR( OC_TTD | OC_UINT16, 1, &_old_produced[1].waiting ),
OCL_VAR( OC_TTO | OC_FILE_U8 | OC_VAR_U16, 1, &_old_produced[0].waiting ),
OCL_VAR( OC_TTO | OC_FILE_U8 | OC_VAR_U16, 1, &_old_produced[1].waiting ),
OCL_SVAR( OC_UINT8, Industry, produced[0].rate ),
OCL_SVAR( OC_UINT8, Industry, produced[1].rate ),
OCL_VAR( OC_UINT8, 1, &_old_produced[0].rate ),
OCL_VAR( OC_UINT8, 1, &_old_produced[1].rate ),
OCL_NULL( 3 ), ///< used to be industry's accepts_cargo
OCL_SVAR( OC_UINT8, Industry, prod_level ),
OCL_SVAR( OC_UINT16, Industry, produced[0].history[THIS_MONTH].production ),
OCL_SVAR( OC_UINT16, Industry, produced[1].history[THIS_MONTH].production ),
OCL_SVAR( OC_UINT16, Industry, produced[0].history[THIS_MONTH].transported ),
OCL_SVAR( OC_UINT16, Industry, produced[1].history[THIS_MONTH].transported ),
OCL_VAR( OC_UINT16, 1, &_old_produced[0].history[THIS_MONTH].production ),
OCL_VAR( OC_UINT16, 1, &_old_produced[1].history[THIS_MONTH].production ),
OCL_VAR( OC_UINT16, 1, &_old_produced[0].history[THIS_MONTH].transported ),
OCL_VAR( OC_UINT16, 1, &_old_produced[1].history[THIS_MONTH].transported ),
OCL_NULL( 2 ), ///< last_month_pct_transported, now computed on the fly
OCL_SVAR( OC_UINT16, Industry, produced[0].history[LAST_MONTH].production ),
OCL_SVAR( OC_UINT16, Industry, produced[1].history[LAST_MONTH].production ),
OCL_SVAR( OC_UINT16, Industry, produced[0].history[LAST_MONTH].transported ),
OCL_SVAR( OC_UINT16, Industry, produced[1].history[LAST_MONTH].transported ),
OCL_VAR( OC_UINT16, 1, &_old_produced[0].history[LAST_MONTH].production ),
OCL_VAR( OC_UINT16, 1, &_old_produced[1].history[LAST_MONTH].production ),
OCL_VAR( OC_UINT16, 1, &_old_produced[0].history[LAST_MONTH].transported ),
OCL_VAR( OC_UINT16, 1, &_old_produced[1].history[LAST_MONTH].transported ),
OCL_SVAR( OC_UINT8, Industry, type ),
OCL_SVAR( OC_TTO | OC_FILE_U8 | OC_VAR_U16, Industry, counter ),
@ -854,6 +858,10 @@ static bool LoadOldIndustry(LoadgameState *ls, int num)
if (!LoadChunk(ls, i, industry_chunk)) return false;
if (i->location.tile != 0) {
/* Copy data from old fixed arrays to industry. */
std::copy(std::begin(_old_accepted), std::end(_old_accepted), std::back_inserter(i->accepted));
std::copy(std::begin(_old_produced), std::end(_old_produced), std::back_inserter(i->produced));
i->town = RemapTown(i->location.tile);
if (_savegame_type == SGT_TTO) {
@ -867,6 +875,7 @@ static bool LoadOldIndustry(LoadgameState *ls, int num)
}
Industry::IncIndustryTypeCount(i->type);
TrimIndustryAcceptedProduced(i);
} else {
delete i;
}