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_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_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; }
END_TEST START_TEST(Read_nonzero_int) { char data[128] = ""; static const int64_t nums[] = { 1, 19, INT64_MAX, -1, -19, INT64_MIN }; for (size_t i = 0; i < arr_size(nums); ++i) { sprintf(data, "%" PRId64 " x", nums[i]); Streader* sr = init_with_cstr(data); int64_t num = 0; fail_if(!Streader_read_int(sr, &num), "Could not read %" PRId64, nums[i]); fail_if(num != nums[i], "Streader stored %" PRId64 " instead of %" PRId64, num, nums[i]); fail_if(!Streader_match_char(sr, 'x'), "Streader did not consume %" PRId64 " correctly", nums[i]); } }
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 #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; }
END_TEST START_TEST(Read_zero_int) { Streader* sr = init_with_cstr("0 x"); int64_t num = -1; fail_if(!Streader_read_int(sr, &num), "Could not read 0"); fail_if(num != 0, "Streader stored %" PRId64 " instead of 0", num); fail_if(!Streader_match_char(sr, 'x'), "Streader did not consume 0 correctly"); sr = init_with_cstr("-0 x"); num = 1; fail_if(!Streader_read_int(sr, &num), "Could not read -0"); fail_if(num != 0, "Streader stored %" PRId64 " instead of 0", num); fail_if(!Streader_match_char(sr, 'x'), "Streader did not consume -0 correctly"); }
static bool readf_dict(Streader* sr, const char* key, void* userdata) { if (userdata == NULL) return false; if (strcmp(key, "ten") != 0) return false; int* dest = userdata; int64_t result = 0; if (!Streader_read_int(sr, &result)) return false; *dest = (int)result; return true; }
END_TEST static bool fail_at_index_2(Streader* sr, int32_t index, void* userdata) { (void)sr; (void)userdata; fail_if(index > 2, "List processing continued after failure"); if (index == 2) return false; fail_if(!Streader_read_int(sr, NULL), "Could not read an integer from list: %s", Streader_get_error_desc(sr)); return true; }
END_TEST START_TEST(Reading_too_large_int_in_magnitude_fails) { char data[4][128] = { "" }; sprintf(data[0], "%" PRId64, INT64_MAX); sprintf(data[1], "%" PRId64, INT64_MIN); sprintf(data[2], "%" PRId64, INT64_MAX); sprintf(data[3], "%" PRId64, INT64_MIN); // Make sure the overflowing code below makes sense for (int i = 0; i < 4; ++i) { size_t len = strlen(data[i]); assert(len > 2); assert(isdigit(data[i][len - 1])); assert(data[i][len - 1] != '9'); assert(isdigit(data[i][len - 2])); assert(data[i][len - 2] != '9'); (void)len; } // Overflow data[0] and data[1] by 1 ++data[0][strlen(data[0]) - 1]; ++data[1][strlen(data[1]) - 1]; // Overflow data[2] and data[3] by 10 ++data[2][strlen(data[2]) - 2]; ++data[3][strlen(data[3]) - 2]; // Test reading for (int i = 0; i < 4; ++i) { Streader* sr = init_with_cstr(data[i]); int64_t num = 0; fail_if(Streader_read_int(sr, &num), "Reading overflowing integer %s succeeded", data[i]); } }
END_TEST static bool readf_list(Streader* sr, int32_t index, void* userdata) { (void)index; if (userdata == NULL) return false; int* dest = userdata; int64_t result = 0; if (!Streader_read_int(sr, &result)) return false; *dest = (int)result; 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_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; }