[C++ hidl-gen] Fix multi-dimensional arrays, vectors of arrays.

Bug: 31438033
Change-Id: I254e8bfeb8fbf665dd3836825aa271603be6d14c
diff --git a/ArrayType.cpp b/ArrayType.cpp
index 6862262..2ca27ec 100644
--- a/ArrayType.cpp
+++ b/ArrayType.cpp
@@ -35,20 +35,17 @@
                                   bool specifyNamespaces) const {
     const std::string base = mElementType->getCppType(extra, specifyNamespaces);
 
-    CHECK(extra->empty());
-
-    *extra = "[" + mDimension + "]";
+    *extra = "[" + mDimension + "]" + (*extra);
 
     switch (mode) {
         case StorageMode_Stack:
             return base;
 
         case StorageMode_Argument:
-            return "const " + base;
-
         case StorageMode_Result:
         {
-            extra->clear();
+            *extra = " /* " + base + (*extra) + " */";
+
             return "const " + base + "*";
         }
     }
@@ -103,6 +100,7 @@
             << mDimension
             << " * sizeof("
             << baseType
+            << baseExtra
             << "), &"
             << parentName
             << ");\n";
@@ -112,6 +110,7 @@
 
     emitReaderWriterEmbedded(
             out,
+            0 /* depth */,
             name,
             isReader /* nameIsPointer */,
             parcelObj,
@@ -124,6 +123,7 @@
 
 void ArrayType::emitReaderWriterEmbedded(
         Formatter &out,
+        size_t depth,
         const std::string &name,
         bool nameIsPointer,
         const std::string &parcelObj,
@@ -141,22 +141,35 @@
     std::string baseExtra;
     std::string baseType = mElementType->getCppType(&baseExtra);
 
-    out << "for (size_t _hidl_index = 0; _hidl_index < "
+    std::string iteratorName = "_hidl_index_" + std::to_string(depth);
+
+    out << "for (size_t "
+        << iteratorName
+        << " = 0; "
+        << iteratorName
+        << " < "
         << mDimension
-        << "; ++_hidl_index) {\n";
+        << "; ++"
+        << iteratorName
+        << ") {\n";
 
     out.indent();
 
     mElementType->emitReaderWriterEmbedded(
             out,
-            name + "[_hidl_index]",
+            depth + 1,
+            name + "[" + iteratorName + "]",
             false /* nameIsPointer */,
             parcelObj,
             parcelObjIsPointer,
             isReader,
             mode,
             parentName,
-            offsetText + " + _hidl_index * sizeof(" + baseType + ")");
+            offsetText
+                + " + " + iteratorName + " * sizeof("
+                + baseType
+                + baseExtra
+                + ")");
 
     out.unindent();
 
diff --git a/ArrayType.h b/ArrayType.h
index 26e47de..7a39716 100644
--- a/ArrayType.h
+++ b/ArrayType.h
@@ -45,6 +45,7 @@
 
     void emitReaderWriterEmbedded(
             Formatter &out,
+            size_t depth,
             const std::string &name,
             bool nameIsPointer,
             const std::string &parcelObj,
diff --git a/CompoundType.cpp b/CompoundType.cpp
index b3f4269..8a692c1 100644
--- a/CompoundType.cpp
+++ b/CompoundType.cpp
@@ -142,6 +142,7 @@
 
     emitReaderWriterEmbedded(
             out,
+            0 /* depth */,
             name,
             isReader /* nameIsPointer */,
             parcelObj,
@@ -154,6 +155,7 @@
 
 void CompoundType::emitReaderWriterEmbedded(
         Formatter &out,
+        size_t /* depth */,
         const std::string &name,
         bool nameIsPointer,
         const std::string &parcelObj,
@@ -577,6 +579,7 @@
 
         field->type().emitReaderWriterEmbedded(
                 out,
+                0 /* depth */,
                 field->name(),
                 false /* nameIsPointer */,
                 "parcel",
diff --git a/CompoundType.h b/CompoundType.h
index 42e3b33..b59bbd2 100644
--- a/CompoundType.h
+++ b/CompoundType.h
@@ -54,6 +54,7 @@
 
     void emitReaderWriterEmbedded(
             Formatter &out,
+            size_t depth,
             const std::string &name,
             bool nameIsPointer,
             const std::string &parcelObj,
diff --git a/HandleType.cpp b/HandleType.cpp
index 178a2c7..1ab2e69 100644
--- a/HandleType.cpp
+++ b/HandleType.cpp
@@ -84,6 +84,7 @@
 
 void HandleType::emitReaderWriterEmbedded(
         Formatter &out,
+        size_t /* depth */,
         const std::string &name,
         bool nameIsPointer,
         const std::string &parcelObj,
diff --git a/HandleType.h b/HandleType.h
index 386adbb..94b040d 100644
--- a/HandleType.h
+++ b/HandleType.h
@@ -42,6 +42,7 @@
 
     void emitReaderWriterEmbedded(
             Formatter &out,
+            size_t depth,
             const std::string &name,
             bool nameIsPointer,
             const std::string &parcelObj,
diff --git a/PredefinedType.cpp b/PredefinedType.cpp
index c1468ab..ad9698c 100644
--- a/PredefinedType.cpp
+++ b/PredefinedType.cpp
@@ -105,6 +105,7 @@
 
     emitReaderWriterEmbedded(
             out,
+            0 /* depth */,
             name,
             isReader /* nameIsPointer */,
             parcelObj,
@@ -117,6 +118,7 @@
 
 void PredefinedType::emitReaderWriterEmbedded(
         Formatter &out,
+        size_t /* depth */,
         const std::string &name,
         bool nameIsPointer,
         const std::string &parcelObj,
diff --git a/PredefinedType.h b/PredefinedType.h
index 965b786..84c9441 100644
--- a/PredefinedType.h
+++ b/PredefinedType.h
@@ -41,6 +41,7 @@
 
     void emitReaderWriterEmbedded(
             Formatter &out,
+            size_t depth,
             const std::string &name,
             bool nameIsPointer,
             const std::string &parcelObj,
diff --git a/StringType.cpp b/StringType.cpp
index ea00ddc..d5dba42 100644
--- a/StringType.cpp
+++ b/StringType.cpp
@@ -104,6 +104,7 @@
 
     emitReaderWriterEmbedded(
             out,
+            0 /* depth */,
             name,
             isReader /* nameIsPointer */,
             parcelObj,
@@ -116,6 +117,7 @@
 
 void StringType::emitReaderWriterEmbedded(
         Formatter &out,
+        size_t /* depth */,
         const std::string &name,
         bool nameIsPointer,
         const std::string &parcelObj,
diff --git a/StringType.h b/StringType.h
index 9b7cb04..9062d90 100644
--- a/StringType.h
+++ b/StringType.h
@@ -44,6 +44,7 @@
 
     void emitReaderWriterEmbedded(
             Formatter &out,
+            size_t depth,
             const std::string &name,
             bool nameIsPointer,
             const std::string &parcelObj,
diff --git a/Type.cpp b/Type.cpp
index 9fea80f..8abffd6 100644
--- a/Type.cpp
+++ b/Type.cpp
@@ -94,6 +94,7 @@
 
 void Type::emitReaderWriterEmbedded(
         Formatter &,
+        size_t,
         const std::string &,
         bool,
         const std::string &,
diff --git a/Type.h b/Type.h
index 0f3812b..3e276c2 100644
--- a/Type.h
+++ b/Type.h
@@ -98,6 +98,7 @@
 
     virtual void emitReaderWriterEmbedded(
             Formatter &out,
+            size_t depth,
             const std::string &name,
             bool nameIsPointer,
             const std::string &parcelObj,
diff --git a/VectorType.cpp b/VectorType.cpp
index 9ddb171..8e2c599 100644
--- a/VectorType.cpp
+++ b/VectorType.cpp
@@ -36,9 +36,10 @@
           std::string(specifyNamespaces ? "::android::hardware::" : "")
         + "hidl_vec<"
         + mElementType->getCppType(extra, specifyNamespaces)
+        + (*extra)
         + ">";
 
-    CHECK(extra->empty());
+    extra->clear();
 
     switch (mode) {
         case StorageMode_Stack:
@@ -77,6 +78,7 @@
         out << name
             << " = (const ::android::hardware::hidl_vec<"
             << baseType
+            << baseExtra
             << "> *)"
             << parcelObjDeref
             << "readBuffer(&"
@@ -108,6 +110,7 @@
 
     emitReaderWriterEmbedded(
             out,
+            0 /* depth */,
             name,
             isReader /* nameIsPointer */,
             parcelObj,
@@ -118,8 +121,19 @@
             "0 /* parentOffset */");
 }
 
+// Remove any trailing array indices from the given name, i.e. foo[2][3] => foo
+static std::string StripIndex(const std::string &name) {
+    size_t pos = name.find("[");
+    if (pos == std::string::npos) {
+        return name;
+    }
+
+    return name.substr(0, pos);
+}
+
 void VectorType::emitReaderWriterEmbedded(
         Formatter &out,
+        size_t depth,
         const std::string &name,
         bool nameIsPointer,
         const std::string &parcelObj,
@@ -131,7 +145,7 @@
     std::string baseExtra;
     std::string baseType = Type::getCppType(&baseExtra);
 
-    const std::string childName = "_hidl_" + name + "_child";
+    const std::string childName = "_hidl_" + StripIndex(name) + "_child";
     out << "size_t " << childName << ";\n\n";
 
     emitReaderWriterEmbeddedForTypeName(
@@ -155,22 +169,32 @@
 
     baseType = mElementType->getCppType(&baseExtra);
 
-    out << "for (size_t _hidl_index = 0; _hidl_index < "
+    std::string iteratorName = "_hidl_index_" + std::to_string(depth);
+
+    out << "for (size_t "
+        << iteratorName
+        << " = 0; "
+        << iteratorName
+        << " < "
         << nameDeref
-        << "size(); ++_hidl_index) {\n";
+        << "size(); ++"
+        << iteratorName
+        << ") {\n";
 
     out.indent();
 
     mElementType->emitReaderWriterEmbedded(
             out,
-            (nameIsPointer ? "(*" + name + ")" : name) + "[_hidl_index]",
+            depth + 1,
+            (nameIsPointer ? "(*" + name + ")" : name)
+                + "[" + iteratorName + "]",
             false /* nameIsPointer */,
             parcelObj,
             parcelObjIsPointer,
             isReader,
             mode,
             childName,
-            "_hidl_index * sizeof(" + baseType + ")");
+            iteratorName + " * sizeof(" + baseType + baseExtra + ")");
 
     out.unindent();
 
diff --git a/VectorType.h b/VectorType.h
index fb77b3d..6655530 100644
--- a/VectorType.h
+++ b/VectorType.h
@@ -43,6 +43,7 @@
 
     void emitReaderWriterEmbedded(
             Formatter &out,
+            size_t depth,
             const std::string &name,
             bool nameIsPointer,
             const std::string &parcelObj,
diff --git a/test/main.cpp b/test/main.cpp
index 17170af..00b8cf6 100644
--- a/test/main.cpp
+++ b/test/main.cpp
@@ -162,6 +162,13 @@
             const hidl_vec<hidl_string> &vector,
             haveAStringVec_cb _cb) override;
 
+    Return<void> transposeMe(
+            const float *in /* float[3][5] */, transposeMe_cb _cb) override;
+
+    Return<void> callingDrWho(
+            const MultiDimensional &in,
+            callingDrWho_cb _hidl_cb) override;
+
     Return<void> thisIsNew() override;
 };
 
@@ -343,6 +350,101 @@
     return Void();
 }
 
+static std::string FloatArray2DToString(const float *x, size_t n1, size_t n2) {
+    std::string s;
+    s += "[";
+    for (size_t i = 0; i < n1; ++i) {
+        if (i > 0) {
+            s += ", ";
+        }
+
+        s += "[";
+        for (size_t j = 0; j < n2; ++j) {
+            if (j > 0) {
+                s += ", ";
+            }
+            s += std::to_string(x[i * n2 + j]);
+        }
+        s += "]";
+    }
+    s += "]";
+
+    return s;
+}
+
+Return<void> Bar::transposeMe(
+        const float *in /* float[3][5] */, transposeMe_cb _cb) {
+    ALOGI("SERVER(Bar) transposeMe(%s)",
+          FloatArray2DToString(in, 3, 5).c_str());
+
+    float out[5][3];
+    for (size_t i = 0; i < 5; ++i) {
+        for (size_t j = 0; j < 3; ++j) {
+            out[i][j] = in[5 * j + i];
+        }
+    }
+
+    _cb(&out[0][0]);
+
+    return Void();
+}
+
+static std::string QuuxToString(const IFoo::Quux &val) {
+    std::string s;
+
+    s = "Quux(first='";
+    s += val.first.c_str();
+    s += "', last='";
+    s += val.last.c_str();
+    s += "')";
+
+    return s;
+}
+
+static std::string MultiDimensionalToString(const IFoo::MultiDimensional &val) {
+    std::string s;
+
+    s += "MultiDimensional(";
+
+    s += "quuxMatrix=[";
+    for (size_t i = 0; i < 5; ++i) {
+        if (i > 0) {
+            s += ", ";
+        }
+
+        s += "[";
+        for (size_t j = 0; j < 3; ++j) {
+            if (j > 0) {
+                s += ", ";
+            }
+
+            s += QuuxToString(val.quuxMatrix[i][j]);
+        }
+    }
+    s += "]";
+
+    s += ")";
+
+    return s;
+}
+
+Return<void> Bar::callingDrWho(
+        const MultiDimensional &in, callingDrWho_cb _hidl_cb) {
+    ALOGI("SERVER(Bar) callingDrWho(%s)", MultiDimensionalToString(in).c_str());
+
+    MultiDimensional out;
+    for (size_t i = 0; i < 5; ++i) {
+        for (size_t j = 0; j < 3; ++j) {
+            out.quuxMatrix[i][j].first = in.quuxMatrix[4 - i][2 - j].last;
+            out.quuxMatrix[i][j].last = in.quuxMatrix[4 - i][2 - j].first;
+        }
+    }
+
+    _hidl_cb(out);
+
+    return Void();
+}
+
 Return<void> Bar::thisIsNew() {
     ALOGI("SERVER(Bar) thisIsNew");
 
@@ -653,6 +755,66 @@
     ALOGI("CLIENT haveAStringVec returned.");
 }
 
+TEST_F(HidlTest, FooTransposeMeTest) {
+    float in[3][5];
+    float k = 1.0f;
+    for (size_t i = 0; i < 3; ++i) {
+        for (size_t j = 0; j < 5; ++j, ++k) {
+            in[i][j] = k;
+        }
+    }
+
+    ALOGI("CLIENT call transposeMe(%s).",
+          FloatArray2DToString(&in[0][0], 3, 5).c_str());
+
+    EXPECT_OK(foo->transposeMe(
+                &in[0][0],
+                [&](const auto &out) {
+                    ALOGI("CLIENT transposeMe returned %s.",
+                          FloatArray2DToString(out, 5, 3).c_str());
+
+                    for (size_t i = 0; i < 3; ++i) {
+                        for (size_t j = 0; j < 5; ++j) {
+                            EXPECT_EQ(out[3 * j + i], in[i][j]);
+                        }
+                    }
+                }));
+}
+
+TEST_F(HidlTest, FooCallingDrWhoTest) {
+    IFoo::MultiDimensional in;
+
+    size_t k = 0;
+    for (size_t i = 0; i < 5; ++i) {
+        for (size_t j = 0; j < 3; ++j, ++k) {
+            in.quuxMatrix[i][j].first = ("First " + std::to_string(k)).c_str();
+            in.quuxMatrix[i][j].last = ("Last " + std::to_string(15-k)).c_str();
+        }
+    }
+
+    ALOGI("CLIENT call callingDrWho(%s).",
+          MultiDimensionalToString(in).c_str());
+
+    EXPECT_OK(foo->callingDrWho(
+                in,
+                [&](const auto &out) {
+                    ALOGI("CLIENT callingDrWho returned %s.",
+                          MultiDimensionalToString(out).c_str());
+
+                    for (size_t i = 0; i < 5; ++i) {
+                        for (size_t j = 0; j < 3; ++j) {
+                            EXPECT_STREQ(
+                                out.quuxMatrix[i][j].first.c_str(),
+                                in.quuxMatrix[4-i][2-j].last.c_str());
+
+                            EXPECT_STREQ(
+                                out.quuxMatrix[i][j].last.c_str(),
+                                in.quuxMatrix[4-i][2-j].first.c_str());
+                        }
+                    }
+                }));
+}
+
 TEST_F(HidlTest, BarThisIsNewTest) {
     // Now the tricky part, get access to the derived interface.
     ALOGI("CLIENT call thisIsNew.");