Skip to content

Commit

Permalink
Import Update 2
Browse files Browse the repository at this point in the history
implemented and documented the starting import functions in final state
added more tests
added wiki
added more to README.md
optimized save format even more
  • Loading branch information
gk646 committed May 9, 2024
2 parents 9b42587 + d651d93 commit f3a0a31
Showing 1 changed file with 147 additions and 44 deletions.
191 changes: 147 additions & 44 deletions include/RnImport.h
Original file line number Diff line number Diff line change
Expand Up @@ -40,19 +40,20 @@
// ComponentData = COMPONENTS_PER_NODE * ComponentData(4) + NameHash(4)

namespace raynodes {
class RnImport;
struct RnImport;

// Returns a built import of a ".rn" file as generated from raynodes
// "path" can be relative (to the current CWD) or absolute
RnImport ParseRN(const char* path);
RnImport importRN(const char* path);

#define COMPS_PER_NODE 6
#define DATAPOINTS_PER_COMPONENT 4
#define COMPS_PER_NODE 6 // Max components per node
#define USED_LINE_SEPARATOR '|' // Set this to the linesep used by cxio (default '|')
#define RN_MAX_NAME_LEN 16 // Max length of any component or node identifiers

using ByteIndex = uint32_t;
using NodeID = uint16_t;
using ComponentID = uint8_t;
using TemplateID = uint8_t; // Limits to 256 different nodes

enum class DataType : uint8_t {
BOOLEAN,
Expand All @@ -66,17 +67,17 @@ enum class DataType : uint8_t {

struct ConnectionData {
NodeID fromNode;
uint8_t fromComponent;
int8_t fromComponent; // -1 if its a node-to-node connection
uint8_t fromPin;
NodeID toNode;
uint8_t toComponent;
int8_t toComponent; // -1 if its a node-to-node connection
uint8_t toPin;
};

class ComponentData {
// This uses a single 32bit number to store both the startByte and the ComponentLabel
// Current split 24/8
uint32_t dataHolder;
uint32_t dataHolder = 0;
[[nodiscard]] ComponentID getID() const {
// Extract the lower 8 bits
return static_cast<ComponentID>(dataHolder & 0xFF);
Expand All @@ -87,10 +88,11 @@ class ComponentData {
}

public:
ComponentData(ByteIndex startByte, ComponentID id) {
// Internal function - dont call it
static void AssignData(ComponentData& comp, ByteIndex startByte, ComponentID id) {
// Mask and shift `startByte` into the upper 24 bits, and `id` into the lower 8 bits
dataHolder = (static_cast<uint32_t>(startByte) & 0xFFFFFF) << 8;
dataHolder |= static_cast<uint32_t>(id) & 0xFF;
comp.dataHolder = (static_cast<uint32_t>(startByte) & 0xFFFFFF) << 8;
comp.dataHolder |= static_cast<uint32_t>(id) & 0xFF;
}

// Returns the data of the index-th save call made inside the component - 0 based
Expand All @@ -102,39 +104,40 @@ class ComponentData {

struct NodeData {
NodeID nodeID = 0;
uint32_t typeHash = 0;
std::array<ComponentData, COMPS_PER_NODE> compData;
TemplateID templateID = 0;
};

class RnImport final {
std::vector<NodeData> nodes; // Internal data holder
std::vector<ConnectionData> connections; // Internal data holder
struct NodeTemplate {
uint16_t startByte = 0;
};

[[nodiscard]] const NodeData& getNodeData(NodeID nodeID) const {
for (const auto& n : nodes) {
if (n.nodeID == nodeID) return n;
}
std::abort(); // Node not found!
}
void buildInternals();
struct RnImport final {
uint16_t templateCnt = 0; // Count of templates
NodeTemplate* templates = nullptr; // Node templates
uint16_t nodeCnt = 0; // Count of nodes
NodeData* nodes = nullptr; // Internal data holder
uint16_t connCnt = 0; // Count of connections
ConnectionData* connections = nullptr; // Internal data holder

public:
RnImport(char* fileData, ByteIndex size) : size(size), fileData(fileData) {
const int nodeSizeEstimate = static_cast<int>(static_cast<float>(size) * 0.75F);
nodes.reserve(nodeSizeEstimate / 100);
connections.reserve((size - nodeSizeEstimate) / 50);
buildInternals();
}
char* fileData = nullptr; // Allocated string containing the whole file data
uint32_t size = 0; // Size of fileData

RnImport(char* fileData, ByteIndex size);
RnImport(const RnImport& other) = delete;
RnImport(RnImport&& other) noexcept = delete;
RnImport& operator=(const RnImport& other) = delete;
RnImport& operator=(RnImport&& other) noexcept = delete;
~RnImport() { free(fileData); }
~RnImport() {
free(fileData);
free(templates);
free(nodes);
free(connections);
}

// Returns the data of the index-th save call made inside the specified component - 0 based
// io_save(file, first), io_save(file, second), io_save(file, third);
// Most components will probably only have have 1 - its up to the user to know the correct datatype
// IMPORTANT: strings will be returned as allocated "char*"
// IMPORTANT: strings will be returned as allocated "char*" (strdup)
template <DataType dt>
[[nodiscard]] auto getComponentData(NodeID nodeID, int component, int index = 0) const;

Expand All @@ -148,20 +151,33 @@ class RnImport final {
template <int size>
[[nodiscard]] std::array<NodeID, size> getConnectedNodes(NodeID nodeID, int componentIndex) const;


//TODO build save indicies to read - components names and datatypes

uint32_t size = 0; // Size of fileData
char* fileData = nullptr; // Allocated string containing the whole file data
private:
[[nodiscard]] const NodeData& getNodeData(NodeID nodeID) const {
for (int i = 0; i < nodeCnt; ++i) {
if (nodes[i].nodeID == nodeID) return nodes[i];
}
std::abort(); // Node not found!
}
};
} // namespace raynodes

//--------------IMPLEMENTATION---------------//

// Alot of these helpers are copied from cxutil/cxstring.h
namespace {
void SkipSeparator(char*& ptr, int count) noexcept {
void str_read_into_until(const char* data, char* buffer, size_t buffer_size, char stop) {
size_t count = 0;
while (count < buffer_size - 1 && data[count] != stop && data[count] != '\0') {
buffer[count] = data[count];
count++;
}
buffer[count] = '\0';
}

void SkipCharacter(char*& ptr, const char c, int count) noexcept {
while (*ptr != '\0' && count > 0) {
if (*ptr == USED_LINE_SEPARATOR) {
if (*ptr == c) {
--count;
}
++ptr;
Expand All @@ -183,9 +199,35 @@ char* str_dup(const char* arg) {
}
return newBuff;
}
int str_parse_int(const char* str, const int radix = 10) {
if (str == nullptr || *str == '\0') return 0;

int result = 0;
bool negative = false;
if (*str == '-') {
negative = true;
++str;
}

while (*str) {
char digit = *str;
int value;
if (digit >= '0' && digit <= '9') value = digit - '0';
else if (digit >= 'a' && digit <= 'z') value = 10 + digit - 'a';
else if (digit >= 'A' && digit <= 'Z') value = 10 + digit - 'A';
else break;

if (value >= radix) break;

result = result * radix + value;
++str;
}

return negative ? -result : result;
}
} // namespace

inline raynodes::RnImport raynodes::ParseRN(const char* path) {
inline raynodes::RnImport raynodes::importRN(const char* path) {
FILE* file = fopen(path, "rb"); // Open in binary mode to avoid text translation
if (!file) {
perror("Failed to open file securely");
Expand Down Expand Up @@ -234,19 +276,80 @@ inline raynodes::RnImport raynodes::ParseRN(const char* path) {
return {buffer, static_cast<ByteIndex>(size)};
}

inline void raynodes::RnImport::buildInternals() {}
inline raynodes::RnImport::RnImport(char* fileData, ByteIndex size) : fileData(fileData), size(size) {
auto* indexPtr = fileData;
// Allocating space
{
// This is a fixed format header e.g.:
//--EditorData--
//2|1|-145.238|-170.635|0.960|1|
//--Templates--
//10|

SkipCharacter(indexPtr, '\n', 1);
nodeCnt = str_parse_int(indexPtr);
SkipCharacter(indexPtr, USED_LINE_SEPARATOR, 1);
connCnt = str_parse_int(indexPtr);
SkipCharacter(indexPtr, '\n', 2);
templateCnt = str_parse_int(indexPtr);

nodes = static_cast<NodeData*>(malloc(nodeCnt * sizeof(NodeData)));
connections = static_cast<ConnectionData*>(malloc(connCnt * sizeof(ConnectionData)));
templates = static_cast<NodeTemplate*>(malloc(templateCnt * sizeof(NodeTemplate)));
}
// Parsing the templates
{
SkipCharacter(indexPtr, '\n', 1); // Skip the template count
for (int i = 0; i < templateCnt; ++i) {
int index = str_parse_int(indexPtr);
SkipCharacter(indexPtr, USED_LINE_SEPARATOR, 1);
templates[index] = {static_cast<uint16_t>(indexPtr - fileData)};
SkipCharacter(indexPtr, '\n', 1);
}
}
// Parsing the nodes
{
SkipCharacter(indexPtr, '\n', 1); // Skip nodes section
for (int i = 0; i < nodeCnt; ++i) {
const int templateNum = str_parse_int(indexPtr);
SkipCharacter(indexPtr, USED_LINE_SEPARATOR, 1);
const int nodeID = str_parse_int(indexPtr);
nodes[i] = {static_cast<NodeID>(nodeID), static_cast<TemplateID>(templateNum)};
SkipCharacter(indexPtr, '\n', 1);
}
}
// Parsing connections
{
SkipCharacter(indexPtr, '\n', 1); // Skip connection section
for (int i = 0; i < connCnt; ++i) {
const int fromNode = str_parse_int(indexPtr);
SkipCharacter(indexPtr, USED_LINE_SEPARATOR, 1);
const int from = str_parse_int(indexPtr);
SkipCharacter(indexPtr, USED_LINE_SEPARATOR, 1);
const int out = str_parse_int(indexPtr);
SkipCharacter(indexPtr, USED_LINE_SEPARATOR, 1);
const int toNode = str_parse_int(indexPtr);
SkipCharacter(indexPtr, USED_LINE_SEPARATOR, 1);
const int to = str_parse_int(indexPtr);
SkipCharacter(indexPtr, USED_LINE_SEPARATOR, 1);
const int in = str_parse_int(indexPtr);
connections[i] = {(NodeID)fromNode, (int8_t)from, (uint8_t)out,
(NodeID)toNode, (int8_t)to, (uint8_t)in};
}
}
}

inline raynodes::NodeID raynodes::RnImport::getFirstConnectedNode(NodeID nodeID, int componentIndex) const {
for (const auto& conn : connections) {
if (conn.fromNode == nodeID && conn.fromComponent == componentIndex) return conn.toNode;
}
// for (const auto& conn : connections) {
// if (conn.fromNode == nodeID && conn.fromComponent == componentIndex) return conn.toNode;
// }
return UINT16_MAX;
}

template <raynodes::DataType dt>
auto raynodes::ComponentData::getData(char* fileData, int index) const {
uint32_t startByte = getStartByte();
SkipSeparator(fileData, index);
SkipCharacter(fileData, USED_LINE_SEPARATOR, index);
if constexpr (dt == DataType::BOOLEAN) {
return std::strtol(fileData + startByte, nullptr, 10) == 1;
} else if (dt == DataType::STRING) {
Expand All @@ -269,7 +372,7 @@ auto raynodes::ComponentData::getData(char* fileData, int index) const {

template <raynodes::DataType dt>
auto raynodes::RnImport::getComponentData(NodeID nodeID, int component, int index) const {
return getNodeData(nodeID).compData[component].getData<dt>(fileData, index);
// return getNodeData(nodeID).compData[component].getData<dt>(fileData, index);
}

template <int size>
Expand Down

0 comments on commit f3a0a31

Please sign in to comment.