diff options
Diffstat (limited to 'apps')
-rw-r--r-- | apps/buffering.c | 62 |
1 files changed, 37 insertions, 25 deletions
diff --git a/apps/buffering.c b/apps/buffering.c index dd08b6ce78..35e47fe51e 100644 --- a/apps/buffering.c +++ b/apps/buffering.c | |||
@@ -377,37 +377,43 @@ static struct memory_handle *find_handle(const unsigned int handle_id) | |||
377 | return m; | 377 | return m; |
378 | } | 378 | } |
379 | 379 | ||
380 | /* Move a memory handle and data_size of its data of delta. | 380 | /* Move a memory handle and data_size of its data delta bytes along the buffer. |
381 | Return a pointer to the new location of the handle (null if it hasn't moved). | 381 | delta maximum bytes available to move the handle. If the move is performed |
382 | delta is the value of which to move the struct data, modified to the actual | 382 | it is set to the actual distance moved. |
383 | distance moved. | 383 | data_size is the amount of data to move along with the struct. |
384 | data_size is the amount of data to move along with the struct. */ | 384 | returns a valid memory_handle if the move is successful |
385 | NULL if the handle is NULL, the move would be less than the size of | ||
386 | a memory_handle after correcting for wraps or if the handle is not | ||
387 | found in the linked list for adjustment. This function has no side | ||
388 | effects if NULL is returned. */ | ||
385 | static struct memory_handle *move_handle(const struct memory_handle *h, | 389 | static struct memory_handle *move_handle(const struct memory_handle *h, |
386 | size_t *delta, const size_t data_size) | 390 | size_t *delta, const size_t data_size) |
387 | { | 391 | { |
388 | struct memory_handle *dest; | 392 | struct memory_handle *dest; |
389 | size_t newpos; | 393 | size_t newpos; |
390 | size_t size_to_move; | 394 | size_t size_to_move; |
395 | size_t new_delta = *delta; | ||
391 | int overlap; | 396 | int overlap; |
392 | 397 | ||
393 | if (*delta < sizeof(struct memory_handle)) { | 398 | if (h == NULL) |
394 | /* It's not worth trying to move such a short distance, and it would | ||
395 | * complicate the overlap calculations below */ | ||
396 | return NULL; | 399 | return NULL; |
397 | } | ||
398 | |||
399 | mutex_lock(&llist_mutex); | ||
400 | 400 | ||
401 | size_to_move = sizeof(struct memory_handle) + data_size; | 401 | size_to_move = sizeof(struct memory_handle) + data_size; |
402 | 402 | ||
403 | /* Align to four bytes, down */ | 403 | /* Align to four bytes, down */ |
404 | *delta &= ~3; | 404 | new_delta &= ~3; |
405 | newpos = RINGBUF_ADD((void *)h - (void *)buffer, *delta); | 405 | if (new_delta < sizeof(struct memory_handle)) { |
406 | /* It's not legal to move less than the size of the struct */ | ||
407 | return NULL; | ||
408 | } | ||
409 | |||
410 | mutex_lock(&llist_mutex); | ||
411 | |||
412 | newpos = RINGBUF_ADD((void *)h - (void *)buffer, new_delta); | ||
406 | overlap = RINGBUF_ADD_CROSS(newpos, size_to_move, buffer_len - 1); | 413 | overlap = RINGBUF_ADD_CROSS(newpos, size_to_move, buffer_len - 1); |
407 | 414 | ||
408 | /* This means that moving the data will put it on the wrap */ | ||
409 | if (overlap > 0) { | 415 | if (overlap > 0) { |
410 | /* This means that the memory_handle struct would wrap */ | 416 | /* Some part of the struct + data would wrap, maybe ok */ |
411 | size_t correction; | 417 | size_t correction; |
412 | /* If the overlap lands inside the memory_handle */ | 418 | /* If the overlap lands inside the memory_handle */ |
413 | if ((unsigned)overlap > data_size) { | 419 | if ((unsigned)overlap > data_size) { |
@@ -415,19 +421,22 @@ static struct memory_handle *move_handle(const struct memory_handle *h, | |||
415 | * wrapping, this guarantees an aligned delta, I think */ | 421 | * wrapping, this guarantees an aligned delta, I think */ |
416 | correction = overlap - data_size; | 422 | correction = overlap - data_size; |
417 | } else { | 423 | } else { |
418 | /* Otherwise it falls in the data area and must all be backed out */ | 424 | /* Otherwise the overlap falls in the data area and must all be |
425 | * backed out. This may become conditional if ever we move | ||
426 | * data that is allowed to wrap (ie audio) */ | ||
419 | correction = overlap; | 427 | correction = overlap; |
420 | /* Align to four bytes, up */ | 428 | /* Align correction to four bytes, up */ |
421 | correction = (correction+3) & ~3; | 429 | correction = (correction+3) & ~3; |
422 | if (*delta <= correction) { | ||
423 | /* After correcting, no movement (or, impossibly, backwards) */ | ||
424 | mutex_unlock(&llist_mutex); | ||
425 | return NULL; | ||
426 | } | ||
427 | } | 430 | } |
431 | if (new_delta < correction + sizeof(struct memory_handle)) { | ||
432 | /* Delta cannot end up less than the size of the struct */ | ||
433 | mutex_unlock(&llist_mutex); | ||
434 | return NULL; | ||
435 | } | ||
436 | |||
428 | newpos -= correction; | 437 | newpos -= correction; |
429 | overlap -= correction; | 438 | overlap -= correction; /* Used below to know how to split the data */ |
430 | *delta -= correction; | 439 | new_delta -= correction; |
431 | } | 440 | } |
432 | 441 | ||
433 | dest = (struct memory_handle *)(&buffer[newpos]); | 442 | dest = (struct memory_handle *)(&buffer[newpos]); |
@@ -440,7 +449,7 @@ static struct memory_handle *move_handle(const struct memory_handle *h, | |||
440 | while (m && m->next != h) { | 449 | while (m && m->next != h) { |
441 | m = m->next; | 450 | m = m->next; |
442 | } | 451 | } |
443 | if (h && m && m->next == h) { | 452 | if (m && m->next == h) { |
444 | m->next = dest; | 453 | m->next = dest; |
445 | } else { | 454 | } else { |
446 | mutex_unlock(&llist_mutex); | 455 | mutex_unlock(&llist_mutex); |
@@ -448,6 +457,9 @@ static struct memory_handle *move_handle(const struct memory_handle *h, | |||
448 | } | 457 | } |
449 | } | 458 | } |
450 | 459 | ||
460 | /* All checks pass, update the caller with how far we're moving */ | ||
461 | *delta = new_delta; | ||
462 | |||
451 | /* Update the cache to prevent it from keeping the old location of h */ | 463 | /* Update the cache to prevent it from keeping the old location of h */ |
452 | if (h == cached_handle) | 464 | if (h == cached_handle) |
453 | cached_handle = dest; | 465 | cached_handle = dest; |