Code Coverage Report for src/util/open.c


Hit Total Coverage
Lines: 98 98 100.0%
Branches: 486 486 100.0%

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