Coverage Report

Created: 2025-11-24 06:05

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.46M
#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
594
{
63
594
  jpeg_custom_source_mgr_ptr src = (jpeg_custom_source_mgr_ptr) dinfo->src;
64
65
594
  src->bufused = 0;
66
594
  if (src->bufused_ptr)
67
594
    *src->bufused_ptr = 0;
68
594
  src->start_of_file = TRUE;
69
594
}
70
71
static void custom_init_mem_source (j_decompress_ptr dinfo)
72
199
{
73
  /* nothing to do */
74
199
}
75
76
77
static boolean custom_fill_input_buffer (j_decompress_ptr dinfo)
78
1.46M
{
79
1.46M
  jpeg_custom_source_mgr_ptr src = (jpeg_custom_source_mgr_ptr) dinfo->src;
80
1.46M
  size_t bytes_read;
81
1.46M
  unsigned char *newbuf;
82
83
1.46M
  bytes_read = fread(src->stdio_buffer, 1, STDIO_BUFFER_SIZE, src->infile);
84
1.46M
  if (bytes_read <= 0) {
85
1.45M
    if (src->start_of_file)
86
1.45M
      ERREXIT(dinfo, JERR_INPUT_EMPTY);
87
1.45M
    WARNMS(dinfo, JWRN_JPEG_EOF);
88
    /* Insert fake EOI marker if read failed */
89
1.45M
    src->stdio_buffer[0] = (JOCTET) 0xff;
90
1.45M
    src->stdio_buffer[1] = (JOCTET) JPEG_EOI;
91
1.45M
    bytes_read = 2;
92
1.45M
  } else if (src->buf_ptr && src->buf) {
93
3.24k
    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
3.24k
    memcpy(&src->buf[src->bufused], src->stdio_buffer, bytes_read);
103
3.24k
    src->bufused += bytes_read;
104
3.24k
    if (src->bufused_ptr)
105
3.24k
      *src->bufused_ptr = src->bufused;
106
3.24k
  }
107
108
1.46M
  src->pub.next_input_byte = src->stdio_buffer;
109
1.46M
  src->pub.bytes_in_buffer = bytes_read;
110
1.46M
  src->start_of_file = FALSE;
111
112
1.46M
  return TRUE;
113
1.46M
}
114
115
static boolean custom_fill_mem_input_buffer (j_decompress_ptr dinfo)
116
703k
{
117
703k
  jpeg_custom_source_mgr_ptr src = (jpeg_custom_source_mgr_ptr) dinfo->src;
118
119
703k
  WARNMS(dinfo, JWRN_JPEG_EOF);
120
121
  /* Insert fake EOI marker as there is no more data... */
122
703k
  src->stdio_buffer[0] = (JOCTET) 0xff;
123
703k
  src->stdio_buffer[1] = (JOCTET) JPEG_EOI;
124
125
703k
  src->pub.next_input_byte = src->stdio_buffer;
126
703k
  src->pub.bytes_in_buffer = 2;
127
128
703k
  return TRUE;
129
703k
}
130
131
132
static void custom_skip_input_data (j_decompress_ptr dinfo, long num_bytes)
133
9.06k
{
134
9.06k
  jpeg_custom_source_mgr_ptr src = (jpeg_custom_source_mgr_ptr) dinfo->src;
135
136
9.06k
  if (num_bytes <= 0)
137
0
    return;
138
139
  /* skip "num_bytes" bytes of data from input... */
140
789k
  while (num_bytes > (long) src->pub.bytes_in_buffer) {
141
780k
    num_bytes -= src->pub.bytes_in_buffer;
142
780k
    (void)(src->pub.fill_input_buffer)(dinfo);
143
780k
  }
144
9.06k
  src->pub.next_input_byte += (size_t) num_bytes;
145
9.06k
  src->pub.bytes_in_buffer -= (size_t) num_bytes;
146
9.06k
}
147
148
149
static void custom_term_source (j_decompress_ptr dinfo)
150
410
{
151
410
  jpeg_custom_source_mgr_ptr src = (jpeg_custom_source_mgr_ptr) dinfo->src;
152
153
410
  if (src->bufused_ptr)
154
296
    *src->bufused_ptr = src->bufused;
155
410
}
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
594
{
161
594
  jpeg_custom_source_mgr_ptr src;
162
163
594
  if (!dinfo->src) {
164
    /* Allocate source manager object if needed */
165
594
    dinfo->src = (struct jpeg_source_mgr *)
166
594
      (*dinfo->mem->alloc_small) ((j_common_ptr) dinfo, JPOOL_PERMANENT,
167
594
            sizeof(jpeg_custom_source_mgr));
168
594
    src = (jpeg_custom_source_mgr_ptr) dinfo->src;
169
594
    src->stdio_buffer = (JOCTET *)
170
594
      (*dinfo->mem->alloc_small) ((j_common_ptr) dinfo, JPOOL_PERMANENT,
171
594
            STDIO_BUFFER_SIZE * sizeof(JOCTET));
172
594
  } else {
173
0
    src = (jpeg_custom_source_mgr_ptr) dinfo->src;
174
0
  }
175
176
594
  src->pub.init_source = custom_init_source;
177
594
  src->pub.fill_input_buffer = custom_fill_input_buffer;
178
594
  src->pub.resync_to_restart = jpeg_resync_to_restart;
179
594
  src->pub.skip_input_data = custom_skip_input_data;
180
594
  src->pub.term_source = custom_term_source;
181
594
  src->infile = infile;
182
594
  src->pub.bytes_in_buffer = 0;
183
594
  src->pub.next_input_byte = NULL;
184
185
594
  src->buf_ptr = bufptr;
186
594
  src->buf = (bufptr ? *bufptr : NULL);
187
594
  src->bufsize_ptr = bufsizeptr;
188
594
  src->bufused_ptr = bufusedptr;
189
594
  src->bufsize = (bufsizeptr ? *bufsizeptr : 0);
190
594
  src->incsize = incsize;
191
594
}
192
193
194
195
void jpeg_custom_mem_src(j_decompress_ptr dinfo, unsigned char *buf, size_t bufsize)
196
199
{
197
199
  jpeg_custom_source_mgr_ptr src;
198
199
199
  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
199
  } else {
209
199
    src = (jpeg_custom_source_mgr_ptr) dinfo->src;
210
199
  }
211
212
199
  src->pub.init_source = custom_init_mem_source;
213
199
  src->pub.fill_input_buffer = custom_fill_mem_input_buffer;
214
199
  src->pub.resync_to_restart = jpeg_resync_to_restart;
215
199
  src->pub.skip_input_data = custom_skip_input_data;
216
199
  src->pub.term_source = custom_term_source;
217
199
  src->infile = NULL;
218
199
  src->pub.bytes_in_buffer = bufsize;
219
199
  src->pub.next_input_byte = (JOCTET*) buf;
220
221
199
  src->buf_ptr = NULL;
222
199
  src->buf = NULL;
223
199
  src->bufsize_ptr = NULL;
224
  src->bufused_ptr = NULL;
225
199
  src->bufsize = bufsize;
226
199
  src->incsize = 0;
227
199
}
228
229
230
/* eof :-) */