use const char *, not std::string for efficiency

so we can actually process large maps without OOMing
develop
myk002 2021-09-10 14:19:50 -07:00 committed by Myk
parent 772e386427
commit 42e04fc6ec
1 changed files with 177 additions and 159 deletions

@ -113,55 +113,72 @@ struct tile_context {
df::building* b = NULL; df::building* b = NULL;
}; };
static pair<uint32_t, uint32_t> get_building_size(df::building *b) { // the number of different strings we use is very small so we use a string cache
return pair<uint32_t, uint32_t>(b->x2 - b->x1 + 1, b->y2 - b->y1 + 1); // to limit the number of memory allocations we make. this significantly speeds
} // up processing and allows us to handle very large maps (e.g. 16x16 embarks)
// without running out of memory.
static const char * if_pretty(const tile_context &ctx, const char *c) { // if NULL is passed as the str, the cache is cleared
return ctx.pretty ? c : ""; static const char * cache(const char *str) {
} // this local static assumes that no two blueprints are being generated at
// the same time, which is currently ensured by the higher-level DFHack
static void get_tile_dig(const df::coord &pos, const tile_context &, // command handling code. if this assumption ever becomes untrue, we'll
string &str) { // need to protect the cache with thread synchronization primitives.
static std::set<string> _cache;
if (!str) {
_cache.clear();
return NULL;
}
return _cache.emplace(str).first->c_str();
}
static const char * get_tile_dig(const df::coord &pos, const tile_context &) {
df::tiletype *tt = Maps::getTileType(pos); df::tiletype *tt = Maps::getTileType(pos);
switch (tileShape(tt ? *tt : tiletype::Void)) switch (tileShape(tt ? *tt : tiletype::Void))
{ {
case tiletype_shape::EMPTY: case tiletype_shape::EMPTY:
case tiletype_shape::RAMP_TOP: case tiletype_shape::RAMP_TOP:
str = "h"; break; return "h";
case tiletype_shape::FLOOR: case tiletype_shape::FLOOR:
case tiletype_shape::BOULDER: case tiletype_shape::BOULDER:
case tiletype_shape::PEBBLES: case tiletype_shape::PEBBLES:
case tiletype_shape::BROOK_TOP: case tiletype_shape::BROOK_TOP:
str = "d"; break; return "d";
case tiletype_shape::FORTIFICATION: case tiletype_shape::FORTIFICATION:
str = "F"; break; return "F";
case tiletype_shape::STAIR_UP: case tiletype_shape::STAIR_UP:
str = "u"; break; return "u";
case tiletype_shape::STAIR_DOWN: case tiletype_shape::STAIR_DOWN:
str = "j"; break; return "j";
case tiletype_shape::STAIR_UPDOWN: case tiletype_shape::STAIR_UPDOWN:
str = "i"; break; return "i";
case tiletype_shape::RAMP: case tiletype_shape::RAMP:
str = "r"; break; return "r";
case tiletype_shape::WALL: case tiletype_shape::WALL:
default: default:
break; return NULL;
} }
} }
static void do_block_building(const tile_context &ctx, string &str, string s, static pair<uint32_t, uint32_t> get_building_size(df::building *b) {
bool at_target_pos, bool *add_size = NULL) { return pair<uint32_t, uint32_t>(b->x2 - b->x1 + 1, b->y2 - b->y1 + 1);
}
static const char * if_pretty(const tile_context &ctx, const char *c) {
return ctx.pretty ? c : "";
}
static const char * do_block_building(const tile_context &ctx, const char *s,
bool at_target_pos,
bool *add_size = NULL) {
if(!at_target_pos) { if(!at_target_pos) {
str = if_pretty(ctx, "`"); return if_pretty(ctx, "`");
return;
} }
str = s;
if (add_size) if (add_size)
*add_size = true; *add_size = true;
return s;
} }
static string get_bridge_str(df::building *b) { static const char * get_bridge_str(df::building *b) {
df::building_bridgest *bridge = virtual_cast<df::building_bridgest>(b); df::building_bridgest *bridge = virtual_cast<df::building_bridgest>(b);
if (!bridge) if (!bridge)
return "g"; return "g";
@ -177,13 +194,13 @@ static string get_bridge_str(df::building *b) {
} }
} }
static string get_siege_str(df::building *b) { static const char * get_siege_str(df::building *b) {
df::building_siegeenginest *se = df::building_siegeenginest *se =
virtual_cast<df::building_siegeenginest>(b); virtual_cast<df::building_siegeenginest>(b);
return !se || se->type == df::siegeengine_type::Catapult ? "ic" : "ib"; return !se || se->type == df::siegeengine_type::Catapult ? "ic" : "ib";
} }
static string get_workshop_str(df::building *b) { static const char * get_workshop_str(df::building *b) {
df::building_workshopst *ws = virtual_cast<df::building_workshopst>(b); df::building_workshopst *ws = virtual_cast<df::building_workshopst>(b);
if (!ws) if (!ws)
return "~"; return "~";
@ -219,7 +236,7 @@ static string get_workshop_str(df::building *b) {
} }
} }
static string get_furnace_str(df::building *b) { static const char * get_furnace_str(df::building *b) {
df::building_furnacest *furnace = virtual_cast<df::building_furnacest>(b); df::building_furnacest *furnace = virtual_cast<df::building_furnacest>(b);
if (!furnace) if (!furnace)
return "~"; return "~";
@ -238,7 +255,7 @@ static string get_furnace_str(df::building *b) {
} }
} }
static string get_construction_str(df::building *b) { static const char * get_construction_str(df::building *b) {
df::building_constructionst *cons = df::building_constructionst *cons =
virtual_cast<df::building_constructionst>(b); virtual_cast<df::building_constructionst>(b);
if (!cons) if (!cons)
@ -288,7 +305,7 @@ static string get_construction_str(df::building *b) {
} }
} }
static string get_trap_str(df::building *b) { static const char * get_trap_str(df::building *b) {
df::building_trapst *trap = virtual_cast<df::building_trapst>(b); df::building_trapst *trap = virtual_cast<df::building_trapst>(b);
if (!trap) if (!trap)
return "~"; return "~";
@ -322,14 +339,14 @@ static string get_trap_str(df::building *b) {
case 500: buf << "a"; case 500: buf << "a";
case 10000: buf << "a"; case 10000: buf << "a";
} }
return buf.str(); return cache(buf.str().c_str());
} }
default: default:
return "~"; return "~";
} }
} }
static string get_screw_pump_str(df::building *b) { static const char * get_screw_pump_str(df::building *b) {
df::building_screw_pumpst *sp = virtual_cast<df::building_screw_pumpst>(b); df::building_screw_pumpst *sp = virtual_cast<df::building_screw_pumpst>(b);
if (!sp) if (!sp)
return "~"; return "~";
@ -345,7 +362,7 @@ static string get_screw_pump_str(df::building *b) {
} }
} }
static string get_water_wheel_str(df::building *b) { static const char * get_water_wheel_str(df::building *b) {
df::building_water_wheelst *ww = df::building_water_wheelst *ww =
virtual_cast<df::building_water_wheelst>(b); virtual_cast<df::building_water_wheelst>(b);
if (!ww) if (!ww)
@ -354,7 +371,7 @@ static string get_water_wheel_str(df::building *b) {
return ww->is_vertical ? "Mw" : "Mws"; return ww->is_vertical ? "Mw" : "Mws";
} }
static string get_axle_str(df::building *b) { static const char * get_axle_str(df::building *b) {
df::building_axle_horizontalst *ah = df::building_axle_horizontalst *ah =
virtual_cast<df::building_axle_horizontalst>(b); virtual_cast<df::building_axle_horizontalst>(b);
if (!ah) if (!ah)
@ -363,7 +380,7 @@ static string get_axle_str(df::building *b) {
return ah->is_vertical ? "Mhs" : "Mh"; return ah->is_vertical ? "Mhs" : "Mh";
} }
static string get_roller_str(df::building *b) { static const char * get_roller_str(df::building *b) {
df::building_rollersst *r = virtual_cast<df::building_rollersst>(b); df::building_rollersst *r = virtual_cast<df::building_rollersst>(b);
if (!r) if (!r)
return "~"; return "~";
@ -378,192 +395,192 @@ static string get_roller_str(df::building *b) {
} }
} }
static string get_expansion_str(df::building *b) { static const char * get_build_keys(const df::coord &pos,
pair<uint32_t, uint32_t> size = get_building_size(b); const tile_context &ctx,
std::ostringstream s; bool &add_size) {
s << "(" << size.first << "x" << size.second << ")";
return s.str();
}
static void get_tile_build(const df::coord &pos, const tile_context &ctx,
string &str) {
if (!ctx.b || ctx.b->getType() == building_type::Stockpile) {
return;
}
bool at_nw_corner = static_cast<int32_t>(pos.x) == ctx.b->x1 bool at_nw_corner = static_cast<int32_t>(pos.x) == ctx.b->x1
&& static_cast<int32_t>(pos.y) == ctx.b->y1; && static_cast<int32_t>(pos.y) == ctx.b->y1;
bool at_se_corner = static_cast<int32_t>(pos.x) == ctx.b->x2 bool at_se_corner = static_cast<int32_t>(pos.x) == ctx.b->x2
&& static_cast<int32_t>(pos.y) == ctx.b->y2; && static_cast<int32_t>(pos.y) == ctx.b->y2;
bool at_center = static_cast<int32_t>(pos.x) == ctx.b->centerx bool at_center = static_cast<int32_t>(pos.x) == ctx.b->centerx
&& static_cast<int32_t>(pos.y) == ctx.b->centery; && static_cast<int32_t>(pos.y) == ctx.b->centery;
bool add_size = false;
switch(ctx.b->getType()) { switch(ctx.b->getType()) {
case building_type::Armorstand: case building_type::Armorstand:
str = "a"; break; return "a";
case building_type::Bed: case building_type::Bed:
str = "b"; break; return "b";
case building_type::Chair: case building_type::Chair:
str = "c"; break; return "c";
case building_type::Door: case building_type::Door:
str = "d"; break; return "d";
case building_type::Floodgate: case building_type::Floodgate:
str = "x"; break; return "x";
case building_type::Cabinet: case building_type::Cabinet:
str = "f"; break; return "f";
case building_type::Box: case building_type::Box:
str = "h"; break; return "h";
//case building_type::Kennel is missing //case building_type::Kennel is missing
case building_type::FarmPlot: case building_type::FarmPlot:
do_block_building(ctx, str, "p", at_nw_corner, &add_size); break; return do_block_building(ctx, "p", at_nw_corner, &add_size);
case building_type::Weaponrack: case building_type::Weaponrack:
str = "r"; break; return "r";
case building_type::Statue: case building_type::Statue:
str = "s"; break; return "s";
case building_type::Table: case building_type::Table:
str = "t"; break; return "t";
case building_type::RoadPaved: case building_type::RoadPaved:
do_block_building(ctx, str, "o", at_nw_corner, &add_size); break; return do_block_building(ctx, "o", at_nw_corner, &add_size);
case building_type::RoadDirt: case building_type::RoadDirt:
do_block_building(ctx, str, "O", at_nw_corner, &add_size); break; return do_block_building(ctx, "O", at_nw_corner, &add_size);
case building_type::Bridge: case building_type::Bridge:
do_block_building(ctx, str, get_bridge_str(ctx.b), at_nw_corner, return do_block_building(ctx, get_bridge_str(ctx.b), at_nw_corner,
&add_size); &add_size);
break;
case building_type::Well: case building_type::Well:
str = "l"; break; return "l";
case building_type::SiegeEngine: case building_type::SiegeEngine:
do_block_building(ctx, str, get_siege_str(ctx.b), at_center); return do_block_building(ctx, get_siege_str(ctx.b), at_center);
break;
case building_type::Workshop: case building_type::Workshop:
do_block_building(ctx, str, get_workshop_str(ctx.b), at_center); return do_block_building(ctx, get_workshop_str(ctx.b), at_center);
break;
case building_type::Furnace: case building_type::Furnace:
do_block_building(ctx, str, get_furnace_str(ctx.b), at_center); return do_block_building(ctx, get_furnace_str(ctx.b), at_center);
break;
case building_type::WindowGlass: case building_type::WindowGlass:
str = "y"; break; return "y";
case building_type::WindowGem: case building_type::WindowGem:
str = "Y"; break; return "Y";
case building_type::Construction: case building_type::Construction:
str = get_construction_str(ctx.b); break; return get_construction_str(ctx.b);
case building_type::Shop: case building_type::Shop:
do_block_building(ctx, str, "z", at_center); return do_block_building(ctx, "z", at_center);
break;
case building_type::AnimalTrap: case building_type::AnimalTrap:
str = "m"; break; return "m";
case building_type::Chain: case building_type::Chain:
str = "v"; break; return "v";
case building_type::Cage: case building_type::Cage:
str = "j"; break; return "j";
case building_type::TradeDepot: case building_type::TradeDepot:
do_block_building(ctx, str, "D", at_center); break; return do_block_building(ctx, "D", at_center);
case building_type::Trap: case building_type::Trap:
str = get_trap_str(ctx.b); break; return get_trap_str(ctx.b);
case building_type::ScrewPump: case building_type::ScrewPump:
do_block_building(ctx, str, get_screw_pump_str(ctx.b), at_se_corner); return do_block_building(ctx, get_screw_pump_str(ctx.b), at_se_corner);
break;
case building_type::WaterWheel: case building_type::WaterWheel:
do_block_building(ctx, str, get_water_wheel_str(ctx.b), at_center); return do_block_building(ctx, get_water_wheel_str(ctx.b), at_center);
break;
case building_type::Windmill: case building_type::Windmill:
do_block_building(ctx, str, "Mm", at_center); break; return do_block_building(ctx, "Mm", at_center);
case building_type::GearAssembly: case building_type::GearAssembly:
str = "Mg"; break; return "Mg";
case building_type::AxleHorizontal: case building_type::AxleHorizontal:
do_block_building(ctx, str, get_axle_str(ctx.b), at_nw_corner, return do_block_building(ctx, get_axle_str(ctx.b), at_nw_corner,
&add_size); &add_size);
break;
case building_type::AxleVertical: case building_type::AxleVertical:
str = "Mv"; break; return "Mv";
case building_type::Rollers: case building_type::Rollers:
do_block_building(ctx, str, get_roller_str(ctx.b), at_nw_corner, return do_block_building(ctx, get_roller_str(ctx.b), at_nw_corner,
&add_size); &add_size);
break;
case building_type::Support: case building_type::Support:
str = "S"; break; return "S";
case building_type::ArcheryTarget: case building_type::ArcheryTarget:
str = "A"; break; return "A";
case building_type::TractionBench: case building_type::TractionBench:
str = "R"; break; return "R";
case building_type::Hatch: case building_type::Hatch:
str = "H"; break; return "H";
case building_type::Slab: case building_type::Slab:
//how to mine alt key?!? //how to mine alt key?!?
//alt+s //alt+s
str = "~"; break; return "~";
case building_type::NestBox: case building_type::NestBox:
str = "N"; break; return "N";
case building_type::Hive: case building_type::Hive:
//alt+h //alt+h
str = "~"; break; return "~";
case building_type::GrateWall: case building_type::GrateWall:
str = "W"; break; return "W";
case building_type::GrateFloor: case building_type::GrateFloor:
str = "G"; break; return "G";
case building_type::BarsVertical: case building_type::BarsVertical:
str = "B"; break; return "B";
case building_type::BarsFloor: case building_type::BarsFloor:
//alt+b //alt+b
str = "~"; break; return "~";
default: default:
str = if_pretty(ctx, "~"); return "~";
break;
} }
if (add_size)
str.append(get_expansion_str(ctx.b));
} }
static void get_tile_place(const df::coord &pos, const tile_context &ctx, // returns "~" if keys is NULL; otherwise returns the keys with the building
string &str) { // dimensions in the expansion syntax
if (!ctx.b || ctx.b->getType() != building_type::Stockpile) static const char * add_expansion_syntax(const tile_context &ctx,
return; const char *keys) {
if (!keys)
return "~";
std::ostringstream s;
pair<uint32_t, uint32_t> size = get_building_size(ctx.b);
s << keys << "(" << size.first << "x" << size.second << ")";
return cache(s.str().c_str());
}
if (ctx.b->x1 != static_cast<int32_t>(pos.x) static const char * get_tile_build(const df::coord &pos,
|| ctx.b->y1 != static_cast<int32_t>(pos.y)) { const tile_context &ctx) {
str = if_pretty(ctx, "`"); if (!ctx.b || ctx.b->getType() == building_type::Stockpile) {
return; return NULL;
} }
bool add_size = false;
const char *keys = get_build_keys(pos, ctx, add_size);
if (!add_size)
return keys;
return add_expansion_syntax(ctx, keys);
}
static const char * get_place_keys(const tile_context &ctx) {
df::building_stockpilest* sp = df::building_stockpilest* sp =
virtual_cast<df::building_stockpilest>(ctx.b); virtual_cast<df::building_stockpilest>(ctx.b);
if (!sp) { if (!sp) {
str = "~"; return NULL;
return; }
}
switch (sp->settings.flags.whole) {
case df::stockpile_group_set::mask_animals: return "a";
case df::stockpile_group_set::mask_food: return "f";
case df::stockpile_group_set::mask_furniture: return "u";
case df::stockpile_group_set::mask_corpses: return "y";
case df::stockpile_group_set::mask_refuse: return "r";
case df::stockpile_group_set::mask_wood: return "w";
case df::stockpile_group_set::mask_stone: return "s";
case df::stockpile_group_set::mask_gems: return "e";
case df::stockpile_group_set::mask_bars_blocks: return "b";
case df::stockpile_group_set::mask_cloth: return "h";
case df::stockpile_group_set::mask_leather: return "l";
case df::stockpile_group_set::mask_ammo: return "z";
case df::stockpile_group_set::mask_coins: return "n";
case df::stockpile_group_set::mask_finished_goods: return "g";
case df::stockpile_group_set::mask_weapons: return "p";
case df::stockpile_group_set::mask_armor: return "d";
default: // TODO: handle stockpiles with multiple types
return NULL;
}
}
static const char * get_tile_place(const df::coord &pos,
const tile_context &ctx) {
if (!ctx.b || ctx.b->getType() != building_type::Stockpile)
return NULL;
switch (sp->settings.flags.whole) if (ctx.b->x1 != static_cast<int32_t>(pos.x)
{ || ctx.b->y1 != static_cast<int32_t>(pos.y)) {
case df::stockpile_group_set::mask_animals: str = "a"; break; return if_pretty(ctx, "`");
case df::stockpile_group_set::mask_food: str = "f"; break;
case df::stockpile_group_set::mask_furniture: str = "u"; break;
case df::stockpile_group_set::mask_corpses: str = "y"; break;
case df::stockpile_group_set::mask_refuse: str = "r"; break;
case df::stockpile_group_set::mask_wood: str = "w"; break;
case df::stockpile_group_set::mask_stone: str = "s"; break;
case df::stockpile_group_set::mask_gems: str = "e"; break;
case df::stockpile_group_set::mask_bars_blocks: str = "b"; break;
case df::stockpile_group_set::mask_cloth: str = "h"; break;
case df::stockpile_group_set::mask_leather: str = "l"; break;
case df::stockpile_group_set::mask_ammo: str = "z"; break;
case df::stockpile_group_set::mask_coins: str = "n"; break;
case df::stockpile_group_set::mask_finished_goods: str = "g"; break;
case df::stockpile_group_set::mask_weapons: str = "p"; break;
case df::stockpile_group_set::mask_armor: str = "d"; break;
default: // multiple stockpile types
str = "~";
return;
} }
str.append(get_expansion_str(ctx.b)); return add_expansion_syntax(ctx, get_place_keys(ctx));
} }
static void get_tile_query(const df::coord &, const tile_context &ctx, static const char * get_tile_query(const df::coord &, const tile_context &ctx) {
string &str) { if (!ctx.b || !ctx.b->is_room)
if (ctx.b && ctx.b->is_room) return NULL;
str = "r+"; return "r+";
} }
static bool create_output_dir(color_ostream &out, static bool create_output_dir(color_ostream &out,
@ -613,22 +630,22 @@ static bool get_filename(string &fname,
return true; return true;
} }
typedef map<int16_t /* x */, string> bp_row; typedef map<int16_t /* x */, const char *> bp_row;
typedef map<int16_t /* y */, bp_row> bp_area; typedef map<int16_t /* y */, bp_row> bp_area;
typedef map<int16_t /* z */, bp_area> bp_volume; typedef map<int16_t /* z */, bp_area> bp_volume;
static const bp_area NEW_AREA; static const bp_area NEW_AREA;
static const bp_row NEW_ROW; static const bp_row NEW_ROW;
typedef void (get_tile_fn)(const df::coord &pos, const tile_context &ctx, typedef const char * (get_tile_fn)(const df::coord &pos,
string &str); const tile_context &ctx);
typedef void (init_ctx_fn)(const df::coord &pos, tile_context &ctx); typedef void (init_ctx_fn)(const df::coord &pos, tile_context &ctx);
struct blueprint_processor { struct blueprint_processor {
bp_volume mapdata; bp_volume mapdata;
string phase; const string phase;
get_tile_fn *get_tile; get_tile_fn * const get_tile;
init_ctx_fn *init_ctx; init_ctx_fn * const init_ctx;
blueprint_processor(const string &phase, get_tile_fn *get_tile, blueprint_processor(const string &phase, get_tile_fn *get_tile,
init_ctx_fn *init_ctx = NULL) init_ctx_fn *init_ctx = NULL)
: phase(phase), get_tile(get_tile), init_ctx(init_ctx) { } : phase(phase), get_tile(get_tile), init_ctx(init_ctx) { }
@ -674,10 +691,10 @@ static void write_pretty(ofstream &ofile, const blueprint_options &opts,
if (area && area->count(y)) if (area && area->count(y))
row = &area->at(y); row = &area->at(y);
for (int16_t x = 0; x < opts.width; ++x) { for (int16_t x = 0; x < opts.width; ++x) {
const string *tile = NULL; const char *tile = NULL;
if (row && row->count(x)) if (row && row->count(x))
tile = &row->at(x); tile = row->at(x);
ofile << (tile ? *tile : " ") << ","; ofile << (tile ? tile : " ") << ",";
} }
ofile << "#" << endl; ofile << "#" << endl;
} }
@ -758,9 +775,8 @@ static bool do_transform(color_ostream &out,
for (blueprint_processor &processor : processors) { for (blueprint_processor &processor : processors) {
if (processor.init_ctx) if (processor.init_ctx)
processor.init_ctx(pos, ctx); processor.init_ctx(pos, ctx);
string tile_str; const char *tile_str = processor.get_tile(pos, ctx);
processor.get_tile(pos, ctx, tile_str); if (tile_str) {
if (!tile_str.empty()) {
// ensure our z-index is in the order we want to write // ensure our z-index is in the order we want to write
auto area = processor.mapdata.emplace(abs(z - start.z), auto area = processor.mapdata.emplace(abs(z - start.z),
NEW_AREA); NEW_AREA);
@ -890,7 +906,9 @@ static bool do_blueprint(color_ostream &out,
if (end.z < -1) if (end.z < -1)
end.z = -1; end.z = -1;
return do_transform(out, start, end, options, files); bool ok = do_transform(out, start, end, options, files);
cache(NULL);
return ok;
} }
// entrypoint when called from Lua. returns the names of the generated files // entrypoint when called from Lua. returns the names of the generated files