bool Streader_read_dict(Streader* sr, Dict_item_reader ir, void* userdata) { rassert(sr != NULL); if (Streader_is_error_set(sr)) return false; // Check opening brace if (!(Streader_skip_whitespace(sr) && Streader_match_char(sr, '{'))) { Streader_set_error(sr, "Expected a dictionary opening brace"); return false; } // Check empty dictionary if (Streader_try_match_char(sr, '}')) return true; if (ir == NULL) { Streader_set_error(sr, "Expected an empty dictionary"); return false; } // Read key/value pairs do { char key[STREADER_DICT_KEY_LENGTH_MAX + 1] = ""; if (!(Streader_read_string(sr, STREADER_DICT_KEY_LENGTH_MAX + 1, key) && Streader_match_char(sr, ':') && ir(sr, key, userdata))) return false; } while (Streader_try_match_char(sr, ',')); // Check closing brace if (!Streader_match_char(sr, '}')) { Streader_set_error(sr, "Expected a dictionary closing brace"); return false; } return true; }
bool Streader_read_list(Streader* sr, List_item_reader ir, void* userdata) { rassert(sr != NULL); if (Streader_is_error_set(sr)) return false; // Check opening bracket if (!(Streader_skip_whitespace(sr) && Streader_match_char(sr, '['))) { Streader_set_error(sr, "Expected a list opening bracket"); return false; } // Check empty list if (Streader_try_match_char(sr, ']')) return true; if (ir == NULL) { Streader_set_error(sr, "Expected an empty list"); return false; } // Read elements int32_t index = 0; do { if (!ir(sr, index, userdata)) return false; ++index; } while (Streader_try_match_char(sr, ',')); // Check closing bracket if (!Streader_match_char(sr, ']')) { Streader_set_error(sr, "Expected a list closing bracket"); return false; } return true; }
static bool Streader_read_tuning(Streader* sr, double* cents) { rassert(sr != NULL); rassert(cents != NULL); if (Streader_is_error_set(sr)) return false; if (Streader_try_match_char(sr, '[')) { int64_t num = 0; int64_t den = 0; if (!Streader_readf(sr, "%i,%i]", &num, &den)) return false; if (num <= 0) { Streader_set_error(sr, "Numerator must be positive"); return false; } if (den <= 0) { Streader_set_error(sr, "Denominator must be positive"); return false; } *cents = log2((double)num / (double)den) * 1200; } else { if (!Streader_read_float(sr, cents)) return false; if (!isfinite(*cents)) { Streader_set_error(sr, "Cents value must be finite"); return false; } } return true; }
bool Streader_read_float(Streader* sr, double* dest) { rassert(sr != NULL); if (Streader_is_error_set(sr)) return false; Streader_skip_whitespace(sr); // Note: Not completely accurate, but not supposed to be portable anyway... bool is_negative = false; int64_t significand = 0; int significand_shift = 0; int significant_digits_read = 0; int exponent = 0; bool exponent_is_negative = false; // Negation if (Streader_try_match_char(sr, '-')) is_negative = true; if (Streader_end_reached(sr)) { Streader_set_error(sr, "No digits found"); return false; } // Significand if (CUR_CH == '0') { ++sr->pos; } else if (strchr(NONZERO_DIGITS, CUR_CH) != NULL) { while (!Streader_end_reached(sr) && isdigit(CUR_CH)) { if (significant_digits_read < SIGNIFICANT_MAX) { // Store the digit into our significand rassert(significand < INT64_MAX / 10); significand *= 10; significand += (int64_t)(CUR_CH - '0'); ++significant_digits_read; } else { // We have run out of accuracy, just update magnitude ++significand_shift; } ++sr->pos; } } else { Streader_set_error(sr, "No digits found"); return false; } // Decimal part if (!Streader_end_reached(sr) && CUR_CH == '.') { ++sr->pos; while (!Streader_end_reached(sr) && isdigit(CUR_CH)) { if (significant_digits_read < SIGNIFICANT_MAX) { // Append digit to our significand and compensate in shift rassert(significand < INT64_MAX / 10); significand *= 10; significand += (int64_t)(CUR_CH - '0'); --significand_shift; if (significand != 0) ++significant_digits_read; } ++sr->pos; } // Require at least one digit if (sr->str[sr->pos - 1] == '.') { Streader_set_error(sr, "No digits found after decimal point"); return false; } } // Exponent part if (!Streader_end_reached(sr) && ((CUR_CH == 'e') || (CUR_CH == 'E')) ) { ++sr->pos; if (Streader_end_reached(sr)) { Streader_set_error(sr, "No digits found after exponent indicator"); return false; } if (CUR_CH == '+') { ++sr->pos; } else if (CUR_CH == '-') { exponent_is_negative = true; ++sr->pos; } while (!Streader_end_reached(sr) && isdigit(CUR_CH)) { rassert(exponent < INT_MAX / 10); exponent *= 10; exponent += (int)(CUR_CH - '0'); ++sr->pos; } // Require at least one digit if (!isdigit(sr->str[sr->pos - 1])) { Streader_set_error(sr, "No digits found after exponent indicator"); return false; } } if (!Streader_end_reached(sr) && isalpha(CUR_CH)) { Streader_set_error(sr, "Trailing letters after a number"); return false; } // Optimise trailing zeros away from the significand if (significand != 0) { while (significand % 10 == 0) { significand /= 10; ++significand_shift; } } // Convert the number if (exponent_is_negative) exponent = -exponent; int final_shift = significand_shift + exponent; double result = (double)significand; double abs_magnitude = pow(10, abs(final_shift)); if (final_shift >= 0) result *= abs_magnitude; else result /= abs_magnitude; if (is_negative) result = -result; if (dest != NULL) *dest = result; return true; }
bool Streader_read_int(Streader* sr, int64_t* dest) { rassert(sr != NULL); if (Streader_is_error_set(sr)) return false; Streader_skip_whitespace(sr); int64_t result = 0; const bool negative = Streader_try_match_char(sr, '-'); if (Streader_end_reached(sr)) { Streader_set_error(sr, "Unexpected end of data"); return false; } if (CUR_CH == '0') { ++sr->pos; if (!Streader_end_reached(sr) && isalnum(CUR_CH)) { Streader_set_error(sr, "Unexpected characters after initial zero"); return false; } } else if (strchr(NONZERO_DIGITS, CUR_CH) != NULL) { if (negative) { static const int64_t safe_lower = INT64_MIN / 10; while (!Streader_end_reached(sr) && isdigit(CUR_CH)) { // Check for multiplication overflow if (result < safe_lower) { Streader_set_error(sr, "Integer is too small"); return false; } result *= 10; const int64_t contrib = CUR_CH - '0'; // Check for addition overflow if (INT64_MIN + contrib > result) { Streader_set_error(sr, "Integer is too small"); return false; } result -= contrib; ++sr->pos; } } else { static const int64_t safe_upper = INT64_MAX / 10; while (!Streader_end_reached(sr) && isdigit(CUR_CH)) { // Check for multiplication overflow if (result > safe_upper) { Streader_set_error(sr, "Integer is too large"); return false; } result *= 10; const int64_t contrib = CUR_CH - '0'; // Check for addition overflow if (INT64_MAX - contrib < result) { Streader_set_error(sr, "Integer is too large"); return false; } result += contrib; ++sr->pos; } } } else { Streader_set_error(sr, "No digits found"); return false; } if (dest != NULL) *dest = result; return true; }