CryptoByLeo/Src/DiseñoBenchs/BE64_PUB_DEC.mq5
Nique_372 71b8ff0db9
2026-08-31 08:11:57 -05:00

232 라인
8.5 KiB
MQL5

//+------------------------------------------------------------------+
//| Base64Bench.mq5 |
//| Benchmark: decodificar Base64 con whitespace (\r\n, espacios) |
//| embebido en cualquier posicion, segun gramatica "lax" RFC 7468. |
//| |
//| Competidor A: limpieza a buffer temporal + CryptDecode nativo |
//| Competidor B: decode manual streaming, sin buffers intermedios, |
//| tabla de lookup, todo en un solo recorrido |
//+------------------------------------------------------------------+
#property script_show_inputs
input int InpIterations = 20000; // Iteraciones por competidor
input int InpPayloadBytes = 3000; // Bytes RAW (antes de b64) a generar para el test
//+------------------------------------------------------------------+
//| Tabla de lookup Base64 -> 6 bits. |
//| 0xFF marca: caracter invalido O whitespace (ambos se tratan igual:|
//| "saltar"), simplificando el chequeo a una sola comparacion. |
//+------------------------------------------------------------------+
const uchar g_b64_lut[256] =
{
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x3E, 0xFF, 0xFF, 0xFF, 0x3F,
0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3A, 0x3B, 0x3C, 0x3D, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E,
0x0F, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F, 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28,
0x29, 0x2A, 0x2B, 0x2C, 0x2D, 0x2E, 0x2F, 0x30, 0x31, 0x32, 0x33, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
};
/* Codigo de ia... lo cambie dado qeu el competidor manaul
me parecio interenate asi que lo mejoraremos
bool g_lut_ready = false;
void BuildLUT()
{
ArrayInitialize(g_b64_lut, 0xFF);
string alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
for(int i = 0; i < 64; i++)
g_b64_lut[(uchar)StringGetCharacter(alphabet, i)] = (uchar)i;
g_lut_ready = true;
}*/
//+------------------------------------------------------------------+
//| Genera un "raw" base64 con saltos de linea cada 64 chars (CRLF), |
//| simulando un PEM real, a partir de bytes binarios random. |
//+------------------------------------------------------------------+
void GenerateTestInput(uchar &rawOut[], int payloadBytes)
{
uchar bin[];
ArrayResize(bin, payloadBytes);
for(int i = 0; i < payloadBytes; i++)
bin[i] = (uchar)(MathRand() & 0xFF);
uchar b64[];
uchar key[];
ArrayResize(key, 0);
int n = CryptEncode(CRYPT_BASE64, bin, key, b64);
if(n <= 0)
{
Print("Error generando base64 de prueba: ", GetLastError());
return;
}
// Insertar \r\n cada 64 caracteres, como un PEM real
ArrayResize(rawOut, n + (n / 64 + 2) * 2);
int w = 0, pos = 0;
while(n - pos >= 64)
{
w += ArrayCopy(rawOut, b64, w, pos, 64);
rawOut[w++] = '\r';
rawOut[w++] = '\n';
pos += 64;
}
if(pos < n)
{
w += ArrayCopy(rawOut, b64, w, pos, n - pos);
rawOut[w++] = '\r';
rawOut[w++] = '\n';
}
ArrayResize(rawOut, w);
}
//+------------------------------------------------------------------+
//| COMPETIDOR A: limpieza a buffer temporal + CryptDecode nativo |
//+------------------------------------------------------------------+
int DecodeB64_Native(const uchar &raw[], uchar &out[])
{
int rawLen = ArraySize(raw);
// Buffer temporal presized al tamaño maximo posible (== rawLen, nunca mas)
uchar clean[];
ArrayResize(clean, rawLen);
int w = 0;
for(int i = 0; i < rawLen; i++)
{
uchar c = raw[i];
// Whitespace: espacio, \t, \r, \n -- se descarta
if(c < 33)
continue;
clean[w++] = c;
}
ArrayResize(clean, w);
//uchar key[];
//ArrayResize(key, 0);
// NIQUE: mejora para b quitamos array temporal
return CryptDecode(CRYPT_BASE64, clean, clean, out);
}
//+------------------------------------------------------------------+
//| COMPETIDOR B: decode manual streaming, sin buffers intermedios. |
//| Un solo recorrido de raw[], acumula 4 simbolos validos via LUT, |
//| vuelca directo a out[] presized. Sin arrays temporales de ningun |
//| tipo, sin llamadas a funciones dentro del hot loop. |
//+------------------------------------------------------------------+
int DecodeB64_Manual(const uchar &raw[], uchar &out[])
{
const int rawLen = ArraySize(raw);
ArrayResize(out, (rawLen / 4 + 1) * 3);
int w = 0; // puntero de escritura en out[]
uint acc = 0; // acumulador de bits
int bits = 0; // cuantos bits validos hay en acc
for(int i = 0; i < rawLen; i++)
{
//---
const uchar c = raw[i];
if(c < 33 || c == '=')
continue;
//---
acc = (acc << 6) | g_b64_lut[c];
bits += 6;
if(bits >= 8)
{
bits -= 8;
out[w++] = uchar(acc >> bits);
}
}
ArrayResize(out, w);
return w;
}
//+------------------------------------------------------------------+
//| Verifica que dos buffers de salida sean identicos (sanity check) |
//+------------------------------------------------------------------+
bool BuffersEqual(const uchar &a[], const uchar &b[])
{
int n = ArraySize(a);
if(n != ArraySize(b))
return false;
for(int i = 0; i < n; i++)
if(a[i] != b[i])
return false;
return true;
}
//+------------------------------------------------------------------+
//| Script principal |
//+------------------------------------------------------------------+
void OnStart()
{
// BuildLUT();
MathSrand((int)GetTickCount());
uchar raw[];
GenerateTestInput(raw, InpPayloadBytes);
Print("=== Base64 Decode Benchmark ===");
Print("Payload binario: ", InpPayloadBytes, " bytes -> raw b64 (con CRLF): ", ArraySize(raw), " bytes");
Print("Iteraciones por competidor: ", InpIterations);
// --- Sanity check: ambos deben producir el mismo resultado ---
uchar outA[], outB[];
int resA = DecodeB64_Native(raw, outA);
int resB = DecodeB64_Manual(raw, outB);
if(resA <= 0)
{
Print("ERROR: Competidor A (nativo) fallo. GetLastError=", GetLastError());
return;
}
if(!BuffersEqual(outA, outB))
{
Print("ERROR: Los resultados NO coinciden. A=", resA, " bytes, B=", resB, " bytes. Abortando benchmark.");
return;
}
Print("Sanity check OK: ambos competidores producen el mismo resultado (", resA, " bytes).");
Print("");
// --- Warm-up (evita medir efectos de JIT/cache frio) ---
uchar warm[];
for(int i = 0; i < 100; i++)
{
DecodeB64_Native(raw, warm);
DecodeB64_Manual(raw, warm);
}
// --- Benchmark Competidor A ---
uchar tmpOut[];
ulong t0 = GetMicrosecondCount();
for(int i = 0; i < InpIterations; i++)
DecodeB64_Native(raw, tmpOut);
ulong t1 = GetMicrosecondCount();
double usA = (double)(t1 - t0);
// --- Benchmark Competidor B ---
ulong t2 = GetMicrosecondCount();
for(int i = 0; i < InpIterations; i++)
DecodeB64_Manual(raw, tmpOut);
ulong t3 = GetMicrosecondCount();
double usB = (double)(t3 - t2);
Print("--- Resultados ---");
PrintFormat("A) Nativo (clean buffer + CryptDecode): %.2f us total | %.4f us/iter", usA, usA / InpIterations);
PrintFormat("B) Manual streaming (sin buffers temp): %.2f us total | %.4f us/iter", usB, usB / InpIterations);
PrintFormat("Speedup B vs A: %.2fx", usA / usB);
}
//+------------------------------------------------------------------+
//+------------------------------------------------------------------+