END_TEST START_TEST(Matching_visible_characters_succeed) { static const char* ones[] = { "1", " 1", " 1 ", }; for (size_t i = 0; i < arr_size(ones); ++i) { Streader* sr = init_with_cstr(ones[i]); fail_if(!Streader_match_char(sr, '1'), "Could not match '1' in \"%s\"", ones[i]); fail_if(Streader_is_error_set(sr), "Error set after a successful match in \"%s\"", ones[i]); fail_if(Streader_match_char(sr, '1'), "Streader did not move forwards after a successful match" " in \"%s\"", ones[i]); } static const char* exprs[] = { "1+2", " 1+2", "1 +2", " 1 +2", "1+ 2", "1 + 2", " 1 + 2 ", }; for (size_t i = 0; i < arr_size(exprs); ++i) { Streader* sr = init_with_cstr(exprs[i]); fail_if(!Streader_match_char(sr, '1'), "Could not match '1' in \"%s\"", exprs[i]); fail_if(Streader_is_error_set(sr), "Error set after a successful match in \"%s\"", exprs[i]); fail_if(!Streader_match_char(sr, '+'), "Could not match '+' in \"%s\"", exprs[i]); fail_if(Streader_is_error_set(sr), "Error set after a successful match in \"%s\"", exprs[i]); fail_if(!Streader_match_char(sr, '2'), "Could not match '2' in \"%s\"", exprs[i]); fail_if(Streader_is_error_set(sr), "Error set after a successful match in \"%s\"", exprs[i]); } }
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 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 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 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; }
bool Streader_read_finite_rt(Streader* sr, Value* dest) { rassert(sr != NULL); if (Streader_is_error_set(sr)) return false; Streader_skip_whitespace(sr); const int64_t start_pos = sr->pos; Value* value = VALUE_AUTO; if (Streader_read_bool(sr, &value->value.bool_type)) value->type = VALUE_TYPE_BOOL; else if ((recover(sr, start_pos), Streader_read_int(sr, &value->value.int_type)) && (CUR_CH != '.') && (CUR_CH != 'e') && (CUR_CH != 'E')) value->type = VALUE_TYPE_INT; else if (recover(sr, start_pos), (Streader_read_float(sr, &value->value.float_type) && isfinite(value->value.float_type))) value->type = VALUE_TYPE_FLOAT; else if (recover(sr, start_pos), Streader_read_tstamp(sr, &value->value.Tstamp_type)) value->type = VALUE_TYPE_TSTAMP; if (value->type == VALUE_TYPE_NONE) return false; if (dest != NULL) Value_copy(dest, value); 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; }
Channel_defaults_list* new_Channel_defaults_list(Streader* sr) { rassert(sr != NULL); if (Streader_is_error_set(sr)) return NULL; Channel_defaults_list* cdl = memory_alloc_item(Channel_defaults_list); if (cdl == NULL) { Streader_set_memory_error(sr, "Could not allocate memory for channel defaults"); return NULL; } for (int ch = 0; ch < KQT_CHANNELS_MAX; ++ch) Channel_defaults_init(&cdl->ch_defaults[ch]); if (!Streader_has_data(sr)) return cdl; if (!Streader_read_list(sr, read_ch_defaults_item, cdl)) { del_Channel_defaults_list(cdl); return NULL; } return cdl; }
END_TEST static bool fill_array_index(Streader* sr, const char* key, void* userdata) { fail_if(sr == NULL, "Callback did not get a Streader"); fail_if(Streader_is_error_set(sr), "Callback was called with Streader error set: %s", Streader_get_error_desc(sr)); fail_if(key == NULL, "Callback did not get a key"); fail_if(strlen(key) != 1 || strchr("0123", key[0]) == NULL, "Callback got an unexpected key `%s`", key); fail_if(userdata == NULL, "Callback did not get userdata"); int arr_index = key[0] - '0'; assert(arr_index >= 0); assert(arr_index < 4); int* array = userdata; int64_t num = 0; fail_if(!Streader_read_int(sr, &num), "Could not read integer from dictionary: %s", Streader_get_error_desc(sr)); fail_if(num != arr_index + 10, "Unexpected value %" PRId64 " (expected %d)", num, arr_index + 10); array[arr_index] = (int)num; return true; }
END_TEST static bool check_adjusted_tstamp(Streader* sr, int32_t index, void* userdata) { fail_if(sr == NULL, "Callback did not get a Streader"); fail_if(Streader_is_error_set(sr), "Callback was called with Streader error set: %s", Streader_get_error_desc(sr)); fail_if(index < 0, "Callback got a negative item index (%" PRId32 ")", index); fail_if(index >= item_count, "Callback got too large an index (%" PRId32 ")", index); fail_if(userdata != NULL, "Callback got unexpected userdata"); Tstamp* ts = TSTAMP_AUTO; fail_if(!Streader_read_tstamp(sr, ts), "Could not read timestamp from list index %" PRId32 ": %s", index, Streader_get_error_desc(sr)); fail_if((Tstamp_get_beats(ts) != index + 10) || (Tstamp_get_rem(ts) != index + 100), "Unexpected list item " PRIts " (expected (%d, %d))", PRIVALts(*ts), (int)index + 10, (int)index + 100); return true; }
END_TEST #define item_count 4 static bool inc_doubled_int(Streader* sr, int32_t index, void* userdata) { fail_if(sr == NULL, "Callback did not get a Streader"); fail_if(Streader_is_error_set(sr), "Callback was called with Streader error set: %s", Streader_get_error_desc(sr)); fail_if(index < 0, "Callback got a negative item index (%" PRId32 ")", index); fail_if(index >= item_count, "Callback got too large an index (%" PRId32 ")", index); fail_if(userdata == NULL, "Callback did not get userdata"); int64_t num = 0; fail_if(!Streader_read_int(sr, &num), "Could not read integer from list index %" PRId32 ": %s", index, Streader_get_error_desc(sr)); fail_if(num != index * 2, "Unexpected list item %" PRId64 " (expected %" PRId32 ")", num, index * 2); int* nums = userdata; nums[index] = (int)num + 1; 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; }
Hit_map* new_Hit_map_from_string(Streader* sr) { rassert(sr != NULL); if (Streader_is_error_set(sr)) return NULL; Hit_map* map = memory_alloc_item(Hit_map); if (map == NULL) { Streader_set_memory_error(sr, "Could not allocate memory for hit map"); return NULL; } for (int i = 0; i < KQT_HITS_MAX; ++i) map->hits[i] = NULL; if (!Streader_has_data(sr)) return map; if (!Streader_read_list(sr, read_mapping, map)) { del_Hit_map(map); return NULL; } return map; }
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_has_data(Streader* sr) { rassert(sr != NULL); rassert(!Streader_is_error_set(sr)); Streader_skip_whitespace(sr); return !Streader_end_reached(sr); }
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; }
END_TEST START_TEST(Matching_wrong_characters_fails) { Streader* sr = init_with_cstr("1"); fail_if(Streader_match_char(sr, '2'), "Matched '2' successfully in \"1\""); fail_if(!Streader_is_error_set(sr), "No error set after a failed match"); fail_if(Streader_match_char(sr, '1'), "Match succeeded after an error"); Streader_clear_error(sr); fail_if(Streader_is_error_set(sr), "Streader_clear_error did not remove Streader error"); fail_if(!Streader_match_char(sr, '1'), "Correct match did not succeed after a cleared failure"); }
Order_list* new_Order_list(Streader* sr) { rassert(sr != NULL); if (Streader_is_error_set(sr)) return NULL; // Create the base structure Order_list* ol = memory_alloc_item(Order_list); if (ol == NULL) { Streader_set_memory_error( sr, "Could not allocate memory for order list"); return NULL; } ol->pat_insts = NULL; ol->index_map = NULL; // Create Pattern instance reference vector ol->pat_insts = new_Vector(sizeof(Pat_inst_ref)); if (ol->pat_insts == NULL) { Streader_set_memory_error( sr, "Could not allocate memory for order list"); del_Order_list(ol); return NULL; } // Create reverse index of ol->pat_insts ol->index_map = new_AAtree( (AAtree_item_cmp*)Pat_inst_ref_cmp, (AAtree_item_destroy*)memory_free); if (ol->index_map == NULL) { Streader_set_memory_error( sr, "Could not allocate memory for order list"); del_Order_list(ol); return NULL; } // List is empty by default if (!Streader_has_data(sr)) return ol; // Read the list of Pattern instance references if (!Streader_read_list(sr, read_piref, ol)) { del_Order_list(ol); return NULL; } return ol; }
bool read_default_manifest(Streader* sr) { rassert(sr != NULL); if (Streader_is_error_set(sr)) return false; if (!Streader_has_data(sr)) return false; return Streader_read_dict(sr, read_manifest_entry, NULL); }
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 Streader_try_match_char(Streader* sr, char ch) { rassert(sr != NULL); rassert(!Streader_is_error_set(sr)); const int64_t start_pos = sr->pos; const bool success = Streader_match_char(sr, ch); if (!success) { Streader_clear_error(sr); sr->pos = start_pos; } return success; }
Note_map* new_Note_map_from_string(Streader* sr) { assert(sr != NULL); if (Streader_is_error_set(sr)) return NULL; Note_map* map = memory_alloc_item(Note_map); if (map == NULL) { Streader_set_memory_error( sr, "Could not allocate memory for note map"); return NULL; } map->map = NULL; map->iter = NULL; map->map = new_AAtree( (int (*)(const void*, const void*))Random_list_cmp, (void (*)(void*))del_Random_list); if (map->map == NULL) { del_Note_map(map); Streader_set_memory_error( sr, "Could not allocate memory for note map"); return NULL; } map->iter = new_AAiter(map->map); if (map->iter == NULL) { del_Note_map(map); Streader_set_memory_error( sr, "Could not allocate memory for note map"); return NULL; } if (!Streader_has_data(sr)) return map; if (!Streader_read_list(sr, read_mapping, map)) { del_Note_map(map); return NULL; } return map; }
static bool Streader_try_match_char_seq(Streader* sr, const char* seq) { assert(sr != NULL); assert(!Streader_is_error_set(sr)); assert(seq != NULL); const size_t start_pos = sr->pos; const bool success = Streader_match_char_seq(sr, seq); if (!success) { Streader_clear_error(sr); sr->pos = start_pos; } return success; }
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; }
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_skip_whitespace(Streader* sr) { rassert(sr != NULL); if (Streader_is_error_set(sr)) return false; while (!Streader_end_reached(sr) && isspace(CUR_CH)) { if (CUR_CH == '\n') ++sr->line; ++sr->pos; } rassert(sr->pos <= sr->len); 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; }
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 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; }