mirror of https://github.com/GNOME/gimp.git
794 lines
19 KiB
C
794 lines
19 KiB
C
/* GIMP - The GNU Image Manipulation Program
|
|
* Copyright (C) 1995 Spencer Kimball and Peter Mattis
|
|
*
|
|
* This program is free software: you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation; either version 3 of the License, or
|
|
* (at your option) any later version.
|
|
*
|
|
* This program 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 General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
#include "config.h"
|
|
|
|
#include <errno.h>
|
|
#include <fcntl.h>
|
|
#include <sys/types.h>
|
|
|
|
#ifdef HAVE_UNISTD_H
|
|
#include <unistd.h>
|
|
#endif
|
|
|
|
#include <glib-object.h>
|
|
#include <glib/gstdio.h>
|
|
|
|
#include "libgimpbase/gimpbase.h"
|
|
#include "libgimpconfig/gimpconfig.h"
|
|
|
|
#ifdef G_OS_WIN32
|
|
#include <windows.h>
|
|
#include "libgimpbase/gimpwin32-io.h"
|
|
#endif
|
|
|
|
#include "base-types.h"
|
|
|
|
#ifndef _O_BINARY
|
|
#define _O_BINARY 0
|
|
#endif
|
|
#ifndef _O_TEMPORARY
|
|
#define _O_TEMPORARY 0
|
|
#endif
|
|
|
|
#include "base-utils.h"
|
|
#include "tile.h"
|
|
#include "tile-rowhints.h"
|
|
#include "tile-swap.h"
|
|
#include "tile-private.h"
|
|
#include "tile-cache.h"
|
|
|
|
#include "gimp-intl.h"
|
|
|
|
typedef enum
|
|
{
|
|
SWAP_IN = 1,
|
|
SWAP_OUT,
|
|
SWAP_DELETE
|
|
} SwapCommand;
|
|
|
|
typedef gint (* SwapFunc) (gint fd,
|
|
Tile *tile,
|
|
SwapCommand cmd);
|
|
|
|
|
|
|
|
#define MAX_OPEN_SWAP_FILES 16
|
|
|
|
|
|
typedef struct _SwapFile SwapFile;
|
|
typedef struct _SwapFileGap SwapFileGap;
|
|
|
|
struct _SwapFile
|
|
{
|
|
gchar *filename;
|
|
gint fd;
|
|
GList *gaps;
|
|
gint64 swap_file_end;
|
|
gint64 cur_position;
|
|
};
|
|
|
|
struct _SwapFileGap
|
|
{
|
|
gint64 start;
|
|
gint64 end;
|
|
};
|
|
|
|
|
|
static void tile_swap_command (Tile *tile,
|
|
gint command);
|
|
static void tile_swap_default_in (SwapFile *swap_file,
|
|
Tile *tile);
|
|
static void tile_swap_default_out (SwapFile *swap_file,
|
|
Tile *tile);
|
|
static void tile_swap_default_delete (SwapFile *swap_file,
|
|
Tile *tile);
|
|
|
|
static gint64 tile_swap_find_offset (SwapFile *swap_file,
|
|
gint64 bytes);
|
|
static void tile_swap_open (SwapFile *swap_file);
|
|
static void tile_swap_resize (SwapFile *swap_file,
|
|
gint64 new_size);
|
|
static SwapFileGap * tile_swap_gap_new (gint64 start,
|
|
gint64 end);
|
|
static void tile_swap_gap_destroy (SwapFileGap *gap);
|
|
|
|
|
|
static SwapFile * gimp_swap_file = NULL;
|
|
|
|
static const gint64 swap_file_grow = 1024 * TILE_WIDTH * TILE_HEIGHT * 4;
|
|
|
|
static gboolean seek_err_msg = TRUE;
|
|
static gboolean read_err_msg = TRUE;
|
|
static gboolean write_err_msg = TRUE;
|
|
|
|
#ifdef TILE_PROFILING
|
|
static gulong tile_total_seek = 0;
|
|
|
|
/* how many tiles were swapped out under cache pressure but never
|
|
swapped back in? This does not count idle swapped tiles, as those
|
|
do not contribute to any perceived load or latency */
|
|
static gulong tile_total_wasted_swapout = 0;
|
|
|
|
/* total tile flushes under cache pressure */
|
|
gulong tile_total_zorched = 0;
|
|
gulong tile_total_zorched_swapout = 0;
|
|
static gulong tile_total_zorched_swapin = 0;
|
|
|
|
/* total tiles swapped out to swap file (not total calls to swap out;
|
|
this only counts actual flushes to disk) */
|
|
static gulong tile_total_swapout = 0;
|
|
static gulong tile_unique_swapout = 0;
|
|
gulong tile_idle_swapout = 0;
|
|
|
|
/* total tiles swapped in from swap file (not total calls to swap in;
|
|
this only counts actual tile reads from disk) */
|
|
static gulong tile_total_swapin = 0;
|
|
static gulong tile_unique_swapin = 0;
|
|
|
|
/* total dead time spent waiting to read or write */
|
|
static glong tile_total_swapwait_sec = 0;
|
|
static glong tile_total_swapwait_usec = 0;
|
|
|
|
/* total time spent in tile cache due to cache pressure */
|
|
glong tile_total_interactive_sec = 0;
|
|
glong tile_total_interactive_usec = 0;
|
|
|
|
#endif
|
|
|
|
#ifdef G_OS_WIN32
|
|
|
|
#define LARGE_SEEK(f, o, w) _lseeki64 (f, o, w)
|
|
#define LARGE_TRUNCATE(f, s) win32_large_truncate (f, s)
|
|
|
|
static gint
|
|
win32_large_truncate (gint fd,
|
|
gint64 size)
|
|
{
|
|
if (LARGE_SEEK (fd, size, SEEK_SET) == size &&
|
|
SetEndOfFile ((HANDLE) _get_osfhandle (fd)))
|
|
return 0;
|
|
else
|
|
return -1;
|
|
}
|
|
|
|
#else
|
|
|
|
#define LARGE_SEEK(f, o, t) lseek (f, o, t)
|
|
#define LARGE_TRUNCATE(f, s) ftruncate (f, s)
|
|
|
|
#endif
|
|
|
|
|
|
#ifdef GIMP_UNSTABLE
|
|
static void
|
|
tile_swap_print_gaps (SwapFile *swap_file)
|
|
{
|
|
GList *list;
|
|
|
|
for (list = swap_file->gaps; list; list = list->next)
|
|
{
|
|
SwapFileGap *gap = list->data;
|
|
|
|
g_print (" %"G_GINT64_FORMAT" - %"G_GINT64_FORMAT"\n",
|
|
gap->start, gap->end);
|
|
}
|
|
}
|
|
#endif
|
|
|
|
void
|
|
tile_swap_init (const gchar *path)
|
|
{
|
|
gchar *basename;
|
|
gchar *dirname;
|
|
|
|
g_return_if_fail (gimp_swap_file == NULL);
|
|
g_return_if_fail (path != NULL);
|
|
|
|
dirname = gimp_config_path_expand (path, TRUE, NULL);
|
|
basename = g_strdup_printf ("gimpswap.%lu", (unsigned long) get_pid ());
|
|
|
|
/* create the swap directory if it doesn't exist */
|
|
if (! g_file_test (dirname, G_FILE_TEST_EXISTS))
|
|
g_mkdir_with_parents (dirname,
|
|
S_IRUSR | S_IXUSR | S_IWUSR |
|
|
S_IRGRP | S_IXGRP |
|
|
S_IROTH | S_IXOTH);
|
|
|
|
gimp_swap_file = g_slice_new (SwapFile);
|
|
|
|
gimp_swap_file->filename = g_build_filename (dirname, basename, NULL);
|
|
gimp_swap_file->gaps = NULL;
|
|
gimp_swap_file->swap_file_end = 0;
|
|
gimp_swap_file->cur_position = 0;
|
|
gimp_swap_file->fd = -1;
|
|
|
|
g_free (basename);
|
|
g_free (dirname);
|
|
}
|
|
|
|
void
|
|
tile_swap_exit (void)
|
|
{
|
|
|
|
#ifdef TILE_PROFILING
|
|
extern int tile_exist_peak;
|
|
|
|
g_printerr ("\n\nPeak Tile usage: %d Tile structs\n\n",
|
|
tile_exist_peak);
|
|
|
|
g_printerr ("Total tiles swapped out to disk: %lu\n", tile_total_swapout);
|
|
g_printerr ("Unique tiles swapped out to disk: %lu\n", tile_unique_swapout);
|
|
g_printerr ("Total tiles swapped in from disk: %lu\n", tile_total_swapin);
|
|
g_printerr ("Unique tiles swapped in from disk: %lu\n", tile_unique_swapin);
|
|
g_printerr ("Tiles swapped out by idle swapper: %lu\n", tile_idle_swapout);
|
|
g_printerr ("Total seeks during swapping: %lu\n", tile_total_seek);
|
|
g_printerr ("Total time spent in swap: %f seconds\n\n",
|
|
tile_total_swapwait_sec + 0.000001 * tile_total_swapwait_usec);
|
|
|
|
g_printerr ("Total zorched tiles: %lu\n", tile_total_zorched);
|
|
g_printerr ("Total zorched tiles swapped out: %lu\n",
|
|
tile_total_zorched_swapout);
|
|
g_printerr ("Total zorched tiles swapped back in: %lu\n",
|
|
tile_total_zorched_swapin);
|
|
g_printerr ("Total zorched tiles wasted after swapping out: %lu\n",
|
|
tile_total_wasted_swapout);
|
|
g_printerr ("Total interactive swap/cache delay: %f seconds\n\n",
|
|
tile_total_interactive_sec +
|
|
0.000001 * tile_total_interactive_usec);
|
|
|
|
#endif
|
|
|
|
if (tile_global_refcount () != 0)
|
|
g_warning ("tile ref count balance: %d\n", tile_global_refcount ());
|
|
|
|
g_return_if_fail (gimp_swap_file != NULL);
|
|
|
|
#ifdef GIMP_UNSTABLE
|
|
if (gimp_swap_file->swap_file_end != 0)
|
|
{
|
|
g_warning ("swap file not empty: \"%s\"\n",
|
|
gimp_filename_to_utf8 (gimp_swap_file->filename));
|
|
tile_swap_print_gaps (gimp_swap_file);
|
|
}
|
|
#endif
|
|
|
|
#ifdef G_OS_WIN32
|
|
/* should close before unlink */
|
|
if (gimp_swap_file->fd > 0)
|
|
{
|
|
close (gimp_swap_file->fd);
|
|
gimp_swap_file->fd = -1;
|
|
}
|
|
#endif
|
|
|
|
g_unlink (gimp_swap_file->filename);
|
|
|
|
g_free (gimp_swap_file->filename);
|
|
g_slice_free (SwapFile, gimp_swap_file);
|
|
|
|
gimp_swap_file = NULL;
|
|
}
|
|
|
|
/* check if we can open a swap file */
|
|
gboolean
|
|
tile_swap_test (void)
|
|
{
|
|
g_return_val_if_fail (gimp_swap_file != NULL, FALSE);
|
|
|
|
/* make sure this duplicates the open() call from tile_swap_open() */
|
|
gimp_swap_file->fd = g_open (gimp_swap_file->filename,
|
|
O_CREAT | O_RDWR | _O_BINARY | _O_TEMPORARY,
|
|
S_IRUSR | S_IWUSR);
|
|
|
|
if (gimp_swap_file->fd != -1)
|
|
{
|
|
close (gimp_swap_file->fd);
|
|
gimp_swap_file->fd = -1;
|
|
g_unlink (gimp_swap_file->filename);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
void
|
|
tile_swap_in (Tile *tile)
|
|
{
|
|
if (tile->swap_offset == -1)
|
|
{
|
|
tile_alloc (tile);
|
|
return;
|
|
}
|
|
|
|
tile_swap_command (tile, SWAP_IN);
|
|
}
|
|
|
|
void
|
|
tile_swap_out (Tile *tile)
|
|
{
|
|
tile_swap_command (tile, SWAP_OUT);
|
|
}
|
|
|
|
void
|
|
tile_swap_delete (Tile *tile)
|
|
{
|
|
tile_swap_command (tile, SWAP_DELETE);
|
|
}
|
|
|
|
static void
|
|
tile_swap_command (Tile *tile,
|
|
gint command)
|
|
{
|
|
if (gimp_swap_file->fd == -1)
|
|
{
|
|
tile_swap_open (gimp_swap_file);
|
|
|
|
if (G_UNLIKELY (gimp_swap_file->fd == -1))
|
|
return;
|
|
}
|
|
|
|
switch (command)
|
|
{
|
|
case SWAP_IN:
|
|
tile_swap_default_in (gimp_swap_file, tile);
|
|
break;
|
|
case SWAP_OUT:
|
|
tile_swap_default_out (gimp_swap_file, tile);
|
|
break;
|
|
case SWAP_DELETE:
|
|
tile_swap_default_delete (gimp_swap_file, tile);
|
|
break;
|
|
}
|
|
}
|
|
|
|
/* The actual swap file code. The swap file consists of tiles
|
|
* which have been moved out to disk in order to conserve memory.
|
|
* The swap file format is free form. Any tile in memory may
|
|
* end up anywhere on disk.
|
|
* An actual tile in the swap file consists only of the tile data.
|
|
* The offset of the tile on disk is stored in the tile data structure
|
|
* in memory.
|
|
*/
|
|
|
|
static void
|
|
tile_swap_default_in (SwapFile *swap_file,
|
|
Tile *tile)
|
|
{
|
|
gint nleft;
|
|
gint64 offset;
|
|
#ifdef TILE_PROFILING
|
|
GTimeVal now;
|
|
GTimeVal later;
|
|
#endif
|
|
|
|
if (tile->data)
|
|
return;
|
|
|
|
tile_cache_suspend_idle_swapper();
|
|
|
|
#ifdef TILE_PROFILING
|
|
g_get_current_time (&now);
|
|
tile_total_swapin++;
|
|
|
|
if (tile->zorched)
|
|
tile_total_zorched_swapin++;
|
|
|
|
if (!tile->inonce)
|
|
tile_unique_swapin++;
|
|
|
|
tile->inonce = TRUE;
|
|
#endif
|
|
|
|
if (swap_file->cur_position != tile->swap_offset)
|
|
{
|
|
swap_file->cur_position = tile->swap_offset;
|
|
|
|
#ifdef TILE_PROFILING
|
|
tile_total_seek++;
|
|
#endif
|
|
|
|
offset = LARGE_SEEK (swap_file->fd, tile->swap_offset, SEEK_SET);
|
|
if (offset == -1)
|
|
{
|
|
if (seek_err_msg)
|
|
g_message ("unable to seek to tile location on disk: %s",
|
|
g_strerror (errno));
|
|
seek_err_msg = FALSE;
|
|
return;
|
|
}
|
|
}
|
|
|
|
tile_alloc (tile);
|
|
|
|
nleft = tile->size;
|
|
while (nleft > 0)
|
|
{
|
|
gint err;
|
|
|
|
do
|
|
{
|
|
err = read (swap_file->fd, tile->data + tile->size - nleft, nleft);
|
|
}
|
|
while ((err == -1) && ((errno == EAGAIN) || (errno == EINTR)));
|
|
|
|
if (err <= 0)
|
|
{
|
|
if (read_err_msg)
|
|
g_message ("unable to read tile data from disk: "
|
|
"%s (%d/%d bytes read)",
|
|
g_strerror (errno), err, nleft);
|
|
read_err_msg = FALSE;
|
|
return;
|
|
}
|
|
|
|
nleft -= err;
|
|
}
|
|
|
|
#ifdef TILE_PROFILING
|
|
g_get_current_time (&later);
|
|
tile_total_swapwait_usec += later.tv_usec - now.tv_usec;
|
|
tile_total_swapwait_sec += later.tv_sec - now.tv_sec;
|
|
|
|
if (tile_total_swapwait_usec < 0)
|
|
{
|
|
tile_total_swapwait_usec += 1000000;
|
|
tile_total_swapwait_sec--;
|
|
}
|
|
|
|
if (tile_total_swapwait_usec > 1000000)
|
|
{
|
|
tile_total_swapwait_usec -= 1000000;
|
|
tile_total_swapwait_sec++;
|
|
}
|
|
|
|
tile_total_interactive_usec += later.tv_usec - now.tv_usec;
|
|
tile_total_interactive_sec += later.tv_sec - now.tv_sec;
|
|
|
|
if (tile_total_interactive_usec < 0)
|
|
{
|
|
tile_total_interactive_usec += 1000000;
|
|
tile_total_interactive_sec--;
|
|
}
|
|
|
|
if (tile_total_interactive_usec > 1000000)
|
|
{
|
|
tile_total_interactive_usec -= 1000000;
|
|
tile_total_interactive_sec++;
|
|
}
|
|
|
|
tile->zorched = FALSE;
|
|
tile->zorchout = FALSE;
|
|
#endif
|
|
|
|
swap_file->cur_position += tile->size;
|
|
|
|
/* Do not delete the swap from the file */
|
|
/* tile_swap_default_delete (swap_file, fd, tile); */
|
|
|
|
read_err_msg = seek_err_msg = TRUE;
|
|
}
|
|
|
|
static void
|
|
tile_swap_default_out (SwapFile *swap_file,
|
|
Tile *tile)
|
|
{
|
|
gint bytes;
|
|
gint nleft;
|
|
gint64 offset;
|
|
gint64 newpos;
|
|
#ifdef TILE_PROFILING
|
|
GTimeVal now;
|
|
GTimeVal later;
|
|
g_get_current_time(&now);
|
|
tile_total_swapout++;
|
|
|
|
if (!tile->outonce)
|
|
tile_unique_swapout++;
|
|
|
|
tile->outonce = TRUE;
|
|
#endif
|
|
|
|
bytes = TILE_WIDTH * TILE_HEIGHT * tile->bpp;
|
|
|
|
/* If there is already a valid swap_offset, use it */
|
|
if (tile->swap_offset == -1)
|
|
newpos = tile_swap_find_offset (swap_file, bytes);
|
|
else
|
|
newpos = tile->swap_offset;
|
|
|
|
if (swap_file->cur_position != newpos)
|
|
{
|
|
|
|
#ifdef TILE_PROFILING
|
|
tile_total_seek++;
|
|
#endif
|
|
|
|
offset = LARGE_SEEK (swap_file->fd, newpos, SEEK_SET);
|
|
|
|
if (offset == -1)
|
|
{
|
|
if (seek_err_msg)
|
|
g_message ("unable to seek to tile location on disk: %s",
|
|
g_strerror (errno));
|
|
seek_err_msg = FALSE;
|
|
return;
|
|
}
|
|
|
|
swap_file->cur_position = newpos;
|
|
}
|
|
|
|
nleft = tile->size;
|
|
while (nleft > 0)
|
|
{
|
|
gint err = write (swap_file->fd, tile->data + tile->size - nleft, nleft);
|
|
|
|
if (err <= 0)
|
|
{
|
|
if (write_err_msg)
|
|
g_message ("unable to write tile data to disk: "
|
|
"%s (%d/%d bytes written)",
|
|
g_strerror (errno), err, nleft);
|
|
write_err_msg = FALSE;
|
|
return;
|
|
}
|
|
|
|
nleft -= err;
|
|
}
|
|
|
|
#ifdef TILE_PROFILING
|
|
g_get_current_time (&later);
|
|
tile_total_swapwait_usec += later.tv_usec - now.tv_usec;
|
|
tile_total_swapwait_sec += later.tv_sec - now.tv_sec;
|
|
|
|
if (tile_total_swapwait_usec < 0)
|
|
{
|
|
tile_total_swapwait_usec += 1000000;
|
|
tile_total_swapwait_sec--;
|
|
}
|
|
|
|
if (tile_total_swapwait_usec > 1000000)
|
|
{
|
|
tile_total_swapwait_usec -= 1000000;
|
|
tile_total_swapwait_sec++;
|
|
}
|
|
|
|
#endif
|
|
|
|
swap_file->cur_position += tile->size;
|
|
|
|
/* Do NOT free tile->data because we may be pre-swapping.
|
|
* tile->data is freed in tile_cache_zorch_next
|
|
*/
|
|
tile->dirty = FALSE;
|
|
tile->swap_offset = newpos;
|
|
|
|
write_err_msg = seek_err_msg = TRUE;
|
|
}
|
|
|
|
static void
|
|
tile_swap_default_delete (SwapFile *swap_file,
|
|
Tile *tile)
|
|
{
|
|
SwapFileGap *gap;
|
|
SwapFileGap *gap2;
|
|
GList *tmp;
|
|
GList *tmp2;
|
|
gint64 start;
|
|
gint64 end;
|
|
|
|
if (tile->swap_offset == -1)
|
|
return;
|
|
|
|
#ifdef TILE_PROFILING
|
|
if (tile->zorchout)
|
|
tile_total_wasted_swapout++;
|
|
|
|
tile->zorched=FALSE;
|
|
tile->zorchout=FALSE;
|
|
#endif
|
|
|
|
start = tile->swap_offset;
|
|
end = start + TILE_WIDTH * TILE_HEIGHT * tile->bpp;
|
|
tile->swap_offset = -1;
|
|
|
|
tmp = swap_file->gaps;
|
|
while (tmp)
|
|
{
|
|
gap = tmp->data;
|
|
|
|
if (end == gap->start)
|
|
{
|
|
gap->start = start;
|
|
|
|
if (tmp->prev)
|
|
{
|
|
gap2 = tmp->prev->data;
|
|
|
|
if (gap->start == gap2->end)
|
|
{
|
|
gap2->end = gap->end;
|
|
tile_swap_gap_destroy (gap);
|
|
swap_file->gaps =
|
|
g_list_remove_link (swap_file->gaps, tmp);
|
|
g_list_free (tmp);
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
else if (start == gap->end)
|
|
{
|
|
gap->end = end;
|
|
|
|
if (tmp->next)
|
|
{
|
|
gap2 = tmp->next->data;
|
|
|
|
if (gap->end == gap2->start)
|
|
{
|
|
gap2->start = gap->start;
|
|
tile_swap_gap_destroy (gap);
|
|
swap_file->gaps =
|
|
g_list_remove_link (swap_file->gaps, tmp);
|
|
g_list_free (tmp);
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
else if (end < gap->start)
|
|
{
|
|
gap = tile_swap_gap_new (start, end);
|
|
|
|
tmp2 = g_list_alloc ();
|
|
tmp2->data = gap;
|
|
tmp2->next = tmp;
|
|
tmp2->prev = tmp->prev;
|
|
if (tmp->prev)
|
|
tmp->prev->next = tmp2;
|
|
tmp->prev = tmp2;
|
|
|
|
if (tmp == swap_file->gaps)
|
|
swap_file->gaps = tmp2;
|
|
break;
|
|
}
|
|
else if (!tmp->next)
|
|
{
|
|
gap = tile_swap_gap_new (start, end);
|
|
tmp->next = g_list_alloc ();
|
|
tmp->next->data = gap;
|
|
tmp->next->prev = tmp;
|
|
break;
|
|
}
|
|
|
|
tmp = tmp->next;
|
|
}
|
|
|
|
if (!swap_file->gaps)
|
|
{
|
|
gap = tile_swap_gap_new (start, end);
|
|
swap_file->gaps = g_list_append (swap_file->gaps, gap);
|
|
}
|
|
|
|
tmp = g_list_last (swap_file->gaps);
|
|
gap = tmp->data;
|
|
|
|
if (gap->end == swap_file->swap_file_end)
|
|
{
|
|
tile_swap_resize (swap_file, gap->start);
|
|
tile_swap_gap_destroy (gap);
|
|
swap_file->gaps = g_list_remove_link (swap_file->gaps, tmp);
|
|
g_list_free (tmp);
|
|
}
|
|
}
|
|
|
|
static void
|
|
tile_swap_open (SwapFile *swap_file)
|
|
{
|
|
g_return_if_fail (swap_file->fd == -1);
|
|
|
|
/* duplicate this open() call in tile_swap_test() */
|
|
swap_file->fd = g_open (swap_file->filename,
|
|
O_CREAT | O_RDWR | _O_BINARY | _O_TEMPORARY,
|
|
S_IRUSR | S_IWUSR);
|
|
|
|
if (swap_file->fd == -1)
|
|
g_message (_("Unable to open swap file. GIMP has run out of memory "
|
|
"and cannot use the swap file. Some parts of your images "
|
|
"may be corrupted. Try to save your work using different "
|
|
"filenames, restart GIMP and check the location of the "
|
|
"swap directory in your Preferences."));
|
|
}
|
|
|
|
static void
|
|
tile_swap_resize (SwapFile *swap_file,
|
|
gint64 new_size)
|
|
{
|
|
if (swap_file->swap_file_end > new_size)
|
|
{
|
|
if (LARGE_TRUNCATE (swap_file->fd, new_size) != 0)
|
|
{
|
|
g_message (_("Failed to resize swap file: %s"), g_strerror (errno));
|
|
return;
|
|
}
|
|
}
|
|
|
|
swap_file->swap_file_end = new_size;
|
|
}
|
|
|
|
static gint64
|
|
tile_swap_find_offset (SwapFile *swap_file,
|
|
gint64 bytes)
|
|
{
|
|
SwapFileGap *gap;
|
|
GList *tmp;
|
|
gint64 offset;
|
|
|
|
tmp = swap_file->gaps;
|
|
while (tmp)
|
|
{
|
|
gap = tmp->data;
|
|
|
|
if ((gap->end - gap->start) >= bytes)
|
|
{
|
|
offset = gap->start;
|
|
gap->start += bytes;
|
|
|
|
if (gap->start == gap->end)
|
|
{
|
|
tile_swap_gap_destroy (gap);
|
|
swap_file->gaps = g_list_remove_link (swap_file->gaps, tmp);
|
|
g_list_free (tmp);
|
|
}
|
|
|
|
return offset;
|
|
}
|
|
|
|
tmp = tmp->next;
|
|
}
|
|
|
|
offset = swap_file->swap_file_end;
|
|
|
|
tile_swap_resize (swap_file, swap_file->swap_file_end + swap_file_grow);
|
|
|
|
if ((offset + bytes) < (swap_file->swap_file_end))
|
|
{
|
|
gap = tile_swap_gap_new (offset + bytes, swap_file->swap_file_end);
|
|
swap_file->gaps = g_list_append (swap_file->gaps, gap);
|
|
}
|
|
|
|
return offset;
|
|
}
|
|
|
|
static SwapFileGap *
|
|
tile_swap_gap_new (gint64 start,
|
|
gint64 end)
|
|
{
|
|
SwapFileGap *gap = g_slice_new (SwapFileGap);
|
|
|
|
gap->start = start;
|
|
gap->end = end;
|
|
|
|
return gap;
|
|
}
|
|
|
|
static void
|
|
tile_swap_gap_destroy (SwapFileGap *gap)
|
|
{
|
|
g_slice_free (SwapFileGap, gap);
|
|
}
|