init commit
Some checks failed
Test / test (1.22.x, macos-latest) (push) Has been cancelled
Test / test (1.22.x, ubuntu-latest) (push) Has been cancelled

This commit is contained in:
landaiqing
2026-02-10 14:45:18 +08:00
parent a530a79566
commit 5ce88674da
142 changed files with 12394 additions and 4280 deletions

View File

@@ -1,115 +1,220 @@
// Copyright (C) 2004-2021 Artifex Software, Inc.
//
// This file is part of MuPDF.
//
// MuPDF is free software: you can redistribute it and/or modify it under the
// terms of the GNU Affero General Public License as published by the Free
// Software Foundation, either version 3 of the License, or (at your option)
// any later version.
//
// MuPDF is distributed in the hope that it will be useful, but WITHOUT ANY
// WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
// FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
// details.
//
// You should have received a copy of the GNU Affero General Public License
// along with MuPDF. If not, see <https://www.gnu.org/licenses/agpl-3.0.en.html>
//
// Alternative licensing terms are available from the licensor.
// For commercial licensing, see <https://www.artifex.com/> or contact
// Artifex Software, Inc., 39 Mesa Street, Suite 108A, San Francisco,
// CA 94129, USA, for further information.
#ifndef MUPDF_FITZ_STORE_H
#define MUPDF_FITZ_STORE_H
#include "mupdf/fitz/system.h"
#include "mupdf/fitz/context.h"
#include "mupdf/fitz/output.h"
#include "mupdf/fitz/log.h"
/*
/**
Resource store
MuPDF stores decoded "objects" into a store for potential reuse.
If the size of the store gets too big, objects stored within it can
be evicted and freed to recover space. When MuPDF comes to decode
such an object, it will check to see if a version of this object is
already in the store - if it is, it will simply reuse it. If not, it
will decode it and place it into the store.
If the size of the store gets too big, objects stored within it
can be evicted and freed to recover space. When MuPDF comes to
decode such an object, it will check to see if a version of this
object is already in the store - if it is, it will simply reuse
it. If not, it will decode it and place it into the store.
All objects that can be placed into the store are derived from the
fz_storable type (i.e. this should be the first component of the
objects structure). This allows for consistent (thread safe)
reference counting, and includes a function that will be called to
free the object as soon as the reference count reaches zero.
All objects that can be placed into the store are derived from
the fz_storable type (i.e. this should be the first component of
the objects structure). This allows for consistent (thread safe)
reference counting, and includes a function that will be called
to free the object as soon as the reference count reaches zero.
Most objects offer fz_keep_XXXX/fz_drop_XXXX functions derived
from fz_keep_storable/fz_drop_storable. Creation of such objects
includes a call to FZ_INIT_STORABLE to set up the fz_storable header.
includes a call to FZ_INIT_STORABLE to set up the fz_storable
header.
*/
typedef struct fz_storable fz_storable;
typedef struct fz_storable_s fz_storable;
typedef struct fz_key_storable_s fz_key_storable;
/**
Function type for a function to drop a storable object.
Objects within the store are identified by type by comparing
their drop_fn pointers.
*/
typedef void (fz_store_drop_fn)(fz_context *, fz_storable *);
struct fz_storable_s {
/**
Function type for a function to check whether a storable
object can be dropped at the moment.
Return 0 for 'cannot be dropped', 1 otherwise.
*/
typedef int (fz_store_droppable_fn)(fz_context *, fz_storable *);
/**
Any storable object should include an fz_storable structure
at the start (by convention at least) of their structure.
(Unless it starts with an fz_key_storable, see below).
*/
struct fz_storable {
int refs;
fz_store_drop_fn *drop;
fz_store_droppable_fn *droppable;
};
struct fz_key_storable_s {
/**
Any storable object that can appear in the key of another
storable object should include an fz_key_storable structure
at the start (by convention at least) of their structure.
*/
typedef struct
{
fz_storable storable;
short store_key_refs;
};
} fz_key_storable;
/**
Macros to initialise a storable object.
*/
#define FZ_INIT_STORABLE(S_,RC,DROP) \
do { fz_storable *S = &(S_)->storable; S->refs = (RC); \
S->drop = (DROP); \
S->drop = (DROP); S->droppable = NULL; \
} while (0)
#define FZ_INIT_AWKWARD_STORABLE(S_,RC,DROP,DROPPABLE) \
do { fz_storable *S = &(S_)->storable; S->refs = (RC); \
S->drop = (DROP); S->droppable = (DROPPABLE); \
} while (0)
/**
Macro to initialise a key storable object.
*/
#define FZ_INIT_KEY_STORABLE(KS_,RC,DROP) \
do { fz_key_storable *KS = &(KS_)->key_storable; KS->store_key_refs = 0;\
FZ_INIT_STORABLE(KS,RC,DROP); \
} while (0)
/**
Increment the reference count for a storable object.
Returns the same pointer.
Never throws exceptions.
*/
void *fz_keep_storable(fz_context *, const fz_storable *);
/**
Decrement the reference count for a storable object. When the
reference count hits zero, the drop function for that object
is called to free the object.
Never throws exceptions.
*/
void fz_drop_storable(fz_context *, const fz_storable *);
/**
Increment the (normal) reference count for a key storable
object. Returns the same pointer.
Never throws exceptions.
*/
void *fz_keep_key_storable(fz_context *, const fz_key_storable *);
/**
Decrement the (normal) reference count for a storable object.
When the total reference count hits zero, the drop function for
that object is called to free the object.
Never throws exceptions.
*/
void fz_drop_key_storable(fz_context *, const fz_key_storable *);
/**
Increment the (key) reference count for a key storable
object. Returns the same pointer.
Never throws exceptions.
*/
void *fz_keep_key_storable_key(fz_context *, const fz_key_storable *);
/**
Decrement the (key) reference count for a storable object.
When the total reference count hits zero, the drop function for
that object is called to free the object.
Never throws exceptions.
*/
void fz_drop_key_storable_key(fz_context *, const fz_key_storable *);
/*
The store can be seen as a dictionary that maps keys to fz_storable
values. In order to allow keys of different types to be stored, we
have a structure full of functions for each key 'type'; this
fz_store_type pointer is stored with each key, and tells the store
how to perform certain operations (like taking/dropping a reference,
comparing two keys, outputting details for debugging etc).
/**
The store can be seen as a dictionary that maps keys to
fz_storable values. In order to allow keys of different types to
be stored, we have a structure full of functions for each key
'type'; this fz_store_type pointer is stored with each key, and
tells the store how to perform certain operations (like taking/
dropping a reference, comparing two keys, outputting details for
debugging etc).
The store uses a hash table internally for speed where possible. In
order for this to work, we need a mechanism for turning a generic
'key' into 'a hashable string'. For this purpose the type structure
contains a make_hash_key function pointer that maps from a void *
to a fz_store_hash structure. If make_hash_key function returns 0,
then the key is determined not to be hashable, and the value is
not stored in the hash table.
The store uses a hash table internally for speed where possible.
In order for this to work, we need a mechanism for turning a
generic 'key' into 'a hashable string'. For this purpose the
type structure contains a make_hash_key function pointer that
maps from a void * to a fz_store_hash structure. If
make_hash_key function returns 0, then the key is determined not
to be hashable, and the value is not stored in the hash table.
Some objects can be used both as values within the store, and as a
component of keys within the store. We refer to these objects as
"key storable" objects. In this case, we need to take additional
care to ensure that we do not end up keeping an item within the
store, purely because its value is referred to by another key in
the store.
Some objects can be used both as values within the store, and as
a component of keys within the store. We refer to these objects
as "key storable" objects. In this case, we need to take
additional care to ensure that we do not end up keeping an item
within the store, purely because its value is referred to by
another key in the store.
An example of this are fz_images in PDF files. Each fz_image is
placed into the store to enable it to be easily reused. When the
image is rendered, a pixmap is generated from the image, and the
pixmap is placed into the store so it can be reused on subsequent
renders. The image forms part of the key for the pixmap.
pixmap is placed into the store so it can be reused on
subsequent renders. The image forms part of the key for the
pixmap.
When we close the pdf document (and any associated pages/display
lists etc), we drop the images from the store. This may leave us
in the position of the images having non-zero reference counts
purely because they are used as part of the keys for the pixmaps.
purely because they are used as part of the keys for the
pixmaps.
We therefore use special reference counting functions to keep
track of these "key storable" items, and hence store the number of
references to these items that are used in keys.
track of these "key storable" items, and hence store the number
of references to these items that are used in keys.
When the number of references to an object == the number of
references to an object from keys in the store, we know that we can
remove all the items which have that object as part of the key.
This is done by running a pass over the store, 'reaping' those
items.
references to an object from keys in the store, we know that we
can remove all the items which have that object as part of the
key. This is done by running a pass over the store, 'reaping'
those items.
Reap passes are slower than we would like as they touch every
item in the store. We therefore provide a way to 'batch' such
reap passes together, using fz_defer_reap_start/fz_defer_reap_end
to bracket a region in which many may be triggered.
reap passes together, using fz_defer_reap_start/
fz_defer_reap_end to bracket a region in which many may be
triggered.
*/
typedef struct fz_store_hash_s
typedef struct
{
fz_store_drop_fn *drop;
union
@@ -128,60 +233,76 @@ typedef struct fz_store_hash_s
struct
{
int id;
char has_shape;
char has_group_alpha;
float m[4];
void *ptr;
} im; /* 20 bytes */
} im; /* 28 or 32 bytes */
struct
{
unsigned char src_md5[16];
unsigned char dst_md5[16];
unsigned int ri:2;
unsigned int bp:1;
unsigned int bpp16:1;
unsigned int format:1;
unsigned int proof:1;
unsigned int src_extras:5;
unsigned int dst_extras:5;
unsigned int copy_spots:1;
unsigned int bgr:1;
} link; /* 36 bytes */
} u;
} fz_store_hash; /* 40 or 44 bytes */
typedef struct fz_store_type_s
/**
Every type of object to be placed into the store defines an
fz_store_type. This contains the pointers to functions to
make hashes, manipulate keys, and check for needing reaping.
*/
typedef struct
{
const char *name;
int (*make_hash_key)(fz_context *ctx, fz_store_hash *hash, void *key);
void *(*keep_key)(fz_context *ctx, void *key);
void (*drop_key)(fz_context *ctx, void *key);
int (*cmp_key)(fz_context *ctx, void *a, void *b);
void (*format_key)(fz_context *ctx, char *buf, int size, void *key);
void (*format_key)(fz_context *ctx, char *buf, size_t size, void *key);
int (*needs_reap)(fz_context *ctx, void *key);
} fz_store_type;
/*
fz_store_new_context: Create a new store inside the context
/**
Create a new store inside the context
max: The maximum size (in bytes) that the store is allowed to grow
to. FZ_STORE_UNLIMITED means no limit.
max: The maximum size (in bytes) that the store is allowed to
grow to. FZ_STORE_UNLIMITED means no limit.
*/
void fz_new_store_context(fz_context *ctx, size_t max);
/*
fz_drop_store_context: Drop a reference to the store.
*/
void fz_drop_store_context(fz_context *ctx);
/**
Increment the reference count for the store context. Returns
the same pointer.
/*
fz_keep_store_context: Take a reference to the store.
Never throws exceptions.
*/
fz_store *fz_keep_store_context(fz_context *ctx);
/*
fz_store_item: Add an item to the store.
/**
Decrement the reference count for the store context. When the
reference count hits zero, the store context is freed.
Add an item into the store, returning NULL for success. If an item
with the same key is found in the store, then our item will not be
inserted, and the function will return a pointer to that value
instead. This function takes its own reference to val, as required
(i.e. the caller maintains ownership of its own reference).
Never throws exceptions.
*/
void fz_drop_store_context(fz_context *ctx);
/**
Add an item to the store.
Add an item into the store, returning NULL for success. If an
item with the same key is found in the store, then our item will
not be inserted, and the function will return a pointer to that
value instead. This function takes its own reference to val, as
required (i.e. the caller maintains ownership of its own
reference).
key: The key used to index the item.
@@ -194,28 +315,29 @@ fz_store *fz_keep_store_context(fz_context *ctx);
*/
void *fz_store_item(fz_context *ctx, void *key, void *val, size_t itemsize, const fz_store_type *type);
/*
fz_find_item: Find an item within the store.
/**
Find an item within the store.
drop: The function used to free the value (to ensure we get a value
of the correct type).
drop: The function used to free the value (to ensure we get a
value of the correct type).
key: The key used to index the item.
type: Functions used to manipulate the key.
Returns NULL for not found, otherwise returns a pointer to the value
indexed by key to which a reference has been taken.
Returns NULL for not found, otherwise returns a pointer to the
value indexed by key to which a reference has been taken.
*/
void *fz_find_item(fz_context *ctx, fz_store_drop_fn *drop, void *key, const fz_store_type *type);
/*
fz_remove_item: Remove an item from the store.
/**
Remove an item from the store.
If an item indexed by the given key exists in the store, remove it.
If an item indexed by the given key exists in the store, remove
it.
drop: The function used to free the value (to ensure we get a value
of the correct type).
drop: The function used to free the value (to ensure we get a
value of the correct type).
key: The key used to find the item to remove.
@@ -223,16 +345,16 @@ void *fz_find_item(fz_context *ctx, fz_store_drop_fn *drop, void *key, const fz_
*/
void fz_remove_item(fz_context *ctx, fz_store_drop_fn *drop, void *key, const fz_store_type *type);
/*
fz_empty_store: Evict everything from the store.
/**
Evict every item from the store.
*/
void fz_empty_store(fz_context *ctx);
/*
fz_store_scavenge: Internal function used as part of the scavenging
/**
Internal function used as part of the scavenging
allocator; when we fail to allocate memory, before returning a
failure to the caller, we try to scavenge space within the store by
evicting at least 'size' bytes. The allocator then retries.
failure to the caller, we try to scavenge space within the store
by evicting at least 'size' bytes. The allocator then retries.
size: The number of bytes we are trying to have free.
@@ -242,8 +364,8 @@ void fz_empty_store(fz_context *ctx);
*/
int fz_store_scavenge(fz_context *ctx, size_t size, int *phase);
/*
fz_store_scavenge_external: External function for callers to use
/**
External function for callers to use
to scavenge while trying allocations.
size: The number of bytes we are trying to have free.
@@ -254,28 +376,42 @@ int fz_store_scavenge(fz_context *ctx, size_t size, int *phase);
*/
int fz_store_scavenge_external(fz_context *ctx, size_t size, int *phase);
/*
fz_shrink_store: Evict items from the store until the total size of
/**
Evict items from the store until the total size of
the objects in the store is reduced to a given percentage of its
current size.
percent: %age of current size to reduce the store to.
Returns non zero if we managed to free enough memory, zero otherwise.
Returns non zero if we managed to free enough memory, zero
otherwise.
*/
int fz_shrink_store(fz_context *ctx, unsigned int percent);
/**
Callback function called by fz_filter_store on every item within
the store.
Return 1 to drop the item from the store, 0 to retain.
*/
typedef int (fz_store_filter_fn)(fz_context *ctx, void *arg, void *key);
/**
Filter every element in the store with a matching type with the
given function.
If the function returns 1 for an element, drop the element.
*/
void fz_filter_store(fz_context *ctx, fz_store_filter_fn *fn, void *arg, const fz_store_type *type);
/*
fz_debug_store: Dump the contents of the store for debugging.
/**
Output debugging information for the current state of the store
to the given output channel.
*/
void fz_debug_store(fz_context *ctx);
void fz_debug_store(fz_context *ctx, fz_output *out);
/*
fz_defer_reap_start: Increment the defer reap count.
/**
Increment the defer reap count.
No reap operations will take place (except for those
triggered by an immediate failed malloc) until the
@@ -289,8 +425,8 @@ void fz_debug_store(fz_context *ctx);
*/
void fz_defer_reap_start(fz_context *ctx);
/*
fz_defer_reap_end: Decrement the defer reap count.
/**
Decrement the defer reap count.
If the defer reap count returns to 0, and the store
has reapable objects in, a reap pass will begin.
@@ -303,4 +439,18 @@ void fz_defer_reap_start(fz_context *ctx);
*/
void fz_defer_reap_end(fz_context *ctx);
#ifdef ENABLE_STORE_LOGGING
void fz_log_dump_store(fz_context *ctx, const char *fmt, ...);
#define FZ_LOG_STORE(CTX, ...) fz_log_module(CTX, "STORE", __VA_ARGS__)
#define FZ_LOG_DUMP_STORE(...) fz_log_dump_store(__VA_ARGS__)
#else
#define FZ_LOG_STORE(...) do {} while (0)
#define FZ_LOG_DUMP_STORE(...) do {} while (0)
#endif
#endif