forked from airat77786/MQL5Book
204 lines
6.2 KiB
MQL5
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;
| |||
}
| |||
};
| |||
//+------------------------------------------------------------------+
|