#include <stdio.h> #include <stdlib.h> #include <dirent.h> #include <sys/stat.h> #include <pthread.h> #include <unistd.h> #include <string.h> #include <libgen.h> #include <stdbool.h> #include <assert.h> #include <sys/types.h> #include <fcntl.h> #include <zlib.h> #include <errno.h> #define MAX_THREADS 16 #define MAX_QUEUE 100000 #define PATH_MAX 512 #define CHUNK 16384 char* ignoreUUIDs = NULL; char* base_dir = NULL; char* cache_dir = NULL; char* astc_bin = NULL; typedef struct { void (*function)(void *arg); void *arg; } Task; typedef struct { Task task_queue[MAX_QUEUE]; int front; int rear; int count; int total_tasks;// 總共任務數 int done_tasks;// 完成任務數 pthread_mutex_t lock; pthread_cond_t cond_task; } ThreadPool; ThreadPool pool; int gzip_compress(const char *source, const char *out ) { FILE *src = fopen(source, "rb"); if (!src) { perror("開啟原始檔失敗"); return -1; } // gzip 檔案處理 gzFile dest_file = gzopen(out, "wb9"); // 使用最高壓縮等級 '9' if (!dest_file) { perror("開啟目的檔案失敗"); fclose(src); return -1; } char inbuf[CHUNK]; int num_read; while ((num_read = fread(inbuf, 1, sizeof(inbuf), src)) > 0) { // 寫入壓縮檔案 if (gzwrite(dest_file, inbuf, num_read) != num_read) { perror("寫入壓縮檔案失敗"); gzclose(dest_file); fclose(src); return -1; } } // 關閉檔案 gzclose(dest_file); fclose(src); return 0; } // 新增任務到佇列 void add_task(ThreadPool *pool, void (*function)(void *), void *arg) { pthread_mutex_lock(&pool->lock); assert(pool->count < MAX_QUEUE); Task task; task.function = function; task.arg = arg; pool->task_queue[pool->rear] = task; pool->rear = (pool->rear + 1) % MAX_QUEUE; pool->count++; pthread_cond_signal(&pool->cond_task); pthread_mutex_unlock(&pool->lock); } // 執行緒要執行的任務 void *worker(void *arg) { while (1) { Task task; // 從佇列取出任務時需要確保正確的鎖搭配 pthread_mutex_lock(&pool.lock); while (pool.count == 0) { if(pool.total_tasks && pool.done_tasks >= pool.total_tasks) { pthread_mutex_unlock(&pool.lock); goto END; } pthread_cond_wait(&pool.cond_task, &pool.lock); } task = pool.task_queue[pool.front]; pool.front = (pool.front + 1) % MAX_QUEUE; pool.count--; pthread_mutex_unlock(&pool.lock); (*(task.function))(task.arg); pthread_mutex_lock(&pool.lock); pool.done_tasks++; if(pool.done_tasks >= pool.total_tasks) { pthread_cond_broadcast(&pool.cond_task); pthread_mutex_unlock(&pool.lock); break; } pthread_mutex_unlock(&pool.lock); } END: printf("worker %d: exit : %d, %d\n",*(int*)arg, pool.done_tasks, pool.total_tasks); return NULL; } // 初始化執行緒池 void init_thread_pool(ThreadPool *pool, pthread_t threads[]) { pool->front = 0; pool->rear = 0; pool->count = 0; pool->total_tasks = 0; pool->done_tasks = 0; pthread_mutex_init(&pool->lock, NULL); pthread_cond_init(&pool->cond_task, NULL); for (int i = 0; i < MAX_THREADS; i++) { int *arg = malloc(sizeof(*arg)); *arg = i; pthread_create(&threads[i], NULL, worker, arg); } } void task_function(void *arg) { char* image = (char *)arg; char imageGZ[PATH_MAX]; strcpy(imageGZ, image); strcat(imageGZ, ".gz"); assert(gzip_compress(image, imageGZ)==0); assert(rename(imageGZ, image)==0); } typedef struct { char **files; size_t count; size_t capacity; } FileList; void init_file_list(FileList *list) { list->count = 0; list->capacity = 100; // 初始容量 list->files = malloc(list->capacity * sizeof(char *)); if (list->files == NULL) { perror("Failed to allocate memory"); exit(EXIT_FAILURE); } } void add_file(FileList *list, const char *file_path) { if (list->count >= list->capacity) { list->capacity *= 2; list->files = realloc(list->files, list->capacity * sizeof(char *)); if (list->files == NULL) { perror("Failed to reallocate memory"); exit(EXIT_FAILURE); } } list->files[list->count] = strdup(file_path); if (list->files[list->count] == NULL) { perror("Failed to duplicate string"); exit(EXIT_FAILURE); } list->count++; } void free_file_list(FileList *list) { for (size_t i = 0; i < list->count; i++) { free(list->files[i]); } free(list->files); } int mkdir_p(const char *path) { char tmp[512]; char *p = NULL; size_t len; snprintf(tmp, sizeof(tmp), "%s", path); len = strlen(tmp); if (tmp[len - 1] == '/') tmp[len - 1] = 0; for (p = tmp + 1; *p; p++) { if (*p == '/') { *p = 0; if (mkdir(tmp, S_IRWXU) != 0) { if (errno != EEXIST) { return -1; } } *p = '/'; } } if (mkdir(tmp, S_IRWXU) != 0) { if (errno != EEXIST) { return -1; } } return 0; } int check_file_extension(const char *filename) { const char *dot = strrchr(filename, '.'); if (!dot || dot == filename) { return 0; } const char* extension = dot + 1; if (strcmp(extension, "html") == 0) { return 2; } return 0; } void find_images(const char *directory, FileList *file_list) { DIR *dir; struct dirent *entry; struct stat entry_info; if ((dir = opendir(directory)) == NULL) { perror("Unable to open directory"); return; } while ((entry = readdir(dir)) != NULL) { if (strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0) { continue; // 跳過當前目錄和父目錄 } char path[1024]; snprintf(path, sizeof(path), "%s/%s", directory, entry->d_name); if (stat(path, &entry_info) == 0) { if (S_ISDIR(entry_info.st_mode)) { // 如果是目錄,遞迴呼叫 find_images(path, file_list); } else if (S_ISREG(entry_info.st_mode)) { // 如果是常規檔案,檢查副檔名 if (!check_file_extension(entry->d_name)) { add_file(file_list, path); } } } } closedir(dir); } int main(int argc, char *argv[]) { if (argc != 2) { fprintf(stderr, "Usage: %s <source_dir> \n", argv[0]); return EXIT_FAILURE; } const char *source_dir = argv[1]; FileList image_files; init_file_list(&image_files); pthread_t threads[MAX_THREADS]; init_thread_pool(&pool, threads); find_images(source_dir, &image_files); pool.total_tasks = image_files.count; if(image_files.count == 0){ goto END; } // 新增一些任務到佇列中 for (size_t i = 0; i < image_files.count; i++) { // printf("%s\n", image_files.files[i]); add_task(&pool, task_function, image_files.files[i]); } for (int i = 0; i < MAX_THREADS; i++) { pthread_join(threads[i], NULL); } free_file_list(&image_files); // 等待執行緒池自己退出 pthread_mutex_lock(&pool.lock); pthread_cond_broadcast(&pool.cond_task); pthread_mutex_unlock(&pool.lock); END: printf("All tasks completed. Exiting main.\n"); return 0; }