comparison mupdf-source/platform/x11/prog_stream.c @ 2:b50eed0cc0ef upstream

ADD: MuPDF v1.26.7: the MuPDF source as downloaded by a default build of PyMuPDF 1.26.4. The directory name has changed: no version number in the expanded directory now.
author Franz Glasner <fzglas.hg@dom66.de>
date Mon, 15 Sep 2025 11:43:07 +0200
parents
children
comparison
equal deleted inserted replaced
1:1d09e1dec1d9 2:b50eed0cc0ef
1 // Copyright (C) 2004-2021 Artifex Software, Inc.
2 //
3 // This file is part of MuPDF.
4 //
5 // MuPDF is free software: you can redistribute it and/or modify it under the
6 // terms of the GNU Affero General Public License as published by the Free
7 // Software Foundation, either version 3 of the License, or (at your option)
8 // any later version.
9 //
10 // MuPDF is distributed in the hope that it will be useful, but WITHOUT ANY
11 // WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
12 // FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
13 // details.
14 //
15 // You should have received a copy of the GNU Affero General Public License
16 // along with MuPDF. If not, see <https://www.gnu.org/licenses/agpl-3.0.en.html>
17 //
18 // Alternative licensing terms are available from the licensor.
19 // For commercial licensing, see <https://www.artifex.com/> or contact
20 // Artifex Software, Inc., 39 Mesa Street, Suite 108A, San Francisco,
21 // CA 94129, USA, for further information.
22
23 #include "mupdf/fitz.h"
24
25 #include <errno.h>
26 #include <stdio.h>
27 #include <string.h>
28 #include <assert.h>
29
30 #include "curl_stream.h"
31
32 #ifdef _WIN32
33 #include <windows.h>
34 #else
35 #include <sys/time.h>
36 #include <unistd.h>
37 #include <pthread.h>
38 #endif
39
40 /* File stream - progressive reading to simulate http download */
41
42 typedef struct prog_state
43 {
44 FILE *file;
45 int64_t length;
46 int64_t available; /* guarded by lock below */
47 int kbps;
48 int64_t start_time; /* in milliseconds since epoch */
49 unsigned char buffer[4096];
50 void (*on_data)(void*,int);
51 void *on_data_arg;
52
53 /* We assume either Windows threads or pthreads here. */
54 #ifdef _WIN32
55 void *thread;
56 DWORD thread_id;
57 HANDLE mutex;
58 #else
59 pthread_t thread;
60 pthread_mutex_t mutex;
61 #endif
62 } prog_state;
63
64 static int64_t get_current_time(void)
65 {
66 #ifdef _WIN32
67 return (int)GetTickCount();
68 #else
69 struct timeval now;
70 gettimeofday(&now, NULL);
71 return (int64_t)now.tv_sec*1000 + now.tv_usec/1000;
72 #endif
73 }
74
75 #ifdef _WIN32
76 static int locked;
77
78 static void
79 lock(prog_state *state)
80 {
81 WaitForSingleObject(state->mutex, INFINITE);
82 assert(locked == 0);
83 locked = 1;
84 }
85
86 static void
87 unlock(prog_state *state)
88 {
89 assert(locked == 1);
90 locked = 0;
91 ReleaseMutex(state->mutex);
92 }
93 #else
94 static void
95 lock(prog_state *state)
96 {
97 pthread_mutex_lock(&state->mutex);
98 }
99
100 static void
101 unlock(prog_state *state)
102 {
103 pthread_mutex_unlock(&state->mutex);
104 }
105 #endif
106
107 static int next_prog(fz_context *ctx, fz_stream *stm, size_t len)
108 {
109 prog_state *ps = (prog_state *)stm->state;
110 size_t n;
111
112 if (len > sizeof(ps->buffer))
113 len = sizeof(ps->buffer);
114
115 /* Simulate not all data available yet. */
116 lock(ps);
117 if (ps->available < ps->length)
118 {
119 if (stm->pos > ps->available || ps->available - stm->pos <= 0)
120 {
121 unlock(ps);
122 fz_throw(ctx, FZ_ERROR_TRYLATER, "Not enough data yet");
123 }
124 len = fz_mini(len, ps->available - stm->pos);
125 }
126 unlock(ps);
127
128 n = fread(ps->buffer, 1, len, ps->file);
129 stm->rp = ps->buffer;
130 stm->wp = ps->buffer + n;
131 stm->pos += (int64_t)n;
132
133 if (n < len)
134 {
135 if (ferror(ps->file))
136 fz_throw(ctx, FZ_ERROR_GENERIC, "prog read error: %s", strerror(errno));
137 if (n == 0)
138 return EOF;
139 }
140 return *stm->rp++;
141 }
142
143 static void seek_prog(fz_context *ctx, fz_stream *stm, int64_t offset, int whence)
144 {
145 prog_state *ps = (prog_state *)stm->state;
146
147 if (whence == SEEK_END)
148 {
149 whence = SEEK_SET;
150 offset += ps->length;
151 }
152 else if (whence == SEEK_CUR)
153 {
154 whence = SEEK_SET;
155 offset += stm->pos;
156 }
157
158 if (fseek(ps->file, offset, whence) != 0)
159 fz_throw(ctx, FZ_ERROR_GENERIC, "cannot seek: %s", strerror(errno));
160 stm->pos = offset;
161 stm->wp = stm->rp;
162 }
163
164 static void close_prog(fz_context *ctx, void *state)
165 {
166 prog_state *ps = (prog_state *)state;
167 int n = fclose(ps->file);
168 if (n < 0)
169 fz_warn(ctx, "cannot fclose: %s", strerror(errno));
170 fz_free(ctx, state);
171 }
172
173 static void fetcher_thread(prog_state *ps)
174 {
175 int complete = 0;
176
177 ps->start_time = get_current_time();
178
179 while (!complete)
180 {
181 /* Wait a while. */
182 #ifdef _WIN32
183 Sleep(200);
184 #else
185 usleep(200000);
186 #endif
187
188 lock(ps);
189
190 /* Simulate more data having arrived. */
191 if (ps->available < ps->length)
192 {
193 int64_t av = (get_current_time() - ps->start_time) * ps->kbps;
194 if (av > ps->length)
195 av = ps->length;
196 ps->available = av;
197 }
198 else
199 {
200 complete = 1;
201 }
202
203 unlock(ps);
204
205 /* Ping callback with new data. */
206 if (ps->on_data)
207 ps->on_data(ps->on_data_arg, complete);
208 }
209 }
210
211 #ifdef _WIN32
212 static DWORD WINAPI win_thread(void *lparam)
213 {
214 fetcher_thread((prog_state *)lparam);
215 return 0;
216 }
217 #else
218 static void *pthread_thread(void *arg)
219 {
220 fetcher_thread((prog_state *)arg);
221 return NULL;
222 }
223 #endif
224
225 static fz_stream *
226 fz_open_file_ptr_progressive(fz_context *ctx, FILE *file, int kbps, void (*on_data)(void*,int), void *opaque)
227 {
228 fz_stream *stm;
229 prog_state *state;
230
231 state = fz_malloc_struct(ctx, prog_state);
232 state->file = file;
233 state->kbps = kbps;
234 state->available = 0;
235
236 state->on_data = on_data;
237 state->on_data_arg = opaque;
238
239 fseek(state->file, 0, SEEK_END);
240 state->length = ftell(state->file);
241 fseek(state->file, 0, SEEK_SET);
242
243 #ifdef _WIN32
244 state->mutex = CreateMutex(NULL, FALSE, NULL);
245 if (state->mutex == NULL)
246 fz_throw(ctx, FZ_ERROR_GENERIC, "mutex creation failed");
247
248 state->thread = CreateThread(NULL, 0, win_thread, state, 0, &state->thread_id);
249 if (state->thread == NULL)
250 fz_throw(ctx, FZ_ERROR_GENERIC, "thread creation failed");
251 #else
252 if (pthread_mutex_init(&state->mutex, NULL))
253 fz_throw(ctx, FZ_ERROR_GENERIC, "mutex creation failed");
254
255 if (pthread_create(&state->thread, NULL, pthread_thread, state))
256 fz_throw(ctx, FZ_ERROR_GENERIC, "thread creation failed");
257 #endif
258
259 stm = fz_new_stream(ctx, state, next_prog, close_prog);
260 stm->progressive = 1;
261 stm->seek = seek_prog;
262
263 return stm;
264 }
265
266 fz_stream *
267 fz_open_file_progressive(fz_context *ctx, const char *name, int kbps, void (*on_data)(void*,int), void *opaque)
268 {
269 FILE *f;
270 #ifdef _WIN32
271 f = fz_fopen_utf8(name, "rb");
272 #else
273 f = fopen(name, "rb");
274 #endif
275 if (f == NULL)
276 fz_throw(ctx, FZ_ERROR_GENERIC, "cannot open %s", name);
277 return fz_open_file_ptr_progressive(ctx, f, kbps, on_data, opaque);
278 }