22
22
#include " Selection.hxx"
23
23
#include " SongFilter.hxx"
24
24
#include " SongPrint.hxx"
25
+ #include " DetachedSong.hxx"
25
26
#include " TimePrint.hxx"
26
27
#include " TagPrint.hxx"
27
28
#include " client/Response.hxx"
@@ -139,10 +140,36 @@ PrintPlaylistFull(Response &r, bool base,
139
140
time_print (r, " Last-Modified" , playlist.mtime );
140
141
}
141
142
143
+ static bool
144
+ CompareNumeric (const char *a, const char *b)
145
+ {
146
+ long a_value = strtol (a, nullptr , 10 );
147
+ long b_value = strtol (b, nullptr , 10 );
148
+
149
+ return a_value < b_value;
150
+ }
151
+
152
+ static bool
153
+ CompareTags (TagType type, const Tag &a, const Tag &b)
154
+ {
155
+ const char *a_value = a.GetSortValue (type);
156
+ const char *b_value = b.GetSortValue (type);
157
+
158
+ switch (type) {
159
+ case TAG_DISC:
160
+ case TAG_TRACK:
161
+ return CompareNumeric (a_value, b_value);
162
+
163
+ default :
164
+ return strcmp (a_value, b_value) < 0 ;
165
+ }
166
+ }
167
+
142
168
void
143
169
db_selection_print (Response &r, Partition &partition,
144
170
const DatabaseSelection &selection,
145
171
bool full, bool base,
172
+ TagType sort,
146
173
unsigned window_start, unsigned window_end)
147
174
{
148
175
const Database &db = partition.GetDatabaseOrThrow ();
@@ -161,16 +188,53 @@ db_selection_print(Response &r, Partition &partition,
161
188
std::ref (r), base, _1, _2)
162
189
: VisitPlaylist ();
163
190
164
- if (window_start > 0 ||
165
- window_end < (unsigned )std::numeric_limits<int >::max ())
166
- s = [s, window_start, window_end, &i](const LightSong &song){
167
- const bool in_window = i >= window_start && i < window_end;
168
- ++i;
169
- if (in_window)
170
- s (song);
171
- };
191
+ if (sort == TAG_NUM_OF_ITEM_TYPES) {
192
+ if (window_start > 0 ||
193
+ window_end < (unsigned )std::numeric_limits<int >::max ())
194
+ s = [s, window_start, window_end, &i](const LightSong &song){
195
+ const bool in_window = i >= window_start && i < window_end;
196
+ ++i;
197
+ if (in_window)
198
+ s (song);
199
+ };
172
200
173
- db.Visit (selection, d, s, p);
201
+ db.Visit (selection, d, s, p);
202
+ } else {
203
+ // TODO: allow the database plugin to sort internally
204
+
205
+ /* the client has asked us to sort the result; this is
206
+ pretty expensive, because instead of streaming the
207
+ result to the client, we need to copy it all into
208
+ this std::vector, and then sort it */
209
+ std::vector<DetachedSong> songs;
210
+
211
+ {
212
+ auto collect_songs = [&songs](const LightSong &song){
213
+ songs.emplace_back (song);
214
+ };
215
+
216
+ db.Visit (selection, d, collect_songs, p);
217
+ }
218
+
219
+ std::stable_sort (songs.begin (), songs.end (),
220
+ [sort](const DetachedSong &a, const DetachedSong &b){
221
+ return CompareTags (sort, a.GetTag (),
222
+ b.GetTag ());
223
+ });
224
+
225
+ if (window_end < songs.size ())
226
+ songs.erase (std::next (songs.begin (), window_end),
227
+ songs.end ());
228
+
229
+ if (window_start >= songs.size ())
230
+ return ;
231
+
232
+ songs.erase (songs.begin (),
233
+ std::next (songs.begin (), window_start));
234
+
235
+ for (const auto &song : songs)
236
+ s ((LightSong)song);
237
+ }
174
238
}
175
239
176
240
void
@@ -179,6 +243,7 @@ db_selection_print(Response &r, Partition &partition,
179
243
bool full, bool base)
180
244
{
181
245
db_selection_print (r, partition, selection, full, base,
246
+ TAG_NUM_OF_ITEM_TYPES,
182
247
0 , std::numeric_limits<int >::max ());
183
248
}
184
249
0 commit comments