Add conversion methods to hidl_string and hidl_vec

Add unit test for libhidl.

Test: mma
Test: make libhidl-test

Bug: 30471989
Bug: 31982575

Change-Id: Ic7d73a10e5cc178786c628e18d3659400b1afb92
diff --git a/Android.bp b/Android.bp
index 4a18dec..f9fbe10 100644
--- a/Android.bp
+++ b/Android.bp
@@ -12,6 +12,10 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
+subdirs = [
+    "test",
+]
+
 cc_library_shared {
     name: "libhidl",
     shared_libs: [
diff --git a/HidlSupport.cpp b/HidlSupport.cpp
index ed3e12e..c8a2821 100644
--- a/HidlSupport.cpp
+++ b/HidlSupport.cpp
@@ -20,6 +20,7 @@
 #include <android-base/logging.h>
 #include <cutils/properties.h>
 #include <regex>
+#include <utility>
 #endif
 
 namespace android {
@@ -28,9 +29,9 @@
 static const char *const kEmptyString = "";
 
 hidl_string::hidl_string()
-    : mBuffer(const_cast<char *>(kEmptyString)),
+    : mBuffer(kEmptyString),
       mSize(0),
-      mOwnsBuffer(true) {
+      mOwnsBuffer(false) {
 }
 
 hidl_string::~hidl_string() {
@@ -38,61 +39,100 @@
 }
 
 hidl_string::hidl_string(const char *s) : hidl_string() {
-    *this = s;
+    copyFrom(s, strlen(s));
 }
 
-hidl_string::hidl_string(const hidl_string &other)
-    : mBuffer(const_cast<char *>(kEmptyString)),
-      mSize(0),
-      mOwnsBuffer(true) {
-    setTo(other.c_str(), other.size());
+hidl_string::hidl_string(const hidl_string &other): hidl_string() {
+    copyFrom(other.c_str(), other.size());
+}
+
+hidl_string::hidl_string(const std::string &s) : hidl_string() {
+    copyFrom(s.c_str(), s.size());
+}
+
+hidl_string::hidl_string(hidl_string &&other): hidl_string() {
+    moveFrom(std::forward<hidl_string>(other));
+}
+
+hidl_string &hidl_string::operator=(hidl_string &&other) {
+    if (this != &other) {
+        clear();
+        moveFrom(std::forward<hidl_string>(other));
+    }
+    return *this;
 }
 
 hidl_string &hidl_string::operator=(const hidl_string &other) {
     if (this != &other) {
-        setTo(other.c_str(), other.size());
+        clear();
+        copyFrom(other.c_str(), other.size());
     }
 
     return *this;
 }
 
 hidl_string &hidl_string::operator=(const char *s) {
-    return setTo(s, strlen(s));
+    clear();
+    copyFrom(s, strlen(s));
+    return *this;
 }
 
-hidl_string &hidl_string::setTo(const char *data, size_t size) {
+hidl_string &hidl_string::operator=(const std::string &s) {
     clear();
+    copyFrom(s.c_str(), s.size());
+    return *this;
+}
 
-    mBuffer = (char *)malloc(size + 1);
-    memcpy(mBuffer, data, size);
-    mBuffer[size] = '\0';
+hidl_string::operator std::string() const {
+    return std::string(mBuffer, mSize);
+}
+
+hidl_string::operator const char *() const {
+    return mBuffer;
+}
+
+void hidl_string::copyFrom(const char *data, size_t size) {
+    // assume my resources are freed.
+
+    char *buf = (char *)malloc(size + 1);
+    memcpy(buf, data, size);
+    buf[size] = '\0';
+    mBuffer = buf;
 
     mSize = size;
     mOwnsBuffer = true;
+}
 
-    return *this;
+void hidl_string::moveFrom(hidl_string &&other) {
+    // assume my resources are freed.
+
+    mBuffer = other.mBuffer;
+    mSize = other.mSize;
+    mOwnsBuffer = other.mOwnsBuffer;
+
+    other.mOwnsBuffer = false;
 }
 
 void hidl_string::clear() {
     if (mOwnsBuffer && (mBuffer != kEmptyString)) {
-        free(mBuffer);
+        free(const_cast<char *>(mBuffer));
     }
 
-    mBuffer = const_cast<char *>(kEmptyString);
+    mBuffer = kEmptyString;
     mSize = 0;
-    mOwnsBuffer = true;
+    mOwnsBuffer = false;
 }
 
 void hidl_string::setToExternal(const char *data, size_t size) {
     clear();
 
-    mBuffer = const_cast<char *>(data);
+    mBuffer = data;
     mSize = size;
     mOwnsBuffer = false;
 }
 
 const char *hidl_string::c_str() const {
-    return mBuffer ? mBuffer : "";
+    return mBuffer;
 }
 
 size_t hidl_string::size() const {
diff --git a/include/hidl/HidlSupport.h b/include/hidl/HidlSupport.h
index d5df337..b284f57 100644
--- a/include/hidl/HidlSupport.h
+++ b/include/hidl/HidlSupport.h
@@ -34,15 +34,33 @@
     hidl_string();
     ~hidl_string();
 
+    // copy constructor.
     hidl_string(const hidl_string &);
+    // copy from a C-style string.
     hidl_string(const char *);
+    // copy from an std::string.
+    hidl_string(const std::string &);
+
+    // move constructor.
+    hidl_string(hidl_string &&);
 
     const char *c_str() const;
     size_t size() const;
     bool empty() const;
 
+    // copy assignment operator.
     hidl_string &operator=(const hidl_string &);
+    // copy from a C-style string.
     hidl_string &operator=(const char *s);
+    // copy from an std::string.
+    hidl_string &operator=(const std::string &);
+    // move assignment operator.
+    hidl_string &operator=(hidl_string &&other);
+    // cast to std::string.
+    operator std::string() const;
+    // cast to C-style string. Caller is responsible
+    // to maintain this hidl_string alive.
+    operator const char *() const;
 
     void clear();
 
@@ -61,11 +79,15 @@
     static const size_t kOffsetOfBuffer;
 
 private:
-    char *mBuffer;
+    const char *mBuffer;
     size_t mSize;  // NOT including the terminating '\0'.
-    bool mOwnsBuffer;
+    bool mOwnsBuffer; // if true then mBuffer is a mutable char *
 
-    hidl_string &setTo(const char *data, size_t size);
+    // copy from data with size. Assume that my memory is freed
+    // (through clear(), for example)
+    void copyFrom(const char *data, size_t size);
+    // move from another hidl_string
+    void moveFrom(hidl_string &&);
 };
 
 ////////////////////////////////////////////////////////////////////////////////
@@ -78,10 +100,7 @@
           mOwnsBuffer(true) {
     }
 
-    hidl_vec(const hidl_vec<T> &other)
-        : mBuffer(NULL),
-          mSize(0),
-          mOwnsBuffer(true) {
+    hidl_vec(const hidl_vec<T> &other) : hidl_vec() {
         *this = other;
     }
 
@@ -89,6 +108,10 @@
         *this = static_cast<hidl_vec &&>(other);
     }
 
+    hidl_vec(const std::vector<T> &other) : hidl_vec() {
+        *this = other;
+    }
+
     ~hidl_vec() {
         if (mOwnsBuffer) {
             delete[] mBuffer;
@@ -137,20 +160,30 @@
             if (mOwnsBuffer) {
                 delete[] mBuffer;
             }
-            mBuffer = NULL;
-            mSize = other.mSize;
-            mOwnsBuffer = true;
-            if (mSize > 0) {
-                mBuffer = new T[mSize];
-                for (size_t i = 0; i < mSize; ++i) {
-                    mBuffer[i] = other.mBuffer[i];
-                }
-            }
+            copyFrom(other, other.mSize);
         }
 
         return *this;
     }
 
+    // copy from an std::vector.
+    hidl_vec &operator=(const std::vector<T> &other) {
+        if (mOwnsBuffer) {
+            delete[] mBuffer;
+        }
+        copyFrom(other, other.size());
+        return *this;
+    }
+
+    // cast to an std::vector.
+    operator std::vector<T>() const {
+        std::vector<T> v(mSize);
+        for (size_t i = 0; i < mSize; ++i) {
+            v[i] = mBuffer[i];
+        }
+        return v;
+    }
+
     size_t size() const {
         return mSize;
     }
@@ -200,6 +233,21 @@
     T *mBuffer;
     size_t mSize;
     bool mOwnsBuffer;
+
+    // copy from an array-like object, assuming my resources are freed.
+    template <typename Array>
+    void copyFrom(const Array &data, size_t size) {
+        mSize = size;
+        mOwnsBuffer = true;
+        if (mSize > 0) {
+            mBuffer = new T[size];
+            for (size_t i = 0; i < size; ++i) {
+                mBuffer[i] = data[i];
+            }
+        } else {
+            mBuffer = NULL;
+        }
+    }
 };
 
 ////////////////////////////////////////////////////////////////////////////////
diff --git a/test/Android.bp b/test/Android.bp
new file mode 100644
index 0000000..b645f59
--- /dev/null
+++ b/test/Android.bp
@@ -0,0 +1,19 @@
+cc_test {
+    name: "libhidl-test",
+    gtest: false,
+    srcs: ["main.cpp"],
+
+    shared_libs: [
+        "libbase",
+        "libhidl",
+        "libhwbinder",
+        "liblog",
+        "libutils",
+    ],
+    static_libs: ["libgtest"],
+
+    cflags: [
+        "-O0",
+        "-g",
+    ],
+}
diff --git a/test/main.cpp b/test/main.cpp
new file mode 100644
index 0000000..c2dd773
--- /dev/null
+++ b/test/main.cpp
@@ -0,0 +1,88 @@
+#define LOG_TAG "LibHidlTest"
+
+#include <android-base/logging.h>
+#include <gtest/gtest.h>
+#include <hidl/HidlSupport.h>
+#include <vector>
+
+#define EXPECT_ARRAYEQ(__a1__, __a2__, __size__) EXPECT_TRUE(isArrayEqual(__a1__, __a2__, __size__))
+
+template<typename T, typename S>
+static inline bool isArrayEqual(const T arr1, const S arr2, size_t size) {
+    for(size_t i = 0; i < size; i++)
+        if(arr1[i] != arr2[i])
+            return false;
+    return true;
+}
+
+class LibHidlTest : public ::testing::Test {
+public:
+    virtual void SetUp() override {
+    }
+    virtual void TearDown() override {
+    }
+};
+
+TEST_F(LibHidlTest, StringTest) {
+    using android::hardware::hidl_string;
+    hidl_string s; // empty constructor
+    EXPECT_STREQ(s.c_str(), "");
+    hidl_string s1 = "s1"; // copy = from cstr
+    EXPECT_STREQ(s1.c_str(), "s1");
+    hidl_string s2("s2"); // copy constructor from cstr
+    EXPECT_STREQ(s2.c_str(), "s2");
+    hidl_string s3 = hidl_string("s3"); // move =
+    EXPECT_STREQ(s3.c_str(), "s3");
+    hidl_string s4(hidl_string(hidl_string("s4"))); // move constructor
+    EXPECT_STREQ(s4.c_str(), "s4");
+    hidl_string s5(std::string("s5")); // copy constructor from std::string
+    EXPECT_STREQ(s5, "s5");
+    hidl_string s6 = std::string("s6"); // copy = from std::string
+    EXPECT_STREQ(s6, "s6");
+    hidl_string s7(s6); // copy constructor
+    EXPECT_STREQ(s7, "s6");
+    hidl_string s8 = s7; // copy =
+    EXPECT_STREQ(s8, "s6");
+    char myCString[20] = "myCString";
+    s.setToExternal(&myCString[0], strlen(myCString));
+    EXPECT_STREQ(s, "myCString");
+    myCString[2] = 'D';
+    EXPECT_STREQ(s, "myDString");
+    s.clear(); // should not affect myCString
+    EXPECT_STREQ(myCString, "myDString");
+    // casts
+    s = "great";
+    std::string myString = s;
+    const char *anotherCString = s;
+    EXPECT_EQ(myString, "great");
+    EXPECT_STREQ(anotherCString, "great");
+}
+
+TEST_F(LibHidlTest, VecTest) {
+    using android::hardware::hidl_vec;
+    using std::vector;
+    int32_t array[] = {5, 6, 7};
+    vector<int32_t> v(array, array + 3);
+
+    hidl_vec<int32_t> hv1 = v; // copy =
+    EXPECT_ARRAYEQ(hv1, array, 3);
+    EXPECT_ARRAYEQ(hv1, v, 3);
+    hidl_vec<int32_t> hv2(v); // copy constructor
+    EXPECT_ARRAYEQ(hv2, v, 3);
+
+    vector<int32_t> v2 = hv1; // cast
+    EXPECT_ARRAYEQ(v2, v, 3);
+}
+
+template <typename T>
+void great(android::hardware::hidl_vec<T>) {}
+
+TEST_F(LibHidlTest, VecCopyTest) {
+    android::hardware::hidl_vec<int32_t> v;
+    great(v);
+}
+
+int main(int argc, char **argv) {
+    ::testing::InitGoogleTest(&argc, argv);
+    return RUN_ALL_TESTS();
+}