ref: 0b2ef7963a59ea118b7d41b487814a7ba9c0661c
parent: 7aa5be9878eb81fb1001ed1e3f2c35fbdc4f6edb
author: Timothy B. Terriberry <tterribe@xiph.org>
date: Tue Jul 15 09:43:21 EDT 2025
RTE: Eliminate recursion. Any decent compiler would have turned this recursion into a tail call that consumed no additional stack, but one might not always have a decent compiler. Signed-off-by: Jean-Marc Valin <jeanmarcv@google.com>
--- a/src/extensions.c
+++ b/src/extensions.c
@@ -151,6 +151,74 @@
iter->frame_max = frame_max;
}
+/* Return the next repeated extension.
+ The return value is non-zero if one is found, negative on error, or 0 if we
+ have finished repeating extensions. */
+static int opus_extension_iterator_next_repeat(OpusExtensionIterator *iter,
+ opus_extension_data *ext) {
+ opus_int32 header_size;
+ celt_assert(iter->repeat_frame > 0);
+ for (;iter->repeat_frame < iter->nb_frames; iter->repeat_frame++) {
+ while (iter->src_len > 0) {
+ const unsigned char *curr_data0;
+ int repeat_id_byte;
+ repeat_id_byte = *iter->src_data;
+ iter->src_len = skip_extension(&iter->src_data, iter->src_len,
+ &header_size);
+ /* We skipped this extension earlier, so it should not fail now. */
+ celt_assert(iter->src_len >= 0);
+ /* Don't repeat padding or frame separators with a 0 increment. */
+ if (repeat_id_byte <= 3) continue;
+ /* If the "Repeat These Extensions" extension had L == 0 and this
+ is the last repeated long extension, then force decoding the
+ payload with L = 0. */
+ if (iter->repeat_l == 0
+ && iter->repeat_frame + 1 >= iter->nb_frames
+ && iter->src_data == iter->last_long) {
+ repeat_id_byte &= ~1;
+ }
+ curr_data0 = iter->curr_data;
+ iter->curr_len = skip_extension_payload(&iter->curr_data,
+ iter->curr_len, &header_size, repeat_id_byte,
+ iter->trailing_short_len);
+ if (iter->curr_len < 0) {
+ return OPUS_INVALID_PACKET;
+ }
+ celt_assert(iter->curr_data - iter->data
+ == iter->len - iter->curr_len);
+ /* If we were asked to stop at frame_max, skip extensions for later
+ frames. */
+ if (iter->repeat_frame >= iter->frame_max) {
+ continue;
+ }
+ if (ext != NULL) {
+ ext->id = repeat_id_byte >> 1;
+ ext->frame = iter->repeat_frame;
+ ext->data = curr_data0 + header_size;
+ ext->len = iter->curr_data - curr_data0 - header_size;
+ }
+ return 1;
+ }
+ /* We finished repeating the extensions for this frame. */
+ iter->src_data = iter->repeat_data;
+ iter->src_len = iter->repeat_len;
+ }
+ /* We finished repeating extensions. */
+ iter->repeat_data = iter->curr_data;
+ iter->last_long = NULL;
+ /* If L == 0, advance the frame number to handle the case where we did
+ not consume all of the data with an L == 0 long extension. */
+ if (iter->repeat_l == 0) {
+ iter->curr_frame++;
+ /* Ignore additional padding if this was already the last frame. */
+ if (iter->curr_frame >= iter->nb_frames) {
+ iter->curr_len = 0;
+ }
+ }
+ iter->repeat_frame = 0;
+ return 0;
+}
+
/* Return the next extension (excluding real padding, separators, and repeat
indicators, but including the repeated extensions) in bitstream order.
Due to the extension repetition mechanism, extensions are not necessarily
@@ -162,65 +230,10 @@
return OPUS_INVALID_PACKET;
}
if (iter->repeat_frame > 0) {
+ int ret;
/* We are in the process of repeating some extensions. */
- for (;iter->repeat_frame < iter->nb_frames; iter->repeat_frame++) {
- while (iter->src_len > 0) {
- const unsigned char *curr_data0;
- int repeat_id_byte;
- repeat_id_byte = *iter->src_data;
- iter->src_len = skip_extension(&iter->src_data, iter->src_len,
- &header_size);
- /* We skipped this extension earlier, so it should not fail now. */
- celt_assert(iter->src_len >= 0);
- /* Don't repeat padding or frame separators with a 0 increment. */
- if (repeat_id_byte <= 3) continue;
- /* If the "Repeat These Extensions" extension had L == 0 and this
- is the last repeated long extension, then force decoding the
- payload with L = 0. */
- if (iter->repeat_l == 0
- && iter->repeat_frame + 1 >= iter->nb_frames
- && iter->src_data == iter->last_long) {
- repeat_id_byte &= ~1;
- }
- curr_data0 = iter->curr_data;
- iter->curr_len = skip_extension_payload(&iter->curr_data,
- iter->curr_len, &header_size, repeat_id_byte,
- iter->trailing_short_len);
- if (iter->curr_len < 0) {
- return OPUS_INVALID_PACKET;
- }
- celt_assert(iter->curr_data - iter->data
- == iter->len - iter->curr_len);
- /* If we were asked to stop at frame_max, skip extensions for later
- frames. */
- if (iter->repeat_frame >= iter->frame_max) {
- continue;
- }
- if (ext != NULL) {
- ext->id = repeat_id_byte >> 1;
- ext->frame = iter->repeat_frame;
- ext->data = curr_data0 + header_size;
- ext->len = iter->curr_data - curr_data0 - header_size;
- }
- return 1;
- }
- /* We finished repeating the extensions for this frame. */
- iter->src_data = iter->repeat_data;
- iter->src_len = iter->repeat_len;
- }
- /* We finished repeating extensions. */
- iter->repeat_data = iter->curr_data;
- iter->last_long = NULL;
- /* If L == 0, advance the frame number to handle the case where we did
- not consume all of the data with an L == 0 long extension. */
- if (iter->repeat_l == 0) {
- iter->curr_frame++;
- /* Ignore additional padding if this was already the last frame. */
- if (iter->curr_frame >= iter->nb_frames) {
- iter->curr_len = 0;
- }
- }
- iter->repeat_frame = 0;
+ ret = opus_extension_iterator_next_repeat(iter, ext);
+ if (ret) return ret;
}
/* Checking this here allows opus_extension_iterator_set_frame_max() to be
called at any point. */
@@ -263,12 +276,14 @@
iter->trailing_short_len = 0;
}
else if (id == 2) {
+ int ret;
iter->repeat_l = L;
iter->repeat_frame = iter->curr_frame + 1;
iter->repeat_len = curr_data0 - iter->repeat_data;
iter->src_data = iter->repeat_data;
iter->src_len = iter->repeat_len;
- return opus_extension_iterator_next(iter, ext);
+ ret = opus_extension_iterator_next_repeat(iter, ext);
+ if (ret) return ret;
}
else if (id > 2) {
/* Update the location of the last long extension.
--
⑨