| 37 | } |
| 38 | |
| 39 | uint16_t TemplateStream::readMemoryBlock(char* data, int bufSize) |
| 40 | { |
| 41 | if(data == nullptr || bufSize <= 0) { |
| 42 | return 0; |
| 43 | } |
| 44 | |
| 45 | auto sendValue = [&]() { |
| 46 | assert(value.length() != 0); |
| 47 | auto len = std::min(size_t(bufSize), value.length() - valuePos); |
| 48 | memcpy(data, value.c_str() + valuePos, len); |
| 49 | sendingValue = true; |
| 50 | return len; |
| 51 | }; |
| 52 | |
| 53 | if(sendingValue) { |
| 54 | return sendValue(); |
| 55 | } |
| 56 | |
| 57 | if(valueWaitSize != 0) { |
| 58 | debug_d("[TMPL] #1"); |
| 59 | size_t res = std::min(uint16_t(bufSize), valueWaitSize); |
| 60 | return stream->readMemoryBlock(data, res); |
| 61 | } |
| 62 | |
| 63 | const size_t tagDelimiterLength = 1 + doubleBraces; |
| 64 | |
| 65 | if(size_t(bufSize) <= TEMPLATE_MAX_VAR_NAME_LEN + (2 * tagDelimiterLength)) { |
| 66 | debug_d("[TMPL] #2"); |
| 67 | return 0; |
| 68 | } |
| 69 | |
| 70 | auto findStartTag = [this](char* buf) -> char* { |
| 71 | if(doubleBraces) { |
| 72 | return strstr(buf, "{{"); |
| 73 | } |
| 74 | |
| 75 | char* p = buf; |
| 76 | while((p = strchr(p, '{')) != nullptr && (p[1] <= ' ' || p[1] == '"')) { |
| 77 | ++p; |
| 78 | } |
| 79 | return p; |
| 80 | }; |
| 81 | |
| 82 | auto start = data; |
| 83 | size_t datalen = stream->readMemoryBlock(data, bufSize - 1); |
| 84 | if(datalen != 0) { |
| 85 | data[datalen] = '\0'; // Terminate buffer to mitigate overflow risk |
| 86 | auto tagStart = findStartTag(data); |
| 87 | while(tagStart != nullptr) { |
| 88 | char* curPos = tagStart + tagDelimiterLength; |
| 89 | value = evaluate(curPos); |
| 90 | size_t tailpos = curPos - data; |
| 91 | if(tailpos >= datalen) { |
| 92 | debug_d("[TMPL #3]"); |
| 93 | // Incomplete variable name, end tag not found in buffer |
| 94 | unsigned newlen = tagStart - data; |
| 95 | if(newlen + TEMPLATE_MAX_VAR_NAME_LEN > datalen) { |
| 96 | // Return what we have so far, unless we're at the end of the input stream |