summaryrefslogtreecommitdiff
path: root/SDL_Core/tools/intergen/utils/src/utils/safeformat.cc
blob: 0db0bebe6c7273f606be2d00c79f3457e4f5f213 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
/*
* Typesafe printf-like wrapper around std streams
* by Igor Kozyrenko, 2013
*/

#include "utils/safeformat.h"

#include <sstream>
#include <algorithm>
#include <iterator>

namespace typesafe_format {
namespace impl {

const char kPlaceholderPrefix = '{';
const char kPlaceholderSuffix = '}';

struct Placeholder {
  bool   escaped;
  size_t escaped_prefixes;
  size_t number;
  size_t length;
};

void missing_parameter(std::ostream& os, size_t param_number) {
  os<<"{Parameter "<<param_number<<" is missing}";
}

void missing_parameter(std::wostream& os, size_t param_number) {
  os<<L"{Parameter "<<param_number<<L" is missing}";
}

template <typename CT>
const CT* find_placeholder_tmpl(const CT* text, const CT* end,
                                Placeholder& ph) {
  const CT* prefix_begin = text;
  bool done = false;
  do {
    prefix_begin = std::find(prefix_begin, end, kPlaceholderPrefix);
    // there must be at least two symbols after prefix
    if (std::distance(prefix_begin, end) > 2) {
      const CT* prefix_end = prefix_begin + 1;
      // count prefix escapes if any
      while(*prefix_end == kPlaceholderPrefix &&
            std::distance(prefix_end, end) > 2)
        ++prefix_end;
      size_t prefixes = std::distance(prefix_begin, prefix_end);
      size_t prefix_number = *prefix_end - '0';
      const CT* suffix = prefix_end + 1;
      if (prefix_number < 10 &&  // it is in range of valid placeholders
          *suffix == kPlaceholderSuffix) {
        ph.escaped =          prefixes % 2 == 0;
        ph.escaped_prefixes = prefixes / 2;
        ph.number = prefix_number;
        ph.length = (suffix - prefix_begin) + 1;
        done = true;
      } else {
        prefix_begin = suffix; // continue from the symbol after number
      }
    } else { // prefix_begin is not at least last but two
      prefix_begin = end;
      done = true;
    }
  } while (!done);
  return prefix_begin;
}

template <typename CT>
void format_params_tmpl(const CT* fmt, size_t fmt_length,
                        std::basic_ostream<CT>& os,
                        const Streamable<CT>* params[], size_t params_count) {
  const CT* text = fmt;
  const CT* end = fmt + fmt_length;
  Placeholder placeholder;
  for (const CT* ph_pos = find_placeholder_tmpl(text, end, placeholder);
       ph_pos != end;
       ph_pos = find_placeholder_tmpl(text, end, placeholder)) {
    os.write(text, ph_pos - text);
    for (size_t i = 0; i != placeholder.escaped_prefixes; ++i)
      os.put(kPlaceholderPrefix);
    if (placeholder.escaped) {
      os<<placeholder.number<<kPlaceholderSuffix;
    } else {
      if (placeholder.number < params_count)
        params[placeholder.number]->OutputToStream(os);
      else {
        missing_parameter(os, placeholder.number);
      }
    }
    text = ph_pos + placeholder.length;
  }
  os.write(text, end - text);
}

void format_params(const char* fmt, size_t fmt_length, std::ostream& os,
                   const Streamable<char>* params[], size_t params_count) {
  format_params_tmpl(fmt, fmt_length, os, params, params_count);
}
void format_params(const wchar_t* fmt, size_t fmt_length, std::wostream& os,
                  const Streamable<wchar_t>* params[], size_t params_count) {
  format_params_tmpl(fmt, fmt_length, os, params, params_count);
}



} // namespace impl
} // namespace typesafe_format