Mercurial > hgrepos > Python2 > PyMuPDF
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 } |
