/****************************************************************************/1// Eclipse SUMO, Simulation of Urban MObility; see https://eclipse.dev/sumo2// Copyright (C) 2001-2026 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/// @brief append the given suffix after the file name but before the extension161static std::string appendBeforeExtension(const std::string& path, const std::string& suffix, bool checkSep = true);162/// @}163164/// @name binary writing functions165/// @{166167/** @brief Writes an integer binary168*169* @param[in, out] strm The stream to write into170* @param[in] value The integer to write171* @return Reference to the stream172*/173static std::ostream& writeInt(std::ostream& strm, int value);174175/** @brief Writes a float binary176*177* This method behaves differently depending on the definition of double at compile time.178*179* @param[in, out] strm The stream to write into180* @param[in] value The float to write181* @return Reference to the stream182*/183static std::ostream& writeFloat(std::ostream& strm, double value);184185/** @brief Writes a byte binary186*187* @param[in, out] strm The stream to write into188* @param[in] value The byte to write189* @return Reference to the stream190*/191static std::ostream& writeByte(std::ostream& strm, unsigned char value);192193/** @brief Writes a string binary194*195* Writes the length of the string, first, using writeInt. Writes then the string's196* characters.197*198* @see writeInt199* @param[in, out] strm The stream to write into200* @param[in] value The string to write201* @return Reference to the stream202*/203static std::ostream& writeString(std::ostream& strm, const std::string& value);204205/** @brief Writes a time description binary206*207* This method behaves differently depending on the definition of SUMOTime at compile time,208* which in turn depends on the enabling of subsecond timesteps.209*210* @param[in, out] strm The stream to write into211* @param[in] value The time to write212* @return Reference to the stream213*/214static std::ostream& writeTime(std::ostream& strm, SUMOTime value);215216/** @brief Writes an edge vector binary217*218* @param[in, out] os The stream to write into219* @param[in] edges The edges to write220* @return Reference to the stream221*/222template <typename E>223static std::ostream& writeEdgeVector(std::ostream& os, const std::vector<E>& edges);224225/** @brief Reads an edge vector binary226*227* @param[in] is The stream to read from228* @param[out] edges The edge vector to write into229* @return Reference to the stream230*/231template <typename E>232static void readEdgeVector(std::istream& in, std::vector<const E*>& edges, const std::string& rid);233/// @}234};235236237template <typename E>238std::ostream& FileHelpers::writeEdgeVector(std::ostream& os, const std::vector<E>& edges) {239FileHelpers::writeInt(os, (int)edges.size());240std::vector<int> follow;241int maxFollow = 0;242E prev = edges.front();243for (typename std::vector<E>::const_iterator i = edges.begin() + 1; i != edges.end(); ++i) {244int idx = 0;245for (; idx < prev->getNumSuccessors(); ++idx) {246if (idx > 15) {247break;248}249if (prev->getSuccessors()[idx] == (*i)) {250follow.push_back(idx);251if (idx > maxFollow) {252maxFollow = idx;253}254break;255}256}257if (idx > 15 || idx == prev->getNumSuccessors()) {258follow.clear();259break;260}261prev = *i;262}263if (follow.empty()) {264for (typename std::vector<E>::const_iterator i = edges.begin(); i != edges.end(); ++i) {265FileHelpers::writeInt(os, (*i)->getNumericalID());266}267} else {268const int bits = maxFollow > 3 ? 4 : 2;269const int numFields = 8 * sizeof(int) / bits;270FileHelpers::writeInt(os, -bits);271FileHelpers::writeInt(os, edges.front()->getNumericalID());272int data = 0;273int field = 0;274for (std::vector<int>::const_iterator i = follow.begin(); i != follow.end(); ++i) {275data |= *i;276field++;277if (field == numFields) {278FileHelpers::writeInt(os, data);279data = 0;280field = 0;281} else {282data <<= bits;283}284}285if (field > 0) {286FileHelpers::writeInt(os, data << ((numFields - field - 1) * bits));287}288}289return os;290}291292293template <typename E>294void FileHelpers::readEdgeVector(std::istream& in, std::vector<const E*>& edges, const std::string& rid) {295int size;296in.read((char*) &size, sizeof(int));297edges.reserve(size);298int bitsOrEntry;299in.read((char*) &bitsOrEntry, sizeof(int));300if (bitsOrEntry < 0) {301const int bits = -bitsOrEntry;302const int numFields = 8 * sizeof(int) / bits;303const int mask = (1 << bits) - 1;304int edgeID;305in.read((char*) &edgeID, sizeof(int));306const E* prev = E::getAllEdges()[edgeID];307assert(prev != 0);308edges.push_back(prev);309size--;310int data = 0;311int field = numFields;312for (; size > 0; size--) {313if (field == numFields) {314in.read((char*) &data, sizeof(int));315field = 0;316}317int followIndex = (data >> ((numFields - field - 1) * bits)) & mask;318if (followIndex >= prev->getNumSuccessors()) {319throw ProcessError(TLF("Invalid follower index in route '%'!", rid));320}321prev = prev->getSuccessors()[followIndex];322edges.push_back(prev);323field++;324}325} else {326while (size > 0) {327const E* edge = E::getAllEdges()[bitsOrEntry];328if (edge == 0) {329throw ProcessError(TLF("An edge within the route '%' is not known!", rid));330}331edges.push_back(edge);332size--;333if (size > 0) {334in.read((char*) &bitsOrEntry, sizeof(int));335}336}337}338}339340341