END_TEST START_TEST(Read_valid_string) { static const struct { const char* data; const char* expected; } strings[] = { { "\"\"", "" }, { "\" \"", " " }, { "\" \"", " " }, { "\"\\\"\"", "\"" }, { "\"\\\\\"", "\\" }, { "\"\\/\"", "/" }, { "\"/\"", "/" }, /* { "\"\\b\"", "\b" }, { "\"\\f\"", "\f" }, { "\"\\n\"", "\n" }, { "\"\\r\"", "\r" }, { "\"\\t\"", "\t" }, */ { "\"abc def\"", "abc def" }, }; for (size_t i = 0; i < arr_size(strings); ++i) { char data[128] = ""; sprintf(data, "%s x", strings[i].data); Streader* sr = init_with_cstr(data); char result[128] = "zzz"; fail_if(!Streader_read_string(sr, 128, result), "Could not read string `%s`: %s", data, Streader_get_error_desc(sr)); fail_if(strcmp(result, strings[i].expected) != 0, "Streader stored `%s` instead of `%s`", result, strings[i].expected); fail_if(!Streader_match_char(sr, 'x'), "Streader did not consume string `%s` correctly"); } }
END_TEST START_TEST(Read_valid_tstamp) { static const struct { const char* data; const Tstamp expected; } tstamps[] = { { "[0, 0]", { 0, 0 } }, { "[2,5]", { 2, 5 } }, { " [2,5]", { 2, 5 } }, { "[ 2,5]", { 2, 5 } }, { "[2 ,5]", { 2, 5 } }, { "[2, 5]", { 2, 5 } }, { "[2,5 ]", { 2, 5 } }, { "[-1, 3]", { -1, 3 } }, { "[0, 882161279]", { 0, KQT_TSTAMP_BEAT - 1 } }, }; for (size_t i = 0; i < arr_size(tstamps); ++i) { char data[128] = ""; sprintf(data, "%s x", tstamps[i].data); Streader* sr = init_with_cstr(data); Tstamp* result = Tstamp_set(TSTAMP_AUTO, 99, 99); fail_if(!Streader_read_tstamp(sr, result), "Could not read timestamp " PRIts " from `%s`: %s", PRIVALts(tstamps[i].expected), data, Streader_get_error_desc(sr)); fail_if(Tstamp_cmp(result, &tstamps[i].expected) != 0, "Streader stored " PRIts " instead of " PRIts " when reading `%s`", PRIVALts(*result), PRIVALts(tstamps[i].expected), data); fail_if(!Streader_match_char(sr, 'x'), "Streader did not consume timestamp from `%s` correctly", data); } }
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; }
END_TEST START_TEST(Read_empty_dict) { static const char* dicts[] = { "{} x", "{ }x", "{ } x", }; for (size_t i = 0; i < arr_size(dicts); ++i) { Streader* sr = init_with_cstr(dicts[i]); fail_if(!Streader_read_dict(sr, NULL, NULL), "Could not read empty dictionary from `%s`: %s", dicts[i], Streader_get_error_desc(sr)); fail_if(!Streader_match_char(sr, 'x'), "Streader did not consume empty dictionary from `%s` correctly", dicts[i]); } }
bool Streader_readf(Streader* sr, const char* format, ...) { rassert(sr != NULL); rassert(format != NULL); va_list args; va_start(args, format); while (!Streader_is_error_set(sr) && *format != '\0') { if (*format == '%') { // Conversion characters ++format; switch (*format) { case 'n': { Streader_read_null(sr); } break; case 'b': { bool* dest = va_arg(args, bool*); Streader_read_bool(sr, dest); } break; case 'i': { int64_t* dest = va_arg(args, int64_t*); Streader_read_int(sr, dest); } break; case 'f': { double* dest = va_arg(args, double*); Streader_read_float(sr, dest); } break; case 's': { Streader_readf_str_info info = va_arg(args, Streader_readf_str_info); rassert(info.guard == Streader_readf_str_guard); const int64_t max_bytes = info.max_bytes; char* dest = info.dest; Streader_read_string(sr, max_bytes, dest); } break; case 't': { Tstamp* dest = va_arg(args, Tstamp*); Streader_read_tstamp(sr, dest); } break; case 'p': { Pat_inst_ref* dest = va_arg(args, Pat_inst_ref*); Streader_read_piref(sr, dest); } break; case 'l': { List_item_reader* ir = va_arg(args, List_item_reader*); void* userdata = va_arg(args, void*); Streader_read_list(sr, ir, userdata); } break; case 'd': { Dict_item_reader* ir = va_arg(args, Dict_item_reader*); void* userdata = va_arg(args, void*); Streader_read_dict(sr, ir, userdata); } break; case '%': { Streader_match_char(sr, '%'); } break; default: rassert(false); } } else { // Characters to be matched if (!isspace(*format))
Env_var* new_Env_var_from_string(Streader* sr) { rassert(sr != NULL); if (Streader_is_error_set(sr)) return NULL; char type_name[16] = ""; char name[KQT_VAR_NAME_MAX] = ""; if (!Streader_readf( sr, "[%s,%s,", READF_STR(16, type_name), READF_STR(KQT_VAR_NAME_MAX, name))) return NULL; if (!is_valid_var_name(name)) { Streader_set_error( sr, "Illegal variable name %s" " (Variable names may only contain" " lower-case letters and underscores" " (and digits as other than first characters))", name); return NULL; } Value* value = VALUE_AUTO; if (string_eq(type_name, "bool")) { value->type = VALUE_TYPE_BOOL; Streader_read_bool(sr, &value->value.bool_type); } else if (string_eq(type_name, "int")) { value->type = VALUE_TYPE_INT; Streader_read_int(sr, &value->value.int_type); } else if (string_eq(type_name, "float")) { value->type = VALUE_TYPE_FLOAT; Streader_read_float(sr, &value->value.float_type); } else if (string_eq(type_name, "timestamp")) { value->type = VALUE_TYPE_TSTAMP; Streader_read_tstamp(sr, &value->value.Tstamp_type); } else { Streader_set_error( sr, "Invalid type of environment variable %s: %s", name, type_name); return NULL; } if (!Streader_match_char(sr, ']')) return NULL; Env_var* var = new_Env_var(value->type, name); if (var == NULL) { Streader_set_memory_error( sr, "Could not allocate memory for environment variable"); return NULL; } Env_var_set_value(var, value); return var; }
static bool read_mapping(Streader* sr, int32_t index, void* userdata) { assert(sr != NULL); (void)index; assert(userdata != NULL); Note_map* map = userdata; Random_list* list = memory_alloc_item(Random_list); if (list == NULL) { Streader_set_memory_error( sr, "Could not allocate memory for note map entry"); return false; } double cents = NAN; double force = NAN; if (!Streader_readf(sr, "[[%f,%f],", ¢s, &force)) { memory_free(list); return false; } if (!isfinite(cents)) { Streader_set_error(sr, "Mapping cents is not finite"); memory_free(list); return false; } if (!isfinite(force)) { Streader_set_error(sr, "Mapping force is not finite"); memory_free(list); return false; } list->freq = exp2(cents / 1200) * 440; list->cents = cents; list->force = force; list->entry_count = 0; if (!AAtree_ins(map->map, list)) { Streader_set_memory_error( sr, "Couldn't allocate memory for note map entry"); memory_free(list); return false; } if (!Streader_read_list(sr, read_random_list_entry, list)) return false; if (list->entry_count == 0) { Streader_set_error(sr, "Empty note mapping random list"); return false; } return Streader_match_char(sr, ']'); }
static bool read_mapping(Streader* sr, int32_t index, void* userdata) { rassert(sr != NULL); ignore(index); rassert(userdata != NULL); Hit_map* map = userdata; Random_list* list = memory_alloc_item(Random_list); if (list == NULL) { del_Hit_map(map); Streader_set_memory_error( sr, "Could not allocate memory for hit map random list"); return false; } int64_t hit_index = -1; double force = NAN; if (!Streader_readf(sr, "[[%i,%f],", &hit_index, &force)) { memory_free(list); return false; } if (hit_index < 0 || hit_index >= KQT_HITS_MAX) { Streader_set_error( sr, "Mapping hit index is outside range [0, %d)", KQT_HITS_MAX); memory_free(list); return false; } if (!isfinite(force)) { Streader_set_error( sr, "Mapping force is not finite", KQT_HITS_MAX); memory_free(list); return false; } if (map->hits[hit_index] == NULL) { map->hits[hit_index] = new_AAtree( (AAtree_item_cmp*)Random_list_cmp, (AAtree_item_destroy*)memory_free); if (map->hits[hit_index] == NULL) { Streader_set_memory_error( sr, "Could not allocate memory for hit map"); memory_free(list); return false; } } list->force = force; list->entry_count = 0; if (AAtree_contains(map->hits[hit_index], list)) { Streader_set_error( sr, "Duplicate hit map entry with hit index %" PRId64 ", force %.2f", hit_index, force); memory_free(list); return false; } if (!AAtree_ins(map->hits[hit_index], list)) { Streader_set_memory_error( sr, "Could not allocate memory for hit map random list"); memory_free(list); return false; } if (!Streader_read_list(sr, read_random_list_entry, list)) return false; return Streader_match_char(sr, ']'); }