/* Fix me: This considers empty fields as duplicates. */ void playlist_remove_duplicates_by_scheme (int playlist, int scheme) { int entries = playlist_entry_count (playlist); int count; if (entries < 1) return; playlist_select_all (playlist, FALSE); if (filename_comparisons[scheme] != NULL) { int (* compare) (const char * a, const char * b) = filename_comparisons[scheme]; playlist_sort_by_filename (playlist, compare); char * last = playlist_entry_get_filename (playlist, 0); for (count = 1; count < entries; count ++) { char * current = playlist_entry_get_filename (playlist, count); if (compare (last, current) == 0) playlist_entry_set_selected (playlist, count, TRUE); str_unref (last); last = current; } str_unref (last); } else if (tuple_comparisons[scheme] != NULL) { int (* compare) (const Tuple * a, const Tuple * b) = tuple_comparisons[scheme]; playlist_sort_by_tuple (playlist, compare); Tuple * last = playlist_entry_get_tuple (playlist, 0, FALSE); for (count = 1; count < entries; count ++) { Tuple * current = playlist_entry_get_tuple (playlist, count, FALSE); if (last != NULL && current != NULL && compare (last, current) == 0) playlist_entry_set_selected (playlist, count, TRUE); if (last) tuple_unref (last); last = current; } if (last) tuple_unref (last); } playlist_delete_selected (playlist); }
void audgui_infowin_show (int playlist, int entry) { char * filename = aud_playlist_entry_get_filename (playlist, entry); g_return_if_fail (filename != NULL); PluginHandle * decoder = aud_playlist_entry_get_decoder (playlist, entry, FALSE); if (decoder == NULL) goto FREE; if (aud_custom_infowin (filename, decoder)) goto FREE; Tuple * tuple = aud_playlist_entry_get_tuple (playlist, entry, FALSE); if (tuple == NULL) { char * message = g_strdup_printf (_("No info available for %s.\n"), filename); aud_interface_show_error (message); g_free (message); goto FREE; } infowin_show (playlist, entry, filename, tuple, decoder, aud_file_can_write_tuple (filename, decoder)); tuple_unref (tuple); FREE: str_unref (filename); }
static void ready (void *hook_data, void *user_data) { cleanup_current_track(); Tuple *current_track = aud_playlist_entry_get_tuple(aud_playlist_get_playing(), aud_playlist_get_position(aud_playlist_get_playing()), FALSE); int duration_seconds = tuple_get_int(current_track, FIELD_LENGTH) / 1000; if (duration_seconds <= 30) { tuple_unref(current_track); return; } pthread_mutex_lock(&communication_mutex); now_playing_track = tuple_ref(current_track); now_playing_requested = TRUE; pthread_cond_signal(&communication_signal); pthread_mutex_unlock(&communication_mutex); time_until_scrobble = (((gint64)duration_seconds)*G_USEC_PER_SEC) / 2; if (time_until_scrobble > 4*60*G_USEC_PER_SEC) { time_until_scrobble = 4*60*G_USEC_PER_SEC; } timestamp = g_get_real_time() / G_USEC_PER_SEC; play_started_at = g_get_monotonic_time(); playing_track = current_track; queue_function_ID = g_timeout_add_seconds(time_until_scrobble / G_USEC_PER_SEC, (GSourceFunc) queue_track_to_scrobble, NULL); }
static void mlp_hook_playback_begin(gpointer hook_data, gpointer user_data) { gint playlist = mlp_playlist_get_playing(); gint pos = mlp_playlist_get_position(playlist); if (mlp_playlist_entry_get_length (playlist, pos, FALSE) < 30) { AUDDBG(" *** not submitting due to entry->length < 30"); return; } gchar * filename = mlp_playlist_entry_get_filename (playlist, pos); if (ishttp (filename)) { AUDDBG(" *** not submitting due to HTTP source"); str_unref (filename); return; } str_unref (filename); sc_idle(m_scrobbler); if (submit_tuple) tuple_unref (submit_tuple); submit_tuple = mlp_playlist_entry_get_tuple (playlist, pos, FALSE); if (! submit_tuple) return; sc_addentry(m_scrobbler, submit_tuple, tuple_get_int(submit_tuple, FIELD_LENGTH, NULL) / 1000); if (!track_timeout) track_timeout = g_timeout_add_seconds(1, sc_timeout, NULL); }
static void scan_request_free (ScanRequest * request) { if (request->tuple) tuple_unref (request->tuple); str_unref (request->filename); g_free (request->image_data); str_unref (request->image_file); g_slice_free (ScanRequest, request); }
bool_t playlist_save (int list, const char * filename) { AUDDBG ("Saving playlist %s.\n", filename); PluginHandle * plugin = get_plugin (filename, TRUE); if (! plugin) return FALSE; PlaylistPlugin * pp = plugin_get_header (plugin); g_return_val_if_fail (pp && PLUGIN_HAS_FUNC (pp, load), FALSE); bool_t fast = get_bool (NULL, "metadata_on_play"); VFSFile * file = vfs_fopen (filename, "w"); if (! file) return FALSE; char * title = playlist_get_title (list); int entries = playlist_entry_count (list); Index * filenames = index_new (); index_allocate (filenames, entries); Index * tuples = index_new (); index_allocate (tuples, entries); for (int i = 0; i < entries; i ++) { index_append (filenames, playlist_entry_get_filename (list, i)); index_append (tuples, playlist_entry_get_tuple (list, i, fast)); } bool_t success = pp->save (filename, file, title, filenames, tuples); vfs_fclose (file); str_unref (title); for (int i = 0; i < entries; i ++) { str_unref (index_get (filenames, i)); Tuple * tuple = index_get (tuples, i); if (tuple) tuple_unref (tuple); } index_free (filenames); index_free (tuples); return success; }
static void file_close(void) { plugin->close(); convert_free(); if (output_file != NULL) vfs_fclose(output_file); output_file = NULL; if (tuple) { tuple_unref (tuple); tuple = NULL; } }
static void mlp_hook_playback_end(gpointer mlp_hook_data, gpointer user_data) { sc_idle(m_scrobbler); if (track_timeout) { g_source_remove(track_timeout); track_timeout = 0; } if (submit_tuple != NULL) { tuple_unref (submit_tuple); submit_tuple = NULL; } }
static void * playback_thread (void * unused) { PluginHandle * p = playback_entry_get_decoder (); current_decoder = p ? plugin_get_header (p) : NULL; if (! current_decoder) { char * error = g_strdup_printf (_("No decoder found for %s."), current_filename); interface_show_error (error); g_free (error); playback_error = TRUE; goto DONE; } current_data = NULL; current_bitrate = 0; current_samplerate = 0; current_channels = 0; Tuple * tuple = playback_entry_get_tuple (); read_gain_from_tuple (tuple); if (tuple) tuple_unref (tuple); bool_t seekable = (playback_entry_get_length () > 0); VFSFile * file = vfs_fopen (current_filename, "r"); time_offset = seekable ? playback_entry_get_start_time () : 0; playback_error = ! current_decoder->play (& playback_api, current_filename, file, seekable ? time_offset + initial_seek : 0, seekable ? playback_entry_get_end_time () : -1, paused); output_close_audio (); if (file) vfs_fclose (file); DONE: if (! ready_flag) set_pb_ready (& playback_api); end_source = g_timeout_add (0, end_cb, NULL); return NULL; }
static void cleanup_current_track(void) { timestamp = 0; play_started_at = 0; pause_started_at = 0; time_until_scrobble = 0; if (queue_function_ID != 0) { gboolean success = g_source_remove(queue_function_ID); queue_function_ID = 0; if (!success) { AUDDBG("BUG: No success on g_source_remove!\n"); } } if (playing_track != NULL) { tuple_unref(playing_track); playing_track = NULL; } }
void playlist_select_by_patterns (int playlist, const Tuple * patterns) { const int fields[] = {FIELD_TITLE, FIELD_ALBUM, FIELD_ARTIST, FIELD_FILE_NAME}; int entries = playlist_entry_count (playlist); int field, entry; playlist_select_all (playlist, TRUE); for (field = 0; field < G_N_ELEMENTS (fields); field ++) { char * pattern = tuple_get_str (patterns, fields[field], NULL); regex_t regex; if (! pattern || ! pattern[0] || regcomp (& regex, pattern, REG_ICASE)) { str_unref (pattern); continue; } for (entry = 0; entry < entries; entry ++) { if (! playlist_entry_get_selected (playlist, entry)) continue; Tuple * tuple = playlist_entry_get_tuple (playlist, entry, FALSE); char * string = tuple ? tuple_get_str (tuple, fields[field], NULL) : NULL; if (! string || regexec (& regex, string, 0, NULL, 0)) playlist_entry_set_selected (playlist, entry, FALSE); str_unref (string); if (tuple) tuple_unref (tuple); } regfree (& regex); str_unref (pattern); } }
GList * get_upload_list(void) { GList *up_list=NULL; gint current_play = aud_playlist_get_active(); gint i = (aud_playlist_entry_count(current_play) - 1); for (; i >= 0; i--) { if (aud_playlist_entry_get_selected(current_play, i)) { Tuple * tuple = aud_playlist_entry_get_tuple (current_play, i, FALSE); aud_playlist_entry_set_selected(current_play, i, FALSE); up_list = g_list_prepend (up_list, (void *) tuple); if (tuple) tuple_unref (tuple); } } return g_list_reverse(up_list); }
static void infowin_update_tuple (void * unused) { Tuple * tuple = tuple_new_from_filename (current_file); set_field_str_from_entry (tuple, FIELD_TITLE, entry_title); set_field_str_from_entry (tuple, FIELD_ARTIST, entry_artist); set_field_str_from_entry (tuple, FIELD_ALBUM, entry_album); set_field_str_from_entry (tuple, FIELD_COMMENT, entry_comment); set_field_str_from_entry (tuple, FIELD_GENRE, gtk_bin_get_child ((GtkBin *) entry_genre)); set_field_int_from_entry (tuple, FIELD_YEAR, entry_year); set_field_int_from_entry (tuple, FIELD_TRACK_NUMBER, entry_track); if (aud_file_write_tuple (current_file, current_decoder, tuple)) { ministatus_display_message (_("Metadata updated successfully")); something_changed = FALSE; gtk_widget_set_sensitive (btn_apply, FALSE); } else ministatus_display_message (_("Metadata updating failed")); tuple_unref (tuple); }
static void xspf_add_file (xmlNode * track, const gchar * filename, const gchar * base, Index * filenames, Index * tuples) { xmlNode *nptr; gchar *location = NULL; Tuple * tuple = NULL; for (nptr = track->children; nptr != NULL; nptr = nptr->next) { if (nptr->type == XML_ELEMENT_NODE) { if (!xmlStrcmp(nptr->name, (xmlChar *)"location")) { /* Location is a special case */ gchar *str = (gchar *)xmlNodeGetContent(nptr); if (strstr (str, "://") != NULL) location = str_get (str); else if (str[0] == '/' && base != NULL) { const gchar * colon = strstr (base, "://"); if (colon != NULL) location = str_printf ("%.*s%s", (gint) (colon + 3 - base), base, str); } else if (base != NULL) { const gchar * slash = strrchr (base, '/'); if (slash != NULL) location = str_printf ("%.*s%s", (gint) (slash + 1 - base), base, str); } xmlFree(str); } else { /* Rest of the nodes are handled here */ gint i; gboolean isMeta; xmlChar *findName; if (!xmlStrcmp(nptr->name, (xmlChar *)"meta")) { isMeta = TRUE; findName = xmlGetProp(nptr, (xmlChar *)"rel"); } else { isMeta = FALSE; findName = xmlStrdup(nptr->name); } for (i = 0; i < xspf_nentries; i++) if ((xspf_entries[i].isMeta == isMeta) && !xmlStrcmp(findName, (xmlChar *)xspf_entries[i].xspfName)) { xmlChar *str = xmlNodeGetContent(nptr); switch (xspf_entries[i].type) { case TUPLE_STRING: if (! tuple) tuple = tuple_new (); tuple_set_str(tuple, xspf_entries[i].tupleField, NULL, (gchar *)str); break; case TUPLE_INT: if (! tuple) tuple = tuple_new (); tuple_set_int(tuple, xspf_entries[i].tupleField, NULL, atol((char *)str)); break; default: break; } xmlFree(str); break; } xmlFree(findName); } } } if (location != NULL) { if (tuple) tuple_set_filename (tuple, location); index_append(filenames, location); index_append(tuples, tuple); } else if (tuple) tuple_unref (tuple); }
static bool_t playlist_load_cue (const char * cue_filename, VFSFile * file, char * * title, Index * filenames, Index * tuples) { void * buffer = NULL; vfs_file_read_all (file, & buffer, NULL); if (! buffer) return FALSE; * title = NULL; Cd * cd = cue_parse_string (buffer); g_free (buffer); if (cd == NULL) return FALSE; int tracks = cd_get_ntrack (cd); if (tracks == 0) return FALSE; Track * current = cd_get_track (cd, 1); if (current == NULL) return FALSE; char * track_filename = track_get_filename (current); if (track_filename == NULL) return FALSE; char * filename = uri_construct (track_filename, cue_filename); Tuple * base_tuple = NULL; bool_t base_tuple_scanned = FALSE; for (int track = 1; track <= tracks; track ++) { if (current == NULL || filename == NULL) return FALSE; if (base_tuple == NULL && ! base_tuple_scanned) { base_tuple_scanned = TRUE; PluginHandle * decoder = aud_file_find_decoder (filename, FALSE); if (decoder != NULL) base_tuple = aud_file_read_tuple (filename, decoder); } Track * next = (track + 1 <= tracks) ? cd_get_track (cd, track + 1) : NULL; char * next_filename = (next != NULL) ? uri_construct (track_get_filename (next), cue_filename) : NULL; bool_t last_track = (next_filename == NULL || strcmp (next_filename, filename)); Tuple * tuple = (base_tuple != NULL) ? tuple_copy (base_tuple) : tuple_new_from_filename (filename); tuple_set_int (tuple, FIELD_TRACK_NUMBER, track); int begin = (int64_t) track_get_start (current) * 1000 / 75; tuple_set_int (tuple, FIELD_SEGMENT_START, begin); if (last_track) { if (base_tuple != NULL && tuple_get_value_type (base_tuple, FIELD_LENGTH) == TUPLE_INT) tuple_set_int (tuple, FIELD_LENGTH, tuple_get_int (base_tuple, FIELD_LENGTH) - begin); } else { int length = (int64_t) track_get_length (current) * 1000 / 75; tuple_set_int (tuple, FIELD_LENGTH, length); tuple_set_int (tuple, FIELD_SEGMENT_END, begin + length); } for (int i = 0; i < ARRAY_LEN (pti_map); i ++) tuple_attach_cdtext (tuple, current, pti_map[i].tuple_type, pti_map[i].pti); index_insert (filenames, -1, str_get (filename)); index_insert (tuples, -1, tuple); current = next; str_unref (filename); filename = next_filename; if (last_track && base_tuple != NULL) { tuple_unref (base_tuple); base_tuple = NULL; base_tuple_scanned = FALSE; } } return TRUE; }
static void get_value (void * user, gint row, gint column, GValue * value) { PlaylistWidgetData * data = user; g_return_if_fail (column >= 0 && column < pw_num_cols); g_return_if_fail (row >= 0 && row < aud_playlist_entry_count (data->list)); column = pw_cols[column]; gchar * title = NULL, * artist = NULL, * album = NULL; Tuple * tuple = NULL; if (column == PW_COL_TITLE || column == PW_COL_ARTIST || column == PW_COL_ALBUM) aud_playlist_entry_describe (data->list, row, & title, & artist, & album, TRUE); else if (column == PW_COL_YEAR || column == PW_COL_TRACK || column == PW_COL_GENRE || column == PW_COL_FILENAME || column == PW_COL_PATH || column == PW_COL_BITRATE) tuple = aud_playlist_entry_get_tuple (data->list, row, TRUE); switch (column) { case PW_COL_NUMBER: g_value_set_int (value, 1 + row); break; case PW_COL_TITLE: g_value_set_string (value, title); break; case PW_COL_ARTIST: g_value_set_string (value, artist); break; case PW_COL_YEAR: set_int_from_tuple (value, tuple, FIELD_YEAR); break; case PW_COL_ALBUM: g_value_set_string (value, album); break; case PW_COL_TRACK: set_int_from_tuple (value, tuple, FIELD_TRACK_NUMBER); break; case PW_COL_GENRE: set_string_from_tuple (value, tuple, FIELD_GENRE); break; case PW_COL_QUEUED: set_queued (value, data->list, row); break; case PW_COL_LENGTH: set_length (value, data->list, row); break; case PW_COL_FILENAME: set_string_from_tuple (value, tuple, FIELD_FILE_NAME); break; case PW_COL_PATH: set_string_from_tuple (value, tuple, FIELD_FILE_PATH); break; case PW_COL_CUSTOM:; gchar * custom = aud_playlist_entry_get_title (data->list, row, TRUE); g_value_set_string (value, custom); str_unref (custom); break; case PW_COL_BITRATE: set_int_from_tuple (value, tuple, FIELD_BITRATE); break; } str_unref (title); str_unref (artist); str_unref (album); if (tuple) tuple_unref (tuple); }
/* do_command(): do @cmd after replacing the format codes @cmd: command to run */ static void do_command (char * cmd) { int playlist = aud_playlist_get_playing (); int pos = aud_playlist_get_position (playlist); char *shstring = NULL, *temp, numbuf[32]; gboolean playing; Formatter *formatter; if (cmd && strlen(cmd) > 0) { formatter = formatter_new(); char * ctitle = aud_playlist_entry_get_title (playlist, pos, FALSE); if (ctitle) { temp = escape_shell_chars (ctitle); formatter_associate(formatter, 's', temp); formatter_associate(formatter, 'n', temp); g_free(temp); str_unref (ctitle); } else { formatter_associate(formatter, 's', ""); formatter_associate(formatter, 'n', ""); } char * filename = aud_playlist_entry_get_filename (playlist, pos); if (filename) { temp = escape_shell_chars (filename); formatter_associate(formatter, 'f', temp); g_free(temp); str_unref (filename); } else formatter_associate(formatter, 'f', ""); g_snprintf(numbuf, sizeof(numbuf), "%02d", pos + 1); formatter_associate(formatter, 't', numbuf); int length = aud_playlist_entry_get_length (playlist, pos, FALSE); if (length > 0) { g_snprintf(numbuf, sizeof(numbuf), "%d", length); formatter_associate(formatter, 'l', numbuf); } else formatter_associate(formatter, 'l', "0"); playing = aud_drct_get_playing(); g_snprintf(numbuf, sizeof(numbuf), "%d", playing); formatter_associate(formatter, 'p', numbuf); if (playing) { int brate, srate, chans; aud_drct_get_info (& brate, & srate, & chans); snprintf (numbuf, sizeof numbuf, "%d", brate); formatter_associate (formatter, 'r', numbuf); snprintf (numbuf, sizeof numbuf, "%d", srate); formatter_associate (formatter, 'F', numbuf); snprintf (numbuf, sizeof numbuf, "%d", chans); formatter_associate (formatter, 'c', numbuf); } Tuple * tuple = aud_playlist_entry_get_tuple (aud_playlist_get_active (), pos, 0); char * artist = tuple ? tuple_get_str (tuple, FIELD_ARTIST, NULL) : NULL; if (artist) { formatter_associate(formatter, 'a', artist); str_unref(artist); } else formatter_associate(formatter, 'a', ""); char * album = tuple ? tuple_get_str (tuple, FIELD_ALBUM, NULL) : NULL; if (album) { formatter_associate(formatter, 'b', album); str_unref(album); } else formatter_associate(formatter, 'b', ""); char * title = tuple ? tuple_get_str (tuple, FIELD_TITLE, NULL) : NULL; if (title) { formatter_associate(formatter, 'T', title); str_unref(title); } else formatter_associate(formatter, 'T', ""); if (tuple) tuple_unref (tuple); shstring = formatter_format(formatter, cmd); formatter_destroy(formatter); if (shstring) { execute_command(shstring); /* FIXME: This can possibly be freed too early */ g_free(shstring); } } }
static void send_now_playing() { gchar *error_code = NULL; gchar *error_detail = NULL; /* * now_playing_track can be set to something else while we this method is * running. Creating a local variable avoids to get data for different tracks, * while now_playing_track was updated concurrently. */ Tuple *curr_track = now_playing_track; gchar *tab_remover; gchar *artist = tuple_get_str(curr_track, FIELD_ARTIST, NULL); gchar *album = tuple_get_str(curr_track, FIELD_ALBUM, NULL); gchar *title = tuple_get_str(curr_track, FIELD_TITLE, NULL); tab_remover = remove_tabs(artist); str_unref(artist); artist = tab_remover; tab_remover = remove_tabs(album); str_unref(album); album = tab_remover; tab_remover = remove_tabs(title); str_unref(title); title = tab_remover; tab_remover = NULL; gchar *number = g_strdup_printf("%i", tuple_get_int(curr_track, FIELD_TRACK_NUMBER, NULL)); gchar *length = g_strdup_printf("%i", tuple_get_int(curr_track, FIELD_LENGTH, NULL) / 1000); tuple_unref(curr_track); if (artist != NULL && strlen(artist) > 0 && title != NULL && strlen(title) > 0) { gchar *playingmsg = create_message_to_lastfm("track.updateNowPlaying", 7, "artist", artist, "album", (album == NULL ? "" : album), "track", title, "trackNumber", number, "duration", length, "api_key", SCROBBLER_API_KEY, "sk", session_key); g_free(artist); g_free(album); g_free(title); g_free(number); g_free(length); bool_t success = send_message_to_lastfm(playingmsg); g_free(playingmsg); if (success == FALSE) { AUDDBG("Network problems. Could not send \"now playing\" to last.fm\n"); scrobbling_enabled = FALSE; return; } if (read_scrobble_result(&error_code, &error_detail) == TRUE) { //see scrobble_cached_queue() AUDDBG("NOW PLAYING OK.\n"); } else { AUDDBG("NOW PLAYING NOT OK. Error code: %s. Error detail: %s.\n", error_code, error_detail); //From the API: Now Playing requests that fail should not be retried. if (g_strcmp0(error_code, "9") == 0) { //Bad Session. Reauth. //We don't really care about any other errors. scrobbling_enabled = FALSE; g_free(session_key); session_key = NULL; aud_set_string("scrobbler", "session_key", ""); } } g_free(error_code); g_free(error_detail); //We don't care if the now playing was not accepted, no need to read the result from the server. } }
static void test_iterator_restore_after_insertion() { header(); plan(1); /* Create key_def */ uint32_t fields[] = { 0 }; uint32_t types[] = { FIELD_TYPE_UNSIGNED }; struct key_def *key_def = box_key_def_new(fields, types, 1); assert(key_def != NULL); /* Create format */ struct tuple_format *format = vy_stmt_format_new(&stmt_env, &key_def, 1, NULL, 0, 0, NULL); assert(format != NULL); tuple_format_ref(format); /* Create lsregion */ struct lsregion lsregion; struct slab_cache *slab_cache = cord_slab_cache(); lsregion_create(&lsregion, slab_cache->arena); struct vy_entry select_key = vy_entry_key_new(stmt_env.key_format, key_def, NULL, 0); struct mempool history_node_pool; mempool_create(&history_node_pool, cord_slab_cache(), sizeof(struct vy_history_node)); uint64_t restore_on_value = 20; uint64_t restore_on_value_reverse = 60; char data[16]; char *end = data; end = mp_encode_array(end, 1); end = mp_encode_uint(end, restore_on_value); struct vy_entry restore_on_key; restore_on_key.stmt = vy_stmt_new_replace(format, data, end); restore_on_key.hint = vy_stmt_hint(restore_on_key.stmt, key_def); vy_stmt_set_lsn(restore_on_key.stmt, 100); end = data; end = mp_encode_array(end, 1); end = mp_encode_uint(end, restore_on_value_reverse); struct vy_entry restore_on_key_reverse; restore_on_key_reverse.stmt = vy_stmt_new_replace(format, data, end); restore_on_key_reverse.hint = vy_stmt_hint(restore_on_key_reverse.stmt, key_def); vy_stmt_set_lsn(restore_on_key_reverse.stmt, 100); bool wrong_output = false; int i_fail = 0; for (uint64_t i = 0; i < ((1000ULL * 3) << 2); i++) { uint64_t v = i; bool direct = !(v & 1); v >>= 1; bool has40_50 = v & 1; v >>= 1; bool has40_150 = v & 1; v >>= 1; const size_t possible_count = 9; uint64_t middle_value = possible_count / 2 * 10; /* 40 */ bool hasX_100[possible_count]; /* X = 0,10,20,30,40,50,60,70,80 */ bool addX_100[possible_count]; /* X = 0,10,20,30,40,50,60,70,80 */ bool add_smth = false; for (size_t j = 0; j < possible_count; j++) { uint64_t trinity = v % 3; v /= 3; hasX_100[j] = trinity == 1; addX_100[j] = trinity == 2; add_smth = add_smth || addX_100[j]; } if (!add_smth) continue; uint64_t expected_count = 0; uint64_t expected_values[possible_count]; int64_t expected_lsns[possible_count]; if (direct) { for (size_t j = 0; j < possible_count; j++) { if (hasX_100[j]) { expected_values[expected_count] = j * 10; expected_lsns[expected_count] = 100; expected_count++; } else if (j == possible_count / 2 && has40_50) { expected_values[expected_count] = middle_value; expected_lsns[expected_count] = 50; expected_count++; } } } else { for (size_t k = possible_count; k > 0; k--) { size_t j = k - 1; if (hasX_100[j]) { expected_values[expected_count] = j * 10; expected_lsns[expected_count] = 100; expected_count++; } else if (j == possible_count / 2 && has40_50) { expected_values[expected_count] = middle_value; expected_lsns[expected_count] = 50; expected_count++; } } } /* Create mem */ struct vy_mem *mem = create_test_mem(key_def); if (has40_50) { const struct vy_stmt_template temp = STMT_TEMPLATE(50, REPLACE, 40); vy_mem_insert_template(mem, &temp); } if (has40_150) { const struct vy_stmt_template temp = STMT_TEMPLATE(150, REPLACE, 40); vy_mem_insert_template(mem, &temp); } for (size_t j = 0; j < possible_count; j++) { if (hasX_100[j]) { const struct vy_stmt_template temp = STMT_TEMPLATE(100, REPLACE, j * 10); vy_mem_insert_template(mem, &temp); } } struct vy_mem_iterator itr; struct vy_mem_iterator_stat stats = {0, {0, 0}}; struct vy_read_view rv; rv.vlsn = 100; const struct vy_read_view *prv = &rv; vy_mem_iterator_open(&itr, &stats, mem, direct ? ITER_GE : ITER_LE, select_key, &prv); struct vy_entry e; struct vy_history history; vy_history_create(&history, &history_node_pool); int rc = vy_mem_iterator_next(&itr, &history); e = vy_history_last_stmt(&history); assert(rc == 0); size_t j = 0; while (e.stmt != NULL) { if (j >= expected_count) { wrong_output = true; break; } uint32_t val = 42; tuple_field_u32(e.stmt, 0, &val); if (val != expected_values[j] || vy_stmt_lsn(e.stmt) != expected_lsns[j]) { wrong_output = true; break; } j++; if (direct && val >= middle_value) break; else if(!direct && val <= middle_value) break; int rc = vy_mem_iterator_next(&itr, &history); e = vy_history_last_stmt(&history); assert(rc == 0); } if (e.stmt == NULL && j != expected_count) wrong_output = true; if (wrong_output) { i_fail = i; break; } for (size_t j = 0; j < possible_count; j++) { if (addX_100[j]) { const struct vy_stmt_template temp = STMT_TEMPLATE(100, REPLACE, j * 10); vy_mem_insert_template(mem, &temp); } } expected_count = 0; if (direct) { for (size_t j = 0; j < possible_count; j++) { if (j * 10 <= restore_on_value) continue; if (hasX_100[j] || addX_100[j]) { expected_values[expected_count] = j * 10; expected_lsns[expected_count] = 100; expected_count++; } else if (j == possible_count / 2 && has40_50) { expected_values[expected_count] = middle_value; expected_lsns[expected_count] = 50; expected_count++; } } } else { for (size_t k = possible_count; k > 0; k--) { size_t j = k - 1; if (j * 10 >= restore_on_value_reverse) continue; if (hasX_100[j] || addX_100[j]) { expected_values[expected_count] = j * 10; expected_lsns[expected_count] = 100; expected_count++; } else if (j == possible_count / 2 && has40_50) { expected_values[expected_count] = middle_value; expected_lsns[expected_count] = 50; expected_count++; } } } if (direct) rc = vy_mem_iterator_restore(&itr, restore_on_key, &history); else rc = vy_mem_iterator_restore(&itr, restore_on_key_reverse, &history); e = vy_history_last_stmt(&history); j = 0; while (e.stmt != NULL) { if (j >= expected_count) { wrong_output = true; break; } uint32_t val = 42; tuple_field_u32(e.stmt, 0, &val); if (val != expected_values[j] || vy_stmt_lsn(e.stmt) != expected_lsns[j]) { wrong_output = true; break; } j++; int rc = vy_mem_iterator_next(&itr, &history); e = vy_history_last_stmt(&history); assert(rc == 0); } if (j != expected_count) wrong_output = true; if (wrong_output) { i_fail = i; break; } vy_history_cleanup(&history); vy_mem_delete(mem); lsregion_gc(&lsregion, 2); } ok(!wrong_output, "check wrong_output %d", i_fail); /* Clean up */ mempool_destroy(&history_node_pool); tuple_unref(select_key.stmt); tuple_unref(restore_on_key.stmt); tuple_unref(restore_on_key_reverse.stmt); tuple_format_unref(format); lsregion_destroy(&lsregion); key_def_delete(key_def); fiber_gc(); check_plan(); footer(); }