diff --git a/CMakeLists.txt b/CMakeLists.txt
index 049035d..13ca24a 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -1,6 +1,6 @@
 cmake_minimum_required(VERSION 3.20)
 include(cmake/color.cmake)
-set(BLT_VERSION 2.1.4)
+set(BLT_VERSION 2.1.5)
 
 set(BLT_TARGET BLT)
 
diff --git a/include/blt/std/string.h b/include/blt/std/string.h
index 9dafc52..11e13d3 100644
--- a/include/blt/std/string.h
+++ b/include/blt/std/string.h
@@ -252,6 +252,22 @@ namespace blt::string
 #endif
     }
     
+    inline std::string ensure_ends_with_path_separator(std::string_view string)
+    {
+        if (ends_with(string, '/'))
+            return std::string(string);
+        else
+            return std::string(string) += '/';
+    }
+    
+    inline std::string ensure_ends_with_path_separator(std::string&& string)
+    {
+        if (ends_with(string, '/'))
+            return string;
+        else
+            return (std::move(string) + '/');
+    }
+    
     class match
     {
         private:
diff --git a/libraries/parallel-hashmap b/libraries/parallel-hashmap
index 63acc33..4817a6d 160000
--- a/libraries/parallel-hashmap
+++ b/libraries/parallel-hashmap
@@ -1 +1 @@
-Subproject commit 63acc3336f941c6f324c88eb9ee4ce623a460cd5
+Subproject commit 4817a6d3b8407063cf0328eb92dbb27ee2f55528
diff --git a/src/blt/std/string.cpp b/src/blt/std/string.cpp
index c8c125c..3631657 100644
--- a/src/blt/std/string.cpp
+++ b/src/blt/std/string.cpp
@@ -130,10 +130,13 @@ namespace blt
         {
             auto size = pos - from;
             auto token = s.substr(from, size);
-            tokens.emplace_back(token);
+            if (!token.empty())
+                tokens.emplace_back(token);
             from += size + delim.length();
         }
-        tokens.emplace_back(s.substr(from));
+        auto str = s.substr(from);
+        if (!str.empty())
+            tokens.emplace_back(str);
         return tokens;
     }
     
@@ -146,10 +149,13 @@ namespace blt
         {
             auto size = pos - from;
             auto token = s.substr(from, size);
-            tokens.emplace_back(token);
+            if (!token.empty())
+                tokens.emplace_back(token);
             from += size + 1;
         }
-        tokens.emplace_back(s.substr(from));
+        auto str = s.substr(from);
+        if (!str.empty())
+            tokens.emplace_back(str);
         return tokens;
     }
     
@@ -162,10 +168,13 @@ namespace blt
         {
             auto size = pos - from;
             auto token = s.substr(from, size);
-            tokens.push_back(token);
+            if (!token.empty())
+                tokens.push_back(token);
             from += size + delim.length();
         }
-        tokens.push_back(s.substr(from));
+        auto str = s.substr(from);
+        if (!str.empty())
+            tokens.push_back(str);
         return tokens;
     }
     
@@ -178,10 +187,13 @@ namespace blt
         {
             auto size = pos - from;
             auto token = s.substr(from, size);
-            tokens.push_back(token);
+            if (!token.empty())
+                tokens.push_back(token);
             from += size + 1;
         }
-        tokens.push_back(s.substr(from));
+        auto str = s.substr(from);
+        if (!str.empty())
+            tokens.push_back(str);
         return tokens;
     }