bool Streader_match_char(Streader* sr, char ch) { rassert(sr != NULL); if (Streader_is_error_set(sr)) return false; Streader_skip_whitespace(sr); if (Streader_end_reached(sr)) { Streader_set_error(sr, "Expected '%c' instead of end of data", ch); return false; } if (CUR_CH != ch) { if (isprint(CUR_CH)) Streader_set_error( sr, "Expected '%c' instead of '%c'", ch, CUR_CH); else Streader_set_error( sr, "Expected '%c' instead of %#04hhx", ch, (unsigned)CUR_CH); return false; } ++sr->pos; return true; }
bool Streader_read_tstamp(Streader* sr, Tstamp* dest) { rassert(sr != NULL); if (Streader_is_error_set(sr)) return false; int64_t beats = 0; int64_t rem = 0; if (!(Streader_match_char(sr, '[') && Streader_read_int(sr, &beats) && Streader_match_char(sr, ',') && Streader_read_int(sr, &rem) && Streader_match_char(sr, ']'))) { Streader_set_error(sr, "Expected a valid timestamp"); return false; } if (rem < 0 || rem >= KQT_TSTAMP_BEAT) { Streader_set_error( sr, "Timestamp remainder %" PRId64 " out of range [0..%ld)", rem, KQT_TSTAMP_BEAT); return false; } if (dest != NULL) Tstamp_set(dest, beats, (int32_t)rem); return true; }
bool Streader_match_string(Streader* sr, const char* str) { rassert(sr != NULL); rassert(str != NULL); if (Streader_is_error_set(sr)) return false; Streader_skip_whitespace(sr); if (Streader_end_reached(sr)) { Streader_set_error( sr, "Expected beginning of a string instead of end of data"); return false; } // Match opening double quote if (CUR_CH != '\"') { if (isprint(CUR_CH)) Streader_set_error( sr, "Expected beginning of a string instead of '%c'", CUR_CH); else Streader_set_error( sr, "Expected beginning of a string instead of %#2x", (unsigned)CUR_CH); return false; } ++sr->pos; // Match the string if (!Streader_match_char_seq(sr, str)) return false; // Match closing double quote if (Streader_end_reached(sr) || CUR_CH != '\"') { Streader_set_error(sr, "Unexpected string (expected `%s`)", str); return false; } ++sr->pos; return true; }
bool Streader_read_piref(Streader* sr, Pat_inst_ref* dest) { rassert(sr != NULL); if (Streader_is_error_set(sr)) return false; int64_t pat = 0; int64_t inst = 0; if (!(Streader_match_char(sr, '[') && Streader_read_int(sr, &pat) && Streader_match_char(sr, ',') && Streader_read_int(sr, &inst) && Streader_match_char(sr, ']'))) { Streader_set_error(sr, "Expected a valid pattern instance"); return false; } if (pat < 0 || pat >= KQT_PATTERNS_MAX) { Streader_set_error( sr, "Pattern number %" PRId64 " out of range [0..%ld)" " in pattern instance", pat, KQT_PATTERNS_MAX); return false; } if (inst < 0 || inst >= KQT_PAT_INSTANCES_MAX) { Streader_set_error( sr, "Pattern instance number %" PRId64 " out of range [0..%ld)" " in pattern instance", inst, KQT_PAT_INSTANCES_MAX); return false; } if (dest != NULL) { dest->pat = (int16_t)pat; dest->inst = (int16_t)inst; } return true; }
bool Module_parse_random_seed(Module* module, Streader* sr) { rassert(module != NULL); rassert(sr != NULL); if (Streader_is_error_set(sr)) return false; int64_t seed = 0; if (Streader_has_data(sr)) { if (!Streader_read_int(sr, &seed)) return false; if (seed < 0) { Streader_set_error(sr, "Random seed must be non-negative"); return false; } } module->random_seed = (uint64_t)seed; return true; }
bool Streader_read_bool(Streader* sr, bool* dest) { rassert(sr != NULL); if (Streader_is_error_set(sr)) return false; Streader_skip_whitespace(sr); bool result = false; if (Streader_try_match_char_seq(sr, "true")) { result = true; } else { if (!Streader_match_char_seq(sr, "false")) { Streader_set_error(sr, "Expected a boolean value"); return false; } } if (dest != NULL) *dest = result; return true; }
bool Module_read_mixing_volume(Module* module, Streader* sr) { rassert(module != NULL); rassert(sr != NULL); if (Streader_is_error_set(sr)) return false; double mix_vol = COMP_DEFAULT_MIX_VOL; if (Streader_has_data(sr)) { if (!Streader_read_float(sr, &mix_vol)) return false; if (!isfinite(mix_vol) && mix_vol != -INFINITY) { Streader_set_error(sr, "Invalid mixing volume: %.4f", mix_vol); return false; } } module->mix_vol_dB = mix_vol; module->mix_vol = exp2(module->mix_vol_dB / 6); return true; }
END_TEST static bool test_reported_force(Streader* sr, double expected) { assert(sr != NULL); double actual = NAN; if (!(Streader_readf(sr, "[[0, [") && Streader_match_string(sr, "qf") && Streader_readf(sr, ", null]], [0, [") && Streader_match_string(sr, "Af") && Streader_readf(sr, ", %f]]]", &actual))) return false; if (fabs(actual - expected) > 0.1) { Streader_set_error( sr, "Expected force %.4f, got %.4f", expected, actual); return false; } return true; }
static bool read_env_var(Streader* sr, int32_t index, void* userdata) { assert(sr != NULL); (void)index; assert(userdata != NULL); AAtree* new_vars = userdata; Env_var* var = new_Env_var_from_string(sr); if (var == NULL) return false; if (AAtree_contains(new_vars, Env_var_get_name(var))) { Streader_set_error( sr, "Variable name %s is not unique", Env_var_get_name(var)); del_Env_var(var); return false; } if (!AAtree_ins(new_vars, var)) { Streader_set_memory_error( sr, "Could not allocate memory for environment"); del_Env_var(var); return false; } return true; }
bool Module_read_force_shift(Module* module, Streader* sr) { rassert(module != NULL); rassert(sr != NULL); if (Streader_is_error_set(sr)) return false; double shift = 0; if (Streader_has_data(sr)) { if (!Streader_read_float(sr, &shift)) return false; if (!isfinite(shift) || (shift < -60) || (shift > 0)) { Streader_set_error(sr, "Invalid force shift: %.4f", shift); return false; } } module->force_shift = shift; return true; }
bool read_received_events(Streader* sr, int32_t index, void* userdata) { assert(sr != NULL); (void)index; assert(userdata != NULL); int32_t* expected = userdata; double actual = NAN; const char* event_name = (*expected % 16 == 0) ? "n+" : "vs"; if (!(Streader_readf(sr, "[0, [") && Streader_match_string(sr, event_name) && Streader_readf(sr, ", %f]]", &actual)) ) return false; if ((int)round(actual) != *expected) { Streader_set_error( sr, "Received argument %" PRId64 " instead of %" PRId32, actual, *expected); return false; } *expected = (int)round(actual) + 1; return true; }
Connections* new_Connections_from_string( Streader* sr, Connection_level level, Au_table* au_table, Device* master) { rassert(sr != NULL); rassert(au_table != NULL); rassert(master != NULL); if (Streader_is_error_set(sr)) return NULL; Connections* graph = memory_alloc_item(Connections); if (graph == NULL) { Streader_set_memory_error( sr, "Could not allocate memory for connections"); return NULL; } graph->nodes = NULL; graph->nodes = new_AAtree( (int (*)(const void*, const void*))Device_node_cmp, (void (*)(void*))del_Device_node); mem_error_if(graph->nodes == NULL, graph, NULL, sr); Device_node* master_node = NULL; if (level == CONNECTION_LEVEL_AU) { const Device* iface = Audio_unit_get_output_interface((Audio_unit*)master); master_node = new_Device_node("", au_table, iface); } else { master_node = new_Device_node("", au_table, master); } mem_error_if(master_node == NULL, graph, NULL, sr); mem_error_if(!AAtree_ins(graph->nodes, master_node), graph, master_node, sr); if (!Streader_has_data(sr)) return graph; read_conn_data rcdata = { graph, level, au_table, master }; if (!Streader_read_list(sr, read_connection, &rcdata)) { del_Connections(graph); return NULL; } if (Connections_is_cyclic(graph)) { Streader_set_error(sr, "The connection graph contains a cycle"); del_Connections(graph); return NULL; } return graph; }
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; }
static bool Streader_match_char_seq(Streader* sr, const char* seq) { rassert(sr != NULL); rassert(seq != NULL); rassert(!Streader_is_error_set(sr)); rassert(!Streader_end_reached(sr)); // Check that we have enough data const int64_t expected_len = (int64_t)strlen(seq); if (sr->len - sr->pos < expected_len) { Streader_set_error( sr, "Too few bytes left to contain expected string `%s`", seq); return false; } // Match the string if (strncmp(&sr->str[sr->pos], seq, (size_t)expected_len) != 0) { Streader_set_error(sr, "Unexpected string (expected `%s`)", seq); return false; } // Update position for (int64_t i = 0; i < expected_len; ++i) { if (CUR_CH == '\n') ++sr->line; ++sr->pos; } if (!Streader_end_reached(sr) && isalnum(CUR_CH)) { Streader_set_error( sr, "Unexpected character after expected string `%s`", seq); 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_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 read_expressions_def(Streader* sr, const char* key, void* userdata) { rassert(sr != NULL); rassert(key != NULL); rassert(userdata != NULL); Au_expressions* ae = userdata; if (string_eq(key, "default_note_expr")) { char expr[KQT_VAR_NAME_MAX + 1] = ""; if (!Streader_read_string(sr, KQT_VAR_NAME_MAX + 1, expr)) return false; if ((expr[0] != '\0') && !is_valid_var_name(expr)) { Streader_set_error(sr, "Invalid default note expression: %s", expr); return false; } strcpy(ae->default_note_expr, expr); } else if (string_eq(key, "expressions")) { if (!Streader_read_dict(sr, read_expressions, ae)) { rassert(Streader_is_error_set(sr)); return false; } } else { Streader_set_error(sr, "Unexpected key in expression specification: %s", key); return false; } return true; }
static bool read_ch_defaults_item(Streader* sr, int32_t index, void* userdata) { rassert(sr != NULL); rassert(userdata != NULL); Channel_defaults_list* cdl = userdata; if (index >= KQT_CHANNELS_MAX) { Streader_set_error(sr, "Too many channel descriptions"); return false; } return Channel_defaults_read(&cdl->ch_defaults[index], sr); }
Au_expressions* new_Au_expressions(Streader* sr) { rassert(sr != NULL); if (Streader_is_error_set(sr)) return NULL; Au_expressions* ae = memory_alloc_item(Au_expressions); if (ae == NULL) { Streader_set_memory_error( sr, "Could not allocate memory for audio unit expressions"); return NULL; } memset(ae->default_note_expr, '\0', KQT_VAR_NAME_MAX); ae->entries = new_AAtree( (AAtree_item_cmp*)strcmp, (AAtree_item_destroy*)del_Entry); if (ae->entries == NULL) { Streader_set_memory_error( sr, "Could not allocate memory for audio unit expressions"); del_Au_expressions(ae); return NULL; } if (!Streader_read_dict(sr, read_expressions_def, ae)) { rassert(Streader_is_error_set(sr)); del_Au_expressions(ae); return NULL; } if (ae->default_note_expr[0] != '\0' && !Au_expressions_get_proc_filter(ae, ae->default_note_expr)) { Streader_set_error( sr, "Audio unit expressions do not contain the default expression %s", ae->default_note_expr); del_Au_expressions(ae); return NULL; } return ae; }
bool Streader_read_null(Streader* sr) { rassert(sr != NULL); if (Streader_is_error_set(sr)) return false; Streader_skip_whitespace(sr); if (!Streader_match_char_seq(sr, "null")) { Streader_set_error(sr, "Expected null"); return false; } return true; }
static bool read_piref(Streader* sr, int32_t index, void* userdata) { rassert(sr != NULL); rassert(userdata != NULL); Order_list* ol = userdata; // Read the Pattern instance reference Pat_inst_ref* p = PAT_INST_REF_AUTO; if (!Streader_read_piref(sr, p)) return false; // Check if the Pattern instance is already used Index_mapping* key = INDEX_MAPPING_AUTO; key->p = *p; key->ol_index = index; if (AAtree_contains(ol->index_map, key)) { Streader_set_error( sr, "Duplicate occurrence of pattern instance" " [%" PRId16 ", %" PRId16 "]", p->pat, p->inst); return false; } // Add the reference to our containers if (!Vector_append(ol->pat_insts, p)) { Streader_set_memory_error( sr, "Could not allocate memory for order list"); return false; } Index_mapping* im = new_Index_mapping(key); if (im == NULL || !AAtree_ins(ol->index_map, im)) { memory_free(im); Streader_set_memory_error( sr, "Could not allocate memory for order list"); return false; } return true; }
Tuning_table* new_Tuning_table_from_string(Streader* sr) { rassert(sr != NULL); if (Streader_is_error_set(sr)) return NULL; Tuning_table* tt = new_Tuning_table( TUNING_TABLE_DEFAULT_REF_PITCH, TUNING_TABLE_DEFAULT_OCTAVE_WIDTH); if (tt == NULL) { Streader_set_memory_error( sr, "Couldn't allocate memory for tuning table"); return NULL; } if (Streader_has_data(sr)) { if (!Streader_read_dict(sr, read_tuning_table_item, tt)) { del_Tuning_table(tt); return NULL; } if (tt->ref_note >= tt->note_count) { Streader_set_error(sr, "Reference note doesn't exist: %d", tt->ref_note); del_Tuning_table(tt); return NULL; } } if (!Tuning_table_build_pitch_map(tt)) { Streader_set_memory_error(sr, "Couldn't allocate memory for tuning table"); del_Tuning_table(tt); return NULL; } return tt; }
static bool read_random_list_entry(Streader* sr, int32_t index, void* userdata) { assert(sr != NULL); assert(userdata != NULL); if (index >= NOTE_MAP_RANDOMS_MAX) { Streader_set_error(sr, "Too many note map random list entries"); return false; } Random_list* list = userdata; if (!Sample_entry_parse(&list->entries[list->entry_count], sr)) return false; list->entries[list->entry_count].ref_freq = list->freq; ++list->entry_count; return true; }
static bool read_random_list_entry(Streader* sr, int32_t index, void* userdata) { rassert(sr != NULL); rassert(userdata != NULL); Random_list* list = userdata; if (index >= HIT_MAP_RANDOMS_MAX) { Streader_set_error(sr, "Too many hit map random list entries"); return false; } if (!Sample_entry_parse(&list->entries[list->entry_count], sr)) return false; ++list->entry_count; return true; }
static bool read_note(Streader* sr, int32_t index, void* userdata) { rassert(sr != NULL); rassert(userdata != NULL); if (index >= KQT_TUNING_TABLE_NOTES_MAX) { Streader_set_error(sr, "Too many notes in the tuning table"); return false; } Tuning_table* tt = userdata; double cents = NAN; if (!Streader_read_tuning(sr, ¢s)) return false; Tuning_table_set_note_cents(tt, index, cents); return true; }
static bool read_expressions(Streader* sr, const char* key, void* userdata) { rassert(sr != NULL); rassert(key != NULL); rassert(userdata != NULL); Au_expressions* ae = userdata; if (!is_valid_var_name(key)) { Streader_set_error(sr, "Invalid expression name: %s", key); return false; } Entry* entry = memory_alloc_item(Entry); if (entry == NULL) return false; strcpy(entry->name, key); Param_proc_filter* filter = new_Param_proc_filter(sr); if (filter == NULL) { memory_free(entry); return false; } entry->filter = filter; if (!AAtree_ins(ae->entries, entry)) { del_Param_proc_filter(filter); memory_free(entry); return false; } return true; }
bool read_received_events_bind(Streader* sr, int32_t index, void* userdata) { assert(sr != NULL); (void)index; assert(userdata != NULL); int32_t* expected = userdata; double actual = NAN; if (index == 0 && *expected == 0) { return Streader_readf(sr, "[0, [") && Streader_match_string(sr, "#") && Streader_match_char(sr, ',') && Streader_read_string(sr, 0, NULL) && Streader_readf(sr, "]]"); } if (!(Streader_readf(sr, "[0, [") && Streader_match_string(sr, "n+") && Streader_readf(sr, ", %f]]", &actual)) ) return false; if ((int)round(actual) != *expected) { Streader_set_error( sr, "Received argument %.0f instead of %" PRId32, actual, *expected); return false; } *expected = (int)round(actual) + 1; return true; }
bool Streader_read_string(Streader* sr, int64_t max_bytes, char* dest) { rassert(sr != NULL); rassert(max_bytes >= 0); if (Streader_is_error_set(sr)) return false; Streader_skip_whitespace(sr); if (Streader_end_reached(sr)) { Streader_set_error(sr, "Unexpected end of data"); return false; } // Check opening double quote if (CUR_CH != '\"') { if (isprint(CUR_CH)) Streader_set_error( sr, "Expected beginning of a string instead of '%c'", CUR_CH); else Streader_set_error( sr, "Expected beginning of a string instead of %#2x", (unsigned)CUR_CH); return false; } ++sr->pos; // Parse and copy the string int64_t write_pos = 0; while (!Streader_end_reached(sr) && CUR_CH != '\"') { if (CUR_CH == '\\') { // Escape sequences ++sr->pos; if (Streader_end_reached(sr)) { Streader_set_error(sr, "Unexpected end of data"); return false; } char special_ch = '\0'; if (strchr("\"\\/", CUR_CH) != NULL) special_ch = CUR_CH; else if (CUR_CH == 'b') special_ch = '\b'; else if (CUR_CH == 'f') special_ch = '\f'; else if (CUR_CH == 'n') special_ch = '\n'; else if (CUR_CH == 'r') special_ch = '\r'; else if (CUR_CH == 't') special_ch = '\t'; else if (CUR_CH == 'u') { static const char* upper_hex_digits = "ABCDEF"; static const char* lower_hex_digits = "abcdef"; int32_t code = 0; ++sr->pos; for (int i = 0; i < 4; ++i) { if (Streader_end_reached(sr)) { Streader_set_error(sr, "Unexpected end of data"); return false; } int32_t value = -1; if (isdigit(CUR_CH)) value = CUR_CH - '0'; else if (strchr(upper_hex_digits, CUR_CH) != NULL) value = (int32_t)(strchr(upper_hex_digits, CUR_CH) - upper_hex_digits + 0xa); else if (strchr(lower_hex_digits, CUR_CH) != NULL) value = (int32_t)(strchr(lower_hex_digits, CUR_CH) - lower_hex_digits + 0xa); else { char wrong_char[5] = ""; print_wrong_char(wrong_char, CUR_CH); Streader_set_error( sr, "Expected a hexadecimal digit instead of %s", wrong_char); return false; } rassert(value >= 0); rassert(value < 0x10); code *= 0x10; code += value; ++sr->pos; } if (code < 0x20 || code > 0x7e) { Streader_set_error( sr, "Unicode character U+%04X outside permitted" " range [U+0020,U+007E]", code); return false; } special_ch = (char)code; } else { if (isprint(CUR_CH)) Streader_set_error( sr, "Invalid escape sequence '\\%c'", CUR_CH); else Streader_set_error( sr, "Invalid escape sequence %#2x", CUR_CH); return false; } if (special_ch < 0x20 || special_ch > 0x7e) { char wrong_char[5] = ""; print_wrong_char(wrong_char, special_ch); Streader_set_error(sr, "Invalid special character %s", wrong_char); return false; } if (dest != NULL && write_pos < max_bytes - 1) dest[write_pos++] = special_ch; } else if (iscntrl(CUR_CH)) { Streader_set_error(sr, "Control character %#2x in string"); return false; } else { // Normal characters // TODO: check Unicode if needed if (dest != NULL && write_pos + 1 < max_bytes) dest[write_pos++] = CUR_CH; } ++sr->pos; } // Check closing double quote if (Streader_end_reached(sr)) { Streader_set_error(sr, "Unexpected end of data"); return false; } rassert(CUR_CH == '\"'); ++sr->pos; if (max_bytes > 0 && dest != NULL) { rassert(write_pos < max_bytes); dest[write_pos] = '\0'; } return true; }
static bool read_tuning_table_item(Streader* sr, const char* key, void* userdata) { rassert(sr != NULL); rassert(key != NULL); rassert(userdata != NULL); Tuning_table* tt = userdata; if (string_eq(key, "ref_note")) { int64_t num = 0; if (!Streader_read_int(sr, &num)) return false; if (num < 0 || num >= KQT_TUNING_TABLE_NOTES_MAX) { Streader_set_error( sr, "Invalid reference note number: %" PRId64, num); return false; } tt->ref_note = (int)num; } else if (string_eq(key, "ref_pitch")) { double num = NAN; if (!Streader_read_float(sr, &num)) return false; if (!isfinite(num)) { Streader_set_error(sr, "Invalid reference pitch: %f", num); return false; } tt->ref_pitch = num; } else if (string_eq(key, "pitch_offset")) { double cents = NAN; if (!Streader_read_float(sr, ¢s)) return false; if (!isfinite(cents)) { Streader_set_error(sr, "Pitch offset is not finite"); return false; } tt->global_offset = cents; } else if (string_eq(key, "octave_width")) { double cents = NAN; if (!Streader_read_tuning(sr, ¢s)) return false; if (cents <= 0) { Streader_set_error(sr, "Octave width must be positive"); return false; } Tuning_table_set_octave_width(tt, cents); } else if (string_eq(key, "centre_octave")) { int64_t octave = -1; if (!Streader_read_int(sr, &octave)) return false; if (octave < 0 || octave >= KQT_TUNING_TABLE_OCTAVES) { Streader_set_error(sr, "Invalid centre octave: %" PRId64, octave); return false; } tt->centre_octave = (int)octave; Tuning_table_set_octave_width(tt, tt->octave_width); } else if (string_eq(key, "notes")) { for (int i = 0; i < KQT_TUNING_TABLE_NOTES_MAX; ++i) tt->note_offsets[i] = NAN; if (!Streader_read_list(sr, read_note, tt)) return false; } else { Streader_set_error(sr, "Unrecognised key in tuning table: %s", key); 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; }