assert(res == 0);
}
+template <int rank>
+size_t volume(Dims<rank> d)
+{
+ size_t v = 1;
+ for (int i = 0; i < rank; ++i)
+ {
+ v *= d.sizes[i];
+ }
+ return v;
+}
+
+Dims<4> shapeToDims(const Shape &s)
+{
+ Dims<4> dims;
+ assert(s.size() == 4);
+ int stride = 1;
+ for (int i = 0; i < s.size(); ++i)
+ {
+ dims.sizes[i] = s[i];
+ dims.strides[i] = stride;
+ stride *= s[i];
+ }
+ return dims;
+}
+
+template <class T>
+static inline T deserializeT(const char *&buf)
+{
+ T v;
+ const char *end = buf + sizeof(T);
+ copy(buf, end, reinterpret_cast<char *>(&v));
+ buf = end;
+ return v;
+}
+
+static inline Shape deserializeShape(const char *&buf)
+{
+ Shape s;
+ char rank = deserializeT<char>(buf);
+ s.resize(rank);
+ for (int i = 0; i < rank; ++i)
+ {
+ s.set(i, deserializeT<int32_t>(buf));
+ }
+ return s;
+}
+
+struct Kernel
+{
+ const float *data;
+ Dims<4> dims;
+};
+
+static inline Kernel deserializeKernel(const char *&buf)
+{
+ char dType = deserializeT<char>(buf);
+ assert(dType == 1 && "Unknown data type");
+ char eSize = deserializeT<char>(buf);
+ assert(eSize == 4 && "Unsupported element size");
+ Kernel k;
+ k.dims = shapeToDims(deserializeShape(buf));
+ k.data = reinterpret_cast<const float *>(buf);
+ buf += volume(k.dims) * eSize;
+ return k;
+}
+
template <class ...Args>
void concat(Tensor &out, const char *params, Args &...args)
{