This is better than just testing that the tree is not overdetermined.
Unified · Split
| @@ -31,15 +31,16 @@ · 30 unmodified lines | ||
| 31 | 31 | #include "libavcodec/mjpegenc_huffman.c" |
| 32 | 32 | |
| 33 | 33 | // Validate the computed lengths satisfy the JPEG restrictions and is optimal. |
| 34 | −static int check_lengths(int L, int expected_length, | |
| 35 | − const int *probs, int nprobs) | |
| 34 | +static int check_lengths(int L, const int *probs, int nprobs, | |
| 35 | + int expected_length, const uint8_t expected_len_counts[/* L + 1 */]) | |
| 36 | 36 | { |
| 37 | 37 | HuffTable lengths[256]; |
| 38 | 38 | PTable val_counts[256]; |
| 39 | + uint8_t len_counts[17] = { 0 }; | |
| 39 | 40 | int actual_length = 0, i, j, k, prob, length; |
| 40 | 41 | int ret = 0; |
| 41 | − double cantor_measure = 0; | |
| 42 | 42 | av_assert0(nprobs <= 256); |
| 43 | + av_assert0(L < FF_ARRAY_ELEMS(len_counts)); | |
| 43 | 44 | |
| 44 | 45 | for (i = 0; i < nprobs; i++) { |
| 45 | 46 | val_counts[i] = (PTable){.value = i, .prob = probs[i]}; |
| @@ -47,6 +48,26 @@ static int check_lengths(int L, int expected_length, · 1 unmodified line | ||
| 47 | 48 | |
| 48 | 49 | mjpegenc_huffman_compute_bits(val_counts, lengths, nprobs, L); |
| 49 | 50 | |
| 51 | + for (int i = 0; i < nprobs; ++i) { | |
| 52 | + if (lengths[i].length > L) { | |
| 53 | + fprintf(stderr, | |
| 54 | + "Len %d of element %d of Huffman table with %d elements exceeds max length %d\n", | |
| 55 | + lengths[i].length, i, nprobs, L); | |
| 56 | + return 1; | |
| 57 | + } | |
| 58 | + len_counts[lengths[i].length]++; | |
| 59 | + } | |
| 60 | + // Test that the lengths can be made part of a complete, prefix-free tree: | |
| 61 | + unsigned code = 0; | |
| 62 | + for (int i = 1; i <= L; ++i) { | |
| 63 | + code <<= 1; | |
| 64 | + code += len_counts[i]; | |
| 65 | + } | |
| 66 | + if (code > 1U << L) { | |
| 67 | + fprintf(stderr, "Huffman tree overdetermined/invalid\n"); | |
| 68 | + ret = 1; | |
| 69 | + } | |
| 70 | + | |
| 50 | 71 | for (i = 0; i < nprobs; i++) { |
| 51 | 72 | // Find the value's prob and length |
| 52 | 73 | for (j = 0; j < nprobs; j++) |
| @@ -59,22 +80,18 @@ static int check_lengths(int L, int expected_length, · 6 unmodified lines | ||
| 59 | 80 | |
| 60 | 81 | if (prob) { |
| 61 | 82 | actual_length += prob * length; |
| 62 | − cantor_measure += 1. / (1 << length); | |
| 63 | 83 | } |
| 64 | 84 | |
| 65 | 85 | if (length > L || length < 1) return 1; |
| 66 | 86 | } |
| 67 | − // Check that the codes can be prefix-free. | |
| 68 | − if (cantor_measure > 1) ret = 1; | |
| 69 | 87 | // Check that the total length is optimal |
| 70 | 88 | if (actual_length != expected_length) ret = 1; |
| 71 | 89 | |
| 72 | 90 | if (ret == 1) { |
| 73 | 91 | fprintf(stderr, |
| 74 | − "Cantor measure: %f\n" | |
| 75 | 92 | "Actual length: %d\n" |
| 76 | 93 | "Expected length: %d\n", |
| 77 | − cantor_measure, actual_length, expected_length); | |
| 94 | + actual_length, expected_length); | |
| 78 | 95 | } |
| 79 | 96 | |
| 80 | 97 | return ret; |
| @@ -83,6 +100,9 @@ static int check_lengths(int L, int expected_length, · 2 unmodified lines | ||
| 83 | 100 | static const int probs_zeroes[] = { |
| 84 | 101 | 6, 6, 0, 0, 0 |
| 85 | 102 | }; |
| 103 | +static const uint8_t len_counts_zeroes[] = { | |
| 104 | + 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, | |
| 105 | +}; | |
| 86 | 106 | |
| 87 | 107 | static const int probs_skewed[] = { |
| 88 | 108 | 2, 0, 0, 0, 0, 1, 0, 0, 20, 0, 2, 0, 10, 5, 1, 1, 9, 1, 1, 6, 0, 5, 0, 1, 0, 7, 6, |
| @@ -96,6 +116,9 @@ static const int probs_skewed[] = { · 7 unmodified lines | ||
| 96 | 116 | 0, 3, 0, 0, 28, 0, 2, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 23, 0, 0, 0, 0, |
| 97 | 117 | 0, 21, 1, 0, 3, 24, 2, 0, 0, 7, 0, 0, 1, 5, 1, 2, 0, 5 |
| 98 | 118 | }; |
| 119 | +static const uint8_t len_counts_skewed[] = { | |
| 120 | + 0, 1, 0, 0, 1, 2, 7, 11, 18, 31, 28, 40, 0, 1, 0, 0, 116, | |
| 121 | +}; | |
| 99 | 122 | |
| 100 | 123 | static const int probs_sat[] = { |
| 101 | 124 | 74, 8, 14, 7, 9345, 40, 0, 2014, 2, 1, 115, 0, 2, 1, 194, 388, 20, 0, 0, 2, 1, 121, |
| @@ -110,6 +133,9 @@ static const int probs_sat[] = { · 8 unmodified lines | ||
| 110 | 133 | 0, 1085, 0, 0, 0, 3, 489, 36, 1, 0, 1, 9420, 294, 28, 0, 57, 5, 0, 9, 2, 0, 1, 2, |
| 111 | 134 | 2, 0, 0, 9, 2, 29, 2, 2, 7, 0, 5, 490, 0, 7, 5, 0, 1, 8, 0, 0, 23255, 0, 1 |
| 112 | 135 | }; |
| 136 | +static const uint8_t len_counts_sat[] = { | |
| 137 | + 0, 1, 0, 2, 1, 2, 2, 5, 5, 7, 7, 8, 17, 23, 16, 24, 136, | |
| 138 | +}; | |
| 113 | 139 | |
| 114 | 140 | // Test the example given on @see |
| 115 | 141 | // http://guru.multimedia.cx/small-tasks-for-ffmpeg/ |
| @@ -154,13 +180,13 @@ int main(int argc, char **argv) · 38 unmodified lines | ||
| 154 | 180 | } |
| 155 | 181 | |
| 156 | 182 | // Check handling of zero probabilities |
| 157 | − if (check_lengths(16, 18, probs_zeroes, FF_ARRAY_ELEMS(probs_zeroes))) | |
| 183 | + if (check_lengths(16, probs_zeroes, FF_ARRAY_ELEMS(probs_zeroes), 18, len_counts_zeroes)) | |
| 158 | 184 | ret = 1; |
| 159 | 185 | // Check skewed distribution over 256 without saturated lengths |
| 160 | − if (check_lengths(16, 41282, probs_skewed, FF_ARRAY_ELEMS(probs_skewed))) | |
| 186 | + if (check_lengths(16, probs_skewed, FF_ARRAY_ELEMS(probs_skewed), 41282, len_counts_skewed)) | |
| 161 | 187 | ret = 1; |
| 162 | 188 | // Check skewed distribution over 256 with saturated lengths |
| 163 | − if (check_lengths(16, 669904, probs_sat, FF_ARRAY_ELEMS(probs_sat))) | |
| 189 | + if (check_lengths(16, probs_sat, FF_ARRAY_ELEMS(probs_sat), 669904, len_counts_sat)) | |
| 164 | 190 | ret = 1; |
| 165 | 191 | |
| 166 | 192 | return ret; |