heap.c 14.3 KB
Newer Older
1 2 3 4 5
/*
 * msvcrt.dll heap functions
 *
 * Copyright 2000 Jon Griffiths
 *
6 7 8 9 10 11 12 13 14 15 16 17
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library 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
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
18
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
19
 *
20 21 22 23 24
 * Note: Win32 heap operations are MT safe. We only lock the new
 *       handler and non atomic heap operations
 */

#include "msvcrt.h"
25
#include "mtdll.h"
26 27 28
#include "wine/debug.h"

WINE_DEFAULT_DEBUG_CHANNEL(msvcrt);
29 30

/* MT */
31 32 33
#define LOCK_HEAP   _mlock( _HEAP_LOCK )
#define UNLOCK_HEAP _munlock( _HEAP_LOCK )

34 35 36 37 38 39 40
/* _aligned */
#define SAVED_PTR(x) ((void *)((DWORD_PTR)((char *)x - sizeof(void *)) & \
                               ~(sizeof(void *) - 1)))
#define ALIGN_PTR(ptr, alignment, offset) ((void *) \
    ((((DWORD_PTR)((char *)ptr + alignment + sizeof(void *) + offset)) & \
      ~(alignment - 1)) - offset))

41

42
typedef void (*MSVCRT_new_handler_func)(MSVCRT_size_t size);
43 44 45 46

static MSVCRT_new_handler_func MSVCRT_new_handler;
static int MSVCRT_new_mode;

47 48
/* FIXME - According to documentation it should be 8*1024, at runtime it returns 16 */ 
static unsigned int MSVCRT_amblksiz = 16;
49
/* FIXME - According to documentation it should be 480 bytes, at runtime default is 0 */
50
static MSVCRT_size_t MSVCRT_sbh_threshold = 0;
51

52
/*********************************************************************
Patrik Stridvall's avatar
Patrik Stridvall committed
53
 *		??2@YAPAXI@Z (MSVCRT.@)
54
 */
55
void* CDECL MSVCRT_operator_new(MSVCRT_size_t size)
56 57 58
{
  void *retval = HeapAlloc(GetProcessHeap(), 0, size);
  TRACE("(%ld) returning %p\n", size, retval);
59
  if(retval) return retval;
60
  LOCK_HEAP;
61
  if(MSVCRT_new_handler)
62
    (*MSVCRT_new_handler)(size);
63 64 65 66 67
  UNLOCK_HEAP;
  return retval;
}

/*********************************************************************
Patrik Stridvall's avatar
Patrik Stridvall committed
68
 *		??3@YAXPAX@Z (MSVCRT.@)
69
 */
70
void CDECL MSVCRT_operator_delete(void *mem)
71 72 73 74 75 76 77 78 79
{
  TRACE("(%p)\n", mem);
  HeapFree(GetProcessHeap(), 0, mem);
}


/*********************************************************************
 *		?_query_new_handler@@YAP6AHI@ZXZ (MSVCRT.@)
 */
80
MSVCRT_new_handler_func CDECL MSVCRT__query_new_handler(void)
81 82 83 84 85 86 87 88
{
  return MSVCRT_new_handler;
}


/*********************************************************************
 *		?_query_new_mode@@YAHXZ (MSVCRT.@)
 */
89
int CDECL MSVCRT__query_new_mode(void)
90 91 92 93 94 95 96
{
  return MSVCRT_new_mode;
}

/*********************************************************************
 *		?_set_new_handler@@YAP6AHI@ZP6AHI@Z@Z (MSVCRT.@)
 */
97
MSVCRT_new_handler_func CDECL MSVCRT__set_new_handler(MSVCRT_new_handler_func func)
98 99 100 101 102 103 104 105 106
{
  MSVCRT_new_handler_func old_handler;
  LOCK_HEAP;
  old_handler = MSVCRT_new_handler;
  MSVCRT_new_handler = func;
  UNLOCK_HEAP;
  return old_handler;
}

107 108 109
/*********************************************************************
 *		?set_new_handler@@YAP6AXXZP6AXXZ@Z (MSVCRT.@)
 */
110
MSVCRT_new_handler_func CDECL MSVCRT_set_new_handler(void *func)
111 112 113 114 115 116
{
  TRACE("(%p)\n",func);
  MSVCRT__set_new_handler(NULL);
  return NULL;
}

117 118 119
/*********************************************************************
 *		?_set_new_mode@@YAHH@Z (MSVCRT.@)
 */
120
int CDECL MSVCRT__set_new_mode(int mode)
121 122 123 124 125 126 127 128 129
{
  int old_mode;
  LOCK_HEAP;
  old_mode = MSVCRT_new_mode;
  MSVCRT_new_mode = mode;
  UNLOCK_HEAP;
  return old_mode;
}

130 131 132
/*********************************************************************
 *		_callnewh (MSVCRT.@)
 */
133
int CDECL _callnewh(MSVCRT_size_t size)
134 135 136 137 138 139
{
  if(MSVCRT_new_handler)
    (*MSVCRT_new_handler)(size);
  return 0;
}

140 141 142
/*********************************************************************
 *		_expand (MSVCRT.@)
 */
143
void* CDECL _expand(void* mem, MSVCRT_size_t size)
144 145 146 147 148 149 150
{
  return HeapReAlloc(GetProcessHeap(), HEAP_REALLOC_IN_PLACE_ONLY, mem, size);
}

/*********************************************************************
 *		_heapchk (MSVCRT.@)
 */
151
int CDECL _heapchk(void)
152 153 154
{
  if (!HeapValidate( GetProcessHeap(), 0, NULL))
  {
155 156
    msvcrt_set_errno(GetLastError());
    return MSVCRT__HEAPBADNODE;
157
  }
158
  return MSVCRT__HEAPOK;
159 160 161 162 163
}

/*********************************************************************
 *		_heapmin (MSVCRT.@)
 */
164
int CDECL _heapmin(void)
165 166 167 168
{
  if (!HeapCompact( GetProcessHeap(), 0 ))
  {
    if (GetLastError() != ERROR_CALL_NOT_IMPLEMENTED)
169
      msvcrt_set_errno(GetLastError());
170 171 172 173 174 175 176 177
    return -1;
  }
  return 0;
}

/*********************************************************************
 *		_heapwalk (MSVCRT.@)
 */
178
int CDECL _heapwalk(struct MSVCRT__heapinfo* next)
179 180 181 182 183 184
{
  PROCESS_HEAP_ENTRY phe;

  LOCK_HEAP;
  phe.lpData = next->_pentry;
  phe.cbData = next->_size;
185
  phe.wFlags = next->_useflag == MSVCRT__USEDENTRY ? PROCESS_HEAP_ENTRY_BUSY : 0;
186 187 188 189 190

  if (phe.lpData && phe.wFlags & PROCESS_HEAP_ENTRY_BUSY &&
      !HeapValidate( GetProcessHeap(), 0, phe.lpData ))
  {
    UNLOCK_HEAP;
191 192
    msvcrt_set_errno(GetLastError());
    return MSVCRT__HEAPBADNODE;
193 194 195 196 197 198 199 200
  }

  do
  {
    if (!HeapWalk( GetProcessHeap(), &phe ))
    {
      UNLOCK_HEAP;
      if (GetLastError() == ERROR_NO_MORE_ITEMS)
201 202
         return MSVCRT__HEAPEND;
      msvcrt_set_errno(GetLastError());
203
      if (!phe.lpData)
204 205
        return MSVCRT__HEAPBADBEGIN;
      return MSVCRT__HEAPBADNODE;
206 207 208 209 210 211
    }
  } while (phe.wFlags & (PROCESS_HEAP_REGION|PROCESS_HEAP_UNCOMMITTED_RANGE));

  UNLOCK_HEAP;
  next->_pentry = phe.lpData;
  next->_size = phe.cbData;
212 213
  next->_useflag = phe.wFlags & PROCESS_HEAP_ENTRY_BUSY ? MSVCRT__USEDENTRY : MSVCRT__FREEENTRY;
  return MSVCRT__HEAPOK;
214 215 216 217 218
}

/*********************************************************************
 *		_heapset (MSVCRT.@)
 */
219
int CDECL _heapset(unsigned int value)
220 221
{
  int retval;
222
  struct MSVCRT__heapinfo heap;
223

224
  memset( &heap, 0, sizeof(heap) );
225
  LOCK_HEAP;
226
  while ((retval = _heapwalk(&heap)) == MSVCRT__HEAPOK)
227
  {
228
    if (heap._useflag == MSVCRT__FREEENTRY)
229 230 231
      memset(heap._pentry, value, heap._size);
  }
  UNLOCK_HEAP;
232
  return retval == MSVCRT__HEAPEND? MSVCRT__HEAPOK : retval;
233 234
}

235 236 237
/*********************************************************************
 *		_heapadd (MSVCRT.@)
 */
238
int CDECL _heapadd(void* mem, MSVCRT_size_t size)
239
{
240
  TRACE("(%p,%ld) unsupported in Win32\n", mem,size);
241
  *MSVCRT__errno() = MSVCRT_ENOSYS;
242 243 244
  return -1;
}

245 246 247
/*********************************************************************
 *		_msize (MSVCRT.@)
 */
248
MSVCRT_size_t CDECL _msize(void* mem)
249
{
250 251
  MSVCRT_size_t size = HeapSize(GetProcessHeap(),0,mem);
  if (size == ~(MSVCRT_size_t)0)
252 253 254 255 256 257 258 259 260 261
  {
    WARN(":Probably called with non wine-allocated memory, ret = -1\n");
    /* At least the Win32 crtdll/msvcrt also return -1 in this case */
  }
  return size;
}

/*********************************************************************
 *		calloc (MSVCRT.@)
 */
262
void* CDECL MSVCRT_calloc(MSVCRT_size_t size, MSVCRT_size_t count)
263 264 265 266 267 268 269
{
  return HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY, size * count );
}

/*********************************************************************
 *		free (MSVCRT.@)
 */
270
void CDECL MSVCRT_free(void* ptr)
271 272 273 274 275 276 277
{
  HeapFree(GetProcessHeap(),0,ptr);
}

/*********************************************************************
 *                  malloc (MSVCRT.@)
 */
278
void* CDECL MSVCRT_malloc(MSVCRT_size_t size)
279 280 281
{
  void *ret = HeapAlloc(GetProcessHeap(),0,size);
  if (!ret)
282
      *MSVCRT__errno() = MSVCRT_ENOMEM;
283 284 285 286 287 288
  return ret;
}

/*********************************************************************
 *		realloc (MSVCRT.@)
 */
289
void* CDECL MSVCRT_realloc(void* ptr, MSVCRT_size_t size)
290
{
291 292 293 294
  if (!ptr) return MSVCRT_malloc(size);
  if (size) return HeapReAlloc(GetProcessHeap(), 0, ptr, size);
  MSVCRT_free(ptr);
  return NULL;
295
}
296 297 298 299

/*********************************************************************
 *		__p__amblksiz (MSVCRT.@)
 */
300
unsigned int* CDECL __p__amblksiz(void)
301 302 303
{
  return &MSVCRT_amblksiz;
}
304 305 306 307

/*********************************************************************
 *		_get_sbh_threshold (MSVCRT.@)
 */
308
MSVCRT_size_t CDECL _get_sbh_threshold(void)
309 310 311 312 313 314 315
{
  return MSVCRT_sbh_threshold;
}

/*********************************************************************
 *		_set_sbh_threshold (MSVCRT.@)
 */
316
int CDECL _set_sbh_threshold(MSVCRT_size_t threshold)
317 318 319 320 321 322 323
{
  if(threshold > 1016)
     return 0;
  else
     MSVCRT_sbh_threshold = threshold;
  return 1;
}
324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341

/*********************************************************************
 *		_aligned_free (MSVCRT.@)
 */
void CDECL _aligned_free(void *memblock)
{
    TRACE("(%p)\n", memblock);

    if (memblock)
    {
        void **saved = SAVED_PTR(memblock);
        MSVCRT_free(*saved);
    }
}

/*********************************************************************
 *		_aligned_offset_malloc (MSVCRT.@)
 */
342
void * CDECL _aligned_offset_malloc(MSVCRT_size_t size, MSVCRT_size_t alignment, MSVCRT_size_t offset)
343 344
{
    void *memblock, *temp, **saved;
345
    TRACE("(%lu, %lu, %lu)\n", size, alignment, offset);
346 347 348 349

    /* alignment must be a power of 2 */
    if ((alignment & (alignment - 1)) != 0)
    {
350
        *MSVCRT__errno() = MSVCRT_EINVAL;
351 352 353 354 355 356
        return NULL;
    }

    /* offset must be less than size */
    if (offset >= size)
    {
357
        *MSVCRT__errno() = MSVCRT_EINVAL;
358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384
        return NULL;
    }

    /* don't align to less than void pointer size */
    if (alignment < sizeof(void *))
        alignment = sizeof(void *);

    /* allocate enough space for void pointer and alignment */
    temp = MSVCRT_malloc(size + alignment + sizeof(void *));

    if (!temp)
        return NULL;

    /* adjust pointer for proper alignment and offset */
    memblock = ALIGN_PTR(temp, alignment, offset);

    /* Save the real allocation address below returned address */
    /* so it can be found later to free. */
    saved = SAVED_PTR(memblock);
    *saved = temp;

    return memblock;
}

/*********************************************************************
 *		_aligned_malloc (MSVCRT.@)
 */
385
void * CDECL _aligned_malloc(MSVCRT_size_t size, MSVCRT_size_t alignment)
386
{
387
    TRACE("(%lu, %lu)\n", size, alignment);
388 389 390 391 392 393
    return _aligned_offset_malloc(size, alignment, 0);
}

/*********************************************************************
 *		_aligned_offset_realloc (MSVCRT.@)
 */
394 395
void * CDECL _aligned_offset_realloc(void *memblock, MSVCRT_size_t size,
                                     MSVCRT_size_t alignment, MSVCRT_size_t offset)
396 397
{
    void * temp, **saved;
398
    MSVCRT_size_t old_padding, new_padding, old_size;
399
    TRACE("(%p, %lu, %lu, %lu)\n", memblock, size, alignment, offset);
400 401 402 403 404 405 406

    if (!memblock)
        return _aligned_offset_malloc(size, alignment, offset);

    /* alignment must be a power of 2 */
    if ((alignment & (alignment - 1)) != 0)
    {
407
        *MSVCRT__errno() = MSVCRT_EINVAL;
408 409 410 411 412 413
        return NULL;
    }

    /* offset must be less than size */
    if (offset >= size)
    {
414
        *MSVCRT__errno() = MSVCRT_EINVAL;
415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431
        return NULL;
    }

    if (size == 0)
    {
        _aligned_free(memblock);
        return NULL;
    }

    /* don't align to less than void pointer size */
    if (alignment < sizeof(void *))
        alignment = sizeof(void *);

    /* make sure alignment and offset didn't change */
    saved = SAVED_PTR(memblock);
    if (memblock != ALIGN_PTR(*saved, alignment, offset))
    {
432
        *MSVCRT__errno() = MSVCRT_EINVAL;
433 434 435
        return NULL;
    }

436 437 438 439 440 441 442 443 444
    old_padding = (char *)memblock - (char *)*saved;

    /* Get previous size of block */
    old_size = _msize(*saved);
    if (old_size == -1)
    {
        /* It seems this function was called with an invalid pointer. Bail out. */
        return NULL;
    }
445

446
    /* Adjust old_size to get amount of actual data in old block. */
447
    if (old_size < old_padding)
448 449 450 451
    {
        /* Shouldn't happen. Something's weird, so bail out. */
        return NULL;
    }
452
    old_size -= old_padding;
453 454 455 456 457 458 459 460 461 462 463 464 465

    temp = MSVCRT_realloc(*saved, size + alignment + sizeof(void *));

    if (!temp)
        return NULL;

    /* adjust pointer for proper alignment and offset */
    memblock = ALIGN_PTR(temp, alignment, offset);

    /* Save the real allocation address below returned address */
    /* so it can be found later to free. */
    saved = SAVED_PTR(memblock);

466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497
    new_padding = (char *)memblock - (char *)temp;

/*
   Memory layout of old block is as follows:
   +-------+---------------------+-+--------------------------+-----------+
   |  ...  | "old_padding" bytes | | ... "old_size" bytes ... |    ...    |
   +-------+---------------------+-+--------------------------+-----------+
           ^                     ^ ^
           |                     | |
        *saved               saved memblock

   Memory layout of new block is as follows:
   +-------+-----------------------------+-+----------------------+-------+
   |  ...  |    "new_padding" bytes      | | ... "size" bytes ... |  ...  |
   +-------+-----------------------------+-+----------------------+-------+
           ^                             ^ ^
           |                             | |
          temp                       saved memblock

   However, in the new block, actual data is still written as follows
   (because it was copied by MSVCRT_realloc):
   +-------+---------------------+--------------------------------+-------+
   |  ...  | "old_padding" bytes |   ... "old_size" bytes ...     |  ...  |
   +-------+---------------------+--------------------------------+-------+
           ^                             ^ ^
           |                             | |
          temp                       saved memblock

   Therefore, min(old_size,size) bytes of actual data have to be moved
   from the offset they were at in the old block (temp + old_padding),
   to the offset they have to be in the new block (temp + new_padding == memblock).
*/
498
    if (new_padding != old_padding)
499
        memmove((char *)memblock, (char *)temp + old_padding, (old_size < size) ? old_size : size);
500 501 502 503 504 505 506 507 508

    *saved = temp;

    return memblock;
}

/*********************************************************************
 *		_aligned_realloc (MSVCRT.@)
 */
509
void * CDECL _aligned_realloc(void *memblock, MSVCRT_size_t size, MSVCRT_size_t alignment)
510
{
511
    TRACE("(%p, %lu, %lu)\n", memblock, size, alignment);
512 513
    return _aligned_offset_realloc(memblock, size, alignment, 0);
}