Welcome to mirror list, hosted at ThFree Co, Russian Federation.

parser_string_utils.cc « importer « wavefront_obj « io « blender « source - git.blender.org/blender.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
blob: 3e45529c698f42fecda3a4089b62e6a3ff6baaaf (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
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
/* SPDX-License-Identifier: GPL-2.0-or-later */

#include <fstream>
#include <iostream>
#include <sstream>

#include "BLI_math_vec_types.hh"
#include "BLI_span.hh"
#include "BLI_string_ref.hh"
#include "BLI_vector.hh"

#include "parser_string_utils.hh"

/* Note: these OBJ parser helper functions are planned to get fairly large
 * changes "soon", so don't read too much into current implementation... */

namespace blender::io::obj {
using std::string;

/**
 * Store multiple lines separated by an escaped newline character: `\\n`.
 * Use this before doing any parse operations on the read string.
 */
void read_next_line(std::fstream &file, string &r_line)
{
  std::string new_line;
  while (file.good() && !r_line.empty() && r_line.back() == '\\') {
    new_line.clear();
    const bool ok = static_cast<bool>(std::getline(file, new_line));
    /* Remove the last backslash character. */
    r_line.pop_back();
    r_line.append(new_line);
    if (!ok || new_line.empty()) {
      return;
    }
  }
}

/**
 * Split a line string into the first word (key) and the rest of the line.
 * Also remove leading & trailing spaces as well as `\r` carriage return
 * character if present.
 */
void split_line_key_rest(const StringRef line, StringRef &r_line_key, StringRef &r_rest_line)
{
  if (line.is_empty()) {
    return;
  }

  const int64_t pos_split{line.find_first_of(' ')};
  if (pos_split == StringRef::not_found) {
    /* Use the first character if no space is found in the line. It's usually a comment like:
     * #This is a comment. */
    r_line_key = line.substr(0, 1);
  }
  else {
    r_line_key = line.substr(0, pos_split);
  }

  /* Eat the delimiter also using "+ 1". */
  r_rest_line = line.drop_prefix(r_line_key.size() + 1);
  if (r_rest_line.is_empty()) {
    return;
  }

  /* Remove any leading spaces, trailing spaces & \r character, if any. */
  const int64_t leading_space{r_rest_line.find_first_not_of(' ')};
  if (leading_space != StringRef::not_found) {
    r_rest_line = r_rest_line.drop_prefix(leading_space);
  }

  /* Another way is to do a test run before the actual parsing to find the newline
   * character and use it in the getline. */
  const int64_t carriage_return{r_rest_line.find_first_of('\r')};
  if (carriage_return != StringRef::not_found) {
    r_rest_line = r_rest_line.substr(0, carriage_return + 1);
  }

  const int64_t trailing_space{r_rest_line.find_last_not_of(' ')};
  if (trailing_space != StringRef::not_found) {
    /* The position is of a character that is not ' ', so count of characters is position + 1. */
    r_rest_line = r_rest_line.substr(0, trailing_space + 1);
  }
}

/**
 * Split the given string by the delimiter and fill the given vector.
 * If an intermediate string is empty, or space or null character, it is not appended to the
 * vector.
 */
void split_by_char(StringRef in_string, const char delimiter, Vector<StringRef> &r_out_list)
{
  r_out_list.clear();

  while (!in_string.is_empty()) {
    const int64_t pos_delim{in_string.find_first_of(delimiter)};
    const int64_t word_len = pos_delim == StringRef::not_found ? in_string.size() : pos_delim;

    StringRef word{in_string.data(), word_len};
    if (!word.is_empty() && !(word == " " && !(word[0] == '\0'))) {
      r_out_list.append(word);
    }
    if (pos_delim == StringRef::not_found) {
      return;
    }
    /* Skip the word already stored. */
    in_string = in_string.drop_prefix(word_len);
    /* Skip all delimiters. */
    const int64_t pos_non_delim = in_string.find_first_not_of(delimiter);
    if (pos_non_delim == StringRef::not_found) {
      return;
    }
    in_string = in_string.drop_prefix(std::min(pos_non_delim, in_string.size()));
  }
}

/**
 * Convert the given string to float and assign it to the destination value.
 *
 * If the string cannot be converted to a float, the fallback value is used.
 */
void copy_string_to_float(StringRef src, const float fallback_value, float &r_dst)
{
  try {
    r_dst = std::stof(string(src));
  }
  catch (const std::invalid_argument &inv_arg) {
    std::cerr << "Bad conversion to float:'" << inv_arg.what() << "':'" << src << "'" << std::endl;
    r_dst = fallback_value;
  }
  catch (const std::out_of_range &out_of_range) {
    std::cerr << "Out of range for float:'" << out_of_range.what() << ":'" << src << "'"
              << std::endl;
    r_dst = fallback_value;
  }
}

/**
 * Convert all members of the Span of strings to floats and assign them to the float
 * array members. Usually used for values like coordinates.
 *
 * If a string cannot be converted to a float, the fallback value is used.
 */
void copy_string_to_float(Span<StringRef> src,
                          const float fallback_value,
                          MutableSpan<float> r_dst)
{
  for (int i = 0; i < r_dst.size(); ++i) {
    if (i < src.size()) {
      copy_string_to_float(src[i], fallback_value, r_dst[i]);
    }
    else {
      r_dst[i] = fallback_value;
    }
  }
}

/**
 * Convert the given string to int and assign it to the destination value.
 *
 * If the string cannot be converted to an integer, the fallback value is used.
 */
void copy_string_to_int(StringRef src, const int fallback_value, int &r_dst)
{
  try {
    r_dst = std::stoi(string(src));
  }
  catch (const std::invalid_argument &inv_arg) {
    std::cerr << "Bad conversion to int:'" << inv_arg.what() << "':'" << src << "'" << std::endl;
    r_dst = fallback_value;
  }
  catch (const std::out_of_range &out_of_range) {
    std::cerr << "Out of range for int:'" << out_of_range.what() << ":'" << src << "'"
              << std::endl;
    r_dst = fallback_value;
  }
}

/**
 * Convert the given strings to ints and fill the destination int buffer.
 *
 * If a string cannot be converted to an integer, the fallback value is used.
 */
void copy_string_to_int(Span<StringRef> src, const int fallback_value, MutableSpan<int> r_dst)
{
  for (int i = 0; i < r_dst.size(); ++i) {
    if (i < src.size()) {
      copy_string_to_int(src[i], fallback_value, r_dst[i]);
    }
    else {
      r_dst[i] = fallback_value;
    }
  }
}

std::string replace_all_occurences(StringRef original, StringRef to_remove, StringRef to_add)
{
  std::string clean{original};
  while (true) {
    const std::string::size_type pos = clean.find(to_remove);
    if (pos == std::string::npos) {
      break;
    }
    clean.replace(pos, to_add.size(), to_add);
  }
  return clean;
}

}  // namespace blender::io::obj