Skip to content

Commit caaa955

Browse files
committed
no more heap allocations
1 parent 50e85e5 commit caaa955

File tree

1 file changed

+85
-96
lines changed

1 file changed

+85
-96
lines changed

csscolorparser.cpp

Lines changed: 85 additions & 96 deletions
Original file line numberDiff line numberDiff line change
@@ -203,35 +203,20 @@ float clamp_css_float(T f) { // Clamp to float 0.0 .. 1.0.
203203
return f < 0 ? 0 : f > 1 ? 1 : f;
204204
}
205205

206-
float parse_float(const std::string& str) {
207-
return strtof(str.c_str(), nullptr);
208-
}
209-
210-
int64_t parse_int(const char* str, int& read, uint8_t base) {
211-
char *pos = nullptr;
212-
int64_t val = strtoll(str, &pos, base);
213-
read = pos - str;
206+
int64_t parse_int(const std::string& str, int pos, int& read, uint8_t base) {
207+
const char *s = str.c_str() + pos;
208+
char *end = nullptr;
209+
int64_t val = strtoll(s, &end, base);
210+
read = end - s;
214211
return val;
215212
}
216213

217-
int64_t parse_int(const std::string& str) {
218-
return strtoll(str.c_str(), nullptr, 10);
219-
}
220-
221-
uint8_t parse_css_int(const std::string& str) { // int or percentage.
222-
if (str.length() && str.back() == '%') {
223-
return clamp_css_byte(parse_float(str) / 100.0f * 255.0f);
224-
} else {
225-
return clamp_css_byte(parse_int(str));
226-
}
227-
}
228-
229-
float parse_css_float(const std::string& str) { // float or percentage.
230-
if (str.length() && str.back() == '%') {
231-
return clamp_css_float(parse_float(str) / 100.0f);
232-
} else {
233-
return clamp_css_float(parse_float(str));
234-
}
214+
float parse_float(const std::string& str, int pos, int& read) {
215+
const char *s = str.c_str() + pos;
216+
char *end = nullptr;
217+
float val = strtof(s, &end);
218+
read = end - s;
219+
return val;
235220
}
236221

237222
float css_hue_to_rgb(float m1, float m2, float h) {
@@ -254,7 +239,7 @@ float css_hue_to_rgb(float m1, float m2, float h) {
254239
}
255240

256241
bool match(char c, const std::string& text, size_t& pos, size_t end) {
257-
if (end < pos + 1)
242+
if (pos >= end)
258243
return false;
259244

260245
if (c != tolower(text[pos]))
@@ -266,7 +251,7 @@ bool match(char c, const std::string& text, size_t& pos, size_t end) {
266251

267252
bool match_prefix(const std::string& prefix, const std::string& text, size_t& pos, size_t end) {
268253
size_t length = prefix.size();
269-
if (end < length + pos)
254+
if (length + pos > end)
270255
return false;
271256

272257
for (size_t i = 0; i < length; ++i)
@@ -283,18 +268,6 @@ void skip_whitespace(const std::string& text, size_t& pos, size_t end){
283268
}
284269
}
285270

286-
std::vector<std::string> split(const std::string& s, char delim, size_t position) {
287-
std::vector<std::string> elems;
288-
std::stringstream ss(s);
289-
ss.seekg(position);
290-
291-
std::string item;
292-
while (std::getline(ss, item, delim)) {
293-
elems.push_back(item);
294-
}
295-
return elems;
296-
}
297-
298271
Color CSSColorParser::parse(const std::string& css_str) {
299272
bool valid;
300273
return parse(css_str, valid);
@@ -315,8 +288,8 @@ Color CSSColorParser::parse(const std::string& css_str, bool& valid) {
315288
if (match('#', css_str, pos, end)) {
316289
int read = 0;
317290

318-
const char* str = css_str.c_str() + pos;
319-
int64_t iv = parse_int(str, read, 16);
291+
//const char* str = css_str.c_str() + pos;
292+
int64_t iv = parse_int(css_str, pos, read, 16);
320293
if (iv < 0) {
321294
// Invalid: out of range.
322295
return {};
@@ -374,78 +347,94 @@ Color CSSColorParser::parse(const std::string& css_str, bool& valid) {
374347
return {};
375348
}
376349

377-
bool rgb, hsl = false;
350+
bool rgb, hsl = false, hasAlpha = false;
378351
rgb = match_prefix("rgb", css_str, pos, end);
379352
if (!rgb) {
380353
hsl = match_prefix("hsl", css_str, pos, end);
381354
}
382355
if (rgb || hsl) {
356+
hasAlpha = match('a', css_str, pos, end);
383357

384-
bool hasAlpha = match('a', css_str, pos, end);
358+
if (!match('(', css_str, pos, end)) { return {}; }
359+
}
385360

386-
if (!match('(', css_str, pos, end)) {
387-
return {};
388-
}
361+
if (rgb) {
362+
float values[4] = { 0, 0, 0, 1 };
389363

390-
// TODO: validation
391-
const std::vector<std::string> params = split(css_str, ',', pos);
364+
for (int i = 0; i < (hasAlpha ? 4 : 3); i++) {
365+
if (i > 0 && !match(',', css_str, pos, end)) { return {}; }
392366

393-
float alpha = 1.0f;
367+
int read = 0;
368+
values[i] = parse_float(css_str, pos, read);
394369

395-
if (rgb) {
396-
if (hasAlpha) {
397-
if (params.size() != 4) {
398-
return {};
399-
}
400-
alpha = parse_css_float(params.back());
401-
} else {
402-
if (params.size() != 3) {
403-
return {};
370+
if (read == 0) { return {}; }
371+
pos += read;
372+
373+
if (match('%', css_str, pos, end)) {
374+
if (i < 3) {
375+
values[i] = (values[i] / 100.0f * 255.0f);
376+
} else {
377+
values[i] = clamp_css_float(values[i] / 100.0f);
404378
}
405379
}
380+
skip_whitespace(css_str, pos, end);
381+
}
382+
if (!match(')', css_str, pos, end)) { return {}; }
406383

407-
valid = true;
408-
return {
409-
parse_css_int(params[0]),
410-
parse_css_int(params[1]),
411-
parse_css_int(params[2]),
412-
alpha
413-
};
414-
415-
} else if (hsl) {
416-
if (hasAlpha) {
417-
if (params.size() != 4) {
418-
return {};
419-
}
420-
alpha = parse_css_float(params.back());
421-
} else {
422-
if (params.size() != 3) {
423-
return {};
384+
valid = true;
385+
return {
386+
clamp_css_byte(values[0]),
387+
clamp_css_byte(values[1]),
388+
clamp_css_byte(values[2]),
389+
values[3]
390+
};
391+
392+
} else if (hsl) {
393+
394+
float values[4] = { 0, 0, 0, 1 };
395+
396+
for (int i = 0; i < (hasAlpha ? 4 : 3); i++) {
397+
if (i > 0 && !match(',', css_str, pos, end)) { return {}; }
398+
399+
int read = 0;
400+
values[i] = parse_float(css_str, pos, read);
401+
402+
if (read == 0) { return {}; }
403+
pos += read;
404+
405+
if (match('%', css_str, pos, end)) {
406+
// NB: previously the % was ignored in this case - make it an error?
407+
if (i > 0) {
408+
values[i] = clamp_css_float(values[i] / 100.0f);
424409
}
425410
}
426-
427-
float h = parse_float(params[0].c_str()) / 360.0f;
428-
while (h < 0.0f) h++;
429-
while (h > 1.0f) h--;
430-
431-
// NOTE(deanm): According to the CSS spec s/l should only be
432-
// percentages, but we don't bother and let float or percentage.
433-
float s = parse_css_float(params[1]);
434-
float l = parse_css_float(params[2]);
435-
436-
float m2 = l <= 0.5f ? l * (s + 1.0f) : l + s - l * s;
437-
float m1 = l * 2.0f - m2;
438-
439-
valid = true;
440-
return {
441-
clamp_css_byte(css_hue_to_rgb(m1, m2, h + 1.0f / 3.0f) * 255.0f),
442-
clamp_css_byte(css_hue_to_rgb(m1, m2, h) * 255.0f),
443-
clamp_css_byte(css_hue_to_rgb(m1, m2, h - 1.0f / 3.0f) * 255.0f),
444-
alpha
445-
};
411+
skip_whitespace(css_str, pos, end);
446412
}
413+
if (!match(')', css_str, pos, end)) { return {}; }
414+
415+
float h = values[0] / 360.0f;
416+
while (h < 0.0f) h++;
417+
while (h > 1.0f) h--;
418+
419+
// NOTE(deanm): According to the CSS spec s/l should only be
420+
// percentages, but we don't bother and let float or percentage.
421+
float s = values[1];
422+
float l = values[2];
423+
424+
float m2 = l <= 0.5f ? l * (s + 1.0f) : l + s - l * s;
425+
float m1 = l * 2.0f - m2;
426+
427+
valid = true;
428+
return {
429+
clamp_css_byte(css_hue_to_rgb(m1, m2, h + 1.0f / 3.0f) * 255.0f),
430+
clamp_css_byte(css_hue_to_rgb(m1, m2, h) * 255.0f),
431+
clamp_css_byte(css_hue_to_rgb(m1, m2, h - 1.0f / 3.0f) * 255.0f),
432+
values[3]
433+
};
447434
}
448435

436+
// TODO: ignore trailing whitespace?
437+
449438
size_t length = end - pos;
450439

451440
// Skip if longer than longest named color

0 commit comments

Comments
 (0)