Adding deserialization of index buffer.
diff --git a/include/rive/core/binary_reader.hpp b/include/rive/core/binary_reader.hpp
index 69e37c1..4894bad 100644
--- a/include/rive/core/binary_reader.hpp
+++ b/include/rive/core/binary_reader.hpp
@@ -9,15 +9,16 @@
 namespace rive {
     class BinaryReader {
     private:
-        uint8_t* m_Position;
-        uint8_t* m_End;
+        const uint8_t* m_Position;
+        const uint8_t* m_End;
         bool m_Overflowed;
         size_t m_Length;
 
         void overflow();
 
     public:
-        BinaryReader(uint8_t* bytes, size_t length);
+        BinaryReader(const uint8_t* bytes, size_t length);
+        BinaryReader(Span<const uint8_t> span);
         bool didOverflow() const;
         bool reachedEnd() const;
 
diff --git a/include/rive/shapes/mesh.hpp b/include/rive/shapes/mesh.hpp
index 7816827..1f7761d 100644
--- a/include/rive/shapes/mesh.hpp
+++ b/include/rive/shapes/mesh.hpp
@@ -2,12 +2,17 @@
 #define _RIVE_MESH_HPP_
 #include "rive/generated/shapes/mesh_base.hpp"
 #include "rive/span.hpp"
+#include "rive/refcnt.hpp"
 
 namespace rive {
     class MeshVertex;
+
     class Mesh : public MeshBase {
+
     protected:
+        class IndexBuffer : public std::vector<uint16_t>, public RefCnt {};
         std::vector<MeshVertex*> m_Vertices;
+        rcp<IndexBuffer> m_IndexBuffer;
 
     public:
         StatusCode onAddedDirty(CoreContext* context) override;
@@ -17,6 +22,7 @@
         void copyTriangleIndexBytes(const MeshBase& object) override;
 #ifdef TESTING
         std::vector<MeshVertex*>& vertices() { return m_Vertices; }
+        rcp<IndexBuffer> indices() { return m_IndexBuffer; }
 #endif
     };
 } // namespace rive
diff --git a/src/core/binary_reader.cpp b/src/core/binary_reader.cpp
index d16ee13..b115e65 100644
--- a/src/core/binary_reader.cpp
+++ b/src/core/binary_reader.cpp
@@ -5,7 +5,10 @@
 
 using namespace rive;
 
-BinaryReader::BinaryReader(uint8_t* bytes, size_t length) :
+BinaryReader::BinaryReader(Span<const uint8_t> span) :
+    BinaryReader(span.begin(), span.size()) {}
+
+BinaryReader::BinaryReader(const uint8_t* bytes, size_t length) :
     m_Position(bytes),
     m_End(bytes + length),
     m_Overflowed(false),
@@ -57,7 +60,7 @@
         return Span<const uint8_t>(m_Position, 0);
     }
 
-    uint8_t* start = m_Position;
+    const uint8_t* start = m_Position;
     m_Position += length;
     return Span<const uint8_t>(start, length);
 }
diff --git a/src/shapes/mesh.cpp b/src/shapes/mesh.cpp
index b72a515..f8b41d4 100644
--- a/src/shapes/mesh.cpp
+++ b/src/shapes/mesh.cpp
@@ -1,5 +1,7 @@
 #include "rive/shapes/mesh.hpp"
 #include "rive/shapes/image.hpp"
+#include <limits>
+#include <cassert>
 
 using namespace rive;
 
@@ -25,8 +27,17 @@
 
 void Mesh::decodeTriangleIndexBytes(Span<const uint8_t> value) {
     // decode the triangle index bytes
+    rcp<IndexBuffer> buffer = rcp<IndexBuffer>(new IndexBuffer());
+
+    BinaryReader reader(value);
+    while (!reader.reachedEnd()) {
+        uint64_t index = reader.readVarUint64();
+        assert(index < std::numeric_limits<uint16_t>::max());
+        buffer->push_back(index);
+    }
+    m_IndexBuffer = buffer;
 }
 
 void Mesh::copyTriangleIndexBytes(const MeshBase& object) {
-    // copy the triangle indices from object
+    m_IndexBuffer = object.as<Mesh>()->m_IndexBuffer;
 }
\ No newline at end of file
diff --git a/test/image_mesh_test.cpp b/test/image_mesh_test.cpp
index 3a983c7..d1575f2 100644
--- a/test/image_mesh_test.cpp
+++ b/test/image_mesh_test.cpp
@@ -12,7 +12,7 @@
 #include <catch.hpp>
 #include <cstdio>
 
-TEST_CASE("image with mesh loads correctly", "[assets]") {
+TEST_CASE("image with mesh loads correctly", "[mesh]") {
     RiveFileReader reader("../../test/assets/tape.riv");
     auto file = reader.file();
 
@@ -24,4 +24,42 @@
     REQUIRE(tape->imageAsset()->decodedByteSize == 70903);
     REQUIRE(tape->mesh() != nullptr);
     REQUIRE(tape->mesh()->vertices().size() == 24);
+    REQUIRE(tape->mesh()->indices()->size() == 31 * 3); // Expect 31 triangles.
+}
+
+TEST_CASE("duplicating a mesh shares the indices", "[mesh]") {
+    RiveFileReader reader("../../test/assets/tape.riv");
+    auto file = reader.file();
+
+    auto instance1 = file->artboard()->instance();
+    auto instance2 = file->artboard()->instance();
+    auto instance3 = file->artboard()->instance();
+
+    auto node1 = instance1->find("Tape body.png");
+    auto node2 = instance2->find("Tape body.png");
+    auto node3 = instance3->find("Tape body.png");
+    REQUIRE(node1 != nullptr);
+    REQUIRE(node2 != nullptr);
+    REQUIRE(node3 != nullptr);
+    REQUIRE(node1->is<rive::Image>());
+    REQUIRE(node2->is<rive::Image>());
+    REQUIRE(node3->is<rive::Image>());
+
+    auto tape1 = node1->as<rive::Image>();
+    auto tape2 = node2->as<rive::Image>();
+    auto tape3 = node3->as<rive::Image>();
+    REQUIRE(tape1->imageAsset() != nullptr);
+    REQUIRE(tape1->mesh() != nullptr);
+    REQUIRE(tape2->imageAsset() != nullptr);
+    REQUIRE(tape2->mesh() != nullptr);
+    REQUIRE(tape3->imageAsset() != nullptr);
+    REQUIRE(tape3->mesh() != nullptr);
+
+    REQUIRE(tape1->mesh()->indices()->size() == 31 * 3);
+    REQUIRE(tape2->mesh()->indices()->size() == 31 * 3);
+    REQUIRE(tape3->mesh()->indices()->size() == 31 * 3);
+
+    // Important part, make sure they're all actually the same reference.
+    REQUIRE(tape1->mesh()->indices() == tape2->mesh()->indices());
+    REQUIRE(tape2->mesh()->indices() == tape3->mesh()->indices());
 }