MQL5Book/Include/ws/wstools.mqh
super.admin 1c8e83ce31 convert
2025-05-30 16:09:41 +02:00

204 lines
6.2 KiB
MQL5

//+------------------------------------------------------------------+
//| wstools.mqh |
//| Copyright 2020-2022, MetaQuotes Ltd. |
//| https://www.mql5.com |
//+------------------------------------------------------------------+
#include "..\URL.mqh"
//+------------------------------------------------------------------+
//| Various helper functions |
//+------------------------------------------------------------------+
namespace WsTools
{
// Parse HTTP headers (multiline) to an array of key[][0]/value[][1]
int parseHeaders(const string header, string &retVal[][2])
{
string fields[];
const int n = StringSplit(header, '\n', fields);
ArrayResize(retVal, 0);
for(int i = 0; i < n; i++)
{
StringReplace(fields[i], "\t", " ");
string match[];
if(StringSplit(URL::trim(fields[i]), ':', match) >= 2)
{
const int m = ArrayRange(retVal, 0);
ArrayResize(retVal, m + 1);
StringToLower(match[0]); // make key case-insensitive
retVal[m][0] = match[0]; // NB: if the same header key occured many times, it'll be added many times
StringTrimLeft(match[1]); // skip leading blank after ':'
retVal[m][1] = match[1];
for(int j = 2; j < ArraySize(match); j++)
{
retVal[m][1] += ":" + match[j];
}
}
}
if(StringFind(header, "GET ") == 0)
{
const int p = StringFind(header, " HTTP/");
if(p > 0)
{
const int m = ArrayRange(retVal, 0);
ArrayResize(retVal, m + 1);
retVal[m][0] = "GET";
retVal[m][1] = StringSubstr(header, 4, p - 4);
}
}
return ArrayRange(retVal, 0);
}
string arrayToHex(const uchar &array[], const int max = -1)
{
string res = "";
const int count = max == -1 ? ArraySize(array) : fmin(max, ArraySize(array));
for(int i = 0; i < count; i++)
{
res += StringFormat("%.02X ", array[i]);
}
if(count < ArraySize(array)) res += "...";
return res;
}
int charCount(const string data, const uchar c)
{
int count = 0;
for(int i = 0; i < StringLen(data); i++)
{
if(data[i] == c) count++;
}
return count;
}
union BYTES4
{
uchar chars[4];
uint num;
BYTES4(): num(0) { }
BYTES4(uint n): num(n) { }
BYTES4(const string &data) { WsTools::StringToByteArray(data, chars, 0, 4); }
BYTES4(const uchar &data[], const int offset = 0) { ArrayCopy(chars, data, 0, offset, 4); }
uchar operator[](int i) { return chars[i]; }
};
void pack4(uint number, uchar &data[], const int offset = 0)
{
BYTES4 b;
b.num = MathSwap(number);
ArrayCopy(data, b.chars, offset);
}
uint unpack4(const string data, const int offset = 0)
{
BYTES4 b;
b.chars[0] = (uchar)data[0 + offset];
b.chars[1] = (uchar)data[1 + offset];
b.chars[2] = (uchar)data[2 + offset];
b.chars[3] = (uchar)data[3 + offset];
return MathSwap(b.num); // TODO: consider redo without swap
}
uint unpack4(const uchar &data[], const int offset = 0)
{
BYTES4 b;
b.chars[0] = (uchar)data[0 + offset];
b.chars[1] = (uchar)data[1 + offset];
b.chars[2] = (uchar)data[2 + offset];
b.chars[3] = (uchar)data[3 + offset];
return MathSwap(b.num); // TODO: consider redo without swap
}
union BYTES2
{
uchar chars[2];
ushort num;
BYTES2(): num(0) { }
BYTES2(ushort n): num(n) { }
uchar operator[](int i) { return chars[i]; }
};
void pack2(ushort number, uchar &data[], const int offset = 0)
{
BYTES2 b;
b.num = MathSwap(number);
ArrayCopy(data, b.chars, offset);
}
ushort unpack2(const string data, const int offset = 0)
{
BYTES2 b;
b.chars[0] = (uchar)data[0 + offset];
b.chars[1] = (uchar)data[1 + offset];
return MathSwap(b.num);
}
ushort unpack2(const uchar &data[], const int offset = 0)
{
BYTES2 b;
b.chars[0] = (uchar)data[0 + offset];
b.chars[1] = (uchar)data[1 + offset];
return MathSwap(b.num);
}
template<typename T>
void push(T *&array[], T *ptr)
{
const int n = ArraySize(array);
ArrayResize(array, n + 1);
array[n] = ptr;
}
template<typename T>
void push(T &array[][2], T key, T value)
{
const int n = ArrayRange(array, 0);
ArrayResize(array, n + 1);
array[n][0] = key;
array[n][1] = value;
}
// NB: StringToCharArray copies to specific part (start/count) of the receiving array (which is confusing)
// StringToByteArray is intended to copy specific part of the given string
int StringToByteArray(const string text, uchar &array[], const int start = 0, const int count = -1, const uint cp = CP_ACP)
{
if(cp == CP_ACP)
{
const int n = count == -1 ? StringLen(text) - start : count;
ArrayResize(array, n);
for(int i = 0; i < n; i++)
{
array[i] = (uchar)StringGetCharacter(text, i + start);
}
return n;
}
else
{
int n = StringToCharArray((start || count != -1) ?
StringSubstr(text, start, count) : text, array, 0, -1, cp);
if(n > 0 && array[n - 1] == 0)
{
ArrayResize(array, --n);
}
return n;
}
return 0;
}
string stringify(const uchar &data[], const int limit = -1)
{
string result = "";
const int count = limit == -1 ? ArraySize(data) : MathMin(limit, ArraySize(data));
StringReserve(result, count);
for(int i = 0; i < count; i++)
{
result += StringFormat("%C", data[i]);
}
return result;
}
};
//+------------------------------------------------------------------+