Article-24231-ONNX-Models-P.../OMV Protobuf.mqh

376 lines
No EOL
12 KiB
MQL5

//+------------------------------------------------------------------+
//| OMV Protobuf.mqh |
//| Copyright 2026, Allan Munene Mutiiria. |
//| https://t.me/Forex_Algo_Trader |
//+------------------------------------------------------------------+
#property copyright "Copyright 2026, Allan Munene Mutiiria."
#property link "https://t.me/Forex_Algo_Trader"
#ifndef OMV_PROTOBUF_MQH
#define OMV_PROTOBUF_MQH
//+------------------------------------------------------------------+
//| Wire type constants |
//+------------------------------------------------------------------+
#define PB_VARINT 0 // Variable length integer
#define PB_FIXED64 1 // Eight byte value
#define PB_LENGTH 2 // Length prefixed bytes
#define PB_START 3 // Deprecated group start
#define PB_END 4 // Deprecated group end
#define PB_FIXED32 5 // Four byte value
//+------------------------------------------------------------------+
//| Four byte float overlay |
//+------------------------------------------------------------------+
union PbFloatBits
{
// Hold the raw stored bits
uint bits;
// Read the same bits as a number
float value;
};
//+------------------------------------------------------------------+
//| Eight byte double overlay |
//+------------------------------------------------------------------+
union PbDoubleBits
{
// Hold the raw stored bits
ulong bits;
// Read the same bits as a number
double value;
};
//+------------------------------------------------------------------+
//| Protobuf byte cursor |
//+------------------------------------------------------------------+
class CPbReader
{
private:
// Store the bytes being read
uchar m_buf[];
// Track the cursor position
int m_pos;
// Mark one past the last readable byte
int m_end;
// Flag a read that ran past the end
bool m_failed;
public:
CPbReader(void);
bool Attach(const uchar &src[], const int from, const int to);
bool LoadFile(const string path);
bool Eof(void) const { return m_pos >= m_end || m_failed; }
bool Failed(void) const { return m_failed; }
int Pos(void) const { return m_pos; }
int End(void) const { return m_end; }
int Size(void) const { return ArraySize(m_buf); }
void Bytes(uchar &out[]) const { ArrayCopy(out, m_buf); }
ulong ReadVarint(void);
bool ReadTag(int &field, int &wire);
bool ReadString(string &out);
bool ReadBlock(int &from, int &to);
uint ReadFixed32(void);
ulong ReadFixed64(void);
float ReadFloat(void);
double ReadDouble(void);
bool SkipField(const int wire);
};
//+------------------------------------------------------------------+
//| Construct an empty reader |
//+------------------------------------------------------------------+
CPbReader::CPbReader(void)
{
//--- Set the cursor to an empty range
m_pos = 0;
m_end = 0;
//--- Clear the failure flag
m_failed = false;
}
//+------------------------------------------------------------------+
//| Attach the cursor to a slice of an existing buffer |
//+------------------------------------------------------------------+
bool CPbReader::Attach(const uchar &src[], const int from, const int to)
{
//--- Get the source length
const int n = ArraySize(src);
//--- Reject a range the source cannot satisfy
if(from < 0 || to > n || from > to)
{
m_failed = true;
return false;
}
//--- Size the buffer to the requested slice
ArrayResize(m_buf, to - from);
//--- Copy every byte of the slice
for(int i = from; i < to; i++)
m_buf[i - from] = src[i];
//--- Reset the cursor over the copied slice
m_pos = 0;
m_end = to - from;
m_failed = false;
//--- Report success
return true;
}
//+------------------------------------------------------------------+
//| Read a whole file from the terminal Files folder |
//+------------------------------------------------------------------+
bool CPbReader::LoadFile(const string path)
{
//--- Open the file in binary mode
const int handle = FileOpen(path, FILE_READ | FILE_BIN);
//--- Abort when the file cannot be opened
if(handle == INVALID_HANDLE)
{
m_failed = true;
return false;
}
//--- Measure the file
const int size = (int)FileSize(handle);
//--- Abort on an empty file
if(size <= 0)
{
FileClose(handle);
m_failed = true;
return false;
}
//--- Size the buffer to the whole file
ArrayResize(m_buf, size);
//--- Read every byte in one call
const int got = (int)FileReadArray(handle, m_buf, 0, size);
//--- Close before parsing anything
FileClose(handle);
//--- Abort on a short read
if(got != size)
{
m_failed = true;
return false;
}
//--- Reset the cursor over the loaded bytes
m_pos = 0;
m_end = size;
m_failed = false;
//--- Report success
return true;
}
//+------------------------------------------------------------------+
//| Read a base 128 variable length integer |
//+------------------------------------------------------------------+
ulong CPbReader::ReadVarint(void)
{
//--- Start the accumulator and the bit offset
ulong value = 0;
int shift = 0;
//--- Consume bytes until the continuation bit clears
while(m_pos < m_end)
{
//--- Take the next byte
const uchar b = m_buf[m_pos++];
//--- Add its seven payload bits at the current offset
value |= ((ulong)(b & 0x7F)) << shift;
//--- Return once the high bit signals the last byte
if((b & 0x80) == 0)
return value;
//--- Advance to the next seven bit group
shift += 7;
//--- Fail beyond the width of a sixty four bit value
if(shift > 63)
{
m_failed = true;
return 0;
}
}
//--- Fail when the buffer ends mid number
m_failed = true;
return 0;
}
//+------------------------------------------------------------------+
//| Read a field tag into its number and wire type |
//+------------------------------------------------------------------+
bool CPbReader::ReadTag(int &field, int &wire)
{
//--- Clear both outputs
field = 0;
wire = 0;
//--- Report the end of the buffer without failing
if(m_pos >= m_end)
return false;
//--- Read the packed tag
const ulong tag = ReadVarint();
//--- Abort on a broken read
if(m_failed)
return false;
//--- Split the low three bits as the wire type
wire = (int)(tag & 0x07);
//--- Take the remaining bits as the field number
field = (int)(tag >> 3);
//--- Report a usable field number
return field > 0;
}
//+------------------------------------------------------------------+
//| Read a length prefixed field as text |
//+------------------------------------------------------------------+
bool CPbReader::ReadString(string &out)
{
//--- Clear the output
out = "";
//--- Read the byte count
const int len = (int)ReadVarint();
//--- Abort when the count is broken or runs past the end
if(m_failed || len < 0 || m_pos + len > m_end)
{
m_failed = true;
return false;
}
//--- Accept an empty string
if(len == 0)
return true;
//--- Copy the payload into a temporary array
uchar tmp[];
ArrayResize(tmp, len);
for(int i = 0; i < len; i++)
tmp[i] = m_buf[m_pos + i];
//--- Step the cursor past the payload
m_pos += len;
//--- Decode the payload as UTF-8
out = CharArrayToString(tmp, 0, len, CP_UTF8);
//--- Report success
return true;
}
//+------------------------------------------------------------------+
//| Locate a nested message and step over it |
//+------------------------------------------------------------------+
bool CPbReader::ReadBlock(int &from, int &to)
{
//--- Clear both outputs
from = 0;
to = 0;
//--- Read the byte count
const int len = (int)ReadVarint();
//--- Abort when the count is broken or runs past the end
if(m_failed || len < 0 || m_pos + len > m_end)
{
m_failed = true;
return false;
}
//--- Report where the payload starts and ends
from = m_pos;
to = m_pos + len;
//--- Step the cursor past the payload
m_pos = to;
//--- Report success
return true;
}
//+------------------------------------------------------------------+
//| Read four bytes least significant first |
//+------------------------------------------------------------------+
uint CPbReader::ReadFixed32(void)
{
//--- Fail when four bytes are not available
if(m_pos + 4 > m_end)
{
m_failed = true;
return 0;
}
//--- Assemble the value one byte at a time
uint value = 0;
for(int i = 0; i < 4; i++)
value |= ((uint)m_buf[m_pos + i]) << (8 * i);
//--- Step the cursor past the value
m_pos += 4;
//--- Return the assembled value
return value;
}
//+------------------------------------------------------------------+
//| Read eight bytes least significant first |
//+------------------------------------------------------------------+
ulong CPbReader::ReadFixed64(void)
{
//--- Fail when eight bytes are not available
if(m_pos + 8 > m_end)
{
m_failed = true;
return 0;
}
//--- Assemble the value one byte at a time
ulong value = 0;
for(int i = 0; i < 8; i++)
value |= ((ulong)m_buf[m_pos + i]) << (8 * i);
//--- Step the cursor past the value
m_pos += 8;
//--- Return the assembled value
return value;
}
//+------------------------------------------------------------------+
//| Read four bytes as a float |
//+------------------------------------------------------------------+
float CPbReader::ReadFloat(void)
{
//--- Load the raw bits into the overlay
PbFloatBits cast;
cast.bits = ReadFixed32();
//--- Return zero on a broken read
if(m_failed)
return 0.0f;
//--- Return the same bits read as a number
return cast.value;
}
//+------------------------------------------------------------------+
//| Read eight bytes as a double |
//+------------------------------------------------------------------+
double CPbReader::ReadDouble(void)
{
//--- Load the raw bits into the overlay
PbDoubleBits cast;
cast.bits = ReadFixed64();
//--- Return zero on a broken read
if(m_failed)
return 0.0;
//--- Return the same bits read as a number
return cast.value;
}
//+------------------------------------------------------------------+
//| Step over an unwanted field |
//+------------------------------------------------------------------+
bool CPbReader::SkipField(const int wire)
{
//--- Consume the field according to its wire type
switch(wire)
{
case PB_VARINT:
ReadVarint();
return !m_failed;
case PB_FIXED64:
ReadFixed64();
return !m_failed;
case PB_FIXED32:
ReadFixed32();
return !m_failed;
case PB_LENGTH:
{
//--- Read the length and jump past the payload
int from, to;
return ReadBlock(from, to);
}
}
//--- Fail on a deprecated group marker
m_failed = true;
return false;
}
#endif // OMV_PROTOBUF_MQH
//+------------------------------------------------------------------+