Coverage Report

Created: 2026-08-13 06:44

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/jpegoptim/jpegsrc.c
Line
Count
Source
1
/*
2
 * jpegsrc.c
3
 *
4
 * Copyright (C) 2022-2025 Timo Kokkonen
5
 * All Rights Reserved.
6
 *
7
 * Custom libjpeg "Source Manager" for reading from a file handle
8
 * and optionally saving the input also into a memory buffer.
9
 *
10
 * SPDX-License-Identifier: GPL-3.0-or-later
11
 *
12
 * This file is part of JPEGoptim.
13
 *
14
 * JPEGoptim is free software: you can redistribute it and/or modify
15
 * it under the terms of the GNU General Public License as published by
16
 * the Free Software Foundation, either version 3 of the License, or
17
 * (at your option) any later version.
18
 *
19
 * JPEGoptim is distributed in the hope that it will be useful,
20
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
21
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
22
 * GNU General Public License for more details.
23
 *
24
 * You should have received a copy of the GNU General Public License
25
 * along with JPEGoptim. If not, see <https://www.gnu.org/licenses/>.
26
 */
27
28
#include <stdio.h>
29
#include <stdlib.h>
30
#include <string.h>
31
#include <jpeglib.h>
32
#include <jerror.h>
33
34
#include "jpegoptim.h"
35
36
1.44M
#define STDIO_BUFFER_SIZE 4096
37
38
39
/* Custom jpeg source manager object */
40
41
typedef struct {
42
  struct jpeg_source_mgr pub; /* public fields */
43
44
  unsigned char **buf_ptr;
45
  size_t *bufsize_ptr;
46
  size_t *bufused_ptr;
47
  size_t incsize;
48
  unsigned char *buf;
49
  size_t bufsize;
50
  size_t bufused;
51
52
  FILE *infile;
53
  JOCTET *stdio_buffer;
54
  boolean start_of_file;
55
} jpeg_custom_source_mgr;
56
57
typedef jpeg_custom_source_mgr* jpeg_custom_source_mgr_ptr;
58
59
60
61
static void custom_init_source (j_decompress_ptr dinfo)
62
522
{
63
522
  jpeg_custom_source_mgr_ptr src = (jpeg_custom_source_mgr_ptr) dinfo->src;
64
65
522
  src->bufused = 0;
66
522
  if (src->bufused_ptr)
67
522
    *src->bufused_ptr = 0;
68
522
  src->start_of_file = TRUE;
69
522
}
70
71
static void custom_init_mem_source (j_decompress_ptr dinfo)
72
275
{
73
  /* nothing to do */
74
275
}
75
76
77
static boolean custom_fill_input_buffer (j_decompress_ptr dinfo)
78
1.44M
{
79
1.44M
  jpeg_custom_source_mgr_ptr src = (jpeg_custom_source_mgr_ptr) dinfo->src;
80
1.44M
  size_t bytes_read;
81
1.44M
  unsigned char *newbuf;
82
83
1.44M
  bytes_read = fread(src->stdio_buffer, 1, STDIO_BUFFER_SIZE, src->infile);
84
1.44M
  if (bytes_read <= 0) {
85
1.44M
    if (src->start_of_file)
86
1.44M
      ERREXIT(dinfo, JERR_INPUT_EMPTY);
87
1.44M
    WARNMS(dinfo, JWRN_JPEG_EOF);
88
    /* Insert fake EOI marker if read failed */
89
1.44M
    src->stdio_buffer[0] = (JOCTET) 0xff;
90
1.44M
    src->stdio_buffer[1] = (JOCTET) JPEG_EOI;
91
1.44M
    bytes_read = 2;
92
1.44M
  } else if (src->buf_ptr && src->buf) {
93
4.00k
    if (bytes_read > (src->bufsize - src->bufused)) {
94
      /* Need to allocate more memory for the buffer. */
95
0
      src->bufsize += src->incsize;
96
0
      newbuf = realloc(src->buf, src->bufsize);
97
0
      if (!newbuf) ERREXIT1(dinfo, JERR_OUT_OF_MEMORY, 42);
98
0
      src->buf = newbuf;
99
0
      *src->buf_ptr = newbuf;
100
0
      src->incsize *= 2;
101
0
    }
102
4.00k
    memcpy(&src->buf[src->bufused], src->stdio_buffer, bytes_read);
103
4.00k
    src->bufused += bytes_read;
104
4.00k
    if (src->bufused_ptr)
105
4.00k
      *src->bufused_ptr = src->bufused;
106
4.00k
  }
107
108
1.44M
  src->pub.next_input_byte = src->stdio_buffer;
109
1.44M
  src->pub.bytes_in_buffer = bytes_read;
110
1.44M
  src->start_of_file = FALSE;
111
112
1.44M
  return TRUE;
113
1.44M
}
114
115
static boolean custom_fill_mem_input_buffer (j_decompress_ptr dinfo)
116
978k
{
117
978k
  jpeg_custom_source_mgr_ptr src = (jpeg_custom_source_mgr_ptr) dinfo->src;
118
119
978k
  WARNMS(dinfo, JWRN_JPEG_EOF);
120
121
  /* Insert fake EOI marker as there is no more data... */
122
978k
  src->stdio_buffer[0] = (JOCTET) 0xff;
123
978k
  src->stdio_buffer[1] = (JOCTET) JPEG_EOI;
124
125
978k
  src->pub.next_input_byte = src->stdio_buffer;
126
978k
  src->pub.bytes_in_buffer = 2;
127
128
978k
  return TRUE;
129
978k
}
130
131
132
static void custom_skip_input_data (j_decompress_ptr dinfo, long num_bytes)
133
12.0k
{
134
12.0k
  jpeg_custom_source_mgr_ptr src = (jpeg_custom_source_mgr_ptr) dinfo->src;
135
136
12.0k
  if (num_bytes <= 0)
137
0
    return;
138
139
  /* skip "num_bytes" bytes of data from input... */
140
969k
  while (num_bytes > (long) src->pub.bytes_in_buffer) {
141
957k
    num_bytes -= src->pub.bytes_in_buffer;
142
957k
    (void)(src->pub.fill_input_buffer)(dinfo);
143
957k
  }
144
12.0k
  src->pub.next_input_byte += (size_t) num_bytes;
145
12.0k
  src->pub.bytes_in_buffer -= (size_t) num_bytes;
146
12.0k
}
147
148
149
static void custom_term_source (j_decompress_ptr dinfo)
150
525
{
151
525
  jpeg_custom_source_mgr_ptr src = (jpeg_custom_source_mgr_ptr) dinfo->src;
152
153
525
  if (src->bufused_ptr)
154
374
    *src->bufused_ptr = src->bufused;
155
525
}
156
157
158
void jpeg_custom_src(j_decompress_ptr dinfo, FILE *infile,
159
    unsigned char **bufptr, size_t *bufsizeptr, size_t *bufusedptr, size_t incsize)
160
522
{
161
522
  jpeg_custom_source_mgr_ptr src;
162
163
522
  if (!dinfo->src) {
164
    /* Allocate source manager object if needed */
165
522
    dinfo->src = (struct jpeg_source_mgr *)
166
522
      (*dinfo->mem->alloc_small) ((j_common_ptr) dinfo, JPOOL_PERMANENT,
167
522
            sizeof(jpeg_custom_source_mgr));
168
522
    src = (jpeg_custom_source_mgr_ptr) dinfo->src;
169
522
    src->stdio_buffer = (JOCTET *)
170
522
      (*dinfo->mem->alloc_small) ((j_common_ptr) dinfo, JPOOL_PERMANENT,
171
522
            STDIO_BUFFER_SIZE * sizeof(JOCTET));
172
522
  } else {
173
0
    src = (jpeg_custom_source_mgr_ptr) dinfo->src;
174
0
  }
175
176
522
  src->pub.init_source = custom_init_source;
177
522
  src->pub.fill_input_buffer = custom_fill_input_buffer;
178
522
  src->pub.resync_to_restart = jpeg_resync_to_restart;
179
522
  src->pub.skip_input_data = custom_skip_input_data;
180
522
  src->pub.term_source = custom_term_source;
181
522
  src->infile = infile;
182
522
  src->pub.bytes_in_buffer = 0;
183
522
  src->pub.next_input_byte = NULL;
184
185
522
  src->buf_ptr = bufptr;
186
522
  src->buf = (bufptr ? *bufptr : NULL);
187
522
  src->bufsize_ptr = bufsizeptr;
188
522
  src->bufused_ptr = bufusedptr;
189
522
  src->bufsize = (bufsizeptr ? *bufsizeptr : 0);
190
522
  src->incsize = incsize;
191
522
}
192
193
194
195
void jpeg_custom_mem_src(j_decompress_ptr dinfo, unsigned char *buf, size_t bufsize)
196
275
{
197
275
  jpeg_custom_source_mgr_ptr src;
198
199
275
  if (!dinfo->src) {
200
    /* Allocate source manager object if needed */
201
0
    dinfo->src = (struct jpeg_source_mgr *)
202
0
      (*dinfo->mem->alloc_small) ((j_common_ptr) dinfo, JPOOL_PERMANENT,
203
0
            sizeof(jpeg_custom_source_mgr));
204
0
    src = (jpeg_custom_source_mgr_ptr) dinfo->src;
205
0
    src->stdio_buffer = (JOCTET *)
206
0
      (*dinfo->mem->alloc_small) ((j_common_ptr) dinfo, JPOOL_PERMANENT,
207
0
            STDIO_BUFFER_SIZE * sizeof(JOCTET));
208
275
  } else {
209
275
    src = (jpeg_custom_source_mgr_ptr) dinfo->src;
210
275
  }
211
212
275
  src->pub.init_source = custom_init_mem_source;
213
275
  src->pub.fill_input_buffer = custom_fill_mem_input_buffer;
214
275
  src->pub.resync_to_restart = jpeg_resync_to_restart;
215
275
  src->pub.skip_input_data = custom_skip_input_data;
216
275
  src->pub.term_source = custom_term_source;
217
275
  src->infile = NULL;
218
275
  src->pub.bytes_in_buffer = bufsize;
219
275
  src->pub.next_input_byte = (JOCTET*) buf;
220
221
275
  src->buf_ptr = NULL;
222
275
  src->buf = NULL;
223
275
  src->bufsize_ptr = NULL;
224
  src->bufused_ptr = NULL;
225
275
  src->bufsize = bufsize;
226
275
  src->incsize = 0;
227
275
}
228
229
230
/* eof :-) */