/****************************************************************************/1// Eclipse SUMO, Simulation of Urban MObility; see https://eclipse.dev/sumo2// Copyright (C) 2001-2025 German Aerospace Center (DLR) and others.3// This program and the accompanying materials are made available under the4// terms of the Eclipse Public License 2.0 which is available at5// https://www.eclipse.org/legal/epl-2.0/6// This Source Code may also be made available under the following Secondary7// Licenses when the conditions for such availability set forth in the Eclipse8// Public License 2.0 are satisfied: GNU General Public License, version 29// or later which is available at10// https://www.gnu.org/licenses/old-licenses/gpl-2.0-standalone.html11// SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later12/****************************************************************************/13/// @file FileHelpers.h14/// @author Daniel Krajzewicz15/// @author Michael Behrisch16/// @author Jakob Erdmann17/// @date Mon, 17 Dec 200118///19// Functions for an easier usage of files20/****************************************************************************/21#pragma once22#include <config.h>23#include <cassert>24#include <fstream>25#include <string>26#include <vector>27#include "SUMOTime.h"282930// ===========================================================================31// class definitions32// ===========================================================================33/**34* @class FileHelpers35* @brief Functions for an easier usage of files and paths36*/37class FileHelpers {38public:39/// @name file access functions40/// @{4142/** @brief Checks whether the given file is readable43*44* @param[in] path The path to the file that shall be examined45* @return Whether the named file is readable46*/47static bool isReadable(std::string path);4849/** @brief Checks whether the given file is a directory50*51* @param[in] path The path to the file that shall be examined52* @return Whether the named file is a directory.53*/54static bool isDirectory(std::string path);55/// @}5657/// @name file path evaluating functions58/// @{5960/** @brief Removes the file information from the given path61*62* @param[in] path The path to the file to return the folder it is located in63* @return The directory of the named file64*/65static std::string getFilePath(const std::string& path);6667/** @brief Removes the path information from the given path68*69* @param[in] path The path to the file to return the file (with extension)70* @return the named file (with extension)71*/72static std::string getFileFromPath(std::string path, const bool removeExtension);7374/** @brief Add an extension to the given file path75*76* @param[in] path The path to the file77* @param[in] extension new extension (with dot, example: '.xml')78* @return the new path with extension, the same path if it already has the extension, or a empty string if path is invalid79*/80static std::string addExtension(const std::string& path, const std::string& extension);8182/** @brief Returns the second path as a relative path to the first file83*84* Given the position of the configuration file, and the information where a second85* file is relative to the configuration file's position, we want to known where86* this second file can be found. This method gets the path to the configuration file87* (including the configuration file name) and the path to get the relative position88* of and returns this relative position.89*90* @param[in] configPath The path the configuration file (including the config's file name)91* @param[in] path The path to the references file (relativ to configuration path)92* @return The file's position (relative to curent working directory)93*/94static std::string getConfigurationRelative(const std::string& configPath, const std::string& path);9596/** @brief Returns the information whether the given name represents a socket97*98* A file name is meant to describe a socket address if a colon is found at a position99* larger than one.100*101* @param[in] name The name of a file102* @return Whether the name names a socket103*/104static bool isSocket(const std::string& name);105106/** @brief Returns the information whether the given path is absolute107*108* A path is meant to be absolute, if109* @arg it is a socket110* @arg it starts with a "/" (Linux)111* @arg it has a ':' at the second position (Windows)112*113* @param[in] path The path to examine114* @return Whether the path is absolute115*/116static bool isAbsolute(const std::string& path);117118/** @brief Returns the path from a configuration so that it is accessible from the current working directory119*120* If the path is absolute, it is returned. Otherwise, the file's position121* is computed regarding the configuration path (see getConfigurationRelative).122*123* @see isAbsolute124* @see getConfigurationRelative125* @param[in] filename The path to the file to be examined126* @param[in] basePath The path the configuration file (including the config's file name)127* @return The file's position128*/129static std::string checkForRelativity(const std::string& filename, const std::string& basePath);130131/** @brief Get the current working directory132*133* @return The working directory (pwd)134*/135static std::string getCurrentDir();136137/** @brief Splits the given file path into directory components.138*139* The path gets normalized such that redundant "." and empty components are removed.140* Furthermore it will not contain a ".." after a directory name.141*142* @param[in] filename The path of a file143* @return the list parent directories144*/145static std::vector<std::string> splitDirs(const std::string& filename);146147/** @brief Fixes the relative path for the given filename in relation to the basePath (usually a config file).148*149* @param[in] filename The path of a file150* @param[in] basePath The path of another file referring to the former151* @param[in] force whether the replacement should be made even if the filename is absolute152* @param[in] curDir the current working dir (mainly for easier testing), "" will trigger a call of getCurrentDir153* @return the corrected relative file path154*/155static std::string fixRelative(const std::string& filename, const std::string& basePath, const bool force, std::string curDir = "");156157/// @brief prepend the given prefix to the last path component of the given file path158static std::string prependToLastPathComponent(const std::string& prefix, const std::string& path);159160/// @}161162/// @name binary writing functions163/// @{164165/** @brief Writes an integer binary166*167* @param[in, out] strm The stream to write into168* @param[in] value The integer to write169* @return Reference to the stream170*/171static std::ostream& writeInt(std::ostream& strm, int value);172173/** @brief Writes a float binary174*175* This method behaves differently depending on the definition of double at compile time.176*177* @param[in, out] strm The stream to write into178* @param[in] value The float to write179* @return Reference to the stream180*/181static std::ostream& writeFloat(std::ostream& strm, double value);182183/** @brief Writes a byte binary184*185* @param[in, out] strm The stream to write into186* @param[in] value The byte to write187* @return Reference to the stream188*/189static std::ostream& writeByte(std::ostream& strm, unsigned char value);190191/** @brief Writes a string binary192*193* Writes the length of the string, first, using writeInt. Writes then the string's194* characters.195*196* @see writeInt197* @param[in, out] strm The stream to write into198* @param[in] value The string to write199* @return Reference to the stream200*/201static std::ostream& writeString(std::ostream& strm, const std::string& value);202203/** @brief Writes a time description binary204*205* This method behaves differently depending on the definition of SUMOTime at compile time,206* which in turn depends on the enabling of subsecond timesteps.207*208* @param[in, out] strm The stream to write into209* @param[in] value The time to write210* @return Reference to the stream211*/212static std::ostream& writeTime(std::ostream& strm, SUMOTime value);213214/** @brief Writes an edge vector binary215*216* @param[in, out] os The stream to write into217* @param[in] edges The edges to write218* @return Reference to the stream219*/220template <typename E>221static std::ostream& writeEdgeVector(std::ostream& os, const std::vector<E>& edges);222223/** @brief Reads an edge vector binary224*225* @param[in] is The stream to read from226* @param[out] edges The edge vector to write into227* @return Reference to the stream228*/229template <typename E>230static void readEdgeVector(std::istream& in, std::vector<const E*>& edges, const std::string& rid);231/// @}232};233234235template <typename E>236std::ostream& FileHelpers::writeEdgeVector(std::ostream& os, const std::vector<E>& edges) {237FileHelpers::writeInt(os, (int)edges.size());238std::vector<int> follow;239int maxFollow = 0;240E prev = edges.front();241for (typename std::vector<E>::const_iterator i = edges.begin() + 1; i != edges.end(); ++i) {242int idx = 0;243for (; idx < prev->getNumSuccessors(); ++idx) {244if (idx > 15) {245break;246}247if (prev->getSuccessors()[idx] == (*i)) {248follow.push_back(idx);249if (idx > maxFollow) {250maxFollow = idx;251}252break;253}254}255if (idx > 15 || idx == prev->getNumSuccessors()) {256follow.clear();257break;258}259prev = *i;260}261if (follow.empty()) {262for (typename std::vector<E>::const_iterator i = edges.begin(); i != edges.end(); ++i) {263FileHelpers::writeInt(os, (*i)->getNumericalID());264}265} else {266const int bits = maxFollow > 3 ? 4 : 2;267const int numFields = 8 * sizeof(int) / bits;268FileHelpers::writeInt(os, -bits);269FileHelpers::writeInt(os, edges.front()->getNumericalID());270int data = 0;271int field = 0;272for (std::vector<int>::const_iterator i = follow.begin(); i != follow.end(); ++i) {273data |= *i;274field++;275if (field == numFields) {276FileHelpers::writeInt(os, data);277data = 0;278field = 0;279} else {280data <<= bits;281}282}283if (field > 0) {284FileHelpers::writeInt(os, data << ((numFields - field - 1) * bits));285}286}287return os;288}289290291template <typename E>292void FileHelpers::readEdgeVector(std::istream& in, std::vector<const E*>& edges, const std::string& rid) {293int size;294in.read((char*) &size, sizeof(int));295edges.reserve(size);296int bitsOrEntry;297in.read((char*) &bitsOrEntry, sizeof(int));298if (bitsOrEntry < 0) {299const int bits = -bitsOrEntry;300const int numFields = 8 * sizeof(int) / bits;301const int mask = (1 << bits) - 1;302int edgeID;303in.read((char*) &edgeID, sizeof(int));304const E* prev = E::getAllEdges()[edgeID];305assert(prev != 0);306edges.push_back(prev);307size--;308int data = 0;309int field = numFields;310for (; size > 0; size--) {311if (field == numFields) {312in.read((char*) &data, sizeof(int));313field = 0;314}315int followIndex = (data >> ((numFields - field - 1) * bits)) & mask;316if (followIndex >= prev->getNumSuccessors()) {317throw ProcessError(TLF("Invalid follower index in route '%'!", rid));318}319prev = prev->getSuccessors()[followIndex];320edges.push_back(prev);321field++;322}323} else {324while (size > 0) {325const E* edge = E::getAllEdges()[bitsOrEntry];326if (edge == 0) {327throw ProcessError(TLF("An edge within the route '%' is not known!", rid));328}329edges.push_back(edge);330size--;331if (size > 0) {332in.read((char*) &bitsOrEntry, sizeof(int));333}334}335}336}337338339