openMSX
MSXDevice.cc
Go to the documentation of this file.
1 #include "MSXDevice.hh"
2 #include "XMLElement.hh"
3 #include "MSXMotherBoard.hh"
4 #include "HardwareConfig.hh"
6 #include "MSXCPUInterface.hh"
7 #include "CacheLine.hh"
8 #include "TclObject.hh"
9 #include "Math.hh"
10 #include "MSXException.hh"
11 #include "one_of.hh"
12 #include "ranges.hh"
13 #include "serialize.hh"
14 #include "stl.hh"
15 #include "unreachable.hh"
16 #include "xrange.hh"
17 #include <cassert>
18 #include <cstring>
19 
20 using std::string;
21 
22 namespace openmsx {
23 
24 MSXDevice::MSXDevice(const DeviceConfig& config, const string& name)
25  : deviceConfig(config)
26 {
27  initName(name);
28 }
29 
31  : deviceConfig(config)
32 {
33  initName(getDeviceConfig().getAttribute("id"));
34 }
35 
36 void MSXDevice::initName(const string& name)
37 {
38  deviceName = name;
39  if (getMotherBoard().findDevice(deviceName)) {
40  unsigned n = 0;
41  do {
42  deviceName = strCat(name, " (", ++n, ')');
43  } while (getMotherBoard().findDevice(deviceName));
44  }
45 }
46 
48 {
49  staticInit();
50 
51  lockDevices();
52  registerSlots();
53  registerPorts();
54 }
55 
57 {
58  unregisterPorts();
59  unregisterSlots();
60  unlockDevices();
61  assert(referencedBy.empty());
62 }
63 
64 void MSXDevice::staticInit()
65 {
66  static bool alreadyInit = false;
67  if (alreadyInit) return;
68  alreadyInit = true;
69 
70  memset(unmappedRead, 0xFF, sizeof(unmappedRead));
71 }
72 
74 {
76 }
77 
78 void MSXDevice::testRemove(span<const std::unique_ptr<MSXDevice>> removed) const
79 {
80  // Typically 'referencedBy' contains very few elements, so a simple
81  // O(n*m) algorithm is fine.
82  std::string err;
83  for (const auto* dev : referencedBy) {
84  if (!contains(removed, dev, [](const auto& d) { return d.get(); })) {
85  strAppend(err, ' ', dev->getName());
86  }
87  }
88  if (!err.empty()) {
89  throw MSXException("Still in use by:", err);
90  }
91 }
92 
93 void MSXDevice::lockDevices()
94 {
95  // This code can only handle backward references: the thing that is
96  // referenced must already be instantiated, we don't try to change the
97  // instantiation order. For the moment this is good enough (only ADVRAM
98  // (an extension) uses it to refer to the VDP (inside a machine)). If
99  // needed we can implement something more sophisticated later without
100  // changing the format of the config files.
101  for (auto& c : getDeviceConfig().getChildren("device")) {
102  const auto& name = c->getAttribute("idref");
103  auto* dev = getMotherBoard().findDevice(name);
104  if (!dev) {
105  throw MSXException(
106  "Unsatisfied dependency: '", getName(),
107  "' depends on unavailable device '",
108  name, "'.");
109  }
110  references.push_back(dev);
111  dev->referencedBy.push_back(this);
112  }
113 }
114 
115 void MSXDevice::unlockDevices()
116 {
117  for (auto& r : references) {
118  move_pop_back(r->referencedBy, rfind_unguarded(r->referencedBy, this));
119  }
120 }
121 
123 {
124  // init() must already be called
125  return references;
126 }
127 
128 EmuTime::param MSXDevice::getCurrentTime() const
129 {
130  return getMotherBoard().getCurrentTime();
131 }
133 {
134  return getMotherBoard().getCPU();
135 }
137 {
138  return getMotherBoard().getCPUInterface();
139 }
141 {
142  return getMotherBoard().getScheduler();
143 }
145 {
146  return getMotherBoard().getMSXCliComm();
147 }
149 {
150  return getMotherBoard().getReactor();
151 }
153 {
155 }
157 {
158  return getMotherBoard().getLedStatus();
159 }
161 {
163 }
164 
165 void MSXDevice::registerSlots()
166 {
167  MemRegions tmpMemRegions;
168  unsigned align = getBaseSizeAlignment();
169  assert(Math::ispow2(align));
170  for (auto& m : getDeviceConfig().getChildren("mem")) {
171  unsigned base = m->getAttributeAsInt("base");
172  unsigned size = m->getAttributeAsInt("size");
173  if ((base >= 0x10000) || (size > 0x10000) || ((base + size) > 0x10000)) {
174  throw MSXException(
175  "Invalid memory specification for device ",
176  getName(), " should be in range "
177  "[0x0000,0x10000).");
178  }
179  if (((base & (align - 1)) || (size & (align - 1))) &&
180  !allowUnaligned()) {
181  throw MSXException(
182  "invalid memory specification for device ",
183  getName(), " should be aligned on at least 0x",
184  hex_string<4>(align), '.');
185  }
186  tmpMemRegions.emplace_back(BaseSize{base, size});
187  }
188  if (tmpMemRegions.empty()) {
189  return;
190  }
191 
192  // find primary and secondary slot specification
193  auto& slotManager = getMotherBoard().getSlotManager();
194  auto* primaryConfig = getDeviceConfig2().getPrimary();
195  auto* secondaryConfig = getDeviceConfig2().getSecondary();
196  if (primaryConfig) {
197  ps = slotManager.getSlotNum(primaryConfig->getAttribute("slot"));
198  } else {
199  throw MSXException("Invalid memory specification");
200  }
201  if (secondaryConfig) {
202  const auto& ss_str = secondaryConfig->getAttribute("slot");
203  ss = slotManager.getSlotNum(ss_str);
204  if ((-16 <= ss) && (ss <= -1) && (ss != ps)) {
205  throw MSXException(
206  "Invalid secondary slot specification: \"",
207  ss_str, "\".");
208  }
209  } else {
210  ss = 0;
211  }
212 
213  // This is only for backwards compatibility: in the past we added extra
214  // attributes "primary_slot" and "secondary_slot" (in each MSXDevice
215  // config) instead of changing the 'any' value of the slot attribute of
216  // the (possibly shared) <primary> and <secondary> tags. When loading
217  // an old savestate these tags can still occur, so keep this code. Also
218  // remove these attributes to convert to the new format.
219  const auto& config = getDeviceConfig();
220  if (config.hasAttribute("primary_slot")) {
221  auto& mutableConfig = const_cast<XMLElement&>(config);
222  const auto& primSlot = config.getAttribute("primary_slot");
223  ps = slotManager.getSlotNum(primSlot);
224  mutableConfig.removeAttribute("primary_slot");
225  if (config.hasAttribute("secondary_slot")) {
226  const auto& secondSlot = config.getAttribute("secondary_slot");
227  ss = slotManager.getSlotNum(secondSlot);
228  mutableConfig.removeAttribute("secondary_slot");
229  }
230  }
231 
232  // decode special values for 'ss'
233  if ((-128 <= ss) && (ss < 0)) {
234  if ((0 <= ps) && (ps < 4) &&
235  getCPUInterface().isExpanded(ps)) {
236  ss += 128;
237  } else {
238  ss = 0;
239  }
240  }
241 
242  // decode special values for 'ps'
243  if (ps == -256) {
244  slotManager.getAnyFreeSlot(ps, ss);
245  } else if (ps < 0) {
246  // specified slot by name (carta, cartb, ...)
247  slotManager.getSpecificSlot(-ps - 1, ps, ss);
248  } else {
249  // numerical specified slot (0, 1, 2, 3)
250  }
251  assert((0 <= ps) && (ps <= 3));
252 
253  if (!getCPUInterface().isExpanded(ps)) {
254  ss = -1;
255  }
256 
257  // Store actual slot in config. This has two purposes:
258  // - Make sure that devices that are grouped under the same
259  // <primary>/<secondary> tags actually use the same slot. (This
260  // matters when the value of some of the slot attributes is "any").
261  // - Fix the slot number so that it remains the same after a
262  // savestate/loadstate.
263  assert(primaryConfig);
264  primaryConfig->setAttribute("slot", strCat(ps));
265  if (secondaryConfig) {
266  string slot = (ss == -1) ? "X" : strCat(ss);
267  secondaryConfig->setAttribute("slot", std::move(slot));
268  } else {
269  if (ss != -1) {
270  throw MSXException(
271  "Missing <secondary> tag for device", getName());
272  }
273  }
274 
275  int logicalSS = (ss == -1) ? 0 : ss;
276  for (auto& r : tmpMemRegions) {
278  *this, ps, logicalSS, r.base, r.size);
279  memRegions.push_back(r);
280  }
281 
282  // Mark the slot as 'in-use' so that future searches for free external
283  // slots don't return this slot anymore. If the slot was not an
284  // external slot, this call has no effect. Multiple MSXDevices from the
285  // same extension (the same HardwareConfig) can all allocate the same
286  // slot (later they should also all free this slot).
287  slotManager.allocateSlot(ps, ss, getHardwareConfig());
288 }
289 
290 void MSXDevice::unregisterSlots()
291 {
292  if (memRegions.empty()) return;
293 
294  int logicalSS = (ss == -1) ? 0 : ss;
295  for (const auto& [base, size] : memRegions) {
297  *this, ps, logicalSS, base, size);
298  }
299 
300  // See comments above about allocateSlot() for more details:
301  // - has no effect for non-external slots
302  // - can be called multiple times for the same slot
304 }
305 
306 void MSXDevice::getVisibleMemRegion(unsigned& base, unsigned& size) const
307 {
308  // init() must already be called
309  if (memRegions.empty()) {
310  base = 0;
311  size = 0;
312  return;
313  }
314 
315  auto lowest = min_value(memRegions, &BaseSize::base);
316  auto highest = max_value(memRegions, &BaseSize::end);
317  assert(lowest <= highest);
318  base = lowest;
319  size = highest - lowest;
320 }
321 
322 void MSXDevice::registerPorts()
323 {
324  for (auto& i : getDeviceConfig().getChildren("io")) {
325  unsigned base = i->getAttributeAsInt("base");
326  unsigned num = i->getAttributeAsInt("num");
327  const auto& type = i->getAttribute("type", "IO");
328  if (((base + num) > 256) || (num == 0) ||
329  (type != one_of("I", "O", "IO"))) {
330  throw MSXException("Invalid IO port specification");
331  }
332  for (auto port : xrange(base, base + num)) {
333  if (type == one_of("I", "IO")) {
334  getCPUInterface().register_IO_In(port, this);
335  inPorts.push_back(port);
336  }
337  if (type == one_of("O", "IO")) {
338  getCPUInterface().register_IO_Out(port, this);
339  outPorts.push_back(port);
340  }
341  }
342  }
343 }
344 
345 void MSXDevice::unregisterPorts()
346 {
347  for (auto& p : inPorts) {
349  }
350  for (auto& p : outPorts) {
352  }
353 }
354 
355 
356 void MSXDevice::reset(EmuTime::param /*time*/)
357 {
358  // nothing
359 }
360 
362 {
363  return 0xFF;
364 }
365 
366 void MSXDevice::powerDown(EmuTime::param /*time*/)
367 {
368  // nothing
369 }
370 
371 void MSXDevice::powerUp(EmuTime::param time)
372 {
373  reset(time);
374 }
375 
376 string MSXDevice::getName() const
377 {
378  return deviceName;
379 }
380 
382 {
383  result.addListElement(getName());
384 }
385 
387 {
388  result.addDictKeyValue("type", getDeviceConfig().getName());
389  getExtraDeviceInfo(result);
390 }
391 
393 {
394  // nothing
395 }
396 
398 {
399  return CacheLine::SIZE;
400 }
401 
402 
403 byte MSXDevice::readIO(word /*port*/, EmuTime::param /*time*/)
404 {
405  // read from unmapped IO
406  return 0xFF;
407 }
408 
409 void MSXDevice::writeIO(word /*port*/, byte /*value*/, EmuTime::param /*time*/)
410 {
411  // write to unmapped IO, do nothing
412 }
413 
414 byte MSXDevice::peekIO(word /*port*/, EmuTime::param /*time*/) const
415 {
416  return 0xFF;
417 }
418 
419 
420 byte MSXDevice::readMem(word /*address*/, EmuTime::param /*time*/)
421 {
422  // read from unmapped memory
423  return 0xFF;
424 }
425 
426 const byte* MSXDevice::getReadCacheLine(word /*start*/) const
427 {
428  return nullptr; // uncacheable
429 }
430 
431 void MSXDevice::writeMem(word /*address*/, byte /*value*/,
432  EmuTime::param /*time*/)
433 {
434  // write to unmapped memory, do nothing
435 }
436 
437 byte MSXDevice::peekMem(word address, EmuTime::param /*time*/) const
438 {
439  word base = address & CacheLine::HIGH;
440  if (const byte* cache = getReadCacheLine(base)) {
441  word offset = address & CacheLine::LOW;
442  return cache[offset];
443  } else {
444  // peek not supported for this device
445  return 0xFF;
446  }
447 }
448 
449 void MSXDevice::globalWrite(word /*address*/, byte /*value*/,
450  EmuTime::param /*time*/)
451 {
452  UNREACHABLE;
453 }
454 
455 void MSXDevice::globalRead(word /*address*/, EmuTime::param /*time*/)
456 {
457  UNREACHABLE;
458 }
459 
460 byte* MSXDevice::getWriteCacheLine(word /*start*/) const
461 {
462  return nullptr; // uncacheable
463 }
464 
465 
466 // calls 'action(<start2>, <size2>, args..., ps, ss)'
467 // with 'start', 'size' clipped to each of the ranges in 'memRegions'
468 template<typename Action, typename... Args>
469 void MSXDevice::clip(unsigned start, unsigned size, Action action, Args... args)
470 {
471  int ss2 = (ss != -1) ? ss : 0;
472  unsigned end = start + size;
473  for (auto [base, fullBsize] : memRegions) {
474  // split on 16kB boundaries
475  while (fullBsize > 0) {
476  unsigned bsize = std::min(fullBsize, ((base + 0x4000) & ~0x3fff) - base);
477 
478  unsigned baseEnd = base + bsize;
479  // intersect [start, end) with [base, baseEnd) -> [clipStart, clipEnd)
480  unsigned clipStart = std::max(start, base);
481  unsigned clipEnd = std::min(end, baseEnd);
482  if (clipStart < clipEnd) { // non-empty
483  unsigned clipSize = clipEnd - clipStart;
484  action(clipStart, clipSize, args..., ps, ss2);
485  }
486 
487  base += bsize;
488  fullBsize -= bsize;
489  }
490  }
491 }
492 
493 void MSXDevice::invalidateDeviceRWCache(unsigned start, unsigned size)
494 {
495  clip(start, size, [&](auto... args) { getCPUInterface().invalidateRWCache(args...); });
496 }
497 void MSXDevice::invalidateDeviceRCache(unsigned start, unsigned size)
498 {
499  clip(start, size, [&](auto... args) { getCPUInterface().invalidateRCache(args...); });
500 }
501 void MSXDevice::invalidateDeviceWCache(unsigned start, unsigned size)
502 {
503  clip(start, size, [&](auto... args) { getCPUInterface().invalidateWCache(args...); });
504 }
505 
506 void MSXDevice::fillDeviceRWCache(unsigned start, unsigned size, byte* rwData)
507 {
508  fillDeviceRWCache(start, size, rwData, rwData);
509 }
510 void MSXDevice::fillDeviceRWCache(unsigned start, unsigned size, const byte* rData, byte* wData)
511 {
512  assert(!allowUnaligned());
513  clip(start, size, [&](auto... args) { getCPUInterface().fillRWCache(args...); }, rData, wData);
514 }
515 void MSXDevice::fillDeviceRCache(unsigned start, unsigned size, const byte* rData)
516 {
517  assert(!allowUnaligned());
518  clip(start, size, [&](auto... args) { getCPUInterface().fillRCache(args...); }, rData);
519 }
520 void MSXDevice::fillDeviceWCache(unsigned start, unsigned size, byte* wData)
521 {
522  assert(!allowUnaligned());
523  clip(start, size, [&](auto... args) { getCPUInterface().fillWCache(args...); }, wData);
524 }
525 
526 template<typename Archive>
527 void MSXDevice::serialize(Archive& ar, unsigned /*version*/)
528 {
529  // When this method is called, the method init() has already been
530  // called (thus also registerSlots() and registerPorts()).
531  ar.serialize("name", deviceName);
532 }
534 
535 } // namespace openmsx
Definition: one_of.hh:7
void freeSlot(int ps, int ss, const HardwareConfig &hwConfig)
XMLElement * getSecondary() const
Definition: DeviceConfig.hh:55
XMLElement * getPrimary() const
Definition: DeviceConfig.hh:51
MSXMotherBoard & getMotherBoard() const
void register_IO_Out(byte port, MSXDevice *device)
Devices can register their Out ports.
void invalidateRWCache(word start, unsigned size, int ps, int ss)
void register_IO_In(byte port, MSXDevice *device)
Devices can register their In ports.
void fillWCache(unsigned start, unsigned size, byte *wData, int ps, int ss)
void unregister_IO_In(byte port, MSXDevice *device)
void invalidateRCache(word start, unsigned size, int ps, int ss)
void fillRWCache(unsigned start, unsigned size, const byte *rData, byte *wData, int ps, int ss)
void registerMemDevice(MSXDevice &device, int ps, int ss, int base, int size)
Devices can register themself in the MSX slotstructure.
void invalidateWCache(word start, unsigned size, int ps, int ss)
void unregisterMemDevice(MSXDevice &device, int ps, int ss, int base, int size)
void unregister_IO_Out(byte port, MSXDevice *device)
void fillRCache(unsigned start, unsigned size, const byte *rData, int ps, int ss)
bool isExpanded(int ps) const
An MSXDevice is an emulated hardware component connected to the bus of the emulated MSX.
Definition: MSXDevice.hh:32
MSXMotherBoard & getMotherBoard() const
Get the mother board this device belongs to.
Definition: MSXDevice.cc:73
virtual void init()
Definition: MSXDevice.cc:47
void invalidateDeviceRCache()
Definition: MSXDevice.hh:209
void fillDeviceWCache(unsigned start, unsigned size, byte *wData)
Definition: MSXDevice.cc:520
PluggingController & getPluggingController() const
Definition: MSXDevice.cc:160
virtual void powerUp(EmuTime::param time)
This method is called when MSX is powered up.
Definition: MSXDevice.cc:371
const DeviceConfig & getDeviceConfig2() const
Definition: MSXDevice.hh:233
void fillDeviceRWCache(unsigned start, unsigned size, byte *rwData)
Calls MSXCPUInterface::fillXXCache() for the specific (part of) the slot that this device is located ...
Definition: MSXDevice.cc:506
virtual ~MSXDevice()=0
Definition: MSXDevice.cc:56
CliComm & getCliComm() const
Definition: MSXDevice.cc:144
virtual const byte * getReadCacheLine(word start) const
Test that the memory in the interval [start, start + CacheLine::SIZE) is cacheable for reading.
Definition: MSXDevice.cc:426
void invalidateDeviceWCache()
Definition: MSXDevice.hh:210
void getDeviceInfo(TclObject &result) const
Get device info.
Definition: MSXDevice.cc:386
virtual void writeIO(word port, byte value, EmuTime::param time)
Write a byte to a given IO port at a certain time to this device.
Definition: MSXDevice.cc:409
virtual byte readIRQVector()
Gets IRQ vector used in IM2.
Definition: MSXDevice.cc:361
virtual void globalWrite(word address, byte value, EmuTime::param time)
Global writes.
Definition: MSXDevice.cc:449
Reactor & getReactor() const
Definition: MSXDevice.cc:148
virtual bool allowUnaligned() const
By default we don't allow unaligned <mem> specifications in the config file.
Definition: MSXDevice.hh:290
virtual unsigned getBaseSizeAlignment() const
The 'base' and 'size' attribute values need to be at least aligned to CacheLine::SIZE.
Definition: MSXDevice.cc:397
MSXDevice(const MSXDevice &)=delete
const XMLElement & getDeviceConfig() const
Get the configuration section for this device.
Definition: MSXDevice.hh:230
std::vector< MSXDevice * > Devices
Definition: MSXDevice.hh:37
virtual byte readMem(word address, EmuTime::param time)
Read a byte from a location at a certain time from this device.
Definition: MSXDevice.cc:420
virtual void getExtraDeviceInfo(TclObject &result) const
Definition: MSXDevice.cc:392
void getVisibleMemRegion(unsigned &base, unsigned &size) const
Returns the range where this device is visible in memory.
Definition: MSXDevice.cc:306
virtual void writeMem(word address, byte value, EmuTime::param time)
Write a given byte to a given location at a certain time to this device.
Definition: MSXDevice.cc:431
virtual byte peekIO(word port, EmuTime::param time) const
Read a byte from a given IO port.
Definition: MSXDevice.cc:414
virtual byte peekMem(word address, EmuTime::param time) const
Read a byte from a given memory location.
Definition: MSXDevice.cc:437
MSXCPU & getCPU() const
Definition: MSXDevice.cc:132
const HardwareConfig & getHardwareConfig() const
Returns the hardwareconfig this device belongs to.
Definition: MSXDevice.hh:43
static byte unmappedRead[0x10000]
Definition: MSXDevice.hh:300
LedStatus & getLedStatus() const
Definition: MSXDevice.cc:156
void serialize(Archive &ar, unsigned version)
Definition: MSXDevice.cc:527
void invalidateDeviceRWCache()
Calls MSXCPUInterface::invalidateXXCache() for the specific (part of) the slot that this device is lo...
Definition: MSXDevice.hh:208
Scheduler & getScheduler() const
Definition: MSXDevice.cc:140
virtual void getNameList(TclObject &result) const
Returns list of name(s) of this device.
Definition: MSXDevice.cc:381
virtual byte * getWriteCacheLine(word start) const
Test that the memory in the interval [start, start + CacheLine::SIZE) is cacheable for writing.
Definition: MSXDevice.cc:460
void fillDeviceRCache(unsigned start, unsigned size, const byte *rData)
Definition: MSXDevice.cc:515
CommandController & getCommandController() const
Definition: MSXDevice.cc:152
void testRemove(span< const std::unique_ptr< MSXDevice >> removed) const
Checks whether this device can be removed (no other device has a reference to it).
Definition: MSXDevice.cc:78
const Devices & getReferences() const
Get the device references that are specified for this device.
Definition: MSXDevice.cc:122
virtual void powerDown(EmuTime::param time)
This method is called when MSX is powered down.
Definition: MSXDevice.cc:366
virtual void globalRead(word address, EmuTime::param time)
Global reads.
Definition: MSXDevice.cc:455
virtual std::string getName() const
Returns a human-readable name for this device.
Definition: MSXDevice.cc:376
virtual void reset(EmuTime::param time)
This method is called on reset.
Definition: MSXDevice.cc:356
EmuTime::param getCurrentTime() const
Definition: MSXDevice.cc:128
MSXCPUInterface & getCPUInterface() const
Definition: MSXDevice.cc:136
virtual byte readIO(word port, EmuTime::param time)
Read a byte from an IO port at a certain time from this device.
Definition: MSXDevice.cc:403
PluggingController & getPluggingController()
MSXCPUInterface & getCPUInterface()
EmuTime::param getCurrentTime()
Convenience method: This is the same as getScheduler().getCurrentTime().
CartridgeSlotManager & getSlotManager()
MSXDevice * findDevice(std::string_view name)
Find a MSXDevice by name.
CommandController & getCommandController()
Central administration of Connectors and Pluggables.
Contains the main loop of openMSX.
Definition: Reactor.hh:67
void addListElement(const T &t)
Definition: TclObject.hh:129
void addDictKeyValue(const Key &key, const Value &value)
Definition: TclObject.hh:143
Definition: span.hh:126
constexpr bool ispow2(T x) noexcept
Is the given number an integral power of two? That is, does it have exactly one 1-bit in binary repre...
Definition: Math.hh:57
constexpr vecN< N, T > min(const vecN< N, T > &x, const vecN< N, T > &y)
Definition: gl_vec.hh:269
constexpr vecN< N, T > max(const vecN< N, T > &x, const vecN< N, T > &y)
Definition: gl_vec.hh:287
constexpr unsigned LOW
Definition: CacheLine.hh:9
constexpr unsigned HIGH
Definition: CacheLine.hh:10
constexpr unsigned SIZE
Definition: CacheLine.hh:7
This file implemented 3 utility functions:
Definition: Autofire.cc:9
uint16_t word
16 bit unsigned integer
Definition: openmsx.hh:29
size_t size(std::string_view utf8)
#define INSTANTIATE_SERIALIZE_METHODS(CLASS)
Definition: serialize.hh:983
auto min_value(InputIterator first, InputIterator last, Proj proj={})
Definition: stl.hh:203
auto max_value(InputIterator first, InputIterator last, Proj proj={})
Definition: stl.hh:222
void move_pop_back(VECTOR &v, typename VECTOR::iterator it)
Erase the pointed to element from the given vector.
Definition: stl.hh:132
auto rfind_unguarded(RANGE &range, const VAL &val, Proj proj={})
Similar to the find(_if)_unguarded functions above, but searches from the back to front.
Definition: stl.hh:107
constexpr bool contains(ITER first, ITER last, const VAL &val)
Check if a range contains a given value, using linear search.
Definition: stl.hh:30
std::string strCat(Ts &&...ts)
Definition: strCat.hh:591
void strAppend(std::string &result, Ts &&...ts)
Definition: strCat.hh:669
#define UNREACHABLE
Definition: unreachable.hh:38
constexpr auto xrange(T e)
Definition: xrange.hh:155
constexpr auto end(const zstring_view &x)
Definition: zstring_view.hh:84