From ee2e73cd7b5a1de68c8316e916c4ef3a88302bed Mon Sep 17 00:00:00 2001 From: Lars-Dominik Braun Date: Sun, 4 Aug 2013 18:53:43 +0200 Subject: piano: Generic linked lists MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Introduces generic linked list structure and functions (like append, delete, …). Removes a lot of copy&pasted code and improves code readability/reusability. Heads up: This change breaks libpiano’s ABI. --- src/libpiano/list.c | 112 +++++++++++++++++++++++++++++++++ src/libpiano/piano.c | 27 ++++---- src/libpiano/piano.h | 42 ++++++++++--- src/libpiano/request.c | 6 +- src/libpiano/response.c | 163 +++++++++--------------------------------------- 5 files changed, 194 insertions(+), 156 deletions(-) create mode 100644 src/libpiano/list.c (limited to 'src/libpiano') diff --git a/src/libpiano/list.c b/src/libpiano/list.c new file mode 100644 index 0000000..2e83ab0 --- /dev/null +++ b/src/libpiano/list.c @@ -0,0 +1,112 @@ +/* +Copyright (c) 2013 + Lars-Dominik Braun + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. +*/ + +#include + +#include "piano.h" + +#define PianoListForeach(l) for (; (l) != NULL; (l) = (void *) (l)->next) + +/* append element e to list l, return new list head + */ +void *PianoListAppend (PianoListHead_t * const l, PianoListHead_t * const e) { + assert (e != NULL); + assert (e->next == NULL); + + if (l == NULL) { + return e; + } else { + PianoListHead_t *curr = l; + while (curr->next != NULL) { + curr = curr->next; + } + curr->next = e; + return l; + } +} + +/* prepend element e to list l, returning new list head + */ +void *PianoListPrepend (PianoListHead_t * const l, PianoListHead_t * const e) { + assert (e != NULL); + assert (e->next == NULL); + + e->next = l; + return e; +} + +/* delete element e from list l, return new list head + */ +void *PianoListDelete (PianoListHead_t * const l, PianoListHead_t * const e) { + assert (l != NULL); + assert (e != NULL); + + PianoListHead_t *first = l, *curr = l, *prev = NULL; + PianoListForeach (curr) { + if (curr == e) { + /* found it! */ + if (prev != NULL) { + prev->next = curr->next; + } else { + /* no successor */ + first = curr->next; + } + break; + } + prev = curr; + } + + return first; +} + +/* get nth element of list + */ +void *PianoListGet (PianoListHead_t * const l, const size_t n) { + PianoListHead_t *curr = l; + size_t i = n; + + PianoListForeach (curr) { + if (i == 0) { + return curr; + } + --i; + } + + return NULL; +} + +/* count elements in list l + */ +size_t PianoListCount (const PianoListHead_t * const l) { + assert (l != NULL); + + size_t count = 0; + const PianoListHead_t *curr = l; + + PianoListForeach (curr) { + ++count; + } + + return count; +} + diff --git a/src/libpiano/piano.c b/src/libpiano/piano.c index 9020a6a..b519f49 100644 --- a/src/libpiano/piano.c +++ b/src/libpiano/piano.c @@ -1,5 +1,5 @@ /* -Copyright (c) 2008-2012 +Copyright (c) 2008-2013 Lars-Dominik Braun Permission is hereby granted, free of charge, to any person obtaining a copy @@ -80,7 +80,7 @@ static void PianoDestroyArtists (PianoArtist_t *artists) { free (curArtist->musicId); free (curArtist->seedId); lastArtist = curArtist; - curArtist = curArtist->next; + curArtist = (PianoArtist_t *) curArtist->head.next; free (lastArtist); } } @@ -113,7 +113,7 @@ static void PianoDestroyStations (PianoStation_t *stations) { curStation = stations; while (curStation != NULL) { lastStation = curStation; - curStation = curStation->next; + curStation = (PianoStation_t *) curStation->head.next; PianoDestroyStation (lastStation); free (lastStation); } @@ -141,7 +141,7 @@ void PianoDestroyPlaylist (PianoSong_t *playlist) { free (curSong->detailUrl); free (curSong->trackToken); lastSong = curSong; - curSong = curSong->next; + curSong = (PianoSong_t *) curSong->head.next; free (lastSong); } } @@ -163,7 +163,7 @@ static void PianoDestroyGenres (PianoGenre_t *genres) { free (curGenre->name); free (curGenre->musicId); lastGenre = curGenre; - curGenre = curGenre->next; + curGenre = (PianoGenre_t *) curGenre->head.next; free (lastGenre); } } @@ -201,7 +201,7 @@ void PianoDestroy (PianoHandle_t *ph) { PianoDestroyGenres (curGenreCat->genres); free (curGenreCat->name); lastGenreCat = curGenreCat; - curGenreCat = curGenreCat->next; + curGenreCat = (PianoGenreCategory_t *) curGenreCat->head.next; free (lastGenreCat); } memset (ph, 0, sizeof (*ph)); @@ -221,14 +221,17 @@ void PianoDestroyRequest (PianoRequest_t *req) { * @param search for this * @return the first station structure matching the given id */ -PianoStation_t *PianoFindStationById (PianoStation_t *stations, - const char *searchStation) { - while (stations != NULL) { - if (strcmp (stations->id, searchStation) == 0) { - return stations; +PianoStation_t *PianoFindStationById (PianoStation_t * const stations, + const char * const searchStation) { + assert (searchStation != NULL); + + PianoStation_t *currStation = stations; + PianoListForeachP (currStation) { + if (strcmp (currStation->id, searchStation) == 0) { + return currStation; } - stations = stations->next; } + return NULL; } diff --git a/src/libpiano/piano.h b/src/libpiano/piano.h index 04f33b5..ce66171 100644 --- a/src/libpiano/piano.h +++ b/src/libpiano/piano.h @@ -1,5 +1,5 @@ /* -Copyright (c) 2008-2011 +Copyright (c) 2008-2013 Lars-Dominik Braun Permission is hereby granted, free of charge, to any person obtaining a copy @@ -42,19 +42,23 @@ THE SOFTWARE. #define PIANO_RPC_HOST "tuner.pandora.com" #define PIANO_RPC_PATH "/services/json/?" +typedef struct PianoListHead { + struct PianoListHead *next; +} PianoListHead_t; + typedef struct PianoUserInfo { char *listenerId; char *authToken; } PianoUserInfo_t; typedef struct PianoStation { + PianoListHead_t head; char isCreator; char isQuickMix; char useQuickMix; /* station will be included in quickmix */ char *name; char *id; char *seedId; - struct PianoStation *next; } PianoStation_t; typedef enum { @@ -78,6 +82,7 @@ typedef enum { } PianoAudioQuality_t; typedef struct PianoSong { + PianoListHead_t head; char *artist; char *stationId; char *album; @@ -92,28 +97,27 @@ typedef struct PianoSong { float fileGain; PianoSongRating_t rating; PianoAudioFormat_t audioFormat; - struct PianoSong *next; } PianoSong_t; /* currently only used for search results */ typedef struct PianoArtist { + PianoListHead_t head; char *name; char *musicId; char *seedId; int score; - struct PianoArtist *next; } PianoArtist_t; typedef struct PianoGenre { + PianoListHead_t head; char *name; char *musicId; - struct PianoGenre *next; } PianoGenre_t; typedef struct PianoGenreCategory { + PianoListHead_t head; char *name; PianoGenre_t *genres; - struct PianoGenreCategory *next; } PianoGenreCategory_t; typedef struct PianoPartner { @@ -298,6 +302,27 @@ typedef enum { PIANO_RET_P_RATE_LIMIT = PIANO_RET_OFFSET+1039, } PianoReturn_t; +/* list stuff */ +#ifndef __GNUC__ +# define __attribute__(x) +#endif +size_t PianoListCount (const PianoListHead_t * const l); +#define PianoListCountP(l) PianoListCount(&(l)->head) +void *PianoListAppend (PianoListHead_t * const l, PianoListHead_t * const e) + __attribute__ ((warn_unused_result)); +#define PianoListAppendP(l,e) PianoListAppend(&(l)->head, &(e)->head) +void *PianoListDelete (PianoListHead_t * const l, PianoListHead_t * const e) + __attribute__ ((warn_unused_result)); +#define PianoListDeleteP(l,e) PianoListDelete(&(l)->head, &(e)->head) +#define PianoListNextP(e) ((void *) (e)->head.next) +void *PianoListPrepend (PianoListHead_t * const l, PianoListHead_t * const e) + __attribute__ ((warn_unused_result)); +#define PianoListPrependP(l,e) PianoListPrepend (&(l)->head, &(e)->head) +void *PianoListGet (PianoListHead_t * const l, const size_t n); +#define PianoListGetP(l,n) PianoListGet (&(l)->head, n) +#define PianoListForeachP(l) for (; (l) != NULL; (l) = (void *) (l)->head.next) + +/* memory management */ PianoReturn_t PianoInit (PianoHandle_t *, const char *, const char *, const char *, const char *, const char *); @@ -306,12 +331,15 @@ void PianoDestroyPlaylist (PianoSong_t *); void PianoDestroySearchResult (PianoSearchResult_t *); void PianoDestroyStationInfo (PianoStationInfo_t *); +/* pandora rpc */ PianoReturn_t PianoRequest (PianoHandle_t *, PianoRequest_t *, PianoRequestType_t); PianoReturn_t PianoResponse (PianoHandle_t *, PianoRequest_t *); void PianoDestroyRequest (PianoRequest_t *); -PianoStation_t *PianoFindStationById (PianoStation_t *, const char *); +/* misc */ +PianoStation_t *PianoFindStationById (PianoStation_t * const, + const char * const); const char *PianoErrorToStr (PianoReturn_t); #endif /* _PIANO_H */ diff --git a/src/libpiano/request.c b/src/libpiano/request.c index 85d6286..82bf350 100644 --- a/src/libpiano/request.c +++ b/src/libpiano/request.c @@ -1,5 +1,5 @@ /* -Copyright (c) 2008-2012 +Copyright (c) 2008-2013 Lars-Dominik Braun Permission is hereby granted, free of charge, to any person obtaining a copy @@ -272,14 +272,12 @@ PianoReturn_t PianoRequest (PianoHandle_t *ph, PianoRequest_t *req, PianoStation_t *curStation = ph->stations; json_object *a = json_object_new_array (); - while (curStation != NULL) { + PianoListForeachP (curStation) { /* quick mix can't contain itself */ if (curStation->useQuickMix && !curStation->isQuickMix) { json_object_array_add (a, json_object_new_string (curStation->id)); } - - curStation = curStation->next; } json_object_object_add (j, "quickMixStationIds", a); diff --git a/src/libpiano/response.c b/src/libpiano/response.c index ae7b140..41bbfd3 100644 --- a/src/libpiano/response.c +++ b/src/libpiano/response.c @@ -1,5 +1,5 @@ /* -Copyright (c) 2008-2012 +Copyright (c) 2008-2013 Lars-Dominik Braun Permission is hereby granted, free of charge, to any person obtaining a copy @@ -193,21 +193,13 @@ PianoReturn_t PianoResponse (PianoHandle_t *ph, PianoRequest_t *req) { } /* start new linked list or append */ - if (ph->stations == NULL) { - ph->stations = tmpStation; - } else { - PianoStation_t *curStation = ph->stations; - while (curStation->next != NULL) { - curStation = curStation->next; - } - curStation->next = tmpStation; - } + ph->stations = PianoListAppendP (ph->stations, tmpStation); } /* fix quickmix flags */ if (mix != NULL) { PianoStation_t *curStation = ph->stations; - while (curStation != NULL) { + PianoListForeachP (curStation) { for (size_t i = 0; i < json_object_array_length (mix); i++) { json_object *id = json_object_array_get_idx (mix, i); if (strcmp (json_object_get_string (id), @@ -215,7 +207,6 @@ PianoReturn_t PianoResponse (PianoHandle_t *ph, PianoRequest_t *req) { curStation->useQuickMix = true; } } - curStation = curStation->next; } } break; @@ -293,16 +284,7 @@ PianoReturn_t PianoResponse (PianoHandle_t *ph, PianoRequest_t *req) { break; } - /* begin linked list or append */ - if (playlist == NULL) { - playlist = song; - } else { - PianoSong_t *curSong = playlist; - while (curSong->next != NULL) { - curSong = curSong->next; - } - curSong->next = song; - } + playlist = PianoListAppendP (playlist, song); } reqData->retPlaylist = playlist; @@ -340,23 +322,9 @@ PianoReturn_t PianoResponse (PianoHandle_t *ph, PianoRequest_t *req) { assert (station != NULL); - /* delete station from local station list */ - PianoStation_t *curStation = ph->stations, *lastStation = NULL; - while (curStation != NULL) { - if (curStation == station) { - if (lastStation != NULL) { - lastStation->next = curStation->next; - } else { - /* first station in list */ - ph->stations = curStation->next; - } - PianoDestroyStation (curStation); - free (curStation); - break; - } - lastStation = curStation; - curStation = curStation->next; - } + ph->stations = PianoListDeleteP (ph->stations, station); + PianoDestroyStation (station); + free (station); break; } @@ -385,16 +353,8 @@ PianoReturn_t PianoResponse (PianoHandle_t *ph, PianoRequest_t *req) { artist->name = PianoJsonStrdup (a, "artistName"); artist->musicId = PianoJsonStrdup (a, "musicToken"); - /* add result to linked list */ - if (searchResult->artists == NULL) { - searchResult->artists = artist; - } else { - PianoArtist_t *curArtist = searchResult->artists; - while (curArtist->next != NULL) { - curArtist = curArtist->next; - } - curArtist->next = artist; - } + searchResult->artists = + PianoListAppendP (searchResult->artists, artist); } } @@ -413,16 +373,8 @@ PianoReturn_t PianoResponse (PianoHandle_t *ph, PianoRequest_t *req) { song->artist = PianoJsonStrdup (s, "artistName"); song->musicId = PianoJsonStrdup (s, "musicToken"); - /* add result to linked list */ - if (searchResult->songs == NULL) { - searchResult->songs = song; - } else { - PianoSong_t *curSong = searchResult->songs; - while (curSong->next != NULL) { - curSong = curSong->next; - } - curSong->next = song; - } + searchResult->songs = + PianoListAppendP (searchResult->songs, song); } } break; @@ -438,32 +390,14 @@ PianoReturn_t PianoResponse (PianoHandle_t *ph, PianoRequest_t *req) { PianoJsonParseStation (result, tmpStation); - if (ph->stations == NULL) { - ph->stations = tmpStation; - } else { - PianoStation_t *curStation = ph->stations, *prevStation = NULL; - while (curStation->next != NULL) { - /* replace if station with same id exists already */ - if (strcmp (curStation->id, tmpStation->id) == 0) { - if (prevStation == NULL) { - ph->stations = tmpStation; - } else { - prevStation->next = tmpStation; - } - tmpStation->next = curStation->next; - - PianoDestroyStation (curStation); - free (curStation); - break; - } - prevStation = curStation; - curStation = curStation->next; - } - /* append otherwise */ - if (tmpStation->next == NULL) { - curStation->next = tmpStation; - } + PianoStation_t *search = PianoFindStationById (ph->stations, + tmpStation->id); + if (search != NULL) { + ph->stations = PianoListDeleteP (ph->stations, search); + PianoDestroyStation (search); + free (search); } + ph->stations = PianoListAppendP (ph->stations, tmpStation); break; } @@ -514,29 +448,14 @@ PianoReturn_t PianoResponse (PianoHandle_t *ph, PianoRequest_t *req) { tmpGenre->musicId = PianoJsonStrdup (s, "stationToken"); - /* append station */ - if (tmpGenreCategory->genres == NULL) { - tmpGenreCategory->genres = tmpGenre; - } else { - PianoGenre_t *curGenre = - tmpGenreCategory->genres; - while (curGenre->next != NULL) { - curGenre = curGenre->next; - } - curGenre->next = tmpGenre; - } - } - } - /* append category */ - if (ph->genreStations == NULL) { - ph->genreStations = tmpGenreCategory; - } else { - PianoGenreCategory_t *curCat = ph->genreStations; - while (curCat->next != NULL) { - curCat = curCat->next; + tmpGenreCategory->genres = + PianoListAppendP (tmpGenreCategory->genres, + tmpGenre); } - curCat->next = tmpGenreCategory; } + + ph->genreStations = PianoListAppendP (ph->genreStations, + tmpGenreCategory); } } break; @@ -615,15 +534,8 @@ PianoReturn_t PianoResponse (PianoHandle_t *ph, PianoRequest_t *req) { seedSong->artist = PianoJsonStrdup (s, "artistName"); seedSong->seedId = PianoJsonStrdup (s, "seedId"); - if (info->songSeeds == NULL) { - info->songSeeds = seedSong; - } else { - PianoSong_t *curSong = info->songSeeds; - while (curSong->next != NULL) { - curSong = curSong->next; - } - curSong->next = seedSong; - } + info->songSeeds = PianoListAppendP (info->songSeeds, + seedSong); } } @@ -643,15 +555,8 @@ PianoReturn_t PianoResponse (PianoHandle_t *ph, PianoRequest_t *req) { seedArtist->name = PianoJsonStrdup (a, "artistName"); seedArtist->seedId = PianoJsonStrdup (a, "seedId"); - if (info->artistSeeds == NULL) { - info->artistSeeds = seedArtist; - } else { - PianoArtist_t *curArtist = info->artistSeeds; - while (curArtist->next != NULL) { - curArtist = curArtist->next; - } - curArtist->next = seedArtist; - } + info->artistSeeds = + PianoListAppendP (info->artistSeeds, seedArtist); } } } @@ -679,16 +584,8 @@ PianoReturn_t PianoResponse (PianoHandle_t *ph, PianoRequest_t *req) { json_object_object_get (s, "isPositive")) ? PIANO_RATE_LOVE : PIANO_RATE_BAN; - - if (info->feedback == NULL) { - info->feedback = feedbackSong; - } else { - PianoSong_t *curSong = info->feedback; - while (curSong->next != NULL) { - curSong = curSong->next; - } - curSong->next = feedbackSong; - } + info->feedback = PianoListAppendP (info->feedback, + feedbackSong); } } } -- cgit v1.2.3