]> Pileus Git - ~andy/gtk/blob - tests/pixbuf-lowmem.c
172c162fcfa3e18a629f93bc63ac4fa2b127ff24
[~andy/gtk] / tests / pixbuf-lowmem.c
1 /* -*- Mode: C; c-basic-offset: 2; -*- */
2 /* GdkPixbuf library - test loaders
3  *
4  * Copyright (C) 2001 Søren Sandmann (sandmann@daimi.au.dk)
5  *
6  * This program is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; either version 2 of the License, or
9  * (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program; if not, write to the Free Software
18  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
19  */
20
21 #include "config.h"
22 #include "gdk-pixbuf/gdk-pixbuf.h"
23 #include <stdio.h>
24 #include <stdlib.h>
25 #include <time.h>
26 #include <string.h>
27
28 #define PRETEND_MEM_SIZE (16 * 1024 * 1024)
29 #define REMAINING_MEM_SIZE 100000
30
31
32 static int current_allocation = 0;
33 static int max_allocation = 0;
34
35 #define HEADER_SPACE sizeof(void*)
36
37 static gpointer
38 record_bytes (gpointer mem, gsize bytes)
39 {
40   if (mem == NULL ||
41       (current_allocation + bytes) > max_allocation)
42     {
43       if (mem)
44         free (mem);
45       
46       return NULL;
47     }
48   
49   *(void **)mem = GINT_TO_POINTER (bytes);
50
51   g_assert (GPOINTER_TO_INT (*(void**)mem) == bytes);
52   
53   g_assert (current_allocation >= 0);
54   current_allocation += bytes;
55   g_assert (current_allocation >= 0);
56   
57   g_assert ( mem == (void*) ((((char*)mem) + HEADER_SPACE) - HEADER_SPACE) );
58   return ((char*)mem) + HEADER_SPACE;
59 }
60
61 static gpointer
62 limited_try_malloc (gsize n_bytes)
63 {
64   return record_bytes (malloc (n_bytes + HEADER_SPACE), n_bytes);
65 }
66
67 static gpointer
68 limited_malloc (gsize n_bytes)
69 {
70   return limited_try_malloc (n_bytes);
71 }
72
73 static gpointer
74 limited_calloc (gsize n_blocks,
75                 gsize n_block_bytes)
76 {
77   int bytes = n_blocks * n_block_bytes + HEADER_SPACE;
78   gpointer mem = malloc (bytes);
79   memset (mem, 0, bytes);
80   return record_bytes (mem, n_blocks * n_block_bytes);
81 }
82
83 static void
84 limited_free (gpointer mem)
85 {
86   gpointer real = ((char*)mem) - HEADER_SPACE;
87
88   g_assert (current_allocation >= 0);
89   current_allocation -= GPOINTER_TO_INT (*(void**)real);
90   g_assert (current_allocation >= 0);
91   
92   free (real);
93 }
94
95 static gpointer
96 limited_try_realloc (gpointer mem,
97                      gsize    n_bytes)
98 {
99   if (mem == NULL)
100     {
101       return limited_try_malloc (n_bytes);
102     }
103   else
104     {
105       gpointer real;
106
107       g_assert (mem);
108
109       real = ((char*)mem) - HEADER_SPACE;
110       
111       g_assert (current_allocation >= 0);
112       current_allocation -= GPOINTER_TO_INT (*(void**)real);
113       g_assert (current_allocation >= 0);
114
115       return record_bytes (realloc (real, n_bytes + HEADER_SPACE), n_bytes);
116     }
117 }
118
119 static gpointer
120 limited_realloc (gpointer mem,
121                  gsize    n_bytes)
122 {
123   return limited_try_realloc (mem, n_bytes);
124 }
125
126 static GMemVTable limited_table = {
127   limited_malloc,
128   limited_realloc,
129   limited_free,
130   limited_calloc,
131   limited_try_malloc,
132   limited_try_realloc
133 };
134
135 static void
136 mem_test (const gchar *bytes, gsize len)
137 {
138   gboolean did_fail = FALSE;
139   GError *err = NULL;
140   GdkPixbufLoader *loader; 
141   GList *loaders = NULL;
142   GList *i;
143   
144   do {
145     loader = gdk_pixbuf_loader_new ();
146     gdk_pixbuf_loader_write (loader, (guchar *) bytes, len, &err);
147     if (err)
148       {
149         g_error_free (err);
150         err = NULL;
151         did_fail = TRUE;
152       }
153     gdk_pixbuf_loader_close (loader, NULL);
154     if (err)
155       {
156         g_error_free (err);
157         err = NULL;
158         did_fail = TRUE;
159       }
160     loaders = g_list_prepend (loaders, loader);
161   } while (!did_fail);
162   
163   for (i = loaders; i != NULL; i = i->next)
164     g_object_unref (i->data);
165   g_list_free (loaders);
166 }
167
168 static void
169 almost_exhaust_memory (void)
170 {
171   gpointer x = g_malloc (REMAINING_MEM_SIZE);
172   while (g_try_malloc (REMAINING_MEM_SIZE / 10))
173     ;
174   g_free (x);
175 }
176
177 static void
178 usage (void)
179 {
180   g_print ("usage: pixbuf-lowmem <pretend_memory_size> <files>\n");
181   exit (EXIT_FAILURE);
182 }
183
184 int
185 main (int argc, char **argv)
186 {
187   int i;
188   char *endptr;
189
190   if (argc <= 2)
191     usage();
192   
193   max_allocation = strtol (argv[1], &endptr, 10);
194   if (endptr == argv[1])
195     usage();
196
197   /* Set a malloc which emulates low mem */
198   g_mem_set_vtable (&limited_table);
199   
200   g_type_init ();
201   g_log_set_always_fatal (G_LOG_LEVEL_WARNING | G_LOG_LEVEL_ERROR | G_LOG_LEVEL_CRITICAL);
202   
203   /* memory tests */
204
205   /* How do the loaders behave when memory is low?
206      It depends on the state the above tests left the 
207      memory in.
208
209      - Sometimes the png loader tries to report an 
210        "out of memory", but then g_strdup_printf() calls
211        g_malloc(), which fails.
212        
213      - There are unchecked realloc()s inside libtiff, which means it
214        will never work with low memory, unless something drastic is
215        done, like allocating a lot of memory upfront and release it
216        before entering libtiff.  Also, some TIFFReadRGBAImage calls
217        returns successfully, even though they have called the error
218        handler with an 'out of memory' message.
219   */
220
221   almost_exhaust_memory ();
222
223   g_print ("Allocated %dK of %dK, %dK free during tests\n",
224            current_allocation / 1024, max_allocation / 1024,
225            (max_allocation - current_allocation) / 1024);
226
227   for (i = 2; i < argc; ++i)
228     {
229       gchar *contents;
230       gsize size;
231       GError *err = NULL;
232
233       if (!g_file_get_contents (argv[i], &contents, &size, &err))
234         {
235           g_print ("couldn't read %s: %s\n", argv[i], err->message);
236           exit (EXIT_FAILURE);
237         }
238       else
239         {
240           g_print ("%-40s memory            ", argv[i]);
241           fflush (stdout);
242           mem_test (contents, size);
243           g_print ("\tpassed\n");
244           g_free (contents);
245         }
246     }
247   
248   return 0;
249 }