From def449607c73a85b6847144cc0a9551de8144cd0 Mon Sep 17 00:00:00 2001
From: Gary Talent <gtalent2@gmail.com>
Date: Sun, 24 Feb 2019 23:57:31 -0600
Subject: [PATCH] [ox/mc] Add Walker

---
 deps/ox/src/ox/mc/CMakeLists.txt      |   2 +
 deps/ox/src/ox/mc/deftypes.hpp        |  67 ++++++++++-----
 deps/ox/src/ox/mc/defwriter.cpp       |   5 +-
 deps/ox/src/ox/mc/defwriter.hpp       |   2 +-
 deps/ox/src/ox/mc/mc.hpp              |   1 +
 deps/ox/src/ox/mc/optype.hpp          |  26 +++++-
 deps/ox/src/ox/mc/presencemask.cpp    |  14 +---
 deps/ox/src/ox/mc/presencemask.hpp    |  30 ++++++-
 deps/ox/src/ox/mc/read.cpp            |   3 +-
 deps/ox/src/ox/mc/read.hpp            |  11 ++-
 deps/ox/src/ox/mc/test/CMakeLists.txt |   2 -
 deps/ox/src/ox/mc/walker.hpp          | 116 ++++++++++++++++++++++++++
 deps/ox/src/ox/mc/write.cpp           |   1 +
 deps/ox/src/ox/mc/write.hpp           |   6 +-
 14 files changed, 234 insertions(+), 52 deletions(-)
 create mode 100644 deps/ox/src/ox/mc/walker.hpp

diff --git a/deps/ox/src/ox/mc/CMakeLists.txt b/deps/ox/src/ox/mc/CMakeLists.txt
index 7aff415d..2824b974 100644
--- a/deps/ox/src/ox/mc/CMakeLists.txt
+++ b/deps/ox/src/ox/mc/CMakeLists.txt
@@ -3,6 +3,7 @@ add_library(
 		defwriter.cpp
 		presencemask.cpp
 		read.cpp
+		walker.cpp
 		write.cpp
 )
 
@@ -27,6 +28,7 @@ install(
 		presencemask.hpp
 		read.hpp
 		types.hpp
+		walker.cpp
 		write.hpp
 	DESTINATION
 		include/ox/mc
diff --git a/deps/ox/src/ox/mc/deftypes.hpp b/deps/ox/src/ox/mc/deftypes.hpp
index 40e50431..be7ecb17 100644
--- a/deps/ox/src/ox/mc/deftypes.hpp
+++ b/deps/ox/src/ox/mc/deftypes.hpp
@@ -22,26 +22,29 @@ enum class PrimitiveType: uint8_t {
 	Bool = 2,
 	Float = 3,
 	String = 4,
-	List = 5,
-	Struct = 6,
+	Struct = 5,
 };
 
+using FieldName = String;
+
 struct Field {
 	// order of fields matters
 
 	// only serialize type name if type has already been serialized
 	const struct Type *type = nullptr;
-	String fieldName;
+	FieldName fieldName;
 	int subscriptLevels = 0;
 
 	// do not serialize the following
+	String typeName; // gives reference to type for lookup if type is null
 	bool serializeType = false;
 };
 
 using FieldList = Vector<Field>;
+using TypeName = String;
 
 struct Type {
-	String typeName;
+	TypeName typeName;
 	PrimitiveType primitiveType;
 	// fieldList only applies to structs
 	Vector<Field> fieldList;
@@ -59,23 +62,6 @@ struct Type {
 };
 
 
-template<typename T>
-int ioOp(T *io, Field *field) {
-	int32_t err = 0;
-	io->setTypeInfo("ox::mc::Field", 5);
-	if (field->serializeType) {
-		err |= io->op("typeName", "");
-		err |= io->op("type", &field->type);
-	} else {
-		err |= io->op("typeName", &field->type->typeName);
-		err |= io->op("type", static_cast<decltype(&field->type)>(nullptr));
-	}
-	err |= io->op("fieldName", &field->fieldName);
-	// defaultValue is unused now, but placeholder for backwards compatibility
-	err |= io->op("defaultValue", nullptr);
-	return err;
-}
-
 template<typename T>
 int ioOp(T *io, Type *type) {
 	int32_t err = 0;
@@ -87,6 +73,45 @@ int ioOp(T *io, Type *type) {
 	return err;
 }
 
+template<typename T>
+int ioOpWrite(T *io, Field *field) {
+	int32_t err = 0;
+	io->setTypeInfo("ox::mc::Field", 4);
+	if (field->serializeType) {
+		err |= io->op("typeName", "");
+		err |= io->op("type", field->type);
+	} else {
+		err |= io->op("typeName", &field->type->typeName);
+		err |= io->op("type", static_cast<decltype(field->type)>(nullptr));
+	}
+	err |= io->op("fieldName", &field->fieldName);
+	// defaultValue is unused now, but placeholder for backwards compatibility
+	err |= io->op("defaultValue", nullptr);
+	return err;
+}
+
+template<typename T>
+int ioOpRead(T *io, Field *field) {
+	int32_t err = 0;
+	io->setTypeInfo("ox::mc::Field", 4);
+	err |= io->op("typeName", &field->typeName);
+	if (field->typeName == "") {
+		field->serializeType = true;
+		if (field->type == nullptr) {
+			field->type = new Type;
+		}
+		err |= io->op("type", field->type);
+	} else {
+		// should be empty, so discard
+		Type t;
+		err |= io->op("type", &t);
+	}
+	err |= io->op("fieldName", &field->fieldName);
+	// defaultValue is unused now, but placeholder for backwards compatibility
+	err |= io->op("defaultValue", nullptr);
+	return err;
+}
+
 using TypeStore = ox::HashMap<mc::String, mc::Type>;
 
 }
diff --git a/deps/ox/src/ox/mc/defwriter.cpp b/deps/ox/src/ox/mc/defwriter.cpp
index ae3ba90b..a9698292 100644
--- a/deps/ox/src/ox/mc/defwriter.cpp
+++ b/deps/ox/src/ox/mc/defwriter.cpp
@@ -21,9 +21,7 @@ MetalClawDefWriter::MetalClawDefWriter(mc::TypeStore *typeStore) {
 }
 
 MetalClawDefWriter::~MetalClawDefWriter() {
-	if (m_typeStoreOwnerRef) {
-		delete m_typeStoreOwnerRef;
-	}
+	delete m_typeStoreOwnerRef;
 }
 
 constexpr mc::Type *MetalClawDefWriter::type(int8_t*, bool *alreadyExisted) {
@@ -96,6 +94,7 @@ constexpr mc::Type *MetalClawDefWriter::type(bool*, bool *alreadyExisted) {
 }
 
 constexpr void MetalClawDefWriter::setTypeInfo(const char *name, int) {
+	m_typeAlreayExisted = m_typeStore->contains(name);
 	m_type = &m_typeStore->at(name);
 	m_type->typeName = name;
 	m_type->primitiveType = mc::PrimitiveType::Struct;
diff --git a/deps/ox/src/ox/mc/defwriter.hpp b/deps/ox/src/ox/mc/defwriter.hpp
index e0835462..45682a1b 100644
--- a/deps/ox/src/ox/mc/defwriter.hpp
+++ b/deps/ox/src/ox/mc/defwriter.hpp
@@ -80,7 +80,7 @@ class MetalClawDefWriter {
 
 		constexpr void setTypeInfo(const char *name, int fields);
 
-      constexpr OpType opType() {
+      static constexpr OpType opType() {
 			return OpType::WriteDefinition;
       }
 
diff --git a/deps/ox/src/ox/mc/mc.hpp b/deps/ox/src/ox/mc/mc.hpp
index 7d1c88ab..7b24c350 100644
--- a/deps/ox/src/ox/mc/mc.hpp
+++ b/deps/ox/src/ox/mc/mc.hpp
@@ -12,4 +12,5 @@
 #include "defwriter.hpp"
 #include "read.hpp"
 #include "types.hpp"
+#include "walker.hpp"
 #include "write.hpp"
diff --git a/deps/ox/src/ox/mc/optype.hpp b/deps/ox/src/ox/mc/optype.hpp
index f6cac4c5..19ec49df 100644
--- a/deps/ox/src/ox/mc/optype.hpp
+++ b/deps/ox/src/ox/mc/optype.hpp
@@ -8,7 +8,7 @@
 
 #pragma once
 
-#include "ox/std/error.hpp"
+#include <ox/std/error.hpp>
 
 namespace ox {
 
@@ -18,13 +18,33 @@ enum class OpType {
 	WriteDefinition = 3,
 };
 
+// empty default implementations of ioOp functions
+
+template<typename T, typename O>
+ox::Error ioOpRead(T*, O*) {
+	return OxError(1);
+}
+
+template<typename T, typename O>
+ox::Error ioOpWrite(T*, O*) {
+	return OxError(1);
+}
+
+template<typename T, typename O>
+ox::Error ioOpWriteDefinition(T*, O*) {
+	return OxError(1);
+}
+
 template<typename T, typename O>
 ox::Error ioOp(T *io, O *obj) {
-	if (io->opType() == ox::OpType::Read) {
+	if constexpr (T::opType() == ox::OpType::Read) {
 		return ioOpRead(io, obj);
-	} else {
+	} else if constexpr (T::opType() == ox::OpType::Write) {
 		return ioOpWrite(io, obj);
+	} else if constexpr (T::opType() == ox::OpType::WriteDefinition) {
+		return ioOpWriteDefinition(io, obj);
 	}
+	return OxError(1);
 }
 
 }
diff --git a/deps/ox/src/ox/mc/presencemask.cpp b/deps/ox/src/ox/mc/presencemask.cpp
index 006a21b2..106b7db5 100644
--- a/deps/ox/src/ox/mc/presencemask.cpp
+++ b/deps/ox/src/ox/mc/presencemask.cpp
@@ -14,11 +14,11 @@ namespace ox {
 
 FieldPresenseMask::FieldPresenseMask(uint8_t *mask, std::size_t maxLen) {
 	m_mask = mask;
-	m_maxLen = maxLen;
+	m_maskLen = maxLen;
 }
 
 bool FieldPresenseMask::get(int i) {
-	if (i / 8 < m_maxLen) {
+	if (i / 8 < m_maskLen) {
 		return (m_mask[i / 8] >> (i % 8)) & 1;
 	} else {
 		return MC_PRESENCEMASKOUTBOUNDS;
@@ -26,7 +26,7 @@ bool FieldPresenseMask::get(int i) {
 }
 
 int FieldPresenseMask::set(int i, bool on) {
-	if (i / 8 < m_maxLen) {
+	if (i / 8 < m_maskLen) {
 		if (on) {
 			m_mask[i / 8] |= 1 << (i % 8);
 		} else {
@@ -38,12 +38,4 @@ int FieldPresenseMask::set(int i, bool on) {
 	}
 }
 
-void FieldPresenseMask::setMaxLen(int maxLen) {
-	m_maxLen = maxLen;
-}
-
-int FieldPresenseMask::getMaxLen() {
-	return m_maxLen;
-}
-
 }
diff --git a/deps/ox/src/ox/mc/presencemask.hpp b/deps/ox/src/ox/mc/presencemask.hpp
index 3da1627e..9d411a2e 100644
--- a/deps/ox/src/ox/mc/presencemask.hpp
+++ b/deps/ox/src/ox/mc/presencemask.hpp
@@ -14,8 +14,9 @@ namespace ox {
 
 class FieldPresenseMask {
 	private:
-		uint8_t *m_mask;
-		int m_maxLen = 0;
+		uint8_t *m_mask = nullptr;
+		int m_maskLen = 0;
+		int m_fields = 0;
 
 	public:
 		FieldPresenseMask(uint8_t *mask, std::size_t maxLen);
@@ -24,9 +25,30 @@ class FieldPresenseMask {
 
 		int set(int i, bool on);
 
-		void setMaxLen(int);
+		constexpr void setFields(int) noexcept;
+
+		constexpr int getFields() noexcept;
+
+		constexpr void setMaxLen(int) noexcept;
+
+		constexpr int getMaxLen() noexcept;
 
-		int getMaxLen();
 };
 
+constexpr void FieldPresenseMask::setFields(int fields) noexcept {
+	m_fields = fields;
+}
+
+constexpr int FieldPresenseMask::getFields() noexcept {
+	return m_fields;
+}
+
+constexpr void FieldPresenseMask::setMaxLen(int maxLen) noexcept {
+	m_maskLen = maxLen;
+}
+
+constexpr int FieldPresenseMask::getMaxLen() noexcept {
+	return m_maskLen;
+}
+
 }
diff --git a/deps/ox/src/ox/mc/read.cpp b/deps/ox/src/ox/mc/read.cpp
index 3a3dcc31..b401cc49 100644
--- a/deps/ox/src/ox/mc/read.cpp
+++ b/deps/ox/src/ox/mc/read.cpp
@@ -91,7 +91,7 @@ Error MetalClawReader::op(const char*, McStr val) {
 	return err;
 }
 
-std::size_t MetalClawReader::arrayLength(const char*) {
+[[nodiscard]] std::size_t MetalClawReader::arrayLength() {
 	std::size_t len = 0;
 	if (m_fieldPresence.get(m_field)) {
 		// read the length
@@ -116,6 +116,7 @@ std::size_t MetalClawReader::stringLength(const char*) {
 void MetalClawReader::setTypeInfo(const char*, int fields) {
 	m_fields = fields;
 	m_buffIt = (fields / 8 + 1) - (fields % 8 == 0);
+	m_fieldPresence.setFields(fields);
 	m_fieldPresence.setMaxLen(m_buffIt);
 }
 
diff --git a/deps/ox/src/ox/mc/read.hpp b/deps/ox/src/ox/mc/read.hpp
index 0b12134c..41dc2d73 100644
--- a/deps/ox/src/ox/mc/read.hpp
+++ b/deps/ox/src/ox/mc/read.hpp
@@ -46,6 +46,11 @@ class MetalClawReader {
 
 		int op(const char*, bool *val);
 
+		/**
+		 * Reads an array length from the current location in the buffer.
+		 */
+		[[nodiscard]] std::size_t arrayLength();
+
 		template<typename T>
 		int op(const char*, T *val, std::size_t len);
 
@@ -87,9 +92,9 @@ class MetalClawReader {
 		 */
 		bool fields();
 
-      static constexpr OpType opType() {
-          return OpType::Read;
-      }
+		static constexpr OpType opType() {
+			return OpType::Read;
+		}
 
 	private:
 		template<typename I>
diff --git a/deps/ox/src/ox/mc/test/CMakeLists.txt b/deps/ox/src/ox/mc/test/CMakeLists.txt
index ca5a06fd..a4b1e270 100644
--- a/deps/ox/src/ox/mc/test/CMakeLists.txt
+++ b/deps/ox/src/ox/mc/test/CMakeLists.txt
@@ -1,5 +1,3 @@
-cmake_minimum_required(VERSION 2.8)
-
 add_executable(
 	McTest
 		tests.cpp
diff --git a/deps/ox/src/ox/mc/walker.hpp b/deps/ox/src/ox/mc/walker.hpp
new file mode 100644
index 00000000..1ff945f8
--- /dev/null
+++ b/deps/ox/src/ox/mc/walker.hpp
@@ -0,0 +1,116 @@
+/*
+ * Copyright 2015 - 2018 gtalent2@gmail.com
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+#pragma once
+
+#include <ox/std/error.hpp>
+
+#include "deftypes.hpp"
+#include "read.hpp"
+
+namespace ox {
+
+template<typename T>
+class MetalClawWalker {
+	template<typename FH>
+	friend ox::Error ioOp(class MetalClawReader*, MetalClawWalker<FH>*);
+
+	private:
+		mc::Type *m_type = nullptr;
+		T *m_fieldHandler = nullptr;
+		Vector<mc::FieldName> m_path;
+		Vector<mc::TypeName> m_typePath;
+
+	public:
+		MetalClawWalker(T *fieldHandler);
+
+		[[nodiscard]] const mc::Type *type() const noexcept;
+
+		void read(const mc::Field&, MetalClawReader *rdr);
+
+	protected:
+		void pushNamePath(mc::FieldName fn);
+
+		void popNamePath();
+
+};
+
+template<typename T>
+MetalClawWalker<T>::MetalClawWalker(T *fieldHandler) {
+	m_fieldHandler = fieldHandler;
+}
+
+template<typename T>
+const mc::Type *MetalClawWalker<T>::type() const noexcept {
+	return m_type;
+}
+
+template<typename T>
+void MetalClawWalker<T>::read(const mc::Field &f, MetalClawReader *rdr) {
+	const auto &pathCr = m_path;
+	m_fieldHandler->read(pathCr, f, rdr);
+}
+
+template<typename FH>
+static ox::Error parseField(mc::Field field, MetalClawReader *rdr, MetalClawWalker<FH> *walker) {
+	ox::Error err = 0;
+	walker->pushNamePath(field.fieldName);
+	if (field.subscriptLevels) {
+		// add array handling
+		auto child = rdr->child();
+		const auto arrayLen = rdr->arrayLength();
+		auto f = field;
+		--f.subscriptLevels;
+		rdr->setTypeInfo(field.type->typeName.c_str(), arrayLen);
+		BString<100> subscript;
+		for (ArrayLength i = 0; i < arrayLen; i++) {
+			subscript = "[";
+			subscript += i;
+			subscript += "]";
+			walker->pushNamePath(subscript);
+			err |= parseField(f, &child, walker);
+			walker->pophNamePath();
+		}
+	} else {
+		switch (field.type->primitiveType) {
+			case mc::PrimitiveType::UnsignedInteger:
+			case mc::PrimitiveType::SignedInteger:
+			case mc::PrimitiveType::Bool:
+			case mc::PrimitiveType::Float:
+			case mc::PrimitiveType::String:
+				err |= walker->read(field, rdr);
+				break;
+			case mc::PrimitiveType::Struct:
+				if (rdr->fieldPresent()) {
+					auto child = rdr->child();
+					rdr->setTypeInfo(field.type->typeName.c_str(), field.type->fieldList.size());
+					err |= ioOp(&child, walker);
+				} else {
+					// skip and discard absent field
+					int discard;
+					err |= rdr->op("", &discard);
+				}
+				break;
+		}
+	}
+	walker->popNamePath();
+	return err;
+}
+
+template<typename FH>
+ox::Error ioOp(MetalClawReader *rdr, MetalClawWalker<FH> *walker) {
+	Error err = 0;
+	auto &fields = walker->type()->fieldList;
+	for (std::size_t i = 0; i < fields.size(); i++) {
+		auto &field = fields[i];
+		parseField(field, rdr, walker);
+	}
+	return OxError(err);
+}
+
+}
diff --git a/deps/ox/src/ox/mc/write.cpp b/deps/ox/src/ox/mc/write.cpp
index 8ae92f8f..6a3cbcdd 100644
--- a/deps/ox/src/ox/mc/write.cpp
+++ b/deps/ox/src/ox/mc/write.cpp
@@ -85,6 +85,7 @@ Error MetalClawWriter::op(const char*, McStr val) {
 void MetalClawWriter::setTypeInfo(const char*, int fields) {
 	m_fields = fields;
 	m_buffIt = (fields / 8 + 1) - (fields % 8 == 0);
+	m_fieldPresence.setFields(fields);
 	m_fieldPresence.setMaxLen(m_buffIt);
 }
 
diff --git a/deps/ox/src/ox/mc/write.hpp b/deps/ox/src/ox/mc/write.hpp
index 84a4e47c..f1a059e3 100644
--- a/deps/ox/src/ox/mc/write.hpp
+++ b/deps/ox/src/ox/mc/write.hpp
@@ -65,9 +65,9 @@ class MetalClawWriter {
 
 		std::size_t size();
 
-      static constexpr OpType opType() {
-          return OpType::Write;
-      }
+		static constexpr OpType opType() {
+			return OpType::Write;
+		}
 
 	private:
 		template<typename I>