Code Coverage Report for src/util/open.c


Hit Total Coverage
Lines: 93 93 100.0%
Branches: 504 504 100.0%

1 /*
2 * libnogg: a decoder library for Ogg Vorbis streams
3 * Copyright (c) 2014-2024 Andrew Church <achurch@achurch.org>
4 *
5 * This software may be copied and redistributed under certain conditions;
6 * see the file "COPYING" in the source code distribution for details.
7 * NO WARRANTY is provided with this software.
8 */
9
10 #include "include/nogg.h"
11 #include "src/common.h"
12 #include "src/util/open.h"
13 #include "src/util/memory.h"
14
15 #include <stdlib.h>
16
17 /*************************************************************************/
18 /**************************** Local routines *****************************/
19 /*************************************************************************/
20
21 #ifdef ENABLE_ASM_X86_AVX2
22
23 /**
24 * x86_cpuid: Return the specified data from the x86 CPUID instruction.
25 *
26 * [Parameters]
27 * eax: EAX (primary selector) value for the CPUID instruction.
28 * ecx: ECX (secondary selector) value for the CPUID instruction.
29 * index: Index of register to return (0=EAX, 1=EBX, 2=ECX, 3=EDX).
30 */
31 static inline uint32_t x86_cpuid(uint32_t eax, uint32_t ecx, int index)
32 {
33 if (index == 0) {
34 uint32_t result;
35 __asm__("cpuid" : "=a" (result) : "a" (eax), "c" (ecx));
36 return result;
37 } else if (index == 1) {
38 uint32_t result;
39 __asm__("cpuid" : "=b" (result) : "a" (eax), "c" (ecx));
40 return result;
41 } else if (index == 2) {
42 uint32_t result;
43 __asm__("cpuid" : "=c" (result) : "a" (eax), "c" (ecx));
44 return result;
45 } else if (index == 3) {
46 uint32_t result;
47 __asm__("cpuid" : "=d" (result) : "a" (eax), "c" (ecx));
48 return result;
49 } else {
50 ASSERT(!"invalid index");
51 }
52 }
53
54 #endif // ENABLE_ASM_X86_AVX2
55
56 /*************************************************************************/
57 /*************************** Interface routine ***************************/
58 /*************************************************************************/
59
60 vorbis_t *open_common(const open_params_t *params, vorbis_error_t *error_ret)
61 {
62 ASSERT(params != NULL);
63 ASSERT(params->callbacks != NULL);
64
65 vorbis_t *handle = NULL;
66 vorbis_error_t error = VORBIS_NO_ERROR;
67
68 /* Validate the parameters. */
69 (18/18) if ((params->callbacks->malloc != NULL) != (params->callbacks->free != NULL)) {
70 error = VORBIS_ERROR_INVALID_ARGUMENT;
71 goto exit;
72 }
73 (18/18) if (params->packet_mode) {
74 (36/36) if (!params->id_packet || params->id_packet_len <= 0
75 (36/36) || !params->setup_packet || params->setup_packet_len <= 0) {
76 error = VORBIS_ERROR_INVALID_ARGUMENT;
77 goto exit;
78 }
79 } else {
80 (18/18) if (!params->callbacks->read) {
81 error = VORBIS_ERROR_INVALID_ARGUMENT;
82 goto exit;
83 }
84 (18/18) if (params->callbacks->length
85 (36/36) && (!params->callbacks->tell || !params->callbacks->seek)) {
86 error = VORBIS_ERROR_INVALID_ARGUMENT;
87 goto exit;
88 }
89 }
90
91 /* Perform CPU runtime checks. */
92 #ifdef ENABLE_ASM_X86_AVX2
93 if (!(x86_cpuid(7, 0, 1) & (1u << 5))) { // AVX2
94 error = VORBIS_ERROR_NO_CPU_SUPPORT;
95 goto exit;
96 }
97 /* All current CPUs with AVX2 also support FMA, so this should be a
98 * no-op, but play it safe. */
99 if (!(x86_cpuid(1, 0, 2) & (1u << 12))) { // FMA (FMA3)
100 error = VORBIS_ERROR_NO_CPU_SUPPORT;
101 goto exit;
102 }
103 #endif
104
105 /* Allocate and initialize a handle structure. */
106 (18/18) if (params->callbacks->malloc) {
107 ASSERT(!params->open_callback);
108 handle = (*params->callbacks->malloc)(
109 params->callback_data, sizeof(*handle), 0);
110 } else {
111 handle = malloc(sizeof(*handle));
112 }
113 (18/18) if (!handle) {
114 error = VORBIS_ERROR_INSUFFICIENT_RESOURCES;
115 goto exit;
116 }
117 handle->packet_mode = params->packet_mode;
118 handle->read_int16_only =
119 ((params->options & VORBIS_OPTION_READ_INT16_ONLY) != 0);
120 handle->callbacks = *params->callbacks;
121 (18/18) if (handle->packet_mode) {
122 handle->callbacks.length = NULL;
123 handle->callbacks.tell = NULL;
124 handle->callbacks.seek = NULL;
125 handle->callbacks.read = NULL;
126 handle->callbacks.close = NULL;
127 }
128 (18/18) if (params->open_callback) {
129 handle->callback_data =
130 (*params->open_callback)(handle, params->callback_data);
131 } else {
132 handle->callback_data = params->callback_data;
133 }
134 (18/18) if (handle->callbacks.length) {
135 handle->data_length =
136 (*handle->callbacks.length)(handle->callback_data);
137 } else {
138 handle->data_length = -1;
139 }
140 handle->read_error_flag = 0;
141 handle->eos_flag = 0;
142 handle->frame_pos = 0;
143 handle->decode_buf_len = 0;
144 handle->decode_buf_pos = 0;
145
146 /* Create an stb_vorbis handle for the stream. */
147 int stb_error;
148 (18/18) if (params->packet_mode) {
149 handle->decoder = stb_vorbis_open_packet(
150 handle, params->id_packet, params->id_packet_len,
151 params->setup_packet, params->setup_packet_len,
152 params->options, &stb_error);
153 } else {
154 handle->decoder = stb_vorbis_open_callbacks(
155 handle->callbacks.read, handle->callbacks.seek,
156 handle->callbacks.tell, handle->callback_data,
157 handle, handle->data_length, params->options, &stb_error);
158 }
159 (18/18) if (!handle->decoder) {
160 (18/18) if (stb_error == VORBIS_outofmem) {
161 error = VORBIS_ERROR_INSUFFICIENT_RESOURCES;
162 (18/18) } else if (stb_error == VORBIS_unexpected_eof
163 (18/18) || stb_error == VORBIS_reached_eof
164 (18/18) || stb_error == VORBIS_missing_capture_pattern
165 (18/18) || stb_error == VORBIS_invalid_stream_structure_version
166 (18/18) || stb_error == VORBIS_invalid_first_page
167 (18/18) || stb_error == VORBIS_invalid_stream) {
168 error = VORBIS_ERROR_STREAM_INVALID;
169 } else {
170 error = VORBIS_ERROR_DECODE_SETUP_FAILED;
171 }
172 goto error_free_handle;
173 }
174
175 /* Save the audio parameters. */
176 stb_vorbis_info info = stb_vorbis_get_info(handle->decoder);
177 handle->channels = info.channels;
178 handle->rate = info.sample_rate;
179
180 /* Allocate a decoding buffer based on the maximum decoded frame size.
181 * We align this to a 64-byte boundary to help optimizations which
182 * require aligned data. */
183 (18/18) const int sample_size = (handle->read_int16_only ? 2 : 4);
184 const int32_t decode_buf_size =
185 sample_size * handle->channels * info.max_frame_size;
186 handle->decode_buf = mem_alloc(handle, decode_buf_size, 64);
187 (18/18) if (!handle->decode_buf) {
188 error = VORBIS_ERROR_INSUFFICIENT_RESOURCES;
189 goto error_close_decoder;
190 }
191
192 exit:
193 (18/18) if (error_ret) {
194 *error_ret = error;
195 }
196 return handle;
197
198 error_close_decoder:
199 stb_vorbis_close(handle->decoder);
200 error_free_handle:
201 (18/18) if (params->callbacks->free) {
202 ASSERT(!params->open_callback);
203 (*params->callbacks->free)(params->callback_data, handle);
204 } else {
205 free(handle);
206 }
207 handle = NULL;
208 goto exit;
209 }
210
211 /*************************************************************************/
212 /*************************************************************************/