1 /*****************************************************************************
2 * list.c
3 *****************************************************************************
4 * Copyright (C) 2010-2017 L-SMASH project
5 *
6 * Authors: Yusuke Nakamura <muken.the.vfrmaniac@gmail.com>
7 *
8 * Permission to use, copy, modify, and/or distribute this software for any
9 * purpose with or without fee is hereby granted, provided that the above
10 * copyright notice and this permission notice appear in all copies.
11 *
12 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
13 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
14 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
15 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
16 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
17 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
18 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
19 *****************************************************************************/
20
21 /* This file is available under an ISC license. */
22
23 #include "internal.h" /* must be placed first */
24
25 #include <string.h>
26
lsmash_list_clear(lsmash_entry_list_t * list)27 static void lsmash_list_clear
28 (
29 lsmash_entry_list_t *list
30 )
31 {
32 list->head = NULL;
33 list->tail = NULL;
34 list->last_accessed_entry = NULL;
35 list->last_accessed_number = 0;
36 list->entry_count = 0;
37 list->eliminator = NULL;
38 }
39
lsmash_list_init_orig(lsmash_entry_list_t * list,lsmash_entry_data_eliminator eliminator)40 void lsmash_list_init_orig
41 (
42 lsmash_entry_list_t *list,
43 lsmash_entry_data_eliminator eliminator
44 )
45 {
46 assert( eliminator != NULL );
47 list->head = NULL;
48 list->tail = NULL;
49 list->last_accessed_entry = NULL;
50 list->last_accessed_number = 0;
51 list->entry_count = 0;
52 list->eliminator = eliminator;
53 }
54
lsmash_list_create_orig(lsmash_entry_data_eliminator eliminator)55 lsmash_entry_list_t *lsmash_list_create_orig
56 (
57 lsmash_entry_data_eliminator eliminator
58 )
59 {
60 lsmash_entry_list_t *list = lsmash_malloc( sizeof(lsmash_entry_list_t) );
61 if( !list )
62 return NULL;
63 lsmash_list_init( list, eliminator );
64 return list;
65 }
66
lsmash_list_destroy(lsmash_entry_list_t * list)67 void lsmash_list_destroy
68 (
69 lsmash_entry_list_t *list
70 )
71 {
72 lsmash_list_remove_entries( list );
73 lsmash_free( list );
74 }
75
lsmash_list_add_entry(lsmash_entry_list_t * list,void * data)76 int lsmash_list_add_entry
77 (
78 lsmash_entry_list_t *list,
79 void *data
80 )
81 {
82 if( !list )
83 return LSMASH_ERR_FUNCTION_PARAM;
84 lsmash_entry_t *entry = lsmash_malloc( sizeof(lsmash_entry_t) );
85 if( !entry )
86 return LSMASH_ERR_MEMORY_ALLOC;
87 entry->next = NULL;
88 entry->prev = list->tail;
89 entry->data = data;
90 if( list->head )
91 list->tail->next = entry;
92 else
93 list->head = entry;
94 list->tail = entry;
95 list->entry_count += 1;
96 return 0;
97 }
98
lsmash_list_remove_entry_direct(lsmash_entry_list_t * list,lsmash_entry_t * entry)99 int lsmash_list_remove_entry_direct
100 (
101 lsmash_entry_list_t *list,
102 lsmash_entry_t *entry
103 )
104 {
105 if( !list || !entry )
106 return LSMASH_ERR_FUNCTION_PARAM;
107 assert( !entry->data || list->eliminator );
108 lsmash_entry_t *next = entry->next;
109 lsmash_entry_t *prev = entry->prev;
110 if( entry == list->head )
111 list->head = next;
112 else
113 prev->next = next;
114 if( entry == list->tail )
115 list->tail = prev;
116 else
117 next->prev = prev;
118 if( entry->data )
119 list->eliminator( entry->data );
120 if( entry == list->last_accessed_entry )
121 {
122 if( next )
123 list->last_accessed_entry = next;
124 else if( prev )
125 {
126 list->last_accessed_entry = prev;
127 list->last_accessed_number -= 1;
128 }
129 else
130 {
131 list->last_accessed_entry = NULL;
132 list->last_accessed_number = 0;
133 }
134 }
135 else
136 {
137 /* We can't know the current entry number immediately,
138 * so discard the last accessed entry info because time is wasted to know it. */
139 list->last_accessed_entry = NULL;
140 list->last_accessed_number = 0;
141 }
142 lsmash_free( entry );
143 list->entry_count -= 1;
144 return 0;
145 }
146
lsmash_list_remove_entry(lsmash_entry_list_t * list,uint32_t entry_number)147 int lsmash_list_remove_entry
148 (
149 lsmash_entry_list_t *list,
150 uint32_t entry_number
151 )
152 {
153 lsmash_entry_t *entry = lsmash_list_get_entry( list, entry_number );
154 return lsmash_list_remove_entry_direct( list, entry );
155 }
156
lsmash_list_remove_entry_tail(lsmash_entry_list_t * list)157 int lsmash_list_remove_entry_tail
158 (
159 lsmash_entry_list_t *list
160 )
161 {
162 return lsmash_list_remove_entry_direct( list, list->tail );
163 }
164
lsmash_list_remove_entries(lsmash_entry_list_t * list)165 void lsmash_list_remove_entries
166 (
167 lsmash_entry_list_t *list
168 )
169 {
170 if( !list )
171 return;
172 /* Note that it's valid that list contain no entries or no data to be eliminated. */
173 for( lsmash_entry_t *entry = list->head; entry; )
174 {
175 lsmash_entry_t *next = entry->next;
176 if( entry->data )
177 list->eliminator( entry->data );
178 lsmash_free( entry );
179 entry = next;
180 }
181 lsmash_entry_data_eliminator eliminator = list->eliminator;
182 lsmash_list_clear( list );
183 list->eliminator = eliminator;
184 }
185
lsmash_list_move_entries(lsmash_entry_list_t * dst,lsmash_entry_list_t * src)186 void lsmash_list_move_entries
187 (
188 lsmash_entry_list_t *dst,
189 lsmash_entry_list_t *src
190 )
191 {
192 *dst = *src;
193 lsmash_entry_data_eliminator eliminator = src->eliminator;
194 lsmash_list_clear( src );
195 src->eliminator = eliminator;
196 }
197
lsmash_list_get_entry(lsmash_entry_list_t * list,uint32_t entry_number)198 lsmash_entry_t *lsmash_list_get_entry
199 (
200 lsmash_entry_list_t *list,
201 uint32_t entry_number
202 )
203 {
204 if( !list || !entry_number || entry_number > list->entry_count )
205 return NULL;
206 int shortcut = 1;
207 lsmash_entry_t *entry = NULL;
208 if( list->last_accessed_entry )
209 {
210 if( entry_number == list->last_accessed_number )
211 entry = list->last_accessed_entry;
212 else if( entry_number == list->last_accessed_number + 1 )
213 entry = list->last_accessed_entry->next;
214 else if( entry_number == list->last_accessed_number - 1 )
215 entry = list->last_accessed_entry->prev;
216 else
217 shortcut = 0;
218 }
219 else
220 shortcut = 0;
221 if( !shortcut )
222 {
223 if( entry_number <= (list->entry_count >> 1) )
224 {
225 /* Look for from the head. */
226 uint32_t distance_plus_one = entry_number;
227 for( entry = list->head; entry && --distance_plus_one; entry = entry->next );
228 }
229 else
230 {
231 /* Look for from the tail. */
232 uint32_t distance = list->entry_count - entry_number;
233 for( entry = list->tail; entry && distance--; entry = entry->prev );
234 }
235 }
236 if( entry )
237 {
238 list->last_accessed_entry = entry;
239 list->last_accessed_number = entry_number;
240 }
241 return entry;
242 }
243
lsmash_list_get_entry_data(lsmash_entry_list_t * list,uint32_t entry_number)244 void *lsmash_list_get_entry_data
245 (
246 lsmash_entry_list_t *list,
247 uint32_t entry_number
248 )
249 {
250 lsmash_entry_t *entry = lsmash_list_get_entry( list, entry_number );
251 return entry ? entry->data : NULL;
252 }
253