Coverage Report

Created: 2025-07-23 06:19

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