13:34
  +   lY>eJ}@?q1h_I|+ ?     /*
   +----------------------------------------------------------------------+
   | Zend Engine                                                          |
   +----------------------------------------------------------------------+
   | Copyright (c) 1998-2016 Zend Technologies Ltd. (http://www.zend.com) |
   +----------------------------------------------------------------------+
   | This source file is subject to version 2.00 of the Zend license,     |
   | that is bundled with this package in the file LICENSE, and is        |
   | available through the world-wide-web at the following url:           |
   | http://www.zend.com/license/2_00.txt.                                |
   | If you did not receive a copy of the Zend license and are unable to  |
   | obtain it through the world-wide-web, please send a note to          |
   | license@zend.com so we can mail you a copy immediately.              |
   +----------------------------------------------------------------------+
   | Authors: David Wang <planetbeing@gmail.com>                          |
   |          Dmitry Stogov <dmitry@zend.com>                             |
   +----------------------------------------------------------------------+
*/

/* $Id$ */

#include "zend.h"
#include "zend_API.h"

#define GC_ROOT_BUFFER_MAX_ENTRIES 10000

#ifdef ZTS
ZEND_API int gc_globals_id;
#else
ZEND_API zend_gc_globals gc_globals;
#endif

static void root_buffer_dtor(zend_gc_globals *gc_globals TSRMLS_DC)
{
	if (gc_globals->buf) {
		free(gc_globals->buf);
		gc_globals->buf = NULL;
	}	
}

static void gc_globals_ctor_ex(zend_gc_globals *gc_globals TSRMLS_DC)
{
	gc_globals->gc_enabled = 0;
	gc_globals->gc_active = 0;

	gc_globals->buf = NULL;

	gc_globals->roots.next = &gc_globals->roots;
	gc_globals->roots.prev = &gc_globals->roots;
	gc_globals->unused = NULL;
	gc_globals->zval_to_free = NULL;
	gc_globals->free_list = NULL;
	gc_globals->next_to_free = NULL;

	gc_globals->gc_runs = 0;
	gc_globals->collected = 0;

#if GC_BENCH
	gc_globals->root_buf_length = 0;
	gc_globals->root_buf_peak = 0;
	gc_globals->zval_possible_root = 0;
	gc_globals->zobj_possible_root = 0;
	gc_globals->zval_buffered = 0;
	gc_globals->zobj_buffered = 0;
	gc_globals->zval_remove_from_buffer = 0;
	gc_globals->zobj_remove_from_buffer = 0;
	gc_globals->zval_marked_grey = 0;
	gc_globals->zobj_marked_grey = 0;
#endif
}

ZEND_API void gc_globals_ctor(TSRMLS_D)
{
#ifdef ZTS
	ts_allocate_id(&gc_globals_id, sizeof(zend_gc_globals), (ts_allocate_ctor) gc_globals_ctor_ex, (ts_allocate_dtor) root_buffer_dtor);
#else
	gc_globals_ctor_ex(&gc_globals);
#endif
}

ZEND_API void gc_globals_dtor(TSRMLS_D)
{
#ifndef ZTS
	root_buffer_dtor(&gc_globals TSRMLS_DC);
#endif
}

ZEND_API void gc_reset(TSRMLS_D)
{
	GC_G(gc_runs) = 0;
	GC_G(collected) = 0;

#if GC_BENCH
	GC_G(root_buf_length) = 0;
	GC_G(root_buf_peak) = 0;
	GC_G(zval_possible_root) = 0;
	GC_G(zobj_possible_root) = 0;
	GC_G(zval_buffered) = 0;
	GC_G(zobj_buffered) = 0;
	GC_G(zval_remove_from_buffer) = 0;
	GC_G(zobj_remove_from_buffer) = 0;
	GC_G(zval_marked_grey) = 0;
	GC_G(zobj_marked_grey) = 0;
#endif

	GC_G(roots).next = &GC_G(roots);
	GC_G(roots).prev = &GC_G(roots);

	if (GC_G(buf)) {
		GC_G(unused) = NULL;
		GC_G(first_unused) = GC_G(buf);

		GC_G(zval_to_free) = NULL;
	} else {
		GC_G(unused) = NULL;
		GC_G(first_unused) = NULL;
		GC_G(last_unused) = NULL;
	}
}

ZEND_API void gc_init(TSRMLS_D)
{
	if (GC_G(buf) == NULL && GC_G(gc_enabled)) {
		GC_G(buf) = (gc_root_buffer*) malloc(sizeof(gc_root_buffer) * GC_ROOT_BUFFER_MAX_ENTRIES);
		GC_G(last_unused) = &GC_G(buf)[GC_ROOT_BUFFER_MAX_ENTRIES];
		gc_reset(TSRMLS_C);
	}
}

ZEND_API void gc_zval_possible_root(zval *zv TSRMLS_DC)
{
	if (UNEXPECTED(GC_G(free_list) != NULL &&
	               GC_ZVAL_ADDRESS(zv) != NULL &&
		           GC_ZVAL_GET_COLOR(zv) == GC_BLACK) &&
		           (GC_ZVAL_ADDRESS(zv) < GC_G(buf) ||
		            GC_ZVAL_ADDRESS(zv) >= GC_G(last_unused))) {
		/* The given zval is a garbage that is going to be deleted by
		 * currentl