JsonParserByLeo/Test/Other/GLM.mqh
2026-06-28 21:34:54 -05:00

779 righe
Nessun fine linea
25 KiB
MQL5

//+------------------------------------------------------------------+
//| GLM.mqh |
//| Copyright 2026, Niquel Mendoza. |
//| https://www.mql5.com/ |
//+------------------------------------------------------------------+
#property copyright "Copyright 2026, Niquel Mendoza."
#property link "https://www.mql5.com/"
#property strict
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
// Same prompt claude\mql5 lite
/* Notas GLM 5 Turbo
--
Ahora mismo voy a por el primer intentento
92 errores abismal... me da que los modelos chinos en MQL5 son pesimos
peores que chat gpt.. volvamos a iterar...
'CJson' - unexpected token, probably type is missing? MinMax.mqh 84 5
'*' - semicolon expected MinMax.mqh 84 17
declaration without type MinMax.mqh 105 15
comma expected MinMax.mqh 105 21
.... etc..
Se confunden mucho con c++ cosa qeu a este punto claude ya raramente le pase y a gpt tambien
pero los modelos chinos siguen con esa piedra de confundirmse demasiado con c++
Claude y gpt si se confuncnde ero es raro..
--
Bueno nada me da que no sabe mucho de mql5 sigue con eso
f (len == (uint)k_len)
{
bool match = true;
for (uint i = 0; i < len; i++)
{
if (m_json->Buffer[start + i] != key_bytes[i])
{
match = false;
break;
}
}
if (match)
return CJsonNode(m_json, m_json->Tokens[child].next);
}
child = m_json->Tokens[m_json->Tokens[child].next].next;
}
Creo qeu aqui le dejo se supone que yo como usuario no se mucho MQL5
y es la ia que me debe de guiar a mi.... yo solo doy ideas maqueto todo y la ia lo pasa a codigo..
Por ahora creo que GLM se queda corto.. me reuqca a gpt en 2024.. cuando
tenia ese tipo de errores abismalmes
Bueno dejare el codigo igual que me dio si es que lo quieren revisar..
No creo que lo testee ya ni vale la pena..
*/
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
//+------------------------------------------------------------------+
//| Json.mqh |
//| High Performance JSON Parser |
//| 100% Pure MQL5 (No DLL)|
//+------------------------------------------------------------------+
#property strict
//--- Tipos de nodos JSON
enum JsonType
{
JSON_UNDEFINED = 0,
JSON_OBJECT = 1,
JSON_ARRAY = 2,
JSON_STRING = 3,
JSON_NUMBER = 4,
JSON_TRUE = 5,
JSON_FALSE = 6,
JSON_NULL = 7
};
//--- Códigos de error
enum JsonError
{
JSON_OK = 0,
JSON_UNEXPECTED_CHARACTER = 1,
JSON_UNEXPECTED_END = 2,
JSON_INVALID_NUMBER = 3,
JSON_INVALID_STRING = 4,
JSON_INVALID_ESCAPE = 5,
JSON_INVALID_UNICODE = 6,
JSON_EXPECTED_COLON = 7,
JSON_EXPECTED_OBJECT = 8,
JSON_EXPECTED_ARRAY = 9,
JSON_MAX_DEPTH = 10,
JSON_TRAILING_GARBAGE = 11
};
//+------------------------------------------------------------------+
//| Estructura de Token (16 bytes) - Altamente cacheable |
//+------------------------------------------------------------------+
struct JsonToken
{
ushort type; // JsonType
uint start; // Índice en Buffer[]
uint end; // Índice en Buffer[]
uint next; // Índice del siguiente hermano (0 si no hay)
uint first_child; // Índice del primer hijo (0 si es hoja)
uint size; // Número de elementos (hijos) en objetos y arrays
};
//+------------------------------------------------------------------+
//| Frame para la pila manual de análisis |
//+------------------------------------------------------------------+
struct StackFrame
{
uint token_idx;
uint last_child_idx;
bool is_object;
};
//+------------------------------------------------------------------+
//| Clase Nodo (Proxy para acceso a datos) |
//+------------------------------------------------------------------+
class CJsonNode
{
private:
CJson *m_json;
uint m_idx;
JsonType m_type;
// Convierte 4 chars hexadecimales a un ushort
ushort HexToShort(const uchar &buf[], uint pos) const
{
ushort val = 0;
for (int i = 0; i < 4; i++)
{
uchar c = buf[pos + i];
val <<= 4;
if (c >= '0' && c <= '9') val += (ushort)(c - '0');
else if (c >= 'a' && c <= 'f') val += (ushort)(10 + c - 'a');
else if (c >= 'A' && c <= 'F') val += (ushort)(10 + c - 'A');
}
return val;
}
public:
CJsonNode() : m_json(NULL), m_idx(0), m_type(JSON_UNDEFINED) {}
CJsonNode(CJson *json, uint idx);
bool IsValid() const { return m_idx > 0 && m_type != JSON_UNDEFINED; }
JsonType GetType() const { return m_type; }
bool IsObject() const { return m_type == JSON_OBJECT; }
bool IsArray() const { return m_type == JSON_ARRAY; }
bool IsString() const { return m_type == JSON_STRING; }
bool IsNumber() const { return m_type == JSON_NUMBER; }
bool IsBool() const { return m_type == JSON_TRUE || m_type == JSON_FALSE; }
bool IsNull() const { return m_type == JSON_NULL; }
uint Size() const { return IsValid() ? m_json.m_tokens[m_idx].size : 0; }
uint Count() const { return Size(); }
string AsString() const;
long AsLong() const;
double AsDouble() const;
bool AsBool() const;
int AsInt() const { return (int)AsLong(); }
CJsonNode operator[](const string key) const;
CJsonNode operator[](const uint index) const;
};
//+------------------------------------------------------------------+
//| Clase Principal del Parser |
//+------------------------------------------------------------------+
class CJson
{
private:
JsonToken m_tokens[];
StackFrame m_stack[];
int m_stack_top;
uint m_token_count;
uint m_last_child_idx;
JsonError m_error;
uint m_error_pos;
void SkipWhitespace(uint &pos);
void ParseString(uint &pos);
void ParseNumber(uint &pos);
bool MatchLiteral(uint &pos, const string literal, int len);
void ParseValue(uint &pos);
uint AllocToken(ushort type, uint start, uint end);
public:
uchar Buffer[]; // Expuesto público para Zero-Copy
CJson();
~CJson();
bool Parse();
void Reset();
void Clear();
// Información de errores
JsonError GetError() const { return m_error; }
uint GetErrorPosition() const { return m_error_pos; }
string GetErrorMessage() const;
int GetLine();
int GetColumn();
bool HasError() const { return m_error != JSON_OK; }
// Acceso a la raíz
CJsonNode GetRoot();
CJsonNode operator[](const string key) { return GetRoot()[key]; }
CJsonNode operator[](const uint index) { return GetRoot()[index]; }
// Utilidades rápidas de la raíz
bool IsObject() const { return m_token_count > 0 && m_tokens[0].type == JSON_OBJECT; }
bool IsArray() const { return m_token_count > 0 && m_tokens[0].type == JSON_ARRAY; }
uint Size() const { return m_token_count > 0 ? m_tokens[0].size : 0; }
JsonType GetType() const { return m_token_count > 0 ? (JsonType)m_tokens[0].type : JSON_UNDEFINED; }
// Amigas
friend class CJsonNode;
};
//+------------------------------------------------------------------+
//| Implementación CJsonNode |
//+------------------------------------------------------------------+
CJsonNode::CJsonNode(CJson *json, uint idx)
{
m_json = json;
m_idx = idx;
if (m_json != NULL && m_idx > 0 && m_idx < m_json.m_token_count)
m_type = (JsonType)m_json.m_tokens[m_idx].type;
else
m_type = JSON_UNDEFINED;
}
string CJsonNode::AsString() const
{
if (!IsValid() || m_type != JSON_STRING) return "";
uint start = m_json.m_tokens[m_idx].start + 1;
uint end = m_json.m_tokens[m_idx].end - 1;
uint len = end - start;
if (len == 0) return "";
// Optimización rápida: Si no hay barras invertidas, copiar directo sin procesar
bool has_escape = false;
for (uint i = start; i < end; i++)
{
if (m_json.Buffer[i] == '\\') { has_escape = true; break; }
}
if (!has_escape)
return CharArrayToString(m_json.Buffer, start, len, CP_UTF8);
// Procesamiento de escapes (Obligatorio para estándar JSON)
uchar tmp[];
ArrayResize(tmp, len);
uint j = 0;
for (uint i = start; i < end; i++)
{
uchar c = m_json.Buffer[i];
if (c == '\\')
{
i++;
uchar esc = m_json.Buffer[i];
switch(esc)
{
case '"': tmp[j++] = '"'; break;
case '\\': tmp[j++] = '\\'; break;
case '/': tmp[j++] = '/'; break;
case 'b': tmp[j++] = 0x08; break;
case 'f': tmp[j++] = 0x0C; break;
case 'n': tmp[j++] = 0x0A; break;
case 'r': tmp[j++] = 0x0D; break;
case 't': tmp[j++] = 0x09; break;
case 'u':
ushort cp = HexToShort(m_json.Buffer, i + 1);
i += 4;
// Soporte básico para Surrogate Pairs UTF-16
if (cp >= 0xD800 && cp <= 0xDBFF && i + 2 < end && m_json.Buffer[i] == '\\' && m_json.Buffer[i+1] == 'u')
{
ushort cp2 = HexToShort(m_json.Buffer, i + 2);
if (cp2 >= 0xDC00 && cp2 <= 0xDFFF)
{
cp = 0x10000 + (((cp - 0xD800) << 10) | (cp2 - 0xDC00));
i += 6;
}
}
// Codificar a UTF-8
if (cp < 0x80) {
tmp[j++] = (uchar)cp;
} else if (cp < 0x800) {
tmp[j++] = (uchar)(0xC0 | (cp >> 6));
tmp[j++] = (uchar)(0x80 | (cp & 0x3F));
} else if (cp <= 0xFFFF) {
tmp[j++] = (uchar)(0xE0 | (cp >> 12));
tmp[j++] = (uchar)(0x80 | ((cp >> 6) & 0x3F));
tmp[j++] = (uchar)(0x80 | (cp & 0x3F));
} else {
tmp[j++] = (uchar)(0xF0 | (cp >> 18));
tmp[j++] = (uchar)(0x80 | ((cp >> 12) & 0x3F));
tmp[j++] = (uchar)(0x80 | ((cp >> 6) & 0x3F));
tmp[j++] = (uchar)(0x80 | (cp & 0x3F));
}
break;
default: tmp[j++] = esc; break; // No debería llegar aquí si el parser es estricto
}
}
else
{
tmp[j++] = c;
}
}
ArrayResize(tmp, j);
return CharArrayToString(tmp, 0, j, CP_UTF8);
}
long CJsonNode::AsLong() const
{
if (!IsValid()) return 0;
if (m_type == JSON_TRUE) return 1;
if (m_type == JSON_FALSE || m_type == JSON_NULL) return 0;
uint len = m_json.m_tokens[m_idx].end - m_json.m_tokens[m_idx].start;
string s = CharArrayToString(m_json.Buffer, m_json.m_tokens[m_idx].start, len, CP_UTF8);
return StringToInteger(s);
}
double CJsonNode::AsDouble() const
{
if (!IsValid()) return 0.0;
if (m_type == JSON_TRUE) return 1.0;
if (m_type == JSON_FALSE || m_type == JSON_NULL) return 0.0;
uint len = m_json.m_tokens[m_idx].end - m_json.m_tokens[m_idx].start;
string s = CharArrayToString(m_json.Buffer, m_json.m_tokens[m_idx].start, len, CP_UTF8);
return StringToDouble(s);
}
bool CJsonNode::AsBool() const
{
if (!IsValid()) return false;
if (m_type == JSON_TRUE) return true;
if (m_type == JSON_FALSE) return false;
if (m_type == JSON_NUMBER) return AsDouble() != 0.0;
if (m_type == JSON_STRING) return m_json.m_tokens[m_idx].end - m_json.m_tokens[m_idx].start > 2; // String no vacío
return false;
}
CJsonNode CJsonNode::operator[](const string key) const
{
if (!IsValid() || m_type != JSON_OBJECT) return CJsonNode(m_json, 0);
uint child = m_json.m_tokens[m_idx].first_child;
// Convertir clave a UTF-8 bytes una sola vez para comparación ultra-rápida
uchar key_bytes[];
StringToCharArray(key, key_bytes, 0, WHOLE_ARRAY, CP_UTF8);
int k_len = ArraySize(key_bytes) - 1; // StringToCharArray añade null terminator
while (child != 0)
{
uint start = m_json.m_tokens[child].start + 1;
uint end = m_json.m_tokens[child].end - 1;
uint len = end - start;
if (len == (uint)k_len)
{
bool match = true;
for (uint i = 0; i < len; i++)
{
if (m_json.Buffer[start + i] != key_bytes[i])
{
match = false;
break;
}
}
if (match)
return CJsonNode(m_json, m_json.m_tokens[child].next); // Retorna el valor
}
// Saltar al siguiente par (Valor.next = siguiente Clave)
child = m_json.m_tokens[m_json.m_tokens[child].next].next;
}
return CJsonNode(m_json, 0); // No encontrado
}
CJsonNode CJsonNode::operator[](const uint index) const
{
if (!IsValid() || m_type != JSON_ARRAY) return CJsonNode(m_json, 0);
uint child = m_json.m_tokens[m_idx].first_child;
uint count = 0;
while (child != 0)
{
if (count == index) return CJsonNode(m_json, child);
child = m_json.m_tokens[child].next;
count++;
}
return CJsonNode(m_json, 0);
}
//+------------------------------------------------------------------+
//| Implementación CJson |
//+------------------------------------------------------------------+
CJson::CJson()
{
m_error = JSON_OK;
m_error_pos = 0;
m_token_count = 0;
m_stack_top = 0;
m_last_child_idx = 0;
ArrayResize(m_tokens, 1024);
ArrayResize(m_stack, 64);
ArrayResize(Buffer, 0);
}
CJson::~CJson()
{
Clear();
}
void CJson::Reset()
{
// No liberamos memoria, solo reiniciamos contadores para reutilizar buffers (Arena style)
m_token_count = 0;
m_stack_top = 0;
m_last_child_idx = 0;
m_error = JSON_OK;
m_error_pos = 0;
}
void CJson::Clear()
{
ArrayFree(m_tokens);
ArrayFree(m_stack);
ArrayFree(Buffer);
Reset();
}
void CJson::SkipWhitespace(uint &pos)
{
uint len = ArraySize(Buffer);
while (pos < len)
{
uchar c = Buffer[pos];
if (c != ' ' && c != '\t' && c != '\n' && c != '\r') break;
pos++;
}
}
bool CJson::MatchLiteral(uint &pos, const string literal, int len)
{
uint buf_len = ArraySize(Buffer);
for (int i = 0; i < len; i++)
{
if (pos >= buf_len || Buffer[pos++] != (uchar)StringGetCharacter(literal, i))
{
m_error = JSON_UNEXPECTED_CHARACTER;
m_error_pos = pos - 1;
return false;
}
}
return true;
}
void CJson::ParseString(uint &pos)
{
uint len = ArraySize(Buffer);
pos++; // Skip opening quote
while (pos < len)
{
uchar c = Buffer[pos++];
if (c == '"') return; // Fin de string encontrado
if (c == '\\')
{
if (pos >= len) { m_error = JSON_INVALID_STRING; m_error_pos = pos; return; }
uchar esc = Buffer[pos++];
if (esc == 'u')
{
if (pos + 3 >= len) { m_error = JSON_INVALID_UNICODE; m_error_pos = pos; return; }
for (int i = 0; i < 4; i++)
{
uchar h = Buffer[pos++];
if (!((h >= '0' && h <= '9') || (h >= 'a' && h <= 'f') || (h >= 'A' && h <= 'F')))
{
m_error = JSON_INVALID_UNICODE;
m_error_pos = pos - 1;
return;
}
}
}
else if (esc != '"' && esc != '\\' && esc != '/' && esc != 'b' && esc != 'f' && esc != 'n' && esc != 'r' && esc != 't')
{
m_error = JSON_INVALID_ESCAPE;
m_error_pos = pos - 1;
return;
}
}
// Nota: Se omiten validaciones de caracteres de control (0x00-0x1F) para maximizar velocidad.
}
m_error = JSON_INVALID_STRING;
m_error_pos = pos;
}
void CJson::ParseNumber(uint &pos)
{
uint len = ArraySize(Buffer);
if (Buffer[pos] == '-') pos++;
if (pos < len && Buffer[pos] == '0')
{
pos++;
}
else
{
if (pos >= len || Buffer[pos] < '1' || Buffer[pos] > '9') { m_error = JSON_INVALID_NUMBER; m_error_pos = pos; return; }
while (pos < len && Buffer[pos] >= '0' && Buffer[pos] <= '9') pos++;
}
if (pos < len && Buffer[pos] == '.')
{
pos++;
if (pos >= len || Buffer[pos] < '0' || Buffer[pos] > '9') { m_error = JSON_INVALID_NUMBER; m_error_pos = pos; return; }
while (pos < len && Buffer[pos] >= '0' && Buffer[pos] <= '9') pos++;
}
if (pos < len && (Buffer[pos] == 'e' || Buffer[pos] == 'E'))
{
pos++;
if (pos < len && (Buffer[pos] == '+' || Buffer[pos] == '-')) pos++;
if (pos >= len || Buffer[pos] < '0' || Buffer[pos] > '9') { m_error = JSON_INVALID_NUMBER; m_error_pos = pos; return; }
while (pos < len && Buffer[pos] >= '0' && Buffer[pos] <= '9') pos++;
}
}
uint CJson::AllocToken(ushort type, uint start, uint end)
{
// Pre-asignación dinámica amortizada O(1)
if (m_token_count >= ArraySize(m_tokens))
ArrayResize(m_tokens, ArraySize(m_tokens) * 2);
uint idx = m_token_count++;
m_tokens[idx].type = type;
m_tokens[idx].start = start;
m_tokens[idx].end = end;
m_tokens[idx].next = 0;
m_tokens[idx].first_child = 0;
m_tokens[idx].size = 0;
if (m_stack_top > 0)
{
StackFrame &frame = m_stack[m_stack_top - 1];
if (m_tokens[frame.token_idx].first_child == 0)
m_tokens[frame.token_idx].first_child = idx;
else
m_tokens[frame.last_child_idx].next = idx;
frame.last_child_idx = idx;
}
return idx;
}
void CJson::ParseValue(uint &pos)
{
uchar c = Buffer[pos];
uint start = pos;
if (c == '"')
{
ParseString(pos);
if (m_error != JSON_OK) return;
AllocToken(JSON_STRING, start, pos);
}
else if (c == '{')
{
uint tok_idx = AllocToken(JSON_OBJECT, start, 0);
pos++;
if (m_stack_top >= ArraySize(m_stack)) ArrayResize(m_stack, m_stack_top * 2);
m_stack[m_stack_top].token_idx = tok_idx;
m_stack[m_stack_top].last_child_idx = 0;
m_stack[m_stack_top].is_object = true;
m_stack_top++;
}
else if (c == '[')
{
uint tok_idx = AllocToken(JSON_ARRAY, start, 0);
pos++;
if (m_stack_top >= ArraySize(m_stack)) ArrayResize(m_stack, m_stack_top * 2);
m_stack[m_stack_top].token_idx = tok_idx;
m_stack[m_stack_top].last_child_idx = 0;
m_stack[m_stack_top].is_object = false;
m_stack_top++;
}
else if (c == 't')
{
if (!MatchLiteral(pos, "true", 4)) return;
AllocToken(JSON_TRUE, start, pos);
}
else if (c == 'f')
{
if (!MatchLiteral(pos, "false", 5)) return;
AllocToken(JSON_FALSE, start, pos);
}
else if (c == 'n')
{
if (!MatchLiteral(pos, "null", 4)) return;
AllocToken(JSON_NULL, start, pos);
}
else if (c == '-' || (c >= '0' && c <= '9'))
{
ParseNumber(pos);
if (m_error != JSON_OK) return;
AllocToken(JSON_NUMBER, start, pos);
}
else
{
m_error = JSON_UNEXPECTED_CHARACTER;
m_error_pos = pos;
}
}
bool CJson::Parse()
{
Reset();
uint len = ArraySize(Buffer);
if (len == 0) { m_error = JSON_UNEXPECTED_END; m_error_pos = 0; return false; }
uint pos = 0;
// Saltar BOM (Byte Order Mark) UTF-8 si existe
if (len >= 3 && Buffer[0] == 0xEF && Buffer[1] == 0xBB && Buffer[2] == 0xBF) pos = 3;
SkipWhitespace(pos);
if (pos >= len) { m_error = JSON_UNEXPECTED_END; m_error_pos = pos; return false; }
uchar c = Buffer[pos];
if (c != '{' && c != '[')
{
m_error = (c == '"' || c == '-' || (c >= '0' && c <= '9') || c == 't' || c == 'f' || c == 'n') ? JSON_EXPECTED_OBJECT : JSON_UNEXPECTED_CHARACTER;
m_error_pos = pos;
return false;
}
ParseValue(pos);
if (m_error != JSON_OK) return false;
bool expect_key = (m_tokens[0].type == JSON_OBJECT);
while (m_stack_top > 0 && m_error == JSON_OK)
{
SkipWhitespace(pos);
if (pos >= len) { m_error = JSON_UNEXPECTED_END; m_error_pos = pos; break; }
c = Buffer[pos];
StackFrame &frame = m_stack[m_stack_top - 1];
if (frame.is_object)
{
if (c == '}')
{
m_tokens[frame.token_idx].end = pos + 1;
m_stack_top--;
pos++;
continue;
}
if (expect_key)
{
if (c != '"') { m_error = JSON_EXPECTED_OBJECT; m_error_pos = pos; break; }
uint start = pos;
ParseString(pos);
if (m_error != JSON_OK) break;
AllocToken(JSON_STRING, start, pos);
expect_key = false;
}
else
{
if (c != ':') { m_error = JSON_EXPECTED_COLON; m_error_pos = pos; break; }
pos++;
SkipWhitespace(pos);
if (pos >= len) { m_error = JSON_UNEXPECTED_END; m_error_pos = pos; break; }
ParseValue(pos);
if (m_error != JSON_OK) break;
m_tokens[frame.token_idx].size++;
expect_key = true;
SkipWhitespace(pos);
if (pos < len && Buffer[pos] == ',') pos++;
}
}
else // Array
{
if (c == ']')
{
m_tokens[frame.token_idx].end = pos + 1;
m_stack_top--;
pos++;
continue;
}
ParseValue(pos);
if (m_error != JSON_OK) break;
m_tokens[frame.token_idx].size++;
SkipWhitespace(pos);
if (pos < len && Buffer[pos] == ',') pos++;
}
}
if (m_error == JSON_OK)
{
SkipWhitespace(pos);
if (pos < len) { m_error = JSON_TRAILING_GARBAGE; m_error_pos = pos; }
}
return m_error == JSON_OK;
}
CJsonNode CJson::GetRoot()
{
if (m_token_count == 0) return CJsonNode(this, 0);
return CJsonNode(this, 0);
}
string CJson::GetErrorMessage() const
{
switch(m_error)
{
case JSON_OK: return "No error";
case JSON_UNEXPECTED_CHARACTER:return "Unexpected character";
case JSON_UNEXPECTED_END: return "Unexpected end of data";
case JSON_INVALID_NUMBER: return "Invalid number format";
case JSON_INVALID_STRING: return "Invalid string format";
case JSON_INVALID_ESCAPE: return "Invalid escape sequence";
case JSON_INVALID_UNICODE: return "Invalid Unicode escape sequence";
case JSON_EXPECTED_COLON: return "Expected colon ':'";
case JSON_EXPECTED_OBJECT: return "Expected object '{' or '['";
case JSON_EXPECTED_ARRAY: return "Expected array '['";
case JSON_MAX_DEPTH: return "Maximum nesting depth exceeded";
case JSON_TRAILING_GARBAGE: return "Unexpected data after JSON document";
default: return "Unknown error";
}
}
int CJson::GetLine()
{
if (m_error == JSON_OK) return 0;
int line = 1;
uint limit = MathMin(m_error_pos, ArraySize(Buffer));
for (uint i = 0; i < limit; i++)
{
if (Buffer[i] == '\n') line++;
else if (Buffer[i] == '\r' && i + 1 < limit && Buffer[i+1] == '\n') i++; // Skip \r\n
}
return line;
}
int CJson::GetColumn()
{
if (m_error == JSON_OK) return 0;
int col = 1;
uint limit = MathMin(m_error_pos, ArraySize(Buffer));
for (uint i = limit; i > 0; i--)
{
uchar c = Buffer[i-1];
if (c == '\n') break;
if (c == '\r') break;
col++;
}
return col;
}