From b9506ca8dea2aca4cf7b8c1fe50fbe9af9f0cd0e Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Adrien=20B=C3=A9raud?= <adrien.beraud@savoirfairelinux.com>
Date: Wed, 17 Mar 2021 14:18:36 -0400
Subject: [PATCH] string utils: add string_view split, update base64

Change-Id: Id2d03ca3d645d7178019bf05c1951c6a78019095
---
 src/base64.cpp                                | 22 ++++-----------
 src/base64.h                                  | 17 ++++++-----
 src/ice_transport.cpp                         |  9 ++----
 src/ip_utils.cpp                              | 18 ++++--------
 src/jamidht/jamiaccount.cpp                   |  7 ++---
 src/jamidht/namedirectory.cpp                 |  2 +-
 src/media/media_encoder.cpp                   | 13 +++++----
 src/string_utils.h                            | 28 +++++++++++++++++++
 .../string_utils/testString_utils.cpp         | 20 +++++++++++--
 9 files changed, 81 insertions(+), 55 deletions(-)

diff --git a/src/base64.cpp b/src/base64.cpp
index c9f5f18775..e2b467d1cf 100644
--- a/src/base64.cpp
+++ b/src/base64.cpp
@@ -19,9 +19,6 @@
 #include "base64.h"
 #include "sip/sip_utils.h"
 
-#include <stdint.h>
-#include <stdlib.h>
-#include <iostream>
 #include <pjlib.h>
 #include <pjlib-util/base64.h>
 
@@ -29,18 +26,17 @@ namespace jami {
 namespace base64 {
 
 std::string
-encode(const std::vector<uint8_t>::const_iterator begin,
-       const std::vector<uint8_t>::const_iterator end)
+encode(std::string_view dat)
 {
-    int input_length = std::distance(begin, end);
-    if (input_length == 0)
+    if (dat.empty())
         return {};
 
+    int input_length = dat.size();
     int output_length = PJ_BASE256_TO_BASE64_LEN(input_length);
     std::string out;
     out.resize(output_length);
 
-    if (pj_base64_encode(&(*begin), input_length, &(*out.begin()), &output_length) != PJ_SUCCESS) {
+    if (pj_base64_encode((const uint8_t*)dat.data(), input_length, &(*out.begin()), &output_length) != PJ_SUCCESS) {
         throw base64_exception();
     }
 
@@ -48,20 +44,14 @@ encode(const std::vector<uint8_t>::const_iterator begin,
     return out;
 }
 
-std::string
-encode(const std::vector<uint8_t>& dat)
-{
-    return encode(dat.cbegin(), dat.cend());
-}
-
 std::vector<uint8_t>
-decode(const std::string& str)
+decode(std::string_view str)
 {
     if (str.empty())
         return {};
 
     int output_length = PJ_BASE64_TO_BASE256_LEN(str.length());
-    const pj_str_t input(sip_utils::CONST_PJ_STR(str));
+    auto input = sip_utils::CONST_PJ_STR(str);
 
     std::vector<uint8_t> out;
     out.resize(output_length);
diff --git a/src/base64.h b/src/base64.h
index d7ec56614e..cc2b7fe431 100644
--- a/src/base64.h
+++ b/src/base64.h
@@ -18,23 +18,26 @@
 
 #pragma once
 
-#include <stdint.h>
-#include <stddef.h>
-
 #include <string>
+#include <string_view>
 #include <vector>
 #include <exception>
 
+#include <cstdint>
+
 namespace jami {
 namespace base64 {
 
 class base64_exception : public std::exception
 {};
 
-std::string encode(const std::vector<uint8_t>::const_iterator begin,
-                   const std::vector<uint8_t>::const_iterator end);
-std::string encode(const std::vector<uint8_t>& dat);
-std::vector<uint8_t> decode(const std::string& str);
+std::string encode(std::string_view);
+
+inline std::string encode(const std::vector<uint8_t>& data) {
+    return encode(std::string_view((const char*)data.data(), data.size()));
+}
+
+std::vector<uint8_t> decode(std::string_view);
 
 } // namespace base64
 } // namespace jami
diff --git a/src/ice_transport.cpp b/src/ice_transport.cpp
index d1b45bb5ce..9327dd30f6 100644
--- a/src/ice_transport.cpp
+++ b/src/ice_transport.cpp
@@ -1560,22 +1560,19 @@ ICESDP
 IceTransport::parse_SDP(const std::string& sdp_msg, const IceTransport& ice)
 {
     ICESDP res;
-    std::istringstream stream(sdp_msg);
-    std::string line;
     int nr = 0;
-    while (std::getline(stream, line)) {
+    for (std::string_view line; jami::getline(sdp_msg, line); nr++) {
         if (nr == 0) {
             res.rem_ufrag = line;
         } else if (nr == 1) {
             res.rem_pwd = line;
         } else {
             IceCandidate cand;
-            if (ice.getCandidateFromSDP(line, cand)) {
-                JAMI_DBG("Add remote ICE candidate: %s", line.c_str());
+            if (ice.getCandidateFromSDP(std::string(line), cand)) {
+                JAMI_DBG("Add remote ICE candidate: %.*s", (int)line.size(), line.data());
                 res.rem_candidates.emplace_back(cand);
             }
         }
-        nr++;
     }
     return res;
 }
diff --git a/src/ip_utils.cpp b/src/ip_utils.cpp
index 39315c3ff4..9759c24679 100644
--- a/src/ip_utils.cpp
+++ b/src/ip_utils.cpp
@@ -180,23 +180,17 @@ ip_utils::getHostName(char* out, size_t out_len)
 std::string
 ip_utils::getGateway(char* localHost, ip_utils::subnet_mask prefix)
 {
-    std::string localHostStr(localHost);
+    std::string_view localHostStr(localHost);
     if (prefix == ip_utils::subnet_mask::prefix_32bit)
-        return localHostStr;
+        return std::string(localHostStr);
     std::string defaultGw {};
-    std::istringstream iss(localHostStr);
     // Make a vector of each individual number in the ip address.
-    std::vector<std::string> tokens;
-    std::string token;
-    while (std::getline(iss, token, '.')) {
-        if (!token.empty())
-            tokens.push_back(token);
-    }
+    std::vector<std::string_view> tokens = split_string(localHostStr, '.');
     // Build a gateway address from the individual ip components.
-    for (unsigned int i = 0; i <= (unsigned int) prefix; i++)
+    for (unsigned i = 0; i <= (unsigned) prefix; i++)
         defaultGw += tokens[i] + ".";
-    for (unsigned int i = (unsigned int) ip_utils::subnet_mask::prefix_32bit;
-         i > (unsigned int) prefix + 1;
+    for (unsigned i = (unsigned) ip_utils::subnet_mask::prefix_32bit;
+         i > (unsigned) prefix + 1;
          i--)
         defaultGw += "0.";
     defaultGw += "1";
diff --git a/src/jamidht/jamiaccount.cpp b/src/jamidht/jamiaccount.cpp
index 008938f92a..0b67b7e027 100644
--- a/src/jamidht/jamiaccount.cpp
+++ b/src/jamidht/jamiaccount.cpp
@@ -1929,10 +1929,9 @@ JamiAccount::loadBootstrap() const
 {
     std::vector<std::string> bootstrap;
     if (!hostname_.empty()) {
-        std::istringstream ss(hostname_);
-        std::string node_addr;
-        while (std::getline(ss, node_addr, ';'))
-            bootstrap.emplace_back(std::move(node_addr));
+        std::string_view node_addr;
+        while (jami::getline(hostname_, node_addr, ';'))
+            bootstrap.emplace_back(node_addr);
         for (const auto& b : bootstrap)
             JAMI_DBG("[Account %s] Bootstrap node: %s", getAccountID().c_str(), b.c_str());
     }
diff --git a/src/jamidht/namedirectory.cpp b/src/jamidht/namedirectory.cpp
index 182dc98e1f..45e7c1d75f 100644
--- a/src/jamidht/namedirectory.cpp
+++ b/src/jamidht/namedirectory.cpp
@@ -360,7 +360,7 @@ NameDirectory::registerName(const std::string& addr,
         std::stringstream ss;
         ss << "{\"addr\":\"" << addr << "\",\"owner\":\"" << owner << "\",\"signature\":\""
            << signedname << "\",\"publickey\":\""
-           << base64::encode(jami::Blob(publickey.begin(), publickey.end())) << "\"}";
+           << base64::encode(publickey) << "\"}";
         body = ss.str();
     }
     auto request = std::make_shared<Request>(*httpContext_,
diff --git a/src/media/media_encoder.cpp b/src/media/media_encoder.cpp
index c50f824d84..7413778ee9 100644
--- a/src/media/media_encoder.cpp
+++ b/src/media/media_encoder.cpp
@@ -517,15 +517,16 @@ MediaEncoder::print_sdp()
 #else
     const auto sdp_size = outputCtx_->streams[currentStreamIdx_]->codec->extradata_size + 2048;
 #endif
-    std::string result;
     std::string sdp(sdp_size, '\0');
     av_sdp_create(&outputCtx_, 1, &(*sdp.begin()), sdp_size);
-    std::istringstream iss(sdp);
-    std::string line;
-    while (std::getline(iss, line)) {
+
+    std::string result;
+    result.reserve(sdp_size);
+    std::string_view line;
+    while (jami::getline(sdp, line)) {
         /* strip windows line ending */
-        line = line.substr(0, line.length() - 1);
-        result += line + "\n";
+        result += line.substr(0, line.length() - 1);
+        result += "\n"sv;
     }
 #ifdef DEBUG_SDP
     JAMI_DBG("Sending SDP:\n%s", result.c_str());
diff --git a/src/string_utils.h b/src/string_utils.h
index 1b9493693c..bd13f4eac9 100644
--- a/src/string_utils.h
+++ b/src/string_utils.h
@@ -114,6 +114,34 @@ stod(const std::string& str)
 
 std::string_view trim(std::string_view s);
 
+/**
+ * Split a string_view with an API similar to std::getline.
+ * @param str The input string to iterate on.
+ * @param line The output substring, also used as an iterator.
+ *             It must be default-initialised when this function is used 
+ *             for the first time with a given string,
+ *             and should not be modified by the caller during iteration.
+ * @param delim The delimiter.
+ * @return True if line was set, false if the end of the input was reached.
+ */
+inline
+bool getline(const std::string_view str, std::string_view& line, char delim = '\n')
+{
+    if (str.empty())
+        return false;
+    if (line.data() == nullptr) {
+        // first iteration
+        line = str.substr(0, str.find(delim));
+    } else {
+        size_t prevEnd = line.data() + line.size() - str.data();
+        if (prevEnd >= str.size())
+            return false;
+        auto nextStr = str.substr(prevEnd + 1);
+        line = nextStr.substr(0, nextStr.find(delim));
+    }
+    return  true;
+}
+
 inline
 std::vector<std::string_view> split_string(std::string_view str, char delim)
 {
diff --git a/test/unitTest/string_utils/testString_utils.cpp b/test/unitTest/string_utils/testString_utils.cpp
index 68d8a51ba4..bb114af6dc 100644
--- a/test/unitTest/string_utils/testString_utils.cpp
+++ b/test/unitTest/string_utils/testString_utils.cpp
@@ -23,9 +23,12 @@
 
 #include "string_utils.h"
 #include <string>
+#include <string_view>
 
 #include "../../test_runner.h"
 
+using namespace std::literals;
+
 namespace jami { namespace test {
 
 class StringUtilsTest : public CppUnit::TestFixture {
@@ -90,15 +93,26 @@ StringUtilsTest::to_number_test()
 void
 StringUtilsTest::split_string_test()
 {
-    auto split_string_result = split_string("*fdg454()**{&xcx*", '*');
+    constexpr auto data = "*fdg454()**{&xcx*"sv;
+    auto split_string_result = split_string(data, '*');
     CPPUNIT_ASSERT(split_string_result.size() == 2);
-    CPPUNIT_ASSERT(split_string_result.at(0) == "fdg454()"
-                   && split_string_result.at(1) == "{&xcx");
+    CPPUNIT_ASSERT(split_string_result.at(0) == "fdg454()"sv
+                   && split_string_result.at(1) == "{&xcx"sv);
 
     auto split_string_to_unsigned_result = split_string_to_unsigned("/4545497//45454/", '/');
     CPPUNIT_ASSERT(split_string_to_unsigned_result.size() == 2);
     CPPUNIT_ASSERT(split_string_to_unsigned_result.at(0) == 4545497
                    && split_string_to_unsigned_result.at(1) == 45454);
+
+    std::string_view line;
+    split_string_result.clear();
+    while (jami::getline(data, line, '*')) {
+        split_string_result.emplace_back(line);
+    }
+    CPPUNIT_ASSERT_EQUAL(4, split_string_result.size());
+    CPPUNIT_ASSERT_EQUAL(true, split_string_result.at(0).empty());
+    CPPUNIT_ASSERT_EQUAL("fdg454()"sv, split_string_result.at(1));
+    CPPUNIT_ASSERT_EQUAL(true, split_string_result.at(2).empty());
 }
 
 }} // namespace jami_test
-- 
GitLab